diff --git a/.gitignore b/.gitignore index 3496f3e86..135102f04 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ *.pydevproject .project .metadata -bin/ tmp/ *.tmp *.bak @@ -45,7 +44,6 @@ local.properties [Rr]elease/ x64/ build/ -[Bb]in/ [Oo]bj/ # MSTest test Results diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/liblua.a b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/liblua.a deleted file mode 100644 index f4d65be64..000000000 Binary files a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/liblua.a and /dev/null differ diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/lua/5.1/checks.dll b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/lua/5.1/checks.dll deleted file mode 100644 index 98254c4f4..000000000 Binary files a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/lua/5.1/checks.dll and /dev/null differ diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/lua/5.1/lfs.dll b/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/lua/5.1/lfs.dll deleted file mode 100644 index 6e73ea2c2..000000000 Binary files a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/lua/5.1/lfs.dll and /dev/null differ diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/man/man1/lua.1 b/Moose Development Evironment Setup/LuaFiles/lua/5.1/man/man1/lua.1 deleted file mode 100644 index 24809cc6c..000000000 --- a/Moose Development Evironment Setup/LuaFiles/lua/5.1/man/man1/lua.1 +++ /dev/null @@ -1,163 +0,0 @@ -.\" $Id: lua.man,v 1.11 2006/01/06 16:03:34 lhf Exp $ -.TH LUA 1 "$Date: 2006/01/06 16:03:34 $" -.SH NAME -lua \- Lua interpreter -.SH SYNOPSIS -.B lua -[ -.I options -] -[ -.I script -[ -.I args -] -] -.SH DESCRIPTION -.B lua -is the stand-alone Lua interpreter. -It loads and executes Lua programs, -either in textual source form or -in precompiled binary form. -(Precompiled binaries are output by -.BR luac , -the Lua compiler.) -.B lua -can be used as a batch interpreter and also interactively. -.LP -The given -.I options -(see below) -are executed and then -the Lua program in file -.I script -is loaded and executed. -The given -.I args -are available to -.I script -as strings in a global table named -.BR arg . -If these arguments contain spaces or other characters special to the shell, -then they should be quoted -(but note that the quotes will be removed by the shell). -The arguments in -.B arg -start at 0, -which contains the string -.RI ' script '. -The index of the last argument is stored in -.BR arg.n . -The arguments given in the command line before -.IR script , -including the name of the interpreter, -are available in negative indices in -.BR arg . -.LP -At the very start, -before even handling the command line, -.B lua -executes the contents of the environment variable -.BR LUA_INIT , -if it is defined. -If the value of -.B LUA_INIT -is of the form -.RI '@ filename ', -then -.I filename -is executed. -Otherwise, the string is assumed to be a Lua statement and is executed. -.LP -Options start with -.B '\-' -and are described below. -You can use -.B "'\--'" -to signal the end of options. -.LP -If no arguments are given, -then -.B "\-v \-i" -is assumed when the standard input is a terminal; -otherwise, -.B "\-" -is assumed. -.LP -In interactive mode, -.B lua -prompts the user, -reads lines from the standard input, -and executes them as they are read. -If a line does not contain a complete statement, -then a secondary prompt is displayed and -lines are read until a complete statement is formed or -a syntax error is found. -So, one way to interrupt the reading of an incomplete statement is -to force a syntax error: -adding a -.B ';' -in the middle of a statement is a sure way of forcing a syntax error -(except inside multiline strings and comments; these must be closed explicitly). -If a line starts with -.BR '=' , -then -.B lua -displays the values of all the expressions in the remainder of the -line. The expressions must be separated by commas. -The primary prompt is the value of the global variable -.BR _PROMPT , -if this value is a string; -otherwise, the default prompt is used. -Similarly, the secondary prompt is the value of the global variable -.BR _PROMPT2 . -So, -to change the prompts, -set the corresponding variable to a string of your choice. -You can do that after calling the interpreter -or on the command line -(but in this case you have to be careful with quotes -if the prompt string contains a space; otherwise you may confuse the shell.) -The default prompts are "> " and ">> ". -.SH OPTIONS -.TP -.B \- -load and execute the standard input as a file, -that is, -not interactively, -even when the standard input is a terminal. -.TP -.BI \-e " stat" -execute statement -.IR stat . -You need to quote -.I stat -if it contains spaces, quotes, -or other characters special to the shell. -.TP -.B \-i -enter interactive mode after -.I script -is executed. -.TP -.BI \-l " name" -call -.BI require(' name ') -before executing -.IR script . -Typically used to load libraries. -.TP -.B \-v -show version information. -.SH "SEE ALSO" -.BR luac (1) -.br -http://www.lua.org/ -.SH DIAGNOSTICS -Error messages should be self explanatory. -.SH AUTHORS -R. Ierusalimschy, -L. H. de Figueiredo, -and -W. Celes -.\" EOF diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/man/man1/luac.1 b/Moose Development Evironment Setup/LuaFiles/lua/5.1/man/man1/luac.1 deleted file mode 100644 index d8146782d..000000000 --- a/Moose Development Evironment Setup/LuaFiles/lua/5.1/man/man1/luac.1 +++ /dev/null @@ -1,136 +0,0 @@ -.\" $Id: luac.man,v 1.28 2006/01/06 16:03:34 lhf Exp $ -.TH LUAC 1 "$Date: 2006/01/06 16:03:34 $" -.SH NAME -luac \- Lua compiler -.SH SYNOPSIS -.B luac -[ -.I options -] [ -.I filenames -] -.SH DESCRIPTION -.B luac -is the Lua compiler. -It translates programs written in the Lua programming language -into binary files that can be later loaded and executed. -.LP -The main advantages of precompiling chunks are: -faster loading, -protecting source code from accidental user changes, -and -off-line syntax checking. -.LP -Pre-compiling does not imply faster execution -because in Lua chunks are always compiled into bytecodes before being executed. -.B luac -simply allows those bytecodes to be saved in a file for later execution. -.LP -Pre-compiled chunks are not necessarily smaller than the corresponding source. -The main goal in pre-compiling is faster loading. -.LP -The binary files created by -.B luac -are portable only among architectures with the same word size and byte order. -.LP -.B luac -produces a single output file containing the bytecodes -for all source files given. -By default, -the output file is named -.BR luac.out , -but you can change this with the -.B \-o -option. -.LP -In the command line, -you can mix -text files containing Lua source and -binary files containing precompiled chunks. -This is useful to combine several precompiled chunks, -even from different (but compatible) platforms, -into a single precompiled chunk. -.LP -You can use -.B "'\-'" -to indicate the standard input as a source file -and -.B "'\--'" -to signal the end of options -(that is, -all remaining arguments will be treated as files even if they start with -.BR "'\-'" ). -.LP -The internal format of the binary files produced by -.B luac -is likely to change when a new version of Lua is released. -So, -save the source files of all Lua programs that you precompile. -.LP -.SH OPTIONS -Options must be separate. -.TP -.B \-l -produce a listing of the compiled bytecode for Lua's virtual machine. -Listing bytecodes is useful to learn about Lua's virtual machine. -If no files are given, then -.B luac -loads -.B luac.out -and lists its contents. -.TP -.BI \-o " file" -output to -.IR file , -instead of the default -.BR luac.out . -(You can use -.B "'\-'" -for standard output, -but not on platforms that open standard output in text mode.) -The output file may be a source file because -all files are loaded before the output file is written. -Be careful not to overwrite precious files. -.TP -.B \-p -load files but do not generate any output file. -Used mainly for syntax checking and for testing precompiled chunks: -corrupted files will probably generate errors when loaded. -Lua always performs a thorough integrity test on precompiled chunks. -Bytecode that passes this test is completely safe, -in the sense that it will not break the interpreter. -However, -there is no guarantee that such code does anything sensible. -(None can be given, because the halting problem is unsolvable.) -If no files are given, then -.B luac -loads -.B luac.out -and tests its contents. -No messages are displayed if the file passes the integrity test. -.TP -.B \-s -strip debug information before writing the output file. -This saves some space in very large chunks, -but if errors occur when running a stripped chunk, -then the error messages may not contain the full information they usually do. -For instance, -line numbers and names of local variables are lost. -.TP -.B \-v -show version information. -.SH FILES -.TP 15 -.B luac.out -default output file -.SH "SEE ALSO" -.BR lua (1) -.br -http://www.lua.org/ -.SH DIAGNOSTICS -Error messages should be self explanatory. -.SH AUTHORS -L. H. de Figueiredo, -R. Ierusalimschy and -W. Celes -.\" EOF diff --git a/Moose Development Evironment Setup/MooseDevelopmentEnvironmentSetup.au3 b/Moose Development Evironment Setup/MooseDevelopmentEnvironmentSetup.au3 deleted file mode 100644 index d12fc405a..000000000 --- a/Moose Development Evironment Setup/MooseDevelopmentEnvironmentSetup.au3 +++ /dev/null @@ -1,246 +0,0 @@ -; MooseDevelopmentEnvironmentSetup.exe -; ------------------------------------ -; This program sets up the Moose Development Evironment for Testers and Developers. -; The goal is to make it easy to use the Dynamic Loading Moose.lua, which is more suitable for rapid development and regular changes -; than its static counterpart. -; -; Author : Hugues "GreyEcho" Bousquet - -#include -#include -#include -#include -#include -#include -#include -#include -#Include -#Include -#include - -Global $7zipPath -Global $DCSWorldPath -Global $RepoPath -Global $DCSWorldScriptsMoosePath -Global $MooseDevFolderPath -Global $Log -Global $ProgramFilesDir = @HomeDrive & '\Program Files\' - - -Func CleanExit() - _FileWriteLog($Log, 'INFO:'&@TAB&'Program exited cleanly'&@CRLF) - FileClose($Log) - Exit -EndFunc - -Func Welcome() - #Region ### START Koda GUI section ### Form= - $Form2 = GUICreate("Welcome", 532, 150, 620, 457) - $Label1 = GUICtrlCreateLabel("Welcome to Moose ! ", 120, 16, 217, 33) - GUICtrlSetFont(-1, 18, 800, 0, "Calibri") - $Label2 = GUICtrlCreateLabel("This tool is designed to help you setup your Moose development environment.", 104, 56, 370, 17) - $Button1 = GUICtrlCreateButton("&OK", 268, 115, 75, 25) - $Button2 = GUICtrlCreateButton("&Cancel", 187, 116, 75, 25) - $Label3 = GUICtrlCreateLabel("Before you proceed, please make sure that you correctly installed GitHub, as well as 7-zip.", 104, 80, 423, 17) - $Pic1 = GUICtrlCreatePic("C:\Users\Hugues\Desktop\Moose\MOOSE_Logo_Primary_Color.jpg", 8, 8, 89, 89) - GUISetState(@SW_SHOW) - #EndRegion ### END Koda GUI section ### - - _FileWriteLog($Log, 'INFO:'&@TAB&'In window "Welcome"'&@CRLF) - - While 1 - $nMsg = GUIGetMsg() - Switch $nMsg - case $GUI_EVENT_CLOSE - GUIDelete() - CleanExit() - case $Button2 - GUIDelete() - CleanExit() - case $Button1 - GUIDelete() - ExitLoop - EndSwitch - WEnd - -EndFunc - -Func FoldersLocation() - #Region ### START Koda GUI section ### Form= - $Form1 = GUICreate("Location of your Folders", 603, 237, 585, 425) - $GroupBox1 = GUICtrlCreateGroup("Folder Locations ", 8, 9, 585, 185) - $Input1 = GUICtrlCreateInput("C:\Program Files\7-Zip\", 24, 48, 505, 21) - $Label1 = GUICtrlCreateLabel("7-Zip Location", 24, 32, 72, 17) - $Input2 = GUICtrlCreateInput("C:\Program Files\Eagle Dynamics\DCS World\", 24, 104, 505, 21) - $Label2 = GUICtrlCreateLabel("DCS World Install Location", 24, 88, 131, 17) - $Input3 = GUICtrlCreateInput("C:\Users\Hugues\Documents\GitHub\MOOSE\", 24, 160, 505, 21) - $Label3 = GUICtrlCreateLabel("MOOSE Local Repository Location", 24, 144, 169, 17) - $Button3 = GUICtrlCreateButton("Browse", 528, 48, 57, 21) - $Button4 = GUICtrlCreateButton("Browse", 528, 104, 57, 21) - $Button5 = GUICtrlCreateButton("Browse", 528, 160, 57, 21) - GUICtrlCreateGroup("", -99, -99, 1, 1) - $Button1 = GUICtrlCreateButton("&OK", 308, 203, 75, 25) - $Button2 = GUICtrlCreateButton("&Cancel", 219, 204, 75, 25) - GUISetState(@SW_SHOW) - #EndRegion ### END Koda GUI section ### - - _FileWriteLog($Log, 'INFO:'&@TAB&'In window "Folders Location"'&@CRLF) - - While 1 - $nMsg = GUIGetMsg() - Switch $nMsg - case $GUI_EVENT_CLOSE - GUIDelete() - CleanExit() - case $Button2 - GUIDelete() - CleanExit() - ; Browse buttons - case $Button3 - $7zipPath = FileSelectFolder("Select the 7-Zip Installation Folder", $ProgramFilesDir) - If $7zipPath Then - GUICtrlSetData($Input1, $7zipPath) - EndIf - case $Button4 - $DCSWorldPath = FileSelectFolder("Select the DCS World Installation Folder", $ProgramFilesDir) - If $DCSWorldPath Then - GUICtrlSetData($Input2, $DCSWorldPath) - EndIf - case $Button5 - $RepoPath = FileSelectFolder("Select the local MOOSE GitHub Repository Folder", @MyDocumentsDir) - If $RepoPath Then - GUICtrlSetData($Input3, $RepoPath) - EndIf - ; ok ! - case $Button1 - If FileExists(GUICtrlRead($Input1)) and FileExists(GUICtrlRead($Input2)) and FileExists(GUICtrlRead($Input3)) Then - $7zipPath = GUICtrlRead($Input1) - $DCSWorldPath = GUICtrlRead($Input2) - $RepoPath = GUICtrlRead($Input3) - - ; add trailing '\' when necessary - If StringRight($7zipPath, 1) <> "\" Then - $7zipPath &= "\" - EndIf - If StringRight($DCSWorldPath, 1) <> "\" Then - $DCSWorldPath &= "\" - EndIf - If StringRight($RepoPath, 1) <> "\" Then - $RepoPath &= "\" - EndIf - - DirCreate($DCSWorldPath&'Scripts\Moose\') - $DCSWorldScriptsMoosePath = $DCSWorldPath & 'Scripts\Moose\' - $MooseDevFolderPath = $RepoPath & 'Moose Development\Moose\' - - _FileWriteLog($Log, 'INFO:'&@TAB&'7Zip Path : '&$7zipPath&@CRLF) - _FileWriteLog($Log, 'INFO:'&@TAB&'DCS World Path : '&$DCSWorldPath&@CRLF) - _FileWriteLog($Log, 'INFO:'&@TAB&'Moose Repo Path : '&$RepoPath&@CRLF) - _FileWriteLog($Log, 'INFO:'&@TAB&'DCS World Scripts Path : '&$DCSWorldScriptsMoosePath&@CRLF) - _FileWriteLog($Log, 'INFO:'&@TAB&'Moose Development Folder Path : '&$MooseDevFolderPath&@CRLF) - GUIDelete() - ExitLoop - Else - MsgBox(16, "Error", "One of the file paths is invalid, please check again.") ; TODO : Which one is wrong ? - _FileWriteLog($Log, 'ERROR:'&@TAB&'One of the paths is invalid'&@CRLF) - _FileWriteLog($Log, 'INFO:'&@TAB&'7Zip Path : '&$7zipPath&@CRLF) - _FileWriteLog($Log, 'INFO:'&@TAB&'DCS World Path : '&$DCSWorldPath&@CRLF) - _FileWriteLog($Log, 'INFO:'&@TAB&'Moose Repo Path : '&$RepoPath&@CRLF) - EndIf - EndSwitch - Wend -EndFunc - -Func SetupInProgress() - #Region ### START Koda GUI section ### Form= - $Form3 = GUICreate("Setup In Progress", 522, 237, 638, 427) - $Button1 = GUICtrlCreateButton("&OK", 223, 203, 75, 25) - $Edit1 = GUICtrlCreateEdit("", 8, 8, 505, 185) - GUISetState(@SW_SHOW) - #EndRegion ### END Koda GUI section ### - - GUICtrlSetState($Button1, $GUI_DISABLE) - - local $InstallSuccessfull = 1 - - _FileWriteLog($Log, 'INFO:'&@TAB&'In window "SetupInProgress"'&@CRLF) - - local $TrimmedMooseDevFolderPath = StringTrimRight($MooseDevFolderPath, 1) - - ; Create the Dynamic Link - If FileCreateNTFSLink($TrimmedMooseDevFolderPath, $DCSWorldScriptsMoosePath, $FC_OVERWRITE) Then - _FileWriteLog($Log, 'INFO:'&@TAB&"Hard Link created for "&$TrimmedMooseDevFolderPath&" in "&$DCSWorldScriptsMoosePath&@CRLF) - _GUICtrlEdit_AppendText($Edit1, "Hard Link Ccreated... Ok!"&@CRLF) - Else - _FileWriteLog($Log, 'ERROR:'&@TAB&"Couldn't create a hard link for "&$TrimmedMooseDevFolderPath&" in "&$DCSWorldScriptsMoosePath&@CRLF) - _GUICtrlEdit_AppendText($Edit1, "ERROR : Couldn't create a hard link for "&$TrimmedMooseDevFolderPath&" in "&$DCSWorldScriptsMoosePath&@CRLF) - $InstallSuccessfull = 0 - EndIf - - ; Get the current PATH and append 7Zip's path to it - local $NewPathContent = EnvGet("PATH") - If StringRight($NewPathContent, 1) <> ";" Then - $NewPathContent &= ";" - EndIf - $NewPathContent &= $7zipPath - - ; Add the 7zip folder path to %PATH% - If Not StringInStr(EnvGet("PATH"), "7-Zip") Then - If RegWrite("HKEY_CURRENT_USER\Environment", "Path", "REG_SZ", $NewPathContent) Then - _FileWriteLog($Log, 'INFO:'&@TAB&$7zipPath&" added to %PATH%. PATH = "&EnvGet("PATH")&@CRLF) - _GUICtrlEdit_AppendText($Edit1, "%PATH% Evrionment Variable updated... Ok!"&@CRLF) - Else - _FileWriteLog($Log, 'ERROR:'&@TAB&$7zipPath&" could not to %PATH%. Command :"&'"' & @ComSpec & '" /k ' & 'setx /M PATH "%PATH%;' & $7zipPath&@CRLF) - _GUICtrlEdit_AppendText($Edit1, "ERROR : Couldn't add "&$7zipPath&" to %PATH%"&@CRLF) - $InstallSuccessfull = 0 - EndIf - Else - _FileWriteLog($Log, 'INFO:'&@TAB&$7zipPath&" is already set in %PATH%. PATH = "&EnvGet("PATH")&@CRLF) - _GUICtrlEdit_AppendText($Edit1, "INFO : %PATH% already stores the 7-Zip folder path, no need to modify"&@CRLF) - EndIf - - ; Copy lua folder to ProgramFiles - local $TrimmedLuaPath = @ScriptDir&"\LuaFiles" - local $TrimmedProgramFilesDir = StringTrimRight($ProgramFilesDir, 1) - If DirCopy($TrimmedLuaPath, $TrimmedProgramFilesDir, $FC_OVERWRITE) Then - _FileWriteLog($Log, 'INFO:'&@TAB&$TrimmedLuaPath&" successfully copied to "&$TrimmedProgramFilesDir&@CRLF) - _GUICtrlEdit_AppendText($Edit1, "Lua 5.1 Installation... Ok!"&@CRLF) - Else - _FileWriteLog($Log, 'ERROR:'&@TAB&"Could not copy "&$TrimmedLuaPath&" to "&$TrimmedProgramFilesDir&@CRLF) - _GUICtrlEdit_AppendText($Edit1, "ERROR : Could not install lua 5.1 in "&$ProgramFilesDir&" Please retry, running this program is admin"&@CRLF) - $InstallSuccessfull = 0 - EndIf - - ; Succesfull Message - If $InstallSuccessfull Then - _GUICtrlEdit_AppendText($Edit1, "Setup Complete !"&@CRLF) - _FileWriteLog($Log, 'INFO:'&@TAB&'Setup Successful. Please reboot the computer.'&@CRLF) - Else - _GUICtrlEdit_AppendText($Edit1, "Setup finished, but some problem occured. Please fix them manually or retry the installation process."&@CRLF) - _FileWriteLog($Log, 'INFO:'&@TAB&'Setup finished, but some error occured'&@CRLF) - EndIf - - GUICtrlSetState($Button1, $GUI_ENABLE) - - While 1 - $nMsg = GUIGetMsg() - Switch $nMsg - case $GUI_EVENT_CLOSE - GUIDelete() - CleanExit() - case $Button1 - MsgBox(64, "Reboot", "You need to reboot your system to be able to use the automated .miz manipualtion tools") ; TODO : Automtically reboot ? - GUIDelete() - CleanExit() - EndSwitch - WEnd - -EndFunc - -While 1 - $Log = FileOpen(@ScriptDir & "\mdes.log", 1) - FileWrite($Log, @CRLF&'New Session !'&@CRLF&'============='&@CRLF) - Welcome() - FoldersLocation() - SetupInProgress() -WEnd diff --git a/Moose Development/LDT External Tools/Update Single Moose Test Mission.launch b/Moose Development/LDT External Tools/Moose DOCUMENTATION Generate.launch similarity index 68% rename from Moose Development/LDT External Tools/Update Single Moose Test Mission.launch rename to Moose Development/LDT External Tools/Moose DOCUMENTATION Generate.launch index 5a375b0f0..f652d09d7 100644 --- a/Moose Development/LDT External Tools/Update Single Moose Test Mission.launch +++ b/Moose Development/LDT External Tools/Moose DOCUMENTATION Generate.launch @@ -3,7 +3,7 @@ - - - + + + diff --git a/Moose Development/LDT External Tools/Moose Documentation.launch b/Moose Development/LDT External Tools/Moose Documentation.launch deleted file mode 100644 index 3190773bc..000000000 --- a/Moose Development/LDT External Tools/Moose Documentation.launch +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/Moose Development/LDT External Tools/Moose Dynamic.launch b/Moose Development/LDT External Tools/Moose LOADER Generate Dynamic Moose.lua.launch similarity index 54% rename from Moose Development/LDT External Tools/Moose Dynamic.launch rename to Moose Development/LDT External Tools/Moose LOADER Generate Dynamic Moose.lua.launch index c989a0380..ffc89c77d 100644 --- a/Moose Development/LDT External Tools/Moose Dynamic.launch +++ b/Moose Development/LDT External Tools/Moose LOADER Generate Dynamic Moose.lua.launch @@ -3,7 +3,7 @@ - - - + + + diff --git a/Moose Development/LDT External Tools/Moose Static.launch b/Moose Development/LDT External Tools/Moose LOADER Generate Static Moose.lua.launch similarity index 54% rename from Moose Development/LDT External Tools/Moose Static.launch rename to Moose Development/LDT External Tools/Moose LOADER Generate Static Moose.lua.launch index b4aba4d88..b8a402dc5 100644 --- a/Moose Development/LDT External Tools/Moose Static.launch +++ b/Moose Development/LDT External Tools/Moose LOADER Generate Static Moose.lua.launch @@ -3,7 +3,7 @@ - - - + + + diff --git a/Moose Development/LDT External Tools/Update ALL Moose Test Missions.launch b/Moose Development/LDT External Tools/Moose UPDATE ALL MOOSE Demonstration Missions.launch similarity index 58% rename from Moose Development/LDT External Tools/Update ALL Moose Test Missions.launch rename to Moose Development/LDT External Tools/Moose UPDATE ALL MOOSE Demonstration Missions.launch index 465818763..570f68b6e 100644 --- a/Moose Development/LDT External Tools/Update ALL Moose Test Missions.launch +++ b/Moose Development/LDT External Tools/Moose UPDATE ALL MOOSE Demonstration Missions.launch @@ -3,7 +3,6 @@ - - - + + diff --git a/Moose Development/LDT External Tools/Update SELECTED Moose Test Mission.launch b/Moose Development/LDT External Tools/Moose UPDATE SELECTED Missions.launch similarity index 72% rename from Moose Development/LDT External Tools/Update SELECTED Moose Test Mission.launch rename to Moose Development/LDT External Tools/Moose UPDATE SELECTED Missions.launch index 5a375b0f0..6c6acfad3 100644 --- a/Moose Development/LDT External Tools/Update SELECTED Moose Test Mission.launch +++ b/Moose Development/LDT External Tools/Moose UPDATE SELECTED Missions.launch @@ -3,7 +3,7 @@ - + - + diff --git a/Moose Development/LDT External Tools/Update Moose Test Missions.launch b/Moose Development/LDT External Tools/Update Moose Test Missions.launch deleted file mode 100644 index 3fec0cfe5..000000000 --- a/Moose Development/LDT External Tools/Update Moose Test Missions.launch +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/Moose Development/Maths/Aspect.ggb b/Moose Development/Maths/Aspect.ggb new file mode 100644 index 000000000..be76e89b2 Binary files /dev/null and b/Moose Development/Maths/Aspect.ggb differ diff --git a/Moose Development/Maths/Distance.ggb b/Moose Development/Maths/Distance.ggb new file mode 100644 index 000000000..dc32fd5af Binary files /dev/null and b/Moose Development/Maths/Distance.ggb differ diff --git a/Moose Development/Moose/AI/AI_A2A.lua b/Moose Development/Moose/AI/AI_A2A.lua new file mode 100644 index 000000000..fac8b4eaa --- /dev/null +++ b/Moose Development/Moose/AI/AI_A2A.lua @@ -0,0 +1,732 @@ +--- **AI** -- **AI A2A Air Patrolling or Staging.** +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: +-- +-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. +-- +-- ==== +-- +-- @module AI_A2A + +--BASE:TraceClass("AI_A2A") + + +--- @type AI_A2A +-- @extends Core.Fsm#FSM_CONTROLLABLE + +--- # AI_A2A class, extends @{Fsm#FSM_CONTROLLABLE} +-- +-- The AI_A2A class implements the core functions to operate an AI @{Group} A2A tasking. +-- +-- +-- ## AI_A2A constructor +-- +-- * @{#AI_A2A.New}(): Creates a new AI_A2A object. +-- +-- ## 2. AI_A2A is a FSM +-- +-- ![Process](..\Presentations\AI_PATROL\Dia2.JPG) +-- +-- ### 2.1. AI_A2A States +-- +-- * **None** ( Group ): The process is not started yet. +-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. +-- * **Returning** ( Group ): The AI is returning to Base. +-- * **Stopped** ( Group ): The process is stopped. +-- * **Crashed** ( Group ): The AI has crashed or is dead. +-- +-- ### 2.2. AI_A2A Events +-- +-- * **Start** ( Group ): Start the process. +-- * **Stop** ( Group ): Stop the process. +-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone. +-- * **RTB** ( Group ): Route the AI to the home base. +-- * **Detect** ( Group ): The AI is detecting targets. +-- * **Detected** ( Group ): The AI has detected new targets. +-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. +-- +-- ## 3. Set or Get the AI controllable +-- +-- * @{#AI_A2A.SetControllable}(): Set the AIControllable. +-- * @{#AI_A2A.GetControllable}(): Get the AIControllable. +-- +-- @field #AI_A2A +AI_A2A = { + ClassName = "AI_A2A", +} + +--- Creates a new AI_A2A object +-- @param #AI_A2A self +-- @param Wrapper.Group#GROUP AIGroup The GROUP object to receive the A2A Process. +-- @return #AI_A2A +function AI_A2A:New( AIGroup ) + + -- Inherits from BASE + local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_A2A + + self:SetControllable( AIGroup ) + + self:SetFuelThreshold( .2, 60 ) + self:SetDamageThreshold( 0.4 ) + self:SetDisengageRadius( 70000 ) + + self:SetStartState( "Stopped" ) + + self:AddTransition( "*", "Start", "Started" ) + + --- Start Handler OnBefore for AI_A2A + -- @function [parent=#AI_A2A] OnBeforeStart + -- @param #AI_A2A self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Start Handler OnAfter for AI_A2A + -- @function [parent=#AI_A2A] OnAfterStart + -- @param #AI_A2A self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Start Trigger for AI_A2A + -- @function [parent=#AI_A2A] Start + -- @param #AI_A2A self + + --- Start Asynchronous Trigger for AI_A2A + -- @function [parent=#AI_A2A] __Start + -- @param #AI_A2A self + -- @param #number Delay + + self:AddTransition( "*", "Stop", "Stopped" ) + +--- OnLeave Transition Handler for State Stopped. +-- @function [parent=#AI_A2A] OnLeaveStopped +-- @param #AI_A2A self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @return #boolean Return false to cancel Transition. + +--- OnEnter Transition Handler for State Stopped. +-- @function [parent=#AI_A2A] OnEnterStopped +-- @param #AI_A2A self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. + +--- OnBefore Transition Handler for Event Stop. +-- @function [parent=#AI_A2A] OnBeforeStop +-- @param #AI_A2A self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @return #boolean Return false to cancel Transition. + +--- OnAfter Transition Handler for Event Stop. +-- @function [parent=#AI_A2A] OnAfterStop +-- @param #AI_A2A self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. + +--- Synchronous Event Trigger for Event Stop. +-- @function [parent=#AI_A2A] Stop +-- @param #AI_A2A self + +--- Asynchronous Event Trigger for Event Stop. +-- @function [parent=#AI_A2A] __Stop +-- @param #AI_A2A self +-- @param #number Delay The delay in seconds. + + self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A. + +--- OnBefore Transition Handler for Event Status. +-- @function [parent=#AI_A2A] OnBeforeStatus +-- @param #AI_A2A self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @return #boolean Return false to cancel Transition. + +--- OnAfter Transition Handler for Event Status. +-- @function [parent=#AI_A2A] OnAfterStatus +-- @param #AI_A2A self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. + +--- Synchronous Event Trigger for Event Status. +-- @function [parent=#AI_A2A] Status +-- @param #AI_A2A self + +--- Asynchronous Event Trigger for Event Status. +-- @function [parent=#AI_A2A] __Status +-- @param #AI_A2A self +-- @param #number Delay The delay in seconds. + + self:AddTransition( "*", "RTB", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A. + +--- OnBefore Transition Handler for Event RTB. +-- @function [parent=#AI_A2A] OnBeforeRTB +-- @param #AI_A2A self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @return #boolean Return false to cancel Transition. + +--- OnAfter Transition Handler for Event RTB. +-- @function [parent=#AI_A2A] OnAfterRTB +-- @param #AI_A2A self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. + +--- Synchronous Event Trigger for Event RTB. +-- @function [parent=#AI_A2A] RTB +-- @param #AI_A2A self + +--- Asynchronous Event Trigger for Event RTB. +-- @function [parent=#AI_A2A] __RTB +-- @param #AI_A2A self +-- @param #number Delay The delay in seconds. + +--- OnLeave Transition Handler for State Returning. +-- @function [parent=#AI_A2A] OnLeaveReturning +-- @param #AI_A2A self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @return #boolean Return false to cancel Transition. + +--- OnEnter Transition Handler for State Returning. +-- @function [parent=#AI_A2A] OnEnterReturning +-- @param #AI_A2A self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. + + self:AddTransition( "Patrolling", "Refuel", "Refuelling" ) + + --- Refuel Handler OnBefore for AI_A2A + -- @function [parent=#AI_A2A] OnBeforeRefuel + -- @param #AI_A2A self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Refuel Handler OnAfter for AI_A2A + -- @function [parent=#AI_A2A] OnAfterRefuel + -- @param #AI_A2A self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Refuel Trigger for AI_A2A + -- @function [parent=#AI_A2A] Refuel + -- @param #AI_A2A self + + --- Refuel Asynchronous Trigger for AI_A2A + -- @function [parent=#AI_A2A] __Refuel + -- @param #AI_A2A self + -- @param #number Delay + + self:AddTransition( "*", "Return", "Returning" ) + self:AddTransition( "*", "Hold", "Holding" ) + self:AddTransition( "*", "Home", "Home" ) + self:AddTransition( "*", "LostControl", "LostControl" ) + self:AddTransition( "*", "Fuel", "Fuel" ) + self:AddTransition( "*", "Damaged", "Damaged" ) + self:AddTransition( "*", "Eject", "*" ) + self:AddTransition( "*", "Crash", "Crashed" ) + self:AddTransition( "*", "PilotDead", "*" ) + + self.IdleCount = 0 + + return self +end + +function AI_A2A:SetDispatcher( Dispatcher ) + self.Dispatcher = Dispatcher +end + +function AI_A2A:GetDispatcher() + return self.Dispatcher +end + +function AI_A2A:SetTargetDistance( Coordinate ) + + local CurrentCoord = self.Controllable:GetCoordinate() + self.TargetDistance = CurrentCoord:Get2DDistance( Coordinate ) + + self.ClosestTargetDistance = ( not self.ClosestTargetDistance or self.ClosestTargetDistance > self.TargetDistance ) and self.TargetDistance or self.ClosestTargetDistance +end + + +function AI_A2A:ClearTargetDistance() + + self.TargetDistance = nil + self.ClosestTargetDistance = nil +end + + +--- Sets (modifies) the minimum and maximum speed of the patrol. +-- @param #AI_A2A self +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @return #AI_A2A self +function AI_A2A:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed ) + self:F2( { PatrolMinSpeed, PatrolMaxSpeed } ) + + self.PatrolMinSpeed = PatrolMinSpeed + self.PatrolMaxSpeed = PatrolMaxSpeed +end + + +--- Sets the floor and ceiling altitude of the patrol. +-- @param #AI_A2A self +-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @return #AI_A2A self +function AI_A2A:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude ) + self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } ) + + self.PatrolFloorAltitude = PatrolFloorAltitude + self.PatrolCeilingAltitude = PatrolCeilingAltitude +end + + +--- Sets the home airbase. +-- @param #AI_A2A self +-- @param Wrapper.Airbase#AIRBASE HomeAirbase +-- @return #AI_A2A self +function AI_A2A:SetHomeAirbase( HomeAirbase ) + self:F2( { HomeAirbase } ) + + self.HomeAirbase = HomeAirbase +end + +--- Sets to refuel at the given tanker. +-- @param #AI_A2A self +-- @param Wrapper.Group#GROUP TankerName The group name of the tanker as defined within the Mission Editor or spawned. +-- @return #AI_A2A self +function AI_A2A:SetTanker( TankerName ) + self:F2( { TankerName } ) + + self.TankerName = TankerName +end + + +--- Sets the disengage range, that when engaging a target beyond the specified range, the engagement will be cancelled and the plane will RTB. +-- @param #AI_A2A self +-- @param #number DisengageRadius The disengage range. +-- @return #AI_A2A self +function AI_A2A:SetDisengageRadius( DisengageRadius ) + self:F2( { DisengageRadius } ) + + self.DisengageRadius = DisengageRadius +end + +--- Set the status checking off. +-- @param #AI_A2A self +-- @return #AI_A2A self +function AI_A2A:SetStatusOff() + self:F2() + + self.CheckStatus = false +end + + +--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. +-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. +-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_A2A. +-- Once the time is finished, the old AI will return to the base. +-- @param #AI_A2A self +-- @param #number PatrolFuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel. +-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base. +-- @return #AI_A2A self +function AI_A2A:SetFuelThreshold( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime ) + + self.PatrolManageFuel = true + self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage + self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime + + self.Controllable:OptionRTBBingoFuel( false ) + + return self +end + +--- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base. +-- However, damage cannot be foreseen early on. +-- Therefore, when the damage treshold is reached, +-- the AI will return immediately to the home base (RTB). +-- Note that for groups, the average damage of the complete group will be calculated. +-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25. +-- @param #AI_A2A self +-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged. +-- @return #AI_A2A self +function AI_A2A:SetDamageThreshold( PatrolDamageThreshold ) + + self.PatrolManageDamage = true + self.PatrolDamageThreshold = PatrolDamageThreshold + + return self +end + +--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. +-- @param #AI_A2A self +-- @return #AI_A2A self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A:onafterStart( Controllable, From, Event, To ) + self:F2() + + self:__Status( 10 ) -- Check status status every 30 seconds. + + self:HandleEvent( EVENTS.PilotDead, self.OnPilotDead ) + self:HandleEvent( EVENTS.Crash, self.OnCrash ) + self:HandleEvent( EVENTS.Ejection, self.OnEjection ) + + Controllable:OptionROEHoldFire() + Controllable:OptionROTVertical() +end + + + +--- @param #AI_A2A self +function AI_A2A:onbeforeStatus() + + return self.CheckStatus +end + +--- @param #AI_A2A self +function AI_A2A:onafterStatus() + + self:F( " Checking Status" ) + + if self.Controllable and self.Controllable:IsAlive() then + + local RTB = false + + local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() ) + + if not self:Is( "Holding" ) and not self:Is( "Returning" ) then + local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() ) + self:F({DistanceFromHomeBase=DistanceFromHomeBase}) + + if DistanceFromHomeBase > self.DisengageRadius then + self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" ) + self:Hold( 300 ) + RTB = false + end + end + + if self:Is( "Fuel" ) or self:Is( "Damaged" ) or self:Is( "LostControl" ) then + if DistanceFromHomeBase < 5000 then + self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" ) + self:Home( "Destroy" ) + end + end + + + if not self:Is( "Fuel" ) and not self:Is( "Home" ) then + local Fuel = self.Controllable:GetFuel() + self:F({Fuel=Fuel}) + if Fuel < self.PatrolFuelThresholdPercentage then + if self.TankerName then + self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" ) + self:Refuel() + else + self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" ) + local OldAIControllable = self.Controllable + local AIControllableTemplate = self.Controllable:GetTemplate() + + local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed ) + local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) ) + OldAIControllable:SetTask( TimedOrbitTask, 10 ) + + self:Fuel() + RTB = true + end + else + end + end + + -- TODO: Check GROUP damage function. + local Damage = self.Controllable:GetLife() + local InitialLife = self.Controllable:GetLife0() + self:F( { Damage = Damage, InitialLife = InitialLife, DamageThreshold = self.PatrolDamageThreshold } ) + if ( Damage / InitialLife ) < self.PatrolDamageThreshold then + self:E( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" ) + self:Damaged() + RTB = true + self:SetStatusOff() + end + + -- Check if planes went RTB and are out of control. + if self.Controllable:HasTask() == false then + if not self:Is( "Started" ) and + not self:Is( "Stopped" ) and + not self:Is( "Home" ) then + if self.IdleCount >= 2 then + if Damage ~= InitialLife then + self:Damaged() + else + self:E( self.Controllable:GetName() .. " control lost! " ) + self:LostControl() + end + else + self.IdleCount = self.IdleCount + 1 + end + end + else + self.IdleCount = 0 + end + + if RTB == true then + self:__RTB( 0.5 ) + end + + self:__Status( 10 ) + end +end + + +--- @param Wrapper.Group#GROUP AIGroup +function AI_A2A.RTBRoute( AIGroup, Fsm ) + + AIGroup:F( { "AI_A2A.RTBRoute:", AIGroup:GetName() } ) + + if AIGroup:IsAlive() then + Fsm:__RTB( 0.5 ) + end + +end + +--- @param Wrapper.Group#GROUP AIGroup +function AI_A2A.RTBHold( AIGroup, Fsm ) + + AIGroup:F( { "AI_A2A.RTBHold:", AIGroup:GetName() } ) + if AIGroup:IsAlive() then + Fsm:__RTB( 0.5 ) + Fsm:Return() + local Task = AIGroup:TaskOrbitCircle( 4000, 400 ) + AIGroup:SetTask( Task ) + end + +end + + +--- @param #AI_A2A self +-- @param Wrapper.Group#GROUP AIGroup +function AI_A2A:onafterRTB( AIGroup, From, Event, To ) + self:F( { AIGroup, From, Event, To } ) + + + if AIGroup and AIGroup:IsAlive() then + + self:E( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" ) + + self:ClearTargetDistance() + AIGroup:ClearTasks() + + local EngageRoute = {} + + --- Calculate the target route point. + + local CurrentCoord = AIGroup:GetCoordinate() + local ToTargetCoord = self.HomeAirbase:GetCoordinate() + local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) + local ToAirbaseAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) ) + + local Distance = CurrentCoord:Get2DDistance( ToTargetCoord ) + + local ToAirbaseCoord = CurrentCoord:Translate( 5000, ToAirbaseAngle ) + if Distance < 5000 then + self:E( "RTB and near the airbase!" ) + self:Home() + return + end + --- Create a route point of type air. + local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToTargetSpeed, + true + ) + + self:F( { Angle = ToAirbaseAngle, ToTargetSpeed = ToTargetSpeed } ) + self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } ) + + EngageRoute[#EngageRoute+1] = ToRTBRoutePoint + EngageRoute[#EngageRoute+1] = ToRTBRoutePoint + + AIGroup:OptionROEHoldFire() + AIGroup:OptionROTEvadeFire() + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + AIGroup:WayPointInitialize( EngageRoute ) + + local Tasks = {} + Tasks[#Tasks+1] = AIGroup:TaskFunction( "AI_A2A.RTBRoute", self ) + EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( Tasks ) + + --- NOW ROUTE THE GROUP! + AIGroup:Route( EngageRoute, 0.5 ) + + end + +end + +--- @param #AI_A2A self +-- @param Wrapper.Group#GROUP AIGroup +function AI_A2A:onafterHome( AIGroup, From, Event, To ) + self:F( { AIGroup, From, Event, To } ) + + self:E( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" ) + + if AIGroup and AIGroup:IsAlive() then + end + +end + + + +--- @param #AI_A2A self +-- @param Wrapper.Group#GROUP AIGroup +function AI_A2A:onafterHold( AIGroup, From, Event, To, HoldTime ) + self:F( { AIGroup, From, Event, To } ) + + self:E( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" ) + + if AIGroup and AIGroup:IsAlive() then + local OrbitTask = AIGroup:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed ) + local TimedOrbitTask = AIGroup:TaskControlled( OrbitTask, AIGroup:TaskCondition( nil, nil, nil, nil, HoldTime , nil ) ) + + local RTBTask = AIGroup:TaskFunction( "AI_A2A.RTBHold", self ) + + local OrbitHoldTask = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed ) + + --AIGroup:SetState( AIGroup, "AI_A2A", self ) + + AIGroup:SetTask( AIGroup:TaskCombo( { TimedOrbitTask, RTBTask, OrbitHoldTask } ), 1 ) + end + +end + +--- @param Wrapper.Group#GROUP AIGroup +function AI_A2A.Resume( AIGroup, Fsm ) + + AIGroup:F( { "AI_A2A.Resume:", AIGroup:GetName() } ) + if AIGroup:IsAlive() then + Fsm:__RTB( 0.5 ) + end + +end + +--- @param #AI_A2A self +-- @param Wrapper.Group#GROUP AIGroup +function AI_A2A:onafterRefuel( AIGroup, From, Event, To ) + self:F( { AIGroup, From, Event, To } ) + + self:E( "Group " .. self.Controllable:GetName() .. " ... Refuelling! ( " .. self:GetState() .. " )" ) + + if AIGroup and AIGroup:IsAlive() then + local Tanker = GROUP:FindByName( self.TankerName ) + if Tanker:IsAlive() and Tanker:IsAirPlane() then + + local RefuelRoute = {} + + --- Calculate the target route point. + + local CurrentCoord = AIGroup:GetCoordinate() + local ToRefuelCoord = Tanker:GetCoordinate() + local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) + + --- Create a route point of type air. + local ToRefuelRoutePoint = ToRefuelCoord:WaypointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToRefuelSpeed, + true + ) + + self:F( { ToRefuelSpeed = ToRefuelSpeed } ) + + RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint + RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint + + AIGroup:OptionROEHoldFire() + AIGroup:OptionROTEvadeFire() + + local Tasks = {} + Tasks[#Tasks+1] = AIGroup:TaskRefueling() + Tasks[#Tasks+1] = AIGroup:TaskFunction( self:GetClassName() .. ".Resume", self ) + RefuelRoute[#RefuelRoute].task = AIGroup:TaskCombo( Tasks ) + + AIGroup:Route( RefuelRoute, 0.5 ) + else + self:RTB() + end + end + +end + + + +--- @param #AI_A2A self +function AI_A2A:onafterDead() + self:SetStatusOff() +end + + +--- @param #AI_A2A self +-- @param Core.Event#EVENTDATA EventData +function AI_A2A:OnCrash( EventData ) + + if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then + self:E( self.Controllable:GetUnits() ) + if #self.Controllable:GetUnits() == 1 then + self:__Crash( 1, EventData ) + end + end +end + +--- @param #AI_A2A self +-- @param Core.Event#EVENTDATA EventData +function AI_A2A:OnEjection( EventData ) + + if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then + self:__Eject( 1, EventData ) + end +end + +--- @param #AI_A2A self +-- @param Core.Event#EVENTDATA EventData +function AI_A2A:OnPilotDead( EventData ) + + if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then + self:__PilotDead( 1, EventData ) + end +end diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua new file mode 100644 index 000000000..2509721b9 --- /dev/null +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -0,0 +1,497 @@ +--- **AI** -- **Execute Combat Air Patrol (CAP).** +-- +-- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG) +-- +-- === +-- +-- AI CAP classes makes AI Controllables execute a Combat Air Patrol. +-- +-- There are the following types of CAP classes defined: +-- +-- * @{#AI_A2A_CAP}: Perform a CAP in a zone. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. +-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. +-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. +-- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. +-- +-- ==== +-- +-- @module AI_A2A_Cap + +--BASE:TraceClass("AI_A2A_CAP") + +--- @type AI_A2A_CAP +-- @extends AI.AI_A2A_Patrol#AI_A2A_PATROL + + +--- # AI_A2A_CAP class, extends @{AI_CAP#AI_PATROL_ZONE} +-- +-- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group} +-- and automatically engage any airborne enemies that are within a certain range or within a certain zone. +-- +-- ![Process](..\Presentations\AI_CAP\Dia3.JPG) +-- +-- The AI_A2A_CAP is assigned a @{Group} and this must be done before the AI_A2A_CAP process can be started using the **Start** event. +-- +-- ![Process](..\Presentations\AI_CAP\Dia4.JPG) +-- +-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. +-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. +-- +-- ![Process](..\Presentations\AI_CAP\Dia5.JPG) +-- +-- This cycle will continue. +-- +-- ![Process](..\Presentations\AI_CAP\Dia6.JPG) +-- +-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. +-- +-- ![Process](..\Presentations\AI_CAP\Dia9.JPG) +-- +-- When enemies are detected, the AI will automatically engage the enemy. +-- +-- ![Process](..\Presentations\AI_CAP\Dia10.JPG) +-- +-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. +-- +-- ![Process](..\Presentations\AI_CAP\Dia13.JPG) +-- +-- ## 1. AI_A2A_CAP constructor +-- +-- * @{#AI_A2A_CAP.New}(): Creates a new AI_A2A_CAP object. +-- +-- ## 2. AI_A2A_CAP is a FSM +-- +-- ![Process](..\Presentations\AI_CAP\Dia2.JPG) +-- +-- ### 2.1 AI_A2A_CAP States +-- +-- * **None** ( Group ): The process is not started yet. +-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. +-- * **Engaging** ( Group ): The AI is engaging the bogeys. +-- * **Returning** ( Group ): The AI is returning to Base.. +-- +-- ### 2.2 AI_A2A_CAP Events +-- +-- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. +-- * **@{#AI_A2A_CAP.Engage}**: Let the AI engage the bogeys. +-- * **@{#AI_A2A_CAP.Abort}**: Aborts the engagement and return patrolling in the patrol zone. +-- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. +-- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Unit}. +-- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task. +-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. +-- +-- ## 3. Set the Range of Engagement +-- +-- ![Range](..\Presentations\AI_CAP\Dia11.JPG) +-- +-- An optional range can be set in meters, +-- that will define when the AI will engage with the detected airborne enemy targets. +-- The range can be beyond or smaller than the range of the Patrol Zone. +-- The range is applied at the position of the AI. +-- Use the method @{AI_CAP#AI_A2A_CAP.SetEngageRange}() to define that range. +-- +-- ## 4. Set the Zone of Engagement +-- +-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG) +-- +-- An optional @{Zone} can be set, +-- that will define when the AI will engage with the detected airborne enemy targets. +-- Use the method @{AI_Cap#AI_A2A_CAP.SetEngageZone}() to define that Zone. +-- +-- === +-- +-- @field #AI_A2A_CAP +AI_A2A_CAP = { + ClassName = "AI_A2A_CAP", +} + +--- Creates a new AI_A2A_CAP object +-- @param #AI_A2A_CAP self +-- @param Wrapper.Group#GROUP AIGroup +-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. +-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed EngageMinSpeed The minimum speed of the @{Controllable} in km/h when engaging a target. +-- @param Dcs.DCSTypes#Speed EngageMaxSpeed The maximum speed of the @{Controllable} in km/h when engaging a target. +-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @return #AI_A2A_CAP +function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType ) + + -- Inherits from BASE + local self = BASE:Inherit( self, AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_A2A_CAP + + self.Accomplished = false + self.Engaging = false + + self.EngageMinSpeed = EngageMinSpeed + self.EngageMaxSpeed = EngageMaxSpeed + + self:AddTransition( { "Patrolling", "Engaging", "Returning" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. + + --- OnBefore Transition Handler for Event Engage. + -- @function [parent=#AI_A2A_CAP] OnBeforeEngage + -- @param #AI_A2A_CAP self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Engage. + -- @function [parent=#AI_A2A_CAP] OnAfterEngage + -- @param #AI_A2A_CAP self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Engage. + -- @function [parent=#AI_A2A_CAP] Engage + -- @param #AI_A2A_CAP self + + --- Asynchronous Event Trigger for Event Engage. + -- @function [parent=#AI_A2A_CAP] __Engage + -- @param #AI_A2A_CAP self + -- @param #number Delay The delay in seconds. + +--- OnLeave Transition Handler for State Engaging. +-- @function [parent=#AI_A2A_CAP] OnLeaveEngaging +-- @param #AI_A2A_CAP self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @return #boolean Return false to cancel Transition. + +--- OnEnter Transition Handler for State Engaging. +-- @function [parent=#AI_A2A_CAP] OnEnterEngaging +-- @param #AI_A2A_CAP self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. + + self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. + + --- OnBefore Transition Handler for Event Fired. + -- @function [parent=#AI_A2A_CAP] OnBeforeFired + -- @param #AI_A2A_CAP self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Fired. + -- @function [parent=#AI_A2A_CAP] OnAfterFired + -- @param #AI_A2A_CAP self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Fired. + -- @function [parent=#AI_A2A_CAP] Fired + -- @param #AI_A2A_CAP self + + --- Asynchronous Event Trigger for Event Fired. + -- @function [parent=#AI_A2A_CAP] __Fired + -- @param #AI_A2A_CAP self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. + + --- OnBefore Transition Handler for Event Destroy. + -- @function [parent=#AI_A2A_CAP] OnBeforeDestroy + -- @param #AI_A2A_CAP self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Destroy. + -- @function [parent=#AI_A2A_CAP] OnAfterDestroy + -- @param #AI_A2A_CAP self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Destroy. + -- @function [parent=#AI_A2A_CAP] Destroy + -- @param #AI_A2A_CAP self + + --- Asynchronous Event Trigger for Event Destroy. + -- @function [parent=#AI_A2A_CAP] __Destroy + -- @param #AI_A2A_CAP self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. + + --- OnBefore Transition Handler for Event Abort. + -- @function [parent=#AI_A2A_CAP] OnBeforeAbort + -- @param #AI_A2A_CAP self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Abort. + -- @function [parent=#AI_A2A_CAP] OnAfterAbort + -- @param #AI_A2A_CAP self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Abort. + -- @function [parent=#AI_A2A_CAP] Abort + -- @param #AI_A2A_CAP self + + --- Asynchronous Event Trigger for Event Abort. + -- @function [parent=#AI_A2A_CAP] __Abort + -- @param #AI_A2A_CAP self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP. + + --- OnBefore Transition Handler for Event Accomplish. + -- @function [parent=#AI_A2A_CAP] OnBeforeAccomplish + -- @param #AI_A2A_CAP self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Accomplish. + -- @function [parent=#AI_A2A_CAP] OnAfterAccomplish + -- @param #AI_A2A_CAP self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Accomplish. + -- @function [parent=#AI_A2A_CAP] Accomplish + -- @param #AI_A2A_CAP self + + --- Asynchronous Event Trigger for Event Accomplish. + -- @function [parent=#AI_A2A_CAP] __Accomplish + -- @param #AI_A2A_CAP self + -- @param #number Delay The delay in seconds. + + return self +end + + +--- Set the Engage Zone which defines where the AI will engage bogies. +-- @param #AI_A2A_CAP self +-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP. +-- @return #AI_A2A_CAP self +function AI_A2A_CAP:SetEngageZone( EngageZone ) + self:F2() + + if EngageZone then + self.EngageZone = EngageZone + else + self.EngageZone = nil + end +end + +--- Set the Engage Range when the AI will engage with airborne enemies. +-- @param #AI_A2A_CAP self +-- @param #number EngageRange The Engage Range. +-- @return #AI_A2A_CAP self +function AI_A2A_CAP:SetEngageRange( EngageRange ) + self:F2() + + if EngageRange then + self.EngageRange = EngageRange + else + self.EngageRange = nil + end +end + +--- onafter State Transition for Event Patrol. +-- @param #AI_A2A_CAP self +-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The AI Group managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A_CAP:onafterPatrol( AIGroup, From, Event, To ) + + -- Call the parent Start event handler + self:GetParent(self).onafterPatrol( self, AIGroup, From, Event, To ) + self:HandleEvent( EVENTS.Dead ) + +end + +-- todo: need to fix this global function + +--- @param Wrapper.Group#GROUP AIGroup +function AI_A2A_CAP.AttackRoute( AIGroup, Fsm ) + + AIGroup:F( { "AI_A2A_CAP.AttackRoute:", AIGroup:GetName() } ) + + if AIGroup:IsAlive() then + Fsm:__Engage( 0.5 ) + end +end + +--- @param #AI_A2A_CAP self +-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A_CAP:onbeforeEngage( AIGroup, From, Event, To ) + + if self.Accomplished == true then + return false + end +end + +--- @param #AI_A2A_CAP self +-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The AI Group managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A_CAP:onafterAbort( AIGroup, From, Event, To ) + AIGroup:ClearTasks() + self:__Route( 0.5 ) +end + + +--- @param #AI_A2A_CAP self +-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit ) + + self:F( { AIGroup, From, Event, To, AttackSetUnit} ) + + self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT + + local FirstAttackUnit = self.AttackSetUnit:GetFirst() -- Wrapper.Unit#UNIT + + if FirstAttackUnit and FirstAttackUnit:IsAlive() then -- If there is no attacker anymore, stop the engagement. + + if AIGroup:IsAlive() then + + local EngageRoute = {} + + --- Calculate the target route point. + local CurrentCoord = AIGroup:GetCoordinate() + local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate() + local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed ) + local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) ) + + --- Create a route point of type air. + local ToPatrolRoutePoint = CurrentCoord:Translate( 5000, ToInterceptAngle ):WaypointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToTargetSpeed, + true + ) + + self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } ) + self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } ) + + EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint + EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint + + local AttackTasks = {} + + for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do + local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT + self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } ) + if AttackUnit:IsAlive() and AttackUnit:IsAir() then + AttackTasks[#AttackTasks+1] = AIGroup:TaskAttackUnit( AttackUnit ) + end + end + + if #AttackTasks == 0 then + self:E("No targets found -> Going back to Patrolling") + self:__Abort( 0.5 ) + else + AIGroup:OptionROEOpenFire() + AIGroup:OptionROTPassiveDefense() + + AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( "AI_A2A_CAP.AttackRoute", self ) + EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( AttackTasks ) + end + + AIGroup:Route( EngageRoute, 0.5 ) + end + else + self:E("No targets found -> Going back to Patrolling") + self:__Abort( 0.5 ) + end +end + +--- @param #AI_A2A_CAP self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A_CAP:onafterAccomplish( Controllable, From, Event, To ) + self.Accomplished = true + self:SetDetectionOff() +end + +--- @param #AI_A2A_CAP self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @param Core.Event#EVENTDATA EventData +function AI_A2A_CAP:onafterDestroy( Controllable, From, Event, To, EventData ) + + if EventData.IniUnit then + self.AttackUnits[EventData.IniUnit] = nil + end +end + +--- @param #AI_A2A_CAP self +-- @param Core.Event#EVENTDATA EventData +function AI_A2A_CAP:OnEventDead( EventData ) + self:F( { "EventDead", EventData } ) + + if EventData.IniDCSUnit then + if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then + self:__Destroy( 1, EventData ) + end + end +end + +--- @param Wrapper.Group#GROUP AIGroup +function AI_A2A_CAP.Resume( AIGroup ) + + AIGroup:F( { "AI_A2A_CAP.Resume:", AIGroup:GetName() } ) + if AIGroup:IsAlive() then + local _AI_A2A = AIGroup:GetState( AIGroup, "AI_A2A" ) -- #AI_A2A + _AI_A2A:__Reset( 1 ) + _AI_A2A:__Route( 5 ) + end + +end diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua new file mode 100644 index 000000000..f479a2170 --- /dev/null +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -0,0 +1,3688 @@ +--- **AI** - The AI_A2A_DISPATCHER creates an automatic A2A defense system based on an EWR network targets and coordinating CAP and GCI. +-- +-- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia1.JPG) +-- +-- ==== +-- +-- # QUICK START GUIDE +-- +-- There are basically two classes available to model an A2A defense system. +-- +-- AI\_A2A\_DISPATCHER is the main A2A defense class that models the A2A defense system. +-- AI\_A2A\_GCICAP derives or inherits from AI\_A2A\_DISPATCHER and is a more **noob** user friendly class, but is less flexible. +-- +-- Before you start using the AI\_A2A\_DISPATCHER or AI\_A2A\_GCICAP ask youself the following questions. +-- +-- ## 0. Do I need AI\_A2A\_DISPATCHER or do I need AI\_A2A\_GCICAP? +-- +-- AI\_A2A\_GCICAP, automates a lot of the below questions using the mission editor and requires minimal lua scripting. +-- But the AI\_A2A\_GCICAP provides less flexibility and a lot of options are defaulted. +-- With AI\_A2A\_DISPATCHER you can setup a much more **fine grained** A2A defense mechanism, but some more (easy) lua scripting is required. +-- +-- ## 1. Which Coalition am I modeling an A2A defense system for? blue or red? +-- +-- One AI\_A2A\_DISPATCHER object can create a defense system for **one coalition**, which is blue or red. +-- If you want to create a **mutual defense system**, for both blue and red, then you need to create **two** AI\_A2A\_DISPATCHER **objects**, +-- each governing their defense system. +-- +-- +-- ## 2. Which type of EWR will I setup? Grouping based per AREA, per TYPE or per UNIT? (Later others will follow). +-- +-- The MOOSE framework leverages the @{Detection} classes to perform the EWR detection. +-- Several types of @{Detection} classes exist, and the most common characteristics of these classes is that they: +-- +-- * Perform detections from multiple FACs as one co-operating entity. +-- * Communicate with a Head Quarters, which consolidates each detection. +-- * Groups detections based on a method (per area, per type or per unit). +-- * Communicates detections. +-- +-- ## 3. Which EWR units will be used as part of the detection system? Only Ground or also Airborne? +-- +-- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. +-- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). +-- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. +-- The position of these units is very important as they need to provide enough coverage +-- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. +-- +-- ## 4. Is a border required? +-- +-- Is this a cold car or a hot war situation? In case of a cold war situation, a border can be set that will only trigger defenses +-- if the border is crossed by enemy units. +-- +-- ## 5. What maximum range needs to be checked to allow defenses to engage any attacker? +-- +-- A good functioning defense will have a "maximum range" evaluated to the enemy when CAP will be engaged or GCI will be spawned. +-- +-- ## 6. Which Airbases, Carrier Ships, Farps will take part in the defense system for the Coalition? +-- +-- Carefully plan which airbases will take part in the coalition. Color each airbase in the color of the coalition. +-- +-- ## 7. Which Squadrons will I create and which name will I give each Squadron? +-- +-- The defense system works with Squadrons. Each Squadron must be given a unique name, that forms the **key** to the defense system. +-- Several options and activities can be set per Squadron. +-- +-- ## 8. Where will the Squadrons be located? On Airbases? On Carrier Ships? On Farps? +-- +-- Squadrons are placed as the "home base" on an airfield, carrier or farp. +-- Carefully plan where each Squadron will be located as part of the defense system. +-- +-- ## 9. Which plane models will I assign for each Squadron? Do I need one plane model or more plane models per squadron? +-- +-- Per Squadron, one or multiple plane models can be allocated as **Templates**. +-- These are late activated groups with one airplane or helicopter that start with a specific name, called the **template prefix**. +-- The A2A defense system will select from the given templates a random template to spawn a new plane (group). +-- +-- ## 10. Which payloads, skills and skins will these plane models have? +-- +-- Per Squadron, even if you have one plane model, you can still allocate multiple templates of one plane model, +-- each having different payloads, skills and skins. +-- The A2A defense system will select from the given templates a random template to spawn a new plane (group). +-- +-- ## 11. For each Squadron, which will perform CAP? +-- +-- Per Squadron, evaluate which Squadrons will perform CAP. +-- Not all Squadrons need to perform CAP. +-- +-- ## 12. For each Squadron doing CAP, in which ZONE(s) will the CAP be performed? +-- +-- Per CAP, evaluate **where** the CAP will be performed, in other words, define the **zone**. +-- Near the border or a bit further away? +-- +-- ## 13. For each Squadron doing CAP, which zone types will I create? +-- +-- Per CAP zone, evaluate whether you want: +-- +-- * simple trigger zones +-- * polygon zones +-- * moving zones +-- +-- Depending on the type of zone selected, a different @{Zone} object needs to be created from a ZONE_ class. +-- +-- ## 14. For each Squadron doing CAP, what are the time intervals and CAP amounts to be performed? +-- +-- For each CAP: +-- +-- * **How many** CAP you want to have airborne at the same time? +-- * **How frequent** you want the defense mechanism to check whether to start a new CAP? +-- +-- ## 15. For each Squadron, which will perform GCI? +-- +-- For each Squadron, evaluate which Squadrons will perform GCI? +-- Not all Squadrons need to perform GCI. +-- +-- ## 16. For each Squadron, which takeoff method will I use? +-- +-- For each Squadron, evaluate which takeoff method will be used: +-- +-- * Straight from the air +-- * From the runway +-- * From a parking spot with running engines +-- * From a parking spot with cold engines +-- +-- **The default takeoff method is staight in the air.** +-- +-- ## 17. For each Squadron, which landing method will I use? +-- +-- For each Squadron, evaluate which landing method will be used: +-- +-- * Despawn near the airbase when returning +-- * Despawn after landing on the runway +-- * Despawn after engine shutdown after landing +-- +-- **The default landing method is despawn when near the airbase when returning.** +-- +-- ## 18. For each Squadron, which overhead will I use? +-- +-- For each Squadron, depending on the airplane type (modern, old) and payload, which overhead is required to provide any defense? +-- In other words, if **X** attacker airplanes are detected, how many **Y** defense airplanes need to be spawned per squadron? +-- The **Y** is dependent on the type of airplane (era), payload, fuel levels, skills etc. +-- The overhead is a **factor** that will calculate dynamically how many **Y** defenses will be required based on **X** attackers detected. +-- +-- **The default overhead is 1. A value greater than 1, like 1.5 will increase the overhead with 50%, a value smaller than 1, like 0.5 will decrease the overhead with 50%.** +-- +-- ## 19. For each Squadron, which grouping will I use? +-- +-- When multiple targets are detected, how will defense airplanes be grouped when multiple defense airplanes are spawned for multiple attackers? +-- Per one, two, three, four? +-- +-- **The default grouping is 1. That means, that each spawned defender will act individually.** +-- +-- === +-- +-- ### Authors: **Sven Van de Velde (FlightControl)** rework of GCICAP + introduction of new concepts (squadrons). +-- ### Authors: **Stonehouse**, **SNAFU** in terms of the advice, documentation, and the original GCICAP script. +-- +-- @module AI_A2A_Dispatcher + + + +do -- AI_A2A_DISPATCHER + + --- AI_A2A_DISPATCHER class. + -- @type AI_A2A_DISPATCHER + -- @extends Tasking.DetectionManager#DETECTION_MANAGER + + --- # AI\_A2A\_DISPATCHER class, extends @{Tasking#DETECTION_MANAGER} + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia1.JPG) + -- + -- The @{#AI_A2A_DISPATCHER} class is designed to create an automatic air defence system for a coalition. + -- + -- ==== + -- + -- # Demo Missions + -- + -- ### [AI\_A2A\_DISPATCHER Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching) + -- + -- ==== + -- + -- # YouTube Channel + -- + -- ### [DCS WORLD - MOOSE - A2A GCICAP - Build an automatic A2A Defense System](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0S4KMNUUJpaUs6zZHjLKNx) + -- + -- === + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia3.JPG) + -- + -- It includes automatic spawning of Combat Air Patrol aircraft (CAP) and Ground Controlled Intercept aircraft (GCI) in response to enemy air movements that are detected by a ground based radar network. + -- CAP flights will take off and proceed to designated CAP zones where they will remain on station until the ground radars direct them to intercept detected enemy aircraft or they run short of fuel and must return to base (RTB). When a CAP flight leaves their zone to perform an interception or return to base a new CAP flight will spawn to take their place. + -- If all CAP flights are engaged or RTB then additional GCI interceptors will scramble to intercept unengaged enemy aircraft under ground radar control. + -- With a little time and with a little work it provides the mission designer with a convincing and completely automatic air defence system. + -- In short it is a plug in very flexible and configurable air defence module for DCS World. + -- + -- Note that in order to create a two way A2A defense system, two AI\_A2A\_DISPATCHER defense system may need to be created, for each coalition one. + -- This is a good implementation, because maybe in the future, more coalitions may become available in DCS world. + -- + -- === + -- + -- # USAGE GUIDE + -- + -- ## 1. AI\_A2A\_DISPATCHER constructor: + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_1.JPG) + -- + -- + -- The @{#AI_A2A_DISPATCHER.New}() method creates a new AI\_A2A\_DISPATCHER instance. + -- + -- ### 1.1. Define the **EWR network**: + -- + -- As part of the AI\_A2A\_DISPATCHER :New() constructor, an EWR network must be given as the first parameter. + -- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy. + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia5.JPG) + -- + -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. + -- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). + -- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. + -- The position of these units is very important as they need to provide enough coverage + -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia7.JPG) + -- + -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. + -- For example if they are a long way forward and can detect enemy planes on the ground and taking off + -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. + -- Having the radars further back will mean a slower escalation because fewer targets will be detected and + -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. + -- It all depends on what the desired effect is. + -- + -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional#DETECTION_BASE} object that is given as the input parameter of the AI\_A2A\_DISPATCHER class. + -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, + -- increasing or decreasing the radar coverage of the Early Warning System. + -- + -- See the following example to setup an EWR network containing EWR stations and AWACS. + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_2.JPG) + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_3.JPG) + -- + -- -- Define a SET_GROUP object that builds a collection of groups that define the EWR network. + -- -- Here we build the network with all the groups that have a name starting with DF CCCP AWACS and DF CCCP EWR. + -- DetectionSetGroup = SET_GROUP:New() + -- DetectionSetGroup:FilterPrefixes( { "DF CCCP AWACS", "DF CCCP EWR" } ) + -- DetectionSetGroup:FilterStart() + -- + -- -- Setup the detection and group targets to a 30km range! + -- Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 ) + -- + -- -- Setup the A2A dispatcher, and initialize it. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- The above example creates a SET_GROUP instance, and stores this in the variable (object) **DetectionSetGroup**. + -- **DetectionSetGroup** is then being configured to filter all active groups with a group name starting with **DF CCCP AWACS** or **DF CCCP EWR** to be included in the Set. + -- **DetectionSetGroup** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set. + -- + -- Then a new Detection object is created from the class DETECTION_AREAS. A grouping radius of 30000 is choosen, which is 30km. + -- The **Detection** object is then passed to the @{#AI_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A defense detection mechanism. + -- + -- You could build a **mutual defense system** like this: + -- + -- A2ADispatcher_Red = AI_A2A_DISPATCHER:New( EWR_Red ) + -- A2ADispatcher_Blue = AI_A2A_DISPATCHER:New( EWR_Blue ) + -- + -- ### 2. Define the detected **target grouping radius**: + -- + -- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed. + -- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. + -- Fast planes like in the 80s, need a larger radius than WWII planes. + -- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft. + -- + -- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate + -- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small! + -- + -- ## 3. Set the **Engage Radius**: + -- + -- Define the **Engage Radius** to **engage any target by airborne friendlies**, + -- which are executing **cap** or **returning** from an intercept mission. + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia10.JPG) + -- + -- If there is a target area detected and reported, + -- then any friendlies that are airborne near this target area, + -- will be commanded to (re-)engage that target when available (if no other tasks were commanded). + -- + -- For example, if **50000** or **50km** is given as a value, then any friendly that is airborne within **50km** from the detected target, + -- will be considered to receive the command to engage that target area. + -- + -- You need to evaluate the value of this parameter carefully: + -- + -- * If too small, more intercept missions may be triggered upon detected target areas. + -- * If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. + -- + -- The **default** Engage Radius is defined as **100000** or **100km**. + -- Use the method @{#AI_A2A_DISPATCHER.SetEngageRadius}() to set a specific Engage Radius. + -- **The Engage Radius is defined for ALL squadrons which are operational.** + -- + -- Demonstration Mission: [AID-019 - AI_A2A - Engage Range Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-019%20-%20AI_A2A%20-%20Engage%20Range%20Test) + -- + -- In this example an Engage Radius is set to various values. + -- + -- -- Set 50km as the radius to engage any target by airborne friendlies. + -- A2ADispatcher:SetEngageRadius( 50000 ) + -- + -- -- Set 100km as the radius to engage any target by airborne friendlies. + -- A2ADispatcher:SetEngageRadius() -- 100000 is the default value. + -- + -- + -- ## 4. Set the **Ground Controlled Intercept Radius** or **Gci radius**: + -- + -- When targets are detected that are still really far off, you don't want the AI_A2A_DISPATCHER to launch intercepts just yet. + -- You want it to wait until a certain Gci range is reached, which is the **distance of the closest airbase to target** + -- being **smaller** than the **Ground Controlled Intercept radius** or **Gci radius**. + -- + -- The **default** Gci radius is defined as **200000** or **200km**. Override the default Gci radius when the era of the warfare is early, or, + -- when you don't want to let the AI_A2A_DISPATCHER react immediately when a certain border or area is not being crossed. + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetGciRadius}() to set a specific controlled ground intercept radius. + -- **The Ground Controlled Intercept radius is defined for ALL squadrons which are operational.** + -- + -- Demonstration Mission: [AID-013 - AI_A2A - Intercept Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-013%20-%20AI_A2A%20-%20Intercept%20Test) + -- + -- In these examples, the Gci Radius is set to various values: + -- + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- -- Set 100km as the radius to ground control intercept detected targets from the nearest airbase. + -- A2ADispatcher:SetGciRadius( 100000 ) + -- + -- -- Set 200km as the radius to ground control intercept. + -- A2ADispatcher:SetGciRadius() -- 200000 is the default value. + -- + -- ## 5. Set the **borders**: + -- + -- According to the tactical and strategic design of the mission broadly decide the shape and extent of red and blue territories. + -- They should be laid out such that a border area is created between the two coalitions. + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia4.JPG) + -- + -- **Define a border area to simulate a cold war scenario.** + -- Use the method @{#AI_A2A_DISPATCHER.SetBorderZone}() to create a border zone for the dispatcher. + -- + -- A **cold war** is one where CAP aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border. + -- A **hot war** is one where CAP aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send CAP and GCI aircraft to attack it. + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia9.JPG) + -- + -- If it’s a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Zone#ZONE_BASE}. + -- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than + -- it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. + -- In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. + -- + -- Demonstration Mission: [AID-009 - AI_A2A - Border Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-009 - AI_A2A - Border Test) + -- + -- In this example a border is set for the CCCP A2A dispatcher: + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_4.JPG) + -- + -- -- Setup the A2A dispatcher, and initialize it. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- -- Setup the border. + -- -- Initialize the dispatcher, setting up a border zone. This is a polygon, + -- -- which takes the waypoints of a late activated group with the name CCCP Border as the boundaries of the border area. + -- -- Any enemy crossing this border will be engaged. + -- + -- CCCPBorderZone = ZONE_POLYGON:New( "CCCP Border", GROUP:FindByName( "CCCP Border" ) ) + -- A2ADispatcher:SetBorderZone( CCCPBorderZone ) + -- + -- ## 6. Squadrons: + -- + -- The AI\_A2A\_DISPATCHER works with **Squadrons**, that need to be defined using the different methods available. + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetSquadron}() to **setup a new squadron** active at an airfield, + -- while defining which plane types are being used by the squadron and how many resources are available. + -- + -- Squadrons: + -- + -- * Have name (string) that is the identifier or key of the squadron. + -- * Have specific plane types. + -- * Are located at one airbase. + -- * Optionally have a limited set of resources. The default is that squadrons have **unlimited resources**. + -- + -- The name of the squadron given acts as the **squadron key** in the AI\_A2A\_DISPATCHER:Squadron...() methods. + -- + -- Additionally, squadrons have specific configuration options to: + -- + -- * Control how new aircraft are taking off from the airfield (in the air, cold, hot, at the runway). + -- * Control how returning aircraft are landing at the airfield (in the air near the airbase, after landing, after engine shutdown). + -- * Control the **grouping** of new aircraft spawned at the airfield. If there is more than one aircraft to be spawned, these may be grouped. + -- * Control the **overhead** or defensive strength of the squadron. Depending on the types of planes and amount of resources, the mission designer can choose to increase or reduce the amount of planes spawned. + -- + -- For performance and bug workaround reasons within DCS, squadrons have different methods to spawn new aircraft or land returning or damaged aircraft. + -- + -- This example defines a couple of squadrons. Note the templates defined within the Mission Editor. + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_5.JPG) + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_6.JPG) + -- + -- -- Setup the squadrons. + -- A2ADispatcher:SetSquadron( "Mineralnye", AIRBASE.Caucasus.Mineralnye_Vody, { "SQ CCCP SU-27" }, 20 ) + -- A2ADispatcher:SetSquadron( "Maykop", AIRBASE.Caucasus.Maykop_Khanskaya, { "SQ CCCP MIG-31" }, 20 ) + -- A2ADispatcher:SetSquadron( "Mozdok", AIRBASE.Caucasus.Mozdok, { "SQ CCCP MIG-31" }, 20 ) + -- A2ADispatcher:SetSquadron( "Sochi", AIRBASE.Caucasus.Sochi_Adler, { "SQ CCCP SU-27" }, 20 ) + -- A2ADispatcher:SetSquadron( "Novo", AIRBASE.Caucasus.Novorossiysk, { "SQ CCCP SU-27" }, 20 ) + -- + -- ### 6.1. Set squadron take-off methods + -- + -- Use the various SetSquadronTakeoff... methods to control how squadrons are taking-off from the airfield: + -- + -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoff}() is the generic configuration method to control takeoff from the air, hot, cold or from the runway. See the method for further details. + -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffInAir}() will spawn new aircraft from the squadron directly in the air. + -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromParkingCold}() will spawn new aircraft in without running engines at a parking spot at the airfield. + -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromParkingHot}() will spawn new aircraft in with running engines at a parking spot at the airfield. + -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromRunway}() will spawn new aircraft at the runway at the airfield. + -- + -- **The default landing method is to spawn new aircraft directly in the air.** + -- + -- Use these methods to fine-tune for specific airfields that are known to create bottlenecks, or have reduced airbase efficiency. + -- The more and the longer aircraft need to taxi at an airfield, the more risk there is that: + -- + -- * aircraft will stop waiting for each other or for a landing aircraft before takeoff. + -- * aircraft may get into a "dead-lock" situation, where two aircraft are blocking each other. + -- * aircraft may collide at the airbase. + -- * aircraft may be awaiting the landing of a plane currently in the air, but never lands ... + -- + -- Currently within the DCS engine, the airfield traffic coordination is erroneous and contains a lot of bugs. + -- If you experience while testing problems with aircraft take-off or landing, please use one of the above methods as a solution to workaround these issues! + -- + -- This example sets the default takeoff method to be from the runway. + -- And for a couple of squadrons overrides this default method. + -- + -- -- Setup the Takeoff methods + -- + -- -- The default takeoff + -- A2ADispatcher:SetDefaultTakeOffFromRunway() + -- + -- -- The individual takeoff per squadron + -- A2ADispatcher:SetSquadronTakeoff( "Mineralnye", AI_A2A_DISPATCHER.Takeoff.Air ) + -- A2ADispatcher:SetSquadronTakeoffInAir( "Sochi" ) + -- A2ADispatcher:SetSquadronTakeoffFromRunway( "Mozdok" ) + -- A2ADispatcher:SetSquadronTakeoffFromParkingCold( "Maykop" ) + -- A2ADispatcher:SetSquadronTakeoffFromParkingHot( "Novo" ) + -- + -- + -- ### 6.1. Set Squadron takeoff altitude when spawning new aircraft in the air. + -- + -- In the case of the @{#AI_A2A_DISPATCHER.SetSquadronTakeoffInAir}() there is also an other parameter that can be applied. + -- That is modifying or setting the **altitude** from where planes spawn in the air. + -- Use the method @{#AI_A2A_DISPATCHER.SetSquadronTakeoffInAirAltitude}() to set the altitude for a specific squadron. + -- The default takeoff altitude can be modified or set using the method @{#AI_A2A_DISPATCHER.SetSquadronTakeoffInAirAltitude}(). + -- As part of the method @{#AI_A2A_DISPATCHER.SetSquadronTakeoffInAir}() a parameter can be specified to set the takeoff altitude. + -- If this parameter is not specified, then the default altitude will be used for the squadron. + -- + -- ### 6.2. Set squadron landing methods + -- + -- In analogy with takeoff, the landing methods are to control how squadrons land at the airfield: + -- + -- * @{#AI_A2A_DISPATCHER.SetSquadronLanding}() is the generic configuration method to control landing, namely despawn the aircraft near the airfield in the air, right after landing, or at engine shutdown. + -- * @{#AI_A2A_DISPATCHER.SetSquadronLandingNearAirbase}() will despawn the returning aircraft in the air when near the airfield. + -- * @{#AI_A2A_DISPATCHER.SetSquadronLandingAtRunway}() will despawn the returning aircraft directly after landing at the runway. + -- * @{#AI_A2A_DISPATCHER.SetSquadronLandingAtEngineShutdown}() will despawn the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines. + -- + -- You can use these methods to minimize the airbase coodination overhead and to increase the airbase efficiency. + -- When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the + -- A2A defense system, as no new CAP or GCI planes can takeoff. + -- Note that the method @{#AI_A2A_DISPATCHER.SetSquadronLandingNearAirbase}() will only work for returning aircraft, not for damaged or out of fuel aircraft. + -- Damaged or out-of-fuel aircraft are returning to the nearest friendly airbase and will land, and are out of control from ground control. + -- + -- This example defines the default landing method to be at the runway. + -- And for a couple of squadrons overrides this default method. + -- + -- -- Setup the Landing methods + -- + -- -- The default landing method + -- A2ADispatcher:SetDefaultLandingAtRunway() + -- + -- -- The individual landing per squadron + -- A2ADispatcher:SetSquadronLandingAtRunway( "Mineralnye" ) + -- A2ADispatcher:SetSquadronLandingNearAirbase( "Sochi" ) + -- A2ADispatcher:SetSquadronLandingAtEngineShutdown( "Mozdok" ) + -- A2ADispatcher:SetSquadronLandingNearAirbase( "Maykop" ) + -- A2ADispatcher:SetSquadronLanding( "Novo", AI_A2A_DISPATCHER.Landing.AtRunway ) + -- + -- + -- ### 6.3. Set squadron grouping + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetSquadronGrouping}() to set the grouping of CAP or GCI flights that will take-off when spawned. + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia12.JPG) + -- + -- In the case of GCI, the @{#AI_A2A_DISPATCHER.SetSquadronGrouping}() method has additional behaviour. When there aren't enough CAP flights airborne, a GCI will be initiated for the remaining + -- targets to be engaged. Depending on the grouping parameter, the spawned flights for GCI are grouped into this setting. + -- For example with a group setting of 2, if 3 targets are detected and cannot be engaged by CAP or any airborne flight, + -- a GCI needs to be started, the GCI flights will be grouped as follows: Group 1 of 2 flights and Group 2 of one flight! + -- + -- Even more ... If one target has been detected, and the overhead is 1.5, grouping is 1, then two groups of planes will be spawned, with one unit each! + -- + -- The **grouping value is set for a Squadron**, and can be **dynamically adjusted** during mission execution, so to adjust the defense flights grouping when the tactical situation changes. + -- + -- ### 6.4. Overhead and Balance the effectiveness of the air defenses in case of GCI. + -- + -- The effectiveness can be set with the **overhead parameter**. This is a number that is used to calculate the amount of Units that dispatching command will allocate to GCI in surplus of detected amount of units. + -- The **default value** of the overhead parameter is 1.0, which means **equal balance**. + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia11.JPG) + -- + -- However, depending on the (type of) aircraft (strength and payload) in the squadron and the amount of resources available, this parameter can be changed. + -- + -- The @{#AI_A2A_DISPATCHER.SetSquadronOverhead}() method can be used to tweak the defense strength, + -- taking into account the plane types of the squadron. + -- + -- For example, a MIG-31 with full long-distance A2A missiles payload, may still be less effective than a F-15C with short missiles... + -- So in this case, one may want to use the @{#AI_A2A_DISPATCHER.SetOverhead}() method to allocate more defending planes as the amount of detected attacking planes. + -- The overhead must be given as a decimal value with 1 as the neutral value, which means that overhead values: + -- + -- * Higher than 1.0, for example 1.5, will increase the defense unit amounts. For 4 planes detected, 6 planes will be spawned. + -- * Lower than 1, for example 0.75, will decrease the defense unit amounts. For 4 planes detected, only 3 planes will be spawned. + -- + -- The amount of defending units is calculated by multiplying the amount of detected attacking planes as part of the detected group + -- multiplied by the Overhead and rounded up to the smallest integer. + -- + -- For example ... If one target has been detected, and the overhead is 1.5, grouping is 1, then two groups of planes will be spawned, with one unit each! + -- + -- The **overhead value is set for a Squadron**, and can be **dynamically adjusted** during mission execution, so to adjust the defense overhead when the tactical situation changes. + -- + -- ## 6.5. Squadron fuel treshold. + -- + -- When an airplane gets **out of fuel** to a certain %-tage, which is by default **15% (0.15)**, there are two possible actions that can be taken: + -- - The defender will go RTB, and will be replaced with a new defender if possible. + -- - The defender will refuel at a tanker, if a tanker has been specified for the squadron. + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetSquadronFuelThreshold}() to set the **squadron fuel treshold** of spawned airplanes for all squadrons. + -- + -- ## 7. Setup a squadron for CAP + -- + -- ### 7.1. Set the CAP zones + -- + -- CAP zones are patrol areas where Combat Air Patrol (CAP) flights loiter until they either return to base due to low fuel or are assigned an interception task by ground control. + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia6.JPG) + -- + -- * As the CAP flights wander around within the zone waiting to be tasked, these zones need to be large enough that the aircraft are not constantly turning + -- but do not have to be big and numerous enough to completely cover a border. + -- + -- * CAP zones can be of any type, and are derived from the @{Zone#ZONE_BASE} class. Zones can be @{Zone#ZONE}, @{Zone#ZONE_POLYGON}, @{Zone#ZONE_UNIT}, @{Zone#ZONE_GROUP}, etc. + -- This allows to setup **static, moving and/or complex zones** wherein aircraft will perform the CAP. + -- + -- * Typically 20000-50000 metres width is used and they are spaced so that aircraft in the zone waiting for tasks don’t have to far to travel to protect their coalitions important targets. + -- These targets are chosen as part of the mission design and might be an important airfield or town etc. + -- Zone size is also determined somewhat by territory size, plane types + -- (eg WW2 aircraft might mean smaller zones or more zones because they are slower and take longer to intercept enemy aircraft). + -- + -- * In a **cold war** it is important to make sure a CAP zone doesn’t intrude into enemy territory as otherwise CAP flights will likely cross borders + -- and spark a full scale conflict which will escalate rapidly. + -- + -- * CAP flights do not need to be in the CAP zone before they are “on station†and ready for tasking. + -- + -- * Typically if a CAP flight is tasked and therefore leaves their zone empty while they go off and intercept their target another CAP flight will spawn to take their place. + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia7.JPG) + -- + -- The following example illustrates how CAP zones are coded: + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_8.JPG) + -- + -- -- CAP Squadron execution. + -- CAPZoneEast = ZONE_POLYGON:New( "CAP Zone East", GROUP:FindByName( "CAP Zone East" ) ) + -- A2ADispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 ) + -- A2ADispatcher:SetSquadronCapInterval( "Mineralnye", 2, 30, 60, 1 ) + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_7.JPG) + -- + -- CAPZoneWest = ZONE_POLYGON:New( "CAP Zone West", GROUP:FindByName( "CAP Zone West" ) ) + -- A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" ) + -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_9.JPG) + -- + -- CAPZoneMiddle = ZONE:New( "CAP Zone Middle") + -- A2ADispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" ) + -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) + -- + -- Note the different @{Zone} MOOSE classes being used to create zones of different types. Please click the @{Zone} link for more information about the different zone types. + -- Zones can be circles, can be setup in the mission editor using trigger zones, but can also be setup in the mission editor as polygons and in this case GROUP objects are being used! + -- + -- ## 7.2. Set the squadron to execute CAP: + -- + -- The method @{#AI_A2A_DISPATCHER.SetSquadronCap}() defines a CAP execution for a squadron. + -- + -- Setting-up a CAP zone also requires specific parameters: + -- + -- * The minimum and maximum altitude + -- * The minimum speed and maximum patrol speed + -- * The minimum and maximum engage speed + -- * The type of altitude measurement + -- + -- These define how the squadron will perform the CAP while partrolling. Different terrain types requires different types of CAP. + -- + -- The @{#AI_A2A_DISPATCHER.SetSquadronCapInterval}() method specifies **how much** and **when** CAP flights will takeoff. + -- + -- It is recommended not to overload the air defense with CAP flights, as these will decrease the performance of the overall system. + -- + -- For example, the following setup will create a CAP for squadron "Sochi": + -- + -- A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" ) + -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) + -- + -- ## 7.3. Squadron tanker to refuel when executing CAP and defender is out of fuel. + -- + -- Instead of sending CAP to RTB when out of fuel, you can let CAP refuel in mid air using a tanker. + -- This greatly increases the efficiency of your CAP operations. + -- + -- In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected. + -- Then, use the method @{#AI_A2A_DISPATCHER.SetDefaultTanker}() to set the default tanker for the refuelling. + -- You can also specify a specific tanker for refuelling for a squadron by using the method @{#AI_A2A_DISPATCHER.SetSquadronTanker}(). + -- + -- When the tanker specified is alive and in the air, the tanker will be used for refuelling. + -- + -- For example, the following setup will create a CAP for squadron "Gelend" with a refuel task for the squadron: + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_10.JPG) + -- + -- -- Define the CAP + -- A2ADispatcher:SetSquadron( "Gelend", AIRBASE.Caucasus.Gelendzhik, { "SQ CCCP SU-30" }, 20 ) + -- A2ADispatcher:SetSquadronCap( "Gelend", ZONE:New( "PatrolZoneGelend" ), 4000, 8000, 600, 800, 1000, 1300 ) + -- A2ADispatcher:SetSquadronCapInterval( "Gelend", 2, 30, 600, 1 ) + -- A2ADispatcher:SetSquadronGci( "Gelend", 900, 1200 ) + -- + -- -- Setup the Refuelling for squadron "Gelend", at tanker (group) "TankerGelend" when the fuel in the tank of the CAP defenders is less than 80%. + -- A2ADispatcher:SetSquadronFuelThreshold( "Gelend", 0.8 ) + -- A2ADispatcher:SetSquadronTanker( "Gelend", "TankerGelend" ) + -- + -- ## 8. Setup a squadron for GCI: + -- + -- The method @{#AI_A2A_DISPATCHER.SetSquadronGci}() defines a GCI execution for a squadron. + -- + -- Setting-up a GCI readiness also requires specific parameters: + -- + -- * The minimum speed and maximum patrol speed + -- + -- Essentially this controls how many flights of GCI aircraft can be active at any time. + -- Note allowing large numbers of active GCI flights can adversely impact mission performance on low or medium specification hosts/servers. + -- GCI needs to be setup at strategic airbases. Too far will mean that the aircraft need to fly a long way to reach the intruders, + -- too short will mean that the intruders may have alraedy passed the ideal interception point! + -- + -- For example, the following setup will create a GCI for squadron "Sochi": + -- + -- A2ADispatcher:SetSquadronGci( "Mozdok", 900, 1200 ) + -- + -- ## 9. Other configuration options + -- + -- ### 9.1. Set a tactical display panel: + -- + -- Every 30 seconds, a tactical display panel can be shown that illustrates what the status is of the different groups controlled by AI\_A2A\_DISPATCHER. + -- Use the method @{#AI_A2A_DISPATCHER.SetTacticalDisplay}() to switch on the tactical display panel. The default will not show this panel. + -- Note that there may be some performance impact if this panel is shown. + -- + -- ## 10. Defaults settings. + -- + -- This provides a good overview of the different parameters that are setup or hardcoded by default. + -- For some default settings, a method is available that allows you to tweak the defaults. + -- + -- ## 10.1. Default takeoff method. + -- + -- The default **takeoff method** is set to **in the air**, which means that new spawned airplanes will be spawned directly in the air above the airbase by default. + -- + -- **The default takeoff method can be set for ALL squadrons that don't have an individual takeoff method configured.** + -- + -- * @{#AI_A2A_DISPATCHER.SetDefaultTakeoff}() is the generic configuration method to control takeoff by default from the air, hot, cold or from the runway. See the method for further details. + -- * @{#AI_A2A_DISPATCHER.SetDefaultTakeoffInAir}() will spawn by default new aircraft from the squadron directly in the air. + -- * @{#AI_A2A_DISPATCHER.SetDefaultTakeoffFromParkingCold}() will spawn by default new aircraft in without running engines at a parking spot at the airfield. + -- * @{#AI_A2A_DISPATCHER.SetDefaultTakeoffFromParkingHot}() will spawn by default new aircraft in with running engines at a parking spot at the airfield. + -- * @{#AI_A2A_DISPATCHER.SetDefaultTakeoffFromRunway}() will spawn by default new aircraft at the runway at the airfield. + -- + -- ## 10.2. Default landing method. + -- + -- The default **landing method** is set to **near the airbase**, which means that returning airplanes will be despawned directly in the air by default. + -- + -- The default landing method can be set for ALL squadrons that don't have an individual landing method configured. + -- + -- * @{#AI_A2A_DISPATCHER.SetDefaultLanding}() is the generic configuration method to control by default landing, namely despawn the aircraft near the airfield in the air, right after landing, or at engine shutdown. + -- * @{#AI_A2A_DISPATCHER.SetDefaultLandingNearAirbase}() will despawn by default the returning aircraft in the air when near the airfield. + -- * @{#AI_A2A_DISPATCHER.SetDefaultLandingAtRunway}() will despawn by default the returning aircraft directly after landing at the runway. + -- * @{#AI_A2A_DISPATCHER.SetDefaultLandingAtEngineShutdown}() will despawn by default the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines. + -- + -- ## 10.3. Default overhead. + -- + -- The default **overhead** is set to **1**. That essentially means that there isn't any overhead set by default. + -- + -- The default overhead value can be set for ALL squadrons that don't have an individual overhead value configured. + -- + -- Use the @{#AI_A2A_DISPATCHER.SetDefaultOverhead}() method can be used to set the default overhead or defense strength for ALL squadrons. + -- + -- ## 10.4. Default grouping. + -- + -- The default **grouping** is set to **one airplane**. That essentially means that there won't be any grouping applied by default. + -- + -- The default grouping value can be set for ALL squadrons that don't have an individual grouping value configured. + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultGrouping}() to set the **default grouping** of spawned airplanes for all squadrons. + -- + -- ## 10.5. Default RTB fuel treshold. + -- + -- When an airplane gets **out of fuel** to a certain %-tage, which is **15% (0.15)**, it will go RTB, and will be replaced with a new airplane when applicable. + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultFuelThreshold}() to set the **default fuel treshold** of spawned airplanes for all squadrons. + -- + -- ## 10.6. Default RTB damage treshold. + -- + -- When an airplane is **damaged** to a certain %-tage, which is **40% (0.40)**, it will go RTB, and will be replaced with a new airplane when applicable. + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultDamageThreshold}() to set the **default damage treshold** of spawned airplanes for all squadrons. + -- + -- ## 10.7. Default settings for CAP. + -- + -- ### 10.7.1. Default CAP Time Interval. + -- + -- CAP is time driven, and will evaluate in random time intervals if a new CAP needs to be spawned. + -- The **default CAP time interval** is between **180** and **600** seconds. + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultCapTimeInterval}() to set the **default CAP time interval** of spawned airplanes for all squadrons. + -- Note that you can still change the CAP limit and CAP time intervals for each CAP individually using the @{#AI_A2A_DISPATCHER.SetSquadronCapTimeInterval}() method. + -- + -- ### 10.7.2. Default CAP limit. + -- + -- Multiple CAP can be airborne at the same time for one squadron, which is controlled by the **CAP limit**. + -- The **default CAP limit** is 1 CAP per squadron to be airborne at the same time. + -- Note that the default CAP limit is used when a Squadron CAP is defined, and cannot be changed afterwards. + -- So, ensure that you set the default CAP limit **before** you spawn the Squadron CAP. + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultCapTimeInterval}() to set the **default CAP time interval** of spawned airplanes for all squadrons. + -- Note that you can still change the CAP limit and CAP time intervals for each CAP individually using the @{#AI_A2A_DISPATCHER.SetSquadronCapTimeInterval}() method. + -- + -- ## 10.7.3. Default tanker for refuelling when executing CAP. + -- + -- Instead of sending CAP to RTB when out of fuel, you can let CAP refuel in mid air using a tanker. + -- This greatly increases the efficiency of your CAP operations. + -- + -- In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected. + -- Then, use the method @{#AI_A2A_DISPATCHER.SetDefaultTanker}() to set the tanker for the dispatcher. + -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultFuelTreshold}() to set the %-tage left in the defender airplane tanks when a refuel action is needed. + -- + -- When the tanker specified is alive and in the air, the tanker will be used for refuelling. + -- + -- For example, the following setup will set the default refuel tanker to "Tanker": + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_11.JPG) + -- + -- -- Define the CAP + -- A2ADispatcher:SetSquadron( "Sochi", AIRBASE.Caucasus.Sochi_Adler, { "SQ CCCP SU-34" }, 20 ) + -- A2ADispatcher:SetSquadronCap( "Sochi", ZONE:New( "PatrolZone" ), 4000, 8000, 600, 800, 1000, 1300 ) + -- A2ADispatcher:SetSquadronCapInterval("Sochi", 2, 30, 600, 1 ) + -- A2ADispatcher:SetSquadronGci( "Sochi", 900, 1200 ) + -- + -- -- Set the default tanker for refuelling to "Tanker", when the default fuel treshold has reached 90% fuel left. + -- A2ADispatcher:SetDefaultFuelThreshold( 0.9 ) + -- A2ADispatcher:SetDefaultTanker( "Tanker" ) + -- + -- ## 10.8. Default settings for GCI. + -- + -- ## 10.8.1. Optimal intercept point calculation. + -- + -- When intruders are detected, the intrusion path of the attackers can be monitored by the EWR. + -- Although defender planes might be on standby at the airbase, it can still take some time to get the defenses up in the air if there aren't any defenses airborne. + -- This time can easily take 2 to 3 minutes, and even then the defenders still need to fly towards the target, which takes also time. + -- + -- Therefore, an optimal **intercept point** is calculated which takes a couple of parameters: + -- + -- * The average bearing of the intruders for an amount of seconds. + -- * The average speed of the intruders for an amount of seconds. + -- * An assumed time it takes to get planes operational at the airbase. + -- + -- The **intercept point** will determine: + -- + -- * If there are any friendlies close to engage the target. These can be defenders performing CAP or defenders in RTB. + -- * The optimal airbase from where defenders will takeoff for GCI. + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetIntercept}() to modify the assumed intercept delay time to calculate a valid interception. + -- + -- ## 10.8.2. Default Disengage Radius. + -- + -- The radius to **disengage any target** when the **distance** of the defender to the **home base** is larger than the specified meters. + -- The default Disengage Radius is **300km** (300000 meters). Note that the Disengage Radius is applicable to ALL squadrons! + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetDisengageRadius}() to modify the default Disengage Radius to another distance setting. + -- + -- + -- ## 11. Q & A: + -- + -- ### 11.1. Which countries will be selected for each coalition? + -- + -- Which countries are assigned to a coalition influences which units are available to the coalition. + -- For example because the mission calls for a EWR radar on the blue side the Ukraine might be chosen as a blue country + -- so that the 55G6 EWR radar unit is available to blue. + -- Some countries assign different tasking to aircraft, for example Germany assigns the CAP task to F-4E Phantoms but the USA does not. + -- Therefore if F4s are wanted as a coalition’s CAP or GCI aircraft Germany will need to be assigned to that coalition. + -- + -- ### 11.2. Country, type, load out, skill and skins for CAP and GCI aircraft? + -- + -- * Note these can be from any countries within the coalition but must be an aircraft with one of the main tasks being “CAPâ€. + -- * Obviously skins which are selected must be available to all players that join the mission otherwise they will see a default skin. + -- * Load outs should be appropriate to a CAP mission eg perhaps drop tanks for CAP flights and extra missiles for GCI flights. + -- * These decisions will eventually lead to template aircraft units being placed as late activation units that the script will use as templates for spawning CAP and GCI flights. Up to 4 different aircraft configurations can be chosen for each coalition. The spawned aircraft will inherit the characteristics of the template aircraft. + -- * The selected aircraft type must be able to perform the CAP tasking for the chosen country. + -- + -- + -- @field #AI_A2A_DISPATCHER + AI_A2A_DISPATCHER = { + ClassName = "AI_A2A_DISPATCHER", + Detection = nil, + } + + + --- Enumerator for spawns at airbases + -- @type AI_A2A_DISPATCHER.Takeoff + -- @extends Wrapper.Group#GROUP.Takeoff + + --- @field #AI_A2A_DISPATCHER.Takeoff Takeoff + AI_A2A_DISPATCHER.Takeoff = GROUP.Takeoff + + --- Defnes Landing location. + -- @field Landing + AI_A2A_DISPATCHER.Landing = { + NearAirbase = 1, + AtRunway = 2, + AtEngineShutdown = 3, + } + + --- AI_A2A_DISPATCHER constructor. + -- This is defining the A2A DISPATCHER for one coaliton. + -- The Dispatcher works with a @{Functional#Detection} object that is taking of the detection of targets using the EWR units. + -- The Detection object is polymorphic, depending on the type of detection object choosen, the detection will work differently. + -- @param #AI_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network. + -- @return #AI_A2A_DISPATCHER self + -- @usage + -- + -- -- Setup the Detection, using DETECTION_AREAS. + -- -- First define the SET of GROUPs that are defining the EWR network. + -- -- Here with prefixes DF CCCP AWACS, DF CCCP EWR. + -- DetectionSetGroup = SET_GROUP:New() + -- DetectionSetGroup:FilterPrefixes( { "DF CCCP AWACS", "DF CCCP EWR" } ) + -- DetectionSetGroup:FilterStart() + -- + -- -- Define the DETECTION_AREAS, using the DetectionSetGroup, with a 30km grouping radius. + -- Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 ) + -- + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) -- + -- + function AI_A2A_DISPATCHER:New( Detection ) + + -- Inherits from DETECTION_MANAGER + local self = BASE:Inherit( self, DETECTION_MANAGER:New( nil, Detection ) ) -- #AI_A2A_DISPATCHER + + self.Detection = Detection -- Functional.Detection#DETECTION_AREAS + + -- This table models the DefenderSquadron templates. + self.DefenderSquadrons = {} -- The Defender Squadrons. + self.DefenderSpawns = {} + self.DefenderTasks = {} -- The Defenders Tasks. + self.DefenderDefault = {} -- The Defender Default Settings over all Squadrons. + + -- TODO: Check detection through radar. + self.Detection:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) + --self.Detection:InitDetectRadar( true ) + self.Detection:SetRefreshTimeInterval( 30 ) + + self:SetEngageRadius() + self:SetGciRadius() + self:SetIntercept( 300 ) -- A default intercept delay time of 300 seconds. + self:SetDisengageRadius( 300000 ) -- The default Disengage Radius is 300 km. + + self:SetDefaultTakeoff( AI_A2A_DISPATCHER.Takeoff.Air ) + self:SetDefaultTakeoffInAirAltitude( 500 ) -- Default takeoff is 500 meters above the ground. + self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.NearAirbase ) + self:SetDefaultOverhead( 1 ) + self:SetDefaultGrouping( 1 ) + self:SetDefaultFuelThreshold( 0.15, 0 ) -- 15% of fuel remaining in the tank will trigger the airplane to return to base or refuel. + self:SetDefaultDamageThreshold( 0.4 ) -- When 40% of damage, go RTB. + self:SetDefaultCapTimeInterval( 180, 600 ) -- Between 180 and 600 seconds. + self:SetDefaultCapLimit( 1 ) -- Maximum one CAP per squadron. + + + self:AddTransition( "Started", "Assign", "Started" ) + + --- OnAfter Transition Handler for Event Assign. + -- @function [parent=#AI_A2A_DISPATCHER] OnAfterAssign + -- @param #AI_A2A_DISPATCHER self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Tasking.Task_A2A#AI_A2A Task + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #string PlayerName + + self:AddTransition( "*", "CAP", "*" ) + + --- CAP Handler OnBefore for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] OnBeforeCAP + -- @param #AI_A2A_DISPATCHER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- CAP Handler OnAfter for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] OnAfterCAP + -- @param #AI_A2A_DISPATCHER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- CAP Trigger for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] CAP + -- @param #AI_A2A_DISPATCHER self + + --- CAP Asynchronous Trigger for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] __CAP + -- @param #AI_A2A_DISPATCHER self + -- @param #number Delay + + self:AddTransition( "*", "GCI", "*" ) + + --- GCI Handler OnBefore for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] OnBeforeGCI + -- @param #AI_A2A_DISPATCHER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- GCI Handler OnAfter for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] OnAfterGCI + -- @param #AI_A2A_DISPATCHER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- GCI Trigger for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] GCI + -- @param #AI_A2A_DISPATCHER self + + --- GCI Asynchronous Trigger for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] __GCI + -- @param #AI_A2A_DISPATCHER self + -- @param #number Delay + + self:AddTransition( "*", "ENGAGE", "*" ) + + --- ENGAGE Handler OnBefore for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] OnBeforeENGAGE + -- @param #AI_A2A_DISPATCHER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- ENGAGE Handler OnAfter for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] OnAfterENGAGE + -- @param #AI_A2A_DISPATCHER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- ENGAGE Trigger for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] ENGAGE + -- @param #AI_A2A_DISPATCHER self + + --- ENGAGE Asynchronous Trigger for AI_A2A_DISPATCHER + -- @function [parent=#AI_A2A_DISPATCHER] __ENGAGE + -- @param #AI_A2A_DISPATCHER self + -- @param #number Delay + + + -- Subscribe to the CRASH event so that when planes are shot + -- by a Unit from the dispatcher, they will be removed from the detection... + -- This will avoid the detection to still "know" the shot unit until the next detection. + -- Otherwise, a new intercept or engage may happen for an already shot plane! + + self:HandleEvent( EVENTS.Crash, self.OnEventCrashOrDead ) + self:HandleEvent( EVENTS.Dead, self.OnEventCrashOrDead ) + + self:HandleEvent( EVENTS.Land ) + self:HandleEvent( EVENTS.EngineShutdown ) + + self:SetTacticalDisplay( false ) + + self:__Start( 5 ) + + return self + end + + --- @param #AI_A2A_DISPATCHER self + -- @param Core.Event#EVENTDATA EventData + function AI_A2A_DISPATCHER:OnEventCrashOrDead( EventData ) + self.Detection:ForgetDetectedUnit( EventData.IniUnitName ) + end + + --- @param #AI_A2A_DISPATCHER self + -- @param Core.Event#EVENTDATA EventData + function AI_A2A_DISPATCHER:OnEventLand( EventData ) + self:E( "Landed" ) + local DefenderUnit = EventData.IniUnit + local Defender = EventData.IniGroup + local Squadron = self:GetSquadronFromDefender( Defender ) + if Squadron then + self:F( { SquadronName = Squadron.Name } ) + local LandingMethod = self:GetSquadronLanding( Squadron.Name ) + if LandingMethod == AI_A2A_DISPATCHER.Landing.AtRunway then + local DefenderSize = Defender:GetSize() + if DefenderSize == 1 then + self:RemoveDefenderFromSquadron( Squadron, Defender ) + end + DefenderUnit:Destroy() + return + end + if DefenderUnit:GetLife() ~= DefenderUnit:GetLife0() then + -- Damaged units cannot be repaired anymore. + DefenderUnit:Destroy() + return + end + if DefenderUnit:GetFuel() <= self.DefenderDefault.FuelThreshold then + DefenderUnit:Destroy() + return + end + end + end + + --- @param #AI_A2A_DISPATCHER self + -- @param Core.Event#EVENTDATA EventData + function AI_A2A_DISPATCHER:OnEventEngineShutdown( EventData ) + local DefenderUnit = EventData.IniUnit + local Defender = EventData.IniGroup + local Squadron = self:GetSquadronFromDefender( Defender ) + if Squadron then + self:F( { SquadronName = Squadron.Name } ) + local LandingMethod = self:GetSquadronLanding( Squadron.Name ) + if LandingMethod == AI_A2A_DISPATCHER.Landing.AtEngineShutdown then + local DefenderSize = Defender:GetSize() + if DefenderSize == 1 then + self:RemoveDefenderFromSquadron( Squadron, Defender ) + end + DefenderUnit:Destroy() + end + end + end + + --- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission. + -- If there is a target area detected and reported, then any friendlies that are airborne near this target area, + -- will be commanded to (re-)engage that target when available (if no other tasks were commanded). + -- + -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, + -- will be considered to receive the command to engage that target area. + -- + -- You need to evaluate the value of this parameter carefully: + -- + -- * If too small, more intercept missions may be triggered upon detected target areas. + -- * If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. + -- + -- **Use the method @{#AI_A2A_DISPATCHER.SetEngageRadius}() to modify the default Engage Radius for ALL squadrons.** + -- + -- Demonstration Mission: [AID-019 - AI_A2A - Engage Range Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-019%20-%20AI_A2A%20-%20Engage%20Range%20Test) + -- + -- @param #AI_A2A_DISPATCHER self + -- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- Set 50km as the radius to engage any target by airborne friendlies. + -- A2ADispatcher:SetEngageRadius( 50000 ) + -- + -- -- Set 100km as the radius to engage any target by airborne friendlies. + -- A2ADispatcher:SetEngageRadius() -- 100000 is the default value. + -- + function AI_A2A_DISPATCHER:SetEngageRadius( EngageRadius ) + + self.Detection:SetFriendliesRange( EngageRadius or 100000 ) + + return self + end + + --- Define the radius to disengage any target when the distance to the home base is larger than the specified meters. + -- @param #AI_A2A_DISPATCHER self + -- @param #number DisengageRadius (Optional, Default = 300000) The radius to disengage a target when too far from the home base. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- Set 50km as the Disengage Radius. + -- A2ADispatcher:SetDisengageRadius( 50000 ) + -- + -- -- Set 100km as the Disengage Radius. + -- A2ADispatcher:SetDisngageRadius() -- 300000 is the default value. + -- + function AI_A2A_DISPATCHER:SetDisengageRadius( DisengageRadius ) + + self.DisengageRadius = DisengageRadius or 300000 + + return self + end + + + --- Define the radius to check if a target can be engaged by an ground controlled intercept. + -- When targets are detected that are still really far off, you don't want the AI_A2A_DISPATCHER to launch intercepts just yet. + -- You want it to wait until a certain Gci range is reached, which is the **distance of the closest airbase to target** + -- being **smaller** than the **Ground Controlled Intercept radius** or **Gci radius**. + -- + -- The **default** Gci radius is defined as **200000** or **200km**. Override the default Gci radius when the era of the warfare is early, or, + -- when you don't want to let the AI_A2A_DISPATCHER react immediately when a certain border or area is not being crossed. + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetGciRadius}() to set a specific controlled ground intercept radius. + -- **The Ground Controlled Intercept radius is defined for ALL squadrons which are operational.** + -- + -- Demonstration Mission: [AID-013 - AI_A2A - Intercept Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-013%20-%20AI_A2A%20-%20Intercept%20Test) + -- + -- @param #AI_A2A_DISPATCHER self + -- @param #number GciRadius (Optional, Default = 200000) The radius to ground control intercept detected targets from the nearest airbase. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- -- Set 100km as the radius to ground control intercept detected targets from the nearest airbase. + -- A2ADispatcher:SetGciRadius( 100000 ) + -- + -- -- Set 200km as the radius to ground control intercept. + -- A2ADispatcher:SetGciRadius() -- 200000 is the default value. + -- + function AI_A2A_DISPATCHER:SetGciRadius( GciRadius ) + + self.GciRadius = GciRadius or 200000 + + return self + end + + + + --- Define a border area to simulate a **cold war** scenario. + -- A **cold war** is one where CAP aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border. + -- A **hot war** is one where CAP aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send CAP and GCI aircraft to attack it. + -- If it’s a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Zone#ZONE_BASE}. This method needs to be used for this. + -- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. Set the noborders parameter to 1 + -- @param #AI_A2A_DISPATCHER self + -- @param Core.Zone#ZONE_BASE BorderZone An object derived from ZONE_BASE, or a list of objects derived from ZONE_BASE. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- -- Set one ZONE_POLYGON object as the border for the A2A dispatcher. + -- local BorderZone = ZONE_POLYGON( "CCCP Border", GROUP:FindByName( "CCCP Border" ) ) -- The GROUP object is a late activate helicopter unit. + -- A2ADispatcher:SetBorderZone( BorderZone ) + -- + -- or + -- + -- -- Set two ZONE_POLYGON objects as the border for the A2A dispatcher. + -- local BorderZone1 = ZONE_POLYGON( "CCCP Border1", GROUP:FindByName( "CCCP Border1" ) ) -- The GROUP object is a late activate helicopter unit. + -- local BorderZone2 = ZONE_POLYGON( "CCCP Border2", GROUP:FindByName( "CCCP Border2" ) ) -- The GROUP object is a late activate helicopter unit. + -- A2ADispatcher:SetBorderZone( { BorderZone1, BorderZone2 } ) + -- + -- + function AI_A2A_DISPATCHER:SetBorderZone( BorderZone ) + + self.Detection:SetAcceptZones( BorderZone ) + + return self + end + + --- Display a tactical report every 30 seconds about which aircraft are: + -- * Patrolling + -- * Engaging + -- * Returning + -- * Damaged + -- * Out of Fuel + -- * ... + -- @param #AI_A2A_DISPATCHER self + -- @param #boolean TacticalDisplay Provide a value of **true** to display every 30 seconds a tactical overview. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- -- Now Setup the Tactical Display for debug mode. + -- A2ADispatcher:SetTacticalDisplay( true ) + -- + function AI_A2A_DISPATCHER:SetTacticalDisplay( TacticalDisplay ) + + self.TacticalDisplay = TacticalDisplay + + return self + end + + + --- Set the default damage treshold when defenders will RTB. + -- The default damage treshold is by default set to 40%, which means that when the airplane is 40% damaged, it will go RTB. + -- @param #AI_A2A_DISPATCHER self + -- @param #number DamageThreshold A decimal number between 0 and 1, that expresses the %-tage of the damage treshold before going RTB. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- -- Now Setup the default damage treshold. + -- A2ADispatcher:SetDefaultDamageThreshold( 0.90 ) -- Go RTB when the airplane 90% damaged. + -- + function AI_A2A_DISPATCHER:SetDefaultDamageThreshold( DamageThreshold ) + + self.DefenderDefault.DamageThreshold = DamageThreshold + + return self + end + + + --- Set the default CAP time interval for squadrons, which will be used to determine a random CAP timing. + -- The default CAP time interval is between 180 and 600 seconds. + -- @param #AI_A2A_DISPATCHER self + -- @param #number CapMinSeconds The minimum amount of seconds for the random time interval. + -- @param #number CapMaxSeconds The maximum amount of seconds for the random time interval. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- -- Now Setup the default CAP time interval. + -- A2ADispatcher:SetDefaultCapTimeInterval( 300, 1200 ) -- Between 300 and 1200 seconds. + -- + function AI_A2A_DISPATCHER:SetDefaultCapTimeInterval( CapMinSeconds, CapMaxSeconds ) + + self.DefenderDefault.CapMinSeconds = CapMinSeconds + self.DefenderDefault.CapMaxSeconds = CapMaxSeconds + + return self + end + + + --- Set the default CAP limit for squadrons, which will be used to determine how many CAP can be airborne at the same time for the squadron. + -- The default CAP limit is 1 CAP, which means one CAP group being spawned. + -- @param #AI_A2A_DISPATCHER self + -- @param #number CapLimit The maximum amount of CAP that can be airborne at the same time for the squadron. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- -- Now Setup the default CAP limit. + -- A2ADispatcher:SetDefaultCapLimit( 2 ) -- Maximum 2 CAP per squadron. + -- + function AI_A2A_DISPATCHER:SetDefaultCapLimit( CapLimit ) + + self.DefenderDefault.CapLimit = CapLimit + + return self + end + + + function AI_A2A_DISPATCHER:SetIntercept( InterceptDelay ) + + self.DefenderDefault.InterceptDelay = InterceptDelay + + local Detection = self.Detection -- Functional.Detection#DETECTION_AREAS + Detection:SetIntercept( true, InterceptDelay ) + + return self + end + + + --- Calculates which AI friendlies are nearby the area + -- @param #AI_A2A_DISPATCHER self + -- @param DetectedItem + -- @return #number, Core.CommandCenter#REPORT + function AI_A2A_DISPATCHER:GetAIFriendliesNearBy( DetectedItem ) + + local FriendliesNearBy = self.Detection:GetFriendliesDistance( DetectedItem ) + + return FriendliesNearBy + end + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:GetDefenderTasks() + return self.DefenderTasks or {} + end + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:GetDefenderTask( Defender ) + return self.DefenderTasks[Defender] + end + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:GetDefenderTaskFsm( Defender ) + return self:GetDefenderTask( Defender ).Fsm + end + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:GetDefenderTaskTarget( Defender ) + return self:GetDefenderTask( Defender ).Target + end + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:ClearDefenderTask( Defender ) + if Defender:IsAlive() and self.DefenderTasks[Defender] then + local Target = self.DefenderTasks[Defender].Target + local Message = "Clearing (" .. self.DefenderTasks[Defender].Type .. ") " + Message = Message .. Defender:GetName() + if Target then + Message = Message .. ( Target and ( " from " .. Target.Index .. " [" .. Target.Set:Count() .. "]" ) ) or "" + end + self:F( { Target = Message } ) + end + self.DefenderTasks[Defender] = nil + return self + end + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:ClearDefenderTaskTarget( Defender ) + + local DefenderTask = self:GetDefenderTask( Defender ) + + if Defender:IsAlive() and DefenderTask then + local Target = DefenderTask.Target + local Message = "Clearing (" .. DefenderTask.Type .. ") " + Message = Message .. Defender:GetName() + if Target then + Message = Message .. ( Target and ( " from " .. Target.Index .. " [" .. Target.Set:Count() .. "]" ) ) or "" + end + self:F( { Target = Message } ) + end + if Defender and DefenderTask and DefenderTask.Target then + DefenderTask.Target = nil + end +-- if Defender and DefenderTask then +-- if DefenderTask.Fsm:Is( "Fuel" ) +-- or DefenderTask.Fsm:Is( "LostControl") +-- or DefenderTask.Fsm:Is( "Damaged" ) then +-- self:ClearDefenderTask( Defender ) +-- end +-- end + return self + end + + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:SetDefenderTask( SquadronName, Defender, Type, Fsm, Target ) + + self.DefenderTasks[Defender] = self.DefenderTasks[Defender] or {} + self.DefenderTasks[Defender].Type = Type + self.DefenderTasks[Defender].Fsm = Fsm + self.DefenderTasks[Defender].SquadronName = SquadronName + + if Target then + self:SetDefenderTaskTarget( Defender, Target ) + end + return self + end + + + --- + -- @param #AI_A2A_DISPATCHER self + -- @param Wrapper.Group#GROUP AIGroup + function AI_A2A_DISPATCHER:SetDefenderTaskTarget( Defender, AttackerDetection ) + + local Message = "(" .. self.DefenderTasks[Defender].Type .. ") " + Message = Message .. Defender:GetName() + Message = Message .. ( AttackerDetection and ( " target " .. AttackerDetection.Index .. " [" .. AttackerDetection.Set:Count() .. "]" ) ) or "" + self:F( { AttackerDetection = Message } ) + if AttackerDetection then + self.DefenderTasks[Defender].Target = AttackerDetection + end + return self + end + + + --- This is the main method to define Squadrons programmatically. + -- Squadrons: + -- + -- * Have a **name or key** that is the identifier or key of the squadron. + -- * Have **specific plane types** defined by **templates**. + -- * Are **located at one specific airbase**. Multiple squadrons can be located at one airbase through. + -- * Optionally have a limited set of **resources**. The default is that squadrons have unlimited resources. + -- + -- The name of the squadron given acts as the **squadron key** in the AI\_A2A\_DISPATCHER:Squadron...() methods. + -- + -- Additionally, squadrons have specific configuration options to: + -- + -- * Control how new aircraft are **taking off** from the airfield (in the air, cold, hot, at the runway). + -- * Control how returning aircraft are **landing** at the airfield (in the air near the airbase, after landing, after engine shutdown). + -- * Control the **grouping** of new aircraft spawned at the airfield. If there is more than one aircraft to be spawned, these may be grouped. + -- * Control the **overhead** or defensive strength of the squadron. Depending on the types of planes and amount of resources, the mission designer can choose to increase or reduce the amount of planes spawned. + -- + -- For performance and bug workaround reasons within DCS, squadrons have different methods to spawn new aircraft or land returning or damaged aircraft. + -- + -- @param #AI_A2A_DISPATCHER self + -- + -- @param #string SquadronName A string (text) that defines the squadron identifier or the key of the Squadron. + -- It can be any name, for example `"104th Squadron"` or `"SQ SQUADRON1"`, whatever. + -- As long as you remember that this name becomes the identifier of your squadron you have defined. + -- You need to use this name in other methods too! + -- + -- @param #string AirbaseName The airbase name where you want to have the squadron located. + -- You need to specify here EXACTLY the name of the airbase as you see it in the mission editor. + -- Examples are `"Batumi"` or `"Tbilisi-Lochini"`. + -- EXACTLY the airbase name, between quotes `""`. + -- To ease the airbase naming when using the LDT editor and IntelliSense, the @{Airbase#AIRBASE} class contains enumerations of the airbases of each map. + -- + -- * Caucasus: @{Airbase#AIRBASE.Caucaus} + -- * Nevada or NTTR: @{Airbase#AIRBASE.Nevada} + -- * Normandy: @{Airbase#AIRBASE.Normandy} + -- + -- @param #string TemplatePrefixes A string or an array of strings specifying the **prefix names of the templates** (not going to explain what is templates here again). + -- Examples are `{ "104th", "105th" }` or `"104th"` or `"Template 1"` or `"BLUE PLANES"`. + -- Just remember that your template (groups late activated) need to start with the prefix you have specified in your code. + -- If you have only one prefix name for a squadron, you don't need to use the `{ }`, otherwise you need to use the brackets. + -- + -- @param #number Resources (optional) A number that specifies how many resources are in stock of the squadron. If not specified, the squadron will have infinite resources available. + -- + -- @usage + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- @usage + -- -- This will create squadron "Squadron1" at "Batumi" airbase, and will use plane types "SQ1" and has 40 planes in stock... + -- A2ADispatcher:SetSquadron( "Squadron1", "Batumi", "SQ1", 40 ) + -- + -- @usage + -- -- This will create squadron "Sq 1" at "Batumi" airbase, and will use plane types "Mig-29" and "Su-27" and has 20 planes in stock... + -- -- Note that in this implementation, the A2A dispatcher will select a random plane type when a new plane (group) needs to be spawned for defenses. + -- -- Note the usage of the {} for the airplane templates list. + -- A2ADispatcher:SetSquadron( "Sq 1", "Batumi", { "Mig-29", "Su-27" }, 40 ) + -- + -- @usage + -- -- This will create 2 squadrons "104th" and "23th" at "Batumi" airbase, and will use plane types "Mig-29" and "Su-27" respectively and each squadron has 10 planes in stock... + -- A2ADispatcher:SetSquadron( "104th", "Batumi", "Mig-29", 10 ) + -- A2ADispatcher:SetSquadron( "23th", "Batumi", "Su-27", 10 ) + -- + -- @usage + -- -- This is an example like the previous, but now with infinite resources. + -- -- The Resources parameter is not given in the SetSquadron method. + -- A2ADispatcher:SetSquadron( "104th", "Batumi", "Mig-29" ) + -- A2ADispatcher:SetSquadron( "23th", "Batumi", "Su-27" ) + -- + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetSquadron( SquadronName, AirbaseName, TemplatePrefixes, Resources ) + + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + + local DefenderSquadron = self.DefenderSquadrons[SquadronName] + + DefenderSquadron.Name = SquadronName + DefenderSquadron.Airbase = AIRBASE:FindByName( AirbaseName ) + if not DefenderSquadron.Airbase then + error( "Cannot find airbase with name:" .. AirbaseName ) + end + + DefenderSquadron.Spawn = {} + if type( TemplatePrefixes ) == "string" then + local SpawnTemplate = TemplatePrefixes + self.DefenderSpawns[SpawnTemplate] = self.DefenderSpawns[SpawnTemplate] or SPAWN:New( SpawnTemplate ) -- :InitCleanUp( 180 ) + DefenderSquadron.Spawn[1] = self.DefenderSpawns[SpawnTemplate] + else + for TemplateID, SpawnTemplate in pairs( TemplatePrefixes ) do + self.DefenderSpawns[SpawnTemplate] = self.DefenderSpawns[SpawnTemplate] or SPAWN:New( SpawnTemplate ) -- :InitCleanUp( 180 ) + DefenderSquadron.Spawn[#DefenderSquadron.Spawn+1] = self.DefenderSpawns[SpawnTemplate] + end + end + DefenderSquadron.Resources = Resources + DefenderSquadron.TemplatePrefixes = TemplatePrefixes + + self:E( { Squadron = {SquadronName, AirbaseName, TemplatePrefixes, Resources } } ) + + return self + end + + --- Get an item from the Squadron table. + -- @param #AI_A2A_DISPATCHER self + -- @return #table + function AI_A2A_DISPATCHER:GetSquadron( SquadronName ) + local DefenderSquadron = self.DefenderSquadrons[SquadronName] + + if not DefenderSquadron then + error( "Unknown Squadron:" .. SquadronName ) + end + + return DefenderSquadron + end + + + --- Set a CAP for a Squadron. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The squadron name. + -- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Zone#ZONE_BASE} that defines the zone wherein the CAP will be executed. + -- @param #number FloorAltitude The minimum altitude at which the cap can be executed. + -- @param #number CeilingAltitude the maximum altitude at which the cap can be executed. + -- @param #number PatrolMinSpeed The minimum speed at which the cap can be executed. + -- @param #number PatrolMaxSpeed The maximum speed at which the cap can be executed. + -- @param #number EngageMinSpeed The minimum speed at which the engage can be executed. + -- @param #number EngageMaxSpeed The maximum speed at which the engage can be executed. + -- @param #number AltType The altitude type, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- CAP Squadron execution. + -- CAPZoneEast = ZONE_POLYGON:New( "CAP Zone East", GROUP:FindByName( "CAP Zone East" ) ) + -- A2ADispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 ) + -- A2ADispatcher:SetSquadronCapInterval( "Mineralnye", 2, 30, 60, 1 ) + -- + -- CAPZoneWest = ZONE_POLYGON:New( "CAP Zone West", GROUP:FindByName( "CAP Zone West" ) ) + -- A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" ) + -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) + -- + -- CAPZoneMiddle = ZONE:New( "CAP Zone Middle") + -- A2ADispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" ) + -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) + -- + function AI_A2A_DISPATCHER:SetSquadronCap( SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType ) + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {} + + local DefenderSquadron = self:GetSquadron( SquadronName ) + + local Cap = self.DefenderSquadrons[SquadronName].Cap + Cap.Name = SquadronName + Cap.Zone = Zone + Cap.FloorAltitude = FloorAltitude + Cap.CeilingAltitude = CeilingAltitude + Cap.PatrolMinSpeed = PatrolMinSpeed + Cap.PatrolMaxSpeed = PatrolMaxSpeed + Cap.EngageMinSpeed = EngageMinSpeed + Cap.EngageMaxSpeed = EngageMaxSpeed + Cap.AltType = AltType + + self:SetSquadronCapInterval( SquadronName, self.DefenderDefault.CapLimit, self.DefenderDefault.CapMinSeconds, self.DefenderDefault.CapMaxSeconds, 1 ) + + self:E( { CAP = { SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType } } ) + + -- Add the CAP to the EWR network. + + local RecceSet = self.Detection:GetDetectionSetGroup() + RecceSet:FilterPrefixes( DefenderSquadron.TemplatePrefixes ) + RecceSet:FilterStart() + + self.Detection:SetFriendlyPrefixes( DefenderSquadron.TemplatePrefixes ) + + return self + end + + --- Set the squadron CAP parameters. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The squadron name. + -- @param #number CapLimit (optional) The maximum amount of CAP groups to be spawned. Note that a CAP is a group, so can consist out of 1 to 4 airplanes. The default is 1 CAP group. + -- @param #number LowInterval (optional) The minimum time boundary in seconds when a new CAP will be spawned. The default is 180 seconds. + -- @param #number HighInterval (optional) The maximum time boundary in seconds when a new CAP will be spawned. The default is 600 seconds. + -- @param #number Probability Is not in use, you can skip this parameter. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- CAP Squadron execution. + -- CAPZoneEast = ZONE_POLYGON:New( "CAP Zone East", GROUP:FindByName( "CAP Zone East" ) ) + -- A2ADispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 ) + -- A2ADispatcher:SetSquadronCapInterval( "Mineralnye", 2, 30, 60, 1 ) + -- + -- CAPZoneWest = ZONE_POLYGON:New( "CAP Zone West", GROUP:FindByName( "CAP Zone West" ) ) + -- A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" ) + -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) + -- + -- CAPZoneMiddle = ZONE:New( "CAP Zone Middle") + -- A2ADispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" ) + -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) + -- + function AI_A2A_DISPATCHER:SetSquadronCapInterval( SquadronName, CapLimit, LowInterval, HighInterval, Probability ) + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {} + + local DefenderSquadron = self:GetSquadron( SquadronName ) + + local Cap = self.DefenderSquadrons[SquadronName].Cap + if Cap then + Cap.LowInterval = LowInterval or 180 + Cap.HighInterval = HighInterval or 600 + Cap.Probability = Probability or 1 + Cap.CapLimit = CapLimit or 1 + Cap.Scheduler = Cap.Scheduler or SCHEDULER:New( self ) + local Scheduler = Cap.Scheduler -- Core.Scheduler#SCHEDULER + local ScheduleID = Cap.ScheduleID + local Variance = ( Cap.HighInterval - Cap.LowInterval ) / 2 + local Repeat = Cap.LowInterval + Variance + local Randomization = Variance / Repeat + local Start = math.random( 1, Cap.HighInterval ) + + if ScheduleID then + Scheduler:Stop( ScheduleID ) + end + + Cap.ScheduleID = Scheduler:Schedule( self, self.SchedulerCAP, { SquadronName }, Start, Repeat, Randomization ) + else + error( "This squadron does not exist:" .. SquadronName ) + end + + end + + --- + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The squadron name. + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:GetCAPDelay( SquadronName ) + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {} + + local DefenderSquadron = self:GetSquadron( SquadronName ) + + local Cap = self.DefenderSquadrons[SquadronName].Cap + if Cap then + return math.random( Cap.LowInterval, Cap.HighInterval ) + else + error( "This squadron does not exist:" .. SquadronName ) + end + end + + --- + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The squadron name. + -- @return #table DefenderSquadron + function AI_A2A_DISPATCHER:CanCAP( SquadronName ) + self:F({SquadronName = SquadronName}) + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {} + + local DefenderSquadron = self:GetSquadron( SquadronName ) + + if ( not DefenderSquadron.Resources ) or ( DefenderSquadron.Resources and DefenderSquadron.Resources > 0 ) then + + local Cap = DefenderSquadron.Cap + if Cap then + local CapCount = self:CountCapAirborne( SquadronName ) + self:E( { CapCount = CapCount } ) + if CapCount < Cap.CapLimit then + local Probability = math.random() + if Probability <= Cap.Probability then + return DefenderSquadron + end + end + end + end + return nil + end + + + --- + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The squadron name. + -- @return #table DefenderSquadron + function AI_A2A_DISPATCHER:CanGCI( SquadronName ) + self:F({SquadronName = SquadronName}) + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + self.DefenderSquadrons[SquadronName].Gci = self.DefenderSquadrons[SquadronName].Gci or {} + + local DefenderSquadron = self:GetSquadron( SquadronName ) + + if ( not DefenderSquadron.Resources ) or ( DefenderSquadron.Resources and DefenderSquadron.Resources > 0 ) then + local Gci = DefenderSquadron.Gci + if Gci then + return DefenderSquadron + end + end + return nil + end + + + --- + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The squadron name. + -- @param #number EngageMinSpeed The minimum speed at which the gci can be executed. + -- @param #number EngageMaxSpeed The maximum speed at which the gci can be executed. + -- @usage + -- + -- -- GCI Squadron execution. + -- A2ADispatcher:SetSquadronGci( "Mozdok", 900, 1200 ) + -- A2ADispatcher:SetSquadronGci( "Novo", 900, 2100 ) + -- A2ADispatcher:SetSquadronGci( "Maykop", 900, 1200 ) + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetSquadronGci( SquadronName, EngageMinSpeed, EngageMaxSpeed ) + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + self.DefenderSquadrons[SquadronName].Gci = self.DefenderSquadrons[SquadronName].Gci or {} + + local Intercept = self.DefenderSquadrons[SquadronName].Gci + Intercept.Name = SquadronName + Intercept.EngageMinSpeed = EngageMinSpeed + Intercept.EngageMaxSpeed = EngageMaxSpeed + + self:E( { GCI = { SquadronName, EngageMinSpeed, EngageMaxSpeed } } ) + end + + --- Defines the default amount of extra planes that will take-off as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #number Overhead The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units. + -- The default overhead is 1, so equal balance. The @{#AI_A2A_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength, + -- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance A2A missiles payload, may still be less effective than a F-15C with short missiles... + -- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes. + -- The overhead must be given as a decimal value with 1 as the neutral value, which means that Overhead values: + -- + -- * Higher than 1, will increase the defense unit amounts. + -- * Lower than 1, will decrease the defense unit amounts. + -- + -- The amount of defending units is calculated by multiplying the amount of detected attacking planes as part of the detected group + -- multiplied by the Overhead and rounded up to the smallest integer. + -- + -- The Overhead value set for a Squadron, can be programmatically adjusted (by using this SetOverhead method), to adjust the defense overhead during mission execution. + -- + -- See example below. + -- + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- An overhead of 1,5 with 1 planes detected, will allocate 2 planes ( 1 * 1,5 ) = 1,5 => rounded up gives 2. + -- -- An overhead of 1,5 with 2 planes detected, will allocate 3 planes ( 2 * 1,5 ) = 3 => rounded up gives 3. + -- -- An overhead of 1,5 with 3 planes detected, will allocate 5 planes ( 3 * 1,5 ) = 4,5 => rounded up gives 5 planes. + -- -- An overhead of 1,5 with 4 planes detected, will allocate 6 planes ( 4 * 1,5 ) = 6 => rounded up gives 6 planes. + -- + -- A2ADispatcher:SetDefaultOverhead( 1.5 ) + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetDefaultOverhead( Overhead ) + + self.DefenderDefault.Overhead = Overhead + + return self + end + + + --- Defines the amount of extra planes that will take-off as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @param #number Overhead The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units. + -- The default overhead is 1, so equal balance. The @{#AI_A2A_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength, + -- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance A2A missiles payload, may still be less effective than a F-15C with short missiles... + -- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes. + -- The overhead must be given as a decimal value with 1 as the neutral value, which means that Overhead values: + -- + -- * Higher than 1, will increase the defense unit amounts. + -- * Lower than 1, will decrease the defense unit amounts. + -- + -- The amount of defending units is calculated by multiplying the amount of detected attacking planes as part of the detected group + -- multiplied by the Overhead and rounded up to the smallest integer. + -- + -- The Overhead value set for a Squadron, can be programmatically adjusted (by using this SetOverhead method), to adjust the defense overhead during mission execution. + -- + -- See example below. + -- + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- An overhead of 1,5 with 1 planes detected, will allocate 2 planes ( 1 * 1,5 ) = 1,5 => rounded up gives 2. + -- -- An overhead of 1,5 with 2 planes detected, will allocate 3 planes ( 2 * 1,5 ) = 3 => rounded up gives 3. + -- -- An overhead of 1,5 with 3 planes detected, will allocate 5 planes ( 3 * 1,5 ) = 4,5 => rounded up gives 5 planes. + -- -- An overhead of 1,5 with 4 planes detected, will allocate 6 planes ( 4 * 1,5 ) = 6 => rounded up gives 6 planes. + -- + -- A2ADispatcher:SetSquadronOverhead( "SquadronName", 1.5 ) + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetSquadronOverhead( SquadronName, Overhead ) + + local DefenderSquadron = self:GetSquadron( SquadronName ) + DefenderSquadron.Overhead = Overhead + + return self + end + + + --- Sets the default grouping of new airplanes spawned. + -- Grouping will trigger how new airplanes will be grouped if more than one airplane is spawned for defense. + -- @param #AI_A2A_DISPATCHER self + -- @param #number Grouping The level of grouping that will be applied of the CAP or GCI defenders. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Set a grouping by default per 2 airplanes. + -- A2ADispatcher:SetDefaultGrouping( 2 ) + -- + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetDefaultGrouping( Grouping ) + + self.DefenderDefault.Grouping = Grouping + + return self + end + + + --- Sets the grouping of new airplanes spawned. + -- Grouping will trigger how new airplanes will be grouped if more than one airplane is spawned for defense. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @param #number Grouping The level of grouping that will be applied of the CAP or GCI defenders. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Set a grouping per 2 airplanes. + -- A2ADispatcher:SetSquadronGrouping( "SquadronName", 2 ) + -- + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetSquadronGrouping( SquadronName, Grouping ) + + local DefenderSquadron = self:GetSquadron( SquadronName ) + DefenderSquadron.Grouping = Grouping + + return self + end + + + --- Defines the default method at which new flights will spawn and take-off as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #number Takeoff From the airbase hot, from the airbase cold, in the air, from the runway. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights by default take-off in the air. + -- A2ADispatcher:SetDefaultTakeoff( AI_A2A_Dispatcher.Takeoff.Air ) + -- + -- -- Let new flights by default take-off from the runway. + -- A2ADispatcher:SetDefaultTakeoff( AI_A2A_Dispatcher.Takeoff.Runway ) + -- + -- -- Let new flights by default take-off from the airbase hot. + -- A2ADispatcher:SetDefaultTakeoff( AI_A2A_Dispatcher.Takeoff.Hot ) + -- + -- -- Let new flights by default take-off from the airbase cold. + -- A2ADispatcher:SetDefaultTakeoff( AI_A2A_Dispatcher.Takeoff.Cold ) + -- + -- + -- @return #AI_A2A_DISPATCHER + -- + function AI_A2A_DISPATCHER:SetDefaultTakeoff( Takeoff ) + + self.DefenderDefault.Takeoff = Takeoff + + return self + end + + --- Defines the method at which new flights will spawn and take-off as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @param #number Takeoff From the airbase hot, from the airbase cold, in the air, from the runway. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights take-off in the air. + -- A2ADispatcher:SetSquadronTakeoff( "SquadronName", AI_A2A_Dispatcher.Takeoff.Air ) + -- + -- -- Let new flights take-off from the runway. + -- A2ADispatcher:SetSquadronTakeoff( "SquadronName", AI_A2A_Dispatcher.Takeoff.Runway ) + -- + -- -- Let new flights take-off from the airbase hot. + -- A2ADispatcher:SetSquadronTakeoff( "SquadronName", AI_A2A_Dispatcher.Takeoff.Hot ) + -- + -- -- Let new flights take-off from the airbase cold. + -- A2ADispatcher:SetSquadronTakeoff( "SquadronName", AI_A2A_Dispatcher.Takeoff.Cold ) + -- + -- + -- @return #AI_A2A_DISPATCHER + -- + function AI_A2A_DISPATCHER:SetSquadronTakeoff( SquadronName, Takeoff ) + + local DefenderSquadron = self:GetSquadron( SquadronName ) + DefenderSquadron.Takeoff = Takeoff + + return self + end + + + --- Gets the default method at which new flights will spawn and take-off as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @return #number Takeoff From the airbase hot, from the airbase cold, in the air, from the runway. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights by default take-off in the air. + -- local TakeoffMethod = A2ADispatcher:GetDefaultTakeoff() + -- if TakeOffMethod == , AI_A2A_Dispatcher.Takeoff.InAir then + -- ... + -- end + -- + function AI_A2A_DISPATCHER:GetDefaultTakeoff( ) + + return self.DefenderDefault.Takeoff + end + + --- Gets the method at which new flights will spawn and take-off as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @return #number Takeoff From the airbase hot, from the airbase cold, in the air, from the runway. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights take-off in the air. + -- local TakeoffMethod = A2ADispatcher:GetSquadronTakeoff( "SquadronName" ) + -- if TakeOffMethod == , AI_A2A_Dispatcher.Takeoff.InAir then + -- ... + -- end + -- + function AI_A2A_DISPATCHER:GetSquadronTakeoff( SquadronName ) + + local DefenderSquadron = self:GetSquadron( SquadronName ) + return DefenderSquadron.Takeoff or self.DefenderDefault.Takeoff + end + + + --- Sets flights to default take-off in the air, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights by default take-off in the air. + -- A2ADispatcher:SetDefaultTakeoffInAir() + -- + -- @return #AI_A2A_DISPATCHER + -- + function AI_A2A_DISPATCHER:SetDefaultTakeoffInAir() + + self:SetDefaultTakeoff( AI_A2A_DISPATCHER.Takeoff.Air ) + + return self + end + + + --- Sets flights to take-off in the air, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @param #number TakeoffAltitude (optional) The altitude in meters above the ground. If not given, the default takeoff altitude will be used. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights take-off in the air. + -- A2ADispatcher:SetSquadronTakeoffInAir( "SquadronName" ) + -- + -- @return #AI_A2A_DISPATCHER + -- + function AI_A2A_DISPATCHER:SetSquadronTakeoffInAir( SquadronName, TakeoffAltitude ) + + self:SetSquadronTakeoff( SquadronName, AI_A2A_DISPATCHER.Takeoff.Air ) + + if TakeoffAltitude then + self:SetSquadronTakeoffInAirAltitude( SquadronName, TakeoffAltitude ) + end + + return self + end + + + --- Sets flights by default to take-off from the runway, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights by default take-off from the runway. + -- A2ADispatcher:SetDefaultTakeoffFromRunway() + -- + -- @return #AI_A2A_DISPATCHER + -- + function AI_A2A_DISPATCHER:SetDefaultTakeoffFromRunway() + + self:SetDefaultTakeoff( AI_A2A_DISPATCHER.Takeoff.Runway ) + + return self + end + + + --- Sets flights to take-off from the runway, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights take-off from the runway. + -- A2ADispatcher:SetSquadronTakeoffFromRunway( "SquadronName" ) + -- + -- @return #AI_A2A_DISPATCHER + -- + function AI_A2A_DISPATCHER:SetSquadronTakeoffFromRunway( SquadronName ) + + self:SetSquadronTakeoff( SquadronName, AI_A2A_DISPATCHER.Takeoff.Runway ) + + return self + end + + + --- Sets flights by default to take-off from the airbase at a hot location, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights by default take-off at a hot parking spot. + -- A2ADispatcher:SetDefaultTakeoffFromParkingHot() + -- + -- @return #AI_A2A_DISPATCHER + -- + function AI_A2A_DISPATCHER:SetDefaultTakeoffFromParkingHot() + + self:SetDefaultTakeoff( AI_A2A_DISPATCHER.Takeoff.Hot ) + + return self + end + + --- Sets flights to take-off from the airbase at a hot location, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights take-off in the air. + -- A2ADispatcher:SetSquadronTakeoffFromParkingHot( "SquadronName" ) + -- + -- @return #AI_A2A_DISPATCHER + -- + function AI_A2A_DISPATCHER:SetSquadronTakeoffFromParkingHot( SquadronName ) + + self:SetSquadronTakeoff( SquadronName, AI_A2A_DISPATCHER.Takeoff.Hot ) + + return self + end + + + --- Sets flights to by default take-off from the airbase at a cold location, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights take-off from a cold parking spot. + -- A2ADispatcher:SetDefaultTakeoffFromParkingCold() + -- + -- @return #AI_A2A_DISPATCHER + -- + function AI_A2A_DISPATCHER:SetDefaultTakeoffFromParkingCold() + + self:SetDefaultTakeoff( AI_A2A_DISPATCHER.Takeoff.Cold ) + + return self + end + + + --- Sets flights to take-off from the airbase at a cold location, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights take-off from a cold parking spot. + -- A2ADispatcher:SetSquadronTakeoffFromParkingCold( "SquadronName" ) + -- + -- @return #AI_A2A_DISPATCHER + -- + function AI_A2A_DISPATCHER:SetSquadronTakeoffFromParkingCold( SquadronName ) + + self:SetSquadronTakeoff( SquadronName, AI_A2A_DISPATCHER.Takeoff.Cold ) + + return self + end + + + --- Defines the default altitude where airplanes will spawn in the air and take-off as part of the defense system, when the take-off in the air method has been selected. + -- @param #AI_A2A_DISPATCHER self + -- @param #number TakeoffAltitude The altitude in meters above the ground. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Set the default takeoff altitude when taking off in the air. + -- A2ADispatcher:SetDefaultTakeoffInAirAltitude( 2000 ) -- This makes planes start at 2000 meters above the ground. + -- + -- @return #AI_A2A_DISPATCHER + -- + function AI_A2A_DISPATCHER:SetDefaultTakeoffInAirAltitude( TakeoffAltitude ) + + self.DefenderDefault.TakeoffAltitude = TakeoffAltitude + + return self + end + + --- Defines the default altitude where airplanes will spawn in the air and take-off as part of the defense system, when the take-off in the air method has been selected. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @param #number TakeoffAltitude The altitude in meters above the ground. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Set the default takeoff altitude when taking off in the air. + -- A2ADispatcher:SetSquadronTakeoffInAirAltitude( "SquadronName", 2000 ) -- This makes planes start at 2000 meters above the ground. + -- + -- @return #AI_A2A_DISPATCHER + -- + function AI_A2A_DISPATCHER:SetSquadronTakeoffInAirAltitude( SquadronName, TakeoffAltitude ) + + local DefenderSquadron = self:GetSquadron( SquadronName ) + DefenderSquadron.TakeoffAltitude = TakeoffAltitude + + return self + end + + + --- Defines the default method at which flights will land and despawn as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #number Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights by default despawn near the airbase when returning. + -- A2ADispatcher:SetDefaultLanding( AI_A2A_Dispatcher.Landing.NearAirbase ) + -- + -- -- Let new flights by default despawn after landing land at the runway. + -- A2ADispatcher:SetDefaultLanding( AI_A2A_Dispatcher.Landing.AtRunway ) + -- + -- -- Let new flights by default despawn after landing and parking, and after engine shutdown. + -- A2ADispatcher:SetDefaultLanding( AI_A2A_Dispatcher.Landing.AtEngineShutdown ) + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetDefaultLanding( Landing ) + + self.DefenderDefault.Landing = Landing + + return self + end + + + --- Defines the method at which flights will land and despawn as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @param #number Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights despawn near the airbase when returning. + -- A2ADispatcher:SetSquadronLanding( "SquadronName", AI_A2A_Dispatcher.Landing.NearAirbase ) + -- + -- -- Let new flights despawn after landing land at the runway. + -- A2ADispatcher:SetSquadronLanding( "SquadronName", AI_A2A_Dispatcher.Landing.AtRunway ) + -- + -- -- Let new flights despawn after landing and parking, and after engine shutdown. + -- A2ADispatcher:SetSquadronLanding( "SquadronName", AI_A2A_Dispatcher.Landing.AtEngineShutdown ) + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetSquadronLanding( SquadronName, Landing ) + + local DefenderSquadron = self:GetSquadron( SquadronName ) + DefenderSquadron.Landing = Landing + + return self + end + + + --- Gets the default method at which flights will land and despawn as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @return #number Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights by default despawn near the airbase when returning. + -- local LandingMethod = A2ADispatcher:GetDefaultLanding( AI_A2A_Dispatcher.Landing.NearAirbase ) + -- if LandingMethod == AI_A2A_Dispatcher.Landing.NearAirbase then + -- ... + -- end + -- + function AI_A2A_DISPATCHER:GetDefaultLanding() + + return self.DefenderDefault.Landing + end + + + --- Gets the method at which flights will land and despawn as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @return #number Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let new flights despawn near the airbase when returning. + -- local LandingMethod = A2ADispatcher:GetSquadronLanding( "SquadronName", AI_A2A_Dispatcher.Landing.NearAirbase ) + -- if LandingMethod == AI_A2A_Dispatcher.Landing.NearAirbase then + -- ... + -- end + -- + function AI_A2A_DISPATCHER:GetSquadronLanding( SquadronName ) + + local DefenderSquadron = self:GetSquadron( SquadronName ) + return DefenderSquadron.Landing or self.DefenderDefault.Landing + end + + + --- Sets flights by default to land and despawn near the airbase in the air, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let flights by default to land near the airbase and despawn. + -- A2ADispatcher:SetDefaultLandingNearAirbase() + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetDefaultLandingNearAirbase() + + self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.NearAirbase ) + + return self + end + + + --- Sets flights to land and despawn near the airbase in the air, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let flights to land near the airbase and despawn. + -- A2ADispatcher:SetSquadronLandingNearAirbase( "SquadronName" ) + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetSquadronLandingNearAirbase( SquadronName ) + + self:SetSquadronLanding( SquadronName, AI_A2A_DISPATCHER.Landing.NearAirbase ) + + return self + end + + + --- Sets flights by default to land and despawn at the runway, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let flights by default land at the runway and despawn. + -- A2ADispatcher:SetDefaultLandingAtRunway() + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetDefaultLandingAtRunway() + + self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.AtRunway ) + + return self + end + + + --- Sets flights to land and despawn at the runway, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let flights land at the runway and despawn. + -- A2ADispatcher:SetSquadronLandingAtRunway( "SquadronName" ) + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetSquadronLandingAtRunway( SquadronName ) + + self:SetSquadronLanding( SquadronName, AI_A2A_DISPATCHER.Landing.AtRunway ) + + return self + end + + + --- Sets flights by default to land and despawn at engine shutdown, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let flights by default land and despawn at engine shutdown. + -- A2ADispatcher:SetDefaultLandingAtEngineShutdown() + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown() + + self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.AtEngineShutdown ) + + return self + end + + + --- Sets flights to land and despawn at engine shutdown, as part of the defense system. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @usage: + -- + -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) + -- + -- -- Let flights land and despawn at engine shutdown. + -- A2ADispatcher:SetSquadronLandingAtEngineShutdown( "SquadronName" ) + -- + -- @return #AI_A2A_DISPATCHER + function AI_A2A_DISPATCHER:SetSquadronLandingAtEngineShutdown( SquadronName ) + + self:SetSquadronLanding( SquadronName, AI_A2A_DISPATCHER.Landing.AtEngineShutdown ) + + return self + end + + --- Set the default fuel treshold when defenders will RTB or Refuel in the air. + -- The fuel treshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed. + -- @param #AI_A2A_DISPATCHER self + -- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the %-tage of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- -- Now Setup the default fuel treshold. + -- A2ADispatcher:SetDefaultRefuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. + -- + function AI_A2A_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold ) + + self.DefenderDefault.FuelThreshold = FuelThreshold + + return self + end + + + --- Set the fuel treshold for the squadron when defenders will RTB or Refuel in the air. + -- The fuel treshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the %-tage of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- -- Now Setup the default fuel treshold. + -- A2ADispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. + -- + function AI_A2A_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold ) + + local DefenderSquadron = self:GetSquadron( SquadronName ) + DefenderSquadron.FuelThreshold = FuelThreshold + + return self + end + + --- Set the default tanker where defenders will Refuel in the air. + -- @param #AI_A2A_DISPATCHER self + -- @param #strig TankerName A string defining the group name of the Tanker as defined within the Mission Editor. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- -- Now Setup the default fuel treshold. + -- A2ADispatcher:SetDefaultRefuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. + -- + -- -- Now Setup the default tanker. + -- A2ADispatcher:SetDefaultTanker( "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor. + function AI_A2A_DISPATCHER:SetDefaultTanker( TankerName ) + + self.DefenderDefault.TankerName = TankerName + + return self + end + + + --- Set the squadron tanker where defenders will Refuel in the air. + -- @param #AI_A2A_DISPATCHER self + -- @param #string SquadronName The name of the squadron. + -- @param #strig TankerName A string defining the group name of the Tanker as defined within the Mission Editor. + -- @return #AI_A2A_DISPATCHER + -- @usage + -- + -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- + -- -- Now Setup the squadron fuel treshold. + -- A2ADispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. + -- + -- -- Now Setup the squadron tanker. + -- A2ADispatcher:SetSquadronTanker( "SquadronName", "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor. + function AI_A2A_DISPATCHER:SetSquadronTanker( SquadronName, TankerName ) + + local DefenderSquadron = self:GetSquadron( SquadronName ) + DefenderSquadron.TankerName = TankerName + + return self + end + + + + + --- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:AddDefenderToSquadron( Squadron, Defender, Size ) + self.Defenders = self.Defenders or {} + local DefenderName = Defender:GetName() + self.Defenders[ DefenderName ] = Squadron + if Squadron.Resources then + Squadron.Resources = Squadron.Resources - Size + end + self:E( { DefenderName = DefenderName, SquadronResources = Squadron.Resources } ) + end + + --- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:RemoveDefenderFromSquadron( Squadron, Defender ) + self.Defenders = self.Defenders or {} + local DefenderName = Defender:GetName() + if Squadron.Resources then + Squadron.Resources = Squadron.Resources + Defender:GetSize() + end + self.Defenders[ DefenderName ] = nil + self:F( { DefenderName = DefenderName, SquadronResources = Squadron.Resources } ) + end + + function AI_A2A_DISPATCHER:GetSquadronFromDefender( Defender ) + self.Defenders = self.Defenders or {} + local DefenderName = Defender:GetName() + self:F( { DefenderName = DefenderName } ) + return self.Defenders[ DefenderName ] + end + + + --- Creates an SWEEP task when there are targets for it. + -- @param #AI_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem + -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. + function AI_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + + if DetectedItem.IsDetected == false then + + -- Here we're doing something advanced... We're copying the DetectedSet. + local TargetSetUnit = SET_UNIT:New() + TargetSetUnit:SetDatabase( DetectedSet ) + TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. + + return TargetSetUnit + end + + return nil + end + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:CountCapAirborne( SquadronName ) + + local CapCount = 0 + + local DefenderSquadron = self.DefenderSquadrons[SquadronName] + if DefenderSquadron then + for AIGroup, DefenderTask in pairs( self:GetDefenderTasks() ) do + if DefenderTask.SquadronName == SquadronName then + if DefenderTask.Type == "CAP" then + if AIGroup:IsAlive() then + -- Check if the CAP is patrolling or engaging. If not, this is not a valid CAP, even if it is alive! + -- The CAP could be damaged, lost control, or out of fuel! + if DefenderTask.Fsm:Is( "Patrolling" ) or DefenderTask.Fsm:Is( "Engaging" ) or DefenderTask.Fsm:Is( "Refuelling" )then + CapCount = CapCount + 1 + end + end + end + end + end + end + + return CapCount + end + + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:CountDefendersEngaged( AttackerDetection ) + + -- First, count the active AIGroups Units, targetting the DetectedSet + local AIUnitCount = 0 + + self:E( "Counting Defenders Engaged for Attacker:" ) + local DetectedSet = AttackerDetection.Set + DetectedSet:Flush() + + local DefenderTasks = self:GetDefenderTasks() + for AIGroup, DefenderTask in pairs( DefenderTasks ) do + local AIGroup = AIGroup -- Wrapper.Group#GROUP + local DefenderTask = self:GetDefenderTaskTarget( AIGroup ) + if DefenderTask and DefenderTask.Index == AttackerDetection.Index then + AIUnitCount = AIUnitCount + AIGroup:GetSize() + self:E( "Defender Group Name: " .. AIGroup:GetName() .. ", Size: " .. AIGroup:GetSize() ) + end + end + + return AIUnitCount + end + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:CountDefendersToBeEngaged( AttackerDetection, DefenderCount ) + + local Friendlies = nil + + local AttackerSet = AttackerDetection.Set + local AttackerCount = AttackerSet:Count() + + local DefenderFriendlies = self:GetAIFriendliesNearBy( AttackerDetection ) + + for FriendlyDistance, AIFriendly in UTILS.spairs( DefenderFriendlies or {} ) do + -- We only allow to ENGAGE targets as long as the Units on both sides are balanced. + if AttackerCount > DefenderCount then + local Friendly = AIFriendly:GetGroup() -- Wrapper.Group#GROUP + if Friendly and Friendly:IsAlive() then + -- Ok, so we have a friendly near the potential target. + -- Now we need to check if the AIGroup has a Task. + local DefenderTask = self:GetDefenderTask( Friendly ) + if DefenderTask then + -- The Task should be CAP or GCI + if DefenderTask.Type == "CAP" or DefenderTask.Type == "GCI" then + -- If there is no target, then add the AIGroup to the ResultAIGroups for Engagement to the AttackerSet + if DefenderTask.Target == nil then + if DefenderTask.Fsm:Is( "Returning" ) + or DefenderTask.Fsm:Is( "Patrolling" ) then + Friendlies = Friendlies or {} + Friendlies[Friendly] = Friendly + DefenderCount = DefenderCount + Friendly:GetSize() + self:F( { Friendly = Friendly:GetName(), FriendlyDistance = FriendlyDistance } ) + end + end + end + end + end + else + break + end + end + + return Friendlies + end + + + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:onafterCAP( From, Event, To, SquadronName ) + + self:F({SquadronName = SquadronName}) + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {} + + local DefenderSquadron = self:CanCAP( SquadronName ) + + if DefenderSquadron then + + local Cap = DefenderSquadron.Cap + + if Cap then + + local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ] -- Functional.Spawn#SPAWN + local DefenderGrouping = DefenderSquadron.Grouping or self.DefenderDefault.Grouping + Spawn:InitGrouping( DefenderGrouping ) + + local TakeoffMethod = self:GetSquadronTakeoff( SquadronName ) + local DefenderCAP = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod, DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude ) + self:AddDefenderToSquadron( DefenderSquadron, DefenderCAP, DefenderGrouping ) + + if DefenderCAP then + + local Fsm = AI_A2A_CAP:New( DefenderCAP, Cap.Zone, Cap.FloorAltitude, Cap.CeilingAltitude, Cap.PatrolMinSpeed, Cap.PatrolMaxSpeed, Cap.EngageMinSpeed, Cap.EngageMaxSpeed, Cap.AltType ) + Fsm:SetDispatcher( self ) + Fsm:SetHomeAirbase( DefenderSquadron.Airbase ) + Fsm:SetFuelThreshold( DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold, 60 ) + Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold ) + Fsm:SetDisengageRadius( self.DisengageRadius ) + Fsm:SetTanker( DefenderSquadron.TankerName or self.DefenderDefault.TankerName ) + Fsm:Start() + Fsm:__Patrol( 2 ) + + self:SetDefenderTask( SquadronName, DefenderCAP, "CAP", Fsm ) + + function Fsm:onafterRTB( Defender, From, Event, To ) + self:F({"CAP RTB", Defender:GetName()}) + self:GetParent(self).onafterRTB( self, Defender, From, Event, To ) + local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER + Dispatcher:ClearDefenderTaskTarget( Defender ) + end + + --- @param #AI_A2A_DISPATCHER self + function Fsm:onafterHome( Defender, From, Event, To, Action ) + self:E({"CAP Home", Defender:GetName()}) + self:GetParent(self).onafterHome( self, Defender, From, Event, To ) + + local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER + local Squadron = Dispatcher:GetSquadronFromDefender( Defender ) + + if Action and Action == "Destroy" then + Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender ) + Defender:Destroy() + end + + if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2A_DISPATCHER.Landing.NearAirbase then + Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender ) + Defender:Destroy() + end + end + + end + end + end + + end + + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:onafterENGAGE( From, Event, To, AttackerDetection, Defenders ) + + if Defenders then + + for DefenderID, Defender in pairs( Defenders ) do + + local Fsm = self:GetDefenderTaskFsm( Defender ) + Fsm:__Engage( 1, AttackerDetection.Set ) -- Engage on the TargetSetUnit + + self:SetDefenderTaskTarget( Defender, AttackerDetection ) + + end + end + end + + --- + -- @param #AI_A2A_DISPATCHER self + function AI_A2A_DISPATCHER:onafterGCI( From, Event, To, AttackerDetection, DefendersMissing, DefenderFriendlies ) + + self:F( { From, Event, To, AttackerDetection.Index, DefendersMissing, DefenderFriendlies } ) + + local AttackerSet = AttackerDetection.Set + local AttackerUnit = AttackerSet:GetFirst() + + if AttackerUnit and AttackerUnit:IsAlive() then + local AttackerCount = AttackerSet:Count() + local DefenderCount = 0 + + for DefenderID, DefenderGroup in pairs( DefenderFriendlies or {} ) do + + local Fsm = self:GetDefenderTaskFsm( DefenderGroup ) + Fsm:__Engage( 1, AttackerSet ) -- Engage on the TargetSetUnit + + self:SetDefenderTaskTarget( DefenderGroup, AttackerDetection ) + + DefenderCount = DefenderCount + DefenderGroup:GetSize() + end + + self:F( { DefenderCount = DefenderCount, DefendersMissing = DefendersMissing } ) + DefenderCount = DefendersMissing + + local ClosestDistance = 0 + local ClosestDefenderSquadronName = nil + + local BreakLoop = false + + while( DefenderCount > 0 and not BreakLoop ) do + + self:F( { DefenderSquadrons = self.DefenderSquadrons } ) + + for SquadronName, DefenderSquadron in pairs( self.DefenderSquadrons or {} ) do + + self:F( { GCI = DefenderSquadron.Gci } ) + + for InterceptID, Intercept in pairs( DefenderSquadron.Gci or {} ) do + + self:F( { DefenderSquadron } ) + local SpawnCoord = DefenderSquadron.Airbase:GetCoordinate() -- Core.Point#COORDINATE + local AttackerCoord = AttackerUnit:GetCoordinate() + local InterceptCoord = AttackerDetection.InterceptCoord + self:F({InterceptCoord = InterceptCoord}) + if InterceptCoord then + local InterceptDistance = SpawnCoord:Get2DDistance( InterceptCoord ) + local AirbaseDistance = SpawnCoord:Get2DDistance( AttackerCoord ) + self:F( { InterceptDistance = InterceptDistance, AirbaseDistance = AirbaseDistance, InterceptCoord = InterceptCoord } ) + + if ClosestDistance == 0 or InterceptDistance < ClosestDistance then + + -- Only intercept if the distance to target is smaller or equal to the GciRadius limit. + if AirbaseDistance <= self.GciRadius then + ClosestDistance = InterceptDistance + ClosestDefenderSquadronName = SquadronName + end + end + end + end + end + + if ClosestDefenderSquadronName then + + local DefenderSquadron = self:CanGCI( ClosestDefenderSquadronName ) + + if DefenderSquadron then + + local Gci = self.DefenderSquadrons[ClosestDefenderSquadronName].Gci + + if Gci then + + local DefenderOverhead = DefenderSquadron.Overhead or self.DefenderDefault.Overhead + local DefenderGrouping = DefenderSquadron.Grouping or self.DefenderDefault.Grouping + local DefendersNeeded = math.ceil( DefenderCount * DefenderOverhead ) + + self:F( { Overhead = DefenderOverhead, SquadronOverhead = DefenderSquadron.Overhead , DefaultOverhead = self.DefenderDefault.Overhead } ) + self:F( { Grouping = DefenderGrouping, SquadronGrouping = DefenderSquadron.Grouping, DefaultGrouping = self.DefenderDefault.Grouping } ) + self:F( { DefendersCount = DefenderCount, DefendersNeeded = DefendersNeeded } ) + + while ( DefendersNeeded > 0 ) do + + local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ] -- Functional.Spawn#SPAWN + local DefenderGrouping = ( DefenderGrouping < DefendersNeeded ) and DefenderGrouping or DefendersNeeded + if DefenderGrouping then + Spawn:InitGrouping( DefenderGrouping ) + else + Spawn:InitGrouping() + end + + local TakeoffMethod = self:GetSquadronTakeoff( ClosestDefenderSquadronName ) + local DefenderGCI = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod, DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude ) -- Wrapper.Group#GROUP + self:F( { GCIDefender = DefenderGCI:GetName() } ) + + DefendersNeeded = DefendersNeeded - DefenderGrouping + + self:AddDefenderToSquadron( DefenderSquadron, DefenderGCI, DefenderGrouping ) + + if DefenderGCI then + + DefenderCount = DefenderCount - DefenderGrouping + + local Fsm = AI_A2A_GCI:New( DefenderGCI, Gci.EngageMinSpeed, Gci.EngageMaxSpeed ) + Fsm:SetDispatcher( self ) + Fsm:SetHomeAirbase( DefenderSquadron.Airbase ) + Fsm:SetFuelThreshold( DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold, 60 ) + Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold ) + Fsm:SetDisengageRadius( self.DisengageRadius ) + Fsm:Start() + Fsm:__Engage( 2, AttackerDetection.Set ) -- Engage on the TargetSetUnit + + + self:SetDefenderTask( ClosestDefenderSquadronName, DefenderGCI, "GCI", Fsm, AttackerDetection ) + + + function Fsm:onafterRTB( Defender, From, Event, To ) + self:F({"GCI RTB", Defender:GetName()}) + self:GetParent(self).onafterRTB( self, Defender, From, Event, To ) + + local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER + Dispatcher:ClearDefenderTaskTarget( Defender ) + end + + --- @param #AI_A2A_DISPATCHER self + function Fsm:onafterLostControl( Defender, From, Event, To ) + self:F({"GCI LostControl", Defender:GetName()}) + self:GetParent(self).onafterHome( self, Defender, From, Event, To ) + + local Dispatcher = Fsm:GetDispatcher() -- #AI_A2A_DISPATCHER + local Squadron = Dispatcher:GetSquadronFromDefender( Defender ) + if Defender:IsAboveRunway() then + Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender ) + Defender:Destroy() + end + end + + --- @param #AI_A2A_DISPATCHER self + function Fsm:onafterHome( Defender, From, Event, To, Action ) + self:F({"GCI Home", Defender:GetName()}) + self:GetParent(self).onafterHome( self, Defender, From, Event, To ) + + local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER + local Squadron = Dispatcher:GetSquadronFromDefender( Defender ) + + if Action and Action == "Destroy" then + Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender ) + Defender:Destroy() + end + + if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2A_DISPATCHER.Landing.NearAirbase then + Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender ) + Defender:Destroy() + end + end + end -- if DefenderGCI then + end -- while ( DefendersNeeded > 0 ) do + end + else + -- No more resources, try something else. + -- Subject for a later enhancement to try to depart from another squadron and disable this one. + BreakLoop = true + break + end + else + -- There isn't any closest airbase anymore, break the loop. + break + end + end -- if DefenderSquadron then + end -- if AttackerUnit + end + + + + --- Creates an ENGAGE task when there are human friendlies airborne near the targets. + -- @param #AI_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem + -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. + function AI_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + -- First, count the active AIGroups Units, targetting the DetectedSet + local DefenderCount = self:CountDefendersEngaged( DetectedItem ) + local DefenderGroups = self:CountDefendersToBeEngaged( DetectedItem, DefenderCount ) + + self:F( { DefenderCount = DefenderCount } ) + + -- Only allow ENGAGE when: + -- 1. There are friendly units near the detected attackers. + -- 2. There is sufficient fuel + -- 3. There is sufficient ammo + -- 4. The plane is not damaged + if DefenderGroups and DetectedItem.IsDetected == true then + + return DefenderGroups + end + + return nil, nil + end + + --- Creates an GCI task when there are targets for it. + -- @param #AI_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem + -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. + function AI_A2A_DISPATCHER:EvaluateGCI( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + local AttackerSet = DetectedItem.Set + local AttackerCount = AttackerSet:Count() + + -- First, count the active AIGroups Units, targetting the DetectedSet + local DefenderCount = self:CountDefendersEngaged( DetectedItem ) + local DefendersMissing = AttackerCount - DefenderCount + self:F( { AttackerCount = AttackerCount, DefenderCount = DefenderCount, DefendersMissing = DefendersMissing } ) + + local Friendlies = self:CountDefendersToBeEngaged( DetectedItem, DefenderCount ) + + if DetectedItem.IsDetected == true then + + return DefendersMissing, Friendlies + end + + return nil, nil + end + + + --- Assigns A2A AI Tasks in relation to the detected items. + -- @param #AI_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. + -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. + function AI_A2A_DISPATCHER:ProcessDetected( Detection ) + + local AreaMsg = {} + local TaskMsg = {} + local ChangeMsg = {} + + local TaskReport = REPORT:New() + + + for AIGroup, DefenderTask in pairs( self:GetDefenderTasks() ) do + local AIGroup = AIGroup -- Wrapper.Group#GROUP + if not AIGroup:IsAlive() then + self:ClearDefenderTask( AIGroup ) + else + if DefenderTask.Target then + local AttackerItem = Detection:GetDetectedItem( DefenderTask.Target.Index ) + if not AttackerItem then + self:F( { "Removing obsolete Target:", DefenderTask.Target.Index } ) + self:ClearDefenderTaskTarget( AIGroup ) + else + if DefenderTask.Target.Set then + local AttackerCount = DefenderTask.Target.Set:Count() + if AttackerCount == 0 then + self:F( { "All Targets destroyed in Target, removing:", DefenderTask.Target.Index } ) + self:ClearDefenderTaskTarget( AIGroup ) + end + end + end + end + end + end + + local Report = REPORT:New( "\nTactical Overview" ) + + -- Now that all obsolete tasks are removed, loop through the detected targets. + for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do + + local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedCount = DetectedSet:Count() + local DetectedZone = DetectedItem.Zone + + self:F( { "Target ID", DetectedItem.ItemID } ) + DetectedSet:Flush() + + local DetectedID = DetectedItem.ID + local DetectionIndex = DetectedItem.Index + local DetectedItemChanged = DetectedItem.Changed + + do + local Friendlies = self:EvaluateENGAGE( DetectedItem ) -- Returns a SetUnit if there are targets to be GCIed... + if Friendlies then + self:F( { AIGroups = Friendlies } ) + self:ENGAGE( DetectedItem, Friendlies ) + end + end + + do + local DefendersMissing, Friendlies = self:EvaluateGCI( DetectedItem ) + if DefendersMissing then + self:F( { DefendersMissing = DefendersMissing } ) + self:GCI( DetectedItem, DefendersMissing, Friendlies ) + end + end + + if self.TacticalDisplay then + -- Show tactical situation + Report:Add( string.format( "\n - Target %s ( %s ): ( #%d ) %s" , DetectedItem.ItemID, DetectedItem.Index, DetectedItem.Set:Count(), DetectedItem.Set:GetObjectNames() ) ) + for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do + local Defender = Defender -- Wrapper.Group#GROUP + if DefenderTask.Target and DefenderTask.Target.Index == DetectedItem.Index then + local Fuel = Defender:GetFuel() * 100 + local Damage = Defender:GetLife() / Defender:GetLife0() * 100 + Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s", + Defender:GetName(), + DefenderTask.Type, + DefenderTask.Fsm:GetState(), + Defender:GetSize(), + Fuel, + Damage, + Defender:HasTask() == true and "Executing" or "Idle" ) ) + end + end + end + end + + if self.TacticalDisplay then + Report:Add( "\n - No Targets:") + local TaskCount = 0 + for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do + TaskCount = TaskCount + 1 + local Defender = Defender -- Wrapper.Group#GROUP + if not DefenderTask.Target then + local DefenderHasTask = Defender:HasTask() + local Fuel = Defender:GetFuel() * 100 + local Damage = Defender:GetLife() / Defender:GetLife0() * 100 + Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s", + Defender:GetName(), + DefenderTask.Type, + DefenderTask.Fsm:GetState(), + Defender:GetSize(), + Fuel, + Damage, + Defender:HasTask() == true and "Executing" or "Idle" ) ) + end + end + Report:Add( string.format( "\n - %d Tasks", TaskCount ) ) + + self:E( Report:Text( "\n" ) ) + trigger.action.outText( Report:Text( "\n" ), 25 ) + end + + return true + end + +end + +do + + --- Calculates which HUMAN friendlies are nearby the area + -- @param #AI_A2A_DISPATCHER self + -- @param DetectedItem + -- @return #number, Core.CommandCenter#REPORT + function AI_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem ) + + local DetectedSet = DetectedItem.Set + local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem ) + + local PlayerTypes = {} + local PlayersCount = 0 + + if PlayersNearBy then + local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G() + for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do + local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT + local PlayerName = PlayerUnit:GetPlayerName() + --self:E( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } ) + if PlayerUnit:IsAirPlane() and PlayerName ~= nil then + local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel() + PlayersCount = PlayersCount + 1 + local PlayerType = PlayerUnit:GetTypeName() + PlayerTypes[PlayerName] = PlayerType + if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then + end + end + end + + end + + --self:E( { PlayersCount = PlayersCount } ) + + local PlayerTypesReport = REPORT:New() + + if PlayersCount > 0 then + for PlayerName, PlayerType in pairs( PlayerTypes ) do + PlayerTypesReport:Add( string.format('"%s" in %s', PlayerName, PlayerType ) ) + end + else + PlayerTypesReport:Add( "-" ) + end + + + return PlayersCount, PlayerTypesReport + end + + --- Calculates which friendlies are nearby the area + -- @param #AI_A2A_DISPATCHER self + -- @param DetectedItem + -- @return #number, Core.CommandCenter#REPORT + function AI_A2A_DISPATCHER:GetFriendliesNearBy( Target ) + + local DetectedSet = Target.Set + local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( Target ) + + local FriendlyTypes = {} + local FriendliesCount = 0 + + if FriendlyUnitsNearBy then + local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G() + for FriendlyUnitName, FriendlyUnitData in pairs( FriendlyUnitsNearBy ) do + local FriendlyUnit = FriendlyUnitData -- Wrapper.Unit#UNIT + if FriendlyUnit:IsAirPlane() then + local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel() + FriendliesCount = FriendliesCount + 1 + local FriendlyType = FriendlyUnit:GetTypeName() + FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and ( FriendlyTypes[FriendlyType] + 1 ) or 1 + if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then + end + end + end + + end + + --self:E( { FriendliesCount = FriendliesCount } ) + + local FriendlyTypesReport = REPORT:New() + + if FriendliesCount > 0 then + for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do + FriendlyTypesReport:Add( string.format("%d of %s", FriendlyTypeCount, FriendlyType ) ) + end + else + FriendlyTypesReport:Add( "-" ) + end + + + return FriendliesCount, FriendlyTypesReport + end + + --- + -- @param AI_A2A_DISPATCHER + -- @param #string SquadronName The squadron name. + function AI_A2A_DISPATCHER:SchedulerCAP( SquadronName ) + self:CAP( SquadronName ) + end + +end + +do + + --- @type AI_A2A_GCICAP + -- @extends #AI_A2A_DISPATCHER + + --- # AI\_A2A\_GCICAP class, extends @{AI_A2A_Dispatcher#AI_A2A_DISPATCHER} + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia1.JPG) + -- + -- The AI_A2A_GCICAP class is designed to create an automatic air defence system for a coalition setting up GCI and CAP air defenses. + -- The class derives from @{AI#AI_A2A_DISPATCHER} and thus, all the methods that are defined in the @{AI#AI_A2A_DISPATCHER} class, can be used also in AI\_A2A\_GCICAP. + -- + -- ==== + -- + -- # Demo Missions + -- + -- ### [AI\_A2A\_GCICAP for Caucasus](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-200%20-%20AI_A2A%20-%20GCICAP%20Demonstration) + -- ### [AI\_A2A\_GCICAP for NTTR](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-210%20-%20NTTR%20AI_A2A_GCICAP%20Demonstration) + -- ### [AI\_A2A\_GCICAP for Normandy](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-220%20-%20NORMANDY%20AI_A2A_GCICAP%20Demonstration) + -- + -- ### [AI\_A2A\_GCICAP for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching) + -- + -- ==== + -- + -- # YouTube Channel + -- + -- ### [DCS WORLD - MOOSE - A2A GCICAP - Build an automatic A2A Defense System](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0S4KMNUUJpaUs6zZHjLKNx) + -- + -- === + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia3.JPG) + -- + -- AI\_A2A\_GCICAP includes automatic spawning of Combat Air Patrol aircraft (CAP) and Ground Controlled Intercept aircraft (GCI) in response to enemy + -- air movements that are detected by an airborne or ground based radar network. + -- + -- With a little time and with a little work it provides the mission designer with a convincing and completely automatic air defence system. + -- + -- The AI_A2A_GCICAP provides a lightweight configuration method using the mission editor. Within a very short time, and with very little coding, + -- the mission designer is able to configure a complete A2A defense system for a coalition using the DCS Mission Editor available functions. + -- Using the DCS Mission Editor, you define borders of the coalition which are guarded by GCICAP, + -- configure airbases to belong to the coalition, define squadrons flying certain types of planes or payloads per airbase, and define CAP zones. + -- **Very little lua needs to be applied, a one liner**, which is fully explained below, which can be embedded + -- right in a DO SCRIPT trigger action or in a larger DO SCRIPT FILE trigger action. + -- + -- CAP flights will take off and proceed to designated CAP zones where they will remain on station until the ground radars direct them to intercept + -- detected enemy aircraft or they run short of fuel and must return to base (RTB). + -- + -- When a CAP flight leaves their zone to perform a GCI or return to base a new CAP flight will spawn to take its place. + -- If all CAP flights are engaged or RTB then additional GCI interceptors will scramble to intercept unengaged enemy aircraft under ground radar control. + -- + -- In short it is a plug in very flexible and configurable air defence module for DCS World. + -- + -- ==== + -- + -- # The following actions need to be followed when using AI\_A2A\_GCICAP in your mission: + -- + -- ## 1) Configure a working AI\_A2A\_GCICAP defense system for ONE coalition. + -- + -- ### 1.1) Define which airbases are for which coalition. + -- + -- ![Mission Editor Action](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_GCICAP-ME_1.JPG) + -- + -- Color the airbases red or blue. You can do this by selecting the airbase on the map, and select the coalition blue or red. + -- + -- ### 1.2) Place groups of units given a name starting with a **EWR prefix** of your choice to build your EWR network. + -- + -- ![Mission Editor Action](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_GCICAP-ME_2.JPG) + -- + -- **All EWR groups starting with the EWR prefix (text) will be included in the detection system.** + -- + -- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy. + -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. + -- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). + -- Additionally, ANY other radar capable unit can be part of the EWR network! + -- Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. + -- The position of these units is very important as they need to provide enough coverage + -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. + -- + -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. + -- For example if they are a long way forward and can detect enemy planes on the ground and taking off + -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. + -- Having the radars further back will mean a slower escalation because fewer targets will be detected and + -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. + -- It all depends on what the desired effect is. + -- + -- EWR networks are **dynamically maintained**. By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, + -- increasing or decreasing the radar coverage of the Early Warning System. + -- + -- ### 1.3) Place Airplane or Helicopter Groups with late activation switched on above the airbases to define Squadrons. + -- + -- ![Mission Editor Action](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_GCICAP-ME_3.JPG) + -- + -- These are **templates**, with a given name starting with a **Template prefix** above each airbase that you wanna have a squadron. + -- These **templates** need to be within 1.5km from the airbase center. They don't need to have a slot at the airplane, they can just be positioned above the airbase, + -- without a route, and should only have ONE unit. + -- + -- ![Mission Editor Action](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_GCICAP-ME_4.JPG) + -- + -- **All airplane or helicopter groups that are starting with any of the choosen Template Prefixes will result in a squadron created at the airbase.** + -- + -- ### 1.4) Place floating helicopters to create the CAP zones defined by its route points. + -- + -- ![Mission Editor Action](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_GCICAP-ME_5.JPG) + -- + -- **All airplane or helicopter groups that are starting with any of the choosen Template Prefixes will result in a squadron created at the airbase.** + -- + -- The helicopter indicates the start of the CAP zone. + -- The route points define the form of the CAP zone polygon. + -- + -- ![Mission Editor Action](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_GCICAP-ME_6.JPG) + -- + -- **The place of the helicopter is important, as the airbase closest to the helicopter will be the airbase from where the CAP planes will take off for CAP.** + -- + -- ## 2) There are a lot of defaults set, which can be further modified using the methods in @{AI#AI_A2A_DISPATCHER}: + -- + -- ### 2.1) Planes are taking off in the air from the airbases. + -- + -- This prevents airbases to get cluttered with airplanes taking off, it also reduces the risk of human players colliding with taxiiing airplanes, + -- resulting in the airbase to halt operations. + -- + -- You can change the way how planes take off by using the inherited methods from AI\_A2A\_DISPATCHER: + -- + -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoff}() is the generic configuration method to control takeoff from the air, hot, cold or from the runway. See the method for further details. + -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffInAir}() will spawn new aircraft from the squadron directly in the air. + -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromParkingCold}() will spawn new aircraft in without running engines at a parking spot at the airfield. + -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromParkingHot}() will spawn new aircraft in with running engines at a parking spot at the airfield. + -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromRunway}() will spawn new aircraft at the runway at the airfield. + -- + -- Use these methods to fine-tune for specific airfields that are known to create bottlenecks, or have reduced airbase efficiency. + -- The more and the longer aircraft need to taxi at an airfield, the more risk there is that: + -- + -- * aircraft will stop waiting for each other or for a landing aircraft before takeoff. + -- * aircraft may get into a "dead-lock" situation, where two aircraft are blocking each other. + -- * aircraft may collide at the airbase. + -- * aircraft may be awaiting the landing of a plane currently in the air, but never lands ... + -- + -- Currently within the DCS engine, the airfield traffic coordination is erroneous and contains a lot of bugs. + -- If you experience while testing problems with aircraft take-off or landing, please use one of the above methods as a solution to workaround these issues! + -- + -- ### 2.2) Planes return near the airbase or will land if damaged. + -- + -- When damaged airplanes return to the airbase, they will be routed and will dissapear in the air when they are near the airbase. + -- There are exceptions to this rule, airplanes that aren't "listening" anymore due to damage or out of fuel, will return to the airbase and land. + -- + -- You can change the way how planes land by using the inherited methods from AI\_A2A\_DISPATCHER: + -- + -- * @{#AI_A2A_DISPATCHER.SetSquadronLanding}() is the generic configuration method to control landing, namely despawn the aircraft near the airfield in the air, right after landing, or at engine shutdown. + -- * @{#AI_A2A_DISPATCHER.SetSquadronLandingNearAirbase}() will despawn the returning aircraft in the air when near the airfield. + -- * @{#AI_A2A_DISPATCHER.SetSquadronLandingAtRunway}() will despawn the returning aircraft directly after landing at the runway. + -- * @{#AI_A2A_DISPATCHER.SetSquadronLandingAtEngineShutdown}() will despawn the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines. + -- + -- You can use these methods to minimize the airbase coodination overhead and to increase the airbase efficiency. + -- When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the + -- A2A defense system, as no new CAP or GCI planes can takeoff. + -- Note that the method @{#AI_A2A_DISPATCHER.SetSquadronLandingNearAirbase}() will only work for returning aircraft, not for damaged or out of fuel aircraft. + -- Damaged or out-of-fuel aircraft are returning to the nearest friendly airbase and will land, and are out of control from ground control. + -- + -- ### 2.3) CAP operations setup for specific airbases, will be executed with the following parameters: + -- + -- * The altitude will range between 6000 and 10000 meters. + -- * The CAP speed will vary between 500 and 800 km/h. + -- * The engage speed between 800 and 1200 km/h. + -- + -- You can change or add a CAP zone by using the inherited methods from AI\_A2A\_DISPATCHER: + -- + -- The method @{#AI_A2A_DISPATCHER.SetSquadronCap}() defines a CAP execution for a squadron. + -- + -- Setting-up a CAP zone also requires specific parameters: + -- + -- * The minimum and maximum altitude + -- * The minimum speed and maximum patrol speed + -- * The minimum and maximum engage speed + -- * The type of altitude measurement + -- + -- These define how the squadron will perform the CAP while partrolling. Different terrain types requires different types of CAP. + -- + -- The @{#AI_A2A_DISPATCHER.SetSquadronCapInterval}() method specifies **how much** and **when** CAP flights will takeoff. + -- + -- It is recommended not to overload the air defense with CAP flights, as these will decrease the performance of the overall system. + -- + -- For example, the following setup will create a CAP for squadron "Sochi": + -- + -- A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" ) + -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) + -- + -- ### 2.4) Each airbase will perform GCI when required, with the following parameters: + -- + -- * The engage speed is between 800 and 1200 km/h. + -- + -- You can change or add a GCI parameters by using the inherited methods from AI\_A2A\_DISPATCHER: + -- + -- The method @{#AI_A2A_DISPATCHER.SetSquadronGci}() defines a GCI execution for a squadron. + -- + -- Setting-up a GCI readiness also requires specific parameters: + -- + -- * The minimum speed and maximum patrol speed + -- + -- Essentially this controls how many flights of GCI aircraft can be active at any time. + -- Note allowing large numbers of active GCI flights can adversely impact mission performance on low or medium specification hosts/servers. + -- GCI needs to be setup at strategic airbases. Too far will mean that the aircraft need to fly a long way to reach the intruders, + -- too short will mean that the intruders may have alraedy passed the ideal interception point! + -- + -- For example, the following setup will create a GCI for squadron "Sochi": + -- + -- A2ADispatcher:SetSquadronGci( "Mozdok", 900, 1200 ) + -- + -- ### 2.5) Grouping or detected targets. + -- + -- Detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate + -- group being detected. + -- + -- Targets will be grouped within a radius of 30km by default. + -- + -- The radius indicates that detected targets need to be grouped within a radius of 30km. + -- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. + -- Fast planes like in the 80s, need a larger radius than WWII planes. + -- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft. + -- + -- ## 3) Additional notes: + -- + -- In order to create a two way A2A defense system, **two AI\_A2A\_GCICAP defense systems must need to be created**, for each coalition one. + -- Each defense system needs its own EWR network setup, airplane templates and CAP configurations. + -- + -- This is a good implementation, because maybe in the future, more coalitions may become available in DCS world. + -- + -- ## 4) Coding examples how to use the AI\_A2A\_GCICAP class: + -- + -- ### 4.1) An easy setup: + -- + -- -- Setup the AI_A2A_GCICAP dispatcher for one coalition, and initialize it. + -- GCI_Red = AI_A2A_GCICAP:New( "EWR CCCP", "SQUADRON CCCP", "CAP CCCP", 2 ) + -- -- + -- The following parameters were given to the :New method of AI_A2A_GCICAP, and mean the following: + -- + -- * `"EWR CCCP"`: Groups of the blue coalition are placed that define the EWR network. These groups start with the name `EWR CCCP`. + -- * `"SQUADRON CCCP"`: Late activated Groups objects of the red coalition are placed above the relevant airbases that will contain these templates in the squadron. + -- These late activated Groups start with the name `SQUADRON CCCP`. Each Group object contains only one Unit, and defines the weapon payload, skin and skill level. + -- * `"CAP CCCP"`: CAP Zones are defined using floating, late activated Helicopter Group objects, where the route points define the route of the polygon of the CAP Zone. + -- These Helicopter Group objects start with the name `CAP CCCP`, and will be the locations wherein CAP will be performed. + -- * `2` Defines how many CAP airplanes are patrolling in each CAP zone defined simulateneously. + -- + -- + -- ### 4.2) A more advanced setup: + -- + -- -- Setup the AI_A2A_GCICAP dispatcher for the blue coalition. + -- + -- A2A_GCICAP_Blue = AI_A2A_GCICAP:New( { "BLUE EWR" }, { "104th", "105th", "106th" }, { "104th CAP" }, 4 ) + -- + -- The following parameters for the :New method have the following meaning: + -- + -- * `{ "BLUE EWR" }`: An array of the group name prefixes of the groups of the blue coalition are placed that define the EWR network. These groups start with the name `BLUE EWR`. + -- * `{ "104th", "105th", "106th" } `: An array of the group name prefixes of the Late activated Groups objects of the blue coalition are + -- placed above the relevant airbases that will contain these templates in the squadron. + -- These late activated Groups start with the name `104th` or `105th` or `106th`. + -- * `{ "104th CAP" }`: An array of the names of the CAP zones are defined using floating, late activated helicopter group objects, + -- where the route points define the route of the polygon of the CAP Zone. + -- These Helicopter Group objects start with the name `104th CAP`, and will be the locations wherein CAP will be performed. + -- * `4` Defines how many CAP airplanes are patrolling in each CAP zone defined simulateneously. + -- + -- @field #AI_A2A_GCICAP + AI_A2A_GCICAP = { + ClassName = "AI_A2A_GCICAP", + Detection = nil, + } + + + --- AI_A2A_GCICAP constructor. + -- @param #AI_A2A_GCICAP self + -- @param #string EWRPrefixes A list of prefixes that of groups that setup the Early Warning Radar network. + -- @param #string TemplatePrefixes A list of template prefixes. + -- @param #string CapPrefixes A list of CAP zone prefixes (polygon zones). + -- @param #number CapLimit A number of how many CAP maximum will be spawned. + -- @param #number GroupingRadius The radius in meters wherein detected planes are being grouped as one target area. + -- For airplanes, 6000 (6km) is recommended, and is also the default value of this parameter. + -- @param #number EngageRadius The radius in meters wherein detected airplanes will be engaged by airborne defenders without a task. + -- @param #number GciRadius The radius in meters wherein detected airplanes will GCI. + -- @param #number Resources The amount of resources that will be allocated to each squadron. + -- @return #AI_A2A_GCICAP + -- @usage + -- + -- -- Setup a new GCICAP dispatcher object. Each squadron has unlimited resources. + -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. + -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. + -- -- The CAP Zone prefix is "CAP Zone". + -- -- The CAP Limit is 2. + -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2 ) + -- + -- @usage + -- + -- -- Setup a new GCICAP dispatcher object. Each squadron has unlimited resources. + -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. + -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. + -- -- The CAP Zone prefix is "CAP Zone". + -- -- The CAP Limit is 2. + -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. + -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000 ) + -- + -- @usage + -- + -- -- Setup a new GCICAP dispatcher object. Each squadron has unlimited resources. + -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. + -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. + -- -- The CAP Zone prefix is "CAP Zone". + -- -- The CAP Limit is 2. + -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. + -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, + -- -- will be considered a defense task if the target is within 60km from the defender. + -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000, 60000 ) + -- + -- @usage + -- + -- -- Setup a new GCICAP dispatcher object. Each squadron has unlimited resources. + -- -- The EWR network group prefix is DF CCCP. All groups starting with DF CCCP will be part of the EWR network. + -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. + -- -- The CAP Zone prefix is "CAP Zone". + -- -- The CAP Limit is 2. + -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. + -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, + -- -- will be considered a defense task if the target is within 60km from the defender. + -- -- The GCI Radius is set to 150000. Any target detected within 150km will be considered for GCI engagement. + -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000, 60000, 150000 ) + -- + -- @usage + -- + -- -- Setup a new GCICAP dispatcher object. Each squadron has 30 resources. + -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. + -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. + -- -- The CAP Zone prefix is "CAP Zone". + -- -- The CAP Limit is 2. + -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. + -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, + -- -- will be considered a defense task if the target is within 60km from the defender. + -- -- The GCI Radius is set to 150000. Any target detected within 150km will be considered for GCI engagement. + -- -- The amount of resources for each squadron is set to 30. Thus about 30 resources are allocated to each squadron created. + -- + -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000, 60000, 150000, 30 ) + -- + -- @usage + -- + -- -- Setup a new GCICAP dispatcher object. Each squadron has 30 resources. + -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. + -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. + -- -- The CAP Zone prefix is nil. No CAP is created. + -- -- The CAP Limit is nil. + -- -- The Grouping Radius is nil. The default range of 6km radius will be grouped as a group of targets. + -- -- The Engage Radius is set nil. The default Engage Radius will be used to consider a defenser being assigned to a task. + -- -- The GCI Radius is nil. Any target detected within the default GCI Radius will be considered for GCI engagement. + -- -- The amount of resources for each squadron is set to 30. Thus about 30 resources are allocated to each squadron created. + -- + -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, nil, nil, nil, nil, nil, 30 ) + -- + function AI_A2A_GCICAP:New( EWRPrefixes, TemplatePrefixes, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, Resources ) + + local EWRSetGroup = SET_GROUP:New() + EWRSetGroup:FilterPrefixes( EWRPrefixes ) + EWRSetGroup:FilterStart() + + local Detection = DETECTION_AREAS:New( EWRSetGroup, GroupingRadius or 30000 ) + + local self = BASE:Inherit( self, AI_A2A_DISPATCHER:New( Detection ) ) -- #AI_A2A_GCICAP + + self:SetEngageRadius( EngageRadius ) + self:SetGciRadius( GciRadius ) + + -- Determine the coalition of the EWRNetwork, this will be the coalition of the GCICAP. + local EWRFirst = EWRSetGroup:GetFirst() -- Wrapper.Group#GROUP + local EWRCoalition = EWRFirst:GetCoalition() + + -- Determine the airbases belonging to the coalition. + local AirbaseNames = {} -- #list<#string> + for AirbaseID, AirbaseData in pairs( _DATABASE.AIRBASES ) do + local Airbase = AirbaseData -- Wrapper.Airbase#AIRBASE + local AirbaseName = Airbase:GetName() + if Airbase:GetCoalition() == EWRCoalition then + table.insert( AirbaseNames, AirbaseName ) + end + end + + self.Templates = SET_GROUP + :New() + :FilterPrefixes( TemplatePrefixes ) + :FilterOnce() + + -- Setup squadrons + + self:F( { Airbases = AirbaseNames } ) + self.Templates:Flush() + + for AirbaseID, AirbaseName in pairs( AirbaseNames ) do + local Airbase = _DATABASE:FindAirbase( AirbaseName ) -- Wrapper.Airbase#AIRBASE + local AirbaseName = Airbase:GetName() + local AirbaseCoord = Airbase:GetCoordinate() + local AirbaseZone = ZONE_RADIUS:New( "Airbase", AirbaseCoord:GetVec2(), 3000 ) + local Templates = nil + for TemplateID, Template in pairs( self.Templates:GetSet() ) do + local Template = Template -- Wrapper.Group#GROUP + self:F( { Template = Template:GetName() } ) + local TemplateCoord = Template:GetCoordinate() + if AirbaseZone:IsVec2InZone( TemplateCoord:GetVec2() ) then + Templates = Templates or {} + table.insert( Templates, Template:GetName() ) + end + end + if Templates then + self:SetSquadron( AirbaseName, AirbaseName, Templates, Resources ) + end + end + + -- Setup CAP. + -- Find for each CAP the nearest airbase to the (start or center) of the zone. + -- CAP will be launched from there. + + self.CAPTemplates = SET_GROUP:New() + self.CAPTemplates:FilterPrefixes( CapPrefixes ) + self.CAPTemplates:FilterOnce() + + for CAPID, CAPTemplate in pairs( self.CAPTemplates:GetSet() ) do + local CAPZone = ZONE_POLYGON:New( CAPTemplate:GetName(), CAPTemplate ) + -- Now find the closest airbase from the ZONE (start or center) + local AirbaseDistance = 99999999 + local AirbaseClosest = nil -- Wrapper.Airbase#AIRBASE + for AirbaseID, AirbaseName in pairs( AirbaseNames ) do + local Airbase = _DATABASE:FindAirbase( AirbaseName ) -- Wrapper.Airbase#AIRBASE + local AirbaseName = Airbase:GetName() + local AirbaseCoord = Airbase:GetCoordinate() + local Squadron = self.DefenderSquadrons[AirbaseName] + if Squadron then + local Distance = AirbaseCoord:Get2DDistance( CAPZone:GetCoordinate() ) + if Distance < AirbaseDistance then + AirbaseDistance = Distance + AirbaseClosest = Airbase + end + end + end + if AirbaseClosest then + self:SetSquadronCap( AirbaseClosest:GetName(), CAPZone, 6000, 10000, 500, 800, 800, 1200, "RADIO" ) + self:SetSquadronCapInterval( AirbaseClosest:GetName(), CapLimit, 300, 600, 1 ) + end + end + + -- Setup GCI. + -- GCI is setup for all Squadrons. + for AirbaseID, AirbaseName in pairs( AirbaseNames ) do + local Airbase = _DATABASE:FindAirbase( AirbaseName ) -- Wrapper.Airbase#AIRBASE + local AirbaseName = Airbase:GetName() + local Squadron = self.DefenderSquadrons[AirbaseName] + if Squadron then + self:SetSquadronGci( AirbaseName, 800, 1200 ) + end + end + + self:__Start( 5 ) + + self:HandleEvent( EVENTS.Crash, self.OnEventCrashOrDead ) + self:HandleEvent( EVENTS.Dead, self.OnEventCrashOrDead ) + + self:HandleEvent( EVENTS.Land ) + self:HandleEvent( EVENTS.EngineShutdown ) + + return self + end + + --- AI_A2A_GCICAP constructor with border. + -- @param #AI_A2A_GCICAP self + -- @param #string EWRPrefixes A list of prefixes that of groups that setup the Early Warning Radar network. + -- @param #string TemplatePrefixes A list of template prefixes. + -- @param #string BorderPrefix A Border Zone Prefix. + -- @param #string CapPrefixes A list of CAP zone prefixes (polygon zones). + -- @param #number CapLimit A number of how many CAP maximum will be spawned. + -- @param #number GroupingRadius The radius in meters wherein detected planes are being grouped as one target area. + -- For airplanes, 6000 (6km) is recommended, and is also the default value of this parameter. + -- @param #number EngageRadius The radius in meters wherein detected airplanes will be engaged by airborne defenders without a task. + -- @param #number GciRadius The radius in meters wherein detected airplanes will GCI. + -- @param #number Resources The amount of resources that will be allocated to each squadron. + -- @return #AI_A2A_GCICAP + -- @usage + -- + -- -- Setup a new GCICAP dispatcher object with a border. Each squadron has unlimited resources. + -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. + -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. + -- -- The CAP Zone prefix is "CAP Zone". + -- -- The CAP Limit is 2. + -- + -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2 ) + -- + -- @usage + -- + -- -- Setup a new GCICAP dispatcher object with a border. Each squadron has unlimited resources. + -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. + -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. + -- -- The Border prefix is "Border". This will setup a border using the group defined within the mission editor with the name Border. + -- -- The CAP Zone prefix is "CAP Zone". + -- -- The CAP Limit is 2. + -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. + -- + -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000 ) + -- + -- @usage + -- + -- -- Setup a new GCICAP dispatcher object with a border. Each squadron has unlimited resources. + -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. + -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. + -- -- The Border prefix is "Border". This will setup a border using the group defined within the mission editor with the name Border. + -- -- The CAP Zone prefix is "CAP Zone". + -- -- The CAP Limit is 2. + -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. + -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, + -- -- will be considered a defense task if the target is within 60km from the defender. + -- + -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000, 60000 ) + -- + -- @usage + -- + -- -- Setup a new GCICAP dispatcher object with a border. Each squadron has unlimited resources. + -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. + -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. + -- -- The Border prefix is "Border". This will setup a border using the group defined within the mission editor with the name Border. + -- -- The CAP Zone prefix is "CAP Zone". + -- -- The CAP Limit is 2. + -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. + -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, + -- -- will be considered a defense task if the target is within 60km from the defender. + -- -- The GCI Radius is set to 150000. Any target detected within 150km will be considered for GCI engagement. + -- + -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000, 60000, 150000 ) + -- + -- @usage + -- + -- -- Setup a new GCICAP dispatcher object with a border. Each squadron has 30 resources. + -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. + -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. + -- -- The Border prefix is "Border". This will setup a border using the group defined within the mission editor with the name Border. + -- -- The CAP Zone prefix is "CAP Zone". + -- -- The CAP Limit is 2. + -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. + -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, + -- -- will be considered a defense task if the target is within 60km from the defender. + -- -- The GCI Radius is set to 150000. Any target detected within 150km will be considered for GCI engagement. + -- -- The amount of resources for each squadron is set to 30. Thus about 30 resources are allocated to each squadron created. + -- + -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000, 60000, 150000, 30 ) + -- + -- @usage + -- + -- -- Setup a new GCICAP dispatcher object with a border. Each squadron has 30 resources. + -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. + -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. + -- -- The Border prefix is "Border". This will setup a border using the group defined within the mission editor with the name Border. + -- -- The CAP Zone prefix is nil. No CAP is created. + -- -- The CAP Limit is nil. + -- -- The Grouping Radius is nil. The default range of 6km radius will be grouped as a group of targets. + -- -- The Engage Radius is set nil. The default Engage Radius will be used to consider a defenser being assigned to a task. + -- -- The GCI Radius is nil. Any target detected within the default GCI Radius will be considered for GCI engagement. + -- -- The amount of resources for each squadron is set to 30. Thus about 30 resources are allocated to each squadron created. + -- + -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", nil, nil, nil, nil, nil, 30 ) + -- + function AI_A2A_GCICAP:NewWithBorder( EWRPrefixes, TemplatePrefixes, BorderPrefix, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, Resources ) + + local self = AI_A2A_GCICAP:New( EWRPrefixes, TemplatePrefixes, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, Resources ) + + if BorderPrefix then + self:SetBorderZone( ZONE_POLYGON:New( BorderPrefix, GROUP:FindByName( BorderPrefix ) ) ) + end + + return self + + end + +end + diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua new file mode 100644 index 000000000..5d0c415c7 --- /dev/null +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -0,0 +1,461 @@ +--- **AI** -- **Execute Ground Controlled Interception (GCI).** +-- +-- ![Banner Image](..\Presentations\AI_GCI\Dia1.JPG) +-- +-- === +-- +-- AI A2A_INTEREPT class makes AI Groups execute an Intercept. +-- +-- There are the following types of GCI classes defined: +-- +-- * @{#AI_A2A_GCI}: Perform a GCI in a zone. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- +-- @module AI_A2A_GCI + + +--BASE:TraceClass("AI_A2A_GCI") + + +--- @type AI_A2A_GCI +-- @extends AI.AI_A2A#AI_A2A + + +--- # AI_A2A_GCI class, extends @{AI_A2A#AI_A2A} +-- +-- The AI_A2A_GCI class implements the core functions to intercept intruders. The Engage function will intercept intruders. +-- +-- ![Process](..\Presentations\AI_GCI\Dia3.JPG) +-- +-- The AI_A2A_GCI is assigned a @{Group} and this must be done before the AI_A2A_GCI process can be started using the **Start** event. +-- +-- ![Process](..\Presentations\AI_GCI\Dia4.JPG) +-- +-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. +-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. +-- +-- ![Process](..\Presentations\AI_GCI\Dia5.JPG) +-- +-- This cycle will continue. +-- +-- ![Process](..\Presentations\AI_GCI\Dia6.JPG) +-- +-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. +-- +-- ![Process](..\Presentations\AI_GCI\Dia9.JPG) +-- +-- When enemies are detected, the AI will automatically engage the enemy. +-- +-- ![Process](..\Presentations\AI_GCI\Dia10.JPG) +-- +-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. +-- +-- ![Process](..\Presentations\AI_GCI\Dia13.JPG) +-- +-- ## 1. AI_A2A_GCI constructor +-- +-- * @{#AI_A2A_GCI.New}(): Creates a new AI_A2A_GCI object. +-- +-- ## 2. AI_A2A_GCI is a FSM +-- +-- ![Process](..\Presentations\AI_GCI\Dia2.JPG) +-- +-- ### 2.1 AI_A2A_GCI States +-- +-- * **None** ( Group ): The process is not started yet. +-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. +-- * **Engaging** ( Group ): The AI is engaging the bogeys. +-- * **Returning** ( Group ): The AI is returning to Base.. +-- +-- ### 2.2 AI_A2A_GCI Events +-- +-- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. +-- * **@{#AI_A2A_GCI.Engage}**: Let the AI engage the bogeys. +-- * **@{#AI_A2A_GCI.Abort}**: Aborts the engagement and return patrolling in the patrol zone. +-- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. +-- * **@{#AI_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Unit}. +-- * **@{#AI_A2A_GCI.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task. +-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. +-- +-- ## 3. Set the Range of Engagement +-- +-- ![Range](..\Presentations\AI_GCI\Dia11.JPG) +-- +-- An optional range can be set in meters, +-- that will define when the AI will engage with the detected airborne enemy targets. +-- The range can be beyond or smaller than the range of the Patrol Zone. +-- The range is applied at the position of the AI. +-- Use the method @{AI_GCI#AI_A2A_GCI.SetEngageRange}() to define that range. +-- +-- ## 4. Set the Zone of Engagement +-- +-- ![Zone](..\Presentations\AI_GCI\Dia12.JPG) +-- +-- An optional @{Zone} can be set, +-- that will define when the AI will engage with the detected airborne enemy targets. +-- Use the method @{AI_Cap#AI_A2A_GCI.SetEngageZone}() to define that Zone. +-- +-- === +-- +-- @field #AI_A2A_GCI +AI_A2A_GCI = { + ClassName = "AI_A2A_GCI", +} + + + +--- Creates a new AI_A2A_GCI object +-- @param #AI_A2A_GCI self +-- @param Wrapper.Group#GROUP AIGroup +-- @return #AI_A2A_GCI +function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed ) + + -- Inherits from BASE + local self = BASE:Inherit( self, AI_A2A:New( AIGroup ) ) -- #AI_A2A_GCI + + self.Accomplished = false + self.Engaging = false + + self.EngageMinSpeed = EngageMinSpeed + self.EngageMaxSpeed = EngageMaxSpeed + self.PatrolMinSpeed = EngageMinSpeed + self.PatrolMaxSpeed = EngageMaxSpeed + + self.PatrolAltType = "RADIO" + + self:AddTransition( { "Started", "Engaging", "Returning" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI. + + --- OnBefore Transition Handler for Event Engage. + -- @function [parent=#AI_A2A_GCI] OnBeforeEngage + -- @param #AI_A2A_GCI self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Engage. + -- @function [parent=#AI_A2A_GCI] OnAfterEngage + -- @param #AI_A2A_GCI self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Engage. + -- @function [parent=#AI_A2A_GCI] Engage + -- @param #AI_A2A_GCI self + + --- Asynchronous Event Trigger for Event Engage. + -- @function [parent=#AI_A2A_GCI] __Engage + -- @param #AI_A2A_GCI self + -- @param #number Delay The delay in seconds. + +--- OnLeave Transition Handler for State Engaging. +-- @function [parent=#AI_A2A_GCI] OnLeaveEngaging +-- @param #AI_A2A_GCI self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @return #boolean Return false to cancel Transition. + +--- OnEnter Transition Handler for State Engaging. +-- @function [parent=#AI_A2A_GCI] OnEnterEngaging +-- @param #AI_A2A_GCI self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. + + self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI. + + --- OnBefore Transition Handler for Event Fired. + -- @function [parent=#AI_A2A_GCI] OnBeforeFired + -- @param #AI_A2A_GCI self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Fired. + -- @function [parent=#AI_A2A_GCI] OnAfterFired + -- @param #AI_A2A_GCI self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Fired. + -- @function [parent=#AI_A2A_GCI] Fired + -- @param #AI_A2A_GCI self + + --- Asynchronous Event Trigger for Event Fired. + -- @function [parent=#AI_A2A_GCI] __Fired + -- @param #AI_A2A_GCI self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI. + + --- OnBefore Transition Handler for Event Destroy. + -- @function [parent=#AI_A2A_GCI] OnBeforeDestroy + -- @param #AI_A2A_GCI self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Destroy. + -- @function [parent=#AI_A2A_GCI] OnAfterDestroy + -- @param #AI_A2A_GCI self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Destroy. + -- @function [parent=#AI_A2A_GCI] Destroy + -- @param #AI_A2A_GCI self + + --- Asynchronous Event Trigger for Event Destroy. + -- @function [parent=#AI_A2A_GCI] __Destroy + -- @param #AI_A2A_GCI self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI. + + --- OnBefore Transition Handler for Event Abort. + -- @function [parent=#AI_A2A_GCI] OnBeforeAbort + -- @param #AI_A2A_GCI self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Abort. + -- @function [parent=#AI_A2A_GCI] OnAfterAbort + -- @param #AI_A2A_GCI self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Abort. + -- @function [parent=#AI_A2A_GCI] Abort + -- @param #AI_A2A_GCI self + + --- Asynchronous Event Trigger for Event Abort. + -- @function [parent=#AI_A2A_GCI] __Abort + -- @param #AI_A2A_GCI self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI. + + --- OnBefore Transition Handler for Event Accomplish. + -- @function [parent=#AI_A2A_GCI] OnBeforeAccomplish + -- @param #AI_A2A_GCI self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Accomplish. + -- @function [parent=#AI_A2A_GCI] OnAfterAccomplish + -- @param #AI_A2A_GCI self + -- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Accomplish. + -- @function [parent=#AI_A2A_GCI] Accomplish + -- @param #AI_A2A_GCI self + + --- Asynchronous Event Trigger for Event Accomplish. + -- @function [parent=#AI_A2A_GCI] __Accomplish + -- @param #AI_A2A_GCI self + -- @param #number Delay The delay in seconds. + + return self +end + + +--- onafter State Transition for Event Patrol. +-- @param #AI_A2A_GCI self +-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To ) + + self:HandleEvent( EVENTS.Dead ) + +end + +-- todo: need to fix this global function + +--- @param Wrapper.Group#GROUP AIControllable +function AI_A2A_GCI.InterceptRoute( AIGroup, Fsm ) + + AIGroup:F( { "AI_A2A_GCI.InterceptRoute:", AIGroup:GetName() } ) + + if AIGroup:IsAlive() then + Fsm:__Engage( 0.5 ) + + --local Task = AIGroup:TaskOrbitCircle( 4000, 400 ) + --AIGroup:SetTask( Task ) + end +end + +--- @param #AI_A2A_GCI self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A_GCI:onbeforeEngage( AIGroup, From, Event, To ) + + if self.Accomplished == true then + return false + end +end + +--- @param #AI_A2A_GCI self +-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A_GCI:onafterAbort( AIGroup, From, Event, To ) + AIGroup:ClearTasks() + self:Return() + self:__RTB( 0.5 ) +end + + +--- @param #AI_A2A_GCI self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To, AttackSetUnit ) + + self:F( { AIGroup, From, Event, To, AttackSetUnit} ) + + self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT + + local FirstAttackUnit = self.AttackSetUnit:GetFirst() + + if FirstAttackUnit and FirstAttackUnit:IsAlive() then + + if AIGroup:IsAlive() then + + local EngageRoute = {} + + local CurrentCoord = AIGroup:GetCoordinate() + + --- Calculate the target route point. + + local CurrentCoord = AIGroup:GetCoordinate() + + local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate() + self:SetTargetDistance( ToTargetCoord ) -- For RTB status check + + local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed ) + local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) ) + + --- Create a route point of type air. + local ToPatrolRoutePoint = CurrentCoord:Translate( 15000, ToInterceptAngle ):WaypointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToTargetSpeed, + true + ) + + self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } ) + self:F( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } ) + + EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint + EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint + + local AttackTasks = {} + + for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do + local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT + if AttackUnit:IsAlive() and AttackUnit:IsAir() then + self:T( { "Intercepting Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } ) + AttackTasks[#AttackTasks+1] = AIGroup:TaskAttackUnit( AttackUnit ) + end + end + + if #AttackTasks == 0 then + self:E("No targets found -> Going RTB") + self:Return() + self:__RTB( 0.5 ) + else + AIGroup:OptionROEOpenFire() + AIGroup:OptionROTPassiveDefense() + + AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( "AI_A2A_GCI.InterceptRoute", self ) + EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( AttackTasks ) + end + + AIGroup:Route( EngageRoute, 0.5 ) + + end + else + self:E("No targets found -> Going RTB") + self:Return() + self:__RTB( 0.5 ) + end +end + +--- @param #AI_A2A_GCI self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A_GCI:onafterAccomplish( AIGroup, From, Event, To ) + self.Accomplished = true + self:SetDetectionOff() +end + +--- @param #AI_A2A_GCI self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @param Core.Event#EVENTDATA EventData +function AI_A2A_GCI:onafterDestroy( AIGroup, From, Event, To, EventData ) + + if EventData.IniUnit then + self.AttackUnits[EventData.IniUnit] = nil + end +end + +--- @param #AI_A2A_GCI self +-- @param Core.Event#EVENTDATA EventData +function AI_A2A_GCI:OnEventDead( EventData ) + self:F( { "EventDead", EventData } ) + + if EventData.IniDCSUnit then + if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then + self:__Destroy( 1, EventData ) + end + end +end diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua new file mode 100644 index 000000000..92cd522d3 --- /dev/null +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -0,0 +1,395 @@ +--- **AI** -- **Air Patrolling or Staging.** +-- +-- ![Banner Image](..\Presentations\AI_PATROL\Dia1.JPG) +-- +-- === +-- +-- AI PATROL classes makes AI Controllables execute an Patrol. +-- +-- There are the following types of PATROL classes defined: +-- +-- * @{#AI_A2A_PATROL}: Perform a PATROL in a zone. +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [AI_PATROL Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/PAT%20-%20Patrolling) +-- +-- ### [AI_PATROL Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/PAT%20-%20Patrolling) +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [AI_PATROL YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl35HvYZKA6G22WMt7iI3zky) +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: +-- +-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) +-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. +-- +-- ==== +-- +-- @module AI_A2A_Patrol + + +--- @type AI_A2A_PATROL +-- @extends AI.AI_A2A#AI_A2A + +--- # AI_A2A_PATROL class, extends @{Fsm#FSM_CONTROLLABLE} +-- +-- The AI_A2A_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) +-- +-- The AI_A2A_PATROL is assigned a @{Group} and this must be done before the AI_A2A_PATROL process can be started using the **Start** event. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia4.JPG) +-- +-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. +-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia5.JPG) +-- +-- This cycle will continue. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia6.JPG) +-- +-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia9.JPG) +-- +---- Note that the enemy is not engaged! To model enemy engagement, either tailor the **Detected** event, or +-- use derived AI_ classes to model AI offensive or defensive behaviour. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia10.JPG) +-- +-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. +-- +-- ![Process](..\Presentations\AI_PATROL\Dia11.JPG) +-- +-- ## 1. AI_A2A_PATROL constructor +-- +-- * @{#AI_A2A_PATROL.New}(): Creates a new AI_A2A_PATROL object. +-- +-- ## 2. AI_A2A_PATROL is a FSM +-- +-- ![Process](..\Presentations\AI_PATROL\Dia2.JPG) +-- +-- ### 2.1. AI_A2A_PATROL States +-- +-- * **None** ( Group ): The process is not started yet. +-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. +-- * **Returning** ( Group ): The AI is returning to Base. +-- * **Stopped** ( Group ): The process is stopped. +-- * **Crashed** ( Group ): The AI has crashed or is dead. +-- +-- ### 2.2. AI_A2A_PATROL Events +-- +-- * **Start** ( Group ): Start the process. +-- * **Stop** ( Group ): Stop the process. +-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone. +-- * **RTB** ( Group ): Route the AI to the home base. +-- * **Detect** ( Group ): The AI is detecting targets. +-- * **Detected** ( Group ): The AI has detected new targets. +-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. +-- +-- ## 3. Set or Get the AI controllable +-- +-- * @{#AI_A2A_PATROL.SetControllable}(): Set the AIControllable. +-- * @{#AI_A2A_PATROL.GetControllable}(): Get the AIControllable. +-- +-- ## 4. Set the Speed and Altitude boundaries of the AI controllable +-- +-- * @{#AI_A2A_PATROL.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol. +-- * @{#AI_A2A_PATROL.SetAltitude}(): Set altitude boundaries of the AI, for the next patrol. +-- +-- ## 5. Manage the detection process of the AI controllable +-- +-- The detection process of the AI controllable can be manipulated. +-- Detection requires an amount of CPU power, which has an impact on your mission performance. +-- Only put detection on when absolutely necessary, and the frequency of the detection can also be set. +-- +-- * @{#AI_A2A_PATROL.SetDetectionOn}(): Set the detection on. The AI will detect for targets. +-- * @{#AI_A2A_PATROL.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased. +-- +-- The detection frequency can be set with @{#AI_A2A_PATROL.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. +-- Use the method @{#AI_A2A_PATROL.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI. +-- +-- The detection can be filtered to potential targets in a specific zone. +-- Use the method @{#AI_A2A_PATROL.SetDetectionZone}() to set the zone where targets need to be detected. +-- Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected +-- according the weather conditions. +-- +-- ## 6. Manage the "out of fuel" in the AI_A2A_PATROL +-- +-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. +-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. +-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, +-- while a new AI is targetted to the AI_A2A_PATROL. +-- Once the time is finished, the old AI will return to the base. +-- Use the method @{#AI_A2A_PATROL.ManageFuel}() to have this proces in place. +-- +-- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL +-- +-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on. +-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). +-- Use the method @{#AI_A2A_PATROL.ManageDamage}() to have this proces in place. +-- +-- === +-- +-- @field #AI_A2A_PATROL +AI_A2A_PATROL = { + ClassName = "AI_A2A_PATROL", +} + +--- Creates a new AI_A2A_PATROL object +-- @param #AI_A2A_PATROL self +-- @param Wrapper.Group#GROUP AIGroup +-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. +-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @return #AI_A2A_PATROL self +-- @usage +-- -- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol an AIControllable within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h. +-- PatrolZone = ZONE:New( 'PatrolZone' ) +-- PatrolSpawn = SPAWN:New( 'Patrol Group' ) +-- PatrolArea = AI_A2A_PATROL:New( PatrolZone, 3000, 6000, 600, 900 ) +function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) + + -- Inherits from BASE + local self = BASE:Inherit( self, AI_A2A:New( AIGroup ) ) -- #AI_A2A_PATROL + + self.PatrolZone = PatrolZone + self.PatrolFloorAltitude = PatrolFloorAltitude + self.PatrolCeilingAltitude = PatrolCeilingAltitude + self.PatrolMinSpeed = PatrolMinSpeed + self.PatrolMaxSpeed = PatrolMaxSpeed + + -- defafult PatrolAltType to "RADIO" if not specified + self.PatrolAltType = PatrolAltType or "RADIO" + + self:AddTransition( { "Started", "Refuelling" }, "Patrol", "Patrolling" ) + +--- OnBefore Transition Handler for Event Patrol. +-- @function [parent=#AI_A2A_PATROL] OnBeforePatrol +-- @param #AI_A2A_PATROL self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @return #boolean Return false to cancel Transition. + +--- OnAfter Transition Handler for Event Patrol. +-- @function [parent=#AI_A2A_PATROL] OnAfterPatrol +-- @param #AI_A2A_PATROL self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. + +--- Synchronous Event Trigger for Event Patrol. +-- @function [parent=#AI_A2A_PATROL] Patrol +-- @param #AI_A2A_PATROL self + +--- Asynchronous Event Trigger for Event Patrol. +-- @function [parent=#AI_A2A_PATROL] __Patrol +-- @param #AI_A2A_PATROL self +-- @param #number Delay The delay in seconds. + +--- OnLeave Transition Handler for State Patrolling. +-- @function [parent=#AI_A2A_PATROL] OnLeavePatrolling +-- @param #AI_A2A_PATROL self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @return #boolean Return false to cancel Transition. + +--- OnEnter Transition Handler for State Patrolling. +-- @function [parent=#AI_A2A_PATROL] OnEnterPatrolling +-- @param #AI_A2A_PATROL self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. + + self:AddTransition( "Patrolling", "Route", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_PATROL. + +--- OnBefore Transition Handler for Event Route. +-- @function [parent=#AI_A2A_PATROL] OnBeforeRoute +-- @param #AI_A2A_PATROL self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @return #boolean Return false to cancel Transition. + +--- OnAfter Transition Handler for Event Route. +-- @function [parent=#AI_A2A_PATROL] OnAfterRoute +-- @param #AI_A2A_PATROL self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. + +--- Synchronous Event Trigger for Event Route. +-- @function [parent=#AI_A2A_PATROL] Route +-- @param #AI_A2A_PATROL self + +--- Asynchronous Event Trigger for Event Route. +-- @function [parent=#AI_A2A_PATROL] __Route +-- @param #AI_A2A_PATROL self +-- @param #number Delay The delay in seconds. + + + + self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_PATROL. + + return self +end + + + + +--- Sets (modifies) the minimum and maximum speed of the patrol. +-- @param #AI_A2A_PATROL self +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @return #AI_A2A_PATROL self +function AI_A2A_PATROL:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed ) + self:F2( { PatrolMinSpeed, PatrolMaxSpeed } ) + + self.PatrolMinSpeed = PatrolMinSpeed + self.PatrolMaxSpeed = PatrolMaxSpeed +end + + + +--- Sets the floor and ceiling altitude of the patrol. +-- @param #AI_A2A_PATROL self +-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @return #AI_A2A_PATROL self +function AI_A2A_PATROL:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude ) + self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } ) + + self.PatrolFloorAltitude = PatrolFloorAltitude + self.PatrolCeilingAltitude = PatrolCeilingAltitude +end + + +--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. +-- @param #AI_A2A_PATROL self +-- @return #AI_A2A_PATROL self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A_PATROL:onafterPatrol( Controllable, From, Event, To ) + self:F2() + + self:ClearTargetDistance() + + self:__Route( 1 ) + + self.Controllable:OnReSpawn( + function( PatrolGroup ) + self:E( "ReSpawn" ) + self:__Reset( 1 ) + self:__Route( 5 ) + end + ) +end + + + +--- @param Wrapper.Group#GROUP AIGroup +-- This statis method is called from the route path within the last task at the last waaypoint of the Controllable. +-- Note that this method is required, as triggers the next route when patrolling for the Controllable. +function AI_A2A_PATROL.PatrolRoute( AIGroup, Fsm ) + + AIGroup:F( { "AI_A2A_PATROL.PatrolRoute:", AIGroup:GetName() } ) + + if AIGroup:IsAlive() then + Fsm:Route() + end + +end + + +--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. +-- @param #AI_A2A_PATROL self +-- @param Wrapper.Group#GROUP AIGroup The AIGroup managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_A2A_PATROL:onafterRoute( AIGroup, From, Event, To ) + + self:F2() + + -- When RTB, don't allow anymore the routing. + if From == "RTB" then + return + end + + + if AIGroup:IsAlive() then + + local PatrolRoute = {} + + --- Calculate the target route point. + + local CurrentCoord = AIGroup:GetCoordinate() + + local ToTargetCoord = self.PatrolZone:GetRandomPointVec2() + ToTargetCoord:SetAlt( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ) ) + self:SetTargetDistance( ToTargetCoord ) -- For RTB status check + + local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) + + --- Create a route point of type air. + local ToPatrolRoutePoint = ToTargetCoord:WaypointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + ToTargetSpeed, + true + ) + + PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint + PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint + + local Tasks = {} + Tasks[#Tasks+1] = AIGroup:TaskFunction( "AI_A2A_PATROL.PatrolRoute", self ) + PatrolRoute[#PatrolRoute].task = AIGroup:TaskCombo( Tasks ) + + AIGroup:OptionROEReturnFire() + AIGroup:OptionROTPassiveDefense() + + AIGroup:Route( PatrolRoute, 0.5 ) + end + +end + +--- @param Wrapper.Group#GROUP AIGroup +function AI_A2A_PATROL.Resume( AIGroup ) + + AIGroup:F( { "AI_A2A_PATROL.Resume:", AIGroup:GetName() } ) + if AIGroup:IsAlive() then + local _AI_A2A = AIGroup:GetState( AIGroup, "AI_A2A" ) -- #AI_A2A + _AI_A2A:__Reset( 1 ) + _AI_A2A:__Route( 5 ) + end + +end diff --git a/Moose Development/Moose/AI/AI_BAI.lua b/Moose Development/Moose/AI/AI_BAI.lua new file mode 100644 index 000000000..426bfea04 --- /dev/null +++ b/Moose Development/Moose/AI/AI_BAI.lua @@ -0,0 +1,659 @@ +--- **AI** -- **Provide Battlefield Air Interdiction (bombing).** +-- +-- ![Banner Image](..\Presentations\AI_BAI\Dia1.JPG) +-- +-- === +-- +-- AI_BAI classes makes AI Controllables execute bombing tasks. +-- +-- There are the following types of BAI classes defined: +-- +-- * @{#AI_BAI_ZONE}: Perform a BAI in a zone. +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [AI_BAI Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/BOMB%20-%20Close%20Air%20Support) +-- +-- ### [AI_BAI Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/BOMB%20-%20Close%20Air%20Support) +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [AI_BAI YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3JBO1WDqqpyYRRmIkR2ir2) +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. +-- +-- ==== +-- +-- @module AI_Bai + + +--- AI_BAI_ZONE class +-- @type AI_BAI_ZONE +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. +-- @extends AI.AI_Patrol#AI_PATROL_ZONE + +--- # AI_BAI_ZONE class, extends @{AI_Patrol#AI_PATROL_ZONE} +-- +-- AI_BAI_ZONE derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. +-- +-- The AI_BAI_ZONE class implements the core functions to provide BattleGround Air Interdiction in an Engage @{Zone} by an AIR @{Controllable} or @{Group}. +-- The AI_BAI_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. +-- +-- ![HoldAndEngage](..\Presentations\AI_BAI\Dia3.JPG) +-- +-- The AI_BAI_ZONE is assigned a @{Group} and this must be done before the AI_BAI_ZONE process can be started through the **Start** event. +-- +-- ![Start Event](..\Presentations\AI_BAI\Dia4.JPG) +-- +-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone, +-- using a random speed within the given altitude and speed limits. +-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. +-- This cycle will continue until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +-- +-- ![Route Event](..\Presentations\AI_BAI\Dia5.JPG) +-- +-- When the AI is commanded to provide BattleGround Air Interdiction (through the event **Engage**), the AI will fly towards the Engage Zone. +-- Any target that is detected in the Engage Zone will be reported and will be destroyed by the AI. +-- +-- ![Engage Event](..\Presentations\AI_BAI\Dia6.JPG) +-- +-- The AI will detect the targets and will only destroy the targets within the Engage Zone. +-- +-- ![Engage Event](..\Presentations\AI_BAI\Dia7.JPG) +-- +-- Every target that is destroyed, is reported< by the AI. +-- +-- ![Engage Event](..\Presentations\AI_BAI\Dia8.JPG) +-- +-- Note that the AI does not know when the Engage Zone is cleared, and therefore will keep circling in the zone. +-- +-- ![Engage Event](..\Presentations\AI_BAI\Dia9.JPG) +-- +-- Until it is notified through the event **Accomplish**, which is to be triggered by an observing party: +-- +-- * a FAC +-- * a timed event +-- * a menu option selected by a human +-- * a condition +-- * others ... +-- +-- ![Engage Event](..\Presentations\AI_BAI\Dia10.JPG) +-- +-- When the AI has accomplished the Bombing, it will fly back to the Patrol Zone. +-- +-- ![Engage Event](..\Presentations\AI_BAI\Dia11.JPG) +-- +-- It will keep patrolling there, until it is notified to RTB or move to another BOMB Zone. +-- It can be notified to go RTB through the **RTB** event. +-- +-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. +-- +-- ![Engage Event](..\Presentations\AI_BAI\Dia12.JPG) +-- +-- # 1. AI_BAI_ZONE constructor +-- +-- * @{#AI_BAI_ZONE.New}(): Creates a new AI_BAI_ZONE object. +-- +-- ## 2. AI_BAI_ZONE is a FSM +-- +-- ![Process](..\Presentations\AI_BAI\Dia2.JPG) +-- +-- ### 2.1. AI_BAI_ZONE States +-- +-- * **None** ( Group ): The process is not started yet. +-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. +-- * **Engaging** ( Group ): The AI is engaging the targets in the Engage Zone, executing BOMB. +-- * **Returning** ( Group ): The AI is returning to Base.. +-- +-- ### 2.2. AI_BAI_ZONE Events +-- +-- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. +-- * **@{#AI_BAI_ZONE.Engage}**: Engage the AI to provide BOMB in the Engage Zone, destroying any target it finds. +-- * **@{#AI_BAI_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone. +-- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. +-- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. +-- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Unit}. +-- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Unit}s assigned in the BOMB task. +-- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. +-- +-- ## 3. Modify the Engage Zone behaviour to pinpoint a **map object** or **scenery object** +-- +-- Use the method @{#AI_BAI_ZONE.SearchOff}() to specify that the EngageZone is not to be searched for potential targets (UNITs), but that the center of the zone +-- is the point where a map object is to be destroyed (like a bridge). +-- +-- Example: +-- +-- -- Tell the BAI not to search for potential targets in the BAIEngagementZone, but rather use the center of the BAIEngagementZone as the bombing location. +-- AIBAIZone:SearchOff() +-- +-- Searching can be switched back on with the method @{#AI_BAI_ZONE.SearchOn}(). Use the method @{#AI_BAI_ZONE.SearchOnOff}() to flexibily switch searching on or off. +-- +-- === +-- +-- @field #AI_BAI_ZONE +AI_BAI_ZONE = { + ClassName = "AI_BAI_ZONE", +} + + + +--- Creates a new AI_BAI_ZONE object +-- @param #AI_BAI_ZONE self +-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. +-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Core.Zone#ZONE_BASE EngageZone The zone where the engage will happen. +-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @return #AI_BAI_ZONE self +function AI_BAI_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType ) + + -- Inherits from BASE + local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_BAI_ZONE + + self.EngageZone = EngageZone + self.Accomplished = false + + self:SetDetectionZone( self.EngageZone ) + self:SearchOn() + + self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE. + + --- OnBefore Transition Handler for Event Engage. + -- @function [parent=#AI_BAI_ZONE] OnBeforeEngage + -- @param #AI_BAI_ZONE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Engage. + -- @function [parent=#AI_BAI_ZONE] OnAfterEngage + -- @param #AI_BAI_ZONE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Engage. + -- @function [parent=#AI_BAI_ZONE] Engage + -- @param #AI_BAI_ZONE self + -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. + -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. + -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. + -- If parameter is not defined the unit / controllable will choose expend on its own discretion. + -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. + -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. + -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. + + --- Asynchronous Event Trigger for Event Engage. + -- @function [parent=#AI_BAI_ZONE] __Engage + -- @param #AI_BAI_ZONE self + -- @param #number Delay The delay in seconds. + -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. + -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. + -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. + -- If parameter is not defined the unit / controllable will choose expend on its own discretion. + -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. + -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. + -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. + +--- OnLeave Transition Handler for State Engaging. +-- @function [parent=#AI_BAI_ZONE] OnLeaveEngaging +-- @param #AI_BAI_ZONE self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @return #boolean Return false to cancel Transition. + +--- OnEnter Transition Handler for State Engaging. +-- @function [parent=#AI_BAI_ZONE] OnEnterEngaging +-- @param #AI_BAI_ZONE self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. + + self:AddTransition( "Engaging", "Target", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE. + + self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE. + + --- OnBefore Transition Handler for Event Fired. + -- @function [parent=#AI_BAI_ZONE] OnBeforeFired + -- @param #AI_BAI_ZONE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Fired. + -- @function [parent=#AI_BAI_ZONE] OnAfterFired + -- @param #AI_BAI_ZONE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Fired. + -- @function [parent=#AI_BAI_ZONE] Fired + -- @param #AI_BAI_ZONE self + + --- Asynchronous Event Trigger for Event Fired. + -- @function [parent=#AI_BAI_ZONE] __Fired + -- @param #AI_BAI_ZONE self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE. + + --- OnBefore Transition Handler for Event Destroy. + -- @function [parent=#AI_BAI_ZONE] OnBeforeDestroy + -- @param #AI_BAI_ZONE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Destroy. + -- @function [parent=#AI_BAI_ZONE] OnAfterDestroy + -- @param #AI_BAI_ZONE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Destroy. + -- @function [parent=#AI_BAI_ZONE] Destroy + -- @param #AI_BAI_ZONE self + + --- Asynchronous Event Trigger for Event Destroy. + -- @function [parent=#AI_BAI_ZONE] __Destroy + -- @param #AI_BAI_ZONE self + -- @param #number Delay The delay in seconds. + + + self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE. + + --- OnBefore Transition Handler for Event Abort. + -- @function [parent=#AI_BAI_ZONE] OnBeforeAbort + -- @param #AI_BAI_ZONE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Abort. + -- @function [parent=#AI_BAI_ZONE] OnAfterAbort + -- @param #AI_BAI_ZONE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Abort. + -- @function [parent=#AI_BAI_ZONE] Abort + -- @param #AI_BAI_ZONE self + + --- Asynchronous Event Trigger for Event Abort. + -- @function [parent=#AI_BAI_ZONE] __Abort + -- @param #AI_BAI_ZONE self + -- @param #number Delay The delay in seconds. + + self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE. + + --- OnBefore Transition Handler for Event Accomplish. + -- @function [parent=#AI_BAI_ZONE] OnBeforeAccomplish + -- @param #AI_BAI_ZONE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event Accomplish. + -- @function [parent=#AI_BAI_ZONE] OnAfterAccomplish + -- @param #AI_BAI_ZONE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + + --- Synchronous Event Trigger for Event Accomplish. + -- @function [parent=#AI_BAI_ZONE] Accomplish + -- @param #AI_BAI_ZONE self + + --- Asynchronous Event Trigger for Event Accomplish. + -- @function [parent=#AI_BAI_ZONE] __Accomplish + -- @param #AI_BAI_ZONE self + -- @param #number Delay The delay in seconds. + + return self +end + + +--- Set the Engage Zone where the AI is performing BOMB. Note that if the EngageZone is changed, the AI needs to re-detect targets. +-- @param #AI_BAI_ZONE self +-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing BOMB. +-- @return #AI_BAI_ZONE self +function AI_BAI_ZONE:SetEngageZone( EngageZone ) + self:F2() + + if EngageZone then + self.EngageZone = EngageZone + else + self.EngageZone = nil + end +end + + +--- Specifies whether to search for potential targets in the zone, or let the center of the zone be the bombing coordinate. +-- AI_BAI_ZONE will search for potential targets by default. +-- @param #AI_BAI_ZONE self +-- @return #AI_BAI_ZONE +function AI_BAI_ZONE:SearchOnOff( Search ) + + self.Search = Search + + return self +end + +--- If Search is Off, the current zone coordinate will be the center of the bombing. +-- @param #AI_BAI_ZONE self +-- @return #AI_BAI_ZONE +function AI_BAI_ZONE:SearchOff() + + self:SearchOnOff( false ) + + return self +end + + +--- If Search is On, BAI will search for potential targets in the zone. +-- @param #AI_BAI_ZONE self +-- @return #AI_BAI_ZONE +function AI_BAI_ZONE:SearchOn() + + self:SearchOnOff( true ) + + return self +end + + +--- onafter State Transition for Event Start. +-- @param #AI_BAI_ZONE self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_BAI_ZONE:onafterStart( Controllable, From, Event, To ) + + -- Call the parent Start event handler + self:GetParent(self).onafterStart( self, Controllable, From, Event, To ) + self:HandleEvent( EVENTS.Dead ) + + self:SetDetectionDeactivated() -- When not engaging, set the detection off. +end + +--- @param Wrapper.Controllable#CONTROLLABLE AIControllable +function _NewEngageRoute( AIControllable ) + + AIControllable:T( "NewEngageRoute" ) + local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_BAI#AI_BAI_ZONE + EngageZone:__Engage( 1, EngageZone.EngageSpeed, EngageZone.EngageAltitude, EngageZone.EngageWeaponExpend, EngageZone.EngageAttackQty, EngageZone.EngageDirection ) +end + + +--- @param #AI_BAI_ZONE self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_BAI_ZONE:onbeforeEngage( Controllable, From, Event, To ) + + if self.Accomplished == true then + return false + end +end + +--- @param #AI_BAI_ZONE self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_BAI_ZONE:onafterTarget( Controllable, From, Event, To ) + self:F({"onafterTarget",self.Search,Controllable:IsAlive()}) + + + + if Controllable:IsAlive() then + + local AttackTasks = {} + + if self.Search == true then + for DetectedUnit, Detected in pairs( self.DetectedUnits ) do + local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT + if DetectedUnit:IsAlive() then + if DetectedUnit:IsInZone( self.EngageZone ) then + if Detected == true then + self:F( {"Target: ", DetectedUnit } ) + self.DetectedUnits[DetectedUnit] = false + local AttackTask = Controllable:TaskAttackUnit( DetectedUnit, false, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil ) + self.Controllable:PushTask( AttackTask, 1 ) + end + end + else + self.DetectedUnits[DetectedUnit] = nil + end + end + else + self:F("Attack zone") + local AttackTask = Controllable:TaskAttackMapObject( + self.EngageZone:GetPointVec2():GetVec2(), + true, + self.EngageWeaponExpend, + self.EngageAttackQty, + self.EngageDirection, + self.EngageAltitude + ) + self.Controllable:PushTask( AttackTask, 1 ) + end + + self:__Target( -10 ) + + end +end + + +--- @param #AI_BAI_ZONE self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_BAI_ZONE:onafterAbort( Controllable, From, Event, To ) + Controllable:ClearTasks() + self:__Route( 1 ) +end + +--- @param #AI_BAI_ZONE self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. +-- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. +-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. +-- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To, + EngageSpeed, + EngageAltitude, + EngageWeaponExpend, + EngageAttackQty, + EngageDirection ) + + self:F("onafterEngage") + + self.EngageSpeed = EngageSpeed or 400 + self.EngageAltitude = EngageAltitude or 2000 + self.EngageWeaponExpend = EngageWeaponExpend + self.EngageAttackQty = EngageAttackQty + self.EngageDirection = EngageDirection + + if Controllable:IsAlive() then + + local EngageRoute = {} + + --- Calculate the current route point. + local CurrentVec2 = self.Controllable:GetVec2() + + --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). + local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() + local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) + local ToEngageZoneSpeed = self.PatrolMaxSpeed + local CurrentRoutePoint = CurrentPointVec3:WaypointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + self.EngageSpeed, + true + ) + + EngageRoute[#EngageRoute+1] = CurrentRoutePoint + + local AttackTasks = {} + + if self.Search == true then + + for DetectedUnitID, DetectedUnitData in pairs( self.DetectedUnits ) do + local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + self:T( DetectedUnit ) + if DetectedUnit:IsAlive() then + if DetectedUnit:IsInZone( self.EngageZone ) then + self:F( {"Engaging ", DetectedUnit } ) + AttackTasks[#AttackTasks+1] = Controllable:TaskBombing( + DetectedUnit:GetPointVec2():GetVec2(), + true, + EngageWeaponExpend, + EngageAttackQty, + EngageDirection, + EngageAltitude + ) + end + else + self.DetectedUnits[DetectedUnit] = nil + end + end + else + self:F("Attack zone") + AttackTasks[#AttackTasks+1] = Controllable:TaskAttackMapObject( + self.EngageZone:GetPointVec2():GetVec2(), + true, + EngageWeaponExpend, + EngageAttackQty, + EngageDirection, + EngageAltitude + ) + end + + EngageRoute[#EngageRoute].task = Controllable:TaskCombo( AttackTasks ) + + --- Define a random point in the @{Zone}. The AI will fly to that point within the zone. + + --- Find a random 2D point in EngageZone. + local ToTargetVec2 = self.EngageZone:GetRandomVec2() + self:T2( ToTargetVec2 ) + + --- Obtain a 3D @{Point} from the 2D point + altitude. + local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y ) + + --- Create a route point of type air. + local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir( + self.PatrolAltType, + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + self.EngageSpeed, + true + ) + + EngageRoute[#EngageRoute+1] = ToTargetRoutePoint + + Controllable:OptionROEOpenFire() + Controllable:OptionROTVertical() + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + Controllable:WayPointInitialize( EngageRoute ) + + --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... + Controllable:SetState( Controllable, "EngageZone", self ) + + Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" ) + + --- NOW ROUTE THE GROUP! + Controllable:WayPointExecute( 1 ) + + self:SetRefreshTimeInterval( 2 ) + self:SetDetectionActivated() + self:__Target( -2 ) -- Start Targetting + end +end + + +--- @param #AI_BAI_ZONE self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +function AI_BAI_ZONE:onafterAccomplish( Controllable, From, Event, To ) + self.Accomplished = true + self:SetDetectionDeactivated() +end + + +--- @param #AI_BAI_ZONE self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. +-- @param #string From The From State string. +-- @param #string Event The Event string. +-- @param #string To The To State string. +-- @param Core.Event#EVENTDATA EventData +function AI_BAI_ZONE:onafterDestroy( Controllable, From, Event, To, EventData ) + + if EventData.IniUnit then + self.DetectedUnits[EventData.IniUnit] = nil + end +end + + +--- @param #AI_BAI_ZONE self +-- @param Core.Event#EVENTDATA EventData +function AI_BAI_ZONE:OnEventDead( EventData ) + self:F( { "EventDead", EventData } ) + + if EventData.IniDCSUnit then + if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then + self:__Destroy( 1, EventData ) + end + end +end + + diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index 8b6b51cba..643dfc5b7 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -1,14 +1,46 @@ ---- Single-Player:**No** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** -- **AI Balancing will replace in multi player missions +--- **AI** -- **AI Balancing will replace in multi player missions -- non-occupied human slots with AI groups, in order to provide an engaging simulation environment, -- even when there are hardly any players in the mission.** -- -- ![Banner Image](..\Presentations\AI_Balancer\Dia1.JPG) -- --- === +-- ==== -- --- # 1) @{AI_Balancer#AI_BALANCER} class, extends @{Fsm#FSM_SET} +-- # Demo Missions -- --- The @{AI_Balancer#AI_BALANCER} class monitors and manages as many replacement AI groups as there are +-- ### [AI_BALANCER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/AIB%20-%20AI%20Balancing) +-- +-- ### [AI_BALANCER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AIB%20-%20AI%20Balancing) +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [AI_BALANCER YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl2CJVIrL1TdAumuVS8n64B7) +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: +-- +-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) +-- +-- ==== +-- +-- @module AI_Balancer + +--- @type AI_BALANCER +-- @field Core.Set#SET_CLIENT SetClient +-- @field Functional.Spawn#SPAWN SpawnAI +-- @field Wrapper.Group#GROUP Test +-- @extends Core.Fsm#FSM_SET + + +--- # AI_BALANCER class, extends @{Fsm#FSM_SET} +-- +-- The AI_BALANCER class monitors and manages as many replacement AI groups as there are -- CLIENTS in a SET_CLIENT collection, which are not occupied by human players. -- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions. -- @@ -18,17 +50,17 @@ -- -- The mission designer can tailor the AI_BALANCER behaviour, by implementing a state or event handling method for the following: -- --- * **@{#AI_BALANCER.OnAfterSpawned}**( AISet, From, Event, To, AIGroup ): Define to add extra logic when an AI is spawned. +-- * @{#AI_BALANCER.OnAfterSpawned}( AISet, From, Event, To, AIGroup ): Define to add extra logic when an AI is spawned. -- --- ## 1.1) AI_BALANCER construction +-- ## 1. AI_BALANCER construction -- -- Create a new AI_BALANCER object with the @{#AI_BALANCER.New}() method: -- --- ## 1.2) AI_BALANCER is a FSM +-- ## 2. AI_BALANCER is a FSM -- -- ![Process](..\Presentations\AI_Balancer\Dia13.JPG) -- --- ### 1.2.1) AI_BALANCER States +-- ### 2.1. AI_BALANCER States -- -- * **Monitoring** ( Set ): Monitoring the Set if all AI is spawned for the Clients. -- * **Spawning** ( Set, ClientName ): There is a new AI group spawned with ClientName as the name of reference. @@ -36,7 +68,7 @@ -- * **Destroying** ( Set, AIGroup ): The AI is being destroyed. -- * **Returning** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. Handle this state to customize the return behaviour of the AI, if any. -- --- ### 1.2.2) AI_BALANCER Events +-- ### 2.2. AI_BALANCER Events -- -- * **Monitor** ( Set ): Every 10 seconds, the Monitor event is triggered to monitor the Set. -- * **Spawn** ( Set, ClientName ): Triggers when there is a new AI group to be spawned with ClientName as the name of reference. @@ -44,11 +76,11 @@ -- * **Destroy** ( Set, AIGroup ): The AI is being destroyed. -- * **Return** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. -- --- ## 1.3) AI_BALANCER spawn interval for replacement AI +-- ## 3. AI_BALANCER spawn interval for replacement AI -- -- Use the method @{#AI_BALANCER.InitSpawnInterval}() to set the earliest and latest interval in seconds that is waited until a new replacement AI is spawned. -- --- ## 1.4) AI_BALANCER returns AI to Airbases +-- ## 4. AI_BALANCER returns AI to Airbases -- -- By default, When a human player joins a slot that is AI_BALANCED, the AI group will be destroyed by default. -- However, there are 2 additional options that you can use to customize the destroy behaviour. @@ -59,43 +91,8 @@ -- -- Note that when AI returns to an airbase, the AI_BALANCER will trigger the **Return** event and the AI will return, -- otherwise the AI_BALANCER will trigger a **Destroy** event, and the AI will be destroyed. --- --- === -- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-17: There is still a problem with AI being destroyed, but not respawned. Need to check further upon that. --- --- 2017-01-08: AI_BALANCER:**InitSpawnInterval( Earliest, Latest )** added. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) --- * **SNAFU**: Had a couple of mails with the guys to validate, if the same concept in the GCI/CAP script could be reworked within MOOSE. None of the script code has been used however within the new AI_BALANCER moose class. --- --- ### Authors: --- --- * FlightControl: Framework Design & Programming and Documentation. --- --- @module AI_Balancer - ---- AI_BALANCER class --- @type AI_BALANCER --- @field Core.Set#SET_CLIENT SetClient --- @field Functional.Spawn#SPAWN SpawnAI --- @field Wrapper.Group#GROUP Test --- @extends Core.Fsm#FSM_SET +-- @field #AI_BALANCER AI_BALANCER = { ClassName = "AI_BALANCER", PatrolZones = {}, @@ -154,22 +151,22 @@ end --- Returns the AI to the nearest friendly @{Airbase#AIRBASE}. -- @param #AI_BALANCER self --- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}. +-- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}. -- @param Core.Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Set#SET_AIRBASE}s to evaluate where to return to. -function AI_BALANCER:ReturnToNearestAirbases( ReturnTresholdRange, ReturnAirbaseSet ) +function AI_BALANCER:ReturnToNearestAirbases( ReturnThresholdRange, ReturnAirbaseSet ) self.ToNearestAirbase = true - self.ReturnTresholdRange = ReturnTresholdRange + self.ReturnThresholdRange = ReturnThresholdRange self.ReturnAirbaseSet = ReturnAirbaseSet end --- Returns the AI to the home @{Airbase#AIRBASE}. -- @param #AI_BALANCER self --- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}. -function AI_BALANCER:ReturnToHomeAirbase( ReturnTresholdRange ) +-- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}. +function AI_BALANCER:ReturnToHomeAirbase( ReturnThresholdRange ) self.ToHomeAirbase = true - self.ReturnTresholdRange = ReturnTresholdRange + self.ReturnThresholdRange = ReturnThresholdRange end --- @param #AI_BALANCER self @@ -249,12 +246,12 @@ function AI_BALANCER:onenterMonitoring( SetGroup ) if self.ToNearestAirbase == false and self.ToHomeAirbase == false then self:Destroy( Client.UnitName, AIGroup ) else - -- We test if there is no other CLIENT within the self.ReturnTresholdRange of the first unit of the AI group. + -- We test if there is no other CLIENT within the self.ReturnThresholdRange of the first unit of the AI group. -- If there is a CLIENT, the AI stays engaged and will not return. - -- If there is no CLIENT within the self.ReturnTresholdRange, then the unit will return to the Airbase return method selected. + -- If there is no CLIENT within the self.ReturnThresholdRange, then the unit will return to the Airbase return method selected. local PlayerInRange = { Value = false } - local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnTresholdRange ) + local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnThresholdRange ) self:T2( RangeZone ) diff --git a/Moose Development/Moose/AI/AI_CAP.lua b/Moose Development/Moose/AI/AI_CAP.lua index 1b7dcbe22..ac5916064 100644 --- a/Moose Development/Moose/AI/AI_CAP.lua +++ b/Moose Development/Moose/AI/AI_CAP.lua @@ -1,4 +1,4 @@ ---- **AI** - **Execute Combat Air Patrol (CAP).** +--- **AI** -- **Execute Combat Air Patrol (CAP).** -- -- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG) -- @@ -11,33 +11,34 @@ -- * @{#AI_CAP_ZONE}: Perform a CAP in a zone. -- -- ==== +-- +-- # Demo Missions +-- +-- ### [AI_CAP Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol) +-- +-- ### [AI_CAP Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAP%20-%20Combat%20Air%20Patrol) -- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-15: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [AI_CAP YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1YCyPxJgoZn-CfhwyeW65L) +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: -- -- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. -- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. -- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. -- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. -- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. +-- +-- ==== -- -- @module AI_Cap @@ -48,9 +49,9 @@ -- @extends AI.AI_Patrol#AI_PATROL_ZONE ---- # 1) @{#AI_CAP_ZONE} class, extends @{AI_CAP#AI_PATROL_ZONE} +--- # AI_CAP_ZONE class, extends @{AI_CAP#AI_PATROL_ZONE} -- --- The @{#AI_CAP_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group} +-- The AI_CAP_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. -- -- ![Process](..\Presentations\AI_CAP\Dia3.JPG) @@ -81,22 +82,22 @@ -- -- ![Process](..\Presentations\AI_CAP\Dia13.JPG) -- --- ## 1.1) AI_CAP_ZONE constructor +-- ## 1. AI_CAP_ZONE constructor -- -- * @{#AI_CAP_ZONE.New}(): Creates a new AI_CAP_ZONE object. -- --- ## 1.2) AI_CAP_ZONE is a FSM +-- ## 2. AI_CAP_ZONE is a FSM -- -- ![Process](..\Presentations\AI_CAP\Dia2.JPG) -- --- ### 1.2.1) AI_CAP_ZONE States +-- ### 2.1 AI_CAP_ZONE States -- -- * **None** ( Group ): The process is not started yet. -- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. -- * **Engaging** ( Group ): The AI is engaging the bogeys. -- * **Returning** ( Group ): The AI is returning to Base.. -- --- ### 1.2.2) AI_CAP_ZONE Events +-- ### 2.2 AI_CAP_ZONE Events -- -- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. -- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. @@ -109,7 +110,7 @@ -- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- --- ## 1.3) Set the Range of Engagement +-- ## 3. Set the Range of Engagement -- -- ![Range](..\Presentations\AI_CAP\Dia11.JPG) -- @@ -119,7 +120,7 @@ -- The range is applied at the position of the AI. -- Use the method @{AI_CAP#AI_CAP_ZONE.SetEngageRange}() to define that range. -- --- ## 1.4) Set the Zone of Engagement +-- ## 4. Set the Zone of Engagement -- -- ![Zone](..\Presentations\AI_CAP\Dia12.JPG) -- @@ -129,8 +130,7 @@ -- -- === -- --- @field #AI_CAP_ZONE AI_CAP_ZONE --- +-- @field #AI_CAP_ZONE AI_CAP_ZONE = { ClassName = "AI_CAP_ZONE", } @@ -402,7 +402,7 @@ function AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To ) end if Engage == true then - self:E( 'Detected -> Engaging' ) + self:F( 'Detected -> Engaging' ) self:__Engage( 1 ) end end @@ -440,7 +440,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To ) local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local ToEngageZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( + local CurrentRoutePoint = CurrentPointVec3:WaypointAir( self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, @@ -464,7 +464,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To ) local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y ) --- Create a route point of type air. - local ToPatrolRoutePoint = ToTargetPointVec3:RoutePointAir( + local ToPatrolRoutePoint = ToTargetPointVec3:WaypointAir( self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, @@ -485,13 +485,13 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To ) if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then if self.EngageZone then if DetectedUnit:IsInZone( self.EngageZone ) then - self:E( {"Within Zone and Engaging ", DetectedUnit } ) + self:F( {"Within Zone and Engaging ", DetectedUnit } ) AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) end else if self.EngageRange then if DetectedUnit:GetPointVec3():Get2DDistance(Controllable:GetPointVec3() ) <= self.EngageRange then - self:E( {"Within Range and Engaging", DetectedUnit } ) + self:F( {"Within Range and Engaging", DetectedUnit } ) AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) end else @@ -508,7 +508,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To ) if #AttackTasks == 0 then - self:E("No targets found -> Going back to Patrolling") + self:F("No targets found -> Going back to Patrolling") self:__Abort( 1 ) self:__Route( 1 ) self:SetDetectionActivated() diff --git a/Moose Development/Moose/AI/AI_CAS.lua b/Moose Development/Moose/AI/AI_CAS.lua index 49ffdbc51..bd6b0e18a 100644 --- a/Moose Development/Moose/AI/AI_CAS.lua +++ b/Moose Development/Moose/AI/AI_CAS.lua @@ -10,32 +10,34 @@ -- -- * @{#AI_CAS_ZONE}: Perform a CAS in a zone. -- --- === +-- ==== +-- +-- # Demo Missions +-- +-- ### [AI_CAS Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAS%20-%20Close%20Air%20Support) +-- +-- ### [AI_CAS Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAS%20-%20Close%20Air%20Support) -- --- # **API CHANGE HISTORY** +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [AI_CAS YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3JBO1WDqqpyYRRmIkR2ir2) +-- +-- ==== -- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-15: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: -- -- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. -- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. -- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. -- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. +-- ==== -- -- @module AI_Cas @@ -46,11 +48,11 @@ -- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. -- @extends AI.AI_Patrol#AI_PATROL_ZONE ---- # 1) @{#AI_CAS_ZONE} class, extends @{AI_Patrol#AI_PATROL_ZONE} +--- # AI_CAS_ZONE class, extends @{AI_Patrol#AI_PATROL_ZONE} -- --- @{#AI_CAS_ZONE} derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. +-- AI_CAS_ZONE derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. -- --- The @{#AI_CAS_ZONE} class implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Controllable} or @{Group}. +-- The AI_CAS_ZONE class implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Controllable} or @{Group}. -- The AI_CAS_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. -- -- ![HoldAndEngage](..\Presentations\AI_CAS\Dia3.JPG) @@ -104,22 +106,22 @@ -- -- ![Engage Event](..\Presentations\AI_CAS\Dia12.JPG) -- --- # 1.1) AI_CAS_ZONE constructor +-- ## AI_CAS_ZONE constructor -- -- * @{#AI_CAS_ZONE.New}(): Creates a new AI_CAS_ZONE object. -- --- ## 1.2) AI_CAS_ZONE is a FSM +-- ## AI_CAS_ZONE is a FSM -- -- ![Process](..\Presentations\AI_CAS\Dia2.JPG) -- --- ### 1.2.1) AI_CAS_ZONE States +-- ### 2.1. AI_CAS_ZONE States -- -- * **None** ( Group ): The process is not started yet. -- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. -- * **Engaging** ( Group ): The AI is engaging the targets in the Engage Zone, executing CAS. -- * **Returning** ( Group ): The AI is returning to Base.. -- --- ### 1.2.2) AI_CAS_ZONE Events +-- ### 2.2. AI_CAS_ZONE Events -- -- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. -- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. @@ -134,8 +136,7 @@ -- -- === -- --- @field #AI_CAS_ZONE AI_CAS_ZONE --- +-- @field #AI_CAS_ZONE AI_CAS_ZONE = { ClassName = "AI_CAS_ZONE", } @@ -472,7 +473,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local ToEngageZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( + local CurrentRoutePoint = CurrentPointVec3:WaypointAir( self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, @@ -514,7 +515,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y ) --- Create a route point of type air. - local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir( + local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir( self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, @@ -538,7 +539,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, Controllable:OptionROEOpenFire() Controllable:OptionROTVertical() - self:SetDetectionInterval( 2 ) + self:SetRefreshTimeInterval( 2 ) self:SetDetectionActivated() self:__Target( -2 ) -- Start Targetting end diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua deleted file mode 100644 index 2172045f7..000000000 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ /dev/null @@ -1,1028 +0,0 @@ ----Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Ground** -- --- **Management of logical cargo objects, that can be transported from and to transportation carriers.** --- --- ![Banner Image](..\Presentations\AI_CARGO\CARGO.JPG) --- --- === --- --- Cargo can be of various forms, always are composed out of ONE object ( one unit or one static or one slingload crate ): --- --- * AI_CARGO_UNIT, represented by a @{Unit} in a @{Group}: Cargo can be represented by a Unit in a Group. Destruction of the Unit will mean that the cargo is lost. --- * CARGO_STATIC, represented by a @{Static}: Cargo can be represented by a Static. Destruction of the Static will mean that the cargo is lost. --- * AI_CARGO_PACKAGE, contained in a @{Unit} of a @{Group}: Cargo can be contained within a Unit of a Group. The cargo can be **delivered** by the @{Unit}. If the Unit is destroyed, the cargo will be destroyed also. --- * AI_CARGO_PACKAGE, Contained in a @{Static}: Cargo can be contained within a Static. The cargo can be **collected** from the @Static. If the @{Static} is destroyed, the cargo will be destroyed. --- * CARGO_SLINGLOAD, represented by a @{Cargo} that is transportable: Cargo can be represented by a Cargo object that is transportable. Destruction of the Cargo will mean that the cargo is lost. --- --- * AI_CARGO_GROUPED, represented by a Group of CARGO_UNITs. --- --- # 1) @{#AI_CARGO} class, extends @{Fsm#FSM_PROCESS} --- --- The @{#AI_CARGO} class defines the core functions that defines a cargo object within MOOSE. --- A cargo is a logical object defined that is available for transport, and has a life status within a simulation. --- --- The AI_CARGO is a state machine: it manages the different events and states of the cargo. --- All derived classes from AI_CARGO follow the same state machine, expose the same cargo event functions, and provide the same cargo states. --- --- ## 1.2.1) AI_CARGO Events: --- --- * @{#AI_CARGO.Board}( ToCarrier ): Boards the cargo to a carrier. --- * @{#AI_CARGO.Load}( ToCarrier ): Loads the cargo into a carrier, regardless of its position. --- * @{#AI_CARGO.UnBoard}( ToPointVec2 ): UnBoard the cargo from a carrier. This will trigger a movement of the cargo to the option ToPointVec2. --- * @{#AI_CARGO.UnLoad}( ToPointVec2 ): UnLoads the cargo from a carrier. --- * @{#AI_CARGO.Dead}( Controllable ): The cargo is dead. The cargo process will be ended. --- --- ## 1.2.2) AI_CARGO States: --- --- * **UnLoaded**: The cargo is unloaded from a carrier. --- * **Boarding**: The cargo is currently boarding (= running) into a carrier. --- * **Loaded**: The cargo is loaded into a carrier. --- * **UnBoarding**: The cargo is currently unboarding (=running) from a carrier. --- * **Dead**: The cargo is dead ... --- * **End**: The process has come to an end. --- --- ## 1.2.3) AI_CARGO state transition methods: --- --- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. --- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Leaving** the state. --- The state transition method needs to start with the name **OnLeave + the name of the state**. --- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, --- but then you'll need to specify your own logic using the AIControllable! --- --- * **Entering** the state. --- The state transition method needs to start with the name **OnEnter + the name of the state**. --- These state transition methods need to provide a return value, which is specified at the function description. --- --- # 2) #AI_CARGO_UNIT class --- --- The AI_CARGO_UNIT class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. --- Use the event functions as described above to Load, UnLoad, Board, UnBoard the AI_CARGO_UNIT objects to and from carriers. --- --- # 5) #AI_CARGO_GROUPED class --- --- The AI_CARGO_GROUPED class defines a cargo that is represented by a group of UNIT objects within the simulator, and can be transported by a carrier. --- Use the event functions as described above to Load, UnLoad, Board, UnBoard the AI_CARGO_UNIT objects to and from carriers. --- --- This module is still under construction, but is described above works already, and will keep working ... --- --- @module Cargo - --- Events - --- Board - ---- Boards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo to the Carrier. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#AI_CARGO] Board --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - ---- Boards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo to the Carrier. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#AI_CARGO] __Board --- @param #AI_CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - - --- UnBoard - ---- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. --- The cargo must be in the **Loaded** state. --- @function [parent=#AI_CARGO] UnBoard --- @param #AI_CARGO self --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. - ---- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. --- The cargo must be in the **Loaded** state. --- @function [parent=#AI_CARGO] __UnBoard --- @param #AI_CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. - - --- Load - ---- Loads the cargo to a Carrier. The event will load the cargo into the Carrier regardless of its position. There will be no movement simulated of the cargo loading. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#AI_CARGO] Load --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - ---- Loads the cargo to a Carrier. The event will load the cargo into the Carrier regardless of its position. There will be no movement simulated of the cargo loading. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#AI_CARGO] __Load --- @param #AI_CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - - --- UnLoad - ---- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. --- The cargo must be in the **Loaded** state. --- @function [parent=#AI_CARGO] UnLoad --- @param #AI_CARGO self --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. - ---- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. --- The cargo must be in the **Loaded** state. --- @function [parent=#AI_CARGO] __UnLoad --- @param #AI_CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. - --- State Transition Functions - --- UnLoaded - ---- @function [parent=#AI_CARGO] OnLeaveUnLoaded --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#AI_CARGO] OnEnterUnLoaded --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - --- Loaded - ---- @function [parent=#AI_CARGO] OnLeaveLoaded --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#AI_CARGO] OnEnterLoaded --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - --- Boarding - ---- @function [parent=#AI_CARGO] OnLeaveBoarding --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#AI_CARGO] OnEnterBoarding --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - --- UnBoarding - ---- @function [parent=#AI_CARGO] OnLeaveUnBoarding --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#AI_CARGO] OnEnterUnBoarding --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - - --- TODO: Find all Carrier objects and make the type of the Carriers Wrapper.Unit#UNIT in the documentation. - -CARGOS = {} - -do -- AI_CARGO - - --- @type AI_CARGO - -- @extends Core.Fsm#FSM_PROCESS - -- @field #string Type A string defining the type of the cargo. eg. Engineers, Equipment, Screwdrivers. - -- @field #string Name A string defining the name of the cargo. The name is the unique identifier of the cargo. - -- @field #number Weight A number defining the weight of the cargo. The weight is expressed in kg. - -- @field #number ReportRadius (optional) A number defining the radius in meters when the cargo is signalling or reporting to a Carrier. - -- @field #number NearRadius (optional) A number defining the radius in meters when the cargo is near to a Carrier, so that it can be loaded. - -- @field Wrapper.Controllable#CONTROLLABLE CargoObject The alive DCS object representing the cargo. This value can be nil, meaning, that the cargo is not represented anywhere... - -- @field Wrapper.Controllable#CONTROLLABLE CargoCarrier The alive DCS object carrying the cargo. This value can be nil, meaning, that the cargo is not contained anywhere... - -- @field #boolean Slingloadable This flag defines if the cargo can be slingloaded. - -- @field #boolean Moveable This flag defines if the cargo is moveable. - -- @field #boolean Representable This flag defines if the cargo can be represented by a DCS Unit. - -- @field #boolean Containable This flag defines if the cargo can be contained within a DCS Unit. - AI_CARGO = { - ClassName = "AI_CARGO", - Type = nil, - Name = nil, - Weight = nil, - CargoObject = nil, - CargoCarrier = nil, - Representable = false, - Slingloadable = false, - Moveable = false, - Containable = false, - } - ---- @type AI_CARGO.CargoObjects --- @map < #string, Wrapper.Positionable#POSITIONABLE > The alive POSITIONABLE objects representing the the cargo. - - ---- AI_CARGO Constructor. This class is an abstract class and should not be instantiated. --- @param #AI_CARGO self --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO -function AI_CARGO:New( Type, Name, Weight, ReportRadius, NearRadius ) - - local self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM_CONTROLLABLE - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - self:SetStartState( "UnLoaded" ) - self:AddTransition( "UnLoaded", "Board", "Boarding" ) - self:AddTransition( "Boarding", "Boarding", "Boarding" ) - self:AddTransition( "Boarding", "Load", "Loaded" ) - self:AddTransition( "UnLoaded", "Load", "Loaded" ) - self:AddTransition( "Loaded", "UnBoard", "UnBoarding" ) - self:AddTransition( "UnBoarding", "UnBoarding", "UnBoarding" ) - self:AddTransition( "UnBoarding", "UnLoad", "UnLoaded" ) - self:AddTransition( "Loaded", "UnLoad", "UnLoaded" ) - - - self.Type = Type - self.Name = Name - self.Weight = Weight - self.ReportRadius = ReportRadius - self.NearRadius = NearRadius - self.CargoObject = nil - self.CargoCarrier = nil - self.Representable = false - self.Slingloadable = false - self.Moveable = false - self.Containable = false - - - self.CargoScheduler = SCHEDULER:New() - - CARGOS[self.Name] = self - - return self -end - - ---- Template method to spawn a new representation of the AI_CARGO in the simulator. --- @param #AI_CARGO self --- @return #AI_CARGO -function AI_CARGO:Spawn( PointVec2 ) - self:F() - -end - - ---- Check if CargoCarrier is near the Cargo to be Loaded. --- @param #AI_CARGO self --- @param Core.Point#POINT_VEC2 PointVec2 --- @return #boolean -function AI_CARGO:IsNear( PointVec2 ) - self:F( { PointVec2 } ) - - local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - self:T( Distance ) - - if Distance <= self.NearRadius then - return true - else - return false - end -end - -end - -do -- AI_CARGO_REPRESENTABLE - - --- @type AI_CARGO_REPRESENTABLE - -- @extends #AI_CARGO - AI_CARGO_REPRESENTABLE = { - ClassName = "AI_CARGO_REPRESENTABLE" - } - ---- AI_CARGO_REPRESENTABLE Constructor. --- @param #AI_CARGO_REPRESENTABLE self --- @param Wrapper.Controllable#Controllable CargoObject --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_REPRESENTABLE -function AI_CARGO_REPRESENTABLE:New( CargoObject, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO:New( Type, Name, Weight, ReportRadius, NearRadius ) ) -- #AI_CARGO - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - return self -end - ---- Route a cargo unit to a PointVec2. --- @param #AI_CARGO_REPRESENTABLE self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #number Speed --- @return #AI_CARGO_REPRESENTABLE -function AI_CARGO_REPRESENTABLE:RouteTo( ToPointVec2, Speed ) - self:F2( ToPointVec2 ) - - local Points = {} - - local PointStartVec2 = self.CargoObject:GetPointVec2() - - Points[#Points+1] = PointStartVec2:RoutePointGround( Speed ) - Points[#Points+1] = ToPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 2 ) - return self -end - -end -- AI_CARGO - -do -- AI_CARGO_UNIT - - --- @type AI_CARGO_UNIT - -- @extends #AI_CARGO_REPRESENTABLE - AI_CARGO_UNIT = { - ClassName = "AI_CARGO_UNIT" - } - ---- AI_CARGO_UNIT Constructor. --- @param #AI_CARGO_UNIT self --- @param Wrapper.Unit#UNIT CargoUnit --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_UNIT -function AI_CARGO_UNIT:New( CargoUnit, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, Weight, ReportRadius, NearRadius ) ) -- #AI_CARGO_UNIT - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - self:T( CargoUnit ) - self.CargoObject = CargoUnit - - self:T( self.ClassName ) - - return self -end - ---- Enter UnBoarding State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Point#POINT_VEC2 ToPointVec2 -function AI_CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2 ) - self:F() - - local Angle = 180 - local Speed = 10 - local DeployDistance = 5 - local RouteDistance = 60 - - if From == "Loaded" then - - local CargoCarrierPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, CargoDeployHeading ) - local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading ) - - -- if there is no ToPointVec2 given, then use the CargoRoutePointVec2 - ToPointVec2 = ToPointVec2 or CargoRoutePointVec2 - - local FromPointVec2 = CargoCarrierPointVec2 - - -- Respawn the group... - if self.CargoObject then - self.CargoObject:ReSpawn( CargoDeployPointVec2:GetVec3(), CargoDeployHeading ) - self.CargoCarrier = nil - - local Points = {} - Points[#Points+1] = FromPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = ToPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 1 ) - - self:__UnBoarding( 1, ToPointVec2 ) - end - end - -end - ---- Leave UnBoarding State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Point#POINT_VEC2 ToPointVec2 -function AI_CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "UnBoarding" then - if self:IsNear( ToPointVec2 ) then - return true - else - self:__UnBoarding( 1, ToPointVec2 ) - end - return false - end - -end - ---- UnBoard Event. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Point#POINT_VEC2 ToPointVec2 -function AI_CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - self.CargoInAir = self.CargoObject:InAir() - - self:T( self.CargoInAir ) - - -- Only unboard the cargo when the carrier is not in the air. - -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). - if not self.CargoInAir then - - end - - self:__UnLoad( 1, ToPointVec2 ) - -end - - - ---- Enter UnLoaded State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Point#POINT_VEC2 -function AI_CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "Loaded" then - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = StartPointVec2:Translate( Distance, CargoDeployHeading ) - - ToPointVec2 = ToPointVec2 or POINT_VEC2:New( CargoDeployPointVec2:GetX(), CargoDeployPointVec2:GetY() ) - - -- Respawn the group... - if self.CargoObject then - self.CargoObject:ReSpawn( ToPointVec2:GetVec3(), 0 ) - self.CargoCarrier = nil - end - - end - - if self.OnUnLoadedCallBack then - self.OnUnLoadedCallBack( self, unpack( self.OnUnLoadedParameters ) ) - self.OnUnLoadedCallBack = nil - end - -end - - - ---- Enter Boarding State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - local Speed = 10 - local Angle = 180 - local Distance = 5 - - if From == "UnLoaded" then - local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) - - local Points = {} - - local PointStartVec2 = self.CargoObject:GetPointVec2() - - Points[#Points+1] = PointStartVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 2 ) - end - -end - ---- Leave Boarding State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_UNIT:onleaveBoarding( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - if self:IsNear( CargoCarrier:GetPointVec2() ) then - self:__Load( 1, CargoCarrier ) - return true - else - self:__Boarding( 1, CargoCarrier ) - end - return false -end - ---- Loaded State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier ) - self:F() - - self.CargoCarrier = CargoCarrier - - -- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects). - if self.CargoObject then - self:T("Destroying") - self.CargoObject:Destroy() - end -end - - ---- Board Event. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier ) - self:F() - - self.CargoInAir = self.CargoObject:InAir() - - self:T( self.CargoInAir ) - - -- Only move the group to the carrier when the cargo is not in the air - -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). - if not self.CargoInAir then - self:Load( CargoCarrier ) - end - -end - -end - -do -- AI_CARGO_PACKAGE - - --- @type AI_CARGO_PACKAGE - -- @extends #AI_CARGO_REPRESENTABLE - AI_CARGO_PACKAGE = { - ClassName = "AI_CARGO_PACKAGE" - } - ---- AI_CARGO_PACKAGE Constructor. --- @param #AI_CARGO_PACKAGE self --- @param Wrapper.Unit#UNIT CargoCarrier The UNIT carrying the package. --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_PACKAGE -function AI_CARGO_PACKAGE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO_REPRESENTABLE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) ) -- #AI_CARGO_PACKAGE - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - self:T( CargoCarrier ) - self.CargoCarrier = CargoCarrier - - return self -end - ---- Board Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #number Speed --- @param #number BoardDistance --- @param #number Angle -function AI_CARGO_PACKAGE:onafterOnBoard( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - self:F() - - self.CargoInAir = self.CargoCarrier:InAir() - - self:T( self.CargoInAir ) - - -- Only move the CargoCarrier to the New CargoCarrier when the New CargoCarrier is not in the air. - if not self.CargoInAir then - - local Points = {} - - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - self:T( { CargoCarrierHeading, CargoDeployHeading } ) - local CargoDeployPointVec2 = CargoCarrier:GetPointVec2():Translate( BoardDistance, CargoDeployHeading ) - - Points[#Points+1] = StartPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoCarrier:TaskRoute( Points ) - self.CargoCarrier:SetTask( TaskRoute, 1 ) - end - - self:Boarded( CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - -end - ---- Check if CargoCarrier is near the Cargo to be Loaded. --- @param #AI_CARGO_PACKAGE self --- @param Wrapper.Unit#UNIT CargoCarrier --- @return #boolean -function AI_CARGO_PACKAGE:IsNear( CargoCarrier ) - self:F() - - local CargoCarrierPoint = CargoCarrier:GetPointVec2() - - local Distance = CargoCarrierPoint:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) - self:T( Distance ) - - if Distance <= self.NearRadius then - return true - else - return false - end -end - ---- Boarded Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_PACKAGE:onafterOnBoarded( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - self:F() - - if self:IsNear( CargoCarrier ) then - self:__Load( 1, CargoCarrier, Speed, LoadDistance, Angle ) - else - self:__Boarded( 1, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - end -end - ---- UnBoard Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param #number Speed --- @param #number UnLoadDistance --- @param #number UnBoardDistance --- @param #number Radius --- @param #number Angle -function AI_CARGO_PACKAGE:onafterUnBoard( From, Event, To, CargoCarrier, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle ) - self:F() - - self.CargoInAir = self.CargoCarrier:InAir() - - self:T( self.CargoInAir ) - - -- Only unboard the cargo when the carrier is not in the air. - -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). - if not self.CargoInAir then - - self:_Next( self.FsmP.UnLoad, UnLoadDistance, Angle ) - - local Points = {} - - local StartPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - self:T( { CargoCarrierHeading, CargoDeployHeading } ) - local CargoDeployPointVec2 = StartPointVec2:Translate( UnBoardDistance, CargoDeployHeading ) - - Points[#Points+1] = StartPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = CargoCarrier:TaskRoute( Points ) - CargoCarrier:SetTask( TaskRoute, 1 ) - end - - self:__UnBoarded( 1 , CargoCarrier, Speed ) - -end - ---- UnBoarded Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_PACKAGE:onafterUnBoarded( From, Event, To, CargoCarrier, Speed ) - self:F() - - if self:IsNear( CargoCarrier ) then - self:__UnLoad( 1, CargoCarrier, Speed ) - else - self:__UnBoarded( 1, CargoCarrier, Speed ) - end -end - ---- Load Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #number Speed --- @param #number LoadDistance --- @param #number Angle -function AI_CARGO_PACKAGE:onafterLoad( From, Event, To, CargoCarrier, Speed, LoadDistance, Angle ) - self:F() - - self.CargoCarrier = CargoCarrier - - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = StartPointVec2:Translate( LoadDistance, CargoDeployHeading ) - - local Points = {} - Points[#Points+1] = StartPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoCarrier:TaskRoute( Points ) - self.CargoCarrier:SetTask( TaskRoute, 1 ) - -end - ---- UnLoad Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param #number Distance --- @param #number Angle -function AI_CARGO_PACKAGE:onafterUnLoad( From, Event, To, CargoCarrier, Speed, Distance, Angle ) - self:F() - - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = StartPointVec2:Translate( Distance, CargoDeployHeading ) - - self.CargoCarrier = CargoCarrier - - local Points = {} - Points[#Points+1] = StartPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoCarrier:TaskRoute( Points ) - self.CargoCarrier:SetTask( TaskRoute, 1 ) - -end - - -end - -do -- AI_CARGO_GROUP - - --- @type AI_CARGO_GROUP - -- @extends AI.AI_Cargo#AI_CARGO - -- @field Set#SET_BASE CargoSet A set of cargo objects. - -- @field #string Name A string defining the name of the cargo group. The name is the unique identifier of the cargo. - AI_CARGO_GROUP = { - ClassName = "AI_CARGO_GROUP", - } - ---- AI_CARGO_GROUP constructor. --- @param #AI_CARGO_GROUP self --- @param Core.Set#Set_BASE CargoSet --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_GROUP -function AI_CARGO_GROUP:New( CargoSet, Type, Name, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO:New( Type, Name, 0, ReportRadius, NearRadius ) ) -- #AI_CARGO_GROUP - self:F( { Type, Name, ReportRadius, NearRadius } ) - - self.CargoSet = CargoSet - - - return self -end - -end -- AI_CARGO_GROUP - -do -- AI_CARGO_GROUPED - - --- @type AI_CARGO_GROUPED - -- @extends AI.AI_Cargo#AI_CARGO_GROUP - AI_CARGO_GROUPED = { - ClassName = "AI_CARGO_GROUPED", - } - ---- AI_CARGO_GROUPED constructor. --- @param #AI_CARGO_GROUPED self --- @param Core.Set#Set_BASE CargoSet --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_GROUPED -function AI_CARGO_GROUPED:New( CargoSet, Type, Name, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO_GROUP:New( CargoSet, Type, Name, ReportRadius, NearRadius ) ) -- #AI_CARGO_GROUPED - self:F( { Type, Name, ReportRadius, NearRadius } ) - - return self -end - ---- Enter Boarding State. --- @param #AI_CARGO_GROUPED self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onenterBoarding( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - if From == "UnLoaded" then - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - Cargo:__Board( 1, CargoCarrier ) - end - ) - - self:__Boarding( 1, CargoCarrier ) - end - -end - ---- Enter Loaded State. --- @param #AI_CARGO_GROUPED self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onenterLoaded( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - if From == "UnLoaded" then - -- For each Cargo object within the AI_CARGO_GROUPED, load each cargo to the CargoCarrier. - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - Cargo:Load( CargoCarrier ) - end - end -end - ---- Leave Boarding State. --- @param #AI_CARGO_GROUPED self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onleaveBoarding( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - local Boarded = true - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( Cargo.current ) - if not Cargo:is( "Loaded" ) then - Boarded = false - end - end - - if not Boarded then - self:__Boarding( 1, CargoCarrier ) - else - self:__Load( 1, CargoCarrier ) - end - return Boarded -end - ---- Enter UnBoarding State. --- @param #AI_CARGO_GROUPED self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onenterUnBoarding( From, Event, To, ToPointVec2 ) - self:F() - - local Timer = 1 - - if From == "Loaded" then - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - Cargo:__UnBoard( Timer, ToPointVec2 ) - Timer = Timer + 10 - end - ) - - self:__UnBoarding( 1, ToPointVec2 ) - end - -end - ---- Leave UnBoarding State. --- @param #AI_CARGO_GROUPED self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onleaveUnBoarding( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "UnBoarding" then - local UnBoarded = true - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( Cargo.current ) - if not Cargo:is( "UnLoaded" ) then - UnBoarded = false - end - end - - if UnBoarded then - return true - else - self:__UnBoarding( 1, ToPointVec2 ) - end - - return false - end - -end - ---- UnBoard Event. --- @param #AI_CARGO_GROUPED self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onafterUnBoarding( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - self:__UnLoad( 1, ToPointVec2 ) -end - - - ---- Enter UnLoaded State. --- @param #AI_CARGO_GROUPED self --- @param Core.Point#POINT_VEC2 --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onenterUnLoaded( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - if From == "Loaded" then - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - Cargo:UnLoad( ToPointVec2 ) - end - ) - - end - -end - -end -- AI_CARGO_GROUPED - - - diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua new file mode 100644 index 000000000..d11687396 --- /dev/null +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -0,0 +1,1073 @@ +--- **AI** -- Build large **formations** of AI @{Group}s flying together. +-- +-- ![Banner Image](..\Presentations\AI_FORMATION\Dia1.JPG) +-- +-- === +-- +-- AI_FORMATION makes AI @{GROUP}s fly in formation of various compositions. +-- The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! +-- The purpose of the class is to: +-- +-- * Make formation building a process that can be managed while in flight, rather than a task. +-- * Human players can guide formations, consisting of larget planes. +-- * Build large formations (like a large bomber field). +-- * Form formations that DCS does not support off the shelve. +-- +-- A few remarks: +-- +-- * Depending on the type of plane, the change in direction by the leader may result in the formation getting disentangled while in flight and needs to be rebuild. +-- * Formations are vulnerable to collissions, but is depending on the type of plane, the distance between the planes and the speed and angle executed by the leader. +-- * Formations may take a while to build up. +-- +-- As a result, the AI_FORMATION is not perfect, but is very useful to: +-- +-- * Model large formations when flying straight line. +-- * Make humans guide a large formation, when the planes are wide from each other. +-- +-- There are the following types of classes defined: +-- +-- * @{#AI_FORMATION}: Create a formation from several @{GROUP}s. +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [AI_FORMATION Demo Missions source code]() +-- +-- ### [AI_FORMATION Demo Missions, only for beta testers]() +-- +-- ### [ALL Demo Missions pack of the last release]() +-- +-- ==== +-- +-- # YouTube Channel +-- +--- ### [AI_FORMATION YouTube Channel]() +-- +-- === +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- +-- @module AI_Formation + +--- AI_FORMATION class +-- @type AI_FORMATION +-- @extends Fsm#FSM_SET +-- @field Unit#UNIT FollowUnit +-- @field Set#SET_GROUP FollowGroupSet +-- @field #string FollowName +-- @field #AI_FORMATION.MODE FollowMode The mode the escort is in. +-- @field Scheduler#SCHEDULER FollowScheduler The instance of the SCHEDULER class. +-- @field #number FollowDistance The current follow distance. +-- @field #boolean ReportTargets If true, nearby targets are reported. +-- @Field DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the FollowGroup. +-- @field DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup. +-- @field Menu#MENU_CLIENT FollowMenuResumeMission + + +--- # AI_FORMATION class, extends @{Fsm#FSM_SET} +-- +-- The #AI_FORMATION class allows you to build large formations, make AI follow a @{Client#CLIENT} (player) leader or a @{Unit#UNIT} (AI) leader. +-- +-- AI_FORMATION makes AI @{GROUP}s fly in formation of various compositions. +-- The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! +-- The purpose of the class is to: +-- +-- * Make formation building a process that can be managed while in flight, rather than a task. +-- * Human players can guide formations, consisting of larget planes. +-- * Build large formations (like a large bomber field). +-- * Form formations that DCS does not support off the shelve. +-- +-- A few remarks: +-- +-- * Depending on the type of plane, the change in direction by the leader may result in the formation getting disentangled while in flight and needs to be rebuild. +-- * Formations are vulnerable to collissions, but is depending on the type of plane, the distance between the planes and the speed and angle executed by the leader. +-- * Formations may take a while to build up. +-- +-- As a result, the AI_FORMATION is not perfect, but is very useful to: +-- +-- * Model large formations when flying straight line. You can build close formations when doing this. +-- * Make humans guide a large formation, when the planes are wide from each other. +-- +-- ## AI_FORMATION construction +-- +-- Create a new SPAWN object with the @{#AI_FORMATION.New} method: +-- +-- * @{Follow#AI_FORMATION.New}(): Creates a new AI_FORMATION object from a @{Group#GROUP} for a @{Client#CLIENT} or a @{Unit#UNIT}, with an optional briefing text. +-- +-- ## Formation methods +-- +-- The following methods can be used to set or change the formation: +-- +-- * @{AI_Formation#AI_FORMATION.FormationLine}(): Form a line formation (core formation function). +-- * @{AI_Formation#AI_FORMATION.FormationTrail}(): Form a trail formation. +-- * @{AI_Formation#AI_FORMATION.FormationLeftLine}(): Form a left line formation. +-- * @{AI_Formation#AI_FORMATION.FormationRightLine}(): Form a right line formation. +-- * @{AI_Formation#AI_FORMATION.FormationRightWing}(): Form a right wing formation. +-- * @{AI_Formation#AI_FORMATION.FormationLeftWing}(): Form a left wing formation. +-- * @{AI_Formation#AI_FORMATION.FormationCenterWing}(): Form a center wing formation. +-- * @{AI_Formation#AI_FORMATION.FormationCenterVic}(): Form a Vic formation (same as CenterWing. +-- * @{AI_Formation#AI_FORMATION.FormationCenterBoxed}(): Form a center boxed formation. +-- +-- ## Randomization +-- +-- Use the method @{AI_Formation#AI_FORMATION.SetFlightRandomization}() to simulate the formation flying errors that pilots make while in formation. Is a range set in meters. +-- +-- @usage +-- local FollowGroupSet = SET_GROUP:New():FilterCategories("plane"):FilterCoalitions("blue"):FilterPrefixes("Follow"):FilterStart() +-- FollowGroupSet:Flush() +-- local LeaderUnit = UNIT:FindByName( "Leader" ) +-- local LargeFormation = AI_FORMATION:New( LeaderUnit, FollowGroupSet, "Center Wing Formation", "Briefing" ) +-- LargeFormation:FormationCenterWing( 500, 50, 0, 250, 250 ) +-- LargeFormation:__Start( 1 ) +-- +-- @field #AI_FORMATION +AI_FORMATION = { + ClassName = "AI_FORMATION", + FollowName = nil, -- The Follow Name + FollowUnit = nil, + FollowGroupSet = nil, + FollowMode = 1, + MODE = { + FOLLOW = 1, + MISSION = 2, + }, + FollowScheduler = nil, + OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE, + OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION, +} + +--- AI_FORMATION.Mode class +-- @type AI_FORMATION.MODE +-- @field #number FOLLOW +-- @field #number MISSION + +--- MENUPARAM type +-- @type MENUPARAM +-- @field #AI_FORMATION ParamSelf +-- @field #Distance ParamDistance +-- @field #function ParamFunction +-- @field #string ParamMessage + +--- AI_FORMATION class constructor for an AI group +-- @param #AI_FORMATION self +-- @param Unit#UNIT FollowUnit The UNIT leading the FolllowGroupSet. +-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. +-- @param #string FollowName Name of the escort. +-- @return #AI_FORMATION self +function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefing ) --R2.1 + local self = BASE:Inherit( self, FSM_SET:New( FollowGroupSet ) ) + self:F( { FollowUnit, FollowGroupSet, FollowName } ) + + self.FollowUnit = FollowUnit -- Unit#UNIT + self.FollowGroupSet = FollowGroupSet -- Set#SET_GROUP + + self:SetFlightRandomization( 2 ) + + self:SetStartState( "None" ) + + self:AddTransition( "*", "Stop", "Stopped" ) + + self:AddTransition( "None", "Start", "Following" ) + + self:AddTransition( "*", "FormationLine", "*" ) + --- FormationLine Handler OnBefore for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnBeforeFormationLine + -- @param #AI_FORMATION self + -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + -- @return #boolean + + --- FormationLine Handler OnAfter for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnAfterFormationLine + -- @param #AI_FORMATION self + -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationLine Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] FormationLine + -- @param #AI_FORMATION self + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationLine Asynchronous Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] __FormationLine + -- @param #AI_FORMATION self + -- @param #number Delay + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + self:AddTransition( "*", "FormationTrail", "*" ) + --- FormationTrail Handler OnBefore for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnBeforeFormationTrail + -- @param #AI_FORMATION self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @return #boolean + + --- FormationTrail Handler OnAfter for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnAfterFormationTrail + -- @param #AI_FORMATION self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + + --- FormationTrail Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] FormationTrail + -- @param #AI_FORMATION self + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + + --- FormationTrail Asynchronous Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] __FormationTrail + -- @param #AI_FORMATION self + -- @param #number Delay + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + + self:AddTransition( "*", "FormationStack", "*" ) + --- FormationStack Handler OnBefore for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnBeforeFormationStack + -- @param #AI_FORMATION self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @return #boolean + + --- FormationStack Handler OnAfter for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnAfterFormationStack + -- @param #AI_FORMATION self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + + --- FormationStack Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] FormationStack + -- @param #AI_FORMATION self + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + + --- FormationStack Asynchronous Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] __FormationStack + -- @param #AI_FORMATION self + -- @param #number Delay + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + + self:AddTransition( "*", "FormationLeftLine", "*" ) + --- FormationLeftLine Handler OnBefore for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnBeforeFormationLeftLine + -- @param #AI_FORMATION self + -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + -- @return #boolean + + --- FormationLeftLine Handler OnAfter for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnAfterFormationLeftLine + -- @param #AI_FORMATION self + -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationLeftLine Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] FormationLeftLine + -- @param #AI_FORMATION self + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationLeftLine Asynchronous Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] __FormationLeftLine + -- @param #AI_FORMATION self + -- @param #number Delay + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + self:AddTransition( "*", "FormationRightLine", "*" ) + --- FormationRightLine Handler OnBefore for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnBeforeFormationRightLine + -- @param #AI_FORMATION self + -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + -- @return #boolean + + --- FormationRightLine Handler OnAfter for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnAfterFormationRightLine + -- @param #AI_FORMATION self + -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationRightLine Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] FormationRightLine + -- @param #AI_FORMATION self + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationRightLine Asynchronous Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] __FormationRightLine + -- @param #AI_FORMATION self + -- @param #number Delay + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + self:AddTransition( "*", "FormationLeftWing", "*" ) + --- FormationLeftWing Handler OnBefore for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnBeforeFormationLeftWing + -- @param #AI_FORMATION self + -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + -- @return #boolean + + --- FormationLeftWing Handler OnAfter for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnAfterFormationLeftWing + -- @param #AI_FORMATION self + -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationLeftWing Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] FormationLeftWing + -- @param #AI_FORMATION self + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationLeftWing Asynchronous Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] __FormationLeftWing + -- @param #AI_FORMATION self + -- @param #number Delay + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + self:AddTransition( "*", "FormationRightWing", "*" ) + --- FormationRightWing Handler OnBefore for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnBeforeFormationRightWing + -- @param #AI_FORMATION self + -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + -- @return #boolean + + --- FormationRightWing Handler OnAfter for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnAfterFormationRightWing + -- @param #AI_FORMATION self + -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationRightWing Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] FormationRightWing + -- @param #AI_FORMATION self + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationRightWing Asynchronous Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] __FormationRightWing + -- @param #AI_FORMATION self + -- @param #number Delay + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + self:AddTransition( "*", "FormationCenterWing", "*" ) + --- FormationCenterWing Handler OnBefore for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnBeforeFormationCenterWing + -- @param #AI_FORMATION self + -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + -- @return #boolean + + --- FormationCenterWing Handler OnAfter for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnAfterFormationCenterWing + -- @param #AI_FORMATION self + -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationCenterWing Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] FormationCenterWing + -- @param #AI_FORMATION self + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationCenterWing Asynchronous Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] __FormationCenterWing + -- @param #AI_FORMATION self + -- @param #number Delay + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + self:AddTransition( "*", "FormationVic", "*" ) + --- FormationVic Handler OnBefore for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnBeforeFormationVic + -- @param #AI_FORMATION self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + -- @return #boolean + + --- FormationVic Handler OnAfter for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnAfterFormationVic + -- @param #AI_FORMATION self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationVic Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] FormationVic + -- @param #AI_FORMATION self + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + --- FormationVic Asynchronous Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] __FormationVic + -- @param #AI_FORMATION self + -- @param #number Delay + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + + self:AddTransition( "*", "FormationBox", "*" ) + --- FormationBox Handler OnBefore for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnBeforeFormationBox + -- @param #AI_FORMATION self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + -- @param #number ZLevels The amount of levels on the Z-axis. + -- @return #boolean + + --- FormationBox Handler OnAfter for AI_FORMATION + -- @function [parent=#AI_FORMATION] OnAfterFormationBox + -- @param #AI_FORMATION self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + -- @param #number ZLevels The amount of levels on the Z-axis. + + --- FormationBox Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] FormationBox + -- @param #AI_FORMATION self + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + -- @param #number ZLevels The amount of levels on the Z-axis. + + --- FormationBox Asynchronous Trigger for AI_FORMATION + -- @function [parent=#AI_FORMATION] __FormationBox + -- @param #AI_FORMATION self + -- @param #number Delay + -- @param #number XStart The start position on the X-axis in meters for the first group. + -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. + -- @param #nubmer YStart The start position on the Y-axis in meters for the first group. + -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. + -- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. + -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. + -- @param #number ZLevels The amount of levels on the Z-axis. + + + self:AddTransition( "*", "Follow", "Following" ) + + self:FormationLeftLine( 500, 0, 250, 250 ) + + self.FollowName = FollowName + self.FollowBriefing = FollowBriefing + + + self.CT1 = 0 + self.GT1 = 0 + + self.FollowMode = AI_FORMATION.MODE.MISSION + + return self +end + +--- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to. +-- This allows to visualize where the escort is flying to. +-- @param #AI_FORMATION self +-- @param #boolean SmokeDirection If true, then the direction vector will be smoked. +-- @return #AI_FORMATION +function AI_FORMATION:TestSmokeDirectionVector( SmokeDirection ) --R2.1 + self.SmokeDirectionVector = ( SmokeDirection == true ) and true or false + return self +end + +--- FormationLine Handler OnAfter for AI_FORMATION +-- @param #AI_FORMATION self +-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #number XStart The start position on the X-axis in meters for the first group. +-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. +-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group. +-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. +-- @return #AI_FORMATION +function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1 + self:F( { FollowGroupSet, From , Event ,To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace } ) + + FollowGroupSet:Flush() + + local FollowSet = FollowGroupSet:GetSet() + + local i = 0 + + for FollowID, FollowGroup in pairs( FollowSet ) do + + local PointVec3 = POINT_VEC3:New() + PointVec3:SetX( XStart + i * XSpace ) + PointVec3:SetY( YStart + i * YSpace ) + PointVec3:SetZ( ZStart + i * ZSpace ) + + local Vec3 = PointVec3:GetVec3() + FollowGroup:SetState( self, "FormationVec3", Vec3 ) + i = i + 1 + end + + return self + +end + +--- FormationTrail Handler OnAfter for AI_FORMATION +-- @param #AI_FORMATION self +-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #number XStart The start position on the X-axis in meters for the first group. +-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. +-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @return #AI_FORMATION +function AI_FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart ) --R2.1 + + self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,0,0) + + return self +end + + +--- FormationStack Handler OnAfter for AI_FORMATION +-- @param #AI_FORMATION self +-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #number XStart The start position on the X-axis in meters for the first group. +-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. +-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. +-- @return #AI_FORMATION +function AI_FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace ) --R2.1 + + self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,0,0) + + return self +end + + + + +--- FormationLeftLine Handler OnAfter for AI_FORMATION +-- @param #AI_FORMATION self +-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #number XStart The start position on the X-axis in meters for the first group. +-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. +-- @return #AI_FORMATION +function AI_FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1 + + self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,ZStart,ZSpace) + + return self +end + + +--- FormationRightLine Handler OnAfter for AI_FORMATION +-- @param #AI_FORMATION self +-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #number XStart The start position on the X-axis in meters for the first group. +-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. +-- @return #AI_FORMATION +function AI_FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1 + + self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,-ZStart,-ZSpace) + + return self +end + + +--- FormationLeftWing Handler OnAfter for AI_FORMATION +-- @param #AI_FORMATION self +-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #number XStart The start position on the X-axis in meters for the first group. +-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. +-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. +function AI_FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1 + + self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,ZStart,ZSpace) + + return self +end + + +--- FormationRightWing Handler OnAfter for AI_FORMATION +-- @function [parent=#AI_FORMATION] OnAfterFormationRightWing +-- @param #AI_FORMATION self +-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #number XStart The start position on the X-axis in meters for the first group. +-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. +-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. +function AI_FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1 + + self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,-ZStart,-ZSpace) + + return self +end + + +--- FormationCenterWing Handler OnAfter for AI_FORMATION +-- @param #AI_FORMATION self +-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #number XStart The start position on the X-axis in meters for the first group. +-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. +-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. +-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. +function AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1 + + local FollowSet = FollowGroupSet:GetSet() + + local i = 0 + + for FollowID, FollowGroup in pairs( FollowSet ) do + + local PointVec3 = POINT_VEC3:New() + + local Side = ( i % 2 == 0 ) and 1 or -1 + local Row = i / 2 + 1 + + PointVec3:SetX( XStart + Row * XSpace ) + PointVec3:SetY( YStart ) + PointVec3:SetZ( Side * ( ZStart + i * ZSpace ) ) + + local Vec3 = PointVec3:GetVec3() + FollowGroup:SetState( self, "FormationVec3", Vec3 ) + i = i + 1 + end + + return self +end + + +--- FormationVic Handle for AI_FORMATION +-- @param #AI_FORMATION self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #number XStart The start position on the X-axis in meters for the first group. +-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. +-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. +-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. +-- @return #AI_FORMATION +function AI_FORMATION:onafterFormationVic( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1 + + self:onafterFormationCenterWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace) + + return self +end + +--- FormationBox Handler OnAfter for AI_FORMATION +-- @param #AI_FORMATION self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param #number XStart The start position on the X-axis in meters for the first group. +-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group. +-- @param #nubmer YStart The start position on the Y-axis in meters for the first group. +-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group. +-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group. +-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group. +-- @param #number ZLevels The amount of levels on the Z-axis. +-- @return #AI_FORMATION +function AI_FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels ) --R2.1 + + local FollowSet = FollowGroupSet:GetSet() + + local i = 0 + + for FollowID, FollowGroup in pairs( FollowSet ) do + + local PointVec3 = POINT_VEC3:New() + + local ZIndex = i % ZLevels + local XIndex = math.floor( i / ZLevels ) + local YIndex = math.floor( i / ZLevels ) + + PointVec3:SetX( XStart + XIndex * XSpace ) + PointVec3:SetY( YStart + YIndex * YSpace ) + PointVec3:SetZ( -ZStart - (ZSpace * ZLevels / 2 ) + ZSpace * ZIndex ) + + local Vec3 = PointVec3:GetVec3() + FollowGroup:SetState( self, "FormationVec3", Vec3 ) + i = i + 1 + end + + return self +end + + +--- Use the method @{AI_Formation#AI_FORMATION.SetFlightRandomization}() to make the air units in your formation randomize their flight a bit while in formation. +-- @param #AI_FORMATION self +-- @param #number FlightRandomization The formation flying errors that pilots can make while in formation. Is a range set in meters. +-- @return #AI_FORMATION +function AI_FORMATION:SetFlightRandomization( FlightRandomization ) --R2.1 + + self.FlightRandomization = FlightRandomization + + return self +end + + +--- @param Follow#AI_FORMATION self +function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1 + self:F( ) + + self:T( { self.FollowUnit.UnitName, self.FollowUnit:IsAlive() } ) + if self.FollowUnit:IsAlive() then + + local ClientUnit = self.FollowUnit + + self:T( {ClientUnit.UnitName } ) + + local CT1, CT2, CV1, CV2 + CT1 = ClientUnit:GetState( self, "CT1" ) + + if CT1 == nil or CT1 == 0 then + ClientUnit:SetState( self, "CV1", ClientUnit:GetPointVec3() ) + ClientUnit:SetState( self, "CT1", timer.getTime() ) + else + CT1 = ClientUnit:GetState( self, "CT1" ) + CT2 = timer.getTime() + CV1 = ClientUnit:GetState( self, "CV1" ) + CV2 = ClientUnit:GetPointVec3() + + ClientUnit:SetState( self, "CT1", CT2 ) + ClientUnit:SetState( self, "CV1", CV2 ) + end + + FollowGroupSet:ForEachGroup( + --- @param Wrapper.Group#GROUP FollowGroup + -- @param Wrapper.Unit#UNIT ClientUnit + function( FollowGroup, Formation, ClientUnit, CT1, CV1, CT2, CV2 ) + + FollowGroup:OptionROTPassiveDefense() + FollowGroup:OptionROEReturnFire() + + local GroupUnit = FollowGroup:GetUnit( 1 ) + local FollowFormation = FollowGroup:GetState( self, "FormationVec3" ) + if FollowFormation then + local FollowDistance = FollowFormation.x + + local GT1 = GroupUnit:GetState( self, "GT1" ) + + if CT1 == nil or CT1 == 0 or GT1 == nil or GT1 == 0 then + GroupUnit:SetState( self, "GV1", GroupUnit:GetPointVec3() ) + GroupUnit:SetState( self, "GT1", timer.getTime() ) + else + local CD = ( ( CV2.x - CV1.x )^2 + ( CV2.y - CV1.y )^2 + ( CV2.z - CV1.z )^2 ) ^ 0.5 + local CT = CT2 - CT1 + + local CS = ( 3600 / CT ) * ( CD / 1000 ) / 3.6 + + local CDv = { x = CV2.x - CV1.x, y = CV2.y - CV1.y, z = CV2.z - CV1.z } + local Ca = math.atan2( CDv.x, CDv.z ) + + local GT1 = GroupUnit:GetState( self, "GT1" ) + local GT2 = timer.getTime() + local GV1 = GroupUnit:GetState( self, "GV1" ) + local GV2 = GroupUnit:GetPointVec3() + GV2:AddX( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) ) + GV2:AddY( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) ) + GV2:AddZ( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) ) + GroupUnit:SetState( self, "GT1", GT2 ) + GroupUnit:SetState( self, "GV1", GV2 ) + + + local GD = ( ( GV2.x - GV1.x )^2 + ( GV2.y - GV1.y )^2 + ( GV2.z - GV1.z )^2 ) ^ 0.5 + local GT = GT2 - GT1 + + + -- Calculate the distance + local GDv = { x = GV2.x - CV1.x, y = GV2.y - CV1.y, z = GV2.z - CV1.z } + local Alpha_T = math.atan2( GDv.x, GDv.z ) - math.atan2( CDv.x, CDv.z ) + local Alpha_R = ( Alpha_T < 0 ) and Alpha_T + 2 * math.pi or Alpha_T + local Position = math.cos( Alpha_R ) + local GD = ( ( GDv.x )^2 + ( GDv.z )^2 ) ^ 0.5 + local Distance = GD * Position + - CS * 0,5 + + -- Calculate the group direction vector + local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z } + + -- Calculate GH2, GH2 with the same height as CV2. + local GH2 = { x = GV2.x, y = CV2.y + FollowFormation.y, z = GV2.z } + + -- Calculate the angle of GV to the orthonormal plane + local alpha = math.atan2( GV.x, GV.z ) + + local GVx = FollowFormation.z * math.cos( Ca ) + FollowFormation.x * math.sin( Ca ) + local GVz = FollowFormation.x * math.cos( Ca ) - FollowFormation.z * math.sin( Ca ) + + + -- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2. + -- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2)) + local CVI = { x = CV2.x + CS * 10 * math.sin(Ca), + y = GH2.y - ( Distance + FollowFormation.x ) / 5, -- + FollowFormation.y, + z = CV2.z + CS * 10 * math.cos(Ca), + } + + -- Calculate the direction vector DV of the escort group. We use CVI as the base and CV2 as the direction. + local DV = { x = CV2.x - CVI.x, y = CV2.y - CVI.y, z = CV2.z - CVI.z } + + -- We now calculate the unary direction vector DVu, so that we can multiply DVu with the speed, which is expressed in meters / s. + -- We need to calculate this vector to predict the point the escort group needs to fly to according its speed. + -- The distance of the destination point should be far enough not to have the aircraft starting to swipe left to right... + local DVu = { x = DV.x / FollowDistance, y = DV.y, z = DV.z / FollowDistance } + + -- Now we can calculate the group destination vector GDV. + local GDV = { x = CVI.x, y = CVI.y, z = CVI.z } + + local ADDx = FollowFormation.x * math.cos(alpha) - FollowFormation.z * math.sin(alpha) + local ADDz = FollowFormation.z * math.cos(alpha) + FollowFormation.x * math.sin(alpha) + + local GDV_Formation = { + x = GDV.x - GVx, + y = GDV.y, + z = GDV.z - GVz + } + + if self.SmokeDirectionVector == true then + trigger.action.smoke( GDV, trigger.smokeColor.Green ) + trigger.action.smoke( GDV_Formation, trigger.smokeColor.White ) + end + + + + local Time = 60 + + local Speed = - ( Distance + FollowFormation.x ) / Time + local GS = Speed + CS + if Speed < 0 then + Speed = 0 + end + + -- Now route the escort to the desired point with the desired speed. + FollowGroup:RouteToVec3( GDV_Formation, GS ) -- DCS models speed in Mps (Miles per second) + end + end + end, + self, ClientUnit, CT1, CV1, CT2, CV2 + ) + + self:__Follow( -0.5 ) + end + +end + diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index 404b0d904..30e36cee0 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -12,47 +12,33 @@ -- -- ==== -- --- # **OPEN ISSUES** +-- # Demo Missions -- --- 2017-01-17: When Spawned AI is located at an airbase, it will be routed first back to the airbase after take-off. +-- ### [AI_PATROL Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/PAT%20-%20Patrolling) -- --- 2016-01-17: --- -- Fixed problem with AI returning to base too early and unexpected. --- -- ReSpawning of AI will reset the AI_PATROL and derived classes. --- -- Checked the correct workings of SCHEDULER, and it DOES work correctly. +-- ### [AI_PATROL Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/PAT%20-%20Patrolling) +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) -- -- ==== -- --- # **API CHANGE HISTORY** +-- # YouTube Channel -- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- ### [AI_PATROL YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl35HvYZKA6G22WMt7iI3zky) -- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-17: Rename of class: **AI\_PATROL\_ZONE** is the new name for the old _AI\_PATROLZONE_. --- --- 2017-01-15: Complete revision. AI_PATROL_ZONE is the base class for other AI_PATROL like classes. --- --- 2016-09-01: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** +-- ==== -- +-- ### Author: **Sven Van de Velde (FlightControl)** -- ### Contributions: -- -- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) -- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. -- --- ### Authors: --- --- * **FlightControl**: Design & Programming. +-- ==== -- -- @module AI_Patrol + --- AI_PATROL_ZONE class -- @type AI_PATROL_ZONE -- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. @@ -64,9 +50,9 @@ -- @field Functional.Spawn#SPAWN CoordTest -- @extends Core.Fsm#FSM_CONTROLLABLE ---- # 1) @{#AI_PATROL_ZONE} class, extends @{Fsm#FSM_CONTROLLABLE} +--- # AI_PATROL_ZONE class, extends @{Fsm#FSM_CONTROLLABLE} -- --- The @{#AI_PATROL_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}. +-- The AI_PATROL_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}. -- -- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) -- @@ -97,15 +83,15 @@ -- -- ![Process](..\Presentations\AI_PATROL\Dia11.JPG) -- --- ## 1.1) AI_PATROL_ZONE constructor +-- ## 1. AI_PATROL_ZONE constructor -- -- * @{#AI_PATROL_ZONE.New}(): Creates a new AI_PATROL_ZONE object. -- --- ## 1.2) AI_PATROL_ZONE is a FSM +-- ## 2. AI_PATROL_ZONE is a FSM -- -- ![Process](..\Presentations\AI_PATROL\Dia2.JPG) -- --- ### 1.2.1) AI_PATROL_ZONE States +-- ### 2.1. AI_PATROL_ZONE States -- -- * **None** ( Group ): The process is not started yet. -- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. @@ -113,7 +99,7 @@ -- * **Stopped** ( Group ): The process is stopped. -- * **Crashed** ( Group ): The AI has crashed or is dead. -- --- ### 1.2.2) AI_PATROL_ZONE Events +-- ### 2.2. AI_PATROL_ZONE Events -- -- * **Start** ( Group ): Start the process. -- * **Stop** ( Group ): Stop the process. @@ -123,17 +109,17 @@ -- * **Detected** ( Group ): The AI has detected new targets. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- --- ## 1.3) Set or Get the AI controllable +-- ## 3. Set or Get the AI controllable -- -- * @{#AI_PATROL_ZONE.SetControllable}(): Set the AIControllable. -- * @{#AI_PATROL_ZONE.GetControllable}(): Get the AIControllable. -- --- ## 1.4) Set the Speed and Altitude boundaries of the AI controllable +-- ## 4. Set the Speed and Altitude boundaries of the AI controllable -- -- * @{#AI_PATROL_ZONE.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol. -- * @{#AI_PATROL_ZONE.SetAltitude}(): Set altitude boundaries of the AI, for the next patrol. -- --- ## 1.5) Manage the detection process of the AI controllable +-- ## 5. Manage the detection process of the AI controllable -- -- The detection process of the AI controllable can be manipulated. -- Detection requires an amount of CPU power, which has an impact on your mission performance. @@ -142,7 +128,7 @@ -- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets. -- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased. -- --- The detection frequency can be set with @{#AI_PATROL_ZONE.SetDetectionInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. +-- The detection frequency can be set with @{#AI_PATROL_ZONE.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. -- Use the method @{#AI_PATROL_ZONE.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI. -- -- The detection can be filtered to potential targets in a specific zone. @@ -150,7 +136,7 @@ -- Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected -- according the weather conditions. -- --- ## 1.6) Manage the "out of fuel" in the AI_PATROL_ZONE +-- ## 6. Manage the "out of fuel" in the AI_PATROL_ZONE -- -- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. @@ -159,7 +145,7 @@ -- Once the time is finished, the old AI will return to the base. -- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this proces in place. -- --- ## 1.7) Manage "damage" behaviour of the AI in the AI_PATROL_ZONE +-- ## 7. Manage "damage" behaviour of the AI in the AI_PATROL_ZONE -- -- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on. -- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). @@ -167,8 +153,7 @@ -- -- === -- --- @field #AI_PATROL_ZONE AI_PATROL_ZONE --- +-- @field #AI_PATROL_ZONE AI_PATROL_ZONE = { ClassName = "AI_PATROL_ZONE", } @@ -202,7 +187,7 @@ function AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltit -- defafult PatrolAltType to "RADIO" if not specified self.PatrolAltType = PatrolAltType or "RADIO" - self:SetDetectionInterval( 30 ) + self:SetRefreshTimeInterval( 30 ) self.CheckStatus = true @@ -559,7 +544,7 @@ end -- @param #AI_PATROL_ZONE self -- @param #number Seconds The interval in seconds. -- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionInterval( Seconds ) +function AI_PATROL_ZONE:SetRefreshTimeInterval( Seconds ) self:F2() if Seconds then @@ -606,13 +591,13 @@ end -- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE. -- Once the time is finished, the old AI will return to the base. -- @param #AI_PATROL_ZONE self --- @param #number PatrolFuelTresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel. +-- @param #number PatrolFuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel. -- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base. -- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:ManageFuel( PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime ) +function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime ) self.PatrolManageFuel = true - self.PatrolFuelTresholdPercentage = PatrolFuelTresholdPercentage + self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime return self @@ -625,12 +610,12 @@ end -- Note that for groups, the average damage of the complete group will be calculated. -- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25. -- @param #AI_PATROL_ZONE self --- @param #number PatrolDamageTreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged. +-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged. -- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:ManageDamage( PatrolDamageTreshold ) +function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold ) self.PatrolManageDamage = true - self.PatrolDamageTreshold = PatrolDamageTreshold + self.PatrolDamageThreshold = PatrolDamageThreshold return self end @@ -763,7 +748,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To ) local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local ToPatrolZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( + local CurrentRoutePoint = CurrentPointVec3:WaypointAir( self.PatrolAltType, POINT_VEC3.RoutePointType.TakeOffParking, POINT_VEC3.RoutePointAction.FromParkingArea, @@ -778,7 +763,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To ) local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local ToPatrolZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( + local CurrentRoutePoint = CurrentPointVec3:WaypointAir( self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, @@ -804,7 +789,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To ) local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y ) --- Create a route point of type air. - local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir( + local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir( self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, @@ -846,7 +831,7 @@ function AI_PATROL_ZONE:onafterStatus() local RTB = false local Fuel = self.Controllable:GetUnit(1):GetFuel() - if Fuel < self.PatrolFuelTresholdPercentage then + if Fuel < self.PatrolFuelThresholdPercentage then self:E( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" ) local OldAIControllable = self.Controllable local AIControllableTemplate = self.Controllable:GetTemplate() @@ -861,7 +846,7 @@ function AI_PATROL_ZONE:onafterStatus() -- TODO: Check GROUP damage function. local Damage = self.Controllable:GetLife() - if Damage <= self.PatrolDamageTreshold then + if Damage <= self.PatrolDamageThreshold then self:E( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" ) RTB = true end @@ -892,7 +877,7 @@ function AI_PATROL_ZONE:onafterRTB() local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local ToPatrolZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( + local CurrentRoutePoint = CurrentPointVec3:WaypointAir( self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, diff --git a/Moose Development/Moose/Actions/Act_Account.lua b/Moose Development/Moose/Actions/Act_Account.lua index 14e83b07f..b87e0af93 100644 --- a/Moose Development/Moose/Actions/Act_Account.lua +++ b/Moose Development/Moose/Actions/Act_Account.lua @@ -70,19 +70,20 @@ do -- ACT_ACCOUNT -- Inherits from BASE local self = BASE:Inherit( self, FSM_PROCESS:New() ) -- Core.Fsm#FSM_PROCESS - self:AddTransition( "Assigned", "Start", "Waiting") - self:AddTransition( "*", "Wait", "Waiting") - self:AddTransition( "*", "Report", "Report") - self:AddTransition( "*", "Event", "Account") - self:AddTransition( "Account", "More", "Wait") - self:AddTransition( "Account", "NoMore", "Accounted") - self:AddTransition( "*", "Fail", "Failed") + self:AddTransition( "Assigned", "Start", "Waiting" ) + self:AddTransition( "*", "Wait", "Waiting" ) + self:AddTransition( "*", "Report", "Report" ) + self:AddTransition( "*", "Event", "Account" ) + self:AddTransition( "Account", "Player", "AccountForPlayer" ) + self:AddTransition( "Account", "Other", "AccountForOther" ) + self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "More", "Wait" ) + self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "NoMore", "Accounted" ) + self:AddTransition( "*", "Fail", "Failed" ) - self:AddEndState( "Accounted" ) self:AddEndState( "Failed" ) self:SetStartState( "Assigned" ) - + return self end @@ -97,6 +98,8 @@ do -- ACT_ACCOUNT function ACT_ACCOUNT:onafterStart( ProcessUnit, From, Event, To ) self:HandleEvent( EVENTS.Dead, self.onfuncEventDead ) + self:HandleEvent( EVENTS.Crash, self.onfuncEventCrash ) + self:HandleEvent( EVENTS.Hit ) self:__Wait( 1 ) end @@ -152,7 +155,6 @@ do -- ACT_ACCOUNT_DEADS -- @extends #ACT_ACCOUNT ACT_ACCOUNT_DEADS = { ClassName = "ACT_ACCOUNT_DEADS", - TargetSetUnit = nil, } @@ -160,13 +162,10 @@ do -- ACT_ACCOUNT_DEADS -- @param #ACT_ACCOUNT_DEADS self -- @param Set#SET_UNIT TargetSetUnit -- @param #string TaskName - function ACT_ACCOUNT_DEADS:New( TargetSetUnit, TaskName ) + function ACT_ACCOUNT_DEADS:New() -- Inherits from BASE local self = BASE:Inherit( self, ACT_ACCOUNT:New() ) -- #ACT_ACCOUNT_DEADS - self.TargetSetUnit = TargetSetUnit - self.TaskName = TaskName - self.DisplayInterval = 30 self.DisplayCount = 30 self.DisplayMessage = true @@ -178,8 +177,8 @@ do -- ACT_ACCOUNT_DEADS function ACT_ACCOUNT_DEADS:Init( FsmAccount ) - self.TargetSetUnit = FsmAccount.TargetSetUnit - self.TaskName = FsmAccount.TaskName + self.Task = self:GetTask() + self.TaskName = self.Task:GetName() end --- Process Events @@ -193,50 +192,94 @@ do -- ACT_ACCOUNT_DEADS function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To ) self:E( { ProcessUnit, From, Event, To } ) - self:Message( "Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed." ) + self:Message( "Your group with assigned " .. self.TaskName .. " task has " .. Task.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed." ) end --- StateMachine callback function -- @param #ACT_ACCOUNT_DEADS self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event + -- @param Wrapper.Client#CLIENT ProcessClient + -- @param Tasking.Task#TASK Task -- @param #string From + -- @param #string Event -- @param #string To - function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, Task, From, Event, To, EventData ) - self:T( { ProcessUnit, EventData, From, Event, To } ) + -- @param Core.Event#EVENTDATA EventData + function ACT_ACCOUNT_DEADS:onafterEvent( ProcessClient, Task, From, Event, To, EventData ) + self:T( { ProcessClient:GetName(), Task:GetName(), From, Event, To, EventData } ) - self:T({self.Controllable}) - - self.TargetSetUnit:Flush() - - self:T( { "Before sending Message", EventData.IniUnitName, self.TargetSetUnit:FindUnit( EventData.IniUnitName ) } ) - if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then - self:T( "Sending Message" ) - local TaskGroup = ProcessUnit:GetGroup() - self.TargetSetUnit:Remove( EventData.IniUnitName ) - self:Message( "You hit a target. Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." ) + if Task.TargetSetUnit:FindUnit( EventData.IniUnitName ) then + local PlayerName = ProcessClient:GetPlayerName() + local PlayerHit = self.PlayerHits and self.PlayerHits[EventData.IniUnitName] + if PlayerHit == PlayerName then + self:Player( EventData ) + else + self:Other( EventData ) + end end - self:T( { "After sending Message" } ) end - + --- StateMachine callback function -- @param #ACT_ACCOUNT_DEADS self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event + -- @param Wrapper.Client#CLIENT ProcessClient + -- @param Tasking.Task#TASK Task -- @param #string From + -- @param #string Event -- @param #string To - function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To ) - - if self.TargetSetUnit:Count() > 0 then + -- @param Core.Event#EVENTDATA EventData + function ACT_ACCOUNT_DEADS:onenterAccountForPlayer( ProcessClient, Task, From, Event, To, EventData ) + self:T( { ProcessClient:GetName(), Task:GetName(), From, Event, To, EventData } ) + + local TaskGroup = ProcessClient:GetGroup() + + Task.TargetSetUnit:Remove( EventData.IniUnitName ) + self:Message( "You have destroyed a target.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." ) + + local PlayerName = ProcessClient:GetPlayerName() + Task:AddProgress( PlayerName, "Destroyed " .. EventData.IniTypeName, timer.getTime(), 1 ) + + if Task.TargetSetUnit:Count() > 0 then self:__More( 1 ) else self:__NoMore( 1 ) end end + + --- StateMachine callback function + -- @param #ACT_ACCOUNT_DEADS self + -- @param Wrapper.Client#CLIENT ProcessClient + -- @param Tasking.Task#TASK Task + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Event#EVENTDATA EventData + function ACT_ACCOUNT_DEADS:onenterAccountForOther( ProcessClient, Task, From, Event, To, EventData ) + self:T( { ProcessClient:GetName(), Task:GetName(), From, Event, To, EventData } ) + + local TaskGroup = ProcessClient:GetGroup() + Task.TargetSetUnit:Remove( EventData.IniUnitName ) + self:Message( "One of the task targets has been destroyed.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." ) + + if Task.TargetSetUnit:Count() > 0 then + self:__More( 1 ) + else + self:__NoMore( 1 ) + end + end + --- DCS Events + --- @param #ACT_ACCOUNT_DEADS self + -- @param Core.Event#EVENTDATA EventData + function ACT_ACCOUNT_DEADS:OnEventHit( EventData ) + self:T( { "EventDead", EventData } ) + + if EventData.IniPlayerName and EventData.TgtDCSUnitName then + self.PlayerHits = self.PlayerHits or {} + self.PlayerHits[EventData.TgtDCSUnitName] = EventData.IniPlayerName + end + end + --- @param #ACT_ACCOUNT_DEADS self -- @param Event#EVENTDATA EventData function ACT_ACCOUNT_DEADS:onfuncEventDead( EventData ) @@ -247,4 +290,16 @@ do -- ACT_ACCOUNT_DEADS end end + --- DCS Events + + --- @param #ACT_ACCOUNT_DEADS self + -- @param Event#EVENTDATA EventData + function ACT_ACCOUNT_DEADS:onfuncEventCrash( EventData ) + self:T( { "EventDead", EventData } ) + + if EventData.IniDCSUnit then + self:Event( EventData ) + end + end + end -- ACT_ACCOUNT DEADS diff --git a/Moose Development/Moose/Actions/Act_Assign.lua b/Moose Development/Moose/Actions/Act_Assign.lua index 7d45ad21a..e04139698 100644 --- a/Moose Development/Moose/Actions/Act_Assign.lua +++ b/Moose Development/Moose/Actions/Act_Assign.lua @@ -173,8 +173,6 @@ do -- ACT_ASSIGN_ACCEPT local ProcessGroup = ProcessUnit:GetGroup() - self:Message( "You are assigned to the task " .. self.Task:GetName() ) - self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) end diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index ec5e9fff3..7f98b8b1a 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -82,6 +82,7 @@ do -- ACT_ROUTE -- @field Tasking.Task#TASK TASK -- @field Wrapper.Unit#UNIT ProcessUnit -- @field Core.Zone#ZONE_BASE Zone + -- @field Core.Point#COORDINATE Coordinate -- @extends Core.Fsm#FSM_PROCESS ACT_ROUTE = { ClassName = "ACT_ROUTE", @@ -96,12 +97,13 @@ do -- ACT_ROUTE -- Inherits from BASE local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ROUTE" ) ) -- Core.Fsm#FSM_PROCESS + self:AddTransition( "*", "Reset", "None" ) self:AddTransition( "None", "Start", "Routing" ) - self:AddTransition( "*", "Report", "Reporting" ) - self:AddTransition( "*", "Route", "Routing" ) + self:AddTransition( "*", "Report", "*" ) + self:AddTransition( "Routing", "Route", "Routing" ) self:AddTransition( "Routing", "Pause", "Pausing" ) - self:AddTransition( "*", "Abort", "Aborted" ) self:AddTransition( "Routing", "Arrive", "Arrived" ) + self:AddTransition( "*", "Cancel", "Cancelled" ) self:AddTransition( "Arrived", "Success", "Success" ) self:AddTransition( "*", "Fail", "Failed" ) self:AddTransition( "", "", "" ) @@ -109,11 +111,105 @@ do -- ACT_ROUTE self:AddEndState( "Arrived" ) self:AddEndState( "Failed" ) + self:AddEndState( "Cancelled" ) - self:SetStartState( "None" ) + self:SetStartState( "None" ) + + self:SetRouteMode( "C" ) return self end + + --- Set a Cancel Menu item. + -- @param #ACT_ROUTE self + -- @return #ACT_ROUTE + function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime ) + + MENU_GROUP_COMMAND:New( + MenuGroup, + MenuText, + ParentMenu, + self.MenuCancel, + self + ):SetTime(MenuTime) + + return self + end + + --- Set the route mode. + -- There are 2 route modes supported: + -- + -- * SetRouteMode( "B" ): Route mode is Bearing and Range. + -- * SetRouteMode( "C" ): Route mode is LL or MGRS according coordinate system setup. + -- + -- @param #ACT_ROUTE self + -- @return #ACT_ROUTE + function ACT_ROUTE:SetRouteMode( RouteMode ) + + self.RouteMode = RouteMode + + return self + end + + --- Get the routing text to be displayed. + -- The route mode determines the text displayed. + -- @param #ACT_ROUTE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable + -- @return #string + function ACT_ROUTE:GetRouteText( Controllable ) + + self:E() + + local RouteText = "" + + local Coordinate = nil -- Core.Point#COORDINATE + + if self.Coordinate then + Coordinate = self.Coordinate + end + + if self.Zone then + Coordinate = self.Zone:GetPointVec3( self.Altitude ) + Coordinate:SetHeading( self.Heading ) + end + + + local Task = self:GetTask() -- This is to dermine that the coordinates are for a specific task mode (A2A or A2G). + local CC = self:GetTask():GetMission():GetCommandCenter() + if CC then + if CC:IsModeWWII() then + -- Find closest reference point to the target. + local ShortestDistance = 0 + local ShortestReferencePoint = nil + local ShortestReferenceName = "" + self:E( { CC.ReferencePoints } ) + for ZoneName, Zone in pairs( CC.ReferencePoints ) do + self:E( { ZoneName = ZoneName } ) + local Zone = Zone -- Core.Zone#ZONE + local ZoneCoord = Zone:GetCoordinate() + local ZoneDistance = ZoneCoord:Get2DDistance( self.Coordinate ) + self:E( { ShortestDistance, ShortestReferenceName } ) + if ShortestDistance == 0 or ZoneDistance < ShortestDistance then + ShortestDistance = ZoneDistance + ShortestReferencePoint = ZoneCoord + ShortestReferenceName = CC.ReferenceNames[ZoneName] + end + end + if ShortestReferencePoint then + RouteText = Coordinate:ToStringFromRP( ShortestReferencePoint, ShortestReferenceName, Controllable ) + end + else + RouteText = Coordinate:ToString( Controllable, nil, Task ) + end + end + + return RouteText + end + + + function ACT_ROUTE:MenuCancel() + self:Cancel() + end --- Task Events @@ -189,15 +285,15 @@ do -- ACT_ROUTE_POINT --- Creates a new routing state machine. - -- The task will route a controllable to a PointVec2 until the controllable is within the Range. + -- The task will route a controllable to a Coordinate until the controllable is within the Range. -- @param #ACT_ROUTE_POINT self - -- @param Core.Point#POINT_VEC2 The PointVec2 to Target. + -- @param Core.Point#COORDINATE The Coordinate to Target. -- @param #number Range The Distance to Target. -- @param Core.Zone#ZONE_BASE Zone - function ACT_ROUTE_POINT:New( PointVec2, Range ) + function ACT_ROUTE_POINT:New( Coordinate, Range ) local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_POINT - self.PointVec2 = PointVec2 + self.Coordinate = Coordinate self.Range = Range or 0 self.DisplayInterval = 30 @@ -208,34 +304,38 @@ do -- ACT_ROUTE_POINT return self end + --- Creates a new routing state machine. + -- The task will route a controllable to a Coordinate until the controllable is within the Range. + -- @param #ACT_ROUTE_POINT self function ACT_ROUTE_POINT:Init( FsmRoute ) - self.PointVec2 = FsmRoute.PointVec2 + self.Coordinate = FsmRoute.Coordinate self.Range = FsmRoute.Range or 0 self.DisplayInterval = 30 self.DisplayCount = 30 self.DisplayMessage = true self.DisplayTime = 10 -- 10 seconds is the default + self:SetStartState("None") end - --- Set PointVec2 + --- Set Coordinate -- @param #ACT_ROUTE_POINT self - -- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to. - function ACT_ROUTE_POINT:SetPointVec2( PointVec2 ) - self:F2( { PointVec2 } ) - self.PointVec2 = PointVec2 + -- @param Core.Point#COORDINATE Coordinate The Coordinate to route to. + function ACT_ROUTE_POINT:SetCoordinate( Coordinate ) + self:F2( { Coordinate } ) + self.Coordinate = Coordinate end - --- Get PointVec2 + --- Get Coordinate -- @param #ACT_ROUTE_POINT self - -- @return Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to. - function ACT_ROUTE_POINT:GetPointVec2() - self:F2( { self.PointVec2 } ) - return self.PointVec2 + -- @return Core.Point#COORDINATE Coordinate The Coordinate to route to. + function ACT_ROUTE_POINT:GetCoordinate() + self:F2( { self.Coordinate } ) + return self.Coordinate end - --- Set Range around PointVec2 + --- Set Range around Coordinate -- @param #ACT_ROUTE_POINT self -- @param #number Range The Range to consider the arrival. Default is 10000 meters. function ACT_ROUTE_POINT:SetRange( Range ) @@ -243,7 +343,7 @@ do -- ACT_ROUTE_POINT self.Range = Range or 10000 end - --- Get Range around PointVec2 + --- Get Range around Coordinate -- @param #ACT_ROUTE_POINT self -- @return #number The Range to consider the arrival. Default is 10000 meters. function ACT_ROUTE_POINT:GetRange() @@ -257,7 +357,7 @@ do -- ACT_ROUTE_POINT function ACT_ROUTE_POINT:onfuncHasArrived( ProcessUnit ) if ProcessUnit:IsAlive() then - local Distance = self.PointVec2:Get2DDistance( ProcessUnit:GetPointVec2() ) + local Distance = self.Coordinate:Get2DDistance( ProcessUnit:GetCoordinate() ) if Distance <= self.Range then local RouteText = "You have arrived." @@ -277,10 +377,9 @@ do -- ACT_ROUTE_POINT -- @param #string Event -- @param #string From -- @param #string To - function ACT_ROUTE_POINT:onenterReporting( ProcessUnit, From, Event, To ) + function ACT_ROUTE_POINT:onafterReport( ProcessUnit, From, Event, To ) - local TaskUnitPointVec2 = ProcessUnit:GetPointVec2() - local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( self.PointVec2 ) .. " km." + local RouteText = self:GetRouteText( ProcessUnit ) self:Message( RouteText ) end @@ -329,8 +428,12 @@ do -- ACT_ROUTE_ZONE --- Set Zone -- @param #ACT_ROUTE_ZONE self -- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to. - function ACT_ROUTE_ZONE:SetZone( Zone ) + -- @param #number Altitude + -- @param #number Heading + function ACT_ROUTE_ZONE:SetZone( Zone, Altitude, Heading ) -- R2.2 Added altitude and heading self.Zone = Zone + self.Altitude = Altitude + self.Heading = Heading end --- Get Zone @@ -362,13 +465,10 @@ do -- ACT_ROUTE_ZONE -- @param #string Event -- @param #string From -- @param #string To - function ACT_ROUTE_ZONE:onenterReporting( ProcessUnit, From, Event, To ) + function ACT_ROUTE_ZONE:onafterReport( ProcessUnit, From, Event, To ) + self:E( { ProcessUnit = ProcessUnit } ) - local ZoneVec2 = self.Zone:GetVec2() - local ZonePointVec2 = POINT_VEC2:New( ZoneVec2.x, ZoneVec2.y ) - local TaskUnitVec2 = ProcessUnit:GetVec2() - local TaskUnitPointVec2 = POINT_VEC2:New( TaskUnitVec2.x, TaskUnitVec2.y ) - local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( ZonePointVec2 ) .. " km." + local RouteText = self:GetRouteText( ProcessUnit ) self:Message( RouteText ) end diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index e047c76d1..b3853173d 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -1,36 +1,13 @@ ---- **Core** - BASE forms **the basis of the MOOSE framework**. Each class within the MOOSE framework derives from BASE. +--- **Core** -- BASE forms **the basis of the MOOSE framework**. Each class within the MOOSE framework derives from BASE. -- -- ![Banner Image](..\Presentations\BASE\Dia1.JPG) -- -- === -- --- The @{#BASE} class is the core root class from where every other class in moose is derived. --- --- === --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) --- YYYY-MM-DD: CLASS:**NewFunction( Params )** added --- --- Hereby the change log: --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- +-- ### Author: **Sven Van de Velde (FlightControl)** -- ### Contributions: -- --- * None. --- --- ### Authors: --- --- * **FlightControl**: Design & Programming +-- ==== -- -- @module Base @@ -219,16 +196,21 @@ local _ClassID = 0 BASE = { ClassName = "BASE", ClassID = 0, - _Private = {}, Events = {}, - States = {} + States = {}, + _ = {}, } + +--- @field #BASE.__ +BASE.__ = {} + --- The Formation Class -- @type FORMATION -- @field Cone A cone formation. FORMATION = { - Cone = "Cone" + Cone = "Cone", + Vee = "Vee" } @@ -246,47 +228,19 @@ FORMATION = { -- @return #BASE function BASE:New() local self = routines.utils.deepCopy( self ) -- Create a new self instance - local MetaTable = {} - setmetatable( self, MetaTable ) - self.__index = self + _ClassID = _ClassID + 1 self.ClassID = _ClassID - + + -- This is for "private" methods... + -- When a __ is passed to a method as "self", the __index will search for the method on the public method list too! +-- if rawget( self, "__" ) then + --setmetatable( self, { __index = self.__ } ) +-- end return self end -function BASE:_Destructor() - --self:E("_Destructor") - - --self:EventRemoveAll() -end - - --- THIS IS WHY WE NEED LUA 5.2 ... -function BASE:_SetDestructor() - - -- TODO: Okay, this is really technical... - -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak... - -- Therefore, I am parking this logic until I've properly discussed all this with the community. - - local proxy = newproxy(true) - local proxyMeta = getmetatable(proxy) - - proxyMeta.__gc = function () - env.info("In __gc for " .. self:GetClassNameAndID() ) - if self._Destructor then - self:_Destructor() - end - end - - -- keep the userdata from newproxy reachable until the object - -- table is about to be garbage-collected - then the __gc hook - -- will be invoked and the destructor called - rawset( self, '__proxy', proxy ) - -end - --- This is the worker method to inherit from a parent class. -- @param #BASE self -- @param Child is the Child class that inherits. @@ -294,15 +248,20 @@ end -- @return #BASE Child function BASE:Inherit( Child, Parent ) local Child = routines.utils.deepCopy( Child ) - --local Parent = routines.utils.deepCopy( Parent ) - --local Parent = Parent + if Child ~= nil then - setmetatable( Child, Parent ) - Child.__index = Child - + + -- This is for "private" methods... + -- When a __ is passed to a method as "self", the __index will search for the method on the public method list of the same object too! + if rawget( Child, "__" ) then + setmetatable( Child, { __index = Child.__ } ) + setmetatable( Child.__, { __index = Parent } ) + else + setmetatable( Child, { __index = Parent } ) + end + --Child:_SetDestructor() end - --self:T( 'Inherited from ' .. Parent.ClassName ) return Child end @@ -316,11 +275,76 @@ end -- @param #BASE Child is the Child class from which the Parent class needs to be retrieved. -- @return #BASE function BASE:GetParent( Child ) - local Parent = getmetatable( Child ) --- env.info('Inherited class of ' .. Child.ClassName .. ' is ' .. Parent.ClassName ) + local Parent + -- BASE class has no parent + if Child.ClassName == 'BASE' then + Parent = nil + elseif rawget( Child, "__" ) then + Parent = getmetatable( Child.__ ).__index + else + Parent = getmetatable( Child ).__index + end return Parent end +--- This is the worker method to check if an object is an (sub)instance of a class. +-- +-- ### Examples: +-- +-- * ZONE:New( 'some zone' ):IsInstanceOf( ZONE ) will return true +-- * ZONE:New( 'some zone' ):IsInstanceOf( 'ZONE' ) will return true +-- * ZONE:New( 'some zone' ):IsInstanceOf( 'zone' ) will return true +-- * ZONE:New( 'some zone' ):IsInstanceOf( 'BASE' ) will return true +-- +-- * ZONE:New( 'some zone' ):IsInstanceOf( 'GROUP' ) will return false +-- +-- @param #BASE self +-- @param ClassName is the name of the class or the class itself to run the check against +-- @return #boolean +function BASE:IsInstanceOf( ClassName ) + + -- Is className NOT a string ? + if type( ClassName ) ~= 'string' then + + -- Is className a Moose class ? + if type( ClassName ) == 'table' and ClassName.ClassName ~= nil then + + -- Get the name of the Moose class as a string + ClassName = ClassName.ClassName + + -- className is neither a string nor a Moose class, throw an error + else + + -- I'm not sure if this should take advantage of MOOSE logging function, or throw an error for pcall + local err_str = 'className parameter should be a string; parameter received: '..type( ClassName ) + self:E( err_str ) + -- error( err_str ) + return false + + end + end + + ClassName = string.upper( ClassName ) + + if string.upper( self.ClassName ) == ClassName then + return true + end + + local Parent = self:GetParent(self) + + while Parent do + + if string.upper( Parent.ClassName ) == ClassName then + return true + end + + Parent = Parent:GetParent(Parent) + + end + + return false + +end --- Get the ClassName + ClassID of the class instance. -- The ClassName + ClassID is formatted as '%s#%09d'. -- @param #BASE self @@ -360,7 +384,7 @@ do -- Event Handling -- @param #BASE self -- @return #number The @{Event} processing Priority. function BASE:GetEventPriority() - return self._Private.EventPriority or 5 + return self._.EventPriority or 5 end --- Set the Class @{Event} processing Priority. @@ -370,7 +394,7 @@ do -- Event Handling -- @param #number EventPriority The @{Event} processing Priority. -- @return self function BASE:SetEventPriority( EventPriority ) - self._Private.EventPriority = EventPriority + self._.EventPriority = EventPriority end --- Remove all subscribed events @@ -401,7 +425,7 @@ do -- Event Handling -- @return #BASE function BASE:UnHandleEvent( Event ) - self:EventDispatcher():Remove( self, Event ) + self:EventDispatcher():RemoveEvent( self, Event ) return self end @@ -453,6 +477,12 @@ do -- Event Handling -- @param #BASE self -- @param Core.Event#EVENTDATA EventData The EventData structure. + --- Occurs when an object is dead. + -- initiator : The unit that is dead. + -- @function [parent=#BASE] OnEventDead + -- @param #BASE self + -- @param Core.Event#EVENTDATA EventData The EventData structure. + --- Occurs when an object is completely destroyed. -- initiator : The unit that is was destroyed. -- @function [parent=#BASE] OnEvent @@ -622,7 +652,6 @@ function BASE:SetState( Object, Key, Value ) self.States[ClassNameAndID] = self.States[ClassNameAndID] or {} self.States[ClassNameAndID][Key] = Value - self:T2( { ClassNameAndID, Key, Value } ) return self.States[ClassNameAndID][Key] end @@ -633,7 +662,6 @@ end -- @param #BASE self -- @param Object The object that holds the Value set by the Key. -- @param Key The key that is used to retrieve the value. Note that the key can be a #string, but it can also be any other type! --- @param Value The value to is stored in the Object. -- @return The Value retrieved. function BASE:GetState( Object, Key ) @@ -641,7 +669,6 @@ function BASE:GetState( Object, Key ) if self.States[ClassNameAndID] then local Value = self.States[ClassNameAndID][Key] or false - self:T2( { ClassNameAndID, Key, Value } ) return Value end @@ -912,3 +939,35 @@ end +--- old stuff + +--function BASE:_Destructor() +-- --self:E("_Destructor") +-- +-- --self:EventRemoveAll() +--end + + +-- THIS IS WHY WE NEED LUA 5.2 ... +--function BASE:_SetDestructor() +-- +-- -- TODO: Okay, this is really technical... +-- -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak... +-- -- Therefore, I am parking this logic until I've properly discussed all this with the community. +-- +-- local proxy = newproxy(true) +-- local proxyMeta = getmetatable(proxy) +-- +-- proxyMeta.__gc = function () +-- env.info("In __gc for " .. self:GetClassNameAndID() ) +-- if self._Destructor then +-- self:_Destructor() +-- end +-- end +-- +-- -- keep the userdata from newproxy reachable until the object +-- -- table is about to be garbage-collected - then the __gc hook +-- -- will be invoked and the destructor called +-- rawset( self, '__proxy', proxy ) +-- +--end \ No newline at end of file diff --git a/Moose Development/Moose/Core/Cargo.lua b/Moose Development/Moose/Core/Cargo.lua new file mode 100644 index 000000000..2f5199abb --- /dev/null +++ b/Moose Development/Moose/Core/Cargo.lua @@ -0,0 +1,1573 @@ +--- **Core** -- Management of CARGO logistics, that can be transported from and to transportation carriers. +-- +-- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) +-- +-- === +-- +-- Cargo can be of various forms, always are composed out of ONE object ( one unit or one static or one slingload crate ): +-- +-- * CARGO_UNIT, represented by a @{Unit} in a singleton @{Group}: Cargo can be represented by a Unit in a Group. a CARGO_UNIT is representable... +-- * CARGO_GROUP, represented by a @{Group}. A CARGO_GROUP is reportable... +-- +-- This module is still under construction, but is described above works already, and will keep working ... +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [CARGO Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CGO%20-%20Cargo) +-- +-- ### [CARGO Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CGO%20-%20Cargo) +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [CARGO YouTube Channel](https://www.youtube.com/watch?v=tM00lTlkpYs&list=PL7ZUrU4zZUl2zUTuKrLW5RsO9zLMqUtbf) +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: +-- +-- ==== +-- +-- @module Cargo + +-- Events + +-- Board + +--- Boards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo to the Carrier. +-- The cargo must be in the **UnLoaded** state. +-- @function [parent=#CARGO] Board +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. +-- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). + +--- Boards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo to the Carrier. +-- The cargo must be in the **UnLoaded** state. +-- @function [parent=#CARGO] __Board +-- @param #CARGO self +-- @param #number DelaySeconds The amount of seconds to delay the action. +-- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. +-- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). + + +-- UnBoard + +--- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. +-- The cargo must be in the **Loaded** state. +-- @function [parent=#CARGO] UnBoard +-- @param #CARGO self +-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. + +--- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. +-- The cargo must be in the **Loaded** state. +-- @function [parent=#CARGO] __UnBoard +-- @param #CARGO self +-- @param #number DelaySeconds The amount of seconds to delay the action. +-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. + + +-- Load + +--- Loads the cargo to a Carrier. The event will load the cargo into the Carrier regardless of its position. There will be no movement simulated of the cargo loading. +-- The cargo must be in the **UnLoaded** state. +-- @function [parent=#CARGO] Load +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. + +--- Loads the cargo to a Carrier. The event will load the cargo into the Carrier regardless of its position. There will be no movement simulated of the cargo loading. +-- The cargo must be in the **UnLoaded** state. +-- @function [parent=#CARGO] __Load +-- @param #CARGO self +-- @param #number DelaySeconds The amount of seconds to delay the action. +-- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. + + +-- UnLoad + +--- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. +-- The cargo must be in the **Loaded** state. +-- @function [parent=#CARGO] UnLoad +-- @param #CARGO self +-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. + +--- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. +-- The cargo must be in the **Loaded** state. +-- @function [parent=#CARGO] __UnLoad +-- @param #CARGO self +-- @param #number DelaySeconds The amount of seconds to delay the action. +-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. + +-- State Transition Functions + +-- UnLoaded + +--- @function [parent=#CARGO] OnLeaveUnLoaded +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable +-- @return #boolean + +--- @function [parent=#CARGO] OnEnterUnLoaded +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable + +-- Loaded + +--- @function [parent=#CARGO] OnLeaveLoaded +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable +-- @return #boolean + +--- @function [parent=#CARGO] OnEnterLoaded +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable + +-- Boarding + +--- @function [parent=#CARGO] OnLeaveBoarding +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable +-- @return #boolean + +--- @function [parent=#CARGO] OnEnterBoarding +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable +-- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). + +-- UnBoarding + +--- @function [parent=#CARGO] OnLeaveUnBoarding +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable +-- @return #boolean + +--- @function [parent=#CARGO] OnEnterUnBoarding +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable + + +-- TODO: Find all Carrier objects and make the type of the Carriers Wrapper.Unit#UNIT in the documentation. + +CARGOS = {} + +do -- CARGO + + --- @type CARGO + -- @extends Core.Fsm#FSM_PROCESS + -- @field #string Type A string defining the type of the cargo. eg. Engineers, Equipment, Screwdrivers. + -- @field #string Name A string defining the name of the cargo. The name is the unique identifier of the cargo. + -- @field #number Weight A number defining the weight of the cargo. The weight is expressed in kg. + -- @field #number NearRadius (optional) A number defining the radius in meters when the cargo is near to a Carrier, so that it can be loaded. + -- @field Wrapper.Controllable#CONTROLLABLE CargoObject The alive DCS object representing the cargo. This value can be nil, meaning, that the cargo is not represented anywhere... + -- @field Wrapper.Client#CLIENT CargoCarrier The alive DCS object carrying the cargo. This value can be nil, meaning, that the cargo is not contained anywhere... + -- @field #boolean Slingloadable This flag defines if the cargo can be slingloaded. + -- @field #boolean Moveable This flag defines if the cargo is moveable. + -- @field #boolean Representable This flag defines if the cargo can be represented by a DCS Unit. + -- @field #boolean Containable This flag defines if the cargo can be contained within a DCS Unit. + + --- # (R2.1) CARGO class, extends @{Fsm#FSM_PROCESS} + -- + -- The CARGO class defines the core functions that defines a cargo object within MOOSE. + -- A cargo is a logical object defined that is available for transport, and has a life status within a simulation. + -- + -- The CARGO is a state machine: it manages the different events and states of the cargo. + -- All derived classes from CARGO follow the same state machine, expose the same cargo event functions, and provide the same cargo states. + -- + -- ## CARGO Events: + -- + -- * @{#CARGO.Board}( ToCarrier ): Boards the cargo to a carrier. + -- * @{#CARGO.Load}( ToCarrier ): Loads the cargo into a carrier, regardless of its position. + -- * @{#CARGO.UnBoard}( ToPointVec2 ): UnBoard the cargo from a carrier. This will trigger a movement of the cargo to the option ToPointVec2. + -- * @{#CARGO.UnLoad}( ToPointVec2 ): UnLoads the cargo from a carrier. + -- * @{#CARGO.Dead}( Controllable ): The cargo is dead. The cargo process will be ended. + -- + -- ## CARGO States: + -- + -- * **UnLoaded**: The cargo is unloaded from a carrier. + -- * **Boarding**: The cargo is currently boarding (= running) into a carrier. + -- * **Loaded**: The cargo is loaded into a carrier. + -- * **UnBoarding**: The cargo is currently unboarding (=running) from a carrier. + -- * **Dead**: The cargo is dead ... + -- * **End**: The process has come to an end. + -- + -- ## CARGO state transition methods: + -- + -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. + -- There are 2 moments when state transition methods will be called by the state machine: + -- + -- * **Leaving** the state. + -- The state transition method needs to start with the name **OnLeave + the name of the state**. + -- If the state transition method returns false, then the processing of the state transition will not be done! + -- If you want to change the behaviour of the AIControllable at this event, return false, + -- but then you'll need to specify your own logic using the AIControllable! + -- + -- * **Entering** the state. + -- The state transition method needs to start with the name **OnEnter + the name of the state**. + -- These state transition methods need to provide a return value, which is specified at the function description. + -- + -- @field #CARGO + CARGO = { + ClassName = "CARGO", + Type = nil, + Name = nil, + Weight = nil, + CargoObject = nil, + CargoCarrier = nil, + Representable = false, + Slingloadable = false, + Moveable = false, + Containable = false, + } + +--- @type CARGO.CargoObjects +-- @map < #string, Wrapper.Positionable#POSITIONABLE > The alive POSITIONABLE objects representing the the cargo. + + +--- CARGO Constructor. This class is an abstract class and should not be instantiated. +-- @param #CARGO self +-- @param #string Type +-- @param #string Name +-- @param #number Weight +-- @param #number NearRadius (optional) +-- @return #CARGO +function CARGO:New( Type, Name, Weight ) --R2.1 + + local self = BASE:Inherit( self, FSM:New() ) -- #CARGO + self:F( { Type, Name, Weight } ) + + self:SetStartState( "UnLoaded" ) + self:AddTransition( { "UnLoaded", "Boarding" }, "Board", "Boarding" ) + self:AddTransition( "Boarding" , "Boarding", "Boarding" ) + self:AddTransition( "Boarding", "CancelBoarding", "UnLoaded" ) + self:AddTransition( "Boarding", "Load", "Loaded" ) + self:AddTransition( "UnLoaded", "Load", "Loaded" ) + self:AddTransition( "Loaded", "UnBoard", "UnBoarding" ) + self:AddTransition( "UnBoarding", "UnBoarding", "UnBoarding" ) + self:AddTransition( "UnBoarding", "UnLoad", "UnLoaded" ) + self:AddTransition( "Loaded", "UnLoad", "UnLoaded" ) + self:AddTransition( "*", "Damaged", "Damaged" ) + self:AddTransition( "*", "Destroyed", "Destroyed" ) + self:AddTransition( "*", "Respawn", "UnLoaded" ) + + + self.Type = Type + self.Name = Name + self.Weight = Weight + self.CargoObject = nil + self.CargoCarrier = nil + self.Representable = false + self.Slingloadable = false + self.Moveable = false + self.Containable = false + + self:SetDeployed( false ) + + self.CargoScheduler = SCHEDULER:New() + + CARGOS[self.Name] = self + + + return self +end + +--- Destroy the cargo. +-- @param #CARGO self +function CARGO:Destroy() + if self.CargoObject then + self.CargoObject:Destroy() + end + self:Destroyed() +end + +--- Get the name of the Cargo. +-- @param #CARGO self +-- @return #string The name of the Cargo. +function CARGO:GetName() --R2.1 + return self.Name +end + +--- Get the object name of the Cargo. +-- @param #CARGO self +-- @return #string The object name of the Cargo. +function CARGO:GetObjectName() --R2.1 + if self:IsLoaded() then + return self.CargoCarrier:GetName() + else + return self.CargoObject:GetName() + end +end + +--- Get the type of the Cargo. +-- @param #CARGO self +-- @return #string The type of the Cargo. +function CARGO:GetType() + return self.Type +end + +--- Get the current coordinates of the Cargo. +-- @param #CARGO self +-- @return Core.Point#COORDINATE The coordinates of the Cargo. +function CARGO:GetCoordinate() + return self.CargoObject:GetCoordinate() +end + +--- Check if cargo is destroyed. +-- @param #CARGO self +-- @return #boolean true if destroyed +function CARGO:IsDestroyed() + return self:Is( "Destroyed" ) +end + + +--- Check if cargo is loaded. +-- @param #CARGO self +-- @return #boolean true if loaded +function CARGO:IsLoaded() + return self:Is( "Loaded" ) +end + +--- Check if cargo is unloaded. +-- @param #CARGO self +-- @return #boolean true if unloaded +function CARGO:IsUnLoaded() + return self:Is( "UnLoaded" ) +end + +--- Check if cargo is alive. +-- @param #CARGO self +-- @return #boolean true if unloaded +function CARGO:IsAlive() + + if self:IsLoaded() then + return self.CargoCarrier:IsAlive() + else + return self.CargoObject:IsAlive() + end +end + +--- Set the cargo as deployed +-- @param #CARGO self +function CARGO:SetDeployed( Deployed ) + self.Deployed = Deployed +end + +--- Is the cargo deployed +-- @param #CARGO self +-- @return #boolean +function CARGO:IsDeployed() + return self.Deployed +end + + + + +--- Template method to spawn a new representation of the CARGO in the simulator. +-- @param #CARGO self +-- @return #CARGO +function CARGO:Spawn( PointVec2 ) + self:F() + +end + +--- Signal a flare at the position of the CARGO. +-- @param #CARGO self +-- @param Utilities.Utils#FLARECOLOR FlareColor +function CARGO:Flare( FlareColor ) + if self:IsUnLoaded() then + trigger.action.signalFlare( self.CargoObject:GetVec3(), FlareColor , 0 ) + end +end + +--- Signal a white flare at the position of the CARGO. +-- @param #CARGO self +function CARGO:FlareWhite() + self:Flare( trigger.flareColor.White ) +end + +--- Signal a yellow flare at the position of the CARGO. +-- @param #CARGO self +function CARGO:FlareYellow() + self:Flare( trigger.flareColor.Yellow ) +end + +--- Signal a green flare at the position of the CARGO. +-- @param #CARGO self +function CARGO:FlareGreen() + self:Flare( trigger.flareColor.Green ) +end + +--- Signal a red flare at the position of the CARGO. +-- @param #CARGO self +function CARGO:FlareRed() + self:Flare( trigger.flareColor.Red ) +end + +--- Smoke the CARGO. +-- @param #CARGO self +function CARGO:Smoke( SmokeColor, Range ) + self:F2() + if self:IsUnLoaded() then + if Range then + trigger.action.smoke( self.CargoObject:GetRandomVec3( Range ), SmokeColor ) + else + trigger.action.smoke( self.CargoObject:GetVec3(), SmokeColor ) + end + end +end + +--- Smoke the CARGO Green. +-- @param #CARGO self +function CARGO:SmokeGreen() + self:Smoke( trigger.smokeColor.Green, Range ) +end + +--- Smoke the CARGO Red. +-- @param #CARGO self +function CARGO:SmokeRed() + self:Smoke( trigger.smokeColor.Red, Range ) +end + +--- Smoke the CARGO White. +-- @param #CARGO self +function CARGO:SmokeWhite() + self:Smoke( trigger.smokeColor.White, Range ) +end + +--- Smoke the CARGO Orange. +-- @param #CARGO self +function CARGO:SmokeOrange() + self:Smoke( trigger.smokeColor.Orange, Range ) +end + +--- Smoke the CARGO Blue. +-- @param #CARGO self +function CARGO:SmokeBlue() + self:Smoke( trigger.smokeColor.Blue, Range ) +end + + + + + + +--- Check if Cargo is the given @{Zone}. +-- @param #CARGO self +-- @param Core.Zone#ZONE_BASE Zone +-- @return #boolean **true** if cargo is in the Zone, **false** if cargo is not in the Zone. +function CARGO:IsInZone( Zone ) + self:F( { Zone } ) + + if self:IsLoaded() then + return Zone:IsPointVec2InZone( self.CargoCarrier:GetPointVec2() ) + else + self:F( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } ) + if self.CargoObject:GetSize() ~= 0 then + return Zone:IsPointVec2InZone( self.CargoObject:GetPointVec2() ) + else + return false + end + end + + return nil + +end + + +--- Check if CargoCarrier is near the Cargo to be Loaded. +-- @param #CARGO self +-- @param Core.Point#POINT_VEC2 PointVec2 +-- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). +-- @return #boolean +function CARGO:IsNear( PointVec2, NearRadius ) + self:F( { PointVec2, NearRadius } ) + + local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + self:T( Distance ) + + if Distance <= NearRadius then + return true + else + return false + end +end + +--- Get the current PointVec2 of the cargo. +-- @param #CARGO self +-- @return Core.Point#POINT_VEC2 +function CARGO:GetPointVec2() + return self.CargoObject:GetPointVec2() +end + +--- Get the current Coordinate of the cargo. +-- @param #CARGO self +-- @return Core.Point#COORDINATE +function CARGO:GetCoordinate() + return self.CargoObject:GetCoordinate() +end + +--- Set the weight of the cargo. +-- @param #CARGO self +-- @param #number Weight The weight in kg. +-- @return #CARGO +function CARGO:SetWeight( Weight ) + self.Weight = Weight + return self +end + +end + + +do -- CARGO_REPRESENTABLE + + --- @type CARGO_REPRESENTABLE + -- @extends #CARGO + -- @field test + + --- + -- @field #CARGO_REPRESENTABLE CARGO_REPRESENTABLE + CARGO_REPRESENTABLE = { + ClassName = "CARGO_REPRESENTABLE" + } + + --- CARGO_REPRESENTABLE Constructor. + -- @param #CARGO_REPRESENTABLE self + -- @param #string Type + -- @param #string Name + -- @param #number Weight + -- @param #number ReportRadius (optional) + -- @param #number NearRadius (optional) + -- @return #CARGO_REPRESENTABLE + function CARGO_REPRESENTABLE:New( CargoObject, Type, Name, Weight, ReportRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight, ReportRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE + self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) + + return self + end + + --- Route a cargo unit to a PointVec2. + -- @param #CARGO_REPRESENTABLE self + -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #number Speed + -- @return #CARGO_REPRESENTABLE + function CARGO_REPRESENTABLE:RouteTo( ToPointVec2, Speed ) + self:F2( ToPointVec2 ) + + local Points = {} + + local PointStartVec2 = self.CargoObject:GetPointVec2() + + Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) + Points[#Points+1] = ToPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoObject:TaskRoute( Points ) + self.CargoObject:SetTask( TaskRoute, 2 ) + return self + end + + +end -- CARGO_REPRESENTABLE + + do -- CARGO_REPORTABLE + + --- @type CARGO_REPORTABLE + -- @extends #CARGO + CARGO_REPORTABLE = { + ClassName = "CARGO_REPORTABLE" + } + + --- CARGO_REPORTABLE Constructor. + -- @param #CARGO_REPORTABLE self + -- @param Wrapper.Controllable#Controllable CargoObject + -- @param #string Type + -- @param #string Name + -- @param #number Weight + -- @param #number ReportRadius (optional) + -- @param #number NearRadius (optional) + -- @return #CARGO_REPORTABLE + function CARGO_REPORTABLE:New( CargoObject, Type, Name, Weight, ReportRadius ) + local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight ) ) -- #CARGO_REPORTABLE + self:F( { Type, Name, Weight, ReportRadius } ) + + self.CargoSet = SET_CARGO:New() -- Core.Set#SET_CARGO + + self.ReportRadius = ReportRadius or 1000 + self.CargoObject = CargoObject + + + + return self + end + + --- Check if CargoCarrier is in the ReportRadius for the Cargo to be Loaded. + -- @param #CARGO_REPORTABLE self + -- @param Core.Point#POINT_VEC2 PointVec2 + -- @return #boolean + function CARGO_REPORTABLE:IsInRadius( PointVec2 ) + self:F( { PointVec2 } ) + + local Distance = 0 + if self:IsLoaded() then + Distance = PointVec2:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) + else + Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + end + self:T( Distance ) + + if Distance <= self.ReportRadius then + return true + else + return false + end + + + end + + --- Send a CC message to a GROUP. + -- @param #CARGO_REPORTABLE self + -- @param #string Message + -- @param Wrapper.Group#GROUP TaskGroup + -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. + function CARGO_REPORTABLE:MessageToGroup( Message, TaskGroup, Name ) + + local Prefix = Name and "@ " .. Name .. ": " or "@ " .. TaskGroup:GetCallsign() .. ": " + Message = Prefix .. Message + MESSAGE:New( Message, 20, "Cargo: " .. self:GetName() ):ToGroup( TaskGroup ) + + end + + --- Get the range till cargo will board. + -- @param #CARGO_REPORTABLE self + -- @return #number The range till cargo will board. + function CARGO_REPORTABLE:GetBoardingRange() + return self.ReportRadius + end + + --- Respawn the cargo. + -- @param #CARGO_REPORTABLE self + function CARGO_REPORTABLE:Respawn() + + self:F({"Respawning"}) + + for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do + local Cargo = CargoData -- #CARGO + Cargo:Destroy() + Cargo:SetStartState( "UnLoaded" ) + end + + local CargoObject = self.CargoObject -- Wrapper.Group#GROUP + CargoObject:Destroy() + local Template = CargoObject:GetTemplate() + CargoObject:Respawn( Template ) + + self:SetDeployed( false ) + + local WeightGroup = 0 + + self:SetStartState( "UnLoaded" ) + + end + + +end + +do -- CARGO_UNIT + + --- Hello + -- @type CARGO_UNIT + -- @extends #CARGO_REPRESENTABLE + + --- # CARGO\_UNIT class, extends @{#CARGO_REPRESENTABLE} + -- + -- The CARGO\_UNIT class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. + -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_UNIT objects to and from carriers. + -- + -- === + -- + -- @field #CARGO_UNIT CARGO_UNIT + -- + CARGO_UNIT = { + ClassName = "CARGO_UNIT" + } + +--- CARGO_UNIT Constructor. +-- @param #CARGO_UNIT self +-- @param Wrapper.Unit#UNIT CargoUnit +-- @param #string Type +-- @param #string Name +-- @param #number Weight +-- @param #number ReportRadius (optional) +-- @param #number NearRadius (optional) +-- @return #CARGO_UNIT +function CARGO_UNIT:New( CargoUnit, Type, Name, Weight, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, Weight, NearRadius ) ) -- #CARGO_UNIT + self:F( { Type, Name, Weight, NearRadius } ) + + self:T( CargoUnit ) + self.CargoObject = CargoUnit + + self:T( self.ClassName ) + +-- self:HandleEvent( EVENTS.Dead, +-- --- @param #CARGO Cargo +-- -- @param Core.Event#EVENTDATA EventData +-- function( Cargo, EventData ) +-- if Cargo:GetObjectName() == EventData.IniUnit:GetName() then +-- self:E( { "Cargo destroyed", Cargo } ) +-- Cargo:Destroyed() +-- end +-- end +-- ) + + self:SetEventPriority( 5 ) + + return self +end + +--- CARGO_UNIT Destructor. +-- @param #CARGO_UNIT self +-- @return #CARGO_UNIT +function CARGO_UNIT:Destroy() + + -- Cargo objects are deleted from the _DATABASE and SET_CARGO objects. + self:F( { CargoName = self:GetName() } ) + _EVENTDISPATCHER:CreateEventDeleteCargo( self ) + + return self +end + +--- Enter UnBoarding State. +-- @param #CARGO_UNIT self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Core.Point#POINT_VEC2 ToPointVec2 +function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + NearRadius = NearRadius or 25 + + local Angle = 180 + local Speed = 60 + local DeployDistance = 9 + local RouteDistance = 60 + + if From == "Loaded" then + + local CargoCarrier = self.CargoCarrier -- Wrapper.Controllable#CONTROLLABLE + + local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + + + local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading ) + + + -- if there is no ToPointVec2 given, then use the CargoRoutePointVec2 + ToPointVec2 = ToPointVec2 or CargoRoutePointVec2 + local DirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3(ToPointVec2) + local Angle = CargoCarrierPointVec2:GetAngleDegrees(DirectionVec3) + + local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, Angle ) + + local FromPointVec2 = CargoCarrierPointVec2 + + -- Respawn the group... + if self.CargoObject then + self.CargoObject:ReSpawn( CargoDeployPointVec2:GetVec3(), CargoDeployHeading ) + self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) + self.CargoCarrier = nil + + local Points = {} + Points[#Points+1] = CargoCarrierPointVec2:WaypointGround( Speed ) + + Points[#Points+1] = ToPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoObject:TaskRoute( Points ) + self.CargoObject:SetTask( TaskRoute, 1 ) + + + self:__UnBoarding( 1, ToPointVec2, NearRadius ) + end + end + +end + +--- Leave UnBoarding State. +-- @param #CARGO_UNIT self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Core.Point#POINT_VEC2 ToPointVec2 +function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + NearRadius = NearRadius or 25 + + local Angle = 180 + local Speed = 10 + local Distance = 5 + + if From == "UnBoarding" then + if self:IsNear( ToPointVec2, NearRadius ) then + return true + else + + self:__UnBoarding( 1, ToPointVec2, NearRadius ) + end + return false + end + +end + +--- UnBoard Event. +-- @param #CARGO_UNIT self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Core.Point#POINT_VEC2 ToPointVec2 +function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + NearRadius = NearRadius or 25 + + self.CargoInAir = self.CargoObject:InAir() + + self:T( self.CargoInAir ) + + -- Only unboard the cargo when the carrier is not in the air. + -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). + if not self.CargoInAir then + + end + + self:__UnLoad( 1, ToPointVec2, NearRadius ) + +end + + + +--- Enter UnLoaded State. +-- @param #CARGO_UNIT self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Core.Point#POINT_VEC2 +function CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 ) + self:F( { ToPointVec2, From, Event, To } ) + + local Angle = 180 + local Speed = 10 + local Distance = 5 + + if From == "Loaded" then + local StartPointVec2 = self.CargoCarrier:GetPointVec2() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployPointVec2 = StartPointVec2:Translate( Distance, CargoDeployHeading ) + + ToPointVec2 = ToPointVec2 or POINT_VEC2:New( CargoDeployPointVec2:GetX(), CargoDeployPointVec2:GetY() ) + + -- Respawn the group... + if self.CargoObject then + self.CargoObject:ReSpawn( ToPointVec2:GetVec3(), 0 ) + self.CargoCarrier = nil + end + + end + + if self.OnUnLoadedCallBack then + self.OnUnLoadedCallBack( self, unpack( self.OnUnLoadedParameters ) ) + self.OnUnLoadedCallBack = nil + end + +end + +--- Board Event. +-- @param #CARGO_UNIT self +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { From, Event, To, CargoCarrier, NearRadius } ) + + local NearRadius = NearRadius or 25 + + self.CargoInAir = self.CargoObject:InAir() + + self:T( self.CargoInAir ) + + -- Only move the group to the carrier when the cargo is not in the air + -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). + if not self.CargoInAir then + if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then + self:Load( CargoCarrier, NearRadius, ... ) + else + local Speed = 90 + local Angle = 180 + local Distance = 5 + + NearRadius = NearRadius or 25 + + local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() + local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) + + local Points = {} + + local PointStartVec2 = self.CargoObject:GetPointVec2() + + Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoObject:TaskRoute( Points ) + self.CargoObject:SetTask( TaskRoute, 2 ) + self:__Boarding( -1, CargoCarrier, NearRadius ) + self.RunCount = 0 + end + end + +end + + +--- Boarding Event. +-- @param #CARGO_UNIT self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #number NearRadius +function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) + + + if CargoCarrier and CargoCarrier:IsAlive() then + if CargoCarrier:InAir() == false then + if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then + self:__Load( 1, CargoCarrier, ... ) + else + self:__Boarding( -1, CargoCarrier, NearRadius, ... ) + self.RunCount = self.RunCount + 1 + if self.RunCount >= 20 then + self.RunCount = 0 + local Speed = 90 + local Angle = 180 + local Distance = 5 + + NearRadius = NearRadius or 25 + + local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() + local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) + + local Points = {} + + local PointStartVec2 = self.CargoObject:GetPointVec2() + + Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoObject:TaskRoute( Points ) + self.CargoObject:SetTask( TaskRoute, 0.2 ) + end + end + else + self.CargoObject:MessageToGroup( "Cancelling Boarding... Get back on the ground!", 5, CargoCarrier:GetGroup(), self:GetName() ) + self:CancelBoarding( CargoCarrier, NearRadius, ... ) + self.CargoObject:SetCommand( self.CargoObject:CommandStopRoute( true ) ) + end + else + self:E("Something is wrong") + end + +end + + +--- Enter Boarding State. +-- @param #CARGO_UNIT self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier +function CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) + + local Speed = 90 + local Angle = 180 + local Distance = 5 + + local NearRadius = NearRadius or 25 + + if From == "UnLoaded" or From == "Boarding" then + + end + +end + +--- Loaded State. +-- @param #CARGO_UNIT self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier +function CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier ) + self:F( { From, Event, To, CargoCarrier } ) + + self.CargoCarrier = CargoCarrier + + -- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects). + if self.CargoObject then + self:T("Destroying") + self.CargoObject:Destroy() + end +end + + + +end + + +do -- CARGO_GROUP + + --- @type CARGO_GROUP + -- @extends #CARGO_REPORTABLE + + --- # CARGO\_GROUP class + -- + -- The CARGO\_GROUP class defines a cargo that is represented by a @{Group} object within the simulator, and can be transported by a carrier. + -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_GROUP to and from carrier. + -- + -- @field #CARGO_GROUP CARGO_GROUP + -- + CARGO_GROUP = { + ClassName = "CARGO_GROUP", + } + +--- CARGO_GROUP constructor. +-- @param #CARGO_GROUP self +-- @param Wrapper.Group#GROUP CargoGroup +-- @param #string Type +-- @param #string Name +-- @param #number ReportRadius (optional) +-- @param #number NearRadius (optional) +-- @return #CARGO_GROUP +function CARGO_GROUP:New( CargoGroup, Type, Name, ReportRadius ) + local self = BASE:Inherit( self, CARGO_REPORTABLE:New( CargoGroup, Type, Name, 0, ReportRadius ) ) -- #CARGO_GROUP + self:F( { Type, Name, ReportRadius } ) + + self.CargoObject = CargoGroup + self:SetDeployed( false ) + self.CargoGroup = CargoGroup + + local WeightGroup = 0 + + for UnitID, UnitData in pairs( CargoGroup:GetUnits() ) do + local Unit = UnitData -- Wrapper.Unit#UNIT + local WeightUnit = Unit:GetDesc().massEmpty + WeightGroup = WeightGroup + WeightUnit + local CargoUnit = CARGO_UNIT:New( Unit, Type, Unit:GetName(), WeightUnit ) + self.CargoSet:Add( CargoUnit:GetName(), CargoUnit ) + end + + self:SetWeight( WeightGroup ) + + self:T( { "Weight Cargo", WeightGroup } ) + + -- Cargo objects are added to the _DATABASE and SET_CARGO objects. + _EVENTDISPATCHER:CreateEventNewCargo( self ) + + self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) + + self:SetEventPriority( 4 ) + + return self +end + +--- @param #CARGO_GROUP self +-- @param Core.Event#EVENTDATA EventData +function CARGO_GROUP:OnEventCargoDead( EventData ) + + local Destroyed = false + + if self:IsDestroyed() or self:IsUnLoaded() then + Destroyed = true + for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do + local Cargo = CargoData -- #CARGO + if Cargo:IsAlive() then + Destroyed = false + else + Cargo:Destroyed() + end + end + else + local CarrierName = self.CargoCarrier:GetName() + if CarrierName == EventData.IniDCSUnitName then + MESSAGE:New( "Cargo is lost from carrier " .. CarrierName, 15 ):ToAll() + Destroyed = true + self.CargoCarrier:ClearCargo() + end + end + + if Destroyed then + self:Destroyed() + self:E( { "Cargo group destroyed" } ) + end + +end + +--- Enter Boarding State. +-- @param #CARGO_GROUP self +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { CargoCarrier.UnitName, From, Event, To } ) + + local NearRadius = NearRadius or 25 + + if From == "UnLoaded" then + + -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo, ... ) + Cargo:__Board( 1, CargoCarrier, NearRadius, ... ) + end, ... + ) + + self:__Boarding( 1, CargoCarrier, NearRadius, ... ) + end + +end + +--- Enter Loaded State. +-- @param #CARGO_GROUP self +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onenterLoaded( From, Event, To, CargoCarrier, ... ) + self:F( { From, Event, To, CargoCarrier, ...} ) + + if From == "UnLoaded" then + -- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier. + for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do + Cargo:Load( CargoCarrier ) + end + end + + --self.CargoObject:Destroy() + self.CargoCarrier = CargoCarrier + +end + +--- Leave Boarding State. +-- @param #CARGO_GROUP self +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { CargoCarrier.UnitName, From, Event, To } ) + + local NearRadius = NearRadius or 25 + + local Boarded = true + local Cancelled = false + local Dead = true + + self.CargoSet:Flush() + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do + self:T( { Cargo:GetName(), Cargo.current } ) + if not Cargo:is( "Loaded" ) then + Boarded = false + end + + if Cargo:is( "UnLoaded" ) then + Cancelled = true + end + + if not Cargo:is( "Destroyed" ) then + Dead = false + end + + end + + if not Dead then + + if not Cancelled then + if not Boarded then + self:__Boarding( 1, CargoCarrier, NearRadius, ... ) + else + self:__Load( 1, CargoCarrier, ... ) + end + else + self:__CancelBoarding( 1, CargoCarrier, NearRadius, ... ) + end + else + self:__Destroyed( 1, CargoCarrier, NearRadius, ... ) + end + +end + +--- Get the amount of cargo units in the group. +-- @param #CARGO_GROUP self +-- @return #CARGO_GROUP +function CARGO_GROUP:GetCount() + return self.CargoSet:Count() +end + + +--- Enter UnBoarding State. +-- @param #CARGO_GROUP self +-- @param Core.Point#POINT_VEC2 ToPointVec2 +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) + self:F( {From, Event, To, ToPointVec2, NearRadius } ) + + NearRadius = NearRadius or 25 + + local Timer = 1 + + if From == "Loaded" then + + if self.CargoObject then + self.CargoObject:Destroy() + end + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo, NearRadius ) + + Cargo:__UnBoard( Timer, ToPointVec2, NearRadius ) + Timer = Timer + 10 + end, { NearRadius } + ) + + + self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) + end + +end + +--- Leave UnBoarding State. +-- @param #CARGO_GROUP self +-- @param Core.Point#POINT_VEC2 ToPointVec2 +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + --local NearRadius = NearRadius or 25 + + local Angle = 180 + local Speed = 10 + local Distance = 5 + + if From == "UnBoarding" then + local UnBoarded = true + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do + self:T( Cargo.current ) + if not Cargo:is( "UnLoaded" ) then + UnBoarded = false + end + end + + if UnBoarded then + return true + else + self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) + end + + return false + end + +end + +--- UnBoard Event. +-- @param #CARGO_GROUP self +-- @param Core.Point#POINT_VEC2 ToPointVec2 +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + --local NearRadius = NearRadius or 25 + + self:__UnLoad( 1, ToPointVec2, ... ) +end + + + +--- Enter UnLoaded State. +-- @param #CARGO_GROUP self +-- @param Core.Point#POINT_VEC2 +-- @param #string Event +-- @param #string From +-- @param #string To +function CARGO_GROUP:onenterUnLoaded( From, Event, To, ToPointVec2, ... ) + self:F( { From, Event, To, ToPointVec2 } ) + + if From == "Loaded" then + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo ) + Cargo:UnLoad( ToPointVec2 ) + end + ) + + end + +end + + + --- Respawn the cargo when destroyed + -- @param #CARGO_GROUP self + -- @param #boolean RespawnDestroyed + function CARGO_GROUP:RespawnOnDestroyed( RespawnDestroyed ) + self:F({"In function RespawnOnDestroyed"}) + if RespawnDestroyed then + self.onenterDestroyed = function( self ) + self:F("IN FUNCTION") + self:Respawn() + end + else + self.onenterDestroyed = nil + end + + end + +end -- CARGO_GROUP + +do -- CARGO_PACKAGE + + --- @type CARGO_PACKAGE + -- @extends #CARGO_REPRESENTABLE + CARGO_PACKAGE = { + ClassName = "CARGO_PACKAGE" + } + +--- CARGO_PACKAGE Constructor. +-- @param #CARGO_PACKAGE self +-- @param Wrapper.Unit#UNIT CargoCarrier The UNIT carrying the package. +-- @param #string Type +-- @param #string Name +-- @param #number Weight +-- @param #number ReportRadius (optional) +-- @param #number NearRadius (optional) +-- @return #CARGO_PACKAGE +function CARGO_PACKAGE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) ) -- #CARGO_PACKAGE + self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) + + self:T( CargoCarrier ) + self.CargoCarrier = CargoCarrier + + return self +end + +--- Board Event. +-- @param #CARGO_PACKAGE self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #number Speed +-- @param #number BoardDistance +-- @param #number Angle +function CARGO_PACKAGE:onafterOnBoard( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) + self:F() + + self.CargoInAir = self.CargoCarrier:InAir() + + self:T( self.CargoInAir ) + + -- Only move the CargoCarrier to the New CargoCarrier when the New CargoCarrier is not in the air. + if not self.CargoInAir then + + local Points = {} + + local StartPointVec2 = self.CargoCarrier:GetPointVec2() + local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + self:T( { CargoCarrierHeading, CargoDeployHeading } ) + local CargoDeployPointVec2 = CargoCarrier:GetPointVec2():Translate( BoardDistance, CargoDeployHeading ) + + Points[#Points+1] = StartPointVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoCarrier:TaskRoute( Points ) + self.CargoCarrier:SetTask( TaskRoute, 1 ) + end + + self:Boarded( CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) + +end + +--- Check if CargoCarrier is near the Cargo to be Loaded. +-- @param #CARGO_PACKAGE self +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @return #boolean +function CARGO_PACKAGE:IsNear( CargoCarrier ) + self:F() + + local CargoCarrierPoint = CargoCarrier:GetPointVec2() + + local Distance = CargoCarrierPoint:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) + self:T( Distance ) + + if Distance <= self.NearRadius then + return true + else + return false + end +end + +--- Boarded Event. +-- @param #CARGO_PACKAGE self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier +function CARGO_PACKAGE:onafterOnBoarded( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) + self:F() + + if self:IsNear( CargoCarrier ) then + self:__Load( 1, CargoCarrier, Speed, LoadDistance, Angle ) + else + self:__Boarded( 1, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) + end +end + +--- UnBoard Event. +-- @param #CARGO_PACKAGE self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param #number Speed +-- @param #number UnLoadDistance +-- @param #number UnBoardDistance +-- @param #number Radius +-- @param #number Angle +function CARGO_PACKAGE:onafterUnBoard( From, Event, To, CargoCarrier, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle ) + self:F() + + self.CargoInAir = self.CargoCarrier:InAir() + + self:T( self.CargoInAir ) + + -- Only unboard the cargo when the carrier is not in the air. + -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). + if not self.CargoInAir then + + self:_Next( self.FsmP.UnLoad, UnLoadDistance, Angle ) + + local Points = {} + + local StartPointVec2 = CargoCarrier:GetPointVec2() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + self:T( { CargoCarrierHeading, CargoDeployHeading } ) + local CargoDeployPointVec2 = StartPointVec2:Translate( UnBoardDistance, CargoDeployHeading ) + + Points[#Points+1] = StartPointVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = CargoCarrier:TaskRoute( Points ) + CargoCarrier:SetTask( TaskRoute, 1 ) + end + + self:__UnBoarded( 1 , CargoCarrier, Speed ) + +end + +--- UnBoarded Event. +-- @param #CARGO_PACKAGE self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier +function CARGO_PACKAGE:onafterUnBoarded( From, Event, To, CargoCarrier, Speed ) + self:F() + + if self:IsNear( CargoCarrier ) then + self:__UnLoad( 1, CargoCarrier, Speed ) + else + self:__UnBoarded( 1, CargoCarrier, Speed ) + end +end + +--- Load Event. +-- @param #CARGO_PACKAGE self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #number Speed +-- @param #number LoadDistance +-- @param #number Angle +function CARGO_PACKAGE:onafterLoad( From, Event, To, CargoCarrier, Speed, LoadDistance, Angle ) + self:F() + + self.CargoCarrier = CargoCarrier + + local StartPointVec2 = self.CargoCarrier:GetPointVec2() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployPointVec2 = StartPointVec2:Translate( LoadDistance, CargoDeployHeading ) + + local Points = {} + Points[#Points+1] = StartPointVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoCarrier:TaskRoute( Points ) + self.CargoCarrier:SetTask( TaskRoute, 1 ) + +end + +--- UnLoad Event. +-- @param #CARGO_PACKAGE self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param #number Distance +-- @param #number Angle +function CARGO_PACKAGE:onafterUnLoad( From, Event, To, CargoCarrier, Speed, Distance, Angle ) + self:F() + + local StartPointVec2 = self.CargoCarrier:GetPointVec2() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployPointVec2 = StartPointVec2:Translate( Distance, CargoDeployHeading ) + + self.CargoCarrier = CargoCarrier + + local Points = {} + Points[#Points+1] = StartPointVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoCarrier:TaskRoute( Points ) + self.CargoCarrier:SetTask( TaskRoute, 1 ) + +end + + +end diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 3e9eea077..81c8f13c5 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -1,4 +1,4 @@ ---- This module contains the DATABASE class, managing the database of mission objects. +--- **Core** -- DATABASE manages the database of mission objects. -- -- ==== -- @@ -6,12 +6,14 @@ -- =================================================== -- Mission designers can use the DATABASE class to refer to: -- +-- * STATICS -- * UNITS -- * GROUPS -- * CLIENTS --- * AIRPORTS +-- * AIRBASES -- * PLAYERSJOINED -- * PLAYERS +-- * CARGOS -- -- On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor. -- @@ -33,8 +35,13 @@ -- -- === -- +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: +-- +-- ==== -- @module Database --- @author FlightControl + --- DATABASE class -- @type DATABASE @@ -44,19 +51,25 @@ DATABASE = { Templates = { Units = {}, Groups = {}, + Statics = {}, ClientsByName = {}, ClientsByID = {}, }, UNITS = {}, + UNITS_Index = {}, STATICS = {}, GROUPS = {}, PLAYERS = {}, PLAYERSJOINED = {}, + PLAYERUNITS = {}, CLIENTS = {}, + CARGOS = {}, AIRBASES = {}, COUNTRY_ID = {}, COUNTRY_NAME = {}, NavPoints = {}, + PLAYERSETTINGS = {}, + ZONENAMES = {}, } local _DATABASECoalition = @@ -84,13 +97,15 @@ local _DATABASECategory = function DATABASE:New() -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) -- #DATABASE self:SetEventPriority( 1 ) self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.NewCargo ) + self:HandleEvent( EVENTS.DeleteCargo ) -- Follow alive players and clients self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) @@ -100,8 +115,41 @@ function DATABASE:New() self:_RegisterGroupsAndUnits() self:_RegisterClients() self:_RegisterStatics() - self:_RegisterPlayers() + --self:_RegisterPlayers() self:_RegisterAirbases() + + self.UNITS_Position = 0 + + --- @param #DATABASE self + local function CheckPlayers( self ) + + local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } + for CoalitionId, CoalitionData in pairs( CoalitionsData ) do + --self:E( { "CoalitionData:", CoalitionData } ) + for UnitId, UnitData in pairs( CoalitionData ) do + if UnitData and UnitData:isExist() then + + local UnitName = UnitData:getName() + local PlayerName = UnitData:getPlayerName() + local PlayerUnit = UNIT:Find( UnitData ) + --self:T( { "UnitData:", UnitData, UnitName, PlayerName, PlayerUnit } ) + + if PlayerName and PlayerName ~= "" then + if self.PLAYERS[PlayerName] == nil or self.PLAYERS[PlayerName] ~= UnitName then + --self:E( { "Add player for unit:", UnitName, PlayerName } ) + self:AddPlayer( UnitName, PlayerName ) + --_EVENTDISPATCHER:CreateEventPlayerEnterUnit( PlayerUnit ) + local Settings = SETTINGS:Set( PlayerName ) + Settings:SetPlayerMenu( PlayerUnit ) + end + end + end + end + end + end + + self:E( "Scheduling" ) + PlayerCheckSchedule = SCHEDULER:New( nil, CheckPlayers, { self }, 1, 1 ) return self end @@ -124,6 +172,8 @@ function DATABASE:AddUnit( DCSUnitName ) if not self.UNITS[DCSUnitName] then local UnitRegister = UNIT:Register( DCSUnitName ) self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName ) + + table.insert( self.UNITS_Index, DCSUnitName ) end return self.UNITS[DCSUnitName] @@ -134,7 +184,7 @@ end -- @param #DATABASE self function DATABASE:DeleteUnit( DCSUnitName ) - --self.UNITS[DCSUnitName] = nil + self.UNITS[DCSUnitName] = nil end --- Adds a Static based on the Static Name in the DATABASE. @@ -164,23 +214,6 @@ function DATABASE:FindStatic( StaticName ) return StaticFound end ---- Adds a Airbase based on the Airbase Name in the DATABASE. --- @param #DATABASE self -function DATABASE:AddAirbase( DCSAirbaseName ) - - if not self.AIRBASES[DCSAirbaseName] then - self.AIRBASES[DCSAirbaseName] = AIRBASE:Register( DCSAirbaseName ) - end -end - - ---- Deletes a Airbase from the DATABASE based on the Airbase Name. --- @param #DATABASE self -function DATABASE:DeleteAirbase( DCSAirbaseName ) - - --self.AIRBASES[DCSAirbaseName] = nil -end - --- Finds a AIRBASE based on the AirbaseName. -- @param #DATABASE self -- @param #string AirbaseName @@ -191,6 +224,64 @@ function DATABASE:FindAirbase( AirbaseName ) return AirbaseFound end +--- Adds a Airbase based on the Airbase Name in the DATABASE. +-- @param #DATABASE self +-- @param #string AirbaseName The name of the airbase +function DATABASE:AddAirbase( AirbaseName ) + + if not self.AIRBASES[AirbaseName] then + self.AIRBASES[AirbaseName] = AIRBASE:Register( AirbaseName ) + end +end + + +--- Deletes a Airbase from the DATABASE based on the Airbase Name. +-- @param #DATABASE self +-- @param #string AirbaseName The name of the airbase +function DATABASE:DeleteAirbase( AirbaseName ) + + self.AIRBASES[AirbaseName] = nil +end + +--- Finds an AIRBASE based on the AirbaseName. +-- @param #DATABASE self +-- @param #string AirbaseName +-- @return Wrapper.Airbase#AIRBASE The found AIRBASE. +function DATABASE:FindAirbase( AirbaseName ) + + local AirbaseFound = self.AIRBASES[AirbaseName] + return AirbaseFound +end + +--- Adds a Cargo based on the Cargo Name in the DATABASE. +-- @param #DATABASE self +-- @param #string CargoName The name of the airbase +function DATABASE:AddCargo( Cargo ) + + if not self.CARGOS[Cargo.Name] then + self.CARGOS[Cargo.Name] = Cargo + end +end + + +--- Deletes a Cargo from the DATABASE based on the Cargo Name. +-- @param #DATABASE self +-- @param #string CargoName The name of the airbase +function DATABASE:DeleteCargo( CargoName ) + + self.CARGOS[CargoName] = nil +end + +--- Finds an CARGO based on the CargoName. +-- @param #DATABASE self +-- @param #string CargoName +-- @return Wrapper.Cargo#CARGO The found CARGO. +function DATABASE:FindCargo( CargoName ) + + local CargoFound = self.CARGOS[CargoName] + return CargoFound +end + --- Finds a CLIENT based on the ClientName. -- @param #DATABASE self @@ -244,18 +335,20 @@ function DATABASE:AddPlayer( UnitName, PlayerName ) if PlayerName then self:E( { "Add player for unit:", UnitName, PlayerName } ) - self.PLAYERS[PlayerName] = self:FindUnit( UnitName ) + self.PLAYERS[PlayerName] = UnitName + self.PLAYERUNITS[UnitName] = PlayerName self.PLAYERSJOINED[PlayerName] = PlayerName end end --- Deletes a player from the DATABASE based on the Player Name. -- @param #DATABASE self -function DATABASE:DeletePlayer( PlayerName ) +function DATABASE:DeletePlayer( UnitName, PlayerName ) if PlayerName then self:E( { "Clean player:", PlayerName } ) self.PLAYERS[PlayerName] = nil + self.PLAYERUNITS[UnitName] = PlayerName end end @@ -282,7 +375,7 @@ function DATABASE:Spawn( SpawnTemplate ) SpawnTemplate.CountryID = nil SpawnTemplate.CategoryID = nil - self:_RegisterTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID ) + self:_RegisterGroupTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID ) self:T3( SpawnTemplate ) coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate ) @@ -292,7 +385,12 @@ function DATABASE:Spawn( SpawnTemplate ) SpawnTemplate.CountryID = SpawnCountryID SpawnTemplate.CategoryID = SpawnCategoryID + -- Ensure that for the spawned group and its units, there are GROUP and UNIT objects created in the DATABASE. local SpawnGroup = self:AddGroup( SpawnTemplate.name ) + for UnitID, UnitData in pairs( SpawnTemplate.units ) do + self:AddUnit( UnitData.name ) + end + return SpawnGroup end @@ -318,7 +416,7 @@ end -- @param #DATABASE self -- @param #table GroupTemplate -- @return #DATABASE self -function DATABASE:_RegisterTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID ) +function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID ) local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name) @@ -396,6 +494,54 @@ function DATABASE:GetGroupTemplate( GroupName ) return GroupTemplate end +--- Private method that registers new Static Templates within the DATABASE Object. +-- @param #DATABASE self +-- @param #table GroupTemplate +-- @return #DATABASE self +function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID ) + + local TraceTable = {} + + local StaticTemplateName = env.getValueDictByKey(StaticTemplate.name) + + self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {} + + StaticTemplate.CategoryID = CategoryID + StaticTemplate.CoalitionID = CoalitionID + StaticTemplate.CountryID = CountryID + + self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateName + self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate + self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1] + self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID + self.Templates.Statics[StaticTemplateName].CoalitionID = CoalitionID + self.Templates.Statics[StaticTemplateName].CountryID = CountryID + + + TraceTable[#TraceTable+1] = "Static" + TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].GroupName + + TraceTable[#TraceTable+1] = "Coalition" + TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CoalitionID + TraceTable[#TraceTable+1] = "Category" + TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CategoryID + TraceTable[#TraceTable+1] = "Country" + TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CountryID + + self:E( TraceTable ) +end + + +--- @param #DATABASE self +function DATABASE:GetStaticUnitTemplate( StaticName ) + local StaticTemplate = self.Templates.Statics[StaticName].UnitTemplate + StaticTemplate.SpawnCoalitionID = self.Templates.Statics[StaticName].CoalitionID + StaticTemplate.SpawnCategoryID = self.Templates.Statics[StaticName].CategoryID + StaticTemplate.SpawnCountryID = self.Templates.Statics[StaticName].CountryID + return StaticTemplate +end + + function DATABASE:GetGroupNameFromUnitName( UnitName ) return self.Templates.Units[UnitName].GroupName end @@ -552,7 +698,7 @@ function DATABASE:_EventOnBirth( Event ) self:AddGroup( Event.IniDCSGroupName ) end end - self:_EventOnPlayerEnterUnit( Event ) + --self:_EventOnPlayerEnterUnit( Event ) end end @@ -593,6 +739,8 @@ function DATABASE:_EventOnPlayerEnterUnit( Event ) if not self.PLAYERS[PlayerName] then self:AddPlayer( Event.IniUnitName, PlayerName ) end + local Settings = SETTINGS:Set( PlayerName ) + Settings:SetPlayerMenu( Event.IniUnit ) end end end @@ -608,7 +756,9 @@ function DATABASE:_EventOnPlayerLeaveUnit( Event ) if Event.IniObjectCategory == 1 then local PlayerName = Event.IniUnit:GetPlayerName() if self.PLAYERS[PlayerName] then - self:DeletePlayer( PlayerName ) + local Settings = SETTINGS:Set( PlayerName ) + Settings:RemovePlayerMenu( Event.IniUnit ) + self:DeletePlayer( Event.IniUnit, PlayerName ) end end end @@ -663,9 +813,22 @@ function DATABASE:ForEach( IteratorFunction, FinalizeFunction, arg, Set ) end +--- Iterate the DATABASE and call an iterator function for each **alive** STATIC, providing the STATIC and optional parameters. +-- @param #DATABASE self +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a STATIC parameter. +-- @return #DATABASE self +function DATABASE:ForEachStatic( IteratorFunction, FinalizeFunction, ... ) --R2.1 + self:F2( arg ) + + self:ForEach( IteratorFunction, FinalizeFunction, arg, self.STATICS ) + + return self +end + + --- Iterate the DATABASE and call an iterator function for each **alive** UNIT, providing the UNIT and optional parameters. -- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the database. The function needs to accept a UNIT parameter. +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter. -- @return #DATABASE self function DATABASE:ForEachUnit( IteratorFunction, FinalizeFunction, ... ) self:F2( arg ) @@ -675,14 +838,15 @@ function DATABASE:ForEachUnit( IteratorFunction, FinalizeFunction, ... ) return self end + --- Iterate the DATABASE and call an iterator function for each **alive** GROUP, providing the GROUP and optional parameters. -- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the database. The function needs to accept a GROUP parameter. +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a GROUP parameter. -- @return #DATABASE self -function DATABASE:ForEachGroup( IteratorFunction, ... ) +function DATABASE:ForEachGroup( IteratorFunction, FinalizeFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self.GROUPS ) + self:ForEach( IteratorFunction, FinalizeFunction, arg, self.GROUPS ) return self end @@ -690,12 +854,12 @@ end --- Iterate the DATABASE and call an iterator function for each **ALIVE** player, providing the player name and optional parameters. -- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an player in the database. The function needs to accept the player name. +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept the player name. -- @return #DATABASE self -function DATABASE:ForEachPlayer( IteratorFunction, ... ) +function DATABASE:ForEachPlayer( IteratorFunction, FinalizeFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self.PLAYERS ) + self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERS ) return self end @@ -703,19 +867,32 @@ end --- Iterate the DATABASE and call an iterator function for each player who has joined the mission, providing the Unit of the player and optional parameters. -- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is was a player in the database. The function needs to accept a UNIT parameter. +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter. -- @return #DATABASE self -function DATABASE:ForEachPlayerJoined( IteratorFunction, ... ) +function DATABASE:ForEachPlayerJoined( IteratorFunction, FinalizeFunction, ... ) self:F2( arg ) - self:ForEach( IteratorFunction, arg, self.PLAYERSJOINED ) + self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERSJOINED ) return self end +--- Iterate the DATABASE and call an iterator function for each **ALIVE** player UNIT, providing the player UNIT and optional parameters. +-- @param #DATABASE self +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept the player name. +-- @return #DATABASE self +function DATABASE:ForEachPlayerUnit( IteratorFunction, FinalizeFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERUNITS ) + + return self +end + + --- Iterate the DATABASE and call an iterator function for each CLIENT, providing the CLIENT to the function and optional parameters. -- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive player in the database. The function needs to accept a CLIENT parameter. +-- @param #function IteratorFunction The function that will be called object in the database. The function needs to accept a CLIENT parameter. -- @return #DATABASE self function DATABASE:ForEachClient( IteratorFunction, ... ) self:F2( arg ) @@ -725,7 +902,67 @@ function DATABASE:ForEachClient( IteratorFunction, ... ) return self end +--- Iterate the DATABASE and call an iterator function for each CARGO, providing the CARGO object to the function and optional parameters. +-- @param #DATABASE self +-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a CLIENT parameter. +-- @return #DATABASE self +function DATABASE:ForEachCargo( IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self.CARGOS ) + return self +end + + +--- Handles the OnEventNewCargo event. +-- @param #DATABASE self +-- @param Core.Event#EVENTDATA EventData +function DATABASE:OnEventNewCargo( EventData ) + self:F2( { EventData } ) + + if EventData.Cargo then + self:AddCargo( EventData.Cargo ) + end +end + + +--- Handles the OnEventDeleteCargo. +-- @param #DATABASE self +-- @param Core.Event#EVENTDATA EventData +function DATABASE:OnEventDeleteCargo( EventData ) + self:F2( { EventData } ) + + if EventData.Cargo then + self:DeleteCargo( EventData.Cargo.Name ) + end +end + + +--- Gets the player settings +-- @param #DATABASE self +-- @param #string PlayerName +-- @return Core.Settings#SETTINGS +function DATABASE:GetPlayerSettings( PlayerName ) + self:F2( { PlayerName } ) + return self.PLAYERSETTINGS[PlayerName] +end + + +--- Sets the player settings +-- @param #DATABASE self +-- @param #string PlayerName +-- @param Core.Settings#SETTINGS Settings +-- @return Core.Settings#SETTINGS +function DATABASE:SetPlayerSettings( PlayerName, Settings ) + self:F2( { PlayerName, Settings } ) + self.PLAYERSETTINGS[PlayerName] = Settings +end + + + + +--- @param #DATABASE self function DATABASE:_RegisterTemplates() self:F2() @@ -781,11 +1018,18 @@ function DATABASE:_RegisterTemplates() --self.Units[coa_name][countryName][category] = {} - for group_num, GroupTemplate in pairs(obj_type_data.group) do + for group_num, Template in pairs(obj_type_data.group) do - if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group - self:_RegisterTemplate( - GroupTemplate, + if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group + self:_RegisterGroupTemplate( + Template, + CoalitionSide, + _DATABASECategory[string.lower(CategoryName)], + CountryID + ) + else + self:_RegisterStaticTemplate( + Template, CoalitionSide, _DATABASECategory[string.lower(CategoryName)], CountryID @@ -801,6 +1045,11 @@ function DATABASE:_RegisterTemplates() end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then end --for coa_name, coa_data in pairs(mission.coalition) do + for ZoneID, ZoneData in pairs( env.mission.triggers.zones ) do + local ZoneName = ZoneData.name + self.ZONENAMES[ZoneName] = ZoneName + end + return self end diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 39b0738af..28a4ecf26 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -1,4 +1,4 @@ ---- **Core** - EVENT models DCS **event dispatching** using a **publish-subscribe** model. +--- **Core** -- EVENT models DCS **event dispatching** using a **publish-subscribe** model. -- -- ![Banner Image](..\Presentations\EVENT\Dia1.JPG) -- @@ -157,33 +157,12 @@ -- -- When a static object is involved in the event, the Group and Player fields won't be populated. -- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) --- YYYY-MM-DD: CLASS:**NewFunction( Params )** added --- --- Hereby the change log: --- --- * 2017-03-07: Added the correct event dispatching in case the event is subscribed by a GROUP. --- --- * 2017-02-07: Did a complete revision of the Event Handing API and underlying mechanisms. --- -- === -- --- # **AUTHORS and CONTRIBUTIONS** --- +-- ### Author: **Sven Van de Velde (FlightControl)** -- ### Contributions: -- --- ### Authors: --- --- * [**FlightControl**](https://forums.eagle.ru/member.php?u=89536): Design & Programming & documentation. +-- ==== -- -- @module Event @@ -197,6 +176,9 @@ EVENT = { ClassID = 0, } +world.event.S_EVENT_NEW_CARGO = world.event.S_EVENT_MAX + 1000 +world.event.S_EVENT_DELETE_CARGO = world.event.S_EVENT_MAX + 1001 + --- The different types of events supported by MOOSE. -- Use this structure to subscribe to events using the @{Base#BASE.HandleEvent}() method. -- @type EVENTS @@ -224,13 +206,15 @@ EVENTS = { PlayerComment = world.event.S_EVENT_PLAYER_COMMENT, ShootingStart = world.event.S_EVENT_SHOOTING_START, ShootingEnd = world.event.S_EVENT_SHOOTING_END, + NewCargo = world.event.S_EVENT_NEW_CARGO, + DeleteCargo = world.event.S_EVENT_DELETE_CARGO, } --- The Event structure -- Note that at the beginning of each field description, there is an indication which field will be populated depending on the object type involved in the Event: -- -- * A (Object.Category.)UNIT : A UNIT object type is involved in the Event. --- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event.µ +-- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event.µ -- -- @type EVENTDATA -- @field #number id The identifier of the event. @@ -271,122 +255,156 @@ EVENTS = { -- @field WeaponTgtDCSUnit + local _EVENTMETA = { [world.event.S_EVENT_SHOT] = { Order = 1, + Side = "I", Event = "OnEventShot", Text = "S_EVENT_SHOT" }, [world.event.S_EVENT_HIT] = { Order = 1, + Side = "T", Event = "OnEventHit", Text = "S_EVENT_HIT" }, [world.event.S_EVENT_TAKEOFF] = { Order = 1, + Side = "I", Event = "OnEventTakeoff", Text = "S_EVENT_TAKEOFF" }, [world.event.S_EVENT_LAND] = { Order = 1, + Side = "I", Event = "OnEventLand", Text = "S_EVENT_LAND" }, [world.event.S_EVENT_CRASH] = { Order = -1, + Side = "I", Event = "OnEventCrash", Text = "S_EVENT_CRASH" }, [world.event.S_EVENT_EJECTION] = { Order = 1, + Side = "I", Event = "OnEventEjection", Text = "S_EVENT_EJECTION" }, [world.event.S_EVENT_REFUELING] = { Order = 1, + Side = "I", Event = "OnEventRefueling", Text = "S_EVENT_REFUELING" }, [world.event.S_EVENT_DEAD] = { Order = -1, + Side = "I", Event = "OnEventDead", Text = "S_EVENT_DEAD" }, [world.event.S_EVENT_PILOT_DEAD] = { Order = 1, + Side = "I", Event = "OnEventPilotDead", Text = "S_EVENT_PILOT_DEAD" }, [world.event.S_EVENT_BASE_CAPTURED] = { Order = 1, + Side = "I", Event = "OnEventBaseCaptured", Text = "S_EVENT_BASE_CAPTURED" }, [world.event.S_EVENT_MISSION_START] = { Order = 1, + Side = "N", Event = "OnEventMissionStart", Text = "S_EVENT_MISSION_START" }, [world.event.S_EVENT_MISSION_END] = { Order = 1, + Side = "N", Event = "OnEventMissionEnd", Text = "S_EVENT_MISSION_END" }, [world.event.S_EVENT_TOOK_CONTROL] = { Order = 1, + Side = "N", Event = "OnEventTookControl", Text = "S_EVENT_TOOK_CONTROL" }, [world.event.S_EVENT_REFUELING_STOP] = { Order = 1, + Side = "I", Event = "OnEventRefuelingStop", Text = "S_EVENT_REFUELING_STOP" }, [world.event.S_EVENT_BIRTH] = { Order = 1, + Side = "I", Event = "OnEventBirth", Text = "S_EVENT_BIRTH" }, [world.event.S_EVENT_HUMAN_FAILURE] = { Order = 1, + Side = "I", Event = "OnEventHumanFailure", Text = "S_EVENT_HUMAN_FAILURE" }, [world.event.S_EVENT_ENGINE_STARTUP] = { Order = 1, + Side = "I", Event = "OnEventEngineStartup", Text = "S_EVENT_ENGINE_STARTUP" }, [world.event.S_EVENT_ENGINE_SHUTDOWN] = { Order = 1, + Side = "I", Event = "OnEventEngineShutdown", Text = "S_EVENT_ENGINE_SHUTDOWN" }, [world.event.S_EVENT_PLAYER_ENTER_UNIT] = { Order = 1, + Side = "I", Event = "OnEventPlayerEnterUnit", Text = "S_EVENT_PLAYER_ENTER_UNIT" }, [world.event.S_EVENT_PLAYER_LEAVE_UNIT] = { Order = -1, + Side = "I", Event = "OnEventPlayerLeaveUnit", Text = "S_EVENT_PLAYER_LEAVE_UNIT" }, [world.event.S_EVENT_PLAYER_COMMENT] = { Order = 1, + Side = "I", Event = "OnEventPlayerComment", Text = "S_EVENT_PLAYER_COMMENT" }, [world.event.S_EVENT_SHOOTING_START] = { Order = 1, + Side = "I", Event = "OnEventShootingStart", Text = "S_EVENT_SHOOTING_START" }, [world.event.S_EVENT_SHOOTING_END] = { Order = 1, + Side = "I", Event = "OnEventShootingEnd", Text = "S_EVENT_SHOOTING_END" }, + [EVENTS.NewCargo] = { + Order = 1, + Event = "OnEventNewCargo", + Text = "S_EVENT_NEW_CARGO" + }, + [EVENTS.DeleteCargo] = { + Order = 1, + Event = "OnEventDeleteCargo", + Text = "S_EVENT_DELETE_CARGO" + }, } @@ -401,13 +419,6 @@ function EVENT:New() return self end -function EVENT:EventText( EventID ) - - local EventText = _EVENTMETA[EventID].Text - - return EventText -end - --- Initializes the Events structure for the event -- @param #EVENT self @@ -419,7 +430,7 @@ function EVENT:Init( EventID, EventClass ) if not self.Events[EventID] then -- Create a WEAK table to ensure that the garbage collector is cleaning the event links when the object usage is cleaned. - self.Events[EventID] = setmetatable( {}, { __mode = "k" } ) + self.Events[EventID] = {} end -- Each event has a subtable of EventClasses, ordered by EventPriority. @@ -429,53 +440,56 @@ function EVENT:Init( EventID, EventClass ) end if not self.Events[EventID][EventPriority][EventClass] then - self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "v" } ) + self.Events[EventID][EventPriority][EventClass] = {} end return self.Events[EventID][EventPriority][EventClass] end ---- Removes an Events entry +--- Removes a subscription -- @param #EVENT self -- @param Core.Base#BASE EventClass The self instance of the class for which the event is. -- @param Dcs.DCSWorld#world.event EventID -- @return #EVENT.Events -function EVENT:Remove( EventClass, EventID ) - self:F3( { EventClass, _EVENTMETA[EventID].Text } ) +function EVENT:RemoveEvent( EventClass, EventID ) + + self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } ) - local EventClass = EventClass local EventPriority = EventClass:GetEventPriority() + + self.Events = self.Events or {} + self.Events[EventID] = self.Events[EventID] or {} + self.Events[EventID][EventPriority] = self.Events[EventID][EventPriority] or {} + self.Events[EventID][EventPriority][EventClass] = self.Events[EventID][EventPriority][EventClass] + self.Events[EventID][EventPriority][EventClass] = nil + end ---- Removes an Events entry for a UNIT. +--- Resets subscriptions -- @param #EVENT self --- @param #string UnitName The name of the UNIT. -- @param Core.Base#BASE EventClass The self instance of the class for which the event is. -- @param Dcs.DCSWorld#world.event EventID -- @return #EVENT.Events -function EVENT:RemoveForUnit( UnitName, EventClass, EventID ) - self:F3( { EventClass, _EVENTMETA[EventID].Text } ) +function EVENT:Reset( EventObject ) --R2.1 - local EventClass = EventClass - local EventPriority = EventClass:GetEventPriority() - local Event = self.Events[EventID][EventPriority][EventClass] - Event.EventUnit[UnitName] = nil + self:E( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } ) + + local EventPriority = EventObject:GetEventPriority() + for EventID, EventData in pairs( self.Events ) do + if self.EventsDead then + if self.EventsDead[EventID] then + if self.EventsDead[EventID][EventPriority] then + if self.EventsDead[EventID][EventPriority][EventObject] then + self.Events[EventID][EventPriority][EventObject] = self.EventsDead[EventID][EventPriority][EventObject] + end + end + end + end + end end ---- Removes an Events entry for a GROUP. --- @param #EVENT self --- @param #string GroupName The name of the GROUP. --- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param Dcs.DCSWorld#world.event EventID --- @return #EVENT.Events -function EVENT:RemoveForGroup( GroupName, EventClass, EventID ) - self:F3( { EventClass, _EVENTMETA[EventID].Text } ) - local EventClass = EventClass - local EventPriority = EventClass:GetEventPriority() - local Event = self.Events[EventID][EventPriority][EventClass] - Event.EventGroup[GroupName] = nil -end + --- Clears all event subscriptions for a @{Base#BASE} derived object. -- @param #EVENT self @@ -519,7 +533,6 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID ) local EventData = self:Init( EventID, EventClass ) EventData.EventFunction = EventFunction - EventData.EventClass = EventClass return self end @@ -536,12 +549,8 @@ function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID ) self:F2( UnitName ) local EventData = self:Init( EventID, EventClass ) - if not EventData.EventUnit then - EventData.EventUnit = {} - end - EventData.EventUnit[UnitName] = {} - EventData.EventUnit[UnitName].EventFunction = EventFunction - EventData.EventUnit[UnitName].EventClass = EventClass + EventData.EventUnit = true + EventData.EventFunction = EventFunction return self end @@ -556,12 +565,8 @@ function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID ) self:F2( GroupName ) local Event = self:Init( EventID, EventClass ) - if not Event.EventGroup then - Event.EventGroup = {} - end - Event.EventGroup[GroupName] = {} - Event.EventGroup[GroupName].EventFunction = EventFunction - Event.EventGroup[GroupName].EventClass = EventClass + Event.EventGroup = true + Event.EventFunction = EventFunction return self end @@ -672,6 +677,54 @@ do -- OnEngineShutDown end +do -- Event Creation + + --- Creation of a New Cargo Event. + -- @param #EVENT self + -- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created. + function EVENT:CreateEventNewCargo( Cargo ) + self:F( { Cargo } ) + + local Event = { + id = EVENTS.NewCargo, + time = timer.getTime(), + cargo = Cargo, + } + + world.onEvent( Event ) + end + + --- Creation of a Cargo Deletion Event. + -- @param #EVENT self + -- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created. + function EVENT:CreateEventDeleteCargo( Cargo ) + self:F( { Cargo } ) + + local Event = { + id = EVENTS.DeleteCargo, + time = timer.getTime(), + cargo = Cargo, + } + + world.onEvent( Event ) + end + + --- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event. + -- @param #EVENT self + -- @param Wrapper.Unit#UNIT PlayerUnit. + function EVENT:CreateEventPlayerEnterUnit( PlayerUnit ) + self:F( { PlayerUnit } ) + + local Event = { + id = EVENTS.PlayerEnterUnit, + time = timer.getTime(), + initiator = PlayerUnit:GetDCSObject() + } + + world.onEvent( Event ) + end + +end --- @param #EVENT self -- @param #EVENTDATA Event @@ -687,10 +740,13 @@ function EVENT:onEvent( Event ) return errmsg end - self:E( _EVENTMETA[Event.id].Text, Event ) - - if self and self.Events and self.Events[Event.id] then - + + local EventMeta = _EVENTMETA[Event.id] + + if self and + self.Events and + self.Events[Event.id] and + ( Event.initiator ~= nil or ( Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit ) ) then if Event.initiator then @@ -736,7 +792,7 @@ function EVENT:onEvent( Event ) Event.IniUnitName = Event.IniDCSUnitName Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator ) Event.IniCategory = Event.IniDCSUnit:getDesc().category - Event.IniTypeName = Event.IniDCSUnit:getTypeName() + Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1! end end @@ -795,12 +851,17 @@ function EVENT:onEvent( Event ) --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end - local PriorityOrder = _EVENTMETA[Event.id].Order + if Event.cargo then + Event.Cargo = Event.cargo + Event.CargoName = Event.cargo.Name + end + + local PriorityOrder = EventMeta.Order local PriorityBegin = PriorityOrder == -1 and 5 or 1 local PriorityEnd = PriorityOrder == -1 and 1 or 5 - if Event.IniObjectCategory ~= 3 then - self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) + if Event.IniObjectCategory ~= Object.Category.STATIC then + self:E( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) end for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do @@ -810,186 +871,146 @@ function EVENT:onEvent( Event ) -- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called. for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do + --if Event.IniObjectCategory ~= Object.Category.STATIC then + -- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } ) + --end + Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName ) Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) -- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT. - if ( Event.IniDCSUnitName and EventData.EventUnit and EventData.EventUnit[Event.IniDCSUnitName] ) or - ( Event.TgtDCSUnitName and EventData.EventUnit and EventData.EventUnit[Event.TgtDCSUnitName] ) then + if EventData.EventUnit then - if EventData.EventUnit[Event.IniDCSUnitName] then - - -- First test if a EventFunction is Set, otherwise search for the default function - if EventData.EventUnit[Event.IniDCSUnitName].EventFunction then + -- So now the EventClass must be a UNIT class!!! We check if it is still "Alive". + if EventClass:IsAlive() or + Event.id == EVENTS.Crash or + Event.id == EVENTS.Dead then - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event ) - end, ErrorHandler ) - - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. + local UnitName = EventClass:GetName() + + if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or + ( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then + + -- First test if a EventFunction is Set, otherwise search for the default function + if EventData.EventFunction then + if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) end - + local Result, Value = xpcall( function() - return EventFunction( EventClass, Event ) + return EventData.EventFunction( EventClass, Event ) end, ErrorHandler ) - end - end - end - - if EventData.EventUnit[Event.TgtDCSUnitName] then - - -- First test if a EventFunction is Set, otherwise search for the default function - if EventData.EventUnit[Event.TgtDCSUnitName].EventFunction then - - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventData.EventUnit[Event.TgtDCSUnitName].EventFunction( EventClass, Event ) - end, ErrorHandler ) - - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + + else + + -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. + local EventFunction = EventClass[ EventMeta.Event ] + if EventFunction and type( EventFunction ) == "function" then + + -- Now call the default event function. + if Event.IniObjectCategory ~= 3 then + self:E( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end + + local Result, Value = xpcall( + function() + return EventFunction( EventClass, Event ) + end, ErrorHandler ) end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event ) - end, ErrorHandler ) end end - end - + else + -- The EventClass is not alive anymore, we remove it from the EventHandlers... + self:RemoveEvent( EventClass, Event.id ) + end else -- If the EventData is for a GROUP, the call directly the EventClass EventFunction for the UNIT in that GROUP. - if ( Event.IniDCSUnitName and Event.IniDCSGroupName and Event.IniGroupName and EventData.EventGroup and EventData.EventGroup[Event.IniGroupName] ) or - ( Event.TgtDCSUnitName and Event.TgtDCSGroupName and Event.TgtGroupName and EventData.EventGroup and EventData.EventGroup[Event.TgtGroupName] ) then + if EventData.EventGroup then - if EventData.EventGroup[Event.IniGroupName] then - -- First test if a EventFunction is Set, otherwise search for the default function - if EventData.EventGroup[Event.IniGroupName].EventFunction then + -- So now the EventClass must be a GROUP class!!! We check if it is still "Alive". + if EventClass:IsAlive() or + Event.id == EVENTS.Crash or + Event.id == EVENTS.Dead then - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event ) - end, ErrorHandler ) - - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event ) - end, ErrorHandler ) - end - end - end + -- We can get the name of the EventClass, which is now always a GROUP object. + local GroupName = EventClass:GetName() + + if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or + ( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then - if EventData.EventGroup[Event.TgtGroupName] then - if EventData.EventGroup[Event.TgtGroupName].EventFunction then - - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event ) - end, ErrorHandler ) - - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event ) - end, ErrorHandler ) - end - end - end - - else - - -- If the EventData is not bound to a specific unit, then call the EventClass EventFunction. - -- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon. - if (Event.IniDCSUnit or Event.WeaponUNIT) and not EventData.EventUnit then - - if EventClass == EventData.EventClass then - -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventFunction then - - -- There is an EventFunction defined, so call the EventFunction. + if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - end + self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) + end + local Result, Value = xpcall( function() return EventData.EventFunction( EventClass, Event ) end, ErrorHandler ) + else - + -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] + local EventFunction = EventClass[ EventMeta.Event ] if EventFunction and type( EventFunction ) == "function" then -- Now call the default event function. if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + self:E( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) end - + local Result, Value = xpcall( function() - local Result, Value = EventFunction( EventClass, Event ) - return Result, Value + return EventFunction( EventClass, Event ) end, ErrorHandler ) end end end + else + -- The EventClass is not alive anymore, we remove it from the EventHandlers... + self:RemoveEvent( EventClass, Event.id ) + end + else + + -- If the EventData is not bound to a specific unit, then call the EventClass EventFunction. + -- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon. + if not EventData.EventUnit then + + -- First test if a EventFunction is Set, otherwise search for the default function + if EventData.EventFunction then + + -- There is an EventFunction defined, so call the EventFunction. + if Event.IniObjectCategory ~= 3 then + self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end + local Result, Value = xpcall( + function() + return EventData.EventFunction( EventClass, Event ) + end, ErrorHandler ) + else + + -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. + local EventFunction = EventClass[ EventMeta.Event ] + if EventFunction and type( EventFunction ) == "function" then + + -- Now call the default event function. + if Event.IniObjectCategory ~= 3 then + self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) + end + + local Result, Value = xpcall( + function() + local Result, Value = EventFunction( EventClass, Event ) + return Result, Value + end, ErrorHandler ) + end + end + end end end @@ -997,7 +1018,7 @@ function EVENT:onEvent( Event ) end end else - self:E( { _EVENTMETA[Event.id].Text, Event } ) + self:E( { EventMeta.Text, Event } ) end Event = nil diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 8b6ef0331..d934f68ad 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -1,10 +1,12 @@ ---- **Core** - The **FSM** (**F**inite **S**tate **M**achine) class and derived **FSM\_** classes +--- **Core** -- The **FSM** (**F**inite **S**tate **M**achine) class and derived **FSM\_** classes -- are design patterns allowing efficient (long-lasting) processes and workflows. -- -- ![Banner Image](..\Presentations\FSM\Dia1.JPG) -- -- === -- +-- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**. +-- -- A FSM can only be in one of a finite number of states. -- The machine is in only one state at a time; the state it is in at any given time is called the **current state**. -- It can change from one state to another when initiated by an **__internal__ or __external__ triggering event**, which is called a **transition**. @@ -56,43 +58,57 @@ -- -- ==== -- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) --- YYYY-MM-DD: CLASS:**NewFunction( Params )** added --- --- Hereby the change log: --- --- * 2016-12-18: Released. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** -- +-- ### Author: **Sven Van de Velde (FlightControl)** -- ### Contributions: -- --- * [**Pikey**](https://forums.eagle.ru/member.php?u=62835): Review of documentation & advice for improvements. --- --- ### Authors: --- --- * [**FlightControl**](https://forums.eagle.ru/member.php?u=89536): Design & Programming & documentation. +-- ==== -- -- @module Fsm do -- FSM - --- FSM class --- @type FSM -- @extends Core.Base#BASE - --- # 1) FSM class, extends @{Base#BASE} + --- # FSM class, extends @{Base#BASE} -- + -- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**. + -- + -- A FSM can only be in one of a finite number of states. + -- The machine is in only one state at a time; the state it is in at any given time is called the **current state**. + -- It can change from one state to another when initiated by an **__internal__ or __external__ triggering event**, which is called a **transition**. + -- An **FSM implementation** is defined by **a list of its states**, **its initial state**, and **the triggering events** for **each possible transition**. + -- An FSM implementation is composed out of **two parts**, a set of **state transition rules**, and an implementation set of **state transition handlers**, implementing those transitions. + -- + -- The FSM class supports a **hierarchical implementation of a Finite State Machine**, + -- that is, it allows to **embed existing FSM implementations in a master FSM**. + -- FSM hierarchies allow for efficient FSM re-use, **not having to re-invent the wheel every time again** when designing complex processes. + -- + -- ![Workflow Example](..\Presentations\FSM\Dia2.JPG) + -- + -- The above diagram shows a graphical representation of a FSM implementation for a **Task**, which guides a Human towards a Zone, + -- orders him to destroy x targets and account the results. + -- Other examples of ready made FSM could be: + -- + -- * route a plane to a zone flown by a human + -- * detect targets by an AI and report to humans + -- * account for destroyed targets by human players + -- * handle AI infantry to deploy from or embark to a helicopter or airplane or vehicle + -- * let an AI patrol a zone + -- + -- The **MOOSE framework** uses extensively the FSM class and derived FSM\_ classes, + -- because **the goal of MOOSE is to simplify mission design complexity for mission building**. + -- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes. + -- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used, + -- and tailored** by mission designers through **the implementation of Transition Handlers**. + -- Each of these FSM implementation classes start either with: + -- + -- * an acronym **AI\_**, which indicates an FSM implementation directing **AI controlled** @{GROUP} and/or @{UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class. + -- * an acronym **TASK\_**, which indicates an FSM implementation executing a @{TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class. + -- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{TASK}, seated in a @{CLIENT} (slot) or a @{UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class. + -- -- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG) -- -- The FSM class is the base class of all FSM\_ derived classes. It implements the main functionality to define and execute Finite State Machines. @@ -115,13 +131,13 @@ do -- FSM -- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation. -- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**. -- - -- ## 1.1) FSM Linear Transitions + -- ## FSM Linear Transitions -- -- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**. -- The Lineair transition rule evaluation will always be done from the **current state** of the FSM. -- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop. -- - -- ### 1.1.1) FSM Transition Rules + -- ### FSM Transition Rules -- -- The FSM has transition rules that it follows and validates, as it walks the process. -- These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event. @@ -146,7 +162,7 @@ do -- FSM -- * It can be switched **Off** by triggering event **SwitchOff**. -- * Note that once the Switch is **On** or **Middle**, it can only be switched **Off**. -- - -- ### Some additional comments: + -- #### Some additional comments: -- -- Note that Linear Transition Rules **can be declared in a few variations**: -- @@ -157,7 +173,7 @@ do -- FSM -- -- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" ) -- - -- ### 1.1.2) Transition Handling + -- ### Transition Handling -- -- ![Transition Handlers](..\Presentations\FSM\Dia4.JPG) -- @@ -179,7 +195,7 @@ do -- FSM -- -- On top, each of these methods can have a variable amount of parameters passed. See the example in section [1.1.3](#1.1.3\)-event-triggers). -- - -- ### 1.1.3) Event Triggers + -- ### Event Triggers -- -- ![Event Triggers](..\Presentations\FSM\Dia5.JPG) -- @@ -217,7 +233,7 @@ do -- FSM -- -- Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing! -- - -- ### 1.1.4) Linear Transition Example + -- ### Linear Transition Example -- -- This example is fully implemented in the MOOSE test mission on GITHUB: [FSM-100 - Transition Explanation](https://github.com/FlightControl-Master/MOOSE/blob/master/Moose%20Test%20Missions/FSM%20-%20Finite%20State%20Machine/FSM-100%20-%20Transition%20Explanation/FSM-100%20-%20Transition%20Explanation.lua) -- @@ -299,7 +315,7 @@ do -- FSM -- So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped. -- And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt. -- - -- ## 1.5) FSM Hierarchical Transitions + -- ## FSM Hierarchical Transitions -- -- Hierarchical Transitions allow to re-use readily available and implemented FSMs. -- This becomes in very useful for mission building, where mission designers build complex processes and workflows, @@ -380,7 +396,7 @@ do -- FSM Transition.Event = Event Transition.To = To - self:T( Transition ) + self:T2( Transition ) self._Transitions[Transition] = Transition self:_eventmap( self.Events, Transition ) @@ -402,7 +418,7 @@ do -- FSM -- @param #table ReturnEvents A table indicating for which returned events of the SubFSM which Event must be triggered in the FSM. -- @return Core.Fsm#FSM_PROCESS The SubFSM. function FSM:AddProcess( From, Event, Process, ReturnEvents ) - self:T( { From, Event, Process, ReturnEvents } ) + self:T( { From, Event } ) local Sub = {} Sub.From = From @@ -518,14 +534,14 @@ do -- FSM local __Event = "__" .. EventStructure.Event self[Event] = self[Event] or self:_create_transition(Event) self[__Event] = self[__Event] or self:_delayed_transition(Event) - self:T( "Added methods: " .. Event .. ", " .. __Event ) + self:T2( "Added methods: " .. Event .. ", " .. __Event ) Events[Event] = self.Events[Event] or { map = {} } self:_add_to_map( Events[Event].map, EventStructure ) end function FSM:_submap( subs, sub, name ) - self:F( { sub = sub, name = name } ) + --self:F( { sub = sub, name = name } ) subs[sub.From] = subs[sub.From] or {} subs[sub.From][sub.Event] = subs[sub.From][sub.Event] or {} @@ -553,7 +569,7 @@ do -- FSM return errmsg end if self[handler] then - self:T( "Calling " .. handler ) + self:T2( "Calling " .. handler ) self._EventSchedules[EventName] = nil local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler ) return Value @@ -828,7 +844,7 @@ do -- FSM_CONTROLLABLE -- @param Wrapper.Controllable#CONTROLLABLE FSMControllable -- @return #FSM_CONTROLLABLE function FSM_CONTROLLABLE:SetControllable( FSMControllable ) - self:F( FSMControllable ) + --self:F( FSMControllable:GetName() ) self.Controllable = FSMControllable end @@ -888,7 +904,7 @@ do -- FSM_PROCESS local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_PROCESS - self:F( Controllable, Task ) + --self:F( Controllable ) self:Assign( Controllable, Task ) @@ -944,7 +960,7 @@ do -- FSM_PROCESS -- Copy Processes for ProcessID, Process in pairs( self:GetProcesses() ) do - self:E( { Process} ) + --self:E( { Process:GetName() } ) local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents ) end @@ -967,15 +983,19 @@ do -- FSM_PROCESS -- @param #FSM_PROCESS self -- @return #FSM_PROCESS function FSM_PROCESS:Remove() - self:T( { self:GetClassNameAndID() } ) - + self:F( { self:GetClassNameAndID() } ) + + self:F( "Clearing Schedules" ) + self.CallScheduler:Clear() + -- Copy Processes for ProcessID, Process in pairs( self:GetProcesses() ) do - self:E( { Process} ) - Process.fsm:Remove() - Process.fsm = nil + if Process.fsm then + Process.fsm:Remove() + Process.fsm = nil + end end - + return self end @@ -1042,7 +1062,7 @@ end -- @param Wrapper.Unit#UNIT ProcessUnit -- @return #FSM_PROCESS self function FSM_PROCESS:Assign( ProcessUnit, Task ) - self:T( { Task, ProcessUnit } ) + --self:T( { Task:GetName(), ProcessUnit:GetName() } ) self:SetControllable( ProcessUnit ) self:SetTask( Task ) @@ -1063,12 +1083,7 @@ end self.Task:Fail() end - - function FSM_PROCESS:onenterSuccess( ProcessUnit ) - self:T( "Success" ) - - self.Task:Success() - end + --- StateMachine callback function for a FSM_PROCESS -- @param #FSM_PROCESS self @@ -1077,10 +1092,10 @@ end -- @param #string From -- @param #string To function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy ) - self:T( { ProcessUnit, From, Event, To, Dummy, self:IsTrace() } ) + self:T( { ProcessUnit:GetName(), From, Event, To, Dummy, self:IsTrace() } ) if self:IsTrace() then - MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() + --MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() end self:T( { Scores = self._Scores, To = To } ) @@ -1102,7 +1117,7 @@ do -- FSM_TASK --- FSM_TASK class -- @type FSM_TASK -- @field Tasking.Task#TASK Task - -- @extends Core.Fsm#FSM + -- @extends #FSM --- # FSM_TASK, extends @{#FSM} -- diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 25ea2989e..20705583f 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -26,110 +26,24 @@ -- * @{Menu#MENU_CLIENT_COMMAND}: Manages command menus for CLIENTs. This manages menus for units with the skill level "Client". -- -- === +--- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: -- --- The above menus classes **are derived** from 2 main **abstract** classes defined within the MOOSE framework (so don't use these): --- --- 1) MENU_ BASE abstract base classes (don't use them) --- ==================================================== --- The underlying base menu classes are **NOT** to be used within your missions. --- These are simply abstract base classes defining a couple of fields that are used by the --- derived MENU_ classes to manage menus. --- --- 1.1) @{#MENU_BASE} class, extends @{Base#BASE} --- -------------------------------------------------- --- The @{#MENU_BASE} class defines the main MENU class where other MENU classes are derived from. --- --- 1.2) @{#MENU_COMMAND_BASE} class, extends @{Base#BASE} --- ---------------------------------------------------------- --- The @{#MENU_COMMAND_BASE} class defines the main MENU class where other MENU COMMAND_ classes are derived from, in order to set commands. --- --- === --- --- **The next menus define the MENU classes that you can use within your missions.** --- --- 2) MENU MISSION classes --- ====================== --- The underlying classes manage the menus for a complete mission file. --- --- 2.1) @{#MENU_MISSION} class, extends @{Menu#MENU_BASE} --- --------------------------------------------------------- --- The @{Menu#MENU_MISSION} class manages the main menus for a complete mission. --- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}. --- --- 2.2) @{#MENU_MISSION_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE} --- ------------------------------------------------------------------------- --- The @{Menu#MENU_MISSION_COMMAND} class manages the command menus for a complete mission, which allow players to execute functions during mission execution. --- You can add menus with the @{#MENU_MISSION_COMMAND.New} method, which constructs a MENU_MISSION_COMMAND object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION_COMMAND.Remove}. --- --- === --- --- 3) MENU COALITION classes --- ========================= --- The underlying classes manage the menus for whole coalitions. --- --- 3.1) @{#MENU_COALITION} class, extends @{Menu#MENU_BASE} --- ------------------------------------------------------------ --- The @{Menu#MENU_COALITION} class manages the main menus for coalitions. --- You can add menus with the @{#MENU_COALITION.New} method, which constructs a MENU_COALITION object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION.Remove}. --- --- 3.2) @{Menu#MENU_COALITION_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE} --- ---------------------------------------------------------------------------- --- The @{Menu#MENU_COALITION_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. --- You can add menus with the @{#MENU_COALITION_COMMAND.New} method, which constructs a MENU_COALITION_COMMAND object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION_COMMAND.Remove}. --- --- === --- --- 4) MENU GROUP classes --- ===================== --- The underlying classes manage the menus for groups. Note that groups can be inactive, alive or can be destroyed. --- --- 4.1) @{Menu#MENU_GROUP} class, extends @{Menu#MENU_BASE} --- -------------------------------------------------------- --- The @{Menu#MENU_GROUP} class manages the main menus for coalitions. --- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}. --- --- 4.2) @{Menu#MENU_GROUP_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE} --- ------------------------------------------------------------------------ --- The @{Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. --- You can add menus with the @{#MENU_GROUP_COMMAND.New} method, which constructs a MENU_GROUP_COMMAND object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP_COMMAND.Remove}. --- --- === --- --- 5) MENU CLIENT classes --- ====================== --- The underlying classes manage the menus for units with skill level client or player. --- --- 5.1) @{Menu#MENU_CLIENT} class, extends @{Menu#MENU_BASE} --- --------------------------------------------------------- --- The @{Menu#MENU_CLIENT} class manages the main menus for coalitions. --- You can add menus with the @{#MENU_CLIENT.New} method, which constructs a MENU_CLIENT object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT.Remove}. --- --- 5.2) @{Menu#MENU_CLIENT_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE} --- ------------------------------------------------------------------------- --- The @{Menu#MENU_CLIENT_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. --- You can add menus with the @{#MENU_CLIENT_COMMAND.New} method, which constructs a MENU_CLIENT_COMMAND object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT_COMMAND.Remove}. --- --- === --- --- ### Contributions: - --- ### Authors: FlightControl : Design & Programming --- +-- ==== +-- -- @module Menu do -- MENU_BASE - --- The MENU_BASE class - -- @type MENU_BASE + --- @type MENU_BASE -- @extends Base#BASE + + --- # MENU_BASE class, extends @{Base#BASE} + -- The MENU_BASE class defines the main MENU class where other MENU classes are derived from. + -- This is an abstract class, so don't use it. + -- @field #MENU_BASE MENU_BASE = { ClassName = "MENU_BASE", MenuPath = nil, @@ -165,7 +79,7 @@ do -- MENU_BASE -- @param #string MenuText The text of the child menu. -- @return #MENU_BASE function MENU_BASE:GetMenu( MenuText ) - self:F( { self.Menus, MenuText } ) + self:F2( { Menu = self.Menus[MenuText] } ) return self.Menus[MenuText] end @@ -174,7 +88,7 @@ do -- MENU_BASE -- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}. -- @return #MENU_BASE function MENU_BASE:SetRemoveParent( RemoveParent ) - self:F( { RemoveParent } ) + self:F2( { RemoveParent } ) self.MenuRemoveParent = RemoveParent return self end @@ -189,14 +103,29 @@ do -- MENU_BASE return self end + --- Sets a tag for later selection of menu refresh. + -- @param #MENU_BASE self + -- @param #string MenuTag A Tag or Key that will filter only menu items set with this key. + -- @return #MENU_BASE + function MENU_BASE:SetTag( MenuTag ) + self.MenuTag = MenuTag + return self + end + end do -- MENU_COMMAND_BASE - --- The MENU_COMMAND_BASE class - -- @type MENU_COMMAND_BASE + --- @type MENU_COMMAND_BASE -- @field #function MenuCallHandler -- @extends Core.Menu#MENU_BASE + + --- # MENU_COMMAND_BASE class, extends @{Base#BASE} + -- ---------------------------------------------------------- + -- The MENU_COMMAND_BASE class defines the main MENU class where other MENU COMMAND_ + -- classes are derived from, in order to set commands. + -- + -- @field #MENU_COMMAND_BASE MENU_COMMAND_BASE = { ClassName = "MENU_COMMAND_BASE", CommandMenuFunction = nil, @@ -209,24 +138,53 @@ do -- MENU_COMMAND_BASE -- @return #MENU_COMMAND_BASE function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments ) - local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) + local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) -- #MENU_COMMAND_BASE - self.CommandMenuFunction = CommandMenuFunction - self.MenuCallHandler = function( CommandMenuArguments ) - self.CommandMenuFunction( unpack( CommandMenuArguments ) ) + self:SetCommandMenuFunction( CommandMenuFunction ) + self:SetCommandMenuArguments( CommandMenuArguments ) + self.MenuCallHandler = function() + self.CommandMenuFunction( unpack( self.CommandMenuArguments ) ) end return self end + + --- This sets the new command function of a menu, + -- so that if a menu is regenerated, or if command function changes, + -- that the function set for the menu is loosely coupled with the menu itself!!! + -- If the function changes, no new menu needs to be generated if the menu text is the same!!! + -- @param #MENU_COMMAND_BASE + -- @return #MENU_COMMAND_BASE + function MENU_COMMAND_BASE:SetCommandMenuFunction( CommandMenuFunction ) + self.CommandMenuFunction = CommandMenuFunction + return self + end + + --- This sets the new command arguments of a menu, + -- so that if a menu is regenerated, or if command arguments change, + -- that the arguments set for the menu are loosely coupled with the menu itself!!! + -- If the arguments change, no new menu needs to be generated if the menu text is the same!!! + -- @param #MENU_COMMAND_BASE + -- @return #MENU_COMMAND_BASE + function MENU_COMMAND_BASE:SetCommandMenuArguments( CommandMenuArguments ) + self.CommandMenuArguments = CommandMenuArguments + return self + end end do -- MENU_MISSION - --- The MENU_MISSION class - -- @type MENU_MISSION + --- @type MENU_MISSION -- @extends Core.Menu#MENU_BASE + + --- # MENU_MISSION class, extends @{Menu#MENU_BASE} + -- + -- The MENU_MISSION class manages the main menus for a complete mission. + -- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference. + -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}. + -- @field #MENU_MISSION MENU_MISSION = { ClassName = "MENU_MISSION" } @@ -291,9 +249,16 @@ end do -- MENU_MISSION_COMMAND - --- The MENU_MISSION_COMMAND class - -- @type MENU_MISSION_COMMAND + --- @type MENU_MISSION_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE + + --- # MENU_MISSION_COMMAND class, extends @{Menu#MENU_COMMAND_BASE} + -- + -- The MENU_MISSION_COMMAND class manages the command menus for a complete mission, which allow players to execute functions during mission execution. + -- You can add menus with the @{#MENU_MISSION_COMMAND.New} method, which constructs a MENU_MISSION_COMMAND object and returns you the object reference. + -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION_COMMAND.Remove}. + -- + -- @field #MENU_MISSION_COMMAND MENU_MISSION_COMMAND = { ClassName = "MENU_MISSION_COMMAND" } @@ -315,7 +280,7 @@ do -- MENU_MISSION_COMMAND self:T( { MenuText, CommandMenuFunction, arg } ) - self.MenuPath = missionCommands.addCommand( MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) + self.MenuPath = missionCommands.addCommand( MenuText, self.MenuParentPath, self.MenuCallHandler ) ParentMenu.Menus[self.MenuPath] = self @@ -341,9 +306,16 @@ end do -- MENU_COALITION - --- The MENU_COALITION class - -- @type MENU_COALITION + --- @type MENU_COALITION -- @extends Core.Menu#MENU_BASE + + --- # MENU_COALITION class, extends @{Menu#MENU_BASE} + -- + -- The @{Menu#MENU_COALITION} class manages the main menus for coalitions. + -- You can add menus with the @{#MENU_COALITION.New} method, which constructs a MENU_COALITION object and returns you the object reference. + -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION.Remove}. + -- + -- -- @usage -- -- This demo creates a menu structure for the planes within the red coalition. -- -- To test, join the planes, then look at the other radio menus (Option F10). @@ -380,6 +352,8 @@ do -- MENU_COALITION -- -- local MenuAdd = MENU_COALITION_COMMAND:New( coalition.side.RED, "Add Status Menu", MenuCoalitionRed, AddStatusMenu ) -- local MenuRemove = MENU_COALITION_COMMAND:New( coalition.side.RED, "Remove Status Menu", MenuCoalitionRed, RemoveStatusMenu ) + -- + -- @field #MENU_COALITION MENU_COALITION = { ClassName = "MENU_COALITION" } @@ -446,9 +420,16 @@ end do -- MENU_COALITION_COMMAND - --- The MENU_COALITION_COMMAND class - -- @type MENU_COALITION_COMMAND + --- @type MENU_COALITION_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE + + --- # MENU_COALITION_COMMAND class, extends @{Menu#MENU_COMMAND_BASE} + -- + -- The MENU_COALITION_COMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution. + -- You can add menus with the @{#MENU_COALITION_COMMAND.New} method, which constructs a MENU_COALITION_COMMAND object and returns you the object reference. + -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION_COMMAND.Remove}. + -- + -- @field #MENU_COALITION_COMMAND MENU_COALITION_COMMAND = { ClassName = "MENU_COALITION_COMMAND" } @@ -472,7 +453,7 @@ do -- MENU_COALITION_COMMAND self:T( { MenuText, CommandMenuFunction, arg } ) - self.MenuPath = missionCommands.addCommandForCoalition( self.MenuCoalition, MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) + self.MenuPath = missionCommands.addCommandForCoalition( self.MenuCoalition, MenuText, self.MenuParentPath, self.MenuCallHandler ) ParentMenu.Menus[self.MenuPath] = self @@ -506,6 +487,14 @@ do -- MENU_CLIENT --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. -- @type MENU_CLIENT -- @extends Core.Menu#MENU_BASE + + + --- # MENU_CLIENT class, extends @{Menu#MENU_BASE} + -- + -- The MENU_CLIENT class manages the main menus for coalitions. + -- You can add menus with the @{#MENU_CLIENT.New} method, which constructs a MENU_CLIENT object and returns you the object reference. + -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT.Remove}. + -- -- @usage -- -- This demo creates a menu structure for the two clients of planes. -- -- Each client will receive a different menu structure. @@ -555,6 +544,8 @@ do -- MENU_CLIENT -- MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneClient ) -- end -- end, {}, 10, 10 ) + -- + -- @field #MENU_CLIENT MENU_CLIENT = { ClassName = "MENU_CLIENT" } @@ -644,9 +635,16 @@ do -- MENU_CLIENT end - --- The MENU_CLIENT_COMMAND class - -- @type MENU_CLIENT_COMMAND + --- @type MENU_CLIENT_COMMAND -- @extends Core.Menu#MENU_COMMAND + + --- # MENU_CLIENT_COMMAND class, extends @{Menu#MENU_COMMAND_BASE} + -- + -- The MENU_CLIENT_COMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution. + -- You can add menus with the @{#MENU_CLIENT_COMMAND.New} method, which constructs a MENU_CLIENT_COMMAND object and returns you the object reference. + -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT_COMMAND.Remove}. + -- + -- @field #MENU_CLIENT_COMMAND MENU_CLIENT_COMMAND = { ClassName = "MENU_CLIENT_COMMAND" } @@ -688,7 +686,7 @@ do -- MENU_CLIENT missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] ) end - self.MenuPath = missionCommands.addCommandForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath, self.MenuCallHandler, arg ) + self.MenuPath = missionCommands.addCommandForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath, self.MenuCallHandler ) MenuPath[MenuPathID] = self.MenuPath if ParentMenu and ParentMenu.Menus then @@ -730,9 +728,16 @@ do -- These menu classes are handling this logic with this variable. local _MENUGROUPS = {} - --- The MENU_GROUP class - -- @type MENU_GROUP + --- @type MENU_GROUP -- @extends Core.Menu#MENU_BASE + + + --- #MENU_GROUP class, extends @{Menu#MENU_BASE} + -- + -- The MENU_GROUP class manages the main menus for coalitions. + -- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference. + -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}. + -- -- @usage -- -- This demo creates a menu structure for the two groups of planes. -- -- Each group will receive a different menu structure. @@ -783,6 +788,7 @@ do -- end -- end, {}, 10, 10 ) -- + -- @field #MENU_GROUP MENU_GROUP = { ClassName = "MENU_GROUP" } @@ -804,7 +810,9 @@ do self = MenuGroup._Menus[Path] else self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) - MenuGroup._Menus[Path] = self + --if MenuGroup:IsAlive() then + MenuGroup._Menus[Path] = self + --end self.MenuGroup = MenuGroup self.Path = Path @@ -830,13 +838,14 @@ do --- Removes the sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self -- @param MenuTime + -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @return #MENU_GROUP self - function MENU_GROUP:RemoveSubMenus( MenuTime ) - self:F2( { self.MenuPath, MenuTime, self.MenuTime } ) + function MENU_GROUP:RemoveSubMenus( MenuTime, MenuTag ) + --self:F2( { self.MenuPath, MenuTime, self.MenuTime } ) - self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } ) + self:T( { "Removing Group SubMenus:", MenuTime, MenuTag, self.MenuGroup:GetName(), self.MenuPath } ) for MenuText, Menu in pairs( self.Menus ) do - Menu:Remove( MenuTime ) + Menu:Remove( MenuTime, MenuTag ) end end @@ -845,28 +854,31 @@ do --- Removes the main menu and sub menus recursively of this MENU_GROUP. -- @param #MENU_GROUP self -- @param MenuTime + -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @return #nil - function MENU_GROUP:Remove( MenuTime ) - self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) + function MENU_GROUP:Remove( MenuTime, MenuTag ) + --self:F2( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - self:RemoveSubMenus( MenuTime ) + self:RemoveSubMenus( MenuTime, MenuTag ) if not MenuTime or self.MenuTime ~= MenuTime then - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] - - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuText] = nil - self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 - if self.ParentMenu.MenuCount == 0 then - if self.MenuRemoveParent == true then - self:T( "Removing Parent Menu " ) - self.ParentMenu:Remove() + if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then + if self.MenuGroup._Menus[self.Path] then + self = self.MenuGroup._Menus[self.Path] + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + if self.ParentMenu then + self.ParentMenu.Menus[self.MenuText] = nil + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 + if self.ParentMenu.MenuCount == 0 then + if self.MenuRemoveParent == true then + self:T2( "Removing Parent Menu " ) + self.ParentMenu:Remove() + end end end end - self:T( { "Removing Group Menu:", self.MenuGroup:GetName(), self.MenuGroup._Menus[self.Path].Path } ) + self:T( { "Removing Group Menu:", MenuGroup = self.MenuGroup:GetName() } ) self.MenuGroup._Menus[self.Path] = nil self = nil end @@ -876,9 +888,16 @@ do end - --- The MENU_GROUP_COMMAND class - -- @type MENU_GROUP_COMMAND - -- @extends Core.Menu#MENU_BASE + --- @type MENU_GROUP_COMMAND + -- @extends Core.Menu#MENU_COMMAND_BASE + + --- # MENU_GROUP_COMMAND class, extends @{Menu#MENU_COMMAND_BASE} + -- + -- The @{Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. + -- You can add menus with the @{#MENU_GROUP_COMMAND.New} method, which constructs a MENU_GROUP_COMMAND object and returns you the object reference. + -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP_COMMAND.Remove}. + -- + -- @field #MENU_GROUP_COMMAND MENU_GROUP_COMMAND = { ClassName = "MENU_GROUP_COMMAND" } @@ -894,29 +913,37 @@ do function MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, CommandMenuFunction, ... ) MenuGroup._Menus = MenuGroup._Menus or {} - local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText + local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText if MenuGroup._Menus[Path] then self = MenuGroup._Menus[Path] - self:T( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } ) - else - self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) - MenuGroup._Menus[Path] = self - - self.Path = Path - self.MenuGroup = MenuGroup - self.MenuGroupID = MenuGroup:GetID() - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self:T( { "Adding Group Command Menu:", MenuGroup:GetName(), MenuText, self.MenuParentPath } ) - self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) - - if self.ParentMenu and self.ParentMenu.Menus then - self.ParentMenu.Menus[MenuText] = self - self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 - self:F( { ParentMenu.Menus, MenuText } ) - end + --self:E( { Path=Path } ) + --self:E( { self.MenuTag, self.MenuTime, "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } ) + self:SetCommandMenuFunction( CommandMenuFunction ) + self:SetCommandMenuArguments( arg ) + return self end + self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) + + --if MenuGroup:IsAlive() then + MenuGroup._Menus[Path] = self + --end + + --self:E({Path=Path}) + self.Path = Path + self.MenuGroup = MenuGroup + self.MenuGroupID = MenuGroup:GetID() + self.MenuText = MenuText + self.ParentMenu = ParentMenu + + self:F( { "Adding Group Command Menu:", MenuGroup = MenuGroup:GetName(), MenuText = MenuText, MenuPath = self.MenuParentPath } ) + self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler ) + + if self.ParentMenu and self.ParentMenu.Menus then + self.ParentMenu.Menus[MenuText] = self + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 + self:F2( { ParentMenu.Menus, MenuText } ) + end +-- end return self end @@ -924,28 +951,32 @@ do --- Removes a menu structure for a group. -- @param #MENU_GROUP_COMMAND self -- @param MenuTime + -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @return #nil - function MENU_GROUP_COMMAND:Remove( MenuTime ) - self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) + function MENU_GROUP_COMMAND:Remove( MenuTime, MenuTag ) + --self:F2( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) + --self:E( { MenuTag = MenuTag, MenuTime = self.MenuTime, Path = self.Path } ) if not MenuTime or self.MenuTime ~= MenuTime then - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] - - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - self:T( { "Removing Group Command Menu:", self.MenuGroup:GetName(), self.MenuText, self.Path, self.MenuGroup._Menus[self.Path].Path } ) - - self.ParentMenu.Menus[self.MenuText] = nil - self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 - if self.ParentMenu.MenuCount == 0 then - if self.MenuRemoveParent == true then - self:T( "Removing Parent Menu " ) - self.ParentMenu:Remove() + if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then + if self.MenuGroup._Menus[self.Path] then + self = self.MenuGroup._Menus[self.Path] + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + --self:E( { "Removing Group Command Menu:", MenuGroup = self.MenuGroup:GetName(), MenuText = self.MenuText, MenuPath = self.Path } ) + + self.ParentMenu.Menus[self.MenuText] = nil + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 + if self.ParentMenu.MenuCount == 0 then + if self.MenuRemoveParent == true then + self:T2( "Removing Parent Menu " ) + self.ParentMenu:Remove() + end end + + self.MenuGroup._Menus[self.Path] = nil + self = nil end - - self.MenuGroup._Menus[self.Path] = nil - self = nil end end diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 711b75585..e632f38fc 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -1,21 +1,27 @@ ---- **Core** - MESSAGE class takes are of the **real-time notifications** and **messages to players** during a simulation. +--- **Core** -- MESSAGE class takes are of the **real-time notifications** and **messages to players** during a simulation. -- -- ![Banner Image](..\Presentations\MESSAGE\Dia1.JPG) -- -- === -- --- # 1) @{Message#MESSAGE} class, extends @{Base#BASE} +-- @module Message + +--- The MESSAGE class +-- @type MESSAGE +-- @extends Core.Base#BASE + +--- # MESSAGE class, extends @{Base#BASE} -- -- Message System to display Messages to Clients, Coalitions or All. -- Messages are shown on the display panel for an amount of seconds, and will then disappear. -- Messages can contain a category which is indicating the category of the message. -- --- ## 1.1) MESSAGE construction +-- ## MESSAGE construction -- -- Messages are created with @{Message#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet. -- To send messages, you need to use the To functions. -- --- ## 1.2) Send messages to an audience +-- ## Send messages to an audience -- -- Messages are sent: -- @@ -26,19 +32,21 @@ -- * To the blue coalition using @{Message#MESSAGE.ToBlue}(). -- * To all Players using @{Message#MESSAGE.ToAll}(). -- --- ## 1.3) Send conditionally to an audience +-- ## Send conditionally to an audience -- -- Messages can be sent conditionally to an audience (when a condition is true): -- -- * To all players using @{Message#MESSAGE.ToAllIf}(). -- * To a coalition using @{Message#MESSAGE.ToCoalitionIf}(). -- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: -- --- @module Message - ---- The MESSAGE class --- @type MESSAGE --- @extends Core.Base#BASE +-- ==== +-- +-- @field #MESSAGE MESSAGE = { ClassName = "MESSAGE", MessageCategory = 0, @@ -79,7 +87,7 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory ) self.MessageDuration = MessageDuration or 5 self.MessageTime = timer.getTime() - self.MessageText = MessageText + self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1) self.MessageSent = false self.MessageGroup = false diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 3df7bdbfb..7f9b04620 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1,921 +1,1330 @@ ---- **Core** - **POINT\_VEC** classes define an **extensive API** to **manage 3D points** in the simulation space. --- --- 1) @{Point#POINT_VEC3} class, extends @{Base#BASE} --- ================================================== --- The @{Point#POINT_VEC3} class defines a 3D point in the simulator. --- --- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts. --- In order to keep the credibility of the the author, I want to emphasize that the of the MIST framework was created by Grimes, who you can find on the Eagle Dynamics Forums. --- --- ## 1.1) POINT_VEC3 constructor --- --- A new POINT_VEC3 instance can be created with: --- --- * @{Point#POINT_VEC3.New}(): a 3D point. --- * @{Point#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}. --- --- ## 1.2) Manupulate the X, Y, Z coordinates of the point --- --- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate. --- Methods exist to manupulate these coordinates. --- --- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively. --- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value. --- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}() --- to add or substract a value from the current respective axis value. --- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example: --- --- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3() --- --- ## 1.3) Create waypoints for routes --- --- A POINT_VEC3 can prepare waypoints for Ground, Air and Naval groups to be embedded into a Route. --- --- --- ## 1.5) Smoke, flare, explode, illuminate --- --- At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods: --- --- ### 1.5.1) Smoke --- --- * @{#POINT_VEC3.Smoke}(): To smoke the point in a certain color. --- * @{#POINT_VEC3.SmokeBlue}(): To smoke the point in blue. --- * @{#POINT_VEC3.SmokeRed}(): To smoke the point in red. --- * @{#POINT_VEC3.SmokeOrange}(): To smoke the point in orange. --- * @{#POINT_VEC3.SmokeWhite}(): To smoke the point in white. --- * @{#POINT_VEC3.SmokeGreen}(): To smoke the point in green. --- --- ### 1.5.2) Flare --- --- * @{#POINT_VEC3.Flare}(): To flare the point in a certain color. --- * @{#POINT_VEC3.FlareRed}(): To flare the point in red. --- * @{#POINT_VEC3.FlareYellow}(): To flare the point in yellow. --- * @{#POINT_VEC3.FlareWhite}(): To flare the point in white. --- * @{#POINT_VEC3.FlareGreen}(): To flare the point in green. --- --- ### 1.5.3) Explode --- --- * @{#POINT_VEC3.Explosion}(): To explode the point with a certain intensity. --- --- ### 1.5.4) Illuminate --- --- * @{#POINT_VEC3.IlluminationBomb}(): To illuminate the point. --- +--- **Core** -- **POINT\_VEC** classes define an **extensive API** to **manage 3D points** in the simulation space. +-- +-- ![Banner Image](..\Presentations\POINT\Dia1.JPG) +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [POINT_VEC Demo Missions source code]() +-- +-- ### [POINT_VEC Demo Missions, only for beta testers]() +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [POINT_VEC YouTube Channel]() -- --- 2) @{Point#POINT_VEC2} class, extends @{Point#POINT_VEC3} --- ========================================================= --- The @{Point#POINT_VEC2} class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified. --- --- 2.1) POINT_VEC2 constructor --- --------------------------- --- A new POINT_VEC2 instance can be created with: --- --- * @{Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter. --- * @{Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}. --- --- ## 1.2) Manupulate the X, Altitude, Y coordinates of the 2D point --- --- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate. --- Methods exist to manupulate these coordinates. --- --- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively. --- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value. --- The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods @{#POINT_VEC2.GetLat}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetLon}() respectively. --- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}() --- to add or substract a value from the current respective axis value. --- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example: --- --- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2() --- -- === --- --- **API CHANGE HISTORY** --- ====================== --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-03: POINT\_VEC3:**Explosion( ExplosionIntensity )** added. --- 2017-03-03: POINT\_VEC3:**IlluminationBomb()** added. --- --- 2017-02-18: POINT\_VEC3:**NewFromVec2( Vec2, LandHeightAdd )** added. --- --- 2016-08-12: POINT\_VEC3:**Translate( Distance, Angle )** added. --- --- 2016-08-06: Made PointVec3 and Vec3, PointVec2 and Vec2 terminology used in the code consistent. --- --- * Replaced method _Point_Vec3() to **Vec3**() where the code manages a Vec3. Replaced all references to the method. --- * Replaced method _Point_Vec2() to **Vec2**() where the code manages a Vec2. Replaced all references to the method. --- * Replaced method Random_Point_Vec3() to **RandomVec3**() where the code manages a Vec3. Replaced all references to the method. --- . --- === --- --- ### Authors: --- +-- +-- ### Authors: +-- -- * FlightControl : Design & Programming --- --- ### Contributions: --- +-- +-- ### Contributions: +-- -- @module Point ---- The POINT_VEC3 class --- @type POINT_VEC3 --- @field #number x The x coordinate in 3D space. --- @field #number y The y coordinate in 3D space. --- @field #number z The z coordiante in 3D space. --- @field Utilities.Utils#SMOKECOLOR SmokeColor --- @field Utilities.Utils#FLARECOLOR FlareColor --- @field #POINT_VEC3.RoutePointAltType RoutePointAltType --- @field #POINT_VEC3.RoutePointType RoutePointType --- @field #POINT_VEC3.RoutePointAction RoutePointAction --- @extends Core.Base#BASE -POINT_VEC3 = { - ClassName = "POINT_VEC3", - Metric = true, - RoutePointAltType = { - BARO = "BARO", - }, - RoutePointType = { - TakeOffParking = "TakeOffParking", - TurningPoint = "Turning Point", - }, - RoutePointAction = { - FromParkingArea = "From Parking Area", - TurningPoint = "Turning Point", - }, -} ---- The POINT_VEC2 class --- @type POINT_VEC2 --- @field Dcs.DCSTypes#Distance x The x coordinate in meters. --- @field Dcs.DCSTypes#Distance y the y coordinate in meters. --- @extends Core.Point#POINT_VEC3 -POINT_VEC2 = { - ClassName = "POINT_VEC2", -} + +do -- COORDINATE + + --- @type COORDINATE + -- @extends Core.Base#BASE + + + --- # COORDINATE class, extends @{Base#BASE} + -- + -- COORDINATE defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. + -- + -- ## COORDINATE constructor + -- + -- A new COORDINATE object can be created with: + -- + -- * @{#COORDINATE.New}(): a 3D point. + -- * @{#COORDINATE.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}. + -- * @{#COORDINATE.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}. + -- + -- ## Create waypoints for routes + -- + -- A COORDINATE can prepare waypoints for Ground and Air groups to be embedded into a Route. + -- + -- * @{#COORDINATE.WaypointAir}(): Build an air route point. + -- * @{#COORDINATE.WaypointGround}(): Build a ground route point. + -- + -- Route points can be used in the Route methods of the @{Group#GROUP} class. + -- + -- + -- ## Smoke, flare, explode, illuminate + -- + -- At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods: + -- + -- ### Smoke + -- + -- * @{#COORDINATE.Smoke}(): To smoke the point in a certain color. + -- * @{#COORDINATE.SmokeBlue}(): To smoke the point in blue. + -- * @{#COORDINATE.SmokeRed}(): To smoke the point in red. + -- * @{#COORDINATE.SmokeOrange}(): To smoke the point in orange. + -- * @{#COORDINATE.SmokeWhite}(): To smoke the point in white. + -- * @{#COORDINATE.SmokeGreen}(): To smoke the point in green. + -- + -- ### Flare + -- + -- * @{#COORDINATE.Flare}(): To flare the point in a certain color. + -- * @{#COORDINATE.FlareRed}(): To flare the point in red. + -- * @{#COORDINATE.FlareYellow}(): To flare the point in yellow. + -- * @{#COORDINATE.FlareWhite}(): To flare the point in white. + -- * @{#COORDINATE.FlareGreen}(): To flare the point in green. + -- + -- ### Explode + -- + -- * @{#COORDINATE.Explosion}(): To explode the point with a certain intensity. + -- + -- ### Illuminate + -- + -- * @{#COORDINATE.IlluminationBomb}(): To illuminate the point. + -- + -- + -- ## 3D calculation methods + -- + -- Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method: + -- + -- ### Distance + -- + -- * @{#COORDINATE.Get3DDistance}(): Obtain the distance from the current 3D point to the provided 3D point in 3D space. + -- * @{#COORDINATE.Get2DDistance}(): Obtain the distance from the current 3D point to the provided 3D point in 2D space. + -- + -- ### Angle + -- + -- * @{#COORDINATE.GetAngleDegrees}(): Obtain the angle in degrees from the current 3D point with the provided 3D direction vector. + -- * @{#COORDINATE.GetAngleRadians}(): Obtain the angle in radians from the current 3D point with the provided 3D direction vector. + -- * @{#COORDINATE.GetDirectionVec3}(): Obtain the 3D direction vector from the current 3D point to the provided 3D point. + -- + -- ### Translation + -- + -- * @{#COORDINATE.Translate}(): Translate the current 3D point towards an other 3D point using the given Distance and Angle. + -- + -- ### Get the North correction of the current location + -- + -- * @{#COORDINATE.GetNorthCorrection}(): Obtains the north correction at the current 3D point. + -- + -- + -- ## Point Randomization + -- + -- Various methods exist to calculate random locations around a given 3D point. + -- + -- * @{#COORDINATE.GetRandomVec2InRadius}(): Provides a random 2D vector around the current 3D point, in the given inner to outer band. + -- * @{#COORDINATE.GetRandomVec3InRadius}(): Provides a random 3D vector around the current 3D point, in the given inner to outer band. + -- + -- + -- ## Metric system + -- + -- * @{#COORDINATE.IsMetric}(): Returns if the 3D point is Metric or Nautical Miles. + -- * @{#COORDINATE.SetMetric}(): Sets the 3D point to Metric or Nautical Miles. + -- + -- + -- ## Coorinate text generation + -- + -- * @{#COORDINATE.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance. + -- * @{#COORDINATE.ToStringLL}(): Generates a Latutude & Longutude text. + -- + -- @field #COORDINATE + COORDINATE = { + ClassName = "COORDINATE", + } + + + --- COORDINATE constructor. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North. + -- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing to the Right. + -- @param Dcs.DCSTypes#Distance z The z coordinate of the Vec3 point, pointing to the Right. + -- @return #COORDINATE + function COORDINATE:New( x, y, z ) + + local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE + self.x = x + self.y = y + self.z = z + + return self + end + + --- Create a new COORDINATE object from Vec2 coordinates. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. + -- @param Dcs.DCSTypes#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height. + -- @return #COORDINATE + function COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) + + local LandHeight = land.getHeight( Vec2 ) + + LandHeightAdd = LandHeightAdd or 0 + LandHeight = LandHeight + LandHeightAdd + + local self = self:New( Vec2.x, LandHeight, Vec2.y ) -- #COORDINATE + + self:F2( self ) + + return self + + end + + --- Create a new COORDINATE object from Vec3 coordinates. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point. + -- @return #COORDINATE + function COORDINATE:NewFromVec3( Vec3 ) + + local self = self:New( Vec3.x, Vec3.y, Vec3.z ) -- #COORDINATE + + self:F2( self ) + + return self + end + + + --- Return the coordinates of the COORDINATE in Vec3 format. + -- @param #COORDINATE self + -- @return Dcs.DCSTypes#Vec3 The Vec3 format coordinate. + function COORDINATE:GetVec3() + return { x = self.x, y = self.y, z = self.z } + end + + + --- Return the coordinates of the COORDINATE in Vec2 format. + -- @param #COORDINATE self + -- @return Dcs.DCSTypes#Vec2 The Vec2 format coordinate. + function COORDINATE:GetVec2() + return { x = self.x, y = self.z } + end + + --TODO: check this to replace + --- Calculate the distance from a reference @{DCSTypes#Vec2}. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Vec2 Vec2Reference The reference @{DCSTypes#Vec2}. + -- @return Dcs.DCSTypes#Distance The distance from the reference @{DCSTypes#Vec2} in meters. + function COORDINATE:DistanceFromVec2( Vec2Reference ) + self:F2( Vec2Reference ) + + local Distance = ( ( Vec2Reference.x - self.x ) ^ 2 + ( Vec2Reference.y - self.z ) ^2 ) ^0.5 + + self:T2( Distance ) + return Distance + end + + + --- Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters. + -- @param Dcs.DCSTypes#Angle Angle The Angle in degrees. + -- @return #COORDINATE The new calculated COORDINATE. + function COORDINATE:Translate( Distance, Angle ) + local SX = self.x + local SY = self.z + local Radians = Angle / 180 * math.pi + local TX = Distance * math.cos( Radians ) + SX + local TY = Distance * math.sin( Radians ) + SY + + return COORDINATE:NewFromVec2( { x = TX, y = TY } ) + end + + --- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Distance OuterRadius + -- @param Dcs.DCSTypes#Distance InnerRadius + -- @return Dcs.DCSTypes#Vec2 Vec2 + function COORDINATE:GetRandomVec2InRadius( OuterRadius, InnerRadius ) + self:F2( { OuterRadius, InnerRadius } ) + + local Theta = 2 * math.pi * math.random() + local Radials = math.random() + math.random() + if Radials > 1 then + Radials = 2 - Radials + end + + local RadialMultiplier + if InnerRadius and InnerRadius <= OuterRadius then + RadialMultiplier = ( OuterRadius - InnerRadius ) * Radials + InnerRadius + else + RadialMultiplier = OuterRadius * Radials + end + + local RandomVec2 + if OuterRadius > 0 then + RandomVec2 = { x = math.cos( Theta ) * RadialMultiplier + self.x, y = math.sin( Theta ) * RadialMultiplier + self.z } + else + RandomVec2 = { x = self.x, y = self.z } + end + + return RandomVec2 + end + + + --- Return a random Vec3 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Distance OuterRadius + -- @param Dcs.DCSTypes#Distance InnerRadius + -- @return Dcs.DCSTypes#Vec3 Vec3 + function COORDINATE:GetRandomVec3InRadius( OuterRadius, InnerRadius ) + + local RandomVec2 = self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) + local y = self.y + math.random( InnerRadius, OuterRadius ) + local RandomVec3 = { x = RandomVec2.x, y = y, z = RandomVec2.y } + + return RandomVec3 + end + + --- Return the height of the land at the coordinate. + -- @param #COORDINATE self + -- @return #number + function COORDINATE:GetLandHeight() + local Vec2 = { x = self.x, y = self.z } + return land.getHeight( Vec2 ) + end + + + function COORDINATE:SetHeading( Heading ) + self.Heading = Heading + end + + + --- Return a direction vector Vec3 from COORDINATE to the COORDINATE. + -- @param #COORDINATE self + -- @param #COORDINATE TargetCoordinate The target COORDINATE. + -- @return Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. + function COORDINATE:GetDirectionVec3( TargetCoordinate ) + return { x = TargetCoordinate.x - self.x, y = TargetCoordinate.y - self.y, z = TargetCoordinate.z - self.z } + end + + --- Get a correction in radians of the real magnetic north of the COORDINATE. + -- @param #COORDINATE self + -- @return #number CorrectionRadians The correction in radians. + function COORDINATE:GetNorthCorrectionRadians() + local TargetVec3 = self:GetVec3() + local lat, lon = coord.LOtoLL(TargetVec3) + local north_posit = coord.LLtoLO(lat + 1, lon) + return math.atan2( north_posit.z - TargetVec3.z, north_posit.x - TargetVec3.x ) + end + + + --- Return an angle in radians from the COORDINATE using a direction vector in Vec3 format. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. + -- @return #number DirectionRadians The angle in radians. + function COORDINATE:GetAngleRadians( DirectionVec3 ) + local DirectionRadians = math.atan2( DirectionVec3.z, DirectionVec3.x ) + --DirectionRadians = DirectionRadians + self:GetNorthCorrectionRadians() + if DirectionRadians < 0 then + DirectionRadians = DirectionRadians + 2 * math.pi -- put dir in range of 0 to 2*pi ( the full circle ) + end + return DirectionRadians + end + + --- Return an angle in degrees from the COORDINATE using a direction vector in Vec3 format. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. + -- @return #number DirectionRadians The angle in degrees. + function COORDINATE:GetAngleDegrees( DirectionVec3 ) + local AngleRadians = self:GetAngleRadians( DirectionVec3 ) + local Angle = UTILS.ToDegree( AngleRadians ) + return Angle + end + + + --- Return the 2D distance in meters between the target COORDINATE and the COORDINATE. + -- @param #COORDINATE self + -- @param #COORDINATE TargetCoordinate The target COORDINATE. + -- @return Dcs.DCSTypes#Distance Distance The distance in meters. + function COORDINATE:Get2DDistance( TargetCoordinate ) + local TargetVec3 = TargetCoordinate:GetVec3() + local SourceVec3 = self:GetVec3() + return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5 + end + + --- Return the 3D distance in meters between the target COORDINATE and the COORDINATE. + -- @param #COORDINATE self + -- @param #COORDINATE TargetCoordinate The target COORDINATE. + -- @return Dcs.DCSTypes#Distance Distance The distance in meters. + function COORDINATE:Get3DDistance( TargetCoordinate ) + local TargetVec3 = TargetCoordinate:GetVec3() + local SourceVec3 = self:GetVec3() + return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5 + end + + + --- Provides a bearing text in degrees. + -- @param #COORDINATE self + -- @param #number AngleRadians The angle in randians. + -- @param #number Precision The precision. + -- @param Core.Settings#SETTINGS Settings + -- @return #string The bearing text in degrees. + function COORDINATE:GetBearingText( AngleRadians, Precision, Settings ) + + local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS + + local AngleDegrees = UTILS.Round( UTILS.ToDegree( AngleRadians ), Precision ) + + local s = string.format( '%03d°', AngleDegrees ) + + return s + end + + --- Provides a distance text expressed in the units of measurement. + -- @param #COORDINATE self + -- @param #number Distance The distance in meters. + -- @param Core.Settings#SETTINGS Settings + -- @return #string The distance text expressed in the units of measurement. + function COORDINATE:GetDistanceText( Distance, Settings ) + + local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS + + local DistanceText + + if Settings:IsMetric() then + DistanceText = " for " .. UTILS.Round( Distance / 1000, 2 ) .. " km" + else + DistanceText = " for " .. UTILS.Round( UTILS.MetersToNM( Distance ), 2 ) .. " miles" + end + + return DistanceText + end + + --- Return the altitude text of the COORDINATE. + -- @param #COORDINATE self + -- @return #string Altitude text. + function COORDINATE:GetAltitudeText( Settings ) + local Altitude = self.y + local Settings = Settings or _SETTINGS + if Altitude ~= 0 then + if Settings:IsMetric() then + return " at " .. UTILS.Round( self.y, -3 ) .. " meters" + else + return " at " .. UTILS.Round( UTILS.MetersToFeet( self.y ), -3 ) .. " feet" + end + else + return "" + end + end + + + --- Provides a Bearing / Range string + -- @param #COORDINATE self + -- @param #number AngleRadians The angle in randians + -- @param #number Distance The distance + -- @param Core.Settings#SETTINGS Settings + -- @return #string The BR Text + function COORDINATE:GetBRText( AngleRadians, Distance, Settings ) + + local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS + + local BearingText = self:GetBearingText( AngleRadians, 0, Settings ) + local DistanceText = self:GetDistanceText( Distance, Settings ) + + local BRText = BearingText .. DistanceText + + return BRText + end + + --- Provides a Bearing / Range / Altitude string + -- @param #COORDINATE self + -- @param #number AngleRadians The angle in randians + -- @param #number Distance The distance + -- @param Core.Settings#SETTINGS Settings + -- @return #string The BRA Text + function COORDINATE:GetBRAText( AngleRadians, Distance, Settings ) + + local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS + + local BearingText = self:GetBearingText( AngleRadians, 0, Settings ) + local DistanceText = self:GetDistanceText( Distance, Settings ) + local AltitudeText = self:GetAltitudeText( Settings ) + + local BRAText = BearingText .. DistanceText .. AltitudeText -- When the POINT is a VEC2, there will be no altitude shown. + + return BRAText + end + + + + --- Add a Distance in meters from the COORDINATE horizontal plane, with the given angle, and calculate the new COORDINATE. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters. + -- @param Dcs.DCSTypes#Angle Angle The Angle in degrees. + -- @return #COORDINATE The new calculated COORDINATE. + function COORDINATE:Translate( Distance, Angle ) + local SX = self.x + local SZ = self.z + local Radians = Angle / 180 * math.pi + local TX = Distance * math.cos( Radians ) + SX + local TZ = Distance * math.sin( Radians ) + SZ + + return COORDINATE:New( TX, self.y, TZ ) + end + + + + --- Build an air type route point. + -- @param #COORDINATE self + -- @param #COORDINATE.RoutePointAltType AltType The altitude type. + -- @param #COORDINATE.RoutePointType Type The route point type. + -- @param #COORDINATE.RoutePointAction Action The route point action. + -- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. + -- @param #boolean SpeedLocked true means the speed is locked. + -- @return #table The route point. + function COORDINATE:WaypointAir( AltType, Type, Action, Speed, SpeedLocked ) + self:F2( { AltType, Type, Action, Speed, SpeedLocked } ) + + local RoutePoint = {} + RoutePoint.x = self.x + RoutePoint.y = self.z + RoutePoint.alt = self.y + RoutePoint.alt_type = AltType or "RADIO" + + RoutePoint.type = Type or nil + RoutePoint.action = Action or nil + + RoutePoint.speed = ( Speed and Speed / 3.6 ) or ( 500 / 3.6 ) + RoutePoint.speed_locked = true + + -- ["task"] = + -- { + -- ["id"] = "ComboTask", + -- ["params"] = + -- { + -- ["tasks"] = + -- { + -- }, -- end of ["tasks"] + -- }, -- end of ["params"] + -- }, -- end of ["task"] + + + RoutePoint.task = {} + RoutePoint.task.id = "ComboTask" + RoutePoint.task.params = {} + RoutePoint.task.params.tasks = {} + + + return RoutePoint + end + + --- Build an ground type route point. + -- @param #COORDINATE self + -- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. + -- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right". + -- @return #table The route point. + function COORDINATE:WaypointGround( Speed, Formation ) + self:F2( { Formation, Speed } ) + + local RoutePoint = {} + RoutePoint.x = self.x + RoutePoint.y = self.z + + RoutePoint.action = Formation or "" + + + RoutePoint.speed = ( Speed or 999 ) / 3.6 + RoutePoint.speed_locked = true + + -- ["task"] = + -- { + -- ["id"] = "ComboTask", + -- ["params"] = + -- { + -- ["tasks"] = + -- { + -- }, -- end of ["tasks"] + -- }, -- end of ["params"] + -- }, -- end of ["task"] + + + RoutePoint.task = {} + RoutePoint.task.id = "ComboTask" + RoutePoint.task.params = {} + RoutePoint.task.params.tasks = {} + + + return RoutePoint + end + + --- Creates an explosion at the point of a certain intensity. + -- @param #COORDINATE self + -- @param #number ExplosionIntensity + function COORDINATE:Explosion( ExplosionIntensity ) + self:F2( { ExplosionIntensity } ) + trigger.action.explosion( self:GetVec3(), ExplosionIntensity ) + end + + --- Creates an illumination bomb at the point. + -- @param #COORDINATE self + function COORDINATE:IlluminationBomb() + self:F2() + trigger.action.illuminationBomb( self:GetVec3() ) + end + + + --- Smokes the point in a color. + -- @param #COORDINATE self + -- @param Utilities.Utils#SMOKECOLOR SmokeColor + function COORDINATE:Smoke( SmokeColor ) + self:F2( { SmokeColor } ) + trigger.action.smoke( self:GetVec3(), SmokeColor ) + end + + --- Smoke the COORDINATE Green. + -- @param #COORDINATE self + function COORDINATE:SmokeGreen() + self:F2() + self:Smoke( SMOKECOLOR.Green ) + end + + --- Smoke the COORDINATE Red. + -- @param #COORDINATE self + function COORDINATE:SmokeRed() + self:F2() + self:Smoke( SMOKECOLOR.Red ) + end + + --- Smoke the COORDINATE White. + -- @param #COORDINATE self + function COORDINATE:SmokeWhite() + self:F2() + self:Smoke( SMOKECOLOR.White ) + end + + --- Smoke the COORDINATE Orange. + -- @param #COORDINATE self + function COORDINATE:SmokeOrange() + self:F2() + self:Smoke( SMOKECOLOR.Orange ) + end + + --- Smoke the COORDINATE Blue. + -- @param #COORDINATE self + function COORDINATE:SmokeBlue() + self:F2() + self:Smoke( SMOKECOLOR.Blue ) + end + + --- Flares the point in a color. + -- @param #COORDINATE self + -- @param Utilities.Utils#FLARECOLOR FlareColor + -- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. + function COORDINATE:Flare( FlareColor, Azimuth ) + self:F2( { FlareColor } ) + trigger.action.signalFlare( self:GetVec3(), FlareColor, Azimuth and Azimuth or 0 ) + end + + --- Flare the COORDINATE White. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. + function COORDINATE:FlareWhite( Azimuth ) + self:F2( Azimuth ) + self:Flare( FLARECOLOR.White, Azimuth ) + end + + --- Flare the COORDINATE Yellow. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. + function COORDINATE:FlareYellow( Azimuth ) + self:F2( Azimuth ) + self:Flare( FLARECOLOR.Yellow, Azimuth ) + end + + --- Flare the COORDINATE Green. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. + function COORDINATE:FlareGreen( Azimuth ) + self:F2( Azimuth ) + self:Flare( FLARECOLOR.Green, Azimuth ) + end + + --- Flare the COORDINATE Red. + -- @param #COORDINATE self + function COORDINATE:FlareRed( Azimuth ) + self:F2( Azimuth ) + self:Flare( FLARECOLOR.Red, Azimuth ) + end + + --- Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate. + -- @param #COORDINATE self + -- @param #COORDINATE ToCoordinate + -- @return #boolean true If the ToCoordinate has LOS with the Coordinate, otherwise false. + function COORDINATE:IsLOS( ToCoordinate ) + + -- Measurement of visibility should not be from the ground, so Adding a hypotethical 2 meters to each Coordinate. + local FromVec3 = self:GetVec3() + FromVec3.y = FromVec3.y + 2 + + local ToVec3 = ToCoordinate:GetVec3() + ToVec3.y = ToVec3.y + 2 + + local IsLOS = land.isVisible( FromVec3, ToVec3 ) + + return IsLOS + end + + + --- Return a BR string from a COORDINATE to the COORDINATE. + -- @param #COORDINATE self + -- @param #COORDINATE TargetCoordinate The target COORDINATE. + -- @return #string The BR text. + function COORDINATE:ToStringBR( FromCoordinate, Settings ) + local DirectionVec3 = FromCoordinate:GetDirectionVec3( self ) + local AngleRadians = self:GetAngleRadians( DirectionVec3 ) + local Distance = self:Get2DDistance( FromCoordinate ) + return "BR, " .. self:GetBRText( AngleRadians, Distance, Settings ) + end + + --- Return a BRAA string from a COORDINATE to the COORDINATE. + -- @param #COORDINATE self + -- @param #COORDINATE TargetCoordinate The target COORDINATE. + -- @return #string The BR text. + function COORDINATE:ToStringBRA( FromCoordinate, Settings ) + local DirectionVec3 = FromCoordinate:GetDirectionVec3( self ) + local AngleRadians = self:GetAngleRadians( DirectionVec3 ) + local Distance = FromCoordinate:Get2DDistance( self ) + local Altitude = self:GetAltitudeText() + return "BRA, " .. self:GetBRAText( AngleRadians, Distance, Settings ) + end + + --- Return a BULLS string from a COORDINATE to the BULLS of the coalition. + -- @param #COORDINATE self + -- @param Dcs.DCSCoalition#coalition.side Coalition The coalition. + -- @return #string The BR text. + function COORDINATE:ToStringBULLS( Coalition, Settings ) + local TargetCoordinate = COORDINATE:NewFromVec3( coalition.getMainRefPoint( Coalition ) ) + local DirectionVec3 = self:GetDirectionVec3( TargetCoordinate ) + local AngleRadians = self:GetAngleRadians( DirectionVec3 ) + local Distance = self:Get2DDistance( TargetCoordinate ) + local Altitude = self:GetAltitudeText() + return "BULLS, " .. self:GetBRText( AngleRadians, Distance, Settings ) + end + + --- Return an aspect string from a COORDINATE to the Angle of the object. + -- @param #COORDINATE self + -- @param #COORDINATE TargetCoordinate The target COORDINATE. + -- @return #string The Aspect string, which is Hot, Cold or Flanking. + function COORDINATE:ToStringAspect( TargetCoordinate ) + local Heading = self.Heading + local DirectionVec3 = self:GetDirectionVec3( TargetCoordinate ) + local Angle = self:GetAngleDegrees( DirectionVec3 ) + + if Heading then + local Aspect = Angle - Heading + if Aspect > -135 and Aspect <= -45 then + return "Flanking" + end + if Aspect > -45 and Aspect <= 45 then + return "Hot" + end + if Aspect > 45 and Aspect <= 135 then + return "Flanking" + end + if Aspect > 135 or Aspect <= -135 then + return "Cold" + end + end + return "" + end + + --- Provides a Lat Lon string in Degree Minute Second format. + -- @param #COORDINATE self + -- @param Core.Settings#SETTINGS Settings (optional) Settings + -- @return #string The LL DMS Text + function COORDINATE:ToStringLLDMS( Settings ) + + local LL_Accuracy = Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy + local lat, lon = coord.LOtoLL( self:GetVec3() ) + return "LL DMS, " .. UTILS.tostringLL( lat, lon, LL_Accuracy, true ) + end + + --- Provides a Lat Lon string in Degree Decimal Minute format. + -- @param #COORDINATE self + -- @param Core.Settings#SETTINGS Settings (optional) Settings + -- @return #string The LL DDM Text + function COORDINATE:ToStringLLDDM( Settings ) + + local LL_Accuracy = Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy + local lat, lon = coord.LOtoLL( self:GetVec3() ) + return "LL DDM, " .. UTILS.tostringLL( lat, lon, LL_Accuracy, false ) + end + + --- Provides a MGRS string + -- @param #COORDINATE self + -- @param Core.Settings#SETTINGS Settings (optional) Settings + -- @return #string The MGRS Text + function COORDINATE:ToStringMGRS( Settings ) --R2.1 Fixes issue #424. + + local MGRS_Accuracy = Settings and Settings.MGRS_Accuracy or _SETTINGS.MGRS_Accuracy + local lat, lon = coord.LOtoLL( self:GetVec3() ) + local MGRS = coord.LLtoMGRS( lat, lon ) + return "MGRS, " .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy ) + end + + --- Provides a coordinate string of the point, based on a coordinate format system: + -- * Uses default settings in COORDINATE. + -- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default. + -- @param #COORDINATE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable + -- @param Core.Settings#SETTINGS Settings + -- @return #string The coordinate Text in the configured coordinate system. + function COORDINATE:ToStringFromRP( ReferenceCoord, ReferenceName, Controllable, Settings ) -- R2.2 + + self:E( { ReferenceCoord = ReferenceCoord, ReferenceName = ReferenceName } ) + + local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS + + local IsAir = Controllable and Controllable:IsAirPlane() or false + + if IsAir then + local DirectionVec3 = ReferenceCoord:GetDirectionVec3( self ) + local AngleRadians = self:GetAngleRadians( DirectionVec3 ) + local Distance = self:Get2DDistance( ReferenceCoord ) + return "Targets are the last seen " .. self:GetBRText( AngleRadians, Distance, Settings ) .. " from " .. ReferenceName + else + local DirectionVec3 = ReferenceCoord:GetDirectionVec3( self ) + local AngleRadians = self:GetAngleRadians( DirectionVec3 ) + local Distance = self:Get2DDistance( ReferenceCoord ) + return "Target are located " .. self:GetBRText( AngleRadians, Distance, Settings ) .. " from " .. ReferenceName + end + + return nil + + end + + --- Provides a coordinate string of the point, based on a coordinate format system: + -- * Uses default settings in COORDINATE. + -- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default. + -- @param #COORDINATE self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable + -- @param Core.Settings#SETTINGS Settings + -- @param Tasking.Task#TASK Task The task for which coordinates need to be calculated. + -- @return #string The coordinate Text in the configured coordinate system. + function COORDINATE:ToString( Controllable, Settings, Task ) -- R2.2 + + self:F( { Controllable = Controllable and Controllable:GetName() } ) + + local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS + + local ModeA2A = true + + if Task then + if Task:IsInstanceOf( TASK_A2A ) then + ModeA2A = true + else + if Task:IsInstanceOf( TASK_A2G ) then + ModeA2A = false + else + if Task:IsInstanceOf( TASK_CARGO ) then + ModeA2A = false + end + end + end + else + local IsAir = Controllable and Controllable:IsAirPlane() or false + if IsAir then + ModeA2A = true + else + ModeA2A = false + end + end + + + if ModeA2A then + if Settings:IsA2A_BRAA() then + if Controllable then + local Coordinate = Controllable:GetCoordinate() + return self:ToStringBRA( Coordinate, Settings ) + else + return self:ToStringMGRS( Settings ) + end + end + if Settings:IsA2A_BULLS() then + local Coalition = Controllable:GetCoalition() + return self:ToStringBULLS( Coalition, Settings ) + end + if Settings:IsA2A_LL_DMS() then + return self:ToStringLLDMS( Settings ) + end + if Settings:IsA2A_LL_DDM() then + return self:ToStringLLDDM( Settings ) + end + if Settings:IsA2A_MGRS() then + return self:ToStringMGRS( Settings ) + end + else + if Settings:IsA2G_BR() then + -- If no Controllable is given to calculate the BR from, then MGRS will be used!!! + if Controllable then + local Coordinate = Controllable:GetCoordinate() + return Controllable and self:ToStringBR( Coordinate, Settings ) or self:ToStringMGRS( Settings ) + else + return self:ToStringMGRS( Settings ) + end + end + if Settings:IsA2G_LL_DMS() then + return self:ToStringLLDMS( Settings ) + end + if Settings:IsA2G_LL_DDM() then + return self:ToStringLLDDM( Settings ) + end + if Settings:IsA2G_MGRS() then + return self:ToStringMGRS( Settings ) + end + end + + return nil + + end + +end + do -- POINT_VEC3 ---- RoutePoint AltTypes --- @type POINT_VEC3.RoutePointAltType --- @field BARO "BARO" - ---- RoutePoint Types --- @type POINT_VEC3.RoutePointType --- @field TakeOffParking "TakeOffParking" --- @field TurningPoint "Turning Point" - ---- RoutePoint Actions --- @type POINT_VEC3.RoutePointAction --- @field FromParkingArea "From Parking Area" --- @field TurningPoint "Turning Point" - --- Constructor. - ---- Create a new POINT_VEC3 object. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North. --- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing Upwards. --- @param Dcs.DCSTypes#Distance z The z coordinate of the Vec3 point, pointing to the Right. --- @return Core.Point#POINT_VEC3 self -function POINT_VEC3:New( x, y, z ) - - local self = BASE:Inherit( self, BASE:New() ) - self.x = x - self.y = y - self.z = z - - return self -end - ---- Create a new POINT_VEC3 object from Vec2 coordinates. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. --- @return Core.Point#POINT_VEC3 self -function POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd ) - - local LandHeight = land.getHeight( Vec2 ) - - LandHeightAdd = LandHeightAdd or 0 - LandHeight = LandHeight + LandHeightAdd - - self = self:New( Vec2.x, LandHeight, Vec2.y ) - - self:F2( self ) - - return self -end - ---- Create a new POINT_VEC3 object from Vec3 coordinates. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point. --- @return Core.Point#POINT_VEC3 self -function POINT_VEC3:NewFromVec3( Vec3 ) - - self = self:New( Vec3.x, Vec3.y, Vec3.z ) - self:F2( self ) - return self -end - - ---- Return the coordinates of the POINT_VEC3 in Vec3 format. --- @param #POINT_VEC3 self --- @return Dcs.DCSTypes#Vec3 The Vec3 coodinate. -function POINT_VEC3:GetVec3() - return { x = self.x, y = self.y, z = self.z } -end - ---- Return the coordinates of the POINT_VEC3 in Vec2 format. --- @param #POINT_VEC3 self --- @return Dcs.DCSTypes#Vec2 The Vec2 coodinate. -function POINT_VEC3:GetVec2() - return { x = self.x, y = self.z } -end - - ---- Return the x coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #number The x coodinate. -function POINT_VEC3:GetX() - return self.x -end - ---- Return the y coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #number The y coodinate. -function POINT_VEC3:GetY() - return self.y -end - ---- Return the z coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #number The z coodinate. -function POINT_VEC3:GetZ() - return self.z -end - ---- Set the x coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number x The x coordinate. --- @return #POINT_VEC3 -function POINT_VEC3:SetX( x ) - self.x = x - return self -end - ---- Set the y coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number y The y coordinate. --- @return #POINT_VEC3 -function POINT_VEC3:SetY( y ) - self.y = y - return self -end - ---- Set the z coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number z The z coordinate. --- @return #POINT_VEC3 -function POINT_VEC3:SetZ( z ) - self.z = z - return self -end - ---- Add to the x coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number x The x coordinate value to add to the current x coodinate. --- @return #POINT_VEC3 -function POINT_VEC3:AddX( x ) - self.x = self.x + x - return self -end - ---- Add to the y coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number y The y coordinate value to add to the current y coodinate. --- @return #POINT_VEC3 -function POINT_VEC3:AddY( y ) - self.y = self.y + y - return self -end - ---- Add to the z coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number z The z coordinate value to add to the current z coodinate. --- @return #POINT_VEC3 -function POINT_VEC3:AddZ( z ) - self.z = self.z +z - return self -end - ---- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance OuterRadius --- @param Dcs.DCSTypes#Distance InnerRadius --- @return Dcs.DCSTypes#Vec2 Vec2 -function POINT_VEC3:GetRandomVec2InRadius( OuterRadius, InnerRadius ) - self:F2( { OuterRadius, InnerRadius } ) - - local Theta = 2 * math.pi * math.random() - local Radials = math.random() + math.random() - if Radials > 1 then - Radials = 2 - Radials - end - - local RadialMultiplier - if InnerRadius and InnerRadius <= OuterRadius then - RadialMultiplier = ( OuterRadius - InnerRadius ) * Radials + InnerRadius - else - RadialMultiplier = OuterRadius * Radials - end - - local RandomVec2 - if OuterRadius > 0 then - RandomVec2 = { x = math.cos( Theta ) * RadialMultiplier + self:GetX(), y = math.sin( Theta ) * RadialMultiplier + self:GetZ() } - else - RandomVec2 = { x = self:GetX(), y = self:GetZ() } - end - - return RandomVec2 -end - ---- Return a random POINT_VEC2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance OuterRadius --- @param Dcs.DCSTypes#Distance InnerRadius --- @return #POINT_VEC2 -function POINT_VEC3:GetRandomPointVec2InRadius( OuterRadius, InnerRadius ) - self:F2( { OuterRadius, InnerRadius } ) - - return POINT_VEC2:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) ) -end - ---- Return a random Vec3 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance OuterRadius --- @param Dcs.DCSTypes#Distance InnerRadius --- @return Dcs.DCSTypes#Vec3 Vec3 -function POINT_VEC3:GetRandomVec3InRadius( OuterRadius, InnerRadius ) - - local RandomVec2 = self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) - local y = self:GetY() + math.random( InnerRadius, OuterRadius ) - local RandomVec3 = { x = RandomVec2.x, y = y, z = RandomVec2.y } - - return RandomVec3 -end - ---- Return a random POINT_VEC3 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance OuterRadius --- @param Dcs.DCSTypes#Distance InnerRadius --- @return #POINT_VEC3 -function POINT_VEC3:GetRandomPointVec3InRadius( OuterRadius, InnerRadius ) - - return POINT_VEC3:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) ) -end - - ---- Return a direction vector Vec3 from POINT_VEC3 to the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3. --- @return Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. -function POINT_VEC3:GetDirectionVec3( TargetPointVec3 ) - return { x = TargetPointVec3:GetX() - self:GetX(), y = TargetPointVec3:GetY() - self:GetY(), z = TargetPointVec3:GetZ() - self:GetZ() } -end - ---- Get a correction in radians of the real magnetic north of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #number CorrectionRadians The correction in radians. -function POINT_VEC3:GetNorthCorrectionRadians() - local TargetVec3 = self:GetVec3() - local lat, lon = coord.LOtoLL(TargetVec3) - local north_posit = coord.LLtoLO(lat + 1, lon) - return math.atan2( north_posit.z - TargetVec3.z, north_posit.x - TargetVec3.x ) -end - - ---- Return a direction in radians from the POINT_VEC3 using a direction vector in Vec3 format. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. --- @return #number DirectionRadians The direction in radians. -function POINT_VEC3:GetDirectionRadians( DirectionVec3 ) - local DirectionRadians = math.atan2( DirectionVec3.z, DirectionVec3.x ) - --DirectionRadians = DirectionRadians + self:GetNorthCorrectionRadians() - if DirectionRadians < 0 then - DirectionRadians = DirectionRadians + 2 * math.pi -- put dir in range of 0 to 2*pi ( the full circle ) - end - return DirectionRadians -end - ---- Return the 2D distance in meters between the target POINT_VEC3 and the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3. --- @return Dcs.DCSTypes#Distance Distance The distance in meters. -function POINT_VEC3:Get2DDistance( TargetPointVec3 ) - local TargetVec3 = TargetPointVec3:GetVec3() - local SourceVec3 = self:GetVec3() - return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5 -end - ---- Return the 3D distance in meters between the target POINT_VEC3 and the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3. --- @return Dcs.DCSTypes#Distance Distance The distance in meters. -function POINT_VEC3:Get3DDistance( TargetPointVec3 ) - local TargetVec3 = TargetPointVec3:GetVec3() - local SourceVec3 = self:GetVec3() - return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5 -end - ---- Provides a Bearing / Range string --- @param #POINT_VEC3 self --- @param #number AngleRadians The angle in randians --- @param #number Distance The distance --- @return #string The BR Text -function POINT_VEC3:ToStringBR( AngleRadians, Distance ) - - AngleRadians = UTILS.Round( UTILS.ToDegree( AngleRadians ), 0 ) - if self:IsMetric() then - Distance = UTILS.Round( Distance / 1000, 2 ) - else - Distance = UTILS.Round( UTILS.MetersToNM( Distance ), 2 ) - end - - local s = string.format( '%03d', AngleRadians ) .. ' for ' .. Distance - - s = s .. self:GetAltitudeText() -- When the POINT is a VEC2, there will be no altitude shown. - - return s -end - ---- Provides a Bearing / Range string --- @param #POINT_VEC3 self --- @param #number AngleRadians The angle in randians --- @param #number Distance The distance --- @return #string The BR Text -function POINT_VEC3:ToStringLL( acc, DMS ) - - acc = acc or 3 - local lat, lon = coord.LOtoLL( self:GetVec3() ) - return UTILS.tostringLL(lat, lon, acc, DMS) -end - ---- Return the altitude text of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #string Altitude text. -function POINT_VEC3:GetAltitudeText() - if self:IsMetric() then - return ' at ' .. UTILS.Round( self:GetY(), 0 ) - else - return ' at ' .. UTILS.Round( UTILS.MetersToFeet( self:GetY() ), 0 ) - end -end - ---- Return a BR string from a POINT_VEC3 to the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3. --- @return #string The BR text. -function POINT_VEC3:GetBRText( TargetPointVec3 ) - local DirectionVec3 = self:GetDirectionVec3( TargetPointVec3 ) - local AngleRadians = self:GetDirectionRadians( DirectionVec3 ) - local Distance = self:Get2DDistance( TargetPointVec3 ) - return self:ToStringBR( AngleRadians, Distance ) -end - ---- Sets the POINT_VEC3 metric or NM. --- @param #POINT_VEC3 self --- @param #boolean Metric true means metric, false means NM. -function POINT_VEC3:SetMetric( Metric ) - self.Metric = Metric -end - ---- Gets if the POINT_VEC3 is metric or NM. --- @param #POINT_VEC3 self --- @return #boolean Metric true means metric, false means NM. -function POINT_VEC3:IsMetric() - return self.Metric -end - ---- Add a Distance in meters from the POINT_VEC3 horizontal plane, with the given angle, and calculate the new POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters. --- @param Dcs.DCSTypes#Angle Angle The Angle in degrees. --- @return #POINT_VEC3 The new calculated POINT_VEC3. -function POINT_VEC3:Translate( Distance, Angle ) - local SX = self:GetX() - local SZ = self:GetZ() - local Radians = Angle / 180 * math.pi - local TX = Distance * math.cos( Radians ) + SX - local TZ = Distance * math.sin( Radians ) + SZ - - return POINT_VEC3:New( TX, self:GetY(), TZ ) -end - - - ---- Build an air type route point. --- @param #POINT_VEC3 self --- @param #POINT_VEC3.RoutePointAltType AltType The altitude type. --- @param #POINT_VEC3.RoutePointType Type The route point type. --- @param #POINT_VEC3.RoutePointAction Action The route point action. --- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. --- @param #boolean SpeedLocked true means the speed is locked. --- @return #table The route point. -function POINT_VEC3:RoutePointAir( AltType, Type, Action, Speed, SpeedLocked ) - self:F2( { AltType, Type, Action, Speed, SpeedLocked } ) - - local RoutePoint = {} - RoutePoint.x = self.x - RoutePoint.y = self.z - RoutePoint.alt = self.y - RoutePoint.alt_type = AltType - - RoutePoint.type = Type - RoutePoint.action = Action - - RoutePoint.speed = Speed / 3.6 - RoutePoint.speed_locked = true - --- ["task"] = --- { --- ["id"] = "ComboTask", --- ["params"] = --- { --- ["tasks"] = --- { --- }, -- end of ["tasks"] --- }, -- end of ["params"] --- }, -- end of ["task"] - - - RoutePoint.task = {} - RoutePoint.task.id = "ComboTask" - RoutePoint.task.params = {} - RoutePoint.task.params.tasks = {} + --- The POINT_VEC3 class + -- @type POINT_VEC3 + -- @field #number x The x coordinate in 3D space. + -- @field #number y The y coordinate in 3D space. + -- @field #number z The z coordiante in 3D space. + -- @field Utilities.Utils#SMOKECOLOR SmokeColor + -- @field Utilities.Utils#FLARECOLOR FlareColor + -- @field #POINT_VEC3.RoutePointAltType RoutePointAltType + -- @field #POINT_VEC3.RoutePointType RoutePointType + -- @field #POINT_VEC3.RoutePointAction RoutePointAction + -- @extends Core.Point#COORDINATE - return RoutePoint -end + --- # POINT_VEC3 class, extends @{Point#COORDINATE} + -- + -- POINT_VEC3 defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. + -- + -- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts. + -- In order to keep the credibility of the the author, + -- I want to emphasize that the formulas embedded in the MIST framework were created by Grimes or previous authors, + -- who you can find on the Eagle Dynamics Forums. + -- + -- + -- ## POINT_VEC3 constructor + -- + -- A new POINT_VEC3 object can be created with: + -- + -- * @{#POINT_VEC3.New}(): a 3D point. + -- * @{#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}. + -- + -- + -- ## Manupulate the X, Y, Z coordinates of the POINT_VEC3 + -- + -- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate. + -- Methods exist to manupulate these coordinates. + -- + -- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively. + -- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value. + -- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}() + -- to add or substract a value from the current respective axis value. + -- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example: + -- + -- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3() + -- + -- + -- ## 3D calculation methods + -- + -- Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method: + -- + -- + -- ## Point Randomization + -- + -- Various methods exist to calculate random locations around a given 3D point. + -- + -- * @{#POINT_VEC3.GetRandomPointVec3InRadius}(): Provides a random 3D point around the current 3D point, in the given inner to outer band. + -- + -- + -- @field #POINT_VEC3 + POINT_VEC3 = { + ClassName = "POINT_VEC3", + Metric = true, + RoutePointAltType = { + BARO = "BARO", + }, + RoutePointType = { + TakeOffParking = "TakeOffParking", + TurningPoint = "Turning Point", + }, + RoutePointAction = { + FromParkingArea = "From Parking Area", + TurningPoint = "Turning Point", + }, + } ---- Build an ground type route point. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Speed Speed Speed in km/h. --- @param #POINT_VEC3.RoutePointAction Formation The route point Formation. --- @return #table The route point. -function POINT_VEC3:RoutePointGround( Speed, Formation ) - self:F2( { Formation, Speed } ) + --- RoutePoint AltTypes + -- @type POINT_VEC3.RoutePointAltType + -- @field BARO "BARO" - local RoutePoint = {} - RoutePoint.x = self.x - RoutePoint.y = self.z - - RoutePoint.action = Formation or "" + --- RoutePoint Types + -- @type POINT_VEC3.RoutePointType + -- @field TakeOffParking "TakeOffParking" + -- @field TurningPoint "Turning Point" + + --- RoutePoint Actions + -- @type POINT_VEC3.RoutePointAction + -- @field FromParkingArea "From Parking Area" + -- @field TurningPoint "Turning Point" + + -- Constructor. + + --- Create a new POINT_VEC3 object. + -- @param #POINT_VEC3 self + -- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North. + -- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing Upwards. + -- @param Dcs.DCSTypes#Distance z The z coordinate of the Vec3 point, pointing to the Right. + -- @return Core.Point#POINT_VEC3 + function POINT_VEC3:New( x, y, z ) + + local self = BASE:Inherit( self, COORDINATE:New( x, y, z ) ) -- Core.Point#POINT_VEC3 + self:F2( self ) + return self + end - RoutePoint.speed = Speed / 3.6 - RoutePoint.speed_locked = true + --- Create a new POINT_VEC3 object from Vec2 coordinates. + -- @param #POINT_VEC3 self + -- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. + -- @param Dcs.DCSTypes#Distance LandHeightAdd (optional) Add a landheight. + -- @return Core.Point#POINT_VEC3 self + function POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd ) + + local self = BASE:Inherit( self, COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) ) -- Core.Point#POINT_VEC3 + self:F2( self ) + + return self + end + + + --- Create a new POINT_VEC3 object from Vec3 coordinates. + -- @param #POINT_VEC3 self + -- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point. + -- @return Core.Point#POINT_VEC3 self + function POINT_VEC3:NewFromVec3( Vec3 ) + + local self = BASE:Inherit( self, COORDINATE:NewFromVec3( Vec3 ) ) -- Core.Point#POINT_VEC3 + self:F2( self ) --- ["task"] = --- { --- ["id"] = "ComboTask", --- ["params"] = --- { --- ["tasks"] = --- { --- }, -- end of ["tasks"] --- }, -- end of ["params"] --- }, -- end of ["task"] + return self + end - RoutePoint.task = {} - RoutePoint.task.id = "ComboTask" - RoutePoint.task.params = {} - RoutePoint.task.params.tasks = {} - - - return RoutePoint -end ---- Creates an explosion at the point of a certain intensity. --- @param #POINT_VEC3 self --- @param #number ExplosionIntensity -function POINT_VEC3:Explosion( ExplosionIntensity ) - self:F2( { ExplosionIntensity } ) - trigger.action.explosion( self:GetVec3(), ExplosionIntensity ) -end + --- Return the x coordinate of the POINT_VEC3. + -- @param #POINT_VEC3 self + -- @return #number The x coodinate. + function POINT_VEC3:GetX() + return self.x + end ---- Creates an illumination bomb at the point. --- @param #POINT_VEC3 self -function POINT_VEC3:IlluminationBomb() - self:F2() - trigger.action.illuminationBomb( self:GetVec3() ) -end + --- Return the y coordinate of the POINT_VEC3. + -- @param #POINT_VEC3 self + -- @return #number The y coodinate. + function POINT_VEC3:GetY() + return self.y + end + --- Return the z coordinate of the POINT_VEC3. + -- @param #POINT_VEC3 self + -- @return #number The z coodinate. + function POINT_VEC3:GetZ() + return self.z + end ---- Smokes the point in a color. --- @param #POINT_VEC3 self --- @param Utilities.Utils#SMOKECOLOR SmokeColor -function POINT_VEC3:Smoke( SmokeColor ) - self:F2( { SmokeColor } ) - trigger.action.smoke( self:GetVec3(), SmokeColor ) -end + --- Set the x coordinate of the POINT_VEC3. + -- @param #POINT_VEC3 self + -- @param #number x The x coordinate. + -- @return #POINT_VEC3 + function POINT_VEC3:SetX( x ) + self.x = x + return self + end ---- Smoke the POINT_VEC3 Green. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeGreen() - self:F2() - self:Smoke( SMOKECOLOR.Green ) -end + --- Set the y coordinate of the POINT_VEC3. + -- @param #POINT_VEC3 self + -- @param #number y The y coordinate. + -- @return #POINT_VEC3 + function POINT_VEC3:SetY( y ) + self.y = y + return self + end ---- Smoke the POINT_VEC3 Red. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeRed() - self:F2() - self:Smoke( SMOKECOLOR.Red ) -end + --- Set the z coordinate of the POINT_VEC3. + -- @param #POINT_VEC3 self + -- @param #number z The z coordinate. + -- @return #POINT_VEC3 + function POINT_VEC3:SetZ( z ) + self.z = z + return self + end ---- Smoke the POINT_VEC3 White. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeWhite() - self:F2() - self:Smoke( SMOKECOLOR.White ) -end + --- Add to the x coordinate of the POINT_VEC3. + -- @param #POINT_VEC3 self + -- @param #number x The x coordinate value to add to the current x coodinate. + -- @return #POINT_VEC3 + function POINT_VEC3:AddX( x ) + self.x = self.x + x + return self + end ---- Smoke the POINT_VEC3 Orange. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeOrange() - self:F2() - self:Smoke( SMOKECOLOR.Orange ) -end + --- Add to the y coordinate of the POINT_VEC3. + -- @param #POINT_VEC3 self + -- @param #number y The y coordinate value to add to the current y coodinate. + -- @return #POINT_VEC3 + function POINT_VEC3:AddY( y ) + self.y = self.y + y + return self + end ---- Smoke the POINT_VEC3 Blue. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeBlue() - self:F2() - self:Smoke( SMOKECOLOR.Blue ) -end + --- Add to the z coordinate of the POINT_VEC3. + -- @param #POINT_VEC3 self + -- @param #number z The z coordinate value to add to the current z coodinate. + -- @return #POINT_VEC3 + function POINT_VEC3:AddZ( z ) + self.z = self.z +z + return self + end ---- Flares the point in a color. --- @param #POINT_VEC3 self --- @param Utilities.Utils#FLARECOLOR FlareColor --- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -function POINT_VEC3:Flare( FlareColor, Azimuth ) - self:F2( { FlareColor } ) - trigger.action.signalFlare( self:GetVec3(), FlareColor, Azimuth and Azimuth or 0 ) -end + --- Return a random POINT_VEC3 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. + -- @param #POINT_VEC3 self + -- @param Dcs.DCSTypes#Distance OuterRadius + -- @param Dcs.DCSTypes#Distance InnerRadius + -- @return #POINT_VEC3 + function POINT_VEC3:GetRandomPointVec3InRadius( OuterRadius, InnerRadius ) ---- Flare the POINT_VEC3 White. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -function POINT_VEC3:FlareWhite( Azimuth ) - self:F2( Azimuth ) - self:Flare( FLARECOLOR.White, Azimuth ) -end - ---- Flare the POINT_VEC3 Yellow. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -function POINT_VEC3:FlareYellow( Azimuth ) - self:F2( Azimuth ) - self:Flare( FLARECOLOR.Yellow, Azimuth ) -end - ---- Flare the POINT_VEC3 Green. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -function POINT_VEC3:FlareGreen( Azimuth ) - self:F2( Azimuth ) - self:Flare( FLARECOLOR.Green, Azimuth ) -end - ---- Flare the POINT_VEC3 Red. --- @param #POINT_VEC3 self -function POINT_VEC3:FlareRed( Azimuth ) - self:F2( Azimuth ) - self:Flare( FLARECOLOR.Red, Azimuth ) -end + return POINT_VEC3:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) ) + end end do -- POINT_VEC2 - - ---- POINT_VEC2 constructor. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North. --- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing to the Right. --- @param Dcs.DCSTypes#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height. --- @return Core.Point#POINT_VEC2 -function POINT_VEC2:New( x, y, LandHeightAdd ) - - local LandHeight = land.getHeight( { ["x"] = x, ["y"] = y } ) + --- @type POINT_VEC2 + -- @field Dcs.DCSTypes#Distance x The x coordinate in meters. + -- @field Dcs.DCSTypes#Distance y the y coordinate in meters. + -- @extends Core.Point#COORDINATE - LandHeightAdd = LandHeightAdd or 0 - LandHeight = LandHeight + LandHeightAdd + --- # POINT_VEC2 class, extends @{Point#COORDINATE} + -- + -- The @{Point#POINT_VEC2} class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified. + -- + -- ## POINT_VEC2 constructor + -- + -- A new POINT_VEC2 instance can be created with: + -- + -- * @{Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter. + -- * @{Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}. + -- + -- ## Manupulate the X, Altitude, Y coordinates of the 2D point + -- + -- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate. + -- Methods exist to manupulate these coordinates. + -- + -- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively. + -- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value. + -- The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods @{#POINT_VEC2.GetLat}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetLon}() respectively. + -- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}() + -- to add or substract a value from the current respective axis value. + -- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example: + -- + -- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2() + -- + -- @field #POINT_VEC2 + POINT_VEC2 = { + ClassName = "POINT_VEC2", + } - self = BASE:Inherit( self, POINT_VEC3:New( x, LandHeight, y ) ) - self:F2( self ) - - return self -end - ---- Create a new POINT_VEC2 object from Vec2 coordinates. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. --- @return Core.Point#POINT_VEC2 self -function POINT_VEC2:NewFromVec2( Vec2, LandHeightAdd ) - - local LandHeight = land.getHeight( Vec2 ) - - LandHeightAdd = LandHeightAdd or 0 - LandHeight = LandHeight + LandHeightAdd - - self = BASE:Inherit( self, POINT_VEC3:New( Vec2.x, LandHeight, Vec2.y ) ) - self:F2( self ) - - return self -end - ---- Create a new POINT_VEC2 object from Vec3 coordinates. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point. --- @return Core.Point#POINT_VEC2 self -function POINT_VEC2:NewFromVec3( Vec3 ) - - local self = BASE:Inherit( self, BASE:New() ) - local Vec2 = { x = Vec3.x, y = Vec3.z } - - local LandHeight = land.getHeight( Vec2 ) - - self = BASE:Inherit( self, POINT_VEC3:New( Vec2.x, LandHeight, Vec2.y ) ) - self:F2( self ) - - return self -end - ---- Return the x coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @return #number The x coodinate. -function POINT_VEC2:GetX() - return self.x -end - ---- Return the y coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @return #number The y coodinate. -function POINT_VEC2:GetY() - return self.z -end - ---- Return the altitude (height) of the land at the POINT_VEC2. --- @param #POINT_VEC2 self --- @return #number The land altitude. -function POINT_VEC2:GetAlt() - return land.getHeight( { x = self.x, y = self.z } ) -end - ---- Return Return the Lat(itude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.x). --- @param #POINT_VEC2 self --- @return #number The x coodinate. -function POINT_VEC2:GetLat() - return self.x -end - ---- Return the Lon(gitude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.z). --- @param #POINT_VEC2 self --- @return #number The y coodinate. -function POINT_VEC2:GetLon() - return self.z -end - ---- Set the x coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number x The x coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:SetX( x ) - self.x = x - return self -end - ---- Set the y coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number y The y coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:SetY( y ) - self.z = y - return self -end - ---- Set the Lat(itude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.x). --- @param #POINT_VEC2 self --- @param #number x The x coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:SetLat( x ) - self.x = x - return self -end - ---- Set the altitude of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number Altitude The land altitude. If nothing (nil) is given, then the current land altitude is set. --- @return #POINT_VEC2 -function POINT_VEC2:SetAlt( Altitude ) - self.y = Altitude or land.getHeight( { x = self.x, y = self.z } ) - return self -end - ---- Set the Lon(gitude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.z). --- @param #POINT_VEC2 self --- @param #number y The y coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:SetLon( z ) - self.z = z - return self -end - ---- Add to the x coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number x The x coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:AddX( x ) - self.x = self.x + x - return self -end - ---- Add to the y coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number y The y coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:AddY( y ) - self.z = self.z + y - return self -end - ---- Add to the current land height an altitude. --- @param #POINT_VEC2 self --- @param #number Altitude The Altitude to add. If nothing (nil) is given, then the current land altitude is set. --- @return #POINT_VEC2 -function POINT_VEC2:AddAlt( Altitude ) - self.y = land.getHeight( { x = self.x, y = self.z } ) + Altitude or 0 - return self -end + --- POINT_VEC2 constructor. + -- @param #POINT_VEC2 self + -- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North. + -- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing to the Right. + -- @param Dcs.DCSTypes#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height. + -- @return Core.Point#POINT_VEC2 + function POINT_VEC2:New( x, y, LandHeightAdd ) ---- Calculate the distance from a reference @{#POINT_VEC2}. --- @param #POINT_VEC2 self --- @param #POINT_VEC2 PointVec2Reference The reference @{#POINT_VEC2}. --- @return Dcs.DCSTypes#Distance The distance from the reference @{#POINT_VEC2} in meters. -function POINT_VEC2:DistanceFromPointVec2( PointVec2Reference ) - self:F2( PointVec2Reference ) - - local Distance = ( ( PointVec2Reference:GetX() - self:GetX() ) ^ 2 + ( PointVec2Reference:GetY() - self:GetY() ) ^2 ) ^0.5 - - self:T2( Distance ) - return Distance -end + local LandHeight = land.getHeight( { ["x"] = x, ["y"] = y } ) ---- Calculate the distance from a reference @{DCSTypes#Vec2}. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Vec2 Vec2Reference The reference @{DCSTypes#Vec2}. --- @return Dcs.DCSTypes#Distance The distance from the reference @{DCSTypes#Vec2} in meters. -function POINT_VEC2:DistanceFromVec2( Vec2Reference ) - self:F2( Vec2Reference ) - - local Distance = ( ( Vec2Reference.x - self:GetX() ) ^ 2 + ( Vec2Reference.y - self:GetY() ) ^2 ) ^0.5 - - self:T2( Distance ) - return Distance -end + LandHeightAdd = LandHeightAdd or 0 + LandHeight = LandHeight + LandHeightAdd + + local self = BASE:Inherit( self, COORDINATE:New( x, LandHeight, y ) ) -- Core.Point#POINT_VEC2 + self:F2( self ) + + return self + end + + --- Create a new POINT_VEC2 object from Vec2 coordinates. + -- @param #POINT_VEC2 self + -- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. + -- @return Core.Point#POINT_VEC2 self + function POINT_VEC2:NewFromVec2( Vec2, LandHeightAdd ) + + local LandHeight = land.getHeight( Vec2 ) + + LandHeightAdd = LandHeightAdd or 0 + LandHeight = LandHeight + LandHeightAdd + + local self = BASE:Inherit( self, COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) ) -- #POINT_VEC2 + self:F2( self ) + + return self + end + + --- Create a new POINT_VEC2 object from Vec3 coordinates. + -- @param #POINT_VEC2 self + -- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point. + -- @return Core.Point#POINT_VEC2 self + function POINT_VEC2:NewFromVec3( Vec3 ) + + local self = BASE:Inherit( self, COORDINATE:NewFromVec3( Vec3 ) ) -- #POINT_VEC2 + self:F2( self ) + + return self + end + + --- Return the x coordinate of the POINT_VEC2. + -- @param #POINT_VEC2 self + -- @return #number The x coodinate. + function POINT_VEC2:GetX() + return self.x + end + + --- Return the y coordinate of the POINT_VEC2. + -- @param #POINT_VEC2 self + -- @return #number The y coodinate. + function POINT_VEC2:GetY() + return self.z + end + + --- Set the x coordinate of the POINT_VEC2. + -- @param #POINT_VEC2 self + -- @param #number x The x coordinate. + -- @return #POINT_VEC2 + function POINT_VEC2:SetX( x ) + self.x = x + return self + end + + --- Set the y coordinate of the POINT_VEC2. + -- @param #POINT_VEC2 self + -- @param #number y The y coordinate. + -- @return #POINT_VEC2 + function POINT_VEC2:SetY( y ) + self.z = y + return self + end + + --- Return Return the Lat(itude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.x). + -- @param #POINT_VEC2 self + -- @return #number The x coodinate. + function POINT_VEC2:GetLat() + return self.x + end + + --- Set the Lat(itude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.x). + -- @param #POINT_VEC2 self + -- @param #number x The x coordinate. + -- @return #POINT_VEC2 + function POINT_VEC2:SetLat( x ) + self.x = x + return self + end + + --- Return the Lon(gitude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.z). + -- @param #POINT_VEC2 self + -- @return #number The y coodinate. + function POINT_VEC2:GetLon() + return self.z + end + + --- Set the Lon(gitude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.z). + -- @param #POINT_VEC2 self + -- @param #number y The y coordinate. + -- @return #POINT_VEC2 + function POINT_VEC2:SetLon( z ) + self.z = z + return self + end + + --- Return the altitude (height) of the land at the POINT_VEC2. + -- @param #POINT_VEC2 self + -- @return #number The land altitude. + function POINT_VEC2:GetAlt() + return self.y ~= 0 or land.getHeight( { x = self.x, y = self.z } ) + end + + --- Set the altitude of the POINT_VEC2. + -- @param #POINT_VEC2 self + -- @param #number Altitude The land altitude. If nothing (nil) is given, then the current land altitude is set. + -- @return #POINT_VEC2 + function POINT_VEC2:SetAlt( Altitude ) + self.y = Altitude or land.getHeight( { x = self.x, y = self.z } ) + return self + end + + --- Add to the x coordinate of the POINT_VEC2. + -- @param #POINT_VEC2 self + -- @param #number x The x coordinate. + -- @return #POINT_VEC2 + function POINT_VEC2:AddX( x ) + self.x = self.x + x + return self + end + + --- Add to the y coordinate of the POINT_VEC2. + -- @param #POINT_VEC2 self + -- @param #number y The y coordinate. + -- @return #POINT_VEC2 + function POINT_VEC2:AddY( y ) + self.z = self.z + y + return self + end + + --- Add to the current land height an altitude. + -- @param #POINT_VEC2 self + -- @param #number Altitude The Altitude to add. If nothing (nil) is given, then the current land altitude is set. + -- @return #POINT_VEC2 + function POINT_VEC2:AddAlt( Altitude ) + self.y = land.getHeight( { x = self.x, y = self.z } ) + Altitude or 0 + return self + end ---- Return no text for the altitude of the POINT_VEC2. --- @param #POINT_VEC2 self --- @return #string Empty string. -function POINT_VEC2:GetAltitudeText() - return '' -end + --- Return a random POINT_VEC2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC2. + -- @param #POINT_VEC2 self + -- @param Dcs.DCSTypes#Distance OuterRadius + -- @param Dcs.DCSTypes#Distance InnerRadius + -- @return #POINT_VEC2 + function POINT_VEC2:GetRandomPointVec2InRadius( OuterRadius, InnerRadius ) + self:F2( { OuterRadius, InnerRadius } ) ---- Add a Distance in meters from the POINT_VEC2 orthonormal plane, with the given angle, and calculate the new POINT_VEC2. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters. --- @param Dcs.DCSTypes#Angle Angle The Angle in degrees. --- @return #POINT_VEC2 The new calculated POINT_VEC2. -function POINT_VEC2:Translate( Distance, Angle ) - local SX = self:GetX() - local SY = self:GetY() - local Radians = Angle / 180 * math.pi - local TX = Distance * math.cos( Radians ) + SX - local TY = Distance * math.sin( Radians ) + SY - - return POINT_VEC2:New( TX, TY ) -end + return POINT_VEC2:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) ) + end + + -- TODO: Check this to replace + --- Calculate the distance from a reference @{#POINT_VEC2}. + -- @param #POINT_VEC2 self + -- @param #POINT_VEC2 PointVec2Reference The reference @{#POINT_VEC2}. + -- @return Dcs.DCSTypes#Distance The distance from the reference @{#POINT_VEC2} in meters. + function POINT_VEC2:DistanceFromPointVec2( PointVec2Reference ) + self:F2( PointVec2Reference ) + + local Distance = ( ( PointVec2Reference.x - self.x ) ^ 2 + ( PointVec2Reference.z - self.z ) ^2 ) ^ 0.5 + + self:T2( Distance ) + return Distance + end end diff --git a/Moose Development/Moose/Core/Radio.lua b/Moose Development/Moose/Core/Radio.lua index 38735de28..1d41d25b7 100644 --- a/Moose Development/Moose/Core/Radio.lua +++ b/Moose Development/Moose/Core/Radio.lua @@ -1,9 +1,11 @@ ---- **Core** - The RADIO class is responsible for **transmitting radio communications**. --- --- --- bitmap +--- **Core** -- The RADIO Module is responsible for everything that is related to radio transmission and you can hear in DCS, be it TACAN beacons, Radio transmissions... +-- +-- ![Banner Image](..\Presentations\RADIO\Dia1.JPG) -- -- === -- +-- The Radio contains 2 classes : RADIO and BEACON +-- -- What are radio communications in DCS ? -- -- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM), @@ -23,37 +25,39 @@ -- -- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft, -- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below). --- If a FC3 airacraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. +-- If a FC3 airacraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. The same is true for TACAN beacons. If your aircaft isn't compatible, +-- you won't hear/be able to use the TACAN beacon informations. -- -- === -- --- ### Authors: Hugues "Grey_Echo" Bousquet +-- ### Author: Hugues "Grey_Echo" Bousquet -- -- @module Radio ---- # 1) RADIO class, extends @{Base#BASE} + +--- # RADIO class, extends @{Base#BASE} -- --- ## 1.1) RADIO usage +-- ## RADIO usage -- -- There are 3 steps to a successful radio transmission. -- --- * First, you need to **"add" a @{#RADIO} object** to your @{Positionable#POSITIONABLE}. This is done using the @{Positionable#POSITIONABLE.GetRadio}() function, +-- * First, you need to **"add a @{#RADIO} object** to your @{Positionable#POSITIONABLE}. This is done using the @{Positionable#POSITIONABLE.GetRadio}() function, -- * Then, you will **set the relevant parameters** to the transmission (see below), --- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{Positionable#POSITIONABLE.Broadcast}() function. +-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{RADIO.Broadcast}() function. -- -- Methods to set relevant parameters for both a @{Unit#UNIT} or a @{Group#GROUP} or any other @{Positionable#POSITIONABLE} -- -- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"), --- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission, +-- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission. -- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission. +-- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped. If you need your transmission to be looped, you might need a @{#BEACON} instead... -- -- Additional Methods to set relevant parameters if the transmiter is a @{Unit#UNIT} or a @{Group#GROUP} -- --- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped, -- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration, -- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call -- --- Additional Methods to set relevant parameters if the transmiter is any other @{Wrapper.Positionable#POSITIONABLE} +-- Additional Methods to set relevant parameters if the transmiter is any other @{Positionable#POSITIONABLE} -- -- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts -- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call @@ -68,14 +72,14 @@ -- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission. -- -- @type RADIO --- @field Wrapper.Positionable#POSITIONABLE Positionable The transmiter +-- @field Positionable#POSITIONABLE Positionable The transmiter -- @field #string FileName Name of the sound file -- @field #number Frequency Frequency of the transmission in Hz -- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM) -- @field #string Subtitle Subtitle of the transmission -- @field #number SubtitleDuration Duration of the Subtitle in seconds -- @field #number Power Power of the antenna is Watts --- @field #boolean Loop +-- @field #boolean Loop (default true) -- @extends Core.Base#BASE RADIO = { ClassName = "RADIO", @@ -85,19 +89,19 @@ RADIO = { Subtitle = "", SubtitleDuration = 0, Power = 100, - Loop = 0, + Loop = true, } --- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast +-- If you want to create a RADIO, you probably should use @{Positionable#POSITIONABLE.GetRadio}() instead -- @param #RADIO self -- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. -- @return #RADIO Radio -- @return #nil If Positionable is invalid --- @usage --- -- If you want to create a RADIO, you probably should use @{Positionable#POSITIONABLE.GetRadio}() instead function RADIO:New(Positionable) local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO + self.Loop = true -- default Loop to true (not sure the above RADIO definition actually is working) self:F(Positionable) if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid @@ -203,12 +207,18 @@ function RADIO:SetLoop(Loop) end --- Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration +-- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration -- @param #RADIO self -- @param #string Subtitle -- @param #number SubtitleDuration in s -- @return #RADIO self -- @usage --- -- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration +-- -- create the broadcaster and attaches it a RADIO +-- local MyUnit = UNIT:FindByName("MyUnit") +-- local MyUnitRadio = MyUnit:GetRadio() +-- +-- -- add a subtitle for the next transmission, which will be up for 10s +-- MyUnitRadio:SetSubtitle("My Subtitle, 10) function RADIO:SetSubtitle(Subtitle, SubtitleDuration) self:F2({Subtitle, SubtitleDuration}) if type(Subtitle) == "string" then @@ -228,29 +238,32 @@ function RADIO:SetSubtitle(Subtitle, SubtitleDuration) end --- Create a new transmission, that is to say, populate the RADIO with relevant data +-- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP, +-- but it will work with a UNIT or a GROUP anyway. +-- Only the #RADIO and the Filename are mandatory -- @param #RADIO self -- @param #string FileName -- @param #number Frequency in MHz -- @param #number Modulation either radio.modulation.AM or radio.modulation.FM -- @param #number Power in W -- @return #RADIO self --- @usage --- -- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP, --- but it will work with a UNIT or a GROUP anyway --- -- Only the RADIO and the Filename are mandatory -function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power) +function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power, Loop) self:F({FileName, Frequency, Modulation, Power}) self:SetFileName(FileName) if Frequency then self:SetFrequency(Frequency) end if Modulation then self:SetModulation(Modulation) end if Power then self:SetPower(Power) end + if Loop then self:SetLoop(Loop) end return self end --- Create a new transmission, that is to say, populate the RADIO with relevant data +-- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP, +-- but it will work for any @{Positionable#POSITIONABLE}. +-- Only the RADIO and the Filename are mandatory. -- @param #RADIO self -- @param #string FileName -- @param #string Subtitle @@ -259,10 +272,6 @@ end -- @param #number Modulation either radio.modulation.AM or radio.modulation.FM -- @param #boolean Loop -- @return #RADIO self --- @usage --- -- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP, --- but it will work for any POSITIONABLE --- -- Only the RADIO and the Filename are mandatory function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop) self:F({FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop}) @@ -277,18 +286,18 @@ function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequen end --- Actually Broadcast the transmission +-- * The Radio has to be populated with the new transmission before broadcasting. +-- * Please use RADIO setters or either @{Radio#RADIO.NewGenericTransmission} or @{Radio#RADIO.NewUnitTransmission} +-- * This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE +-- * If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission() +-- * If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command +-- * If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored. +-- * If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration are ignored -- @param #RADIO self -- @return #RADIO self --- @usage --- -- The Radio has to be populated with the new transmission before broadcasting. --- -- Please use RADIO setters or either @{Radio#RADIO.NewGenericTransmission} or @{Radio#RADIO.NewUnitTransmission} --- -- This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE --- -- If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission() --- -- If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command --- -- If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored. --- -- If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration and Loop are ignored function RADIO:Broadcast() self:F() + -- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then self:T2("Broadcasting from a UNIT or a GROUP") @@ -303,18 +312,17 @@ function RADIO:Broadcast() }) else -- If the POSITIONABLE is anything else, we revert to the general singleton function + -- I need to give it a unique name, so that the transmission can be stopped later. I use the class ID self:T2("Broadcasting from a POSITIONABLE") - trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, false, self.Frequency, self.Power) + trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, self.Loop, self.Frequency, self.Power, tostring(self.ID)) end return self end --- Stops a transmission +-- This function is especially usefull to stop the broadcast of looped transmissions -- @param #RADIO self -- @return #RADIO self --- @usage --- -- Especially usefull to stop the broadcast of looped transmissions --- -- Only works with broadcasts from UNIT or GROUP function RADIO:StopBroadcast() self:F() -- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command @@ -324,7 +332,257 @@ function RADIO:StopBroadcast() params = {} }) else - self:E("This broadcast can't be stopped. It's not looped either, so please wait for the end of the sound file playback") + -- Else, we use the appropriate singleton funciton + trigger.action.stopRadioTransmission(tostring(self.ID)) end return self +end + + +--- # BEACON class, extends @{Base#BASE} +-- +-- After attaching a @{#BEACON} to your @{Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want. +-- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon. +-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is +-- attach to a cargo crate, for exemple. +-- +-- ## AA TACAN Beacon usage +-- +-- This beacon only works with airborne @{Unit#UNIT} or a @{Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon. +-- Use @#BEACON:StopAATACAN}() to stop it. +-- +-- ## General Purpose Radio Beacon usage +-- +-- This beacon will work with any @{Positionable#POSITIONABLE}, but **it won't follow the @{Positionable#POSITIONABLE}** ! This means that you should only use it with +-- @{Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon. +-- Use @{#BEACON:StopRadioBeacon}() to stop it. +-- +-- @type BEACON +-- @extends Core.Base#BASE +BEACON = { + ClassName = "BEACON", +} + +--- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.AATACAN} or @{#BEACON.Generic} +-- If you want to create a BEACON, you probably should use @{Positionable#POSITIONABLE.GetBeacon}() instead. +-- @param #BEACON self +-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. +-- @return #BEACON Beacon +-- @return #nil If Positionable is invalid +function BEACON:New(Positionable) + local self = BASE:Inherit(self, BASE:New()) + + self:F(Positionable) + + if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid + self.Positionable = Positionable + return self + end + + self:E({"The passed positionable is invalid, no BEACON created", Positionable}) + return nil +end + + +--- Converts a TACAN Channel/Mode couple into a frequency in Hz +-- @param #BEACON self +-- @param #number TACANChannel +-- @param #string TACANMode +-- @return #number Frequecy +-- @return #nil if parameters are invalid +function BEACON:_TACANToFrequency(TACANChannel, TACANMode) + self:F3({TACANChannel, TACANMode}) + + if type(TACANChannel) ~= "number" then + if TACANMode ~= "X" and TACANMode ~= "Y" then + return nil -- error in arguments + end + end + +-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137. +-- I have no idea what it does but it seems to work + local A = 1151 -- 'X', channel >= 64 + local B = 64 -- channel >= 64 + + if TACANChannel < 64 then + B = 1 + end + + if TACANMode == 'Y' then + A = 1025 + if TACANChannel < 64 then + A = 1088 + end + else -- 'X' + if TACANChannel < 64 then + A = 962 + end + end + + return (A + TACANChannel - B) * 1000000 +end + + +--- Activates a TACAN BEACON on an Aircraft. +-- @param #BEACON self +-- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels +-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon +-- @param #boolean Bearing Can the BEACON be homed on ? +-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever. +-- @return #BEACON self +-- @usage +-- -- Let's create a TACAN Beacon for a tanker +-- local myUnit = UNIT:FindByName("MyUnit") +-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon +-- +-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon +function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration) + self:F({TACANChannel, Message, Bearing, BeaconDuration}) + + local IsValid = true + + if not self.Positionable:IsAir() then + self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting", self.Positionable}) + IsValid = false + end + + local Frequency = self:_TACANToFrequency(TACANChannel, "Y") + if not Frequency then + self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"}) + IsValid = false + end + + -- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing + -- or 14 (TACAN_AA_MODE_Y) if it does not + local System + if Bearing then + System = 5 + else + System = 14 + end + + if IsValid then -- Starts the BEACON + self:T2({"AA TACAN BEACON started !"}) + self.Positionable:SetCommand({ + id = "ActivateBeacon", + params = { + type = 4, + system = System, + callsign = Message, + frequency = Frequency, + } + }) + + if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD + SCHEDULER:New( nil, + function() + self:StopAATACAN() + end, {}, BeaconDuration) + end + end + + return self +end + +--- Stops the AA TACAN BEACON +-- @param #BEACON self +-- @return #BEACON self +function BEACON:StopAATACAN() + self:F() + if not self.Positionable then + self:E({"Start the beacon first before stoping it !"}) + else + self.Positionable:SetCommand({ + id = 'DeactivateBeacon', + params = { + } + }) + end +end + + +--- Activates a general pupose Radio Beacon +-- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency. +-- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8. +-- They can home in on these specific frequencies : +-- * **Mi8** +-- * R-828 -> 20-60MHz +-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM +-- * ARK9 -> 150-1300KHz +-- * **Huey** +-- * AN/ARC-131 -> 30-76 Mhz FM +-- @param #BEACON self +-- @param #string FileName The name of the audio file +-- @param #number Frequency in MHz +-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM +-- @param #number Power in W +-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever. +-- @return #BEACON self +-- @usage +-- -- Let's create a beacon for a unit in distress. +-- -- Frequency will be 40MHz FM (home-able by a Huey's AN/ARC-131) +-- -- The beacon they use is battery-powered, and only lasts for 5 min +-- local UnitInDistress = UNIT:FindByName("Unit1") +-- local UnitBeacon = UnitInDistress:GetBeacon() +-- +-- -- Set the beacon and start it +-- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60) +function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration) + self:F({FileName, Frequency, Modulation, Power, BeaconDuration}) + local IsValid = false + + -- Check the filename + if type(FileName) == "string" then + if FileName:find(".ogg") or FileName:find(".wav") then + if not FileName:find("l10n/DEFAULT/") then + FileName = "l10n/DEFAULT/" .. FileName + end + IsValid = true + end + end + if not IsValid then + self:E({"File name invalid. Maybe something wrong with the extension ? ", FileName}) + end + + -- Check the Frequency + if type(Frequency) ~= "number" and IsValid then + self:E({"Frequency invalid. ", Frequency}) + IsValid = false + end + Frequency = Frequency * 1000000 -- Conversion to Hz + + -- Check the modulation + if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then --TODO Maybe make this future proof if ED decides to add an other modulation ? + self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation}) + IsValid = false + end + + -- Check the Power + if type(Power) ~= "number" and IsValid then + self:E({"Power is invalid. ", Power}) + IsValid = false + end + Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that + + if IsValid then + self:T2({"Activating Beacon on ", Frequency, Modulation}) + -- Note that this is looped. I have to give this transmission a unique name, I use the class ID + trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID)) + + if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD + SCHEDULER:New( nil, + function() + self:StopRadioBeacon() + end, {}, BeaconDuration) + end + end +end + +--- Stops the AA TACAN BEACON +-- @param #BEACON self +-- @return #BEACON self +function BEACON:StopRadioBeacon() + self:F() + -- The unique name of the transmission is the class ID + trigger.action.stopRadioTransmission(tostring(self.ID)) end \ No newline at end of file diff --git a/Moose Development/Moose/Core/Report.lua b/Moose Development/Moose/Core/Report.lua new file mode 100644 index 000000000..653e1f696 --- /dev/null +++ b/Moose Development/Moose/Core/Report.lua @@ -0,0 +1,86 @@ +--- The REPORT class +-- @type REPORT +-- @extends Core.Base#BASE +REPORT = { + ClassName = "REPORT", + Title = "", +} + +--- Create a new REPORT. +-- @param #REPORT self +-- @param #string Title +-- @return #REPORT +function REPORT:New( Title ) + + local self = BASE:Inherit( self, BASE:New() ) -- #REPORT + + self.Report = {} + + self:SetTitle( Title or "" ) + self:SetIndent( 3 ) + + return self +end + +--- Has the REPORT Text? +-- @param #REPORT self +-- @return #boolean +function REPORT:HasText() --R2.1 + + return #self.Report > 0 +end + + +--- Set indent of a REPORT. +-- @param #REPORT self +-- @param #number Indent +-- @return #REPORT +function REPORT:SetIndent( Indent ) --R2.1 + self.Indent = Indent + return self +end + + +--- Add a new line to a REPORT. +-- @param #REPORT self +-- @param #string Text +-- @return #REPORT +function REPORT:Add( Text ) + self.Report[#self.Report+1] = Text + return self +end + +--- Add a new line to a REPORT. +-- @param #REPORT self +-- @param #string Text +-- @return #REPORT +function REPORT:AddIndent( Text ) --R2.1 + self.Report[#self.Report+1] = string.rep(" ", self.Indent ) .. Text:gsub("\n","\n"..string.rep( " ", self.Indent ) ) + return self +end + +--- Produces the text of the report, taking into account an optional delimeter, which is \n by default. +-- @param #REPORT self +-- @param #string Delimiter (optional) A delimiter text. +-- @return #string The report text. +function REPORT:Text( Delimiter ) + Delimiter = Delimiter or "\n" + local ReportText = ( self.Title ~= "" and self.Title .. Delimiter or self.Title ) .. table.concat( self.Report, Delimiter ) or "" + return ReportText +end + +--- Sets the title of the report. +-- @param #REPORT self +-- @param #string Title The title of the report. +-- @return #REPORT +function REPORT:SetTitle( Title ) + self.Title = Title + return self +end + +--- Gets the amount of report items contained in the report. +-- @param #REPORT self +-- @return #number Returns the number of report items contained in the report. 0 is returned if no report items are contained in the report. The title is not counted for. +function REPORT:GetCount() + return #self.Report +end diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index a7962e677..adba0c601 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -1,4 +1,4 @@ ---- This module defines the SCHEDULEDISPATCHER class, which is used by a central object called _SCHEDULEDISPATCHER. +--- **Core** -- SCHEDULEDISPATCHER dispatches the different schedules. -- -- === -- @@ -27,8 +27,6 @@ -- -- === -- --- === --- -- ### Contributions: - -- ### Authors: FlightControl : Design & Programming -- @@ -57,6 +55,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop } ) self.CallID = self.CallID + 1 + local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or "" -- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. @@ -64,30 +63,30 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) -- or {} + self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) if Scheduler.MasterObject then - self.ObjectSchedulers[self.CallID] = Scheduler - self:F3( { CallID = self.CallID, ObjectScheduler = tostring(self.ObjectSchedulers[self.CallID]), MasterObject = tostring(Scheduler.MasterObject) } ) + self.ObjectSchedulers[CallID] = Scheduler + self:F3( { CallID = CallID, ObjectScheduler = tostring(self.ObjectSchedulers[CallID]), MasterObject = tostring(Scheduler.MasterObject) } ) else - self.PersistentSchedulers[self.CallID] = Scheduler - self:F3( { CallID = self.CallID, PersistentScheduler = self.PersistentSchedulers[self.CallID] } ) + self.PersistentSchedulers[CallID] = Scheduler + self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } ) end self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } ) self.Schedule[Scheduler] = self.Schedule[Scheduler] or {} - self.Schedule[Scheduler][self.CallID] = {} - self.Schedule[Scheduler][self.CallID].Function = ScheduleFunction - self.Schedule[Scheduler][self.CallID].Arguments = ScheduleArguments - self.Schedule[Scheduler][self.CallID].StartTime = timer.getTime() + ( Start or 0 ) - self.Schedule[Scheduler][self.CallID].Start = Start + .1 - self.Schedule[Scheduler][self.CallID].Repeat = Repeat - self.Schedule[Scheduler][self.CallID].Randomize = Randomize - self.Schedule[Scheduler][self.CallID].Stop = Stop + self.Schedule[Scheduler][CallID] = {} + self.Schedule[Scheduler][CallID].Function = ScheduleFunction + self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments + self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 ) + self.Schedule[Scheduler][CallID].Start = Start + .1 + self.Schedule[Scheduler][CallID].Repeat = Repeat or 0 + self.Schedule[Scheduler][CallID].Randomize = Randomize or 0 + self.Schedule[Scheduler][CallID].Stop = Stop - self:T3( self.Schedule[Scheduler][self.CallID] ) + self:T3( self.Schedule[Scheduler][CallID] ) - self.Schedule[Scheduler][self.CallID].CallHandler = function( CallID ) + self.Schedule[Scheduler][CallID].CallHandler = function( CallID ) self:F2( CallID ) local ErrorHandler = function( errmsg ) @@ -102,14 +101,15 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr if not Scheduler then Scheduler = self.PersistentSchedulers[CallID] end - - self:T3( { Scheduler = Scheduler } ) + + --self:T3( { Scheduler = Scheduler } ) if Scheduler then + local MasterObject = tostring(Scheduler.MasterObject) local Schedule = self.Schedule[Scheduler][CallID] - self:T3( { Schedule = Schedule } ) + --self:T3( { Schedule = Schedule } ) local ScheduleObject = Scheduler.SchedulerObject --local ScheduleObjectName = Scheduler.SchedulerObject:GetNameAndClassID() @@ -135,10 +135,13 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr end local CurrentTime = timer.getTime() - local StartTime = CurrentTime + Start + local StartTime = Schedule.StartTime + + self:F3( { Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } ) + if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then - if Repeat ~= 0 and ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) then + if Repeat ~= 0 and ( ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) ) then local ScheduleTime = CurrentTime + Repeat + @@ -147,7 +150,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr ( Randomize * Repeat / 2 ) ) + 0.01 - self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } ) + --self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } ) return ScheduleTime -- returns the next time the function needs to be called. else self:Stop( Scheduler, CallID ) @@ -156,15 +159,15 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr self:Stop( Scheduler, CallID ) end else - self:E( "Scheduled obscolete call for CallID: " .. CallID ) + self:E( "Scheduled obsolete call for CallID: " .. CallID ) end return nil end - self:Start( Scheduler, self.CallID ) + self:Start( Scheduler, CallID ) - return self.CallID + return CallID end function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID ) @@ -184,14 +187,15 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) -- Only start when there is no ScheduleID defined! -- This prevents to "Start" the scheduler twice with the same CallID... if not Schedule[CallID].ScheduleID then + Schedule[CallID].StartTime = timer.getTime() -- Set the StartTime field to indicate when the scheduler started. Schedule[CallID].ScheduleID = timer.scheduleFunction( Schedule[CallID].CallHandler, CallID, - timer.getTime() + Schedule[CallID].Start + timer.getTime() + Schedule[CallID].Start ) end else - for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do + for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do self:Start( Scheduler, CallID ) -- Recursive end end @@ -209,7 +213,7 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) Schedule[CallID].ScheduleID = nil end else - for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do + for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do self:Stop( Scheduler, CallID ) -- Recursive end end @@ -218,7 +222,7 @@ end function SCHEDULEDISPATCHER:Clear( Scheduler ) self:F2( { Scheduler = Scheduler } ) - for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do + for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do self:Stop( Scheduler, CallID ) -- Recursive end end diff --git a/Moose Development/Moose/Core/Scheduler.lua b/Moose Development/Moose/Core/Scheduler.lua index 2244d9b1b..4b712d215 100644 --- a/Moose Development/Moose/Core/Scheduler.lua +++ b/Moose Development/Moose/Core/Scheduler.lua @@ -1,34 +1,33 @@ ---- **Core** - SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**. +--- **Core** -- SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**. -- -- ![Banner Image](..\Presentations\SCHEDULER\Dia1.JPG) -- -- === -- --- # 1) @{Scheduler#SCHEDULER} class, extends @{Base#BASE} +-- SCHEDULER manages the **scheduling of functions**: -- --- The @{Scheduler#SCHEDULER} class creates schedule. --- --- ## 1.1) SCHEDULER constructor --- --- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters: --- --- * @{Scheduler#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection. --- * @{Scheduler#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection. --- * @{Scheduler#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters. --- * @{Scheduler#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters. --- --- ## 1.2) SCHEDULER timer stopping and (re-)starting. --- --- The SCHEDULER can be stopped and restarted with the following methods: --- --- * @{Scheduler#SCHEDULER.Start}(): (Re-)Start the schedules within the SCHEDULER object. If a CallID is provided to :Start(), only the schedule referenced by CallID will be (re-)started. --- * @{Scheduler#SCHEDULER.Stop}(): Stop the schedules within the SCHEDULER object. If a CallID is provided to :Stop(), then only the schedule referenced by CallID will be stopped. --- --- ## 1.3) Create a new schedule --- --- With @{Scheduler#SCHEDULER.Schedule}() a new time event can be scheduled. This function is used by the :New() constructor when a new schedule is planned. +-- * optionally in an optional specified time interval, +-- * optionally **repeating** with a specified time repeat interval, +-- * optionally **randomizing** with a specified time interval randomization factor, +-- * optionally **stop** the repeating after a specified time interval. -- -- === +-- +-- # Demo Missions +-- +-- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SCH%20-%20Scheduler) +-- +-- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler) +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [SCHEDULER YouTube Channel (none)]() +-- +-- ==== -- -- ### Contributions: -- @@ -38,10 +37,6 @@ -- -- * FlightControl : Design & Programming -- --- ### Test Missions: --- --- * SCH - Scheduler --- -- === -- -- @module Scheduler @@ -51,6 +46,153 @@ -- @type SCHEDULER -- @field #number ScheduleID the ID of the scheduler. -- @extends Core.Base#BASE + + +--- # SCHEDULER class, extends @{Base#BASE} +-- +-- The SCHEDULER class creates schedule. +-- +-- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**. +-- The ScheduleID is returned when the method @{#SCHEDULER.Schedule}() is called. +-- It is recommended to store the ScheduleID in a variable, as it is used in the methods @{SCHEDULER.Start}() and @{SCHEDULER.Stop}(), +-- which can start and stop specific repeating schedules respectively within a SCHEDULER object. +-- +-- ## SCHEDULER constructor +-- +-- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters: +-- +-- The @{#SCHEDULER.New}() method returns 2 variables: +-- +-- 1. The SCHEDULER object reference. +-- 2. The first schedule planned in the SCHEDULER object. +-- +-- To clarify the different appliances, lets have a look at the following examples: +-- +-- ### Construct a SCHEDULER object without a persistent schedule. +-- +-- * @{#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection. +-- +-- SchedulerObject = SCHEDULER:New() +-- SchedulerID = SchedulerObject:Schedule( nil, ScheduleFunction, {} ) +-- +-- The above example creates a new SchedulerObject, but does not schedule anything. +-- A separate schedule is created by using the SchedulerObject using the method :Schedule..., which returns a ScheduleID +-- +-- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence... +-- +-- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection. +-- +-- ZoneObject = ZONE:New( "ZoneName" ) +-- SchedulerObject = SCHEDULER:New( ZoneObject ) +-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} ) +-- ... +-- ZoneObject = nil +-- garbagecollect() +-- +-- The above example creates a new SchedulerObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE. +-- A separate schedule is created by using the SchedulerObject using the method :Schedule()..., which returns a ScheduleID +-- Later in the logic, the ZoneObject is put to nil, and garbage is collected. +-- As a result, the ScheduleObject will cancel any planned schedule. +-- +-- ### Construct a SCHEDULER object with a persistent schedule. +-- +-- * @{#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters. +-- +-- SchedulerObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} ) +-- +-- The above example creates a new SchedulerObject, and does schedule the first schedule as part of the call. +-- Note that 2 variables are returned here: SchedulerObject, ScheduleID... +-- +-- ### Construct a SCHEDULER object without a schedule, but volatile to the Object existence... +-- +-- * @{#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters. +-- +-- ZoneObject = ZONE:New( "ZoneName" ) +-- SchedulerObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} ) +-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} ) +-- ... +-- ZoneObject = nil +-- garbagecollect() +-- +-- The above example creates a new SchedulerObject, and schedules a method call (ScheduleFunction), +-- and is bound to the existence of ZoneObject, which is a ZONE object (ZoneObject). +-- Both a ScheduleObject and a SchedulerID variable are returned. +-- Later in the logic, the ZoneObject is put to nil, and garbage is collected. +-- As a result, the ScheduleObject will cancel the planned schedule. +-- +-- ## SCHEDULER timer stopping and (re-)starting. +-- +-- The SCHEDULER can be stopped and restarted with the following methods: +-- +-- * @{#SCHEDULER.Start}(): (Re-)Start the schedules within the SCHEDULER object. If a CallID is provided to :Start(), only the schedule referenced by CallID will be (re-)started. +-- * @{#SCHEDULER.Stop}(): Stop the schedules within the SCHEDULER object. If a CallID is provided to :Stop(), then only the schedule referenced by CallID will be stopped. +-- +-- ZoneObject = ZONE:New( "ZoneName" ) +-- SchedulerObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} ) +-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 10 ) +-- ... +-- SchedulerObject:Stop( SchedulerID ) +-- ... +-- SchedulerObject:Start( SchedulerID ) +-- +-- The above example creates a new SchedulerObject, and does schedule the first schedule as part of the call. +-- Note that 2 variables are returned here: SchedulerObject, ScheduleID... +-- Later in the logic, the repeating schedule with SchedulerID is stopped. +-- A bit later, the repeating schedule with SchedulerId is (re)-started. +-- +-- ## Create a new schedule +-- +-- With the method @{#SCHEDULER.Schedule}() a new time event can be scheduled. +-- This method is used by the :New() constructor when a new schedule is planned. +-- +-- Consider the following code fragment of the SCHEDULER object creation. +-- +-- ZoneObject = ZONE:New( "ZoneName" ) +-- SchedulerObject = SCHEDULER:New( ZoneObject ) +-- +-- Several parameters can be specified that influence the behaviour of a Schedule. +-- +-- ### A single schedule, immediately executed +-- +-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} ) +-- +-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milleseconds ... +-- +-- ### A single schedule, planned over time +-- +-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 ) +-- +-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds ... +-- +-- ### A schedule with a repeating time interval, planned over time +-- +-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 ) +-- +-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds, +-- and repeating 60 every seconds ... +-- +-- ### A schedule with a repeating time interval, planned over time, with time interval randomization +-- +-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 ) +-- +-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds, +-- and repeating 60 seconds, with a 50% time interval randomization ... +-- So the repeating time interval will be randomized using the **0.5**, +-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat, +-- which is in this example between **30** and **90** seconds. +-- +-- ### A schedule with a repeating time interval, planned over time, with time interval randomization, and stop after a time interval +-- +-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 ) +-- +-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds, +-- The schedule will repeat every 60 seconds. +-- So the repeating time interval will be randomized using the **0.5**, +-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat, +-- which is in this example between **30** and **90** seconds. +-- The schedule will stop after **300** seconds. +-- +-- @field #SCHEDULER SCHEDULER = { ClassName = "SCHEDULER", Schedules = {}, @@ -68,7 +210,8 @@ SCHEDULER = { -- @return #SCHEDULER self. -- @return #number The ScheduleID of the planned schedule. function SCHEDULER:New( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop ) - local self = BASE:Inherit( self, BASE:New() ) + + local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER self:F2( { Start, Repeat, RandomizeFactor, Stop } ) local ScheduleID = nil diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index bfb6ac69e..f89224259 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1,4 +1,4 @@ ---- **Core** - SET_ classes define **collections** of objects to perform **bulk actions** and logically **group** objects. +--- **Core** -- SET_ classes define **collections** of objects to perform **bulk actions** and logically **group** objects. -- -- ![Banner Image](..\Presentations\SET\Dia1.JPG) -- @@ -23,12 +23,12 @@ -- * Validate the presence of objects in the SET. -- * Trigger events when objects in the SET change a zone presence. -- --- ### Authors: +-- ==== -- --- * FlightControl : Design & Programming --- +-- ### Author: **Sven Van de Velde (FlightControl)** -- ### Contributions: -- +-- ==== -- -- @module Set @@ -84,11 +84,6 @@ function SET_BASE:New( Database ) self.TimeInterval = 0.001 self.Set = {} - - self.List = {} - self.List.__index = self.List - self.List = setmetatable( { Count = 0 }, self.List ) - self.Index = {} self.CallScheduler = SCHEDULER:New( self ) @@ -124,26 +119,10 @@ end -- @param Core.Base#BASE Object -- @return Core.Base#BASE The added BASE Object. function SET_BASE:Add( ObjectName, Object ) - self:F2( ObjectName ) + self:F( ObjectName ) - local t = { _ = Object } - - if self.List.last then - self.List.last._next = t - t._prev = self.List.last - self.List.last = t - else - -- this is the first node - self.List.first = t - self.List.last = t - end - - self.List.Count = self.List.Count + 1 - - self.Set[ObjectName] = t._ - + self.Set[ObjectName] = Object table.insert( self.Index, ObjectName ) - end --- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index. @@ -165,45 +144,20 @@ end -- @param #SET_BASE self -- @param #string ObjectName function SET_BASE:Remove( ObjectName ) - self:F( ObjectName ) - local t = self.Set[ObjectName] + local Object = self.Set[ObjectName] - self:E( { ObjectName, t } ) + self:F3( { ObjectName, Object } ) - if t then - if t._next then - if t._prev then - t._next._prev = t._prev - t._prev._next = t._next - else - -- this was the first node - t._next._prev = nil - self.List._first = t._next - end - elseif t._prev then - -- this was the last node - t._prev._next = nil - self.List._last = t._prev - else - -- this was the only node - self.List._first = nil - self.List._last = nil - end - - t._next = nil - t._prev = nil - self.List.Count = self.List.Count - 1 - + if Object then for Index, Key in ipairs( self.Index ) do if Key == ObjectName then table.remove( self.Index, Index ) + self.Set[ObjectName] = nil break end end - self.Set[ObjectName] = nil - end end @@ -215,19 +169,16 @@ end function SET_BASE:Get( ObjectName ) self:F( ObjectName ) - local t = self.Set[ObjectName] - - self:T3( { ObjectName, t } ) - - return t + local Object = self.Set[ObjectName] + self:T3( { ObjectName, Object } ) + return Object end --- Gets the first object from the @{Set#SET_BASE} and derived classes. -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetFirst() - self:F() local ObjectName = self.Index[1] local FirstObject = self.Set[ObjectName] @@ -239,7 +190,6 @@ end -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetLast() - self:F() local ObjectName = self.Index[#self.Index] local LastObject = self.Set[ObjectName] @@ -251,12 +201,9 @@ end -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetRandom() - self:F() local RandomItem = self.Set[self.Index[math.random(#self.Index)]] - self:T3( { RandomItem } ) - return RandomItem end @@ -266,7 +213,7 @@ end -- @return #number Count function SET_BASE:Count() - return #self.Index or 0 + return self.Index and #self.Index or 0 end @@ -342,6 +289,26 @@ function SET_BASE:_FilterStart() return self end +--- Starts the filtering of the Dead events for the collection. +-- @param #SET_BASE self +-- @return #SET_BASE self +function SET_BASE:FilterDeads() --R2.1 allow deads to be filtered to automatically handle deads in the collection. + + self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + + return self +end + +--- Starts the filtering of the Crash events for the collection. +-- @param #SET_BASE self +-- @return #SET_BASE self +function SET_BASE:FilterCrashes() --R2.1 allow crashes to be filtered to automatically handle crashes in the collection. + + self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + + return self +end + --- Stops the filtering for the defined collection. -- @param #SET_BASE self -- @return #SET_BASE self @@ -430,7 +397,7 @@ function SET_BASE:_EventOnDeadOrCrash( Event ) if Event.IniDCSUnit then local ObjectName, Object = self:FindInDatabase( Event ) - if ObjectName and Object ~= nil then + if ObjectName then self:Remove( ObjectName ) end end @@ -465,7 +432,7 @@ function SET_BASE:_EventOnPlayerLeaveUnit( Event ) local PlayerCount = 0 for _, DCSUnit in pairs( GroupUnits ) do if DCSUnit ~= Event.IniDCSUnit then - if DCSUnit:getPlayer() ~= nil then + if DCSUnit:getPlayerName() ~= nil then PlayerCount = PlayerCount + 1 end end @@ -584,6 +551,20 @@ function SET_BASE:IsIncludeObject( Object ) return true end +--- Gets a string with all the object names. +-- @param #SET_BASE self +-- @return #string A string with the names of the objects. +function SET_BASE:GetObjectNames() + self:F3() + + local ObjectNames = "" + for ObjectName, Object in pairs( self.Set ) do + ObjectNames = ObjectNames .. ObjectName .. ", " + end + + return ObjectNames +end + --- Flushes the current SET_BASE contents in the log ... (for debugging reasons). -- @param #SET_BASE self -- @return #string A string with the names of the objects. @@ -603,7 +584,7 @@ end --- @type SET_GROUP -- @extends Core.Set#SET_BASE ---- # 2) SET_GROUP class, extends @{Set#SET_BASE} +--- # SET_GROUP class, extends @{Set#SET_BASE} -- -- Mission designers can use the @{Set#SET_GROUP} class to build sets of groups belonging to certain: -- @@ -612,18 +593,18 @@ end -- * Countries -- * Starting with certain prefix strings. -- --- ## 2.1) SET_GROUP constructor +-- ## 1. SET_GROUP constructor -- -- Create a new SET_GROUP object with the @{#SET_GROUP.New} method: -- -- * @{#SET_GROUP.New}: Creates a new SET_GROUP object. -- --- ## 2.2) Add or Remove GROUP(s) from SET_GROUP +-- ## 2. Add or Remove GROUP(s) from SET_GROUP -- -- GROUPS can be added and removed using the @{Set#SET_GROUP.AddGroupsByName} and @{Set#SET_GROUP.RemoveGroupsByName} respectively. -- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. -- --- ## 2.3) SET_GROUP filter criteria +-- ## 3. SET_GROUP filter criteria -- -- You can set filter criteria to define the set of groups within the SET_GROUP. -- Filter criteria are defined by: @@ -632,6 +613,15 @@ end -- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). -- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies). -- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s). +-- +-- For the Category Filter, extra methods have been added: +-- +-- * @{#SET_GROUP.FilterCategoryAirplane}: Builds the SET_GROUP from airplanes. +-- * @{#SET_GROUP.FilterCategoryHelicopter}: Builds the SET_GROUP from helicopters. +-- * @{#SET_GROUP.FilterCategoryGround}: Builds the SET_GROUP from ground vehicles or infantry. +-- * @{#SET_GROUP.FilterCategoryShip}: Builds the SET_GROUP from ships. +-- * @{#SET_GROUP.FilterCategoryStructure}: Builds the SET_GROUP from structures. +-- -- -- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: -- @@ -641,7 +631,7 @@ end -- -- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Zone#ZONE}. -- --- ## 2.4) SET_GROUP iterators +-- ## 4. SET_GROUP iterators -- -- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. -- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide. @@ -671,7 +661,7 @@ SET_GROUP = { Categories = { plane = Group.Category.AIRPLANE, helicopter = Group.Category.HELICOPTER, - ground = Group.Category.GROUND_UNIT, + ground = Group.Category.GROUND, -- R2.2 ship = Group.Category.SHIP, structure = Group.Category.STRUCTURE, }, @@ -736,6 +726,31 @@ function SET_GROUP:FindGroup( GroupName ) return GroupFound end +--- Iterate the SET_GROUP while identifying the nearest object from a @{Point#POINT_VEC2}. +-- @param #SET_GROUP self +-- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest object in the set. +-- @return Wrapper.Group#GROUP The closest group. +function SET_GROUP:FindNearestGroupFromPointVec2( PointVec2 ) + self:F2( PointVec2 ) + + local NearestGroup = nil + local ClosestDistance = nil + + for ObjectID, ObjectData in pairs( self.Set ) do + if NearestGroup == nil then + NearestGroup = ObjectData + ClosestDistance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) + else + local Distance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) + if Distance < ClosestDistance then + NearestGroup = ObjectData + ClosestDistance = Distance + end + end + end + + return NearestGroup +end --- Builds a set of groups of coalitions. @@ -775,6 +790,48 @@ function SET_GROUP:FilterCategories( Categories ) return self end +--- Builds a set of groups out of ground category. +-- @param #SET_GROUP self +-- @return #SET_GROUP self +function SET_GROUP:FilterCategoryGround() + self:FilterCategories( "ground" ) + return self +end + +--- Builds a set of groups out of airplane category. +-- @param #SET_GROUP self +-- @return #SET_GROUP self +function SET_GROUP:FilterCategoryAirplane() + self:FilterCategories( "plane" ) + return self +end + +--- Builds a set of groups out of helicopter category. +-- @param #SET_GROUP self +-- @return #SET_GROUP self +function SET_GROUP:FilterCategoryHelicopter() + self:FilterCategories( "helicopter" ) + return self +end + +--- Builds a set of groups out of ship category. +-- @param #SET_GROUP self +-- @return #SET_GROUP self +function SET_GROUP:FilterCategoryShip() + self:FilterCategories( "ship" ) + return self +end + +--- Builds a set of groups out of structure category. +-- @param #SET_GROUP self +-- @return #SET_GROUP self +function SET_GROUP:FilterCategoryStructure() + self:FilterCategories( "structure" ) + return self +end + + + --- Builds a set of groups of defined countries. -- Possible current countries are those known within DCS world. -- @param #SET_GROUP self @@ -827,6 +884,23 @@ function SET_GROUP:FilterStart() return self end +--- Handles the OnDead or OnCrash event for alive groups set. +-- Note: The GROUP object in the SET_GROUP collection will only be removed if the last unit is destroyed of the GROUP. +-- @param #SET_GROUP self +-- @param Core.Event#EVENTDATA Event +function SET_GROUP:_EventOnDeadOrCrash( Event ) + self:F3( { Event } ) + + if Event.IniDCSUnit then + local ObjectName, Object = self:FindInDatabase( Event ) + if ObjectName then + if Event.IniDCSGroup:getSize() == 1 then -- Only remove if the last unit of the group was destroyed. + self:Remove( ObjectName ) + end + end + end +end + --- Handles the Database to check on an event (birth) that the Object was added in the Database. -- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! -- @param #SET_GROUP self @@ -936,6 +1010,185 @@ function SET_GROUP:ForEachGroupNotInZone( ZoneObject, IteratorFunction, ... ) return self end +--- Iterate the SET_GROUP and return true if all the @{Wrapper.Group#GROUP} are completely in the @{Core.Zone#ZONE} +-- @param #SET_GROUP self +-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. +-- @return #boolean true if all the @{Wrapper.Group#GROUP} are completly in the @{Core.Zone#ZONE}, false otherwise +-- @usage +-- local MyZone = ZONE:New("Zone1") +-- local MySetGroup = SET_GROUP:New() +-- MySetGroup:AddGroupsByName({"Group1", "Group2"}) +-- +-- if MySetGroup:AllCompletelyInZone(MyZone) then +-- MESSAGE:New("All the SET's GROUP are in zone !", 10):ToAll() +-- else +-- MESSAGE:New("Some or all SET's GROUP are outside zone !", 10):ToAll() +-- end +function SET_GROUP:AllCompletelyInZone(Zone) + self:F2(Zone) + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if not GroupData:IsCompletelyInZone(Zone) then + return false + end + end + return true +end + +--- Iterate the SET_GROUP and return true if at least one of the @{Wrapper.Group#GROUP} is completely inside the @{Core.Zone#ZONE} +-- @param #SET_GROUP self +-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. +-- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is completly inside the @{Core.Zone#ZONE}, false otherwise. +-- @usage +-- local MyZone = ZONE:New("Zone1") +-- local MySetGroup = SET_GROUP:New() +-- MySetGroup:AddGroupsByName({"Group1", "Group2"}) +-- +-- if MySetGroup:AnyCompletelyInZone(MyZone) then +-- MESSAGE:New("At least one GROUP is completely in zone !", 10):ToAll() +-- else +-- MESSAGE:New("No GROUP is completely in zone !", 10):ToAll() +-- end +function SET_GROUP:AnyCompletelyInZone(Zone) + self:F2(Zone) + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then + return true + end + end + return false +end + +--- Iterate the SET_GROUP and return true if at least one @{#UNIT} of one @{GROUP} of the @{SET_GROUP} is in @{ZONE} +-- @param #SET_GROUP self +-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. +-- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is partly or completly inside the @{Core.Zone#ZONE}, false otherwise. +-- @usage +-- local MyZone = ZONE:New("Zone1") +-- local MySetGroup = SET_GROUP:New() +-- MySetGroup:AddGroupsByName({"Group1", "Group2"}) +-- +-- if MySetGroup:AnyPartlyInZone(MyZone) then +-- MESSAGE:New("At least one GROUP has at least one UNIT in zone !", 10):ToAll() +-- else +-- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll() +-- end +function SET_GROUP:AnyInZone(Zone) + self:F2(Zone) + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsPartlyInZone(Zone) or GroupData:IsCompletelyInZone(Zone) then + return true + end + end + return false +end + +--- Iterate the SET_GROUP and return true if at least one @{GROUP} of the @{SET_GROUP} is partly in @{ZONE}. +-- Will return false if a @{GROUP} is fully in the @{ZONE} +-- @param #SET_GROUP self +-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. +-- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is partly or completly inside the @{Core.Zone#ZONE}, false otherwise. +-- @usage +-- local MyZone = ZONE:New("Zone1") +-- local MySetGroup = SET_GROUP:New() +-- MySetGroup:AddGroupsByName({"Group1", "Group2"}) +-- +-- if MySetGroup:AnyPartlyInZone(MyZone) then +-- MESSAGE:New("At least one GROUP is partially in the zone, but none are fully in it !", 10):ToAll() +-- else +-- MESSAGE:New("No GROUP are in zone, or one (or more) GROUP is completely in it !", 10):ToAll() +-- end +function SET_GROUP:AnyPartlyInZone(Zone) + self:F2(Zone) + local IsPartlyInZone = false + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then + return false + elseif GroupData:IsPartlyInZone(Zone) then + IsPartlyInZone = true -- at least one GROUP is partly in zone + end + end + + if IsPartlyInZone then + return true + else + return false + end +end + +--- Iterate the SET_GROUP and return true if no @{GROUP} of the @{SET_GROUP} is in @{ZONE} +-- This could also be achieved with `not SET_GROUP:AnyPartlyInZone(Zone)`, but it's easier for the +-- mission designer to add a dedicated method +-- @param #SET_GROUP self +-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. +-- @return #boolean true if no @{Wrapper.Group#GROUP} is inside the @{Core.Zone#ZONE} in any way, false otherwise. +-- @usage +-- local MyZone = ZONE:New("Zone1") +-- local MySetGroup = SET_GROUP:New() +-- MySetGroup:AddGroupsByName({"Group1", "Group2"}) +-- +-- if MySetGroup:NoneInZone(MyZone) then +-- MESSAGE:New("No GROUP is completely in zone !", 10):ToAll() +-- else +-- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll() +-- end +function SET_GROUP:NoneInZone(Zone) + self:F2(Zone) + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if not GroupData:IsNotInZone(Zone) then -- If the GROUP is in Zone in any way + return false + end + end + return true +end + +--- Iterate the SET_GROUP and count how many GROUPs are completely in the Zone +-- That could easily be done with SET_GROUP:ForEachGroupCompletelyInZone(), but this function +-- provides an easy to use shortcut... +-- @param #SET_GROUP self +-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. +-- @return #number the number of GROUPs completely in the Zone +-- @usage +-- local MyZone = ZONE:New("Zone1") +-- local MySetGroup = SET_GROUP:New() +-- MySetGroup:AddGroupsByName({"Group1", "Group2"}) +-- +-- MESSAGE:New("There are " .. MySetGroup:CountInZone(MyZone) .. " GROUPs in the Zone !", 10):ToAll() +function SET_GROUP:CountInZone(Zone) + self:F2(Zone) + local Count = 0 + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then + Count = Count + 1 + end + end + return Count +end + +--- Iterate the SET_GROUP and count how many UNITs are completely in the Zone +-- @param #SET_GROUP self +-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. +-- @return #number the number of GROUPs completely in the Zone +-- @usage +-- local MyZone = ZONE:New("Zone1") +-- local MySetGroup = SET_GROUP:New() +-- MySetGroup:AddGroupsByName({"Group1", "Group2"}) +-- +-- MESSAGE:New("There are " .. MySetGroup:CountUnitInZone(MyZone) .. " UNITs in the Zone !", 10):ToAll() +function SET_GROUP:CountUnitInZone(Zone) + self:F2(Zone) + local Count = 0 + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + Count = Count + GroupData:CountInZone(Zone) + end + return Count +end ----- Iterate the SET_GROUP and call an interator function for each **alive** player, providing the Group of the player and optional parameters. ---- @param #SET_GROUP self @@ -1008,7 +1261,7 @@ function SET_GROUP:IsIncludeObject( MooseGroup ) local MooseGroupPrefix = false for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do self:T3( { "Prefix:", string.find( MooseGroup:GetName(), GroupPrefix, 1 ), GroupPrefix } ) - if string.find( MooseGroup:GetName(), GroupPrefix, 1 ) then + if string.find( MooseGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then MooseGroupPrefix = true end end @@ -1113,6 +1366,11 @@ SET_UNIT = { } +--- Get the first unit from the set. +-- @function [parent=#SET_UNIT] GetFirst +-- @param #SET_UNIT self +-- @return Wrapper.Unit#UNIT The UNIT object. + --- Creates a new SET_UNIT object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. -- @param #SET_UNIT self -- @return #SET_UNIT @@ -1122,7 +1380,7 @@ SET_UNIT = { function SET_UNIT:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.UNITS ) ) + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.UNITS ) ) -- Core.Set#SET_UNIT return self end @@ -1344,7 +1602,7 @@ end -- @return #string The name of the UNIT -- @return #table The UNIT function SET_UNIT:FindInDatabase( Event ) - self:E( { Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event } ) + self:F2( { Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event } ) return Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName] @@ -1362,6 +1620,54 @@ function SET_UNIT:ForEachUnit( IteratorFunction, ... ) return self end +--- Iterate the SET_UNIT **sorted *per Threat Level** and call an interator function for each **alive** UNIT, providing the UNIT and optional parameters. +-- +-- @param #SET_UNIT self +-- @param #number FromThreatLevel The TreatLevel to start the evaluation **From** (this must be a value between 0 and 10). +-- @param #number ToThreatLevel The TreatLevel to stop the evaluation **To** (this must be a value between 0 and 10). +-- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. +-- @return #SET_UNIT self +-- @usage +-- +-- UnitSet:ForEachUnitPerThreatLevel( 10, 0, +-- -- @param Wrapper.Unit#UNIT UnitObject The UNIT object in the UnitSet, that will be passed to the local function for evaluation. +-- function( UnitObject ) +-- .. logic .. +-- end +-- ) +-- +function SET_UNIT:ForEachUnitPerThreatLevel( FromThreatLevel, ToThreatLevel, IteratorFunction, ... ) --R2.1 Threat Level implementation + self:F2( arg ) + + local ThreatLevelSet = {} + + if self:Count() ~= 0 then + for UnitName, UnitObject in pairs( self.Set ) do + local Unit = UnitObject -- Wrapper.Unit#UNIT + + local ThreatLevel = Unit:GetThreatLevel() + ThreatLevelSet[ThreatLevel] = ThreatLevelSet[ThreatLevel] or {} + ThreatLevelSet[ThreatLevel].Set = ThreatLevelSet[ThreatLevel].Set or {} + ThreatLevelSet[ThreatLevel].Set[UnitName] = UnitObject + self:E( { ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set } ) + end + + local ThreatLevelIncrement = FromThreatLevel <= ToThreatLevel and 1 or -1 + + for ThreatLevel = FromThreatLevel, ToThreatLevel, ThreatLevelIncrement do + self:E( { ThreatLevel = ThreatLevel } ) + local ThreatLevelItem = ThreatLevelSet[ThreatLevel] + if ThreatLevelItem then + self:ForEach( IteratorFunction, arg, ThreatLevelItem.Set ) + end + end + end + + return self +end + + + --- Iterate the SET_UNIT and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. -- @param #SET_UNIT self -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. @@ -1374,7 +1680,7 @@ function SET_UNIT:ForEachUnitCompletelyInZone( ZoneObject, IteratorFunction, ... --- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Unit#UNIT UnitObject function( ZoneObject, UnitObject ) - if UnitObject:IsCompletelyInZone( ZoneObject ) then + if UnitObject:IsInZone( ZoneObject ) then return true else return false @@ -1478,6 +1784,7 @@ end --- Calculate the maxium A2G threat level of the SET_UNIT. -- @param #SET_UNIT self +-- @return #number The maximum threatlevel function SET_UNIT:CalculateThreatLevelA2G() local MaxThreatLevelA2G = 0 @@ -2391,3 +2698,346 @@ function SET_AIRBASE:IsIncludeObject( MAirbase ) self:T2( MAirbaseInclude ) return MAirbaseInclude end + +--- @type SET_CARGO +-- @extends Core.Set#SET_BASE + +--- # (R2.1) SET_CARGO class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_CARGO} class to build sets of cargos optionally belonging to certain: +-- +-- * Coalitions +-- * Types +-- * Name or Prefix +-- +-- ## SET_CARGO constructor +-- +-- Create a new SET_CARGO object with the @{#SET_CARGO.New} method: +-- +-- * @{#SET_CARGO.New}: Creates a new SET_CARGO object. +-- +-- ## Add or Remove CARGOs from SET_CARGO +-- +-- CARGOs can be added and removed using the @{Set#SET_CARGO.AddCargosByName} and @{Set#SET_CARGO.RemoveCargosByName} respectively. +-- These methods take a single CARGO name or an array of CARGO names to be added or removed from SET_CARGO. +-- +-- ## SET_CARGO filter criteria +-- +-- You can set filter criteria to automatically maintain the SET_CARGO contents. +-- Filter criteria are defined by: +-- +-- * @{#SET_CARGO.FilterCoalitions}: Builds the SET_CARGO with the cargos belonging to the coalition(s). +-- * @{#SET_CARGO.FilterPrefixes}: Builds the SET_CARGO with the cargos containing the prefix string(s). +-- * @{#SET_CARGO.FilterTypes}: Builds the SET_CARGO with the cargos belonging to the cargo type(s). +-- * @{#SET_CARGO.FilterCountries}: Builds the SET_CARGO with the cargos belonging to the country(ies). +-- +-- Once the filter criteria have been set for the SET_CARGO, you can start filtering using: +-- +-- * @{#SET_CARGO.FilterStart}: Starts the filtering of the cargos within the SET_CARGO. +-- +-- ## SET_CARGO iterators +-- +-- Once the filters have been defined and the SET_CARGO has been built, you can iterate the SET_CARGO with the available iterator methods. +-- The iterator methods will walk the SET_CARGO set, and call for each cargo within the set a function that you provide. +-- The following iterator methods are currently available within the SET_CARGO: +-- +-- * @{#SET_CARGO.ForEachCargo}: Calls a function for each cargo it finds within the SET_CARGO. +-- +-- @field #SET_CARGO SET_CARGO +-- +SET_CARGO = { + ClassName = "SET_CARGO", + Cargos = {}, + Filter = { + Coalitions = nil, + Types = nil, + Countries = nil, + ClientPrefixes = nil, + }, + FilterMeta = { + Coalitions = { + red = coalition.side.RED, + blue = coalition.side.BLUE, + neutral = coalition.side.NEUTRAL, + }, + }, +} + + +--- (R2.1) Creates a new SET_CARGO object, building a set of cargos belonging to a coalitions and categories. +-- @param #SET_CARGO self +-- @return #SET_CARGO +-- @usage +-- -- Define a new SET_CARGO Object. The DatabaseSet will contain a reference to all Cargos. +-- DatabaseSet = SET_CARGO:New() +function SET_CARGO:New() --R2.1 + -- Inherits from BASE + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CARGOS ) ) -- #SET_CARGO + + return self +end + +--- (R2.1) Add CARGOs to SET_CARGO. +-- @param Core.Set#SET_CARGO self +-- @param #string AddCargoNames A single name or an array of CARGO names. +-- @return self +function SET_CARGO:AddCargosByName( AddCargoNames ) --R2.1 + + local AddCargoNamesArray = ( type( AddCargoNames ) == "table" ) and AddCargoNames or { AddCargoNames } + + for AddCargoID, AddCargoName in pairs( AddCargoNamesArray ) do + self:Add( AddCargoName, CARGO:FindByName( AddCargoName ) ) + end + + return self +end + +--- (R2.1) Remove CARGOs from SET_CARGO. +-- @param Core.Set#SET_CARGO self +-- @param Wrapper.Cargo#CARGO RemoveCargoNames A single name or an array of CARGO names. +-- @return self +function SET_CARGO:RemoveCargosByName( RemoveCargoNames ) --R2.1 + + local RemoveCargoNamesArray = ( type( RemoveCargoNames ) == "table" ) and RemoveCargoNames or { RemoveCargoNames } + + for RemoveCargoID, RemoveCargoName in pairs( RemoveCargoNamesArray ) do + self:Remove( RemoveCargoName.CargoName ) + end + + return self +end + + +--- (R2.1) Finds a Cargo based on the Cargo Name. +-- @param #SET_CARGO self +-- @param #string CargoName +-- @return Wrapper.Cargo#CARGO The found Cargo. +function SET_CARGO:FindCargo( CargoName ) --R2.1 + + local CargoFound = self.Set[CargoName] + return CargoFound +end + + + +--- (R2.1) Builds a set of cargos of coalitions. +-- Possible current coalitions are red, blue and neutral. +-- @param #SET_CARGO self +-- @param #string Coalitions Can take the following values: "red", "blue", "neutral". +-- @return #SET_CARGO self +function SET_CARGO:FilterCoalitions( Coalitions ) --R2.1 + if not self.Filter.Coalitions then + self.Filter.Coalitions = {} + end + if type( Coalitions ) ~= "table" then + Coalitions = { Coalitions } + end + for CoalitionID, Coalition in pairs( Coalitions ) do + self.Filter.Coalitions[Coalition] = Coalition + end + return self +end + +--- (R2.1) Builds a set of cargos of defined cargo types. +-- Possible current types are those types known within DCS world. +-- @param #SET_CARGO self +-- @param #string Types Can take those type strings known within DCS world. +-- @return #SET_CARGO self +function SET_CARGO:FilterTypes( Types ) --R2.1 + if not self.Filter.Types then + self.Filter.Types = {} + end + if type( Types ) ~= "table" then + Types = { Types } + end + for TypeID, Type in pairs( Types ) do + self.Filter.Types[Type] = Type + end + return self +end + + +--- (R2.1) Builds a set of cargos of defined countries. +-- Possible current countries are those known within DCS world. +-- @param #SET_CARGO self +-- @param #string Countries Can take those country strings known within DCS world. +-- @return #SET_CARGO self +function SET_CARGO:FilterCountries( Countries ) --R2.1 + if not self.Filter.Countries then + self.Filter.Countries = {} + end + if type( Countries ) ~= "table" then + Countries = { Countries } + end + for CountryID, Country in pairs( Countries ) do + self.Filter.Countries[Country] = Country + end + return self +end + + +--- (R2.1) Builds a set of cargos of defined cargo prefixes. +-- All the cargos starting with the given prefixes will be included within the set. +-- @param #SET_CARGO self +-- @param #string Prefixes The prefix of which the cargo name starts with. +-- @return #SET_CARGO self +function SET_CARGO:FilterPrefixes( Prefixes ) --R2.1 + if not self.Filter.CargoPrefixes then + self.Filter.CargoPrefixes = {} + end + if type( Prefixes ) ~= "table" then + Prefixes = { Prefixes } + end + for PrefixID, Prefix in pairs( Prefixes ) do + self.Filter.CargoPrefixes[Prefix] = Prefix + end + return self +end + + + +--- (R2.1) Starts the filtering. +-- @param #SET_CARGO self +-- @return #SET_CARGO self +function SET_CARGO:FilterStart() --R2.1 + + if _DATABASE then + self:_FilterStart() + end + + self:HandleEvent( EVENTS.NewCargo ) + self:HandleEvent( EVENTS.DeleteCargo ) + + return self +end + + +--- (R2.1) Handles the Database to check on an event (birth) that the Object was added in the Database. +-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! +-- @param #SET_CARGO self +-- @param Core.Event#EVENTDATA Event +-- @return #string The name of the CARGO +-- @return #table The CARGO +function SET_CARGO:AddInDatabase( Event ) --R2.1 + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] +end + +--- (R2.1) Handles the Database to check on any event that Object exists in the Database. +-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! +-- @param #SET_CARGO self +-- @param Core.Event#EVENTDATA Event +-- @return #string The name of the CARGO +-- @return #table The CARGO +function SET_CARGO:FindInDatabase( Event ) --R2.1 + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] +end + +--- (R2.1) Iterate the SET_CARGO and call an interator function for each CARGO, providing the CARGO and optional parameters. +-- @param #SET_CARGO self +-- @param #function IteratorFunction The function that will be called when there is an alive CARGO in the SET_CARGO. The function needs to accept a CARGO parameter. +-- @return #SET_CARGO self +function SET_CARGO:ForEachCargo( IteratorFunction, ... ) --R2.1 + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self.Set ) + + return self +end + +--- (R2.1) Iterate the SET_CARGO while identifying the nearest @{Cargo#CARGO} from a @{Point#POINT_VEC2}. +-- @param #SET_CARGO self +-- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest @{Cargo#CARGO}. +-- @return Wrapper.Cargo#CARGO The closest @{Cargo#CARGO}. +function SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) --R2.1 + self:F2( PointVec2 ) + + local NearestCargo = self:FindNearestObjectFromPointVec2( PointVec2 ) + return NearestCargo +end + + + +--- (R2.1) +-- @param #SET_CARGO self +-- @param AI.AI_Cargo#AI_CARGO MCargo +-- @return #SET_CARGO self +function SET_CARGO:IsIncludeObject( MCargo ) --R2.1 + self:F2( MCargo ) + + local MCargoInclude = true + + if MCargo then + local MCargoName = MCargo:GetName() + + if self.Filter.Coalitions then + local MCargoCoalition = false + for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do + local CargoCoalitionID = MCargo:GetCoalition() + self:T3( { "Coalition:", CargoCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == CargoCoalitionID then + MCargoCoalition = true + end + end + self:T( { "Evaluated Coalition", MCargoCoalition } ) + MCargoInclude = MCargoInclude and MCargoCoalition + end + + if self.Filter.Types then + local MCargoType = false + for TypeID, TypeName in pairs( self.Filter.Types ) do + self:T3( { "Type:", MCargo:GetType(), TypeName } ) + if TypeName == MCargo:GetType() then + MCargoType = true + end + end + self:T( { "Evaluated Type", MCargoType } ) + MCargoInclude = MCargoInclude and MCargoType + end + + if self.Filter.CargoPrefixes then + local MCargoPrefix = false + for CargoPrefixId, CargoPrefix in pairs( self.Filter.CargoPrefixes ) do + self:T3( { "Prefix:", string.find( MCargo.Name, CargoPrefix, 1 ), CargoPrefix } ) + if string.find( MCargo.Name, CargoPrefix, 1 ) then + MCargoPrefix = true + end + end + self:T( { "Evaluated Prefix", MCargoPrefix } ) + MCargoInclude = MCargoInclude and MCargoPrefix + end + end + + self:T2( MCargoInclude ) + return MCargoInclude +end + +--- (R2.1) Handles the OnEventNewCargo event for the Set. +-- @param #SET_CARGO self +-- @param Core.Event#EVENTDATA EventData +function SET_CARGO:OnEventNewCargo( EventData ) --R2.1 + + if EventData.Cargo then + if EventData.Cargo and self:IsIncludeObject( EventData.Cargo ) then + self:Add( EventData.Cargo.Name , EventData.Cargo ) + end + end +end + +--- (R2.1) Handles the OnDead or OnCrash event for alive units set. +-- @param #SET_CARGO self +-- @param Core.Event#EVENTDATA EventData +function SET_CARGO:OnEventDeleteCargo( EventData ) --R2.1 + self:F3( { EventData } ) + + if EventData.Cargo then + local Cargo = _DATABASE:FindCargo( EventData.Cargo.Name ) + if Cargo and Cargo.Name then + self:Remove( Cargo.Name ) + end + end +end + diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua new file mode 100644 index 000000000..2730f4d1c --- /dev/null +++ b/Moose Development/Moose/Core/Settings.lua @@ -0,0 +1,546 @@ +--- **Core** -- **SETTINGS** classe defines the format settings management for measurement. +-- +-- ![Banner Image](..\Presentations\SETTINGS\Dia1.JPG) +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [SETTINGS Demo Missions source code]() +-- +-- ### [SETTINGS Demo Missions, only for beta testers]() +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [SETTINGS YouTube Channel]() +-- +-- === +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: +-- +-- ==== +-- +-- @module Settings + + +--- @type SETTINGS +-- @field #number LL_Accuracy +-- @field #boolean LL_DMS +-- @field #number MGRS_Accuracy +-- @field #string A2GSystem +-- @field #string A2ASystem +-- @extends Core.Base#BASE + +--- # SETTINGS class, extends @{Base#BASE} +-- +-- @field #SETTINGS +SETTINGS = { + ClassName = "SETTINGS", +} + + + +do -- SETTINGS + + --- SETTINGS constructor. + -- @param #SETTINGS self + -- @return #SETTINGS + function SETTINGS:Set( PlayerName ) + + if PlayerName == nil then + local self = BASE:Inherit( self, BASE:New() ) -- #SETTINGS + self:SetMetric() -- Defaults + self:SetA2G_BR() -- Defaults + self:SetA2A_BRAA() -- Defaults + self:SetLL_Accuracy( 3 ) -- Defaults + self:SetMGRS_Accuracy( 5 ) -- Defaults + return self + else + local Settings = _DATABASE:GetPlayerSettings( PlayerName ) + if not Settings then + Settings = BASE:Inherit( self, BASE:New() ) -- #SETTINGS + _DATABASE:SetPlayerSettings( PlayerName, Settings ) + end + return Settings + end + end + + + --- Sets the SETTINGS metric. + -- @param #SETTINGS self + function SETTINGS:SetMetric() + self.Metric = true + end + + --- Gets if the SETTINGS is metric. + -- @param #SETTINGS self + -- @return #boolean true if metric. + function SETTINGS:IsMetric() + return ( self.Metric ~= nil and self.Metric == true ) or ( self.Metric == nil and _SETTINGS:IsMetric() ) + end + + --- Sets the SETTINGS imperial. + -- @param #SETTINGS self + function SETTINGS:SetImperial() + self.Metric = false + end + + --- Gets if the SETTINGS is imperial. + -- @param #SETTINGS self + -- @return #boolean true if imperial. + function SETTINGS:IsImperial() + return ( self.Metric ~= nil and self.Metric == false ) or ( self.Metric == nil and _SETTINGS:IsMetric() ) + end + + --- Sets the SETTINGS LL accuracy. + -- @param #SETTINGS self + -- @param #number LL_Accuracy + -- @return #SETTINGS + function SETTINGS:SetLL_Accuracy( LL_Accuracy ) + self.LL_Accuracy = LL_Accuracy + end + + --- Gets the SETTINGS LL accuracy. + -- @param #SETTINGS self + -- @return #number + function SETTINGS:GetLL_DDM_Accuracy() + return self.LL_DDM_Accuracy or _SETTINGS:GetLL_DDM_Accuracy() + end + + --- Sets the SETTINGS MGRS accuracy. + -- @param #SETTINGS self + -- @param #number MGRS_Accuracy + -- @return #SETTINGS + function SETTINGS:SetMGRS_Accuracy( MGRS_Accuracy ) + self.MGRS_Accuracy = MGRS_Accuracy + end + + --- Gets the SETTINGS MGRS accuracy. + -- @param #SETTINGS self + -- @return #number + function SETTINGS:GetMGRS_Accuracy() + return self.MGRS_Accuracy or _SETTINGS:GetMGRS_Accuracy() + end + + + + + --- Sets A2G LL DMS + -- @param #SETTINGS self + -- @return #SETTINGS + function SETTINGS:SetA2G_LL_DMS() + self.A2GSystem = "LL DMS" + end + + --- Sets A2G LL DDM + -- @param #SETTINGS self + -- @return #SETTINGS + function SETTINGS:SetA2G_LL_DDM() + self.A2GSystem = "LL DDM" + end + + --- Is LL DMS + -- @param #SETTINGS self + -- @return #boolean true if LL DMS + function SETTINGS:IsA2G_LL_DMS() + return ( self.A2GSystem and self.A2GSystem == "LL DMS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS() ) + end + + --- Is LL DDM + -- @param #SETTINGS self + -- @return #boolean true if LL DDM + function SETTINGS:IsA2G_LL_DDM() + return ( self.A2GSystem and self.A2GSystem == "LL DDM" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM() ) + end + + --- Sets A2G MGRS + -- @param #SETTINGS self + -- @return #SETTINGS + function SETTINGS:SetA2G_MGRS() + self.A2GSystem = "MGRS" + end + + --- Is MGRS + -- @param #SETTINGS self + -- @return #boolean true if MGRS + function SETTINGS:IsA2G_MGRS() + return ( self.A2GSystem and self.A2GSystem == "MGRS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_MGRS() ) + end + + --- Sets A2G BRA + -- @param #SETTINGS self + -- @return #SETTINGS + function SETTINGS:SetA2G_BR() + self.A2GSystem = "BR" + end + + --- Is BRA + -- @param #SETTINGS self + -- @return #boolean true if BRA + function SETTINGS:IsA2G_BR() + return ( self.A2GSystem and self.A2GSystem == "BR" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BR() ) + end + + --- Sets A2A BRA + -- @param #SETTINGS self + -- @return #SETTINGS + function SETTINGS:SetA2A_BRAA() + self.A2ASystem = "BRAA" + end + + --- Is BRA + -- @param #SETTINGS self + -- @return #boolean true if BRA + function SETTINGS:IsA2A_BRAA() + self:E( { BRA = ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() ) } ) + return ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() ) + end + + --- Sets A2A BULLS + -- @param #SETTINGS self + -- @return #SETTINGS + function SETTINGS:SetA2A_BULLS() + self.A2ASystem = "BULLS" + end + + --- Is BULLS + -- @param #SETTINGS self + -- @return #boolean true if BULLS + function SETTINGS:IsA2A_BULLS() + return ( self.A2ASystem and self.A2ASystem == "BULLS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BULLS() ) + end + + --- Sets A2A LL DMS + -- @param #SETTINGS self + -- @return #SETTINGS + function SETTINGS:SetA2A_LL_DMS() + self.A2ASystem = "LL DMS" + end + + --- Sets A2A LL DDM + -- @param #SETTINGS self + -- @return #SETTINGS + function SETTINGS:SetA2A_LL_DDM() + self.A2ASystem = "LL DDM" + end + + --- Is LL DMS + -- @param #SETTINGS self + -- @return #boolean true if LL DMS + function SETTINGS:IsA2A_LL_DMS() + return ( self.A2ASystem and self.A2ASystem == "LL DMS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS() ) + end + + --- Is LL DDM + -- @param #SETTINGS self + -- @return #boolean true if LL DDM + function SETTINGS:IsA2A_LL_DDM() + return ( self.A2ASystem and self.A2ASystem == "LL DDM" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM() ) + end + + --- Sets A2A MGRS + -- @param #SETTINGS self + -- @return #SETTINGS + function SETTINGS:SetA2A_MGRS() + self.A2ASystem = "MGRS" + end + + --- Is MGRS + -- @param #SETTINGS self + -- @return #boolean true if MGRS + function SETTINGS:IsA2A_MGRS() + return ( self.A2ASystem and self.A2ASystem == "MGRS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_MGRS() ) + end + + --- @param #SETTINGS self + -- @return #SETTINGS + function SETTINGS:SetSystemMenu( MenuGroup, RootMenu ) + + local MenuText = "System Settings" + + local MenuTime = timer.getTime() + + local SettingsMenu = MENU_GROUP:New( MenuGroup, MenuText, RootMenu ):SetTime( MenuTime ) + + local A2GCoordinateMenu = MENU_GROUP:New( MenuGroup, "A2G Coordinate System", SettingsMenu ):SetTime( MenuTime ) + + + if not self:IsA2G_LL_DMS() then + MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime ) + end + + if not self:IsA2G_LL_DDM() then + MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime ) + end + + if self:IsA2G_LL_DDM() then + MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime ) + MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime ) + MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 3", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime ) + end + + if not self:IsA2G_BR() then + MENU_GROUP_COMMAND:New( MenuGroup, "Bearing, Range (BR)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime ) + end + + if not self:IsA2G_MGRS() then + MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime ) + end + + if self:IsA2G_MGRS() then + MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 1", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime ) + MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 2", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime ) + MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 3", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime ) + MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 4", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 4 ):SetTime( MenuTime ) + MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime ) + end + + local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, "A2A Coordinate System", SettingsMenu ):SetTime( MenuTime ) + + if not self:IsA2A_LL_DMS() then + MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime ) + end + + if not self:IsA2A_LL_DDM() then + MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime ) + end + + if self:IsA2A_LL_DDM() then + MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2ACoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime ) + MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2ACoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime ) + MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 3", A2ACoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime ) + end + + if not self:IsA2A_BULLS() then + MENU_GROUP_COMMAND:New( MenuGroup, "Bullseye (BULLS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime ) + end + + if not self:IsA2A_BRAA() then + MENU_GROUP_COMMAND:New( MenuGroup, "Bearing Range Altitude Aspect (BRAA)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime ) + end + + if not self:IsA2A_MGRS() then + MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime ) + end + + if self:IsA2A_MGRS() then + MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 1", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime ) + MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 2", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime ) + MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 3", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime ) + MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 4", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 4 ):SetTime( MenuTime ) + MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime ) + end + + local MetricsMenu = MENU_GROUP:New( MenuGroup, "Measures and Weights System", SettingsMenu ):SetTime( MenuTime ) + + if self:IsMetric() then + MENU_GROUP_COMMAND:New( MenuGroup, "Imperial (Miles,Feet)", MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, false ):SetTime( MenuTime ) + end + + if self:IsImperial() then + MENU_GROUP_COMMAND:New( MenuGroup, "Metric (Kilometers,Meters)", MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, true ):SetTime( MenuTime ) + end + + SettingsMenu:Remove( MenuTime ) + + return self + end + + --- @param #SETTINGS self + -- @param RootMenu + -- @param Wrapper.Client#CLIENT PlayerUnit + -- @param #string MenuText + -- @return #SETTINGS + function SETTINGS:SetPlayerMenu( PlayerUnit ) + + local PlayerGroup = PlayerUnit:GetGroup() + local PlayerName = PlayerUnit:GetPlayerName() + local PlayerNames = PlayerGroup:GetPlayerNames() + + local PlayerMenu = MENU_GROUP:New( PlayerGroup, 'Settings "' .. PlayerName .. '"' ) + + self.PlayerMenu = PlayerMenu + + local A2GCoordinateMenu = MENU_GROUP:New( PlayerGroup, "A2G Coordinate System", PlayerMenu ) + + if not self:IsA2G_LL_DMS() then + MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" ) + end + + if not self:IsA2G_LL_DDM() then + MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" ) + end + + if self:IsA2G_LL_DDM() then + MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 ) + MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 ) + MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 3", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 ) + end + + if not self:IsA2G_BR() then + MENU_GROUP_COMMAND:New( PlayerGroup, "Bearing, Range (BR)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" ) + end + + if not self:IsA2G_MGRS() then + MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" ) + end + + if self:IsA2G_MGRS() then + MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 1", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 ) + MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 2", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 ) + MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 3", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 ) + MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 4", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 ) + MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 5", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 5 ) + end + + local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, "A2A Coordinate System", PlayerMenu ) + + + if not self:IsA2A_LL_DMS() then + MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" ) + end + + if not self:IsA2A_LL_DDM() then + MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" ) + end + + if self:IsA2A_LL_DDM() then + MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 ) + MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 ) + MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 3", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 ) + end + + if not self:IsA2A_BULLS() then + MENU_GROUP_COMMAND:New( PlayerGroup, "Bullseye (BULLS)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" ) + end + + if not self:IsA2A_BRAA() then + MENU_GROUP_COMMAND:New( PlayerGroup, "Bearing Range Altitude Aspect (BRAA)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" ) + end + + if not self:IsA2A_MGRS() then + MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" ) + end + + if self:IsA2A_MGRS() then + MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 1", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 ) + MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 2", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 ) + MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 3", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 ) + MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 4", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 ) + MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 5", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 5 ) + end + + local MetricsMenu = MENU_GROUP:New( PlayerGroup, "Measures and Weights System", PlayerMenu ) + + if self:IsMetric() then + MENU_GROUP_COMMAND:New( PlayerGroup, "Imperial (Miles,Feet)", MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false ) + end + + if self:IsImperial() then + MENU_GROUP_COMMAND:New( PlayerGroup, "Metric (Kilometers,Meters)", MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true ) + end + + return self + end + + --- @param #SETTINGS self + -- @param RootMenu + -- @param Wrapper.Client#CLIENT PlayerUnit + -- @return #SETTINGS + function SETTINGS:RemovePlayerMenu( PlayerUnit ) + + if self.PlayerMenu then + self.PlayerMenu:Remove() + end + + return self + end + + + --- @param #SETTINGS self + function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem ) + self.A2GSystem = A2GSystem + MESSAGE:New( string.format("Settings: Default A2G coordinate system set to %s for all players!.", A2GSystem ), 5 ):ToAll() + self:SetSystemMenu( MenuGroup, RootMenu ) + end + + --- @param #SETTINGS self + function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem ) + self.A2ASystem = A2ASystem + MESSAGE:New( string.format("Settings: Default A2A coordinate system set to %s for all players!.", A2ASystem ), 5 ):ToAll() + self:SetSystemMenu( MenuGroup, RootMenu ) + end + + --- @param #SETTINGS self + function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy ) + self.LL_Accuracy = LL_Accuracy + MESSAGE:New( string.format("Settings: Default LL accuracy set to %s for all players!.", LL_Accuracy ), 5 ):ToAll() + self:SetSystemMenu( MenuGroup, RootMenu ) + end + + --- @param #SETTINGS self + function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy ) + self.MGRS_Accuracy = MGRS_Accuracy + MESSAGE:New( string.format("Settings: Default MGRS accuracy set to %s for all players!.", MGRS_Accuracy ), 5 ):ToAll() + self:SetSystemMenu( MenuGroup, RootMenu ) + end + + --- @param #SETTINGS self + function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW ) + self.Metric = MW + MESSAGE:New( string.format("Settings: Default measurement format set to %s for all players!.", MW and "Metric" or "Imperial" ), 5 ):ToAll() + self:SetSystemMenu( MenuGroup, RootMenu ) + end + + do + --- @param #SETTINGS self + function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem ) + BASE:E( {self, PlayerUnit:GetName(), A2GSystem} ) + self.A2GSystem = A2GSystem + MESSAGE:New( string.format("Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup ) + self:RemovePlayerMenu(PlayerUnit) + self:SetPlayerMenu(PlayerUnit) + end + + --- @param #SETTINGS self + function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem ) + self.A2ASystem = A2ASystem + MESSAGE:New( string.format("Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup ) + self:RemovePlayerMenu(PlayerUnit) + self:SetPlayerMenu(PlayerUnit) + end + + --- @param #SETTINGS self + function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy ) + self.LL_Accuracy = LL_Accuracy + MESSAGE:New( string.format("Settings: A2G LL format accuracy set to %d for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup ) + self:RemovePlayerMenu(PlayerUnit) + self:SetPlayerMenu(PlayerUnit) + end + + --- @param #SETTINGS self + function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy ) + self.MGRS_Accuracy = MGRS_Accuracy + MESSAGE:New( string.format("Settings: A2G MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup ) + self:RemovePlayerMenu(PlayerUnit) + self:SetPlayerMenu(PlayerUnit) + end + + --- @param #SETTINGS self + function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW ) + self.Metric = MW + MESSAGE:New( string.format("Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup ) + self:RemovePlayerMenu(PlayerUnit) + self:SetPlayerMenu(PlayerUnit) + end + + end + +end + + diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua new file mode 100644 index 000000000..0d2b01109 --- /dev/null +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -0,0 +1,163 @@ +--- **Core** -- Spawn dynamically new STATICs in your missions. +-- +-- ![Banner Image](..\Presentations\SPAWNSTATIC\Dia1.JPG) +-- +-- ==== +-- +-- SPAWNSTATIC spawns static structures in your missions dynamically. See below the SPAWNSTATIC class documentation. +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [SPAWNSTATIC Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPS - Spawning Statics) +-- +-- ### [SPAWNSTATIC Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPS%20-%20Spawning%20Statics) +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [SPAWNSTATIC YouTube Channel]() +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: +-- +-- ==== +-- +-- @module SpawnStatic + + + +--- @type SPAWNSTATIC +-- @extends Core.Base#BASE + + +--- # SPAWNSTATIC class, extends @{Base#BASE} +-- +-- The SPAWNSTATIC class allows to spawn dynamically new @{Static}s. +-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME), +-- SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy" +-- these properties to create a new static object and place it at the desired coordinate. +-- +-- New spawned @{Static}s get **the same name** as the name of the template Static, +-- or gets the given name when a new name is provided at the Spawn method. +-- By default, spawned @{Static}s will follow a naming convention at run-time: +-- +-- * Spawned @{Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**, +-- and _nnn_ is a **counter from 0 to 99999**. +-- +-- +-- ## SPAWNSTATIC construction methods +-- +-- Create a new SPAWNSTATIC object with the @{#SPAWNSTATIC.NewFromStatic}(): +-- +-- * @{#SPAWNSTATIC.NewFromStatic}(): Creates a new SPAWNSTATIC object given a name that is used as the base of the naming of each spawned Static. +-- +-- ## **Spawn** methods +-- +-- Groups can be spawned at different times and methods: +-- +-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(): Spawn a new group from a POINT_VEC2 coordinate. +-- (The group will be spawned at land height ). +-- * @{#SPAWNSTATIC.SpawnFromZone}(): Spawn a new group in a @{Zone}. +-- +-- @field #SPAWNSTATIC SPAWNSTATIC +-- +SPAWNSTATIC = { + ClassName = "SPAWNSTATIC", +} + + +--- @type SPAWNSTATIC.SpawnZoneTable +-- @list SpawnZone + + +--- Creates the main object to spawn a @{Static} defined in the ME. +-- @param #SPAWNSTATIC self +-- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix. +-- @return #SPAWNSTATIC +function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, CountryID ) --R2.1 + local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC + self:F( { SpawnTemplatePrefix } ) + + local TemplateStatic = StaticObject.getByName( SpawnTemplatePrefix ) + if TemplateStatic then + self.SpawnTemplatePrefix = SpawnTemplatePrefix + self.CountryID = CountryID + self.SpawnIndex = 0 + else + error( "SPAWNSTATIC:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) + end + + self:SetEventPriority( 5 ) + + return self +end + +--- Creates the main object to spawn a @{Static} based on a type name. +-- @param #SPAWNSTATIC self +-- @param #string SpawnTypeName is the name of the type. +-- @return #SPAWNSTATIC +function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, CountryID ) --R2.1 + local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC + self:F( { SpawnTypeName } ) + + self.SpawnTypeName = SpawnTypeName + self.CountryID = CountryID + self.SpawnIndex = 0 + + self:SetEventPriority( 5 ) + + return self +end + + +--- Creates a new @{Static} from a POINT_VEC2. +-- @param #SPAWNSTATIC self +-- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static. +-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360. +-- @param #string (optional) The name of the new static. +-- @return #SPAWNSTATIC +function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1 + self:F( { PointVec2, Heading, NewName } ) + + local CountryName = _DATABASE.COUNTRY_NAME[self.CountryID] + + local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix ) + + StaticTemplate.x = PointVec2:GetLat() + StaticTemplate.y = PointVec2:GetLon() + + StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex ) + StaticTemplate.heading = ( Heading / 180 ) * math.pi + + StaticTemplate.CountryID = nil + StaticTemplate.CoalitionID = nil + StaticTemplate.CategoryID = nil + + local Static = coalition.addStaticObject( self.CountryID, StaticTemplate ) + + self.SpawnIndex = self.SpawnIndex + 1 + + return Static +end + +--- Creates a new @{Static} from a @{Zone}. +-- @param #SPAWNSTATIC self +-- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static. +-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360. +-- @param #string (optional) The name of the new static. +-- @return #SPAWNSTATIC +function SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName ) --R2.1 + self:F( { Zone, Heading, NewName } ) + + local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName ) + + return Static +end + diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua new file mode 100644 index 000000000..5e6eb014f --- /dev/null +++ b/Moose Development/Moose/Core/Spot.lua @@ -0,0 +1,291 @@ +--- **Core** -- Management of SPOT logistics, that can be transported from and to transportation carriers. +-- +-- ![Banner Image](..\Presentations\SPOT\Dia1.JPG) +-- +-- ==== +-- +-- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to: +-- +-- * Spot for a defined duration. +-- * wiggle the spot at the target. +-- * Provide a @{Unit} as a target, instead of a point. +-- * Implement a status machine, LaseOn, LaseOff. +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [SPOT Demo Missions source code]() +-- +-- ### [SPOT Demo Missions, only for beta testers]() +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [SPOT YouTube Channel]() +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: +-- +-- * [**Ciribob**](https://forums.eagle.ru/member.php?u=112175): Showing the way how to lase targets + how laser codes work!!! Explained the autolase script. +-- * [**EasyEB**](https://forums.eagle.ru/member.php?u=112055): Ideas and Beta Testing +-- * [**Wingthor**](https://forums.eagle.ru/member.php?u=123698): Beta Testing +-- +-- ==== +-- +-- @module Spot + + +do + + --- @type SPOT + -- @extends Core.Fsm#FSM + + + --- # SPOT class, extends @{Fsm#FSM} + -- + -- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to: + -- + -- * Mark targets for a defined duration. + -- * wiggle the spot at the target. + -- * Provide a @{Unit} as a target, instead of a point. + -- * Implement a status machine, LaseOn, LaseOff. + -- + -- ## 1. SPOT constructor + -- + -- * @{#SPOT.New}(..\Presentations\SPOT\Dia2.JPG): Creates a new SPOT object. + -- + -- ## 2. SPOT is a FSM + -- + -- ![Process]() + -- + -- ### 2.1 SPOT States + -- + -- * **Off**: Lasing is switched off. + -- * **On**: Lasing is switched on. + -- * **Destroyed**: Target is destroyed. + -- + -- ### 2.2 SPOT Events + -- + -- * **@{#SPOT.LaseOn}(Target, LaserCode, Duration)**: Lase to a target. + -- * **@{#SPOT.LaseOff}()**: Stop lasing the target. + -- * **@{#SPOT.Lasing}()**: Target is being lased. + -- * **@{#SPOT.Destroyed}()**: Triggered when target is destroyed. + -- + -- ## 3. Check if a Target is being lased + -- + -- The method @{#SPOT.IsLasing}() indicates whether lasing is on or off. + -- + -- @field #SPOT + SPOT = { + ClassName = "SPOT", + } + + --- SPOT Constructor. + -- @param #SPOT self + -- @param Wrapper.Unit#UNIT Recce + -- @param #number LaserCode + -- @param #number Duration + -- @return #SPOT + function SPOT:New( Recce ) + + local self = BASE:Inherit( self, FSM:New() ) -- #SPOT + self:F( {} ) + + self:SetStartState( "Off" ) + self:AddTransition( "Off", "LaseOn", "On" ) + + --- LaseOn Handler OnBefore for SPOT + -- @function [parent=#SPOT] OnBeforeLaseOn + -- @param #SPOT self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- LaseOn Handler OnAfter for SPOT + -- @function [parent=#SPOT] OnAfterLaseOn + -- @param #SPOT self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- LaseOn Trigger for SPOT + -- @function [parent=#SPOT] LaseOn + -- @param #SPOT self + + --- LaseOn Asynchronous Trigger for SPOT + -- @function [parent=#SPOT] __LaseOn + -- @param #SPOT self + -- @param #number Delay + + + + self:AddTransition( "On", "Lasing", "On" ) + self:AddTransition( { "On", "Destroyed" } , "LaseOff", "Off" ) + + --- LaseOff Handler OnBefore for SPOT + -- @function [parent=#SPOT] OnBeforeLaseOff + -- @param #SPOT self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- LaseOff Handler OnAfter for SPOT + -- @function [parent=#SPOT] OnAfterLaseOff + -- @param #SPOT self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- LaseOff Trigger for SPOT + -- @function [parent=#SPOT] LaseOff + -- @param #SPOT self + + --- LaseOff Asynchronous Trigger for SPOT + -- @function [parent=#SPOT] __LaseOff + -- @param #SPOT self + -- @param #number Delay + + self:AddTransition( "*" , "Destroyed", "Destroyed" ) + + --- Destroyed Handler OnBefore for SPOT + -- @function [parent=#SPOT] OnBeforeDestroyed + -- @param #SPOT self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Destroyed Handler OnAfter for SPOT + -- @function [parent=#SPOT] OnAfterDestroyed + -- @param #SPOT self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Destroyed Trigger for SPOT + -- @function [parent=#SPOT] Destroyed + -- @param #SPOT self + + --- Destroyed Asynchronous Trigger for SPOT + -- @function [parent=#SPOT] __Destroyed + -- @param #SPOT self + -- @param #number Delay + + + + self.Recce = Recce + + self.LaseScheduler = SCHEDULER:New( self ) + + self:SetEventPriority( 5 ) + + self.Lasing = false + + return self + end + + --- @param #SPOT self + -- @param From + -- @param Event + -- @param To + -- @param Wrapper.Positionable#POSITIONABLE Target + -- @param #number LaserCode + -- @param #number Duration + function SPOT:onafterLaseOn( From, Event, To, Target, LaserCode, Duration ) + self:E( { "LaseOn", Target, LaserCode, Duration } ) + + local function StopLase( self ) + self:LaseOff() + end + + self.Target = Target + self.LaserCode = LaserCode + + self.Lasing = true + + local RecceDcsUnit = self.Recce:GetDCSObject() + + self.SpotIR = Spot.createInfraRed( RecceDcsUnit, { x = 0, y = 2, z = 0 }, Target:GetPointVec3():AddY(1):GetVec3() ) + self.SpotLaser = Spot.createLaser( RecceDcsUnit, { x = 0, y = 2, z = 0 }, Target:GetPointVec3():AddY(1):GetVec3(), LaserCode ) + + if Duration then + self.ScheduleID = self.LaseScheduler:Schedule( self, StopLase, {self}, Duration ) + end + + self:HandleEvent( EVENTS.Dead ) + + self:__Lasing( -1 ) + end + + --- @param #SPOT self + -- @param Core.Event#EVENTDATA EventData + function SPOT:OnEventDead(EventData) + self:E( { Dead = EventData.IniDCSUnitName, Target = self.Target } ) + if self.Target then + if EventData.IniDCSUnitName == self.Target:GetName() then + self:E( {"Target dead ", self.Target:GetName() } ) + self:Destroyed() + self:LaseOff() + end + end + end + + --- @param #SPOT self + -- @param From + -- @param Event + -- @param To + function SPOT:onafterLasing( From, Event, To ) + + if self.Target:IsAlive() then + self.SpotIR:setPoint( self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/100):AddX(math.random(-100,100)/100):GetVec3() ) + self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() ) + self:__Lasing( -0.2 ) + else + self:E( { "Target is not alive", self.Target:IsAlive() } ) + end + + end + + --- @param #SPOT self + -- @param From + -- @param Event + -- @param To + -- @return #SPOT + function SPOT:onafterLaseOff( From, Event, To ) + + self:E( {"Stopped lasing for ", self.Target:GetName() , SpotIR = self.SportIR, SpotLaser = self.SpotLaser } ) + + self.Lasing = false + + self.SpotIR:destroy() + self.SpotLaser:destroy() + + self.SpotIR = nil + self.SpotLaser = nil + + if self.ScheduleID then + self.LaseScheduler:Stop(self.ScheduleID) + end + self.ScheduleID = nil + + self.Target = nil + + return self + end + + --- Check if the SPOT is lasing + -- @param #SPOT self + -- @return #boolean true if it is lasing + function SPOT:IsLasing() + return self.Lasing + end + +end \ No newline at end of file diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 432a360a7..aa061a94f 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1,8 +1,8 @@ ---- **Core** - ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**. +--- **Core** -- ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**. -- -- ![Banner Image](..\Presentations\ZONE\Dia1.JPG) -- --- === +-- ==== -- -- There are essentially two core functions that zones accomodate: -- @@ -27,87 +27,63 @@ -- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. -- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. -- --- === +-- ==== -- --- # **API CHANGE HISTORY** +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: -- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-02-28: ZONE\_BASE:**IsVec2InZone()** replaces ZONE\_BASE:_IsPointVec2InZone()_. --- 2017-02-28: ZONE\_BASE:**IsVec3InZone()** replaces ZONE\_BASE:_IsPointVec3InZone()_. --- 2017-02-28: ZONE\_RADIUS:**IsVec2InZone()** replaces ZONE\_RADIUS:_IsPointVec2InZone()_. --- 2017-02-28: ZONE\_RADIUS:**IsVec3InZone()** replaces ZONE\_RADIUS:_IsPointVec3InZone()_. --- 2017-02-28: ZONE\_POLYGON:**IsVec2InZone()** replaces ZONE\_POLYGON:_IsPointVec2InZone()_. --- 2017-02-28: ZONE\_POLYGON:**IsVec3InZone()** replaces ZONE\_POLYGON:_IsPointVec3InZone()_. --- --- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec2()** added. --- --- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec3()** added. --- --- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec3( inner, outer )** added. --- --- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec2( inner, outer )** added. --- --- 2016-08-15: ZONE\_BASE:**GetName()** added. --- --- 2016-08-15: ZONE\_BASE:**SetZoneProbability( ZoneProbability )** added. --- --- 2016-08-15: ZONE\_BASE:**GetZoneProbability()** added. --- --- 2016-08-15: ZONE\_BASE:**GetZoneMaybe()** added. --- --- === +-- ==== -- -- @module Zone ---- The ZONE_BASE class --- @type ZONE_BASE +--- @type ZONE_BASE -- @field #string ZoneName Name of the zone. -- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. -- @extends Core.Base#BASE ---- # 1) ZONE_BASE class, extends @{Base#BASE} +--- # ZONE_BASE class, extends @{Base#BASE} -- -- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. -- --- ## 1.1) Each zone has a name: +-- ## Each zone has a name: -- -- * @{#ZONE_BASE.GetName}(): Returns the name of the zone. -- --- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: +-- ## Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: -- --- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone. --- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone. +-- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a 2D vector is within the zone. +-- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a 3D vector is within the zone. +-- * @{#ZONE_BASE.IsPointVec2InZone}(): Returns if a 2D point vector is within the zone. +-- * @{#ZONE_BASE.IsPointVec3InZone}(): Returns if a 3D point vector is within the zone. -- --- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones: +-- ## A zone has a probability factor that can be set to randomize a selection between zones: -- --- * @{#ZONE_BASE.SetRandomizeProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% ) --- * @{#ZONE_BASE.GetRandomizeProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% ) +-- * @{#ZONE_BASE.SetZoneProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% ) +-- * @{#ZONE_BASE.GetZoneProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% ) -- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate. -- --- ## 1.4) A zone manages Vectors: +-- ## A zone manages vectors: -- --- * @{#ZONE_BASE.GetVec2}(): Returns the @{DCSTypes#Vec2} coordinate of the zone. --- * @{#ZONE_BASE.GetRandomVec2}(): Define a random @{DCSTypes#Vec2} within the zone. +-- * @{#ZONE_BASE.GetVec2}(): Returns the 2D vector coordinate of the zone. +-- * @{#ZONE_BASE.GetVec3}(): Returns the 3D vector coordinate of the zone. +-- * @{#ZONE_BASE.GetPointVec2}(): Returns the 2D point vector coordinate of the zone. +-- * @{#ZONE_BASE.GetPointVec3}(): Returns the 3D point vector coordinate of the zone. +-- * @{#ZONE_BASE.GetRandomVec2}(): Define a random 2D vector within the zone. +-- * @{#ZONE_BASE.GetRandomPointVec2}(): Define a random 2D point vector within the zone. +-- * @{#ZONE_BASE.GetRandomPointVec3}(): Define a random 3D point vector within the zone. -- --- ## 1.5) A zone has a bounding square: +-- ## A zone has a bounding square: -- -- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone. -- --- ## 1.6) A zone can be marked: +-- ## A zone can be marked: -- -- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color. -- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color. -- --- === --- @field #ZONE_BASE ZONE_BASE +-- @field #ZONE_BASE ZONE_BASE = { ClassName = "ZONE_BASE", ZoneName = "", @@ -144,20 +120,21 @@ function ZONE_BASE:GetName() return self.ZoneName end ---- Returns if a location is within the zone. + +--- Returns if a Vec2 is within the zone. -- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. --- @return #boolean true if the location is within the zone. +-- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 to test. +-- @return #boolean true if the Vec2 is within the zone. function ZONE_BASE:IsVec2InZone( Vec2 ) self:F2( Vec2 ) return false end ---- Returns if a point is within the zone. +--- Returns if a Vec3 is within the zone. -- @param #ZONE_BASE self -- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. --- @return #boolean true if the point is within the zone. +-- @return #boolean true if the Vec3 is within the zone. function ZONE_BASE:IsVec3InZone( Vec3 ) self:F2( Vec3 ) @@ -166,6 +143,31 @@ function ZONE_BASE:IsVec3InZone( Vec3 ) return InZone end +--- Returns if a PointVec2 is within the zone. +-- @param #ZONE_BASE self +-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to test. +-- @return #boolean true if the PointVec2 is within the zone. +function ZONE_BASE:IsPointVec2InZone( PointVec2 ) + self:F2( PointVec2 ) + + local InZone = self:IsVec2InZone( PointVec2:GetVec2() ) + + return InZone +end + +--- Returns if a PointVec3 is within the zone. +-- @param #ZONE_BASE self +-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 to test. +-- @return #boolean true if the PointVec3 is within the zone. +function ZONE_BASE:IsPointVec3InZone( PointVec3 ) + self:F2( PointVec3 ) + + local InZone = self:IsPointVec2InZone( PointVec3 ) + + return InZone +end + + --- Returns the @{DCSTypes#Vec2} coordinate of the zone. -- @param #ZONE_BASE self -- @return #nil. @@ -192,6 +194,22 @@ function ZONE_BASE:GetPointVec2() end +--- Returns a @{Point#COORDINATE} of the zone. +-- @param #ZONE_BASE self +-- @return Core.Point#COORDINATE The Coordinate of the zone. +function ZONE_BASE:GetCoordinate() + self:F2( self.ZoneName ) + + local Vec2 = self:GetVec2() + + local Coordinate = COORDINATE:NewFromVec2( Vec2 ) + + self:T2( { Coordinate } ) + + return Coordinate +end + + --- Returns the @{DCSTypes#Vec3} of the zone. -- @param #ZONE_BASE self -- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. @@ -203,7 +221,7 @@ function ZONE_BASE:GetVec3( Height ) local Vec2 = self:GetVec2() - local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } + local Vec3 = { x = Vec2.x, y = Height and Height or land.getHeight( self:GetVec2() ), z = Vec2.y } self:T2( { Vec3 } ) @@ -226,6 +244,22 @@ function ZONE_BASE:GetPointVec3( Height ) return PointVec3 end +--- Returns a @{Point#COORDINATE} of the zone. +-- @param #ZONE_BASE self +-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @return Core.Point#COORDINATE The Coordinate of the zone. +function ZONE_BASE:GetCoordinate( Height ) --R2.1 + self:F2( self.ZoneName ) + + local Vec3 = self:GetVec3( Height ) + + local PointVec3 = COORDINATE:NewFromVec3( Vec3 ) + + self:T2( { PointVec3 } ) + + return PointVec3 +end + --- Define a random @{DCSTypes#Vec2} within the zone. -- @param #ZONE_BASE self @@ -310,29 +344,29 @@ end -- @type ZONE_RADIUS -- @field Dcs.DCSTypes#Vec2 Vec2 The current location of the zone. -- @field Dcs.DCSTypes#Distance Radius The radius of the zone. --- @extends Core.Zone#ZONE_BASE +-- @extends #ZONE_BASE ---- # 2) @{Zone#ZONE_RADIUS} class, extends @{Zone#ZONE_BASE} +--- # ZONE_RADIUS class, extends @{Zone#ZONE_BASE} -- -- The ZONE_RADIUS class defined by a zone name, a location and a radius. -- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties. -- --- ## 2.1) @{Zone#ZONE_RADIUS} constructor +-- ## ZONE_RADIUS constructor -- -- * @{#ZONE_RADIUS.New}(): Constructor. -- --- ## 2.2) Manage the radius of the zone +-- ## Manage the radius of the zone -- -- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone. -- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone. -- --- ## 2.3) Manage the location of the zone +-- ## Manage the location of the zone -- -- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone. -- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone. -- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter. -- --- ## 2.4) Zone point randomization +-- ## Zone point randomization -- -- Various functions exist to find random points within the zone. -- @@ -340,10 +374,7 @@ end -- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone. -- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight. -- --- === --- --- @field #ZONE_RADIUS ZONE_RADIUS --- +-- @field #ZONE_RADIUS ZONE_RADIUS = { ClassName="ZONE_RADIUS", } @@ -615,19 +646,16 @@ end --- @type ZONE --- @extends Core.Zone#ZONE_RADIUS +--- @type ZONE +-- @extends #ZONE_RADIUS ---- # 3) ZONE class, extends @{Zone#ZONE_RADIUS} +--- # ZONE class, extends @{Zone#ZONE_RADIUS} -- -- The ZONE class, defined by the zone name as defined within the Mission Editor. -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. -- --- === --- --- @field #ZONE ZONE --- +-- @field #ZONE ZONE = { ClassName="ZONE", } @@ -655,20 +683,16 @@ function ZONE:New( ZoneName ) end ---- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. --- @type ZONE_UNIT +--- @type ZONE_UNIT -- @field Wrapper.Unit#UNIT ZoneUNIT -- @extends Core.Zone#ZONE_RADIUS ---- # 4) #ZONE_UNIT class, extends @{Zone#ZONE_RADIUS} +--- # ZONE_UNIT class, extends @{Zone#ZONE_RADIUS} -- -- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. -- --- === --- --- @field #ZONE_UNIT ZONE_UNIT --- +-- @field #ZONE_UNIT ZONE_UNIT = { ClassName="ZONE_UNIT", } @@ -694,7 +718,7 @@ end -- @param #ZONE_UNIT self -- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Unit#UNIT}location. function ZONE_UNIT:GetVec2() - self:F( self.ZoneName ) + self:F2( self.ZoneName ) local ZoneVec2 = self.ZoneUNIT:GetVec2() if ZoneVec2 then @@ -704,7 +728,7 @@ function ZONE_UNIT:GetVec2() return self.LastVec2 end - self:T( { ZoneVec2 } ) + self:T2( { ZoneVec2 } ) return nil end @@ -750,19 +774,15 @@ function ZONE_UNIT:GetVec3( Height ) end --- @type ZONE_GROUP --- @field Wrapper.Group#GROUP ZoneGROUP --- @extends Core.Zone#ZONE_RADIUS +-- @extends #ZONE_RADIUS ---- # 5) #ZONE_GROUP class, extends @{Zone#ZONE_RADIUS} +--- # ZONE_GROUP class, extends @{Zone#ZONE_RADIUS} -- -- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone. -- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- --- === --- --- @field #ZONE_GROUP ZONE_GROUP --- +-- @field #ZONE_GROUP ZONE_GROUP = { ClassName="ZONE_GROUP", } @@ -777,7 +797,7 @@ function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius ) local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneGROUP:GetVec2(), Radius ) ) self:F( { ZoneName, ZoneGROUP:GetVec2(), Radius } ) - self.ZoneGROUP = ZoneGROUP + self._.ZoneGROUP = ZoneGROUP return self end @@ -789,7 +809,7 @@ end function ZONE_GROUP:GetVec2() self:F( self.ZoneName ) - local ZoneVec2 = self.ZoneGROUP:GetVec2() + local ZoneVec2 = self._.ZoneGROUP:GetVec2() self:T( { ZoneVec2 } ) @@ -803,7 +823,7 @@ function ZONE_GROUP:GetRandomVec2() self:F( self.ZoneName ) local Point = {} - local Vec2 = self.ZoneGROUP:GetVec2() + local Vec2 = self._.ZoneGROUP:GetVec2() local angle = math.random() * math.pi*2; Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius(); @@ -814,20 +834,34 @@ function ZONE_GROUP:GetRandomVec2() return Point end +--- Returns a @{Point#POINT_VEC2} object reflecting a random 2D location within the zone. +-- @param #ZONE_GROUP self +-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. +-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. +-- @return Core.Point#POINT_VEC2 The @{Point#POINT_VEC2} object reflecting the random 3D location within the zone. +function ZONE_GROUP:GetRandomPointVec2( inner, outer ) + self:F( self.ZoneName, inner, outer ) + + local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() ) + + self:T3( { PointVec2 } ) + + return PointVec2 +end --- @type ZONE_POLYGON_BASE --- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCSTypes#Vec2}. --- @extends Core.Zone#ZONE_BASE +-- --@field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCSTypes#Vec2}. +-- @extends #ZONE_BASE ---- # 6) ZONE_POLYGON_BASE class, extends @{Zone#ZONE_BASE} +--- # ZONE_POLYGON_BASE class, extends @{Zone#ZONE_BASE} -- -- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. -- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. -- --- ## 6.1) Zone point randomization +-- ## Zone point randomization -- -- Various functions exist to find random points within the zone. -- @@ -835,10 +869,7 @@ end -- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone. -- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. -- --- === --- --- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE --- +-- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE = { ClassName="ZONE_POLYGON_BASE", } @@ -859,24 +890,35 @@ function ZONE_POLYGON_BASE:New( ZoneName, PointsArray ) local i = 0 - self.Polygon = {} + self._.Polygon = {} for i = 1, #PointsArray do - self.Polygon[i] = {} - self.Polygon[i].x = PointsArray[i].x - self.Polygon[i].y = PointsArray[i].y + self._.Polygon[i] = {} + self._.Polygon[i].x = PointsArray[i].x + self._.Polygon[i].y = PointsArray[i].y end return self end +--- Returns the center location of the polygon. +-- @param #ZONE_GROUP self +-- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Group} location. +function ZONE_POLYGON_BASE:GetVec2() + self:F( self.ZoneName ) + + local Bounds = self:GetBoundingSquare() + + return { x = ( Bounds.x2 + Bounds.x1 ) / 2, y = ( Bounds.y2 + Bounds.y1 ) / 2 } +end + --- Flush polygon coordinates as a table in DCS.log. -- @param #ZONE_POLYGON_BASE self -- @return #ZONE_POLYGON_BASE self function ZONE_POLYGON_BASE:Flush() self:F2() - self:E( { Polygon = self.ZoneName, Coordinates = self.Polygon } ) + self:E( { Polygon = self.ZoneName, Coordinates = self._.Polygon } ) return self end @@ -892,17 +934,17 @@ function ZONE_POLYGON_BASE:BoundZone( UnBound ) local Segments = 10 i = 1 - j = #self.Polygon + j = #self._.Polygon - while i <= #self.Polygon do - self:T( { i, j, self.Polygon[i], self.Polygon[j] } ) + while i <= #self._.Polygon do + self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } ) - local DeltaX = self.Polygon[j].x - self.Polygon[i].x - local DeltaY = self.Polygon[j].y - self.Polygon[i].y + local DeltaX = self._.Polygon[j].x - self._.Polygon[i].x + local DeltaY = self._.Polygon[j].y - self._.Polygon[i].y for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line. - local PointX = self.Polygon[i].x + ( Segment * DeltaX / Segments ) - local PointY = self.Polygon[i].y + ( Segment * DeltaY / Segments ) + local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments ) + local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments ) local Tire = { ["country"] = "USA", ["category"] = "Fortifications", @@ -942,17 +984,17 @@ function ZONE_POLYGON_BASE:SmokeZone( SmokeColor ) local Segments = 10 i = 1 - j = #self.Polygon + j = #self._.Polygon - while i <= #self.Polygon do - self:T( { i, j, self.Polygon[i], self.Polygon[j] } ) + while i <= #self._.Polygon do + self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } ) - local DeltaX = self.Polygon[j].x - self.Polygon[i].x - local DeltaY = self.Polygon[j].y - self.Polygon[i].y + local DeltaX = self._.Polygon[j].x - self._.Polygon[i].x + local DeltaY = self._.Polygon[j].y - self._.Polygon[i].y for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line. - local PointX = self.Polygon[i].x + ( Segment * DeltaX / Segments ) - local PointY = self.Polygon[i].y + ( Segment * DeltaY / Segments ) + local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments ) + local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments ) POINT_VEC2:New( PointX, PointY ):Smoke( SmokeColor ) end j = i @@ -978,12 +1020,12 @@ function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) local InPolygon = false Next = 1 - Prev = #self.Polygon + Prev = #self._.Polygon - while Next <= #self.Polygon do - self:T( { Next, Prev, self.Polygon[Next], self.Polygon[Prev] } ) - if ( ( ( self.Polygon[Next].y > Vec2.y ) ~= ( self.Polygon[Prev].y > Vec2.y ) ) and - ( Vec2.x < ( self.Polygon[Prev].x - self.Polygon[Next].x ) * ( Vec2.y - self.Polygon[Next].y ) / ( self.Polygon[Prev].y - self.Polygon[Next].y ) + self.Polygon[Next].x ) + while Next <= #self._.Polygon do + self:T( { Next, Prev, self._.Polygon[Next], self._.Polygon[Prev] } ) + if ( ( ( self._.Polygon[Next].y > Vec2.y ) ~= ( self._.Polygon[Prev].y > Vec2.y ) ) and + ( Vec2.x < ( self._.Polygon[Prev].x - self._.Polygon[Next].x ) * ( Vec2.y - self._.Polygon[Next].y ) / ( self._.Polygon[Prev].y - self._.Polygon[Next].y ) + self._.Polygon[Next].x ) ) then InPolygon = not InPolygon end @@ -1054,17 +1096,17 @@ end -- @return #ZONE_POLYGON_BASE.BoundingSquare The bounding square. function ZONE_POLYGON_BASE:GetBoundingSquare() - local x1 = self.Polygon[1].x - local y1 = self.Polygon[1].y - local x2 = self.Polygon[1].x - local y2 = self.Polygon[1].y + local x1 = self._.Polygon[1].x + local y1 = self._.Polygon[1].y + local x2 = self._.Polygon[1].x + local y2 = self._.Polygon[1].y - for i = 2, #self.Polygon do - self:T2( { self.Polygon[i], x1, y1, x2, y2 } ) - x1 = ( x1 > self.Polygon[i].x ) and self.Polygon[i].x or x1 - x2 = ( x2 < self.Polygon[i].x ) and self.Polygon[i].x or x2 - y1 = ( y1 > self.Polygon[i].y ) and self.Polygon[i].y or y1 - y2 = ( y2 < self.Polygon[i].y ) and self.Polygon[i].y or y2 + for i = 2, #self._.Polygon do + self:T2( { self._.Polygon[i], x1, y1, x2, y2 } ) + x1 = ( x1 > self._.Polygon[i].x ) and self._.Polygon[i].x or x1 + x2 = ( x2 < self._.Polygon[i].x ) and self._.Polygon[i].x or x2 + y1 = ( y1 > self._.Polygon[i].y ) and self._.Polygon[i].y or y1 + y2 = ( y2 < self._.Polygon[i].y ) and self._.Polygon[i].y or y2 end @@ -1073,18 +1115,15 @@ end --- @type ZONE_POLYGON --- @extends Core.Zone#ZONE_POLYGON_BASE +-- @extends #ZONE_POLYGON_BASE ---- # 7) ZONE_POLYGON class, extends @{Zone#ZONE_POLYGON_BASE} +--- # ZONE_POLYGON class, extends @{Zone#ZONE_POLYGON_BASE} -- -- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. -- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- --- === --- --- @field #ZONE_POLYGON ZONE_POLYGON --- +-- @field #ZONE_POLYGON ZONE_POLYGON = { ClassName="ZONE_POLYGON", } @@ -1100,7 +1139,7 @@ function ZONE_POLYGON:New( ZoneName, ZoneGroup ) local GroupPoints = ZoneGroup:GetTaskRoute() local self = BASE:Inherit( self, ZONE_POLYGON_BASE:New( ZoneName, GroupPoints ) ) - self:F( { ZoneName, ZoneGroup, self.Polygon } ) + self:F( { ZoneName, ZoneGroup, self._.Polygon } ) return self end diff --git a/Moose Development/Moose/Functional/AirbasePolice.lua b/Moose Development/Moose/Functional/AirbasePolice.lua index 1afb020ac..1d628db80 100644 --- a/Moose Development/Moose/Functional/AirbasePolice.lua +++ b/Moose Development/Moose/Functional/AirbasePolice.lua @@ -1,4 +1,4 @@ ---- This module contains the AIRBASEPOLICE classes. +--- **Functional** -- This module monitors airbases traffic. -- -- === -- diff --git a/Moose Development/Moose/Functional/CleanUp.lua b/Moose Development/Moose/Functional/CleanUp.lua index 25c8742db..50c3851f1 100644 --- a/Moose Development/Moose/Functional/CleanUp.lua +++ b/Moose Development/Moose/Functional/CleanUp.lua @@ -1,135 +1,234 @@ ---- The CLEANUP class keeps an area clean of crashing or colliding airplanes. It also prevents airplanes from firing within this area. +--- **Functional** -- The CLEANUP_AIRBASE class keeps an area clean of crashing or colliding airplanes. It also prevents airplanes from firing within this area. +-- +-- === +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: +-- +-- ==== +-- -- @module CleanUp --- @author Flightcontrol - - - - - - ---- The CLEANUP class. --- @type CLEANUP +--- @type CLEANUP_AIRBASE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-) +-- @field #map<#string,Wrapper.Airbase#AIRBASE> Airbases Map of Airbases. -- @extends Core.Base#BASE -CLEANUP = { - ClassName = "CLEANUP", - ZoneNames = {}, - TimeInterval = 300, + +--- @type CLEANUP_AIRBASE +-- @extends #CLEANUP_AIRBASE.__ + +--- # CLEANUP_AIRBASE, extends @{Base#BASE} +-- +-- ![Banner Image](..\Presentations\CLEANUP_AIRBASE\Dia1.JPG) +-- +-- The CLEANUP_AIRBASE class keeps airbases clean, and tries to guarantee continuous airbase operations, even under combat. +-- Specific airbases need to be provided that need to be guarded. Each airbase registered, will be guarded within a zone of 8 km around the airbase. +-- Any unit that fires a missile, or shoots within the zone of an airbase, will be monitored by CLEANUP_AIRBASE. +-- Within the 8km zone, units cannot fire any missile, which prevents the airbase runway to receive missile or bomb hits. +-- Any airborne or ground unit that is on the runway below 30 meters (default value) will be automatically removed if it is damaged. +-- +-- This is not a full 100% secure implementation. It is still possible that CLEANUP_AIRBASE cannot prevent (in-time) to keep the airbase clean. +-- The following situations may happen that will still stop the runway of an airbase: +-- +-- * A damaged unit is not removed on time when above the runway, and crashes on the runway. +-- * A bomb or missile is still able to dropped on the runway. +-- * Units collide on the airbase, and could not be removed on time. +-- +-- When a unit is within the airbase zone and needs to be monitored, +-- its status will be checked every 0.25 seconds! This is required to ensure that the airbase is kept clean. +-- But as a result, there is more CPU overload. +-- +-- So as an advise, I suggest you use the CLEANUP_AIRBASE class with care: +-- +-- * Only monitor airbases that really need to be monitored! +-- * Try not to monitor airbases that are likely to be invaded by enemy troops. +-- For these airbases, there is little use to keep them clean, as they will be invaded anyway... +-- +-- By following the above guidelines, you can add airbase cleanup with acceptable CPU overhead. +-- +-- ## 1. CLEANUP_AIRBASE Constructor +-- +-- Creates the main object which is preventing the airbase to get polluted with debris on the runway, which halts the airbase. +-- +-- -- Clean these Zones. +-- CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi ) +-- +-- -- or +-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi ) +-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi ) +-- +-- ## 2. Add or Remove airbases +-- +-- The method @{#CLEANUP_AIRBASE.AddAirbase}() to add an airbase to the cleanup validation process. +-- The method @{#CLEANUP_AIRBASE.RemoveAirbase}() removes an airbase from the cleanup validation process. +-- +-- ## 3. Clean missiles and bombs within the airbase zone. +-- +-- When missiles or bombs hit the runway, the airbase operations stop. +-- Use the method @{#CLEANUP_AIRBASE.SetCleanMissiles}() to control the cleaning of missiles, which will prevent airbases to stop. +-- Note that this method will not allow anymore airbases to be attacked, so there is a trade-off here to do. +-- +-- @field #CLEANUP_AIRBASE +CLEANUP_AIRBASE = { + ClassName = "CLEANUP_AIRBASE", + TimeInterval = 0.2, CleanUpList = {}, } +-- @field #CLEANUP_AIRBASE.__ +CLEANUP_AIRBASE.__ = {} + +--- @field #CLEANUP_AIRBASE.__.Airbases +CLEANUP_AIRBASE.__.Airbases = {} + --- Creates the main object which is handling the cleaning of the debris within the given Zone Names. --- @param #CLEANUP self --- @param #table ZoneNames Is a table of zone names where the debris should be cleaned. Also a single string can be passed with one zone name. --- @param #number TimeInterval The interval in seconds when the clean activity takes place. The default is 300 seconds, thus every 5 minutes. --- @return #CLEANUP +-- @param #CLEANUP_AIRBASE self +-- @param #list<#string> AirbaseNames Is a table of airbase names where the debris should be cleaned. Also a single string can be passed with one airbase name. +-- @return #CLEANUP_AIRBASE -- @usage -- -- Clean these Zones. --- CleanUpAirports = CLEANUP:New( { 'CLEAN Tbilisi', 'CLEAN Kutaisi' }, 150 ) +-- CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi ) -- or --- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 ) --- CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 ) -function CLEANUP:New( ZoneNames, TimeInterval ) +-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi ) +-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi ) +function CLEANUP_AIRBASE:New( AirbaseNames ) - local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP - self:F( { ZoneNames, TimeInterval } ) + local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP_AIRBASE + self:F( { AirbaseNames } ) - if type( ZoneNames ) == 'table' then - self.ZoneNames = ZoneNames + if type( AirbaseNames ) == 'table' then + for AirbaseID, AirbaseName in pairs( AirbaseNames ) do + self:AddAirbase( AirbaseName ) + end else - self.ZoneNames = { ZoneNames } - end - if TimeInterval then - self.TimeInterval = TimeInterval + local AirbaseName = AirbaseNames + self:AddAirbase( AirbaseName ) end - self:HandleEvent( EVENTS.Birth ) + self:HandleEvent( EVENTS.Birth, self.__.OnEventBirth ) - self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval ) + self.__.CleanUpScheduler = SCHEDULER:New( self, self.__.CleanUpSchedule, {}, 1, self.TimeInterval ) + + self:HandleEvent( EVENTS.EngineShutdown , self.__.EventAddForCleanUp ) + self:HandleEvent( EVENTS.EngineStartup, self.__.EventAddForCleanUp ) + self:HandleEvent( EVENTS.Hit, self.__.EventAddForCleanUp ) + self:HandleEvent( EVENTS.PilotDead, self.__.OnEventCrash ) + self:HandleEvent( EVENTS.Dead, self.__.OnEventCrash ) + self:HandleEvent( EVENTS.Crash, self.__.OnEventCrash ) return self end - ---- Destroys a group from the simulator, but checks first if it is still existing! --- @param #CLEANUP self --- @param Dcs.DCSWrapper.Group#Group GroupObject The object to be destroyed. --- @param #string CleanUpGroupName The groupname... -function CLEANUP:_DestroyGroup( GroupObject, CleanUpGroupName ) - self:F( { GroupObject, CleanUpGroupName } ) - - if GroupObject then -- and GroupObject:isExist() then - trigger.action.deactivateGroup(GroupObject) - self:T( { "GroupObject Destroyed", GroupObject } ) - end +--- Adds an airbase to the airbase validation list. +-- @param #CLEANUP_AIRBASE self +-- @param #string AirbaseName +-- @return #CLEANUP_AIRBASE +function CLEANUP_AIRBASE:AddAirbase( AirbaseName ) + self.__.Airbases[AirbaseName] = AIRBASE:FindByName( AirbaseName ) + self:F({"Airbase:", AirbaseName, self.__.Airbases[AirbaseName]:GetDesc()}) + + return self end ---- Destroys a @{DCSWrapper.Unit#Unit} from the simulator, but checks first if it is still existing! --- @param #CLEANUP self --- @param Dcs.DCSWrapper.Unit#Unit CleanUpUnit The object to be destroyed. --- @param #string CleanUpUnitName The Unit name ... -function CLEANUP:_DestroyUnit( CleanUpUnit, CleanUpUnitName ) - self:F( { CleanUpUnit, CleanUpUnitName } ) +--- Removes an airbase from the airbase validation list. +-- @param #CLEANUP_AIRBASE self +-- @param #string AirbaseName +-- @return #CLEANUP_AIRBASE +function CLEANUP_AIRBASE:RemoveAirbase( AirbaseName ) + self.__.Airbases[AirbaseName] = nil + return self +end + +--- Enables or disables the cleaning of missiles within the airbase zones. +-- Airbase operations stop when a missile or bomb is dropped at a runway. +-- Note that when this method is used, the airbase operations won't stop if +-- the missile or bomb was cleaned within the airbase zone, which is 8km from the center of the airbase. +-- However, there is a trade-off to make. Attacks on airbases won't be possible anymore if this method is used. +-- Note, one can also use the method @{#CLEANUP_AIRBASE.RemoveAirbase}() to remove the airbase from the control process as a whole, +-- when an enemy unit is near. That is also an option... +-- @param #CLEANUP_AIRBASE self +-- @param #string CleanMissiles (Default=true) If true, missiles fired are immediately destroyed. If false missiles are not controlled. +-- @return #CLEANUP_AIRBASE +function CLEANUP_AIRBASE:SetCleanMissiles( CleanMissiles ) + + if CleanMissiles then + self:HandleEvent( EVENTS.Shot, self.__.OnEventShot ) + else + self:UnHandleEvent( EVENTS.Shot ) + end +end + +function CLEANUP_AIRBASE.__:IsInAirbase( Vec2 ) + + local InAirbase = false + for AirbaseName, Airbase in pairs( self.__.Airbases ) do + local Airbase = Airbase -- Wrapper.Airbase#AIRBASE + if Airbase:GetZone():IsVec2InZone( Vec2 ) then + InAirbase = true + break; + end + end + + return InAirbase +end + + + +--- Destroys a @{Unit} from the simulator, but checks first if it is still existing! +-- @param #CLEANUP_AIRBASE self +-- @param Wrapper.Unit#UNIT CleanUpUnit The object to be destroyed. +function CLEANUP_AIRBASE.__:DestroyUnit( CleanUpUnit ) + self:F( { CleanUpUnit } ) if CleanUpUnit then - local CleanUpGroup = Unit.getGroup(CleanUpUnit) + local CleanUpUnitName = CleanUpUnit:GetName() + local CleanUpGroup = CleanUpUnit:GetGroup() -- TODO Client bug in 1.5.3 - if CleanUpGroup and CleanUpGroup:isExist() then - local CleanUpGroupUnits = CleanUpGroup:getUnits() + if CleanUpGroup:IsAlive() then + local CleanUpGroupUnits = CleanUpGroup:GetUnits() if #CleanUpGroupUnits == 1 then - local CleanUpGroupName = CleanUpGroup:getName() - --self:CreateEventCrash( timer.getTime(), CleanUpUnit ) - CleanUpGroup:destroy() - self:T( { "Destroyed Group:", CleanUpGroupName } ) + local CleanUpGroupName = CleanUpGroup:GetName() + CleanUpGroup:Destroy() else - CleanUpUnit:destroy() - self:T( { "Destroyed Unit:", CleanUpUnitName } ) + CleanUpUnit:Destroy() end - self.CleanUpList[CleanUpUnitName] = nil -- Cleaning from the list - CleanUpUnit = nil + self.CleanUpList[CleanUpUnitName] = nil end end end --- TODO check Dcs.DCSTypes#Weapon ---- Destroys a missile from the simulator, but checks first if it is still existing! --- @param #CLEANUP self --- @param Dcs.DCSTypes#Weapon MissileObject -function CLEANUP:_DestroyMissile( MissileObject ) - self:F( { MissileObject } ) + +--- Destroys a missile from the simulator, but checks first if it is still existing! +-- @param #CLEANUP_AIRBASE self +-- @param Dcs.DCSTypes#Weapon MissileObject +function CLEANUP_AIRBASE.__:DestroyMissile( MissileObject ) + self:F( { MissileObject } ) + if MissileObject and MissileObject:isExist() then MissileObject:destroy() self:T( "MissileObject Destroyed") end end ---- @param #CLEANUP self +--- @param #CLEANUP_AIRBASE self -- @param Core.Event#EVENTDATA EventData -function CLEANUP:_OnEventBirth( EventData ) +function CLEANUP_AIRBASE.__:OnEventBirth( EventData ) self:F( { EventData } ) self.CleanUpList[EventData.IniDCSUnitName] = {} - self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit - self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup + self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniUnit + self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniGroup self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName - EventData.IniUnit:HandleEvent( EVENTS.EngineShutdown , self._EventAddForCleanUp ) - EventData.IniUnit:HandleEvent( EVENTS.EngineStartup, self._EventAddForCleanUp ) - EventData.IniUnit:HandleEvent( EVENTS.Hit, self._EventAddForCleanUp ) - EventData.IniUnit:HandleEvent( EVENTS.PilotDead, self._EventCrash ) - EventData.IniUnit:HandleEvent( EVENTS.Dead, self._EventCrash ) - EventData.IniUnit:HandleEvent( EVENTS.Crash, self._EventCrash ) - EventData.IniUnit:HandleEvent( EVENTS.Shot, self._EventShot ) - end + --- Detects if a crash event occurs. -- Crashed units go into a CleanUpList for removal. --- @param #CLEANUP self --- @param Dcs.DCSTypes#Event event -function CLEANUP:_EventCrash( Event ) +-- @param #CLEANUP_AIRBASE self +-- @param Core.Event#EVENTDATA Event +function CLEANUP_AIRBASE.__:OnEventCrash( Event ) self:F( { Event } ) --TODO: This stuff is not working due to a DCS bug. Burning units cannot be destroyed. @@ -140,171 +239,164 @@ function CLEANUP:_EventCrash( Event ) -- self:T("after deactivateGroup") -- event.initiator:destroy() - self.CleanUpList[Event.IniDCSUnitName] = {} - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName + if Event.IniDCSUnitName and Event.IniCategory == Object.Category.UNIT then + self.CleanUpList[Event.IniDCSUnitName] = {} + self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniUnit + self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniGroup + self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName + self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName + end end --- Detects if a unit shoots a missile. --- If this occurs within one of the zones, then the weapon used must be destroyed. --- @param #CLEANUP self --- @param Dcs.DCSTypes#Event event -function CLEANUP:_EventShot( Event ) +-- If this occurs within one of the airbases, then the weapon used must be destroyed. +-- @param #CLEANUP_AIRBASE self +-- @param Core.Event#EVENTDATA Event +function CLEANUP_AIRBASE.__:OnEventShot( Event ) self:F( { Event } ) - -- Test if the missile was fired within one of the CLEANUP.ZoneNames. - local CurrentLandingZoneID = 0 - CurrentLandingZoneID = routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) - if ( CurrentLandingZoneID ) then - -- Okay, the missile was fired within the CLEANUP.ZoneNames, destroy the fired weapon. - --_SEADmissile:destroy() - SCHEDULER:New( self, CLEANUP._DestroyMissile, { Event.Weapon }, 0.1 ) + -- Test if the missile was fired within one of the CLEANUP_AIRBASE.AirbaseNames. + if self:IsInAirbase( Event.IniUnit:GetVec2() ) then + -- Okay, the missile was fired within the CLEANUP_AIRBASE.AirbaseNames, destroy the fired weapon. + self:DestroyMissile( Event.Weapon ) end end - ---- Detects if the Unit has an S_EVENT_HIT within the given ZoneNames. If this is the case, destroy the unit. --- @param #CLEANUP self --- @param Dcs.DCSTypes#Event event -function CLEANUP:_EventHitCleanUp( Event ) +--- Detects if the Unit has an S_EVENT_HIT within the given AirbaseNames. If this is the case, destroy the unit. +-- @param #CLEANUP_AIRBASE self +-- @param Core.Event#EVENTDATA Event +function CLEANUP_AIRBASE.__:OnEventHit( Event ) self:F( { Event } ) - if Event.IniDCSUnit then - if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then - self:T( { "Life: ", Event.IniDCSUnitName, ' = ', Event.IniDCSUnit:getLife(), "/", Event.IniDCSUnit:getLife0() } ) - if Event.IniDCSUnit:getLife() < Event.IniDCSUnit:getLife0() then + if Event.IniUnit then + if self:IsInAirbase( Event.IniUnit:GetVec2() ) then + self:T( { "Life: ", Event.IniDCSUnitName, ' = ', Event.IniUnit:GetLife(), "/", Event.IniUnit:GetLife0() } ) + if Event.IniUnit:GetLife() < Event.IniUnit:GetLife0() then self:T( "CleanUp: Destroy: " .. Event.IniDCSUnitName ) - SCHEDULER:New( self, CLEANUP._DestroyUnit, { Event.IniDCSUnit }, 0.1 ) + CLEANUP_AIRBASE.__:DestroyUnit( Event.IniUnit ) end end end - if Event.TgtDCSUnit then - if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then - self:T( { "Life: ", Event.TgtDCSUnitName, ' = ', Event.TgtDCSUnit:getLife(), "/", Event.TgtDCSUnit:getLife0() } ) - if Event.TgtDCSUnit:getLife() < Event.TgtDCSUnit:getLife0() then + if Event.TgtUnit then + if self:IsInAirbase( Event.TgtUnit:GetVec2() ) then + self:T( { "Life: ", Event.TgtDCSUnitName, ' = ', Event.TgtUnit:GetLife(), "/", Event.TgtUnit:GetLife0() } ) + if Event.TgtUnit:GetLife() < Event.TgtUnit:GetLife0() then self:T( "CleanUp: Destroy: " .. Event.TgtDCSUnitName ) - SCHEDULER:New( self, CLEANUP._DestroyUnit, { Event.TgtDCSUnit }, 0.1 ) + CLEANUP_AIRBASE.__:DestroyUnit( Event.TgtUnit ) end end end end --- Add the @{DCSWrapper.Unit#Unit} to the CleanUpList for CleanUp. -function CLEANUP:_AddForCleanUp( CleanUpUnit, CleanUpUnitName ) +-- @param #CLEANUP_AIRBASE self +-- @param Wrapper.Unit#UNIT CleanUpUnit +-- @oaram #string CleanUpUnitName +function CLEANUP_AIRBASE.__:AddForCleanUp( CleanUpUnit, CleanUpUnitName ) self:F( { CleanUpUnit, CleanUpUnitName } ) self.CleanUpList[CleanUpUnitName] = {} self.CleanUpList[CleanUpUnitName].CleanUpUnit = CleanUpUnit self.CleanUpList[CleanUpUnitName].CleanUpUnitName = CleanUpUnitName - self.CleanUpList[CleanUpUnitName].CleanUpGroup = Unit.getGroup(CleanUpUnit) - self.CleanUpList[CleanUpUnitName].CleanUpGroupName = Unit.getGroup(CleanUpUnit):getName() + + local CleanUpGroup = CleanUpUnit:GetGroup() + + self.CleanUpList[CleanUpUnitName].CleanUpGroup = CleanUpGroup + self.CleanUpList[CleanUpUnitName].CleanUpGroupName = CleanUpGroup:GetName() self.CleanUpList[CleanUpUnitName].CleanUpTime = timer.getTime() self.CleanUpList[CleanUpUnitName].CleanUpMoved = false - self:T( { "CleanUp: Add to CleanUpList: ", Unit.getGroup(CleanUpUnit):getName(), CleanUpUnitName } ) + self:T( { "CleanUp: Add to CleanUpList: ", CleanUpGroup:GetName(), CleanUpUnitName } ) end ---- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given ZoneNames. If this is the case, add the Group to the CLEANUP List. --- @param #CLEANUP self --- @param Dcs.DCSTypes#Event event -function CLEANUP:_EventAddForCleanUp( Event ) +--- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given AirbaseNames. If this is the case, add the Group to the CLEANUP_AIRBASE List. +-- @param #CLEANUP_AIRBASE.__ self +-- @param Core.Event#EVENTDATA Event +function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event ) - if Event.IniDCSUnit then + self:F({Event}) + + + if Event.IniDCSUnit and Event.IniCategory == Object.Category.UNIT then if self.CleanUpList[Event.IniDCSUnitName] == nil then - if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then - self:_AddForCleanUp( Event.IniDCSUnit, Event.IniDCSUnitName ) + if self:IsInAirbase( Event.IniUnit:GetVec2() ) then + self:AddForCleanUp( Event.IniUnit, Event.IniDCSUnitName ) end end end - if Event.TgtDCSUnit then + if Event.TgtDCSUnit and Event.TgtCategory == Object.Category.UNIT then if self.CleanUpList[Event.TgtDCSUnitName] == nil then - if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then - self:_AddForCleanUp( Event.TgtDCSUnit, Event.TgtDCSUnitName ) + if self:IsInAirbase( Event.TgtUnit:GetVec2() ) then + self:AddForCleanUp( Event.TgtUnit, Event.TgtDCSUnitName ) end end end end -local CleanUpSurfaceTypeText = { - "LAND", - "SHALLOW_WATER", - "WATER", - "ROAD", - "RUNWAY" - } --- At the defined time interval, CleanUp the Groups within the CleanUpList. --- @param #CLEANUP self -function CLEANUP:_CleanUpScheduler() - self:F( { "CleanUp Scheduler" } ) +-- @param #CLEANUP_AIRBASE self +function CLEANUP_AIRBASE.__:CleanUpSchedule() local CleanUpCount = 0 - for CleanUpUnitName, UnitData in pairs( self.CleanUpList ) do + for CleanUpUnitName, CleanUpListData in pairs( self.CleanUpList ) do CleanUpCount = CleanUpCount + 1 - self:T( { CleanUpUnitName, UnitData } ) - local CleanUpUnit = Unit.getByName(UnitData.CleanUpUnitName) - local CleanUpGroupName = UnitData.CleanUpGroupName - local CleanUpUnitName = UnitData.CleanUpUnitName - if CleanUpUnit then - self:T( { "CleanUp Scheduler", "Checking:", CleanUpUnitName } ) + local CleanUpUnit = CleanUpListData.CleanUpUnit -- Wrapper.Unit#UNIT + local CleanUpGroupName = CleanUpListData.CleanUpGroupName + + if CleanUpUnit:IsAlive() ~= nil then + if _DATABASE:GetStatusGroup( CleanUpGroupName ) ~= "ReSpawn" then - local CleanUpUnitVec3 = CleanUpUnit:getPoint() - --self:T( CleanUpUnitVec3 ) - local CleanUpUnitVec2 = {} - CleanUpUnitVec2.x = CleanUpUnitVec3.x - CleanUpUnitVec2.y = CleanUpUnitVec3.z - --self:T( CleanUpUnitVec2 ) - local CleanUpSurfaceType = land.getSurfaceType(CleanUpUnitVec2) - --self:T( CleanUpSurfaceType ) - - if CleanUpUnit and CleanUpUnit:getLife() <= CleanUpUnit:getLife0() * 0.95 then - if CleanUpSurfaceType == land.SurfaceType.RUNWAY then - if CleanUpUnit:inAir() then - local CleanUpLandHeight = land.getHeight(CleanUpUnitVec2) - local CleanUpUnitHeight = CleanUpUnitVec3.y - CleanUpLandHeight - self:T( { "CleanUp Scheduler", "Height = " .. CleanUpUnitHeight } ) - if CleanUpUnitHeight < 30 then + + local CleanUpCoordinate = CleanUpUnit:GetCoordinate() + + self:T( { "CleanUp Scheduler", CleanUpUnitName } ) + if CleanUpUnit:GetLife() <= CleanUpUnit:GetLife0() * 0.95 then + if CleanUpUnit:IsAboveRunway() then + if CleanUpUnit:InAir() then + + local CleanUpLandHeight = CleanUpCoordinate:GetLandHeight() + local CleanUpUnitHeight = CleanUpCoordinate.y - CleanUpLandHeight + + if CleanUpUnitHeight < 100 then self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because below safe height and damaged." } ) - self:_DestroyUnit(CleanUpUnit, CleanUpUnitName) + self:DestroyUnit( CleanUpUnit ) end else self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because on runway and damaged." } ) - self:_DestroyUnit(CleanUpUnit, CleanUpUnitName) + self:DestroyUnit( CleanUpUnit ) end end end -- Clean Units which are waiting for a very long time in the CleanUpZone. if CleanUpUnit then - local CleanUpUnitVelocity = CleanUpUnit:getVelocity() - local CleanUpUnitVelocityTotal = math.abs(CleanUpUnitVelocity.x) + math.abs(CleanUpUnitVelocity.y) + math.abs(CleanUpUnitVelocity.z) - if CleanUpUnitVelocityTotal < 1 then - if UnitData.CleanUpMoved then - if UnitData.CleanUpTime + 180 <= timer.getTime() then + local CleanUpUnitVelocity = CleanUpUnit:GetVelocityKMH() + if CleanUpUnitVelocity < 1 then + if CleanUpListData.CleanUpMoved then + if CleanUpListData.CleanUpTime + 180 <= timer.getTime() then self:T( { "CleanUp Scheduler", "Destroy due to not moving anymore " .. CleanUpUnitName } ) - self:_DestroyUnit(CleanUpUnit, CleanUpUnitName) + self:DestroyUnit( CleanUpUnit ) end end else - UnitData.CleanUpTime = timer.getTime() - UnitData.CleanUpMoved = true + CleanUpListData.CleanUpTime = timer.getTime() + CleanUpListData.CleanUpMoved = true end end else -- Do nothing ... - self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE + self.CleanUpList[CleanUpUnitName] = nil end else self:T( "CleanUp: Group " .. CleanUpUnitName .. " cannot be found in DCS RTE, removing ..." ) - self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE + self.CleanUpList[CleanUpUnitName] = nil end end self:T(CleanUpCount) diff --git a/Moose Development/Moose/Functional/Designate.lua b/Moose Development/Moose/Functional/Designate.lua new file mode 100644 index 000000000..a70f12f7e --- /dev/null +++ b/Moose Development/Moose/Functional/Designate.lua @@ -0,0 +1,1272 @@ +--- **Functional** -- Management of target **Designation**. Lase, smoke and illuminate targets. +-- +-- --![Banner Image](..\Presentations\DESIGNATE\Dia1.JPG) +-- +-- === +-- +-- DESIGNATE is orchestrating the designation of potential targets executed by a Recce group, +-- and communicates these to a dedicated attacking group of players, +-- so that following a dynamically generated menu system, +-- each detected set of potential targets can be lased or smoked... +-- +-- Targets can be: +-- +-- * **Lased** for a period of time. +-- * **Smoked**. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.) +-- * **Illuminated** through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready. +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- * [**Ciribob**](https://forums.eagle.ru/member.php?u=112175): Showing the way how to lase targets + how laser codes work!!! Explained the autolase script. +-- * [**EasyEB**](https://forums.eagle.ru/member.php?u=112055): Ideas and Beta Testing +-- * [**Wingthor**](https://forums.eagle.ru/member.php?u=123698): Beta Testing +-- +-- +-- ### Authors: +-- +-- * **FlightControl**: Design & Programming +-- +-- @module Designate + + +do -- DESIGNATE + + --- @type DESIGNATE + -- @extends Core.Fsm#FSM_PROCESS + + --- # DESIGNATE class, extends @{Fsm#FSM} + -- + -- DESIGNATE is orchestrating the designation of potential targets executed by a Recce group, + -- and communicates these to a dedicated attacking group of players, + -- so that following a dynamically generated menu system, + -- each detected set of potential targets can be lased or smoked... + -- + -- Targets can be: + -- + -- * **Lased** for a period of time. + -- * **Smoked**. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.) + -- * **Illuminated** through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready. + -- + -- The following terminology is being used throughout this document: + -- + -- * The **DesignateObject** is the object of the DESIGNATE class, which is this class explained in the document. + -- * The **DetectionObject** is the object of a DETECTION_ class (DETECTION_TYPES, DETECTION_AREAS, DETECTION_UNITS), which is executing the detection and grouping of Targets into _DetectionItems_. + -- * **DetectionItems** is the list of detected target groupings by the _DetectionObject_. Each _DetectionItem_ contains a _TargetSet_. + -- * **DetectionItem** is one element of the _DetectionItems_ list, and contains a _TargetSet_. + -- * The **TargetSet** is a SET_UNITS collection of _Targets_, that have been detected by the _DetectionObject_. + -- * A **Target** is a detected UNIT object by the _DetectionObject_. + -- * A **Threat Level** is a number from 0 to 10 that is calculated based on the threat of the Target in an Air to Ground battle scenario. + -- * The **RecceSet** is a SET_GROUP collection that contains the **RecceGroups**. + -- * A **RecceGroup** is a GROUP object containing the **Recces**. + -- * A **Recce** is a UNIT object executing the reconnaissance as part the _DetectionObject_. A Recce can be of any UNIT type. + -- * An **AttackGroup** is a GROUP object that contain _Players_. + -- * A **Player** is an active CLIENT object containing a human player. + -- * A **Designate Menu** is the menu that is dynamically created during the designation process for each _AttackGroup_. + -- + -- The RecceSet is continuously detecting for potential Targets, executing its task as part of the DetectionObject. + -- Once Targets have been detected, the DesignateObject will trigger the **Detect Event**. + -- + -- In order to prevent an overflow in the DesignateObject of detected targets, there is a maximum + -- amount of DetectionItems that can be put in **scope** of the DesignateObject. + -- We call this the **MaximumDesignations** term. + -- + -- As part of the Detect Event, the DetectionItems list is used by the DesignateObject to provide the Players with: + -- + -- * The RecceGroups are reporting to each AttackGroup, sending **Messages** containing the Threat Level and the TargetSet composition. + -- * **Menu options** are created and updated for each AttackGroup, containing the Detection ID and the Coordinates. + -- + -- A Player can then select an action from the Designate Menu. + -- + -- **Note that each selected action will be executed for a TargetSet, thus the Target grouping done by the DetectionObject.** + -- + -- Each **Menu Option** in the Designate Menu has two modes: + -- + -- 1. If the TargetSet **is not being designated**, then the **Designate Menu** option for the target Set will provide options to **Lase** or **Smoke** the targets. + -- 2. If the Target Set **is being designated**, then the **Designate Menu** option will provide an option to stop or cancel the designation. + -- + -- While designating, the RecceGroups will report any change in TargetSet composition or Target presence. + -- + -- The following logic is executed when a TargetSet is selected to be *lased* from the Designation Menu: + -- + -- * The RecceSet is searched for any Recce that is within *designation distance* from a Target in the TargetSet that is currently not being designated. + -- * If there is a Recce found that is currently no designating a target, and is within designation distance from the Target, then that Target will be designated. + -- * During designation, any Recce that does not have Line of Sight (LOS) and is not within disignation distance from the Target, will stop designating the Target, and a report is given. + -- * When a Recce is designating a Target, and that Target is destroyed, then the Recce will stop designating the Target, and will report the event. + -- * When a Recce is designating a Target, and that Recce is destroyed, then the Recce will be removed from the RecceSet and designation will stop without reporting. + -- * When all RecceGroups are destroyed from the RecceSet, then the DesignationObject will stop functioning, and nothing will be reported. + -- + -- In this way, the DesignationObject assists players to designate ground targets for a coordinated attack! + -- + -- Have FUN! + -- + -- ## 1. DESIGNATE constructor + -- + -- * @{#DESIGNATE.New}(): Creates a new DESIGNATE object. + -- + -- ## 2. DESIGNATE is a FSM + -- + -- ![Process](..\Presentations\DESIGNATE\Dia2.JPG) + -- + -- ### 2.1 DESIGNATE States + -- + -- * **Designating** ( Group ): The designation process. + -- + -- ### 2.2 DESIGNATE Events + -- + -- * **@{#DESIGNATE.Detect}**: Detect targets. + -- * **@{#DESIGNATE.LaseOn}**: Lase the targets with the specified Index. + -- * **@{#DESIGNATE.LaseOff}**: Stop lasing the targets with the specified Index. + -- * **@{#DESIGNATE.Smoke}**: Smoke the targets with the specified Index. + -- * **@{#DESIGNATE.Status}**: Report designation status. + -- + -- ## 3. Maximum Designations + -- + -- In order to prevent an overflow of designations due to many Detected Targets, there is a + -- Maximum Designations scope that is set in the DesignationObject. + -- + -- The method @{#DESIGNATE.SetMaximumDesignations}() will put a limit on the amount of designations put in scope of the DesignationObject. + -- Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected. + -- + -- ## 4. Laser codes + -- + -- ### 4.1. Set possible laser codes + -- + -- An array of laser codes can be provided, that will be used by the DESIGNATE when lasing. + -- The laser code is communicated by the Recce when it is lasing a larget. + -- Note that the default laser code is 1113. + -- Working known laser codes are: 1113,1462,1483,1537,1362,1214,1131,1182,1644,1614,1515,1411,1621,1138,1542,1678,1573,1314,1643,1257,1467,1375,1341,1275,1237 + -- + -- Use the method @{#DESIGNATE.SetLaserCodes}() to set the possible laser codes to be selected from. + -- One laser code can be given or an sequence of laser codes through an table... + -- + -- Designate:SetLaserCodes( 1214 ) + -- + -- The above sets one laser code with the value 1214. + -- + -- Designate:SetLaserCodes( { 1214, 1131, 1614, 1138 } ) + -- + -- The above sets a collection of possible laser codes that can be assigned. **Note the { } notation!** + -- + -- ### 4.2. Auto generate laser codes + -- + -- Use the method @{#DESIGNATE.GenerateLaserCodes}() to generate all possible laser codes. Logic implemented and advised by Ciribob! + -- + -- ### 4.3. Add specific lase codes to the lase menu + -- + -- Certain plane types can only drop laser guided ordonnance when targets are lased with specific laser codes. + -- The SU-25T needs targets to be lased using laser code 1113. + -- The A-10A needs targets to be lased using laser code 1680. + -- + -- The method @{#DESIGNATE.AddMenuLaserCode}() to allow a player to lase a target using a specific laser code. + -- Remove such a lase menu option using @{#DESIGNATE.RemoveMenuLaserCode}(). + -- + -- ## 5. Autolase to automatically lase detected targets. + -- + -- DetectionItems can be auto lased once detected by Recces. As such, there is almost no action required from the Players using the Designate Menu. + -- The **auto lase** function can be activated through the Designation Menu. + -- Use the method @{#DESIGNATE.SetAutoLase}() to activate or deactivate the auto lase function programmatically. + -- Note that autolase will automatically activate lasing for ALL DetectedItems. Individual items can be switched-off if required using the Designation Menu. + -- + -- Designate:SetAutoLase( true ) + -- + -- Activate the auto lasing. + -- + -- ## 6. Target prioritization on threat level + -- + -- Targets can be detected of different types in one DetectionItem. Depending on the type of the Target, a different threat level applies in an Air to Ground combat context. + -- SAMs are of a higher threat than normal tanks. So, if the Target type was recognized, the Recces will select those targets that form the biggest threat first, + -- and will continue this until the remaining vehicles with the lowest threat have been reached. + -- + -- This threat level prioritization can be activated using the method @{#DESIGNATE.SetThreatLevelPrioritization}(). + -- If not activated, Targets will be selected in a random order, but most like those first which are the closest to the Recce marking the Target. + -- + -- Designate:SetThreatLevelPrioritization( true ) + -- + -- The example will activate the threat level prioritization for this the Designate object. Threats will be marked based on the threat level of the Target. + -- + -- ## 6. Designate Menu Location for a Mission + -- + -- You can make DESIGNATE work for a @{Mission#MISSION} object. In this way, the designate menu will not appear in the root of the radio menu, but in the menu of the Mission. + -- Use the method @{#DESIGNATE.SetMission}() to set the @{Mission} object for the designate function. + -- + -- ## 7. Status Report + -- + -- A status report is available that displays the current Targets detected, grouped per DetectionItem, and a list of which Targets are currently being marked. + -- + -- * The status report can be shown by selecting "Status" -> "Report Status" from the Designation menu . + -- * The status report can be automatically flashed by selecting "Status" -> "Flash Status On". + -- * The automatic flashing of the status report can be deactivated by selecting "Status" -> "Flash Status Off". + -- * The flashing of the status menu is disabled by default. + -- * The method @{#DESIGNATE.FlashStatusMenu}() can be used to enable or disable to flashing of the status menu. + -- + -- Designate:FlashStatusMenu( true ) + -- + -- The example will activate the flashing of the status menu for this Designate object. + -- + -- @field #DESIGNATE + DESIGNATE = { + ClassName = "DESIGNATE", + } + + --- DESIGNATE Constructor. This class is an abstract class and should not be instantiated. + -- @param #DESIGNATE self + -- @param Tasking.CommandCenter#COMMANDCENTER CC + -- @param Functional.Detection#DETECTION_BASE Detection + -- @param Core.Set#SET_GROUP AttackSet The Attack collection of GROUP objects to designate and report for. + -- @param Tasking.Mission#MISSION Mission (Optional) The Mission where the menu needs to be attached. + -- @return #DESIGNATE + function DESIGNATE:New( CC, Detection, AttackSet, Mission ) + + local self = BASE:Inherit( self, FSM:New() ) -- #DESIGNATE + self:F( { Detection } ) + + self:SetStartState( "Designating" ) + + self:AddTransition( "*", "Detect", "*" ) + --- Detect Handler OnBefore for DESIGNATE + -- @function [parent=#DESIGNATE] OnBeforeDetect + -- @param #DESIGNATE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Detect Handler OnAfter for DESIGNATE + -- @function [parent=#DESIGNATE] OnAfterDetect + -- @param #DESIGNATE self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Detect Trigger for DESIGNATE + -- @function [parent=#DESIGNATE] Detect + -- @param #DESIGNATE self + + --- Detect Asynchronous Trigger for DESIGNATE + -- @function [parent=#DESIGNATE] __Detect + -- @param #DESIGNATE self + -- @param #number Delay + + self:AddTransition( "*", "LaseOn", "Lasing" ) + --- LaseOn Handler OnBefore for DESIGNATE + -- @function [parent=#DESIGNATE ] OnBeforeLaseOn + -- @param #DESIGNATE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- LaseOn Handler OnAfter for DESIGNATE + -- @function [parent=#DESIGNATE ] OnAfterLaseOn + -- @param #DESIGNATE self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- LaseOn Trigger for DESIGNATE + -- @function [parent=#DESIGNATE ] LaseOn + -- @param #DESIGNATE self + + --- LaseOn Asynchronous Trigger for DESIGNATE + -- @function [parent=#DESIGNATE ] __LaseOn + -- @param #DESIGNATE self + -- @param #number Delay + + self:AddTransition( "Lasing", "Lasing", "Lasing" ) + + self:AddTransition( "*", "LaseOff", "Designate" ) + --- LaseOff Handler OnBefore for DESIGNATE + -- @function [parent=#DESIGNATE ] OnBeforeLaseOff + -- @param #DESIGNATE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- LaseOff Handler OnAfter for DESIGNATE + -- @function [parent=#DESIGNATE ] OnAfterLaseOff + -- @param #DESIGNATE self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- LaseOff Trigger for DESIGNATE + -- @function [parent=#DESIGNATE ] LaseOff + -- @param #DESIGNATE self + + --- LaseOff Asynchronous Trigger for DESIGNATE + -- @function [parent=#DESIGNATE ] __LaseOff + -- @param #DESIGNATE self + -- @param #number Delay + + self:AddTransition( "*", "Smoke", "*" ) + --- Smoke Handler OnBefore for DESIGNATE + -- @function [parent=#DESIGNATE ] OnBeforeSmoke + -- @param #DESIGNATE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Smoke Handler OnAfter for DESIGNATE + -- @function [parent=#DESIGNATE ] OnAfterSmoke + -- @param #DESIGNATE self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Smoke Trigger for DESIGNATE + -- @function [parent=#DESIGNATE ] Smoke + -- @param #DESIGNATE self + + --- Smoke Asynchronous Trigger for DESIGNATE + -- @function [parent=#DESIGNATE ] __Smoke + -- @param #DESIGNATE self + -- @param #number Delay + + self:AddTransition( "*", "Illuminate", "*" ) + --- Illuminate Handler OnBefore for DESIGNATE + -- @function [parent=#DESIGNATE] OnBeforeIlluminate + -- @param #DESIGNATE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Illuminate Handler OnAfter for DESIGNATE + -- @function [parent=#DESIGNATE] OnAfterIlluminate + -- @param #DESIGNATE self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Illuminate Trigger for DESIGNATE + -- @function [parent=#DESIGNATE] Illuminate + -- @param #DESIGNATE self + + --- Illuminate Asynchronous Trigger for DESIGNATE + -- @function [parent=#DESIGNATE] __Illuminate + -- @param #DESIGNATE self + -- @param #number Delay + + self:AddTransition( "*", "Done", "*" ) + + self:AddTransition( "*", "Status", "*" ) + --- Status Handler OnBefore for DESIGNATE + -- @function [parent=#DESIGNATE ] OnBeforeStatus + -- @param #DESIGNATE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Status Handler OnAfter for DESIGNATE + -- @function [parent=#DESIGNATE ] OnAfterStatus + -- @param #DESIGNATE self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Status Trigger for DESIGNATE + -- @function [parent=#DESIGNATE ] Status + -- @param #DESIGNATE self + + --- Status Asynchronous Trigger for DESIGNATE + -- @function [parent=#DESIGNATE ] __Status + -- @param #DESIGNATE self + -- @param #number Delay + + self.CC = CC + self.Detection = Detection + self.AttackSet = AttackSet + self.RecceSet = Detection:GetDetectionSetGroup() + self.Recces = {} + self.Designating = {} + self:SetDesignateName() + + self.LaseDuration = 60 + + self:SetFlashStatusMenu( false ) + self:SetMission( Mission ) + + self:SetLaserCodes( { 1688, 1130, 4785, 6547, 1465, 4578 } ) -- set self.LaserCodes + self:SetAutoLase( false, false ) -- set self.Autolase and don't send message. + + self:SetThreatLevelPrioritization( false ) -- self.ThreatLevelPrioritization, default is threat level priorization off + self:SetMaximumDesignations( 5 ) -- Sets the maximum designations. The default is 5 designations. + self:SetMaximumDistanceDesignations( 12000 ) -- Sets the maximum distance on which designations can be accepted. The default is 8000 meters. + self:SetMaximumMarkings( 2 ) -- Per target group, a maximum of 2 markings will be made by default. + + self:SetDesignateMenu() + + self.LaserCodesUsed = {} + + self.MenuLaserCodes = {} -- This map contains the laser codes that will be shown in the designate menu to lase with specific laser codes. + + self.Detection:__Start( 2 ) + + self:__Detect( -15 ) + + self.MarkScheduler = SCHEDULER:New( self ) + + return self + end + + --- Set the flashing of the status menu. + -- @param #DESIGNATE self + -- @param #boolean FlashMenu true: the status menu will be flashed every detection run; false: no flashing of the menu. + -- @return #DESIGNATE + function DESIGNATE:SetFlashStatusMenu( FlashMenu ) --R2.1 + + self.FlashStatusMenu = {} + + self.AttackSet:ForEachGroup( + + --- @param Wrapper.Group#GROUP GroupReport + function( AttackGroup ) + self.FlashStatusMenu[AttackGroup] = FlashMenu + end + ) + + return self + end + + + --- Set the maximum amount of designations. + -- @param #DESIGNATE self + -- @param #number MaximumDesignations + -- @return #DESIGNATE + function DESIGNATE:SetMaximumDesignations( MaximumDesignations ) + self.MaximumDesignations = MaximumDesignations + return self + end + + + --- Set the maximum ground designation distance. + -- @param #DESIGNATE self + -- @param #number MaximumDistanceGroundDesignation Maximum ground designation distance in meters. + -- @return #DESIGNATE + function DESIGNATE:SetMaximumDistanceGroundDesignation( MaximumDistanceGroundDesignation ) + self.MaximumDistanceGroundDesignation = MaximumDistanceGroundDesignation + return self + end + + + --- Set the maximum air designation distance. + -- @param #DESIGNATE self + -- @param #number MaximumDistanceAirDesignation Maximum air designation distance in meters. + -- @return #DESIGNATE + function DESIGNATE:SetMaximumDistanceAirDesignation( MaximumDistanceAirDesignation ) + self.MaximumDistanceAirDesignation = MaximumDistanceAirDesignation + return self + end + + + --- Set the overall maximum distance when designations can be accepted. + -- @param #DESIGNATE self + -- @param #number MaximumDistanceDesignations Maximum distance in meters to accept designations. + -- @return #DESIGNATE + function DESIGNATE:SetMaximumDistanceDesignations( MaximumDistanceDesignations ) + self.MaximumDistanceDesignations = MaximumDistanceDesignations + return self + end + + + --- Set the maximum amount of markings FACs will do, per designated target group. + -- @param #DESIGNATE self + -- @param #number MaximumMarkings Maximum markings FACs will do, per designated target group. + -- @return #DESIGNATE + function DESIGNATE:SetMaximumMarkings( MaximumMarkings ) + self.MaximumMarkings = MaximumMarkings + return self + end + + + --- Set an array of possible laser codes. + -- Each new lase will select a code from this table. + -- @param #DESIGNATE self + -- @param #list<#number> LaserCodes + -- @return #DESIGNATE + function DESIGNATE:SetLaserCodes( LaserCodes ) --R2.1 + + self.LaserCodes = ( type( LaserCodes ) == "table" ) and LaserCodes or { LaserCodes } + self:E( { LaserCodes = self.LaserCodes } ) + + self.LaserCodesUsed = {} + + return self + end + + + --- Add a specific lase code to the designate lase menu to lase targets with a specific laser code. + -- The MenuText will appear in the lase menu. + -- @param #DESIGNATE self + -- @param #number LaserCode The specific laser code to be added to the lase menu. + -- @param #string MenuText The text to be shown to the player. If you specify a %d in the MenuText, the %d will be replaced with the LaserCode specified. + -- @return #DESIGNATE + -- @usage + -- RecceDesignation:AddMenuLaserCode( 1113, "Lase with %d for Su-25T" ) + -- RecceDesignation:AddMenuLaserCode( 1680, "Lase with %d for A-10A" ) + -- + function DESIGNATE:AddMenuLaserCode( LaserCode, MenuText ) + + self.MenuLaserCodes[LaserCode] = MenuText + self:SetDesignateMenu() + + return self + end + + + --- Removes a specific lase code from the designate lase menu. + -- @param #DESIGNATE self + -- @param #number LaserCode The specific laser code that was set to be added to the lase menu. + -- @return #DESIGNATE + -- @usage + -- RecceDesignation:RemoveMenuLaserCode( 1113 ) + -- + function DESIGNATE:RemoveMenuLaserCode( LaserCode ) + + self.MenuLaserCodes[LaserCode] = nil + self:SetDesignateMenu() + + return self + end + + + + + --- Set the name of the designation. The name will appear in the menu. + -- This method can be used to control different designations for different plane types. + -- @param #DESIGNATE self + -- @param #string DesignateName + -- @return #DESIGNATE + function DESIGNATE:SetDesignateName( DesignateName ) + + self.DesignateName = "Designation" .. ( DesignateName and ( " for " .. DesignateName ) or "" ) + + return self + end + + + --- Generate an array of possible laser codes. + -- Each new lase will select a code from this table. + -- The entered value can range from 1111 - 1788, + -- -- but the first digit of the series must be a 1 or 2 + -- -- and the last three digits must be between 1 and 8. + -- The range used to be bugged so its not 1 - 8 but 0 - 7. + -- function below will use the range 1-7 just in case + -- @param #DESIGNATE self + -- @return #DESIGNATE + function DESIGNATE:GenerateLaserCodes() --R2.1 + + self.LaserCodes = {} + + local function containsDigit(_number, _numberToFind) + + local _thisNumber = _number + local _thisDigit = 0 + + while _thisNumber ~= 0 do + _thisDigit = _thisNumber % 10 + _thisNumber = math.floor(_thisNumber / 10) + if _thisDigit == _numberToFind then + return true + end + end + + return false + end + + -- generate list of laser codes + local _code = 1111 + local _count = 1 + while _code < 1777 and _count < 30 do + while true do + _code = _code + 1 + if not containsDigit(_code, 8) + and not containsDigit(_code, 9) + and not containsDigit(_code, 0) then + self:T(_code) + table.insert( self.LaserCodes, _code ) + break + end + end + _count = _count + 1 + end + + self.LaserCodesUsed = {} + + return self + end + + + + --- Set auto lase. + -- Auto lase will start lasing targets immediately when these are in range. + -- @param #DESIGNATE self + -- @param #boolean AutoLase (optional) true sets autolase on, false off. Default is off. + -- @param #boolean Message (optional) true is send message, false or nil won't send a message. Default is no message sent. + -- @return #DESIGNATE + function DESIGNATE:SetAutoLase( AutoLase, Message ) + + self.AutoLase = AutoLase or false + + if Message then + local AutoLaseOnOff = ( self.AutoLase == true ) and "On" or "Off" + local CC = self.CC:GetPositionable() + if CC then + CC:MessageToSetGroup( self.DesignateName .. ": Auto Lase " .. AutoLaseOnOff .. ".", 15, self.AttackSet ) + end + end + + self:CoordinateLase() + self:SetDesignateMenu() + + return self + end + + --- Set priorization of Targets based on the **Threat Level of the Target** in an Air to Ground context. + -- @param #DESIGNATE self + -- @param #boolean Prioritize + -- @return #DESIGNATE + function DESIGNATE:SetThreatLevelPrioritization( Prioritize ) --R2.1 + + self.ThreatLevelPrioritization = Prioritize + + return self + end + + --- Set the MISSION object for which designate will function. + -- When a MISSION object is assigned, the menu for the designation will be located at the Mission Menu. + -- @param #DESIGNATE self + -- @param Tasking.Mission#MISSION Mission The MISSION object. + -- @return #DESIGNATE + function DESIGNATE:SetMission( Mission ) --R2.2 + + self.Mission = Mission + + return self + end + + + --- + -- @param #DESIGNATE self + -- @return #DESIGNATE + function DESIGNATE:onafterDetect() + + self:__Detect( -math.random( 60 ) ) + + self:DesignationScope() + self:CoordinateLase() + self:SendStatus() + self:SetDesignateMenu() + + return self + end + + + --- Adapt the designation scope according the detected items. + -- @param #DESIGNATE self + -- @return #DESIGNATE + function DESIGNATE:DesignationScope() + + local DetectedItems = self.Detection:GetDetectedItems() + + local DetectedItemCount = 0 + + for DesignateIndex, Designating in pairs( self.Designating ) do + local DetectedItem = DetectedItems[DesignateIndex] + if DetectedItem then + -- Check LOS... + local IsDetected = self.Detection:IsDetectedItemDetected( DetectedItem ) + self:F({IsDetected = IsDetected, DetectedItem }) + if IsDetected == false then + self:F("Removing") + -- This Detection is obsolete, remove from the designate scope + self.Designating[DesignateIndex] = nil + self.AttackSet:ForEachGroup( + function( AttackGroup ) + local DetectionText = self.Detection:DetectedItemReportSummary( DesignateIndex, AttackGroup ):Text( ", " ) + self.CC:GetPositionable():MessageToGroup( "Targets out of LOS\n" .. DetectionText, 10, AttackGroup, self.DesignateName ) + end + ) + else + DetectedItemCount = DetectedItemCount + 1 + end + else + -- This Detection is obsolete, remove from the designate scope + self.Designating[DesignateIndex] = nil + end + end + + if DetectedItemCount < 5 then + for DesignateIndex, DetectedItem in pairs( DetectedItems ) do + local IsDetected = self.Detection:IsDetectedItemDetected( DetectedItem ) + if IsDetected == true then + self:F( { DistanceRecce = DetectedItem.DistanceRecce } ) + if DetectedItem.DistanceRecce <= self.MaximumDistanceDesignations then + if self.Designating[DesignateIndex] == nil then + -- ok, we added one item to the designate scope. + self.AttackSet:ForEachGroup( + function( AttackGroup ) + local DetectionText = self.Detection:DetectedItemReportSummary( DesignateIndex, AttackGroup ):Text( ", " ) + self.CC:GetPositionable():MessageToGroup( "Targets detected at \n" .. DetectionText, 10, AttackGroup, self.DesignateName ) + end + ) + self.Designating[DesignateIndex] = "" + break + end + end + end + end + end + + return self + end + + --- Coordinates the Auto Lase. + -- @param #DESIGNATE self + -- @return #DESIGNATE + function DESIGNATE:CoordinateLase() + + local DetectedItems = self.Detection:GetDetectedItems() + + for DesignateIndex, Designating in pairs( self.Designating ) do + local DetectedItem = DetectedItems[DesignateIndex] + if DetectedItem then + if self.AutoLase then + self:LaseOn( DesignateIndex, self.LaseDuration ) + end + end + end + + return self + end + + + --- Sends the status to the Attack Groups. + -- @param #DESIGNATE self + -- @param Wrapper.Group#GROUP AttackGroup + -- @param #number Duration The time in seconds the report should be visible. + -- @return #DESIGNATE + function DESIGNATE:SendStatus( MenuAttackGroup, Duration ) + + Duration = Duration or 10 + + self.AttackSet:ForEachGroup( + + --- @param Wrapper.Group#GROUP GroupReport + function( AttackGroup ) + + if self.FlashStatusMenu[AttackGroup] or ( MenuAttackGroup and ( AttackGroup:GetName() == MenuAttackGroup:GetName() ) ) then + + local DetectedReport = REPORT:New( "Targets ready for Designation:" ) + local DetectedItems = self.Detection:GetDetectedItems() + + for DesignateIndex, Designating in pairs( self.Designating ) do + local DetectedItem = DetectedItems[DesignateIndex] + if DetectedItem then + local Report = self.Detection:DetectedItemReportSummary( DesignateIndex, AttackGroup ):Text( ", " ) + DetectedReport:Add( string.rep( "-", 140 ) ) + DetectedReport:Add( " - " .. Report ) + end + end + + local CC = self.CC:GetPositionable() + + CC:MessageToGroup( DetectedReport:Text( "\n" ), Duration, AttackGroup, self.DesignateName ) + + local DesignationReport = REPORT:New( "Marking Targets:\n" ) + + self.RecceSet:ForEachGroup( + function( RecceGroup ) + local RecceUnits = RecceGroup:GetUnits() + for UnitID, RecceData in pairs( RecceUnits ) do + local Recce = RecceData -- Wrapper.Unit#UNIT + if Recce:IsLasing() then + DesignationReport:Add( " - " .. Recce:GetMessageText( "Marking " .. Recce:GetSpot().Target:GetTypeName() .. " with laser " .. Recce:GetSpot().LaserCode .. "." ) ) + end + end + end + ) + + CC:MessageToGroup( DesignationReport:Text(), Duration, AttackGroup, self.DesignateName ) + end + end + ) + + return self + end + + --- Sets the Designate Menu. + -- @param #DESIGNATE self + -- @return #DESIGNATE + function DESIGNATE:SetDesignateMenu() + + self.AttackSet:Flush() + + self.AttackSet:ForEachGroup( + + --- @param Wrapper.Group#GROUP GroupReport + function( AttackGroup ) + self.MenuDesignate = self.MenuDesignate or {} + + local MissionMenu = nil + + if self.Mission then + MissionMenu = self.Mission:GetRootMenu( AttackGroup ) + end + + local MenuTime = timer.getTime() + + self.MenuDesignate[AttackGroup] = MENU_GROUP:New( AttackGroup, self.DesignateName, MissionMenu ):SetTime( MenuTime ):SetTag( self.DesignateName ) + local MenuDesignate = self.MenuDesignate[AttackGroup] -- Core.Menu#MENU_GROUP + + -- Set Menu option for auto lase + + if self.AutoLase then + MENU_GROUP_COMMAND:New( AttackGroup, "Auto Lase Off", MenuDesignate, self.MenuAutoLase, self, false ):SetTime( MenuTime ):SetTag( self.DesignateName ) + else + MENU_GROUP_COMMAND:New( AttackGroup, "Auto Lase On", MenuDesignate, self.MenuAutoLase, self, true ):SetTime( MenuTime ):SetTag( self.DesignateName ) + end + + local StatusMenu = MENU_GROUP:New( AttackGroup, "Status", MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 15s", StatusMenu, self.MenuStatus, self, AttackGroup, 15 ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 30s", StatusMenu, self.MenuStatus, self, AttackGroup, 30 ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 60s", StatusMenu, self.MenuStatus, self, AttackGroup, 60 ):SetTime( MenuTime ):SetTag( self.DesignateName ) + + if self.FlashStatusMenu[AttackGroup] then + MENU_GROUP_COMMAND:New( AttackGroup, "Flash Status Report Off", StatusMenu, self.MenuFlashStatus, self, AttackGroup, false ):SetTime( MenuTime ):SetTag( self.DesignateName ) + else + MENU_GROUP_COMMAND:New( AttackGroup, "Flash Status Report On", StatusMenu, self.MenuFlashStatus, self, AttackGroup, true ):SetTime( MenuTime ):SetTag( self.DesignateName ) + end + + for DesignateIndex, Designating in pairs( self.Designating ) do + + local DetectedItem = self.Detection:GetDetectedItem( DesignateIndex ) + + if DetectedItem then + + local Coord = self.Detection:GetDetectedItemCoordinate( DesignateIndex ) + local ID = self.Detection:GetDetectedItemID( DesignateIndex ) + local MenuText = ID .. ", " .. Coord:ToString( AttackGroup ) + + if Designating == "" then + MenuText = "(-) " .. MenuText + local DetectedMenu = MENU_GROUP:New( AttackGroup, MenuText, MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND:New( AttackGroup, "Search other target", DetectedMenu, self.MenuForget, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName ) + for LaserCode, MenuText in pairs( self.MenuLaserCodes ) do + MENU_GROUP_COMMAND:New( AttackGroup, string.format( MenuText, LaserCode ), DetectedMenu, self.MenuLaseCode, self, DesignateIndex, 60, LaserCode ):SetTime( MenuTime ):SetTag( self.DesignateName ) + end + MENU_GROUP_COMMAND:New( AttackGroup, "Lase targets", DetectedMenu, self.MenuLaseOn, self, DesignateIndex, 60 ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND:New( AttackGroup, "Smoke red", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Red ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND:New( AttackGroup, "Smoke blue", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Blue ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND:New( AttackGroup, "Smoke green", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Green ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND:New( AttackGroup, "Smoke white", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.White ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND:New( AttackGroup, "Smoke orange", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Orange ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND:New( AttackGroup, "Illuminate", DetectedMenu, self.MenuIlluminate, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName ) + else + if Designating == "Laser" then + MenuText = "(L) " .. MenuText + elseif Designating == "Smoke" then + MenuText = "(S) " .. MenuText + elseif Designating == "Illuminate" then + MenuText = "(I) " .. MenuText + end + local DetectedMenu = MENU_GROUP:New( AttackGroup, MenuText, MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName ) + if Designating == "Laser" then + MENU_GROUP_COMMAND:New( AttackGroup, "Stop lasing", DetectedMenu, self.MenuLaseOff, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName ) + else + end + end + end + end + MenuDesignate:Remove( MenuTime, self.DesignateName ) + end + ) + + return self + end + + --- + -- @param #DESIGNATE self + function DESIGNATE:MenuStatus( AttackGroup, Duration ) + + self:E("Status") + + self:SendStatus( AttackGroup, Duration ) + end + + --- + -- @param #DESIGNATE self + function DESIGNATE:MenuFlashStatus( AttackGroup, Flash ) + + self:E("Flash Status") + + self.FlashStatusMenu[AttackGroup] = Flash + self:SetDesignateMenu() + end + + + --- + -- @param #DESIGNATE self + function DESIGNATE:MenuForget( Index ) + + self:E("Forget") + + self.Designating[Index] = nil + self:SetDesignateMenu() + end + + --- + -- @param #DESIGNATE self + function DESIGNATE:MenuAutoLase( AutoLase ) + + self:E("AutoLase") + + self:SetAutoLase( AutoLase, true ) + end + + --- + -- @param #DESIGNATE self + function DESIGNATE:MenuSmoke( Index, Color ) + + self:E("Designate through Smoke") + + self.Designating[Index] = "Smoke" + self:Smoke( Index, Color ) + end + + --- + -- @param #DESIGNATE self + function DESIGNATE:MenuIlluminate( Index ) + + self:E("Designate through Illumination") + + self.Designating[Index] = "Illuminate" + + self:__Illuminate( 1, Index ) + end + + --- + -- @param #DESIGNATE self + function DESIGNATE:MenuLaseOn( Index, Duration ) + + self:E("Designate through Lase") + + self:__LaseOn( 1, Index, Duration ) + self:SetDesignateMenu() + end + + + --- + -- @param #DESIGNATE self + function DESIGNATE:MenuLaseCode( Index, Duration, LaserCode ) + + self:E( "Designate through Lase using " .. LaserCode ) + + self:__LaseOn( 1, Index, Duration, LaserCode ) + self:SetDesignateMenu() + end + + + --- + -- @param #DESIGNATE self + function DESIGNATE:MenuLaseOff( Index, Duration ) + + self:E("Lasing off") + + self.Designating[Index] = "" + self:__LaseOff( 1, Index ) + self:SetDesignateMenu() + end + + --- + -- @param #DESIGNATE self + function DESIGNATE:onafterLaseOn( From, Event, To, Index, Duration, LaserCode ) + + self.Designating[Index] = "Laser" + self.LaseStart = timer.getTime() + self.LaseDuration = Duration + self:__Lasing( -1, Index, Duration, LaserCode ) + end + + + --- + -- @param #DESIGNATE self + -- @return #DESIGNATE + function DESIGNATE:onafterLasing( From, Event, To, Index, Duration, LaserCodeRequested ) + + + local TargetSetUnit = self.Detection:GetDetectedSet( Index ) + + local MarkingCount = 0 + local MarkedTypes = {} + local ReportTypes = REPORT:New() + local ReportLaserCodes = REPORT:New() + + TargetSetUnit:Flush() + + --self:F( { Recces = self.Recces } ) + for TargetUnit, RecceData in pairs( self.Recces ) do + local Recce = RecceData -- Wrapper.Unit#UNIT + self:F( { TargetUnit = TargetUnit, Recce = Recce:GetName() } ) + if not Recce:IsLasing() then + local LaserCode = Recce:GetLaserCode() -- (Not deleted when stopping with lasing). + self:F( { ClearingLaserCode = LaserCode } ) + self.LaserCodesUsed[LaserCode] = nil + self.Recces[TargetUnit] = nil + end + end + + -- If a specific lasercode is requested, we disable one active lase! + if LaserCodeRequested then + for TargetUnit, RecceData in pairs( self.Recces ) do -- We break after the first has been processed. + local Recce = RecceData -- Wrapper.Unit#UNIT + self:F( { TargetUnit = TargetUnit, Recce = Recce:GetName() } ) + if Recce:IsLasing() then + -- When a Recce is lasing, we switch the lasing off, and clear the references to the lasing in the DESIGNATE class. + Recce:LaseOff() -- Switch off the lasing. + local LaserCode = Recce:GetLaserCode() -- (Not deleted when stopping with lasing). + self:F( { ClearingLaserCode = LaserCode } ) + self.LaserCodesUsed[LaserCode] = nil + self.Recces[TargetUnit] = nil + break + end + end + end + + if self.AutoLase or ( not self.AutoLase and ( self.LaseStart + Duration >= timer.getTime() ) ) then + + TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0, + --- @param Wrapper.Unit#UNIT SmokeUnit + function( TargetUnit ) + + self:F( { TargetUnit = TargetUnit:GetName() } ) + + if MarkingCount < self.MaximumMarkings then + + if TargetUnit:IsAlive() then + + local Recce = self.Recces[TargetUnit] + + if not Recce then + + self:E( "Lasing..." ) + self.RecceSet:Flush() + + for RecceGroupID, RecceGroup in pairs( self.RecceSet:GetSet() ) do + for UnitID, UnitData in pairs( RecceGroup:GetUnits() or {} ) do + + local RecceUnit = UnitData -- Wrapper.Unit#UNIT + local RecceUnitDesc = RecceUnit:GetDesc() + --self:F( { RecceUnit = RecceUnit:GetName(), RecceDescription = RecceUnitDesc } ) + + if RecceUnit:IsLasing() == false then + --self:F( { IsDetected = RecceUnit:IsDetected( TargetUnit ), IsLOS = RecceUnit:IsLOS( TargetUnit ) } ) + + if RecceUnit:IsDetected( TargetUnit ) and RecceUnit:IsLOS( TargetUnit ) then + + local LaserCodeIndex = math.random( 1, #self.LaserCodes ) + local LaserCode = self.LaserCodes[LaserCodeIndex] + --self:F( { LaserCode = LaserCode, LaserCodeUsed = self.LaserCodesUsed[LaserCode] } ) + + if LaserCodeRequested and LaserCodeRequested ~= LaserCode then + LaserCode = LaserCodeRequested + LaserCodeRequested = nil + end + + if not self.LaserCodesUsed[LaserCode] then + + self.LaserCodesUsed[LaserCode] = LaserCodeIndex + local Spot = RecceUnit:LaseUnit( TargetUnit, LaserCode, Duration ) + local AttackSet = self.AttackSet + + function Spot:OnAfterDestroyed( From, Event, To ) + self:E( "Destroyed Message" ) + self.Recce:ToSetGroup( "Target " .. TargetUnit:GetTypeName() .. " destroyed. " .. TargetSetUnit:Count() .. " targets left.", 5, AttackSet, self.DesignateName ) + end + + self.Recces[TargetUnit] = RecceUnit + RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.", 5, self.AttackSet, self.DesignateName ) + -- OK. We have assigned for the Recce a TargetUnit. We can exit the function. + MarkingCount = MarkingCount + 1 + local TargetUnitType = TargetUnit:GetTypeName() + if not MarkedTypes[TargetUnitType] then + MarkedTypes[TargetUnitType] = true + ReportTypes:Add(TargetUnitType) + end + ReportLaserCodes:Add(RecceUnit.LaserCode) + return + end + else + --RecceUnit:MessageToSetGroup( "Can't mark " .. TargetUnit:GetTypeName(), 5, self.AttackSet ) + end + else + -- The Recce is lasing, but the Target is not detected or within LOS. So stop lasing and send a report. + + if not RecceUnit:IsDetected( TargetUnit ) or not RecceUnit:IsLOS( TargetUnit ) then + + local Recce = self.Recces[TargetUnit] -- Wrapper.Unit#UNIT + + if Recce then + Recce:LaseOff() + Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() "out of LOS. Cancelling lase!", 5, self.AttackSet, self.DesignateName ) + end + else + MarkingCount = MarkingCount + 1 + local TargetUnitType = TargetUnit:GetTypeName() + if not MarkedTypes[TargetUnitType] then + MarkedTypes[TargetUnitType] = true + ReportTypes:Add(TargetUnitType) + end + ReportLaserCodes:Add(RecceUnit.LaserCode) + end + end + end + end + else + MarkingCount = MarkingCount + 1 + local TargetUnitType = TargetUnit:GetTypeName() + if not MarkedTypes[TargetUnitType] then + MarkedTypes[TargetUnitType] = true + ReportTypes:Add(TargetUnitType) + end + ReportLaserCodes:Add(Recce.LaserCode) + --Recce:MessageToSetGroup( self.DesignateName .. ": Marking " .. TargetUnit:GetTypeName() .. " with laser " .. Recce.LaserCode .. ".", 5, self.AttackSet ) + end + end + end + end + ) + + local MarkedTypesText = ReportTypes:Text(', ') + local MarkedLaserCodesText = ReportLaserCodes:Text(', ') + for MarkedType, MarketCount in pairs( MarkedTypes ) do + self.CC:GetPositionable():MessageToSetGroup( "Marking " .. MarkingCount .. " x " .. MarkedTypesText .. " with lasers " .. MarkedLaserCodesText .. ".", 5, self.AttackSet, self.DesignateName ) + end + + self:__Lasing( -30, Index, Duration, LaserCodeRequested ) + + self:SetDesignateMenu() + + else + self:__LaseOff( 1 ) + end + + end + + --- + -- @param #DESIGNATE self + -- @return #DESIGNATE + function DESIGNATE:onafterLaseOff( From, Event, To, Index ) + + local CC = self.CC:GetPositionable() + + if CC then + CC:MessageToSetGroup( "Stopped lasing.", 5, self.AttackSet, self.DesignateName ) + end + + local TargetSetUnit = self.Detection:GetDetectedSet( Index ) + + local Recces = self.Recces + + for TargetID, RecceData in pairs( Recces ) do + local Recce = RecceData -- Wrapper.Unit#UNIT + Recce:MessageToSetGroup( "Stopped lasing " .. Recce:GetSpot().Target:GetTypeName() .. ".", 5, self.AttackSet, self.DesignateName ) + Recce:LaseOff() + end + + Recces = nil + self.Recces = {} + self.LaserCodesUsed = {} + + self:SetDesignateMenu() + end + + + --- + -- @param #DESIGNATE self + -- @return #DESIGNATE + function DESIGNATE:onafterSmoke( From, Event, To, Index, Color ) + + local TargetSetUnit = self.Detection:GetDetectedSet( Index ) + local TargetSetUnitCount = TargetSetUnit:Count() + + local MarkedCount = 0 + + TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0, + --- @param Wrapper.Unit#UNIT SmokeUnit + function( SmokeUnit ) + + if MarkedCount < self.MaximumMarkings then + + MarkedCount = MarkedCount + 1 + + self:E( "Smoking ..." ) + + local RecceGroup = self.RecceSet:FindNearestGroupFromPointVec2(SmokeUnit:GetPointVec2()) + local RecceUnit = RecceGroup:GetUnit( 1 ) + + if RecceUnit then + + RecceUnit:MessageToSetGroup( "Smoking " .. SmokeUnit:GetTypeName() .. ".", 5, self.AttackSet, self.DesignateName ) + + self.MarkScheduler:Schedule( self, + function() + if SmokeUnit:IsAlive() then + SmokeUnit:Smoke( Color, 50, 2 ) + end + self:Done( Index ) + end, {}, math.random( 5, 20 ) + ) + end + end + end + ) + + + end + + --- Illuminating + -- @param #DESIGNATE self + -- @return #DESIGNATE + function DESIGNATE:onafterIlluminate( From, Event, To, Index ) + + local TargetSetUnit = self.Detection:GetDetectedSet( Index ) + local TargetUnit = TargetSetUnit:GetFirst() + + if TargetUnit then + local RecceGroup = self.RecceSet:FindNearestGroupFromPointVec2(TargetUnit:GetPointVec2()) + local RecceUnit = RecceGroup:GetUnit( 1 ) + if RecceUnit then + RecceUnit:MessageToSetGroup( "Illuminating " .. TargetUnit:GetTypeName() .. ".", 5, self.AttackSet, self.DesignateName ) + self.MarkScheduler:Schedule( self, + function() + if TargetUnit:IsAlive() then + TargetUnit:GetPointVec3():AddY(300):IlluminationBomb() + end + self:Done( Index ) + end, {}, math.random( 5, 20 ) + ) + end + end + end + + --- Done + -- @param #DESIGNATE self + -- @return #DESIGNATE + function DESIGNATE:onafterDone( From, Event, To, Index ) + + self.Designating[Index] = nil + self:SetDesignateMenu() + end + +end + +-- Help from Ciribob + diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 15d3efd4c..2b67e54dd 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -1,4 +1,4 @@ ---- **Functional** - DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods. +--- **Functional** -- DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods. -- -- ![Banner Image](..\Presentations\DETECTION\Dia1.JPG) -- @@ -7,8 +7,25 @@ -- DETECTION classes facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units). -- DETECTION uses the in-built detection capabilities of DCS World, but adds new functionalities. -- --- Please watch this [youtube video](https://youtu.be/C7p81dUwP-E) that explains the detection concepts. +-- Find the DETECTION classes documentation further in this document in the globals section. -- +-- ==== +-- +-- # Demo Missions +-- +-- ### [DETECTION Demo Missions and Source Code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/DET%20-%20Detection) +-- +-- ### [DETECTION Demo Missions, only for Beta Testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/DET%20-%20Detection) +-- +-- ### [ALL Demo Missions pack of the Latest Release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [DETECTION YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3Cf5jpI6BS0sBOVWK__tji) +-- +-- ==== -- -- ### Contributions: -- @@ -20,19 +37,31 @@ -- -- @module Detection +----BASE:TraceClass("DETECTION_BASE") +----BASE:TraceClass("DETECTION_AREAS") +----BASE:TraceClass("DETECTION_UNITS") +----BASE:TraceClass("DETECTION_TYPES") do -- DETECTION_BASE - --- # 1) DETECTION_BASE class, extends @{Fsm#FSM} + --- @type DETECTION_BASE + -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. + -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. + -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. + -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. + -- @field #number DetectionRun + -- @extends Core.Fsm#FSM + + --- DETECTION_BASE class, extends @{Fsm#FSM} -- -- The DETECTION_BASE class defines the core functions to administer detected objects. -- The DETECTION_BASE class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). -- - -- ## 1.1) DETECTION_BASE constructor + -- ## DETECTION_BASE constructor -- -- Construct a new DETECTION_BASE instance using the @{#DETECTION_BASE.New}() method. -- - -- ## 1.2) DETECTION_BASE initialization + -- ## Initialization -- -- By default, detection will return detected objects with all the detection sensors available. -- However, you can ask how the objects were found with specific detection methods. @@ -48,7 +77,29 @@ do -- DETECTION_BASE -- * @{#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. -- * @{#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. -- - -- ## 1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list + -- ## **Filter** detected units based on **category of the unit** + -- + -- Filter the detected units based on Unit.Category using the method @{#DETECTION_BASE.FilterCategories}(). + -- The different values of Unit.Category can be: + -- + -- * Unit.Category.AIRPLANE + -- * Unit.Category.GROUND_UNIT + -- * Unit.Category.HELICOPTER + -- * Unit.Category.SHIP + -- * Unit.Category.STRUCTURE + -- + -- Multiple Unit.Category entries can be given as a table and then these will be evaluated as an OR expression. + -- + -- Example to filter a single category (Unit.Category.AIRPLANE). + -- + -- DetectionObject:FilterCategories( Unit.Category.AIRPLANE ) + -- + -- Example to filter multiple categories (Unit.Category.AIRPLANE, Unit.Category.HELICOPTER). Note the {}. + -- + -- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) + -- + -- + -- ## **DETECTION_ derived classes** group the detected units into a **DetectedItems[]** list -- -- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later -- of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains @@ -67,7 +118,7 @@ do -- DETECTION_BASE -- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ). -- This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ). -- - -- ## 1.4) Apply additional Filters to fine-tune the detected objects + -- ## **Visual filters** to fine-tune the probability of the detected objects -- -- By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. -- That being said, the DCS World detection algorithm can sometimes be unrealistic. @@ -88,7 +139,8 @@ do -- DETECTION_BASE -- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. -- Only when you experience unrealistic behaviour in your missions, these filters could be applied. -- - -- ### 1.4.1 ) Distance visual detection probability + -- + -- ### Distance visual detection probability -- -- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. -- Also, the speed of accurate detection plays a role. @@ -102,7 +154,7 @@ do -- DETECTION_BASE -- -- Use the method @{Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance. -- - -- ### 1.4.2 ) Alpha Angle visual detection probability + -- ### Alpha Angle visual detection probability -- -- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. @@ -114,7 +166,7 @@ do -- DETECTION_BASE -- -- Use the method @{Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°. -- - -- ### 1.4.3 ) Cloudy Zones detection probability + -- ### Cloudy Zones detection probability -- -- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission @@ -129,12 +181,12 @@ do -- DETECTION_BASE -- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for -- AI not to detect so easily targets within a forrest or village rich area. -- - -- ## 1.5 ) Accept / Reject detected units + -- ## Accept / Reject detected units -- -- DETECTION_BASE can accept or reject successful detections based on the location of the detected object, -- if it is located in range or located inside or outside of specific zones. -- - -- ### 1.5.1 ) Detection acceptance of within range limit + -- ### Detection acceptance of within range limit -- -- A range can be set that will limit a successful detection for a unit. -- Use the method @{Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted. @@ -142,7 +194,7 @@ do -- DETECTION_BASE -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. -- -- -- Build a detect object. - -- local Detection = DETECTION_BASE:New( SetGroup ) + -- local Detection = DETECTION_UNITS:New( SetGroup ) -- -- -- This will accept detected units if the range is below 5000 meters. -- Detection:SetAcceptRange( 5000 ) @@ -151,7 +203,7 @@ do -- DETECTION_BASE -- Detection:Start() -- -- - -- ### 1.5.2 ) Detection acceptance if within zone(s). + -- ### Detection acceptance if within zone(s). -- -- Specific ZONE_BASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONE_BASE object(s). -- Use the method @{Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones. @@ -163,7 +215,7 @@ do -- DETECTION_BASE -- local ZoneAccept2 = ZONE:New( "AcceptZone2" ) -- -- -- Build a detect object. - -- local Detection = DETECTION_BASE:New( SetGroup ) + -- local Detection = DETECTION_UNITS:New( SetGroup ) -- -- -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2. -- Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) @@ -171,7 +223,7 @@ do -- DETECTION_BASE -- -- Start the Detection. -- Detection:Start() -- - -- ### 1.5.3 ) Detection rejectance if within zone(s). + -- ### Detection rejectance if within zone(s). -- -- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s). -- Use the method @{Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. @@ -184,37 +236,37 @@ do -- DETECTION_BASE -- local ZoneReject2 = ZONE:New( "RejectZone2" ) -- -- -- Build a detect object. - -- local Detection = DETECTION_BASE:New( SetGroup ) + -- local Detection = DETECTION_UNITS:New( SetGroup ) -- -- -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2. -- Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) -- -- -- Start the Detection. -- Detection:Start() + -- + -- ## Detection of Friendlies Nearby -- - -- ## 1.6) DETECTION_BASE is a Finite State Machine + -- Use the method @{Detection#DETECTION_BASE.SetFriendliesRange}() to set the range what will indicate when friendlies are nearby + -- a DetectedItem. The default range is 6000 meters. For air detections, it is advisory to use about 30.000 meters. + -- + -- ## DETECTION_BASE is a Finite State Machine -- -- Various Events and State Transitions can be tailored using DETECTION_BASE. -- - -- ### 1.6.1) DETECTION_BASE States + -- ### DETECTION_BASE States -- -- * **Detecting**: The detection is running. -- * **Stopped**: The detection is stopped. -- - -- ### 1.6.2) DETECTION_BASE Events + -- ### DETECTION_BASE Events -- -- * **Start**: Start the detection process. -- * **Detect**: Detect new units. -- * **Detected**: New units have been detected. -- * **Stop**: Stop the detection process. + -- + -- @field #DETECTION_BASE DETECTION_BASE -- - -- @type DETECTION_BASE - -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. - -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. - -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. - -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. - -- @field #number DetectionRun - -- @extends Core.Fsm#FSM DETECTION_BASE = { ClassName = "DETECTION_BASE", DetectionSetGroup = nil, @@ -230,10 +282,16 @@ do -- DETECTION_BASE --- @type DETECTION_BASE.DetectedObject -- @field #string Name - -- @field #boolean Visible + -- @field #boolean IsVisible + -- @field #boolean KnowType + -- @field #boolean KnowDistance -- @field #string Type -- @field #number Distance -- @field #boolean Identified + -- @field #number LastTime + -- @field #boolean LastPos + -- @field #number LastVelocity + --- @type DETECTION_BASE.DetectedItems -- @list <#DETECTION_BASE.DetectedItem> @@ -244,10 +302,10 @@ do -- DETECTION_BASE -- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. -- @field #boolean Changed Documents if the detected area has changes. -- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). - -- @field #number ItemID -- The identifier of the detected area. + -- @field #number ID -- The identifier of the detected area. -- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. -- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - + -- @field Core.Point#COORDINATE Coordinate The last known coordinate of the DetectedItem. --- DETECTION constructor. -- @param #DETECTION_BASE self @@ -264,19 +322,28 @@ do -- DETECTION_BASE self.DetectionSetGroup = DetectionSetGroup - self.DetectionInterval = 30 + self.RefreshTimeInterval = 30 - self:InitDetectVisual( true ) - self:InitDetectOptical( false ) - self:InitDetectRadar( false ) - self:InitDetectRWR( false ) - self:InitDetectIRST( false ) - self:InitDetectDLINK( false ) + self:InitDetectVisual( nil ) + self:InitDetectOptical( nil ) + self:InitDetectRadar( nil ) + self:InitDetectRWR( nil ) + self:InitDetectIRST( nil ) + self:InitDetectDLINK( nil ) + + self:FilterCategories( { + Unit.Category.AIRPLANE, + Unit.Category.GROUND_UNIT, + Unit.Category.HELICOPTER, + Unit.Category.SHIP, + Unit.Category.STRUCTURE + } ) + + self:SetFriendliesRange( 6000 ) -- Create FSM transitions. self:SetStartState( "Stopped" ) - self.CountryID = DetectionSetGroup:GetFirst():GetCountry() self:AddTransition( "Stopped", "Start", "Detecting") @@ -440,7 +507,7 @@ do -- DETECTION_BASE -- @param #string Event The Event string. -- @param #string To The To State string. function DETECTION_BASE:onafterStart(From,Event,To) - self:__Detect(0.1) + self:__Detect( 1 ) end --- @param #DETECTION_BASE self @@ -448,20 +515,20 @@ do -- DETECTION_BASE -- @param #string Event The Event string. -- @param #string To The To State string. function DETECTION_BASE:onafterDetect(From,Event,To) - self:E( {From,Event,To}) + self:E( { From, Event, To } ) local DetectDelay = 0.1 self.DetectionCount = 0 self.DetectionRun = 0 self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table - self.DetectionSetGroup:Flush() - + local DetectionTimeStamp = timer.getTime() + for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - self:E( {DetectionGroupData}) - self:__DetectionGroup( DetectDelay, DetectionGroupData ) -- Process each detection asynchronously. + --self:E( { DetectionGroupData } ) + self:__DetectionGroup( DetectDelay, DetectionGroupData, DetectionTimeStamp ) -- Process each detection asynchronously. self.DetectionCount = self.DetectionCount + 1 - DetectDelay = DetectDelay + 0.1 + DetectDelay = DetectDelay + 1 end end @@ -470,8 +537,8 @@ do -- DETECTION_BASE -- @param #string Event The Event string. -- @param #string To The To State string. -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. - function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup ) - self:E( {From,Event,To}) + function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup, DetectionTimeStamp ) + self:E( { From, Event, To } ) self.DetectionRun = self.DetectionRun + 1 @@ -482,6 +549,7 @@ do -- DETECTION_BASE self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) local DetectionGroupName = DetectionGroup:GetName() + local DetectionUnit = DetectionGroup:GetUnit(1) local DetectedUnits = {} @@ -494,17 +562,29 @@ do -- DETECTION_BASE self.DetectDLINK ) - self:T( DetectedTargets ) + self:F( DetectedTargets ) for DetectionObjectID, Detection in pairs( DetectedTargets ) do local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object - self:T2( DetectedObject ) - if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then + if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then -- and ( DetectedObject:getCategory() == Object.Category.UNIT or DetectedObject:getCategory() == Object.Category.STATIC ) then + local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity = DetectionUnit:IsTargetDetected( + DetectedObject, + self.DetectVisual, + self.DetectOptical, + self.DetectRadar, + self.DetectIRST, + self.DetectRWR, + self.DetectDLINK + ) + + self:T2( { TargetIsDetected = TargetIsDetected, TargetIsVisible = TargetIsVisible, TargetLastTime = TargetLastTime, TargetKnowType = TargetKnowType, TargetKnowDistance = TargetKnowDistance, TargetLastPos = TargetLastPos, TargetLastVelocity = TargetLastVelocity } ) + local DetectionAccepted = true local DetectedObjectName = DetectedObject:getName() + local DetectedObjectType = DetectedObject:getTypeName() local DetectedObjectVec3 = DetectedObject:getPoint() local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } @@ -515,10 +595,22 @@ do -- DETECTION_BASE ( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 + ( DetectedObjectVec3.z - DetectionGroupVec3.z )^2 ) ^ 0.5 / 1000 + + local DetectedUnitCategory = DetectedObject:getDesc().category - self:T( { "Detected Target", DetectionGroupName, DetectedObjectName, Distance } ) - + self:F( { "Detected Target:", DetectionGroupName, DetectedObjectName, DetectedObjectType, Distance, DetectedUnitCategory } ) + -- Calculate Acceptance + + DetectionAccepted = self._.FilterCategories[DetectedUnitCategory] ~= nil and DetectionAccepted or false + +-- if Distance > 15000 then +-- if DetectedUnitCategory == Unit.Category.GROUND_UNIT or DetectedUnitCategory == Unit.Category.SHIP then +-- if DetectedObject:hasSensors( Unit.SensorType.RADAR, Unit.RadarType.AS ) == false then +-- DetectionAccepted = false +-- end +-- end +-- end if self.AcceptRange and Distance > self.AcceptRange then DetectionAccepted = false @@ -527,7 +619,7 @@ do -- DETECTION_BASE if self.AcceptZones then for AcceptZoneID, AcceptZone in pairs( self.AcceptZones ) do local AcceptZone = AcceptZone -- Core.Zone#ZONE_BASE - if AcceptZone:IsPointVec2InZone( DetectedObjectVec2 ) == false then + if AcceptZone:IsVec2InZone( DetectedObjectVec2 ) == false then DetectionAccepted = false end end @@ -596,13 +688,19 @@ do -- DETECTION_BASE HasDetectedObjects = true - if not self.DetectedObjects[DetectedObjectName] then - self.DetectedObjects[DetectedObjectName] = {} - end + self.DetectedObjects[DetectedObjectName] = self.DetectedObjects[DetectedObjectName] or {} self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName - self.DetectedObjects[DetectedObjectName].Visible = Detection.visible - self.DetectedObjects[DetectedObjectName].Type = Detection.type + self.DetectedObjects[DetectedObjectName].IsDetected = TargetIsDetected + self.DetectedObjects[DetectedObjectName].IsVisible = TargetIsVisible + self.DetectedObjects[DetectedObjectName].LastTime = TargetLastTime + self.DetectedObjects[DetectedObjectName].LastPos = TargetLastPos + self.DetectedObjects[DetectedObjectName].LastVelocity = TargetLastVelocity + self.DetectedObjects[DetectedObjectName].KnowType = TargetKnowType + self.DetectedObjects[DetectedObjectName].KnowDistance = Detection.distance -- TargetKnowDistance self.DetectedObjects[DetectedObjectName].Distance = Distance + self.DetectedObjects[DetectedObjectName].DetectionTimeStamp = DetectionTimeStamp + + self:F( { DetectedObject = self.DetectedObjects[DetectedObjectName] } ) local DetectedUnit = UNIT:FindByName( DetectedObjectName ) @@ -625,15 +723,83 @@ do -- DETECTION_BASE end if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then - self:__Detect( self.DetectionInterval ) - self:T( "--> Create Detection Sets" ) - self:CreateDetectionSets() + + -- First check if all DetectedObjects were detected. + -- This is important. When there are DetectedObjects in the list, but were not detected, + -- And these remain undetected for more than 60 seconds, then these DetectedObjects will be flagged as not Detected. + -- IsDetected = false! + -- This is used in A2A_TASK_DISPATCHER to initiate fighter sweeping! The TASK_A2A_INTERCEPT tasks will be replaced with TASK_A2A_SWEEP tasks. + for DetectedObjectName, DetectedObject in pairs( self.DetectedObjects ) do + if self.DetectedObjects[DetectedObjectName].IsDetected == true and self.DetectedObjects[DetectedObjectName].DetectionTimeStamp + 60 <= DetectionTimeStamp then + self.DetectedObjects[DetectedObjectName].IsDetected = false + end + end + + self:CreateDetectionItems() -- Polymorphic call to Create/Update the DetectionItems list for the DETECTION_ class grouping method. + for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do + self:UpdateDetectedItemDetection( DetectedItem ) + self:CleanDetectionItem( DetectedItem, DetectedItemID ) -- Any DetectionItem that has a Set with zero elements in it, must be removed from the DetectionItems list. + end + + self:__Detect( self.RefreshTimeInterval ) end end + end + + do -- DetectionItems Creation + + -- Clean the DetectedItem table. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE + function DETECTION_BASE:CleanDetectionItem( DetectedItem, DetectedItemID ) + self:F2() + + -- We clean all DetectedItems. + -- if there are any remaining DetectedItems with no Set Objects then the Item in the DetectedItems must be deleted. + + local DetectedSet = DetectedItem.Set + + if DetectedSet:Count() == 0 then + self:RemoveDetectedItem( DetectedItemID ) + end + + return self + end + + --- Forget a Unit from a DetectionItem + -- @param #DETECTION_BASE self + -- @param #string UnitName The UnitName that needs to be forgotten from the DetectionItem Sets. + -- @return #DETECTION_BASE + function DETECTION_BASE:ForgetDetectedUnit( UnitName ) + self:F2() + + local DetectedItems = self:GetDetectedItems() + + for DetectedItemIndex, DetectedItem in pairs( DetectedItems ) do + local DetectedSet = self:GetDetectedSet( DetectedItemIndex ) + if DetectedSet then + DetectedSet:RemoveUnitsByName( UnitName ) + end + end + + return self + end + + --- Make a DetectionSet table. This function will be overridden in the derived clsses. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE + function DETECTION_BASE:CreateDetectionItems() + self:F2() + + self:E( "Error, in DETECTION_BASE class..." ) + return self + end + + end do -- Initialization methods @@ -645,6 +811,8 @@ do -- DETECTION_BASE function DETECTION_BASE:InitDetectVisual( DetectVisual ) self.DetectVisual = DetectVisual + + return self end --- Detect Optical. @@ -655,6 +823,8 @@ do -- DETECTION_BASE self:F2() self.DetectOptical = DetectOptical + + return self end --- Detect Radar. @@ -665,6 +835,8 @@ do -- DETECTION_BASE self:F2() self.DetectRadar = DetectRadar + + return self end --- Detect IRST. @@ -675,6 +847,8 @@ do -- DETECTION_BASE self:F2() self.DetectIRST = DetectIRST + + return self end --- Detect RWR. @@ -685,6 +859,8 @@ do -- DETECTION_BASE self:F2() self.DetectRWR = DetectRWR + + return self end --- Detect DLINK. @@ -695,25 +871,102 @@ do -- DETECTION_BASE self:F2() self.DetectDLINK = DetectDLINK + + return self end end + + do -- Filter methods + + --- Filter the detected units based on Unit.Category + -- The different values of Unit.Category can be: + -- + -- * Unit.Category.AIRPLANE + -- * Unit.Category.GROUND_UNIT + -- * Unit.Category.HELICOPTER + -- * Unit.Category.SHIP + -- * Unit.Category.STRUCTURE + -- + -- Multiple Unit.Category entries can be given as a table and then these will be evaluated as an OR expression. + -- + -- Example to filter a single category (Unit.Category.AIRPLANE). + -- + -- DetectionObject:FilterCategories( Unit.Category.AIRPLANE ) + -- + -- Example to filter multiple categories (Unit.Category.AIRPLANE, Unit.Category.HELICOPTER). Note the {}. + -- + -- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) + -- + -- @param #DETECTION_BASE self + -- @param #list FilterCategories The Categories entries + -- @return #DETECTION_BASE self + function DETECTION_BASE:FilterCategories( FilterCategories ) + self:F2() + + self._.FilterCategories = {} + if type( FilterCategories ) == "table" then + for CategoryID, Category in pairs( FilterCategories ) do + self._.FilterCategories[Category] = Category + end + else + self._.FilterCategories[FilterCategories] = FilterCategories + end + return self + + end + + end do --- Set the detection interval time in seconds. -- @param #DETECTION_BASE self - -- @param #number DetectionInterval Interval in seconds. + -- @param #number RefreshTimeInterval Interval in seconds. -- @return #DETECTION_BASE self - function DETECTION_BASE:SetDetectionInterval( DetectionInterval ) + function DETECTION_BASE:SetRefreshTimeInterval( RefreshTimeInterval ) self:F2() - self.DetectionInterval = DetectionInterval + self.RefreshTimeInterval = RefreshTimeInterval return self end end + + do -- Friendlies Radius + + --- Set the radius in meters to validate if friendlies are nearby. + -- @param #DETECTION_BASE self + -- @param #number FriendliesRange Radius to use when checking if Friendlies are nearby. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetFriendliesRange( FriendliesRange ) --R2.2 Friendlies range + self:F2() + + self.FriendliesRange = FriendliesRange + + return self + end + + end + + do -- Intercept Point + + --- Set the parameters to calculate to optimal intercept point. + -- @param #DETECTION_BASE self + -- @param #boolean Intercept Intercept is true if an intercept point is calculated. Intercept is false if it is disabled. The default Intercept is false. + -- @param #number IntereptDelay If Intercept is true, then InterceptDelay is the average time it takes to get airplanes airborne. + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetIntercept( Intercept, InterceptDelay ) + self:F2() + + self.Intercept = Intercept + self.InterceptDelay = InterceptDelay + + return self + end + + end do -- Accept / Reject detected units @@ -731,15 +984,20 @@ do -- DETECTION_BASE --- Accept detections if within the specified zone(s). -- @param #DETECTION_BASE self - -- @param AcceptZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @param Core.Zone#ZONE_BASE AcceptZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. -- @return #DETECTION_BASE self function DETECTION_BASE:SetAcceptZones( AcceptZones ) self:F2() if type( AcceptZones ) == "table" then - self.AcceptZones = AcceptZones + if AcceptZones.ClassName and AcceptZones:IsInstanceOf( ZONE_BASE ) then + self.AcceptZones = { AcceptZones } + else + self.AcceptZones = AcceptZones + end else - self.AcceptZones = { AcceptZones } + self:E( { "AcceptZones must be a list of ZONE_BASE derived objects or one ZONE_BASE derived object", AcceptZones } ) + error() end return self @@ -747,15 +1005,20 @@ do -- DETECTION_BASE --- Reject detections if within the specified zone(s). -- @param #DETECTION_BASE self - -- @param RejectZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. + -- @param Core.Zone#ZONE_BASE RejectZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. -- @return #DETECTION_BASE self function DETECTION_BASE:SetRejectZones( RejectZones ) self:F2() if type( RejectZones ) == "table" then - self.RejectZones = RejectZones + if RejectZones.ClassName and RejectZones:IsInstanceOf( ZONE_BASE ) then + self.RejectZones = { RejectZones } + else + self.RejectZones = RejectZones + end else - self.RejectZones = { RejectZones } + self:E( { "RejectZones must be a list of ZONE_BASE derived objects or one ZONE_BASE derived object", RejectZones } ) + error() end return self @@ -839,15 +1102,15 @@ do -- DETECTION_BASE function DETECTION_BASE:AddChangeItem( DetectedItem, ChangeCode, ItemUnitType ) DetectedItem.Changed = true - local ItemID = DetectedItem.ItemID + local ID = DetectedItem.ID DetectedItem.Changes = DetectedItem.Changes or {} DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} - DetectedItem.Changes[ChangeCode].ItemID = ItemID + DetectedItem.Changes[ChangeCode].ID = ID DetectedItem.Changes[ChangeCode].ItemUnitType = ItemUnitType - self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ItemUnitType } ) - + self:E( { "Change on Detection Item:", DetectedItem.ID, ChangeCode, ItemUnitType } ) + return self end @@ -861,15 +1124,15 @@ do -- DETECTION_BASE function DETECTION_BASE:AddChangeUnit( DetectedItem, ChangeCode, ChangeUnitType ) DetectedItem.Changed = true - local ItemID = DetectedItem.ItemID + local ID = DetectedItem.ID DetectedItem.Changes = DetectedItem.Changes or {} DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] or 0 DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] + 1 - DetectedItem.Changes[ChangeCode].ItemID = ItemID + DetectedItem.Changes[ChangeCode].ID = ID - self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ChangeUnitType } ) + self:E( { "Change on Detection Item:", DetectedItem.ID, ChangeCode, ChangeUnitType } ) return self end @@ -877,15 +1140,92 @@ do -- DETECTION_BASE end - do -- Threat + do -- Friendly calculations + + --- This will allow during friendly search any recce or detection unit to be also considered as a friendly. + -- By default, recce aren't considered friendly, because that would mean that a recce would be also an attacking friendly, + -- and this is wrong. + -- However, in a CAP situation, when the CAP is part of an EWR network, the CAP is also an attacker. + -- This, this method allows to register for a detection the CAP unit name prefixes to be considered CAP. + -- @param #DETECTION_BASE self + -- @param #string FriendlyPrefixes A string or a list of prefixes. + -- @return #DETECTION_BASE + function DETECTION_BASE:SetFriendlyPrefixes( FriendlyPrefixes ) + + self.FriendlyPrefixes = self.FriendlyPrefixes or {} + if type( FriendlyPrefixes ) ~= "table" then + FriendlyPrefixes = { FriendlyPrefixes } + end + for PrefixID, Prefix in pairs( FriendlyPrefixes ) do + self:F( { FriendlyPrefix = Prefix } ) + self.FriendlyPrefixes[Prefix] = Prefix + end + return self + end + + --- Returns if there are friendlies nearby the FAC units ... + -- @param #DETECTION_BASE self + -- @return #boolean true if there are friendlies nearby + function DETECTION_BASE:IsFriendliesNearBy( DetectedItem ) + + return DetectedItem.FriendliesNearBy ~= nil or false + end + + --- Returns friendly units nearby the FAC units ... + -- @param #DETECTION_BASE self + -- @return #map<#string,Wrapper.Unit#UNIT> The map of Friendly UNITs. + function DETECTION_BASE:GetFriendliesNearBy( DetectedItem ) + + return DetectedItem.FriendliesNearBy + end + + --- Filters friendly units by unit category. + -- @param #DETECTION_BASE self + -- @param FriendliesCategory + -- @return #DETECTION_BASE + function DETECTION_BASE:FilterFriendliesCategory( FriendliesCategory ) + self.FriendliesCategory = FriendliesCategory + return self + end + + --- Returns if there are friendlies nearby the intercept ... + -- @param #DETECTION_BASE self + -- @return #boolean trhe if there are friendlies near the intercept. + function DETECTION_BASE:IsFriendliesNearIntercept( DetectedItem ) + + return DetectedItem.FriendliesNearIntercept ~= nil or false + end + + --- Returns friendly units nearby the intercept point ... + -- @param #DETECTION_BASE self + -- @return #map<#string,Wrapper.Unit#UNIT> The map of Friendly UNITs. + function DETECTION_BASE:GetFriendliesNearIntercept( DetectedItem ) + + return DetectedItem.FriendliesNearIntercept + end + + --- Returns the distance used to identify friendlies near the deteted item ... + -- @param #DETECTION_BASE self + -- @return #number The distance. + function DETECTION_BASE:GetFriendliesDistance( DetectedItem ) + + return DetectedItem.FriendliesDistance + end --- Returns if there are friendlies nearby the FAC units ... -- @param #DETECTION_BASE self -- @return #boolean trhe if there are friendlies nearby - function DETECTION_BASE:IsFriendliesNearBy( DetectedItem ) + function DETECTION_BASE:IsPlayersNearBy( DetectedItem ) - self:T3( DetectedItem.FriendliesNearBy ) - return DetectedItem.FriendliesNearBy or false + return DetectedItem.PlayersNearBy ~= nil + end + + --- Returns friendly units nearby the FAC units ... + -- @param #DETECTION_BASE self + -- @return #map<#string,Wrapper.Unit#UNIT> The map of Friendly UNITs. + function DETECTION_BASE:GetPlayersNearBy( DetectedItem ) + + return DetectedItem.PlayersNearBy end --- Background worker function to determine if there are friendlies nearby ... @@ -895,51 +1235,117 @@ do -- DETECTION_BASE local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem local DetectedSet = ReportGroupData.DetectedItem.Set - local DetectedUnit = DetectedSet:GetFirst() + local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT - DetectedItem.FriendliesNearBy = false + DetectedItem.FriendliesNearBy = nil if DetectedUnit then + local InterceptCoord = ReportGroupData.InterceptCoord or DetectedUnit:GetCoordinate() local SphereSearch = { id = world.VolumeType.SPHERE, params = { - point = DetectedUnit:GetVec3(), - radius = 6000, + point = InterceptCoord:GetVec3(), + radius = self.FriendliesRange, } } - --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit - -- @param Wrapper.Group#GROUP ReportGroup - -- @param Set#SET_GROUP ReportSetGroup - local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) + --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit + -- @param Wrapper.Group#GROUP ReportGroup + -- @param Set#SET_GROUP ReportSetGroup + local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) - local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem - local DetectedSet = ReportGroupData.DetectedItem.Set - local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT - local ReportSetGroup = ReportGroupData.ReportSetGroup - - local EnemyCoalition = DetectedUnit:GetCoalition() - - local FoundUnitCoalition = FoundDCSUnit:getCoalition() - local FoundUnitName = FoundDCSUnit:getName() - local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() - local EnemyUnitName = DetectedUnit:GetName() - local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil - - self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) - - if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then - DetectedItem.FriendliesNearBy = true - return false + local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = ReportGroupData.DetectedItem.Set + local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT + local DetectedUnitCoord = DetectedUnit:GetCoordinate() + local InterceptCoord = ReportGroupData.InterceptCoord or DetectedUnitCoord + local ReportSetGroup = ReportGroupData.ReportSetGroup + + local EnemyCoalition = DetectedUnit:GetCoalition() + + local FoundUnitCoalition = FoundDCSUnit:getCoalition() + local FoundUnitName = FoundDCSUnit:getName() + local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() + local EnemyUnitName = DetectedUnit:GetName() + + local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil + self:T( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) + + if FoundUnitInReportSetGroup == true then + -- If the recce was part of the friendlies found, then check if the recce is part of the allowed friendly unit prefixes. + for PrefixID, Prefix in pairs( self.FriendlyPrefixes or {} ) do + self:F( { "FriendlyPrefix:", Prefix } ) + -- In case a match is found (so a recce unit name is part of the friendly prefixes), then report that recce to be part of the friendlies. + -- This is important if CAP planes (so planes using their own radar) to be scanning for targets as part of the EWR network. + -- But CAP planes are also attackers, so they need to be considered friendlies too! + -- I chose to use prefixes because it is the fastest way to check. + if string.find( FoundUnitName, Prefix:gsub ("-", "%%-"), 1 ) then + FoundUnitInReportSetGroup = false + break + end end + end + + self:F( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) + + if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then + local FriendlyUnit = UNIT:Find( FoundDCSUnit ) + local FriendlyUnitName = FriendlyUnit:GetName() + local FriendlyUnitCategory = FriendlyUnit:GetDesc().category + self:T( { FriendlyUnitCategory = FriendlyUnitCategory, FriendliesCategory = self.FriendliesCategory } ) + --if ( not self.FriendliesCategory ) or ( self.FriendliesCategory and ( self.FriendliesCategory == FriendlyUnitCategory ) ) then + DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {} + DetectedItem.FriendliesNearBy[FriendlyUnitName] = FriendlyUnit + local Distance = DetectedUnitCoord:Get2DDistance( FriendlyUnit:GetCoordinate() ) + DetectedItem.FriendliesDistance = DetectedItem.FriendliesDistance or {} + DetectedItem.FriendliesDistance[Distance] = FriendlyUnit + self:T( { FriendlyUnitName = FriendlyUnitName, Distance = Distance } ) + --end return true + end + + return true end world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) + + DetectedItem.PlayersNearBy = nil + local DetectionZone = ZONE_UNIT:New( "DetectionPlayers", DetectedUnit, self.FriendliesRange ) + + _DATABASE:ForEachPlayer( + --- @param Wrapper.Unit#UNIT PlayerUnit + function( PlayerUnitName ) + local PlayerUnit = UNIT:FindByName( PlayerUnitName ) + + if PlayerUnit and PlayerUnit:IsInZone(DetectionZone) then + + local PlayerUnitCategory = PlayerUnit:GetDesc().category + + if ( not self.FriendliesCategory ) or ( self.FriendliesCategory and ( self.FriendliesCategory == PlayerUnitCategory ) ) then + + DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {} + local PlayerUnitName = PlayerUnit:GetName() + + DetectedItem.PlayersNearBy = DetectedItem.PlayersNearBy or {} + DetectedItem.PlayersNearBy[PlayerUnitName] = PlayerUnit + + DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {} + DetectedItem.FriendliesNearBy[PlayerUnitName] = PlayerUnit + + local CenterCoord = DetectedUnit:GetCoordinate() + + local Distance = CenterCoord:Get2DDistance( PlayerUnit:GetCoordinate() ) + DetectedItem.FriendliesDistance = DetectedItem.FriendliesDistance or {} + DetectedItem.FriendliesDistance[Distance] = PlayerUnit + + end + end + end + ) end end @@ -950,19 +1356,23 @@ do -- DETECTION_BASE -- @param #DETECTION_BASE.DetectedObject DetectedObject -- @return #boolean true if already identified. function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) - self:F3( DetectedObject.Name ) + --self:F3( DetectedObject.Name ) local DetectedObjectName = DetectedObject.Name - local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true - self:T3( DetectedObjectIdentified ) - return DetectedObjectIdentified + if DetectedObjectName then + local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true + self:T3( DetectedObjectIdentified ) + return DetectedObjectIdentified + else + return nil + end end --- Identifies a detected object during detection processing. -- @param #DETECTION_BASE self -- @param #DETECTION_BASE.DetectedObject DetectedObject function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) - self:F( { "Identified:", DetectedObject.Name } ) + --self:F( { "Identified:", DetectedObject.Name } ) local DetectedObjectName = DetectedObject.Name self.DetectedObjectsIdentified[DetectedObjectName] = true @@ -989,16 +1399,18 @@ do -- DETECTION_BASE -- @param #string ObjectName -- @return #DETECTION_BASE.DetectedObject function DETECTION_BASE:GetDetectedObject( ObjectName ) - self:F( ObjectName ) + --self:F2( ObjectName ) if ObjectName then local DetectedObject = self.DetectedObjects[ObjectName] - - -- Only return detected objects that are alive! - local DetectedUnit = UNIT:FindByName( ObjectName ) - if DetectedUnit and DetectedUnit:IsAlive() then - if self:IsDetectedObjectIdentified( DetectedObject ) == false then - return DetectedObject + + if DetectedObject then + -- Only return detected objects that are alive! + local DetectedUnit = UNIT:FindByName( ObjectName ) + if DetectedUnit and DetectedUnit:IsAlive() then + if self:IsDetectedObjectIdentified( DetectedObject ) == false then + return DetectedObject + end end end end @@ -1006,14 +1418,41 @@ do -- DETECTION_BASE return nil end + + --- Gets a detected unit type name, taking into account the detection results. + -- @param #DETECTION_BASE self + -- @param Wrapper.Unit#UNIT DetectedUnit + -- @return #string The type name + function DETECTION_BASE:GetDetectedUnitTypeName( DetectedUnit ) + --self:F2( ObjectName ) + + if DetectedUnit and DetectedUnit:IsAlive() then + local DetectedUnitName = DetectedUnit:GetName() + local DetectedObject = self.DetectedObjects[DetectedUnitName] + + if DetectedObject then + if DetectedObject.KnowType then + return DetectedUnit:GetTypeName() + else + return "Unknown" + end + end + else + return "Dead:" .. DetectedUnit:GetName() + end + + return "Undetected:" .. DetectedUnit:GetName() + end + --- Adds a new DetectedItem to the DetectedItems list. -- The DetectedItem is a table and contains a SET_UNIT in the field Set. -- @param #DETECTION_BASE self + -- @param ItemPrefix -- @param #string DetectedItemIndex The index of the DetectedItem. -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. -- @return #DETECTION_BASE.DetectedItem - function DETECTION_BASE:AddDetectedItem( DetectedItemIndex, Set ) + function DETECTION_BASE:AddDetectedItem( ItemPrefix, DetectedItemIndex, Set ) local DetectedItem = {} self.DetectedItemCount = self.DetectedItemCount + 1 @@ -1025,8 +1464,10 @@ do -- DETECTION_BASE self.DetectedItems[self.DetectedItemCount] = DetectedItem end - DetectedItem.Set = Set or SET_UNIT:New() - DetectedItem.ItemID = self.DetectedItemMax + DetectedItem.Set = Set or SET_UNIT:New():FilterDeads():FilterCrashes() + DetectedItem.Index = DetectedItemIndex or self.DetectedItemCount + DetectedItem.ItemID = ItemPrefix .. "." .. self.DetectedItemMax + DetectedItem.ID = self.DetectedItemMax DetectedItem.Removed = false return DetectedItem @@ -1041,7 +1482,7 @@ do -- DETECTION_BASE -- @return #DETECTION_BASE.DetectedItem function DETECTION_BASE:AddDetectedItemZone( DetectedItemIndex, Set, Zone ) - local DetectedItem = self:AddDetectedItem( DetectedItemIndex, Set ) + local DetectedItem = self:AddDetectedItem( "AREA", DetectedItemIndex, Set ) DetectedItem.Zone = Zone @@ -1054,8 +1495,10 @@ do -- DETECTION_BASE -- @param #number DetectedItemIndex The index or position in the DetectedItems list where the item needs to be removed. function DETECTION_BASE:RemoveDetectedItem( DetectedItemIndex ) - self.DetectedItemCount = self.DetectedItemCount - 1 - self.DetectedItems[DetectedItemIndex] = nil + if self.DetectedItems[DetectedItemIndex] then + self.DetectedItemCount = self.DetectedItemCount - 1 + self.DetectedItems[DetectedItemIndex] = nil + end end @@ -1069,7 +1512,7 @@ do -- DETECTION_BASE --- Get the amount of SETs with detected objects. -- @param #DETECTION_BASE self - -- @return #number Count + -- @return #number The amount of detected items. Note that the amount of detected items can differ with the reality, because detections are not real-time but doen in intervals! function DETECTION_BASE:GetDetectedItemsCount() local DetectedCount = self.DetectedItemCount @@ -1090,6 +1533,34 @@ do -- DETECTION_BASE return nil end + --- Get a detected ItemID using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return #string DetectedItemID + function DETECTION_BASE:GetDetectedItemID( Index ) --R2.1 + + local DetectedItem = self.DetectedItems[Index] + if DetectedItem then + return DetectedItem.ItemID + end + + return "" + end + + --- Get a detected ID using a given numeric index. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return #string DetectedItemID + function DETECTION_BASE:GetDetectedID( Index ) --R2.1 + + local DetectedItem = self.DetectedItems[Index] + if DetectedItem then + return DetectedItem.ID + end + + return "" + end + --- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. -- @param #DETECTION_BASE self -- @param #number Index @@ -1104,6 +1575,39 @@ do -- DETECTION_BASE return nil end + + --- Set IsDetected flag for all DetectedItems. + -- @param #DETECTION_BASE self + -- @return #DETECTION_BASE.DetectedItem DetectedItem + -- @return #boolean true if at least one UNIT is detected from the DetectedSet, false if no UNIT was detected from the DetectedSet. + function DETECTION_BASE:UpdateDetectedItemDetection( DetectedItem ) + + local IsDetected = false + + for UnitName, UnitData in pairs( DetectedItem.Set:GetSet() ) do + local DetectedObject = self.DetectedObjects[UnitName] + self:F({UnitName = UnitName, IsDetected = DetectedObject.IsDetected}) + if DetectedObject.IsDetected then + IsDetected = true + break + end + end + + self:F( { IsDetected = DetectedItem.IsDetected } ) + + DetectedItem.IsDetected = IsDetected + + return IsDetected + end + + --- Checks if there is at least one UNIT detected in the Set of the the DetectedItem. + -- @param #DETECTION_BASE self + -- @return #boolean true if at least one UNIT is detected from the DetectedSet, false if no UNIT was detected from the DetectedSet. + function DETECTION_BASE:IsDetectedItemDetected( DetectedItem ) + + return DetectedItem.IsDetected + end + do -- Zones @@ -1111,55 +1615,132 @@ do -- DETECTION_BASE -- @param #DETECTION_BASE self -- @param #number Index -- @return Core.Zone#ZONE_UNIT DetectedZone - function DETECTION_BASE:GetDetectedZone( Index ) + function DETECTION_BASE:GetDetectedItemZone( Index ) local DetectedZone = self.DetectedItems[Index].Zone if DetectedZone then return DetectedZone end + local Detected + return nil end end + + + --- Set the detected item coordinate. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem The DetectedItem to set the coordinate at. + -- @param Core.Point#COORDINATE Coordinate The coordinate to set the last know detected position at. + -- @param Wrapper.Unit#UNIT DetectedItemUnit The unit to set the heading and altitude from. + -- @return #DETECTION_BASE + function DETECTION_BASE:SetDetectedItemCoordinate( DetectedItem, Coordinate, DetectedItemUnit ) + self:F( { Coordinate = Coordinate } ) + if DetectedItem then + if DetectedItemUnit then + DetectedItem.Coordinate = Coordinate + DetectedItem.Coordinate:SetHeading( DetectedItemUnit:GetHeading() ) + DetectedItem.Coordinate.y = DetectedItemUnit:GetAltitude() + DetectedItem.Coordinate.Speed = DetectedItemUnit:GetVelocityMPS() + end + end + end + + + --- Get the detected item coordinate. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return Core.Point#COORDINATE + function DETECTION_BASE:GetDetectedItemCoordinate( Index ) + self:F( { Index = Index } ) + + local DetectedItem = self:GetDetectedItem( Index ) + + if DetectedItem then + return DetectedItem.Coordinate + end + + return nil + end + + --- Set the detected item threatlevel. + -- @param #DETECTION_BASE self + -- @param #DETECTION_BASE.DetectedItem The DetectedItem to calculate the threatlevel for. + -- @return #DETECTION_BASE + function DETECTION_BASE:SetDetectedItemThreatLevel( DetectedItem ) + + local DetectedSet = DetectedItem.Set + + if DetectedItem then + DetectedItem.ThreatLevel = DetectedSet:CalculateThreatLevelA2G() + end + end + + + + --- Get the detected item coordinate. + -- @param #DETECTION_BASE self + -- @param #number Index + -- @return #number ThreatLevel + function DETECTION_BASE:GetDetectedItemThreatLevel( Index ) + self:F( { Index = Index } ) + + local DetectedItem = self:GetDetectedItem( Index ) + + if DetectedItem then + return DetectedItem.ThreatLevel or 0 + end + + return nil + end + + + + + + + --- Menu of a detected item using a given numeric index. + -- @param #DETECTION_BASE self + -- @param Index + -- @return #string + function DETECTION_BASE:DetectedItemMenu( Index, AttackGroup ) + self:F( Index ) + return nil + end + --- Report summary of a detected item using a given numeric index. -- @param #DETECTION_BASE self -- @param Index - -- @return #string - function DETECTION_BASE:DetectedItemReportSummary( Index ) + -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. + -- @param Core.Settings#SETTINGS Settings Message formatting settings to use. + -- @return Core.Report#REPORT + function DETECTION_BASE:DetectedItemReportSummary( Index, AttackGroup, Settings ) self:F( Index ) return nil end --- Report detailed of a detectedion result. -- @param #DETECTION_BASE self + -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. -- @return #string - function DETECTION_BASE:DetectedReportDetailed() + function DETECTION_BASE:DetectedReportDetailed( AttackGroup ) self:F() return nil end --- Get the detection Groups. -- @param #DETECTION_BASE self - -- @return Wrapper.Group#GROUP + -- @return Core.Set#SET_GROUP function DETECTION_BASE:GetDetectionSetGroup() local DetectionSetGroup = self.DetectionSetGroup return DetectionSetGroup end - --- Make a DetectionSet table. This function will be overridden in the derived clsses. - -- @param #DETECTION_BASE self - -- @return #DETECTION_BASE self - function DETECTION_BASE:CreateDetectionSets() - self:F2() - - self:E( "Error, in DETECTION_BASE class..." ) - - end - --- Schedule the DETECTION construction. -- @param #DETECTION_BASE self @@ -1180,7 +1761,7 @@ end do -- DETECTION_UNITS - --- # 2) DETECTION_UNITS class, extends @{Detection#DETECTION_BASE} + --- # DETECTION_UNITS class, extends @{Detection#DETECTION_BASE} -- -- The DETECTION_UNITS class will detect units within the battle zone. -- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. @@ -1226,7 +1807,7 @@ do -- DETECTION_UNITS if ChangeCode == "AU" then local MTUT = {} for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then + if ChangeUnitType ~= "ID" then MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType end end @@ -1236,7 +1817,7 @@ do -- DETECTION_UNITS if ChangeCode == "RU" then local MTUT = {} for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then + if ChangeUnitType ~= "ID" then MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType end end @@ -1254,7 +1835,7 @@ do -- DETECTION_UNITS -- For each DetectedItem, a one field array is created containing the Unit detected. -- @param #DETECTION_UNITS self -- @return #DETECTION_UNITS self - function DETECTION_UNITS:CreateDetectionSets() + function DETECTION_UNITS:CreateDetectionItems() self:F2( #self.DetectedObjects ) -- Loop the current detected items, and check if each object still exists and is detected. @@ -1262,13 +1843,12 @@ do -- DETECTION_UNITS for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT - local DetectedTypeName = DetectedItem.Type for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT local DetectedObject = nil - self:E( DetectedUnit ) + --self:E( DetectedUnit ) if DetectedUnit:IsAlive() then --self:E(DetectedUnit:GetName()) DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) @@ -1277,6 +1857,18 @@ do -- DETECTION_UNITS -- Yes, the DetectedUnit is still detected or exists. Flag as identified. self:IdentifyDetectedObject( DetectedObject ) + + -- Update the detection with the new data provided. + DetectedItem.TypeName = DetectedUnit:GetTypeName() + DetectedItem.CategoryName = DetectedUnit:GetCategoryName() + DetectedItem.Name = DetectedObject.Name + DetectedItem.IsVisible = DetectedObject.IsVisible + DetectedItem.LastTime = DetectedObject.LastTime + DetectedItem.LastPos = DetectedObject.LastPos + DetectedItem.LastVelocity = DetectedObject.LastVelocity + DetectedItem.KnowType = DetectedObject.KnowType + DetectedItem.KnowDistance = DetectedObject.KnowDistance + DetectedItem.Distance = DetectedObject.Distance else -- There was no DetectedObject, remove DetectedUnit from the Set. self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) @@ -1300,11 +1892,16 @@ do -- DETECTION_UNITS local DetectedItem = self:GetDetectedItem( DetectedUnitName ) if not DetectedItem then self:T( "Added new DetectedItem" ) - DetectedItem = self:AddDetectedItem( DetectedUnitName ) - DetectedItem.Type = DetectedUnit:GetTypeName() - DetectedItem.Name = DetectedObjectData.Name - DetectedItem.Visible = DetectedObjectData.Visible - DetectedItem.Distance = DetectedObjectData.Distance + DetectedItem = self:AddDetectedItem( "UNIT", DetectedUnitName ) + DetectedItem.TypeName = DetectedUnit:GetTypeName() + DetectedItem.Name = DetectedObject.Name + DetectedItem.IsVisible = DetectedObject.IsVisible + DetectedItem.LastTime = DetectedObject.LastTime + DetectedItem.LastPos = DetectedObject.LastPos + DetectedItem.LastVelocity = DetectedObject.LastVelocity + DetectedItem.KnowType = DetectedObject.KnowType + DetectedItem.KnowDistance = DetectedObject.KnowDistance + DetectedItem.Distance = DetectedObject.Distance end DetectedItem.Set:AddUnit( DetectedUnit ) @@ -1318,21 +1915,28 @@ do -- DETECTION_UNITS local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem local DetectedSet = DetectedItem.Set + -- Set the last known coordinate. + local DetectedFirstUnit = DetectedSet:GetFirst() + local DetectedFirstUnitCoord = DetectedFirstUnit:GetCoordinate() + self:SetDetectedItemCoordinate( DetectedItem, DetectedFirstUnitCoord, DetectedFirstUnit ) + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table --self:NearestFAC( DetectedItem ) + end end - - --- Report summary of a DetectedItem using a given numeric index. + + --- Menu of a DetectedItem using a given numeric index. -- @param #DETECTION_UNITS self -- @param Index -- @return #string - function DETECTION_UNITS:DetectedItemReportSummary( Index ) + function DETECTION_UNITS:DetectedItemMenu( Index, AttackGroup ) self:F( Index ) local DetectedItem = self:GetDetectedItem( Index ) local DetectedSet = self:GetDetectedSet( Index ) + local DetectedItemID = self:GetDetectedItemID( Index ) self:T( DetectedSet ) if DetectedSet then @@ -1340,58 +1944,88 @@ do -- DETECTION_UNITS local UnitDistanceText = "" local UnitCategoryText = "" - local DetectedItemUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT - - if DetectedItemUnit and DetectedItemUnit:IsAlive() then - self:T(DetectedItemUnit) - - local UnitCategoryName = DetectedItemUnit:GetCategoryName() or "" - local UnitCategoryType = DetectedItemUnit:GetTypeName() or "" - - if DetectedItem.Type and UnitCategoryName and UnitCategoryType then - UnitCategoryText = UnitCategoryName .. " (" .. UnitCategoryType .. ") at " - else - UnitCategoryText = "Unknown target at " - end - - if DetectedItem.Visible == false then - UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " estimated km" - else - UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " km, visual contact" - end - - local DetectedItemPointVec3 = DetectedItemUnit:GetPointVec3() - local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) - - local ThreatLevelA2G = DetectedItemUnit:GetThreatLevel( DetectedItem ) - - ReportSummary = string.format( - "%s - Threat [%s] (%2d) - %s%s", - DetectedItemPointLL, - string.rep( "â– ", ThreatLevelA2G ), - ThreatLevelA2G, - UnitCategoryText, - UnitDistanceText - ) - end - + local DetectedItemCoordinate = self:GetDetectedItemCoordinate( Index ) + local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup ) + + ReportSummary = string.format( + "%s - %s", + DetectedItemID, + DetectedItemCoordText + ) self:T( ReportSummary ) return ReportSummary end end + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_UNITS self + -- @param Index + -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. + -- @param Core.Settings#SETTINGS Settings Message formatting settings to use. + -- @return Core.Report#REPORT The report of the detection items. + function DETECTION_UNITS:DetectedItemReportSummary( Index, AttackGroup, Settings ) + self:F( { Index, self.DetectedItems } ) + + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedItemID = self:GetDetectedItemID( Index ) + + if DetectedItem then + local ReportSummary = "" + local UnitDistanceText = "" + local UnitCategoryText = "" + + if DetectedItem.KnowType then + local UnitCategoryName = DetectedItem.CategoryName + if UnitCategoryName then + UnitCategoryText = UnitCategoryName + end + if DetectedItem.TypeName then + UnitCategoryText = UnitCategoryText .. " (" .. DetectedItem.TypeName .. ")" + end + else + UnitCategoryText = "Unknown" + end + + if DetectedItem.KnowDistance then + if DetectedItem.IsVisible then + UnitDistanceText = " at " .. string.format( "%.2f", DetectedItem.Distance ) .. " km" + end + else + if DetectedItem.IsVisible then + UnitDistanceText = " at +/- " .. string.format( "%.0f", DetectedItem.Distance ) .. " km" + end + end + + --TODO: solve Index reference + local DetectedItemCoordinate = self:GetDetectedItemCoordinate( Index ) + local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup, Settings ) + + local ThreatLevelA2G = self:GetDetectedItemThreatLevel( Index ) + + local Report = REPORT:New() + Report:Add(DetectedItemID .. ", " .. DetectedItemCoordText) + Report:Add( string.format( "Threat: [%s]", string.rep( "â– ", ThreatLevelA2G ) ) ) + Report:Add( string.format("Type: %s%s", UnitCategoryText, UnitDistanceText ) ) + return Report + end + return nil + end + + --- Report detailed of a detection result. -- @param #DETECTION_UNITS self + -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. -- @return #string - function DETECTION_UNITS:DetectedReportDetailed() + function DETECTION_UNITS:DetectedReportDetailed( AttackGroup ) self:F() - local Report = REPORT:New( "Detected units:" ) + local Report = REPORT:New() for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem - local ReportSummary = self:DetectedItemReportSummary( DetectedItemID ) - Report:Add( ReportSummary ) + local ReportSummary = self:DetectedItemReportSummary( DetectedItemID, AttackGroup ) + Report:SetTitle( "Detected units:" ) + Report:Add( ReportSummary:Text() ) end local ReportText = Report:Text() @@ -1434,7 +2068,7 @@ do -- DETECTION_TYPES return self end - + --- Make text documenting the changes of the detected zone. -- @param #DETECTION_TYPES self -- @param #DETECTION_TYPES.DetectedItem DetectedItem @@ -1449,7 +2083,7 @@ do -- DETECTION_TYPES if ChangeCode == "AU" then local MTUT = {} for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then + if ChangeUnitType ~= "ID" then MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType end end @@ -1459,7 +2093,7 @@ do -- DETECTION_TYPES if ChangeCode == "RU" then local MTUT = {} for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then + if ChangeUnitType ~= "ID" then MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType end end @@ -1477,7 +2111,7 @@ do -- DETECTION_TYPES -- For each DetectedItem, a one field array is created containing the Unit detected. -- @param #DETECTION_TYPES self -- @return #DETECTION_TYPES self - function DETECTION_TYPES:CreateDetectionSets() + function DETECTION_TYPES:CreateDetectionItems() self:F2( #self.DetectedObjects ) -- Loop the current detected items, and check if each object still exists and is detected. @@ -1485,7 +2119,7 @@ do -- DETECTION_TYPES for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT - local DetectedTypeName = DetectedItem.Type + local DetectedTypeName = DetectedItem.TypeName for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT @@ -1521,8 +2155,8 @@ do -- DETECTION_TYPES local DetectedTypeName = DetectedUnit:GetTypeName() local DetectedItem = self:GetDetectedItem( DetectedTypeName ) if not DetectedItem then - DetectedItem = self:AddDetectedItem( DetectedTypeName ) - DetectedItem.Type = DetectedUnit:GetTypeName() + DetectedItem = self:AddDetectedItem( "TYPE", DetectedTypeName ) + DetectedItem.TypeName = DetectedUnit:GetTypeName() end DetectedItem.Set:AddUnit( DetectedUnit ) @@ -1531,40 +2165,46 @@ do -- DETECTION_TYPES end end + + + -- Check if there are any friendlies nearby. for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem local DetectedSet = DetectedItem.Set + -- Set the last known coordinate. + local DetectedFirstUnit = DetectedSet:GetFirst() + local DetectedUnitCoord = DetectedFirstUnit:GetCoordinate() + self:SetDetectedItemCoordinate( DetectedItem, DetectedUnitCoord, DetectedFirstUnit ) + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table --self:NearestFAC( DetectedItem ) end + + end - - --- Report summary of a DetectedItem using a given numeric index. + + --- Menu of a DetectedItem using a given numeric index. -- @param #DETECTION_TYPES self -- @param Index -- @return #string - function DETECTION_TYPES:DetectedItemReportSummary( DetectedTypeName ) + function DETECTION_TYPES:DetectedItemMenu( DetectedTypeName, AttackGroup ) self:F( DetectedTypeName ) local DetectedItem = self:GetDetectedItem( DetectedTypeName ) - local DetectedSet = self:GetDetectedSet( DetectedTypeName ) + local DetectedItemID = self:GetDetectedItemID( DetectedTypeName ) - self:T( DetectedItem ) if DetectedItem then - local ThreatLevelA2G = DetectedSet:CalculateThreatLevelA2G() - local DetectedItemsCount = DetectedSet:Count() - local DetectedItemType = DetectedItem.Type - + local DetectedItemCoordinate = self:GetDetectedItemCoordinate( DetectedTypeName ) + local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup ) + local ReportSummary = string.format( - "Threat [%s] (%2d) - %2d of %s", - string.rep( "â– ", ThreatLevelA2G ), - ThreatLevelA2G, - DetectedItemsCount, - DetectedItemType + "%s - %s", + DetectedItemID, + DetectedItemCoordText ) self:T( ReportSummary ) @@ -1572,17 +2212,50 @@ do -- DETECTION_TYPES end end + --- Report summary of a DetectedItem using a given numeric index. + -- @param #DETECTION_TYPES self + -- @param Index + -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. + -- @param Core.Settings#SETTINGS Settings Message formatting settings to use. + -- @return Core.Report#REPORT The report of the detection items. + function DETECTION_TYPES:DetectedItemReportSummary( DetectedTypeName, AttackGroup, Settings ) + self:F( DetectedTypeName ) + + local DetectedItem = self:GetDetectedItem( DetectedTypeName ) + local DetectedSet = self:GetDetectedSet( DetectedTypeName ) + local DetectedItemID = self:GetDetectedItemID( DetectedTypeName ) + + self:T( DetectedItem ) + if DetectedItem then + + local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedTypeName ) + local DetectedItemsCount = DetectedSet:Count() + local DetectedItemType = DetectedItem.TypeName + + local DetectedItemCoordinate = self:GetDetectedItemCoordinate( DetectedTypeName ) + local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup, Settings ) + + local Report = REPORT:New() + Report:Add(DetectedItemID .. ", " .. DetectedItemCoordText) + Report:Add( string.format( "Threat: [%s]", string.rep( "â– ", ThreatLevelA2G ) ) ) + Report:Add( string.format("Type: %2d of %s", DetectedItemsCount, DetectedItemType ) ) + return Report + end + end + --- Report detailed of a detection result. -- @param #DETECTION_TYPES self + -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. -- @return #string - function DETECTION_TYPES:DetectedReportDetailed() + function DETECTION_TYPES:DetectedReportDetailed( AttackGroup ) self:F() - local Report = REPORT:New( "Detected types:" ) + local Report = REPORT:New() for DetectedItemTypeName, DetectedItem in pairs( self.DetectedItems ) do local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem - local ReportSummary = self:DetectedItemReportSummary( DetectedItemTypeName ) - Report:Add( ReportSummary ) + local ReportSummary = self:DetectedItemReportSummary( DetectedItemTypeName, AttackGroup ) + Report:SetTitle( "Detected types:" ) + Report:Add( ReportSummary:Text() ) end local ReportText = Report:Text() @@ -1657,34 +2330,30 @@ do -- DETECTION_AREAS return self end - - --- Report summary of a detected item using a given numeric index. + + + --- Menu of a detected item using a given numeric index. -- @param #DETECTION_AREAS self -- @param Index -- @return #string - function DETECTION_AREAS:DetectedItemReportSummary( Index ) + function DETECTION_AREAS:DetectedItemMenu( Index, AttackGroup ) self:F( Index ) local DetectedItem = self:GetDetectedItem( Index ) + local DetectedItemID = self:GetDetectedItemID( Index ) + if DetectedItem then local DetectedSet = self:GetDetectedSet( Index ) local ReportSummaryItem - local DetectedZone = self:GetDetectedZone( Index ) - local DetectedItemPointVec3 = DetectedZone:GetPointVec3() - local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) - - local ThreatLevelA2G = self:GetTreatLevelA2G( DetectedItem ) - local DetectedItemsCount = DetectedSet:Count() - local DetectedItemsTypes = DetectedSet:GetTypeNames() + local DetectedZone = self:GetDetectedItemZone( Index ) + local DetectedItemCoordinate = DetectedZone:GetCoordinate() + local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup ) local ReportSummary = string.format( - "%s - Threat [%s] (%2d) - %2d of %s", - DetectedItemPointLL, - string.rep( "â– ", ThreatLevelA2G ), - ThreatLevelA2G, - DetectedItemsCount, - DetectedItemsTypes + "%s - %s", + DetectedItemID, + DetectedItemCoordText ) return ReportSummary @@ -1693,75 +2362,114 @@ do -- DETECTION_AREAS return nil end - - --- Returns if there are friendlies nearby the FAC units ... + --- Report summary of a detected item using a given numeric index. -- @param #DETECTION_AREAS self - -- @return #boolean trhe if there are friendlies nearby - function DETECTION_AREAS:IsFriendliesNearBy( DetectedItem ) - - self:T3( DetectedItem.FriendliesNearBy ) - return DetectedItem.FriendliesNearBy or false - end + -- @param Index + -- @param Wrapper.Group#GROUP AttackGroup The group to get the settings for. + -- @param Core.Settings#SETTINGS Settings (Optional) Message formatting settings to use. + -- @return Core.Report#REPORT The report of the detection items. + function DETECTION_AREAS:DetectedItemReportSummary( Index, AttackGroup, Settings ) + self:F( Index ) - --- Calculate the maxium A2G threat level of the DetectedItem. + local DetectedItem = self:GetDetectedItem( Index ) + local DetectedItemID = self:GetDetectedItemID( Index ) + + if DetectedItem then + local DetectedSet = self:GetDetectedSet( Index ) + local ReportSummaryItem + + local DetectedZone = self:GetDetectedItemZone( Index ) + local DetectedItemCoordinate = DetectedZone:GetCoordinate() + local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup, Settings ) + + local ThreatLevelA2G = self:GetDetectedItemThreatLevel( Index ) + local DetectedItemsCount = DetectedSet:Count() + local DetectedItemsTypes = DetectedSet:GetTypeNames() + + local Report = REPORT:New() + Report:Add(DetectedItemID .. ", " .. DetectedItemCoordText) + Report:Add( string.format( "Threat: [%s]", string.rep( "â– ", ThreatLevelA2G ) ) ) + Report:Add( string.format("Type: %2d of %s", DetectedItemsCount, DetectedItemsTypes ) ) + + return Report + end + + return nil + end + + --- Report detailed of a detection result. + -- @param #DETECTION_AREAS self + -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. + -- @return #string + function DETECTION_AREAS:DetectedReportDetailed( AttackGroup ) --R2.1 Fixed missing report + self:F() + + local Report = REPORT:New() + for DetectedItemIndex, DetectedItem in pairs( self.DetectedItems ) do + local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem + local ReportSummary = self:DetectedItemReportSummary( DetectedItemIndex, AttackGroup ) + Report:SetTitle( "Detected areas:" ) + Report:Add( ReportSummary:Text() ) + end + + local ReportText = Report:Text() + + return ReportText + end + + + --- Calculate the optimal intercept point of the DetectedItem. -- @param #DETECTION_AREAS self -- @param #DETECTION_BASE.DetectedItem DetectedItem - function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedItem ) - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( DetectedItem.Set:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end + function DETECTION_AREAS:CalculateIntercept( DetectedItem ) + + local DetectedSpeed = DetectedItem.Coordinate.Speed + local DetectedHeading = DetectedItem.Coordinate.Heading + local DetectedCoord = DetectedItem.Coordinate + + if self.Intercept then + local DetectedSet = DetectedItem.Set + -- todo: speed - self:T3( MaxThreatLevelA2G ) - DetectedItem.MaxThreatLevelA2G = MaxThreatLevelA2G + local TranslateDistance = DetectedSpeed * self.InterceptDelay + + local InterceptCoord = DetectedCoord:Translate( TranslateDistance, DetectedHeading ) + + DetectedItem.InterceptCoord = InterceptCoord + else + DetectedItem.InterceptCoord = DetectedCoord + end end + --- Find the nearest FAC of the DetectedItem. -- @param #DETECTION_AREAS self -- @param #DETECTION_BASE.DetectedItem DetectedItem -- @return Wrapper.Unit#UNIT The nearest FAC unit function DETECTION_AREAS:NearestFAC( DetectedItem ) - local NearestFAC = nil - local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) + local NearestRecce = nil + local DistanceRecce = 1000000000 -- Units are not further than 1000000 km away from an area :-) - for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do - local FACUnit = FACUnitData -- Wrapper.Unit#UNIT - if FACUnit:IsActive() then - local Vec3 = FACUnit:GetVec3() - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) - if Distance < MinDistance then - MinDistance = Distance - NearestFAC = FACUnit + for RecceGroupName, RecceGroup in pairs( self.DetectionSetGroup:GetSet() ) do + for RecceUnit, RecceUnit in pairs( RecceGroup:GetUnits() ) do + if RecceUnit:IsActive() then + local RecceUnitCoord = RecceUnit:GetCoordinate() + local Distance = RecceUnitCoord:Get2DDistance( self:GetDetectedItemCoordinate( DetectedItem.Index ) ) + if Distance < DistanceRecce then + DistanceRecce = Distance + NearestRecce = RecceUnit end end end end - DetectedItem.NearestFAC = NearestFAC + DetectedItem.NearestFAC = NearestRecce + DetectedItem.DistanceRecce = DistanceRecce end - - --- Returns the A2G threat level of the units in the DetectedItem - -- @param #DETECTION_AREAS self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @return #number a scale from 0 to 10. - function DETECTION_AREAS:GetTreatLevelA2G( DetectedItem ) - - self:T3( DetectedItem.MaxThreatLevelA2G ) - return DetectedItem.MaxThreatLevelA2G - end - - - + --- Smoke the detected units -- @param #DETECTION_AREAS self -- @return #DETECTION_AREAS self @@ -1824,39 +2532,39 @@ do -- DETECTION_AREAS for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do if ChangeCode == "AA" then - MT[#MT+1] = "Detected new area " .. ChangeData.ItemID .. ". The center target is a " .. ChangeData.ItemUnitType .. "." + MT[#MT+1] = "Detected new area " .. ChangeData.ID .. ". The center target is a " .. ChangeData.ItemUnitType .. "." end if ChangeCode == "RAU" then - MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". Removed the center target." + MT[#MT+1] = "Changed area " .. ChangeData.ID .. ". Removed the center target." end if ChangeCode == "AAU" then - MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". The new center target is a " .. ChangeData.ItemUnitType .. "." + MT[#MT+1] = "Changed area " .. ChangeData.ID .. ". The new center target is a " .. ChangeData.ItemUnitType .. "." end if ChangeCode == "RA" then - MT[#MT+1] = "Removed old area " .. ChangeData.ItemID .. ". No more targets in this area." + MT[#MT+1] = "Removed old area " .. ChangeData.ID .. ". No more targets in this area." end if ChangeCode == "AU" then local MTUT = {} for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then + if ChangeUnitType ~= "ID" then MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType end end - MT[#MT+1] = "Detected for area " .. ChangeData.ItemID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." + MT[#MT+1] = "Detected for area " .. ChangeData.ID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." end if ChangeCode == "RU" then local MTUT = {} for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then + if ChangeUnitType ~= "ID" then MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType end end - MT[#MT+1] = "Removed for area " .. ChangeData.ItemID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." + MT[#MT+1] = "Removed for area " .. ChangeData.ID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." end end @@ -1869,7 +2577,7 @@ do -- DETECTION_AREAS --- Make a DetectionSet table. This function will be overridden in the derived clsses. -- @param #DETECTION_AREAS self -- @return #DETECTION_AREAS self - function DETECTION_AREAS:CreateDetectionSets() + function DETECTION_AREAS:CreateDetectionItems() self:F2() @@ -1879,6 +2587,7 @@ do -- DETECTION_AREAS for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem + if DetectedItem then self:T( { "Detected Item ID:", DetectedItemID } ) @@ -1905,13 +2614,14 @@ do -- DETECTION_AREAS -- First remove the center unit from the set. DetectedSet:RemoveUnitsByName( DetectedItem.Zone.ZoneUNIT.UnitName ) - self:AddChangeItem( DetectedItem, 'RAU', "Dummy" ) + self:AddChangeItem( DetectedItem, 'RAU', self:GetDetectedUnitTypeName( DetectedItem.Zone.ZoneUNIT ) ) -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) + local DetectedUnitTypeName = self:GetDetectedUnitTypeName( DetectedUnit ) -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. -- If the DetectedUnit was already identified, DetectedObject will be nil. @@ -1919,15 +2629,18 @@ do -- DETECTION_AREAS self:IdentifyDetectedObject( DetectedObject ) AreaExists = true - DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + --DetectedItem.Zone:BoundZone( 12, self.CountryID, true) -- Assign the Unit as the new center unit of the detected area. DetectedItem.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) - self:AddChangeItem( DetectedItem, "AAU", DetectedItem.Zone.ZoneUNIT:GetTypeName() ) + self:AddChangeItem( DetectedItem, "AAU", DetectedUnitTypeName ) -- We don't need to add the DetectedObject to the area set, because it is already there ... break + else + DetectedSet:Remove( DetectedUnitName ) + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitTypeName ) end end end @@ -1943,13 +2656,15 @@ do -- DETECTION_AREAS for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT + local DetectedUnitTypeName = self:GetDetectedUnitTypeName( DetectedUnit ) + local DetectedObject = nil if DetectedUnit:IsAlive() then --self:E(DetectedUnit:GetName()) DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) end if DetectedObject then - + -- Check if the DetectedUnit is within the DetectedItem.Zone if DetectedUnit:IsInZone( DetectedItem.Zone ) then @@ -1959,7 +2674,7 @@ do -- DETECTION_AREAS else -- No, the DetectedUnit is not within the DetectedItem.Zone, remove DetectedUnit from the Set. DetectedSet:Remove( DetectedUnitName ) - self:AddChangeUnit( DetectedItem, "RU", DetectedUnit:GetTypeName() ) + self:AddChangeUnit( DetectedItem, "RU", DetectedUnitTypeName ) end else @@ -1972,13 +2687,15 @@ do -- DETECTION_AREAS end end else - DetectedItem.Zone:BoundZone( 12, self.CountryID, true) + --DetectedItem.Zone:BoundZone( 12, self.CountryID, true) self:RemoveDetectedItem( DetectedItemID ) self:AddChangeItem( DetectedItem, "RA" ) end end end + + -- We iterated through the existing detection areas and: -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. -- - We recentered the detection area to new center units where it was needed. @@ -1994,6 +2711,7 @@ do -- DETECTION_AREAS -- We found an unidentified unit outside of any existing detection area. local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT + local DetectedUnitTypeName = self:GetDetectedUnitTypeName( DetectedUnit ) local AddedToDetectionArea = false @@ -2001,13 +2719,13 @@ do -- DETECTION_AREAS local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem if DetectedItem then - self:T( "Detection Area #" .. DetectedItem.ItemID ) + self:T( "Detection Area #" .. DetectedItem.ID ) local DetectedSet = DetectedItem.Set if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedItem.Zone ) then self:IdentifyDetectedObject( DetectedObject ) DetectedSet:AddUnit( DetectedUnit ) AddedToDetectionArea = true - self:AddChangeUnit( DetectedItem, "AU", DetectedUnit:GetTypeName() ) + self:AddChangeUnit( DetectedItem, "AU", DetectedUnitTypeName ) end end end @@ -2016,12 +2734,12 @@ do -- DETECTION_AREAS -- New detection area local DetectedItem = self:AddDetectedItemZone( nil, - SET_UNIT:New(), + SET_UNIT:New():FilterDeads():FilterCrashes(), ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) ) --self:E( DetectedItem.Zone.ZoneUNIT.UnitName ) DetectedItem.Set:AddUnit( DetectedUnit ) - self:AddChangeItem( DetectedItem, "AA", DetectedUnit:GetTypeName() ) + self:AddChangeItem( DetectedItem, "AA", DetectedUnitTypeName ) end end end @@ -2033,20 +2751,31 @@ do -- DETECTION_AREAS local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem local DetectedSet = DetectedItem.Set + local DetectedFirstUnit = DetectedSet:GetFirst() local DetectedZone = DetectedItem.Zone + + -- Set the last known coordinate to the detection item. + local DetectedZoneCoord = DetectedZone:GetCoordinate() + self:SetDetectedItemCoordinate( DetectedItem, DetectedZoneCoord, DetectedFirstUnit ) + + self:CalculateIntercept( DetectedItem ) self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - self:CalculateThreatLevelA2G( DetectedItem ) -- Calculate A2G threat level + self:SetDetectedItemThreatLevel( DetectedItem ) -- Calculate A2G threat level self:NearestFAC( DetectedItem ) - + + if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then DetectedZone.ZoneUNIT:SmokeRed() end + + --DetectedSet:Flush() + DetectedSet:ForEachUnit( --- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit ) if DetectedUnit:IsAlive() then - self:T( "Detected Set #" .. DetectedItem.ItemID .. ":" .. DetectedUnit:GetName() ) + --self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() ) if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then DetectedUnit:FlareGreen() end @@ -2064,6 +2793,7 @@ do -- DETECTION_AREAS end if DETECTION_AREAS._BoundDetectedZones or self._BoundDetectedZones then + self.CountryID = DetectedSet:GetFirst():GetCountry() DetectedZone:BoundZone( 12, self.CountryID ) end end diff --git a/Moose Development/Moose/Functional/Escort.lua b/Moose Development/Moose/Functional/Escort.lua index dc2d62c44..66496f973 100644 --- a/Moose Development/Moose/Functional/Escort.lua +++ b/Moose Development/Moose/Functional/Escort.lua @@ -1,4 +1,6 @@ ---- Taking the lead of AI escorting your flight. +--- **Functional** -- Taking the lead of AI escorting your flight. +-- +-- ==== -- -- @{#ESCORT} class -- ================ @@ -873,7 +875,7 @@ function ESCORT:_AttackTarget( DetectedItemID ) end, Tasks ) - Tasks[#Tasks+1] = EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } ) + Tasks[#Tasks+1] = EscortGroup:TaskFunction( "_Resume", { "''" } ) EscortGroup:SetTask( EscortGroup:TaskCombo( @@ -1159,19 +1161,23 @@ function ESCORT:_ReportTargetsScheduler() for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do local ClientEscortTargets = EscortGroupData.Detection + --local EscortUnit = EscortGroupData:GetUnit( 1 ) - for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do + for DetectedItemID, DetectedItem in pairs( DetectedItems ) do self:E( { DetectedItemID, DetectedItem } ) -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. - local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID ) + local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID, EscortGroupData.EscortGroup, _DATABASE:GetPlayerSettings( self.EscortClient:GetPlayerName() ) ) if ClientEscortGroupName == EscortGroupName then - DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary + local DetectedMsg = DetectedItemReportSummary:Text("\n") + DetectedMsgs[#DetectedMsgs+1] = DetectedMsg + + self:T( DetectedMsg ) MENU_CLIENT_COMMAND:New( self.EscortClient, - DetectedItemReportSummary, + DetectedMsg, self.EscortMenuAttackNearbyTargets, ESCORT._AttackTarget, self, @@ -1180,10 +1186,12 @@ function ESCORT:_ReportTargetsScheduler() else if self.EscortMenuTargetAssistance then - self:T( DetectedItemReportSummary ) + local DetectedMsg = DetectedItemReportSummary:Text("\n") + self:T( DetectedMsg ) + local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) MENU_CLIENT_COMMAND:New( self.EscortClient, - DetectedItemReportSummary, + DetectedMsg, MenuTargetAssistance, ESCORT._AssistTarget, self, @@ -1199,7 +1207,7 @@ function ESCORT:_ReportTargetsScheduler() end self:E( DetectedMsgs ) if DetectedTargets then - self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient ) + self.EscortGroup:MessageToClient( "Reporting detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient ) else self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient ) end diff --git a/Moose Development/Moose/Functional/MissileTrainer.lua b/Moose Development/Moose/Functional/MissileTrainer.lua index 511cbca44..75c8979f1 100644 --- a/Moose Development/Moose/Functional/MissileTrainer.lua +++ b/Moose Development/Moose/Functional/MissileTrainer.lua @@ -1,4 +1,4 @@ ---- This module contains the MISSILETRAINER class. +--- **Functional** -- MISSILETRAINER helps you to train missile avoidance. -- -- === -- @@ -442,7 +442,7 @@ function MISSILETRAINER._MenuMessages( MenuParameters ) if MenuParameters.Distance ~= nil then self.Distance = MenuParameters.Distance - MESSAGE:New( "Hit detection distance set to " .. self.Distance .. " meters", 15, "Menu" ):ToAll() + MESSAGE:New( "Hit detection distance set to " .. self.Distance * 1000 .. " meters", 15, "Menu" ):ToAll() end end @@ -570,72 +570,76 @@ function MISSILETRAINER:_TrackMissiles() for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do local Client = ClientData.Client - self:T2( { Client:GetName() } ) + + if Client and Client:IsAlive() then - for MissileDataID, MissileData in pairs( ClientData.MissileData ) do - self:T3( MissileDataID ) - - local TrainerSourceUnit = MissileData.TrainerSourceUnit - local TrainerWeapon = MissileData.TrainerWeapon - local TrainerTargetUnit = MissileData.TrainerTargetUnit - local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName - local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched + for MissileDataID, MissileData in pairs( ClientData.MissileData ) do + self:T3( MissileDataID ) - if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then - local PositionMissile = TrainerWeapon:getPosition().p - local TargetVec3 = Client:GetVec3() - - local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 + - ( PositionMissile.y - TargetVec3.y )^2 + - ( PositionMissile.z - TargetVec3.z )^2 - ) ^ 0.5 / 1000 - - if Distance <= self.Distance then - -- Hit alert - TrainerWeapon:destroy() - if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then - - self:T( "killed" ) - - local Message = MESSAGE:New( - string.format( "%s launched by %s killed %s", - TrainerWeapon:getTypeName(), - TrainerSourceUnit:GetTypeName(), - TrainerTargetUnit:GetPlayerName() - ), 15, "Hit Alert" ) - - if self.AlertsToAll == true then - Message:ToAll() - else - Message:ToClient( Client ) + local TrainerSourceUnit = MissileData.TrainerSourceUnit + local TrainerWeapon = MissileData.TrainerWeapon + local TrainerTargetUnit = MissileData.TrainerTargetUnit + local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName + local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched + + if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then + local PositionMissile = TrainerWeapon:getPosition().p + local TargetVec3 = Client:GetVec3() + + local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 + + ( PositionMissile.y - TargetVec3.y )^2 + + ( PositionMissile.z - TargetVec3.z )^2 + ) ^ 0.5 / 1000 + + if Distance <= self.Distance then + -- Hit alert + TrainerWeapon:destroy() + if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then + + self:T( "killed" ) + + local Message = MESSAGE:New( + string.format( "%s launched by %s killed %s", + TrainerWeapon:getTypeName(), + TrainerSourceUnit:GetTypeName(), + TrainerTargetUnit:GetPlayerName() + ), 15, "Hit Alert" ) + + if self.AlertsToAll == true then + Message:ToAll() + else + Message:ToClient( Client ) + end + + MissileData = nil + table.remove( ClientData.MissileData, MissileDataID ) + self:T(ClientData.MissileData) + end + end + else + if not ( TrainerWeapon and TrainerWeapon:isExist() ) then + if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then + -- Weapon does not exist anymore. Delete from Table + local Message = MESSAGE:New( + string.format( "%s launched by %s self destructed!", + TrainerWeaponTypeName, + TrainerSourceUnit:GetTypeName() + ), 5, "Tracking" ) + + if self.AlertsToAll == true then + Message:ToAll() + else + Message:ToClient( Client ) + end end - MissileData = nil table.remove( ClientData.MissileData, MissileDataID ) - self:T(ClientData.MissileData) + self:T( ClientData.MissileData ) end end - else - if not ( TrainerWeapon and TrainerWeapon:isExist() ) then - if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then - -- Weapon does not exist anymore. Delete from Table - local Message = MESSAGE:New( - string.format( "%s launched by %s self destructed!", - TrainerWeaponTypeName, - TrainerSourceUnit:GetTypeName() - ), 5, "Tracking" ) - - if self.AlertsToAll == true then - Message:ToAll() - else - Message:ToClient( Client ) - end - end - MissileData = nil - table.remove( ClientData.MissileData, MissileDataID ) - self:T( ClientData.MissileData ) - end end + else + self.TrackingMissiles[ClientDataID] = nil end end @@ -651,7 +655,7 @@ function MISSILETRAINER:_TrackMissiles() for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do local Client = ClientData.Client - self:T2( { Client:GetName() } ) + --self:T2( { Client:GetName() } ) ClientData.MessageToClient = "" @@ -661,7 +665,7 @@ function MISSILETRAINER:_TrackMissiles() for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do - self:T3( MissileDataID ) + --self:T3( MissileDataID ) local TrainerSourceUnit = MissileData.TrainerSourceUnit local TrainerWeapon = MissileData.TrainerWeapon diff --git a/Moose Development/Moose/Functional/Movement.lua b/Moose Development/Moose/Functional/Movement.lua index 72bc21c62..69317c9e5 100644 --- a/Moose Development/Moose/Functional/Movement.lua +++ b/Moose Development/Moose/Functional/Movement.lua @@ -1,4 +1,8 @@ ---- Limit the simultaneous movement of Groups within a running Mission. +--- **Functional** -- Limit the MOVEMENT of simulaneous moving ground vehicles. +-- +-- ==== +-- +-- Limit the simultaneous movement of Groups within a running Mission. -- This module is defined to improve the performance in missions, and to bring additional realism for GROUND vehicles. -- Performance: If in a DCSRTE there are a lot of moving GROUND units, then in a multi player mission, this WILL create lag if -- the main DCS execution core of your CPU is fully utilized. So, this class will limit the amount of simultaneous moving GROUND units diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 244b36957..53ed278b1 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -1,4 +1,4 @@ ---- Single-Player:**Yes** / Multi-Player:**Yes** / Core:**Yes** -- **Administer the scoring of player achievements, +--- **Functional** -- **Administer the SCORING of player achievements, -- and create a CSV file logging the scoring events for use at team or squadron websites.** -- -- ![Banner Image](..\Presentations\SCORING\Dia1.JPG) @@ -125,6 +125,32 @@ -- -- The above documents that 2 Scoring objects are created, ScoringFirstMission and ScoringSecondMission. -- +-- ### **IMPORTANT!!!* +-- In order to allow DCS world to write CSV files, you need to adapt a configuration file in your DCS world installation **on the server**. +-- For this, browse to the **missionscripting.lua** file in your DCS world installation folder. +-- For me, this installation folder is in _D:\\Program Files\\Eagle Dynamics\\DCS World\Scripts_. +-- +-- Edit a few code lines in the MissionScripting.lua file. Comment out the lines **os**, **io** and **lfs**: +-- +-- do +-- --sanitizeModule('os') +-- --sanitizeModule('io') +-- --sanitizeModule('lfs') +-- require = nil +-- loadlib = nil +-- end +-- +-- When these lines are not sanitized, functions become available to check the time, and to write files to your system at the above specified location. +-- Note that the MissionScripting.lua file provides a warning. So please beware of this warning as outlined by Eagle Dynamics! +-- +-- --Sanitize Mission Scripting environment +-- --This makes unavailable some unsecure functions. +-- --Mission downloaded from server to client may contain potentialy harmful lua code that may use these functions. +-- --You can remove the code below and make availble these functions at your own risk. +-- +-- The MOOSE designer cannot take any responsibility of any damage inflicted as a result of the de-sanitization. +-- That being said, I hope that the SCORING class provides you with a great add-on to score your squad mates achievements. +-- -- ## 1.9) Configure messages. -- -- When players hit or destroy targets, messages are sent. @@ -247,6 +273,8 @@ function SCORING:New( GameName ) -- Default penalty when a player changes coalition. self:SetCoalitionChangePenalty( self.ScaleDestroyPenalty ) + self:SetDisplayMessagePrefix() + -- Event handlers self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) @@ -261,15 +289,23 @@ function SCORING:New( GameName ) end +--- Set a prefix string that will be displayed at each scoring message sent. +-- @param #SCORING self +-- @param #string DisplayMessagePrefix (Default="Scoring: ") The scoring prefix string. +-- @return #SCORING +function SCORING:SetDisplayMessagePrefix( DisplayMessagePrefix ) + self.DisplayMessagePrefix = DisplayMessagePrefix or "" + return self +end + + --- Set the scale for scoring valid destroys (enemy destroys). -- A default calculated score is a value between 1 and 10. -- The scale magnifies the scores given to the players. -- @param #SCORING self -- @param #number Scale The scale of the score given. function SCORING:SetScaleDestroyScore( Scale ) - self.ScaleDestroyScore = Scale - return self end @@ -572,7 +608,7 @@ function SCORING:_AddPlayerFromUnit( UnitData ) if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50 self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] .. + MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] .. "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.", 2 ):ToAll() @@ -590,7 +626,7 @@ function SCORING:_AddPlayerFromUnit( UnitData ) if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then if self.Players[PlayerName].PenaltyWarning < 1 then - MESSAGE:New( "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, + MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, 30 ):ToAll() self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1 @@ -598,10 +634,10 @@ function SCORING:_AddPlayerFromUnit( UnitData ) end if self.Players[PlayerName].Penalty > self.Fratricide then - UnitData:Destroy() - MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", + MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", 10 ):ToAll() + UnitData:GetGroup():Destroy() end end @@ -632,7 +668,7 @@ function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score ) PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score PlayerData.Score = PlayerData.Score + Score - MESSAGE:New( Text, 30 ):ToAll() + MESSAGE:New( self.DisplayMessagePrefix .. Text, 30 ):ToAll() self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() ) end @@ -668,9 +704,7 @@ function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score ) PlayerData.Score = self.Players[PlayerName].Score + Score PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score - MESSAGE:New( "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " .. - Score .. " task score!", - 30 ):ToAll() + MESSAGE:New( self.DisplayMessagePrefix .. MissionName .. " : " .. Text .. " Score: " .. Score, 30 ):ToAll() self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() ) end @@ -698,7 +732,7 @@ function SCORING:_AddMissionScore( Mission, Text, Score ) PlayerData.Score = PlayerData.Score + Score PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score - MESSAGE:New( "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " .. + MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " .. Score .. " mission score!", 60 ):ToAll() @@ -729,7 +763,8 @@ function SCORING:OnEventPlayerLeaveUnit( Event ) if Event.IniUnit then local Menu = self:GetState( Event.IniUnit, "ScoringMenu" ) -- Core.Menu#MENU_GROUP if Menu then - Menu:Remove() + -- TODO: Check if this fixes #281. + --Menu:Remove() end end end @@ -867,7 +902,7 @@ function SCORING:_EventOnHit( Event ) if TargetPlayerName ~= nil then -- It is a player hitting another player ... MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. + :New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, 2 @@ -876,7 +911,7 @@ function SCORING:_EventOnHit( Event ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) else MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit a friendly target " .. + :New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, 2 @@ -891,7 +926,7 @@ function SCORING:_EventOnHit( Event ) PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 if TargetPlayerName ~= nil then -- It is a player hitting another player ... MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. + :New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, 2 @@ -900,7 +935,7 @@ function SCORING:_EventOnHit( Event ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) else MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit an enemy target " .. + :New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, 2 @@ -912,7 +947,7 @@ function SCORING:_EventOnHit( Event ) end else -- A scenery object was hit. MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit a scenery object.", + :New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.", 2 ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) @@ -973,9 +1008,9 @@ function SCORING:_EventOnHit( Event ) PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 MESSAGE - :New( "Player '" .. Event.WeaponPlayerName .. "' hit a friendly target " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. - "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + :New( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. + "Penalty: -" .. PlayerHit.Penalty .. " = " .. Player.Score - Player.Penalty, 2 ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) @@ -986,9 +1021,9 @@ function SCORING:_EventOnHit( Event ) PlayerHit.Score = PlayerHit.Score + 1 PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 MESSAGE - :New( "Player '" .. Event.WeaponPlayerName .. "' hit an enemy target " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. - "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + :New( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. + TargetUnitCategory .. " ( " .. TargetType .. " ) " .. + "Score: +" .. PlayerHit.Score .. " = " .. Player.Score - Player.Penalty, 2 ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) @@ -997,7 +1032,7 @@ function SCORING:_EventOnHit( Event ) end else -- A scenery object was hit. MESSAGE - :New( "Player '" .. Event.WeaponPlayerName .. "' hit a scenery object.", + :New( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit scenery object.", 2 ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) @@ -1096,18 +1131,18 @@ function SCORING:_EventOnDeadOrCrash( Event ) if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player MESSAGE - :New( "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. - TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " .. - "Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + :New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. + "Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty, 15 ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) else MESSAGE - :New( "Player '" .. PlayerName .. "' destroyed a friendly target " .. - TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " .. - "Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, + :New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. + "Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty, 15 ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) @@ -1130,18 +1165,18 @@ function SCORING:_EventOnDeadOrCrash( Event ) TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1 if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player MESSAGE - :New( "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. - TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " .. - "Score: " .. TargetDestroy.Score .. ". Score Total:" .. Player.Score - Player.Penalty, + :New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. + "Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty, 15 ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) else MESSAGE - :New( "Player '" .. PlayerName .. "' destroyed an enemy " .. - TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " .. - "Score: " .. TargetDestroy.Score .. ". Total:" .. Player.Score - Player.Penalty, + :New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. + TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. + "Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty, 15 ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) @@ -1156,7 +1191,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) Player.Score = Player.Score + Score TargetDestroy.Score = TargetDestroy.Score + Score MESSAGE - :New( "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " .. + :New( self.DisplayMessagePrefix .. "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " .. "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty, 15 ) @@ -1175,7 +1210,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) Player.Score = Player.Score + Score TargetDestroy.Score = TargetDestroy.Score + Score MESSAGE - :New( "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." .. + :New( self.DisplayMessagePrefix .. "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." .. "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. "Total: " .. Player.Score - Player.Penalty, 15 ) @@ -1197,7 +1232,7 @@ function SCORING:_EventOnDeadOrCrash( Event ) Player.Score = Player.Score + Score TargetDestroy.Score = TargetDestroy.Score + Score MESSAGE - :New( "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." .. + :New( self.DisplayMessagePrefix .. "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." .. "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. "Total: " .. Player.Score - Player.Penalty, 15 diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index da4bf2d0f..cd176650f 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -1,7 +1,8 @@ ---- Provides defensive behaviour to a set of SAM sites within a running Mission. +--- **Functional** -- Provides defensive behaviour to a set of SAM sites within a running Mission. +-- +-- ==== +-- -- @module Sead --- @author to be searched on the forum --- @author (co) Flightcontrol (Modified and enriched with functionality) --- The SEAD class -- @type SEAD diff --git a/Moose Development/Moose/Functional/Spawn.lua b/Moose Development/Moose/Functional/Spawn.lua index 55cdcbbdd..17f79d6db 100644 --- a/Moose Development/Moose/Functional/Spawn.lua +++ b/Moose Development/Moose/Functional/Spawn.lua @@ -1,162 +1,26 @@ ---- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** -- --- **Spawn groups of units dynamically in your missions.** +--- **Functional** -- Spawn dynamically new GROUPs in your missions. -- -- ![Banner Image](..\Presentations\SPAWN\SPAWN.JPG) -- --- === +-- ==== -- --- # 1) @{#SPAWN} class, extends @{Base#BASE} --- --- The @{#SPAWN} class allows to spawn dynamically new groups, based on pre-defined initialization settings, modifying the behaviour when groups are spawned. --- For each group to be spawned, within the mission editor, a group has to be created with the "late activation flag" set. We call this group the *"Spawn Template"* of the SPAWN object. --- A reference to this Spawn Template needs to be provided when constructing the SPAWN object, by indicating the name of the group within the mission editor in the constructor methods. --- --- Within the SPAWN object, there is an internal index that keeps track of which group from the internal group list was spawned. --- When new groups get spawned by using the SPAWN methods (see below), it will be validated whether the Limits (@{#SPAWN.Limit}) of the SPAWN object are not reached. --- When all is valid, a new group will be created by the spawning methods, and the internal index will be increased with 1. --- --- Regarding the name of new spawned groups, a _SpawnPrefix_ will be assigned for each new group created. --- If you want to have the Spawn Template name to be used as the _SpawnPrefix_ name, use the @{#SPAWN.New} constructor. --- However, when the @{#SPAWN.NewWithAlias} constructor was used, the Alias name will define the _SpawnPrefix_ name. --- Groups will follow the following naming structure when spawned at run-time: --- --- 1. Spawned groups will have the name _SpawnPrefix_#ggg, where ggg is a counter from 0 to 999. --- 2. Spawned units will have the name _SpawnPrefix_#ggg-uu, where uu is a counter from 0 to 99 for each new spawned unit belonging to the group. --- --- Some additional notes that need to be remembered: --- --- * Templates are actually groups defined within the mission editor, with the flag "Late Activation" set. As such, these groups are never used within the mission, but are used by the @{#SPAWN} module. --- * It is important to defined BEFORE you spawn new groups, a proper initialization of the SPAWN instance is done with the options you want to use. --- * When designing a mission, NEVER name groups using a "#" within the name of the group Spawn Template(s), or the SPAWN module logic won't work anymore. --- --- ## 1.1) SPAWN construction methods --- --- Create a new SPAWN object with the @{#SPAWN.New}() or the @{#SPAWN.NewWithAlias}() methods: --- --- * @{#SPAWN.New}(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition). --- * @{#SPAWN.NewWithAlias}(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition), and gives each spawned @{Group} an different name. --- --- It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned. --- The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons. --- So in principle, the group list will contain all parameters and configurations after initialization, and when groups get actually spawned, this spawning can be done quickly and efficient. --- --- ## 1.2) SPAWN initialization methods --- --- A spawn object will behave differently based on the usage of **initialization** methods, which all start with the **Init** prefix: --- --- * @{#SPAWN.InitKeepUnitNames}(): Keeps the unit names as defined within the mission editor, but note that anything after a # mark is ignored, and any spaces before and after the resulting name are removed. IMPORTANT! This method MUST be the first used after :New !!! --- * @{#SPAWN.InitLimit}(): Limits the amount of groups that can be alive at the same time and that can be dynamically spawned. --- * @{#SPAWN.InitRandomizeRoute}(): Randomize the routes of spawned groups, and for air groups also optionally the height. --- * @{#SPAWN.InitRandomizeTemplate}(): Randomize the group templates so that when a new group is spawned, a random group template is selected from one of the templates defined. --- * @{#SPAWN.InitUnControlled}(): Spawn plane groups uncontrolled. --- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a batallion in an array. --- * @{#SPAWN.InitRepeat}(): Re-spawn groups when they land at the home base. Similar methods are @{#SPAWN.InitRepeatOnLanding} and @{#SPAWN.InitRepeatOnEngineShutDown}. --- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens. --- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Unit}s in the @{Group} that is spawned within a **radius band**, given an Outer and Inner radius. --- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Zone}s that are declared using this function. Each zone can be given a probability factor. --- * @{#SPAWN.InitAIOn}(): Turns the AI On when spawning the new @{Group} object. --- * @{#SPAWN.InitAIOff}(): Turns the AI Off when spawning the new @{Group} object. --- * @{#SPAWN.InitAIOnOff}(): Turns the AI On or Off when spawning the new @{Group} object. --- --- ## 1.3) SPAWN spawning methods --- --- Groups can be spawned at different times and methods: --- --- * @{#SPAWN.Spawn}(): Spawn one new group based on the last spawned index. --- * @{#SPAWN.ReSpawn}(): Re-spawn a group based on a given index. --- * @{#SPAWN.SpawnScheduled}(): Spawn groups at scheduled but randomized intervals. You can use @{#SPAWN.SpawnScheduleStart}() and @{#SPAWN.SpawnScheduleStop}() to start and stop the schedule respectively. --- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air). --- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ). --- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}. --- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}. --- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}. --- --- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object. --- You can use the @{GROUP} object to do further actions with the DCSGroup. --- --- ## 1.4) Retrieve alive GROUPs spawned by the SPAWN object --- --- The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution. --- Every time a SPAWN object spawns a new GROUP object, a reference to the GROUP object is added to an internal table of GROUPS. --- SPAWN provides methods to iterate through that internal GROUP object reference table: --- --- * @{#SPAWN.GetFirstAliveGroup}(): Will find the first alive GROUP it has spawned, and return the alive GROUP object and the first Index where the first alive GROUP object has been found. --- * @{#SPAWN.GetNextAliveGroup}(): Will find the next alive GROUP object from a given Index, and return a reference to the alive GROUP object and the next Index where the alive GROUP has been found. --- * @{#SPAWN.GetLastAliveGroup}(): Will find the last alive GROUP object, and will return a reference to the last live GROUP object and the last Index where the last alive GROUP object has been found. --- --- You can use the methods @{#SPAWN.GetFirstAliveGroup}() and sequently @{#SPAWN.GetNextAliveGroup}() to iterate through the alive GROUPS within the SPAWN object, and to actions... See the respective methods for an example. --- The method @{#SPAWN.GetGroupFromIndex}() will return the GROUP object reference from the given Index, dead or alive... --- --- ## 1.5) SPAWN object cleaning --- --- Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive. --- In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't, --- and it may occur that no new groups are or can be spawned as limits are reached. --- To prevent this, a @{#SPAWN.InitCleanUp}() initialization method has been defined that will silently monitor the status of each spawned group. --- Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time. --- There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"... --- In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically. --- This models AI that has succesfully returned to their airbase, to restart their combat activities. --- Check the @{#SPAWN.InitCleanUp}() for further info. --- --- ## 1.6) Catch the @{Group} spawn event in a callback function! --- --- When using the SpawnScheduled method, new @{Group}s are created following the schedule timing parameters. --- When a new @{Group} is spawned, you maybe want to execute actions with that group spawned at the spawn event. --- To SPAWN class supports this functionality through the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method, which takes a function as a parameter that you can define locally. --- Whenever a new @{Group} is spawned, the given function is called, and the @{Group} that was just spawned, is given as a parameter. --- As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned @{Group} object. --- A coding example is provided at the description of the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method. +-- The documentation of the SPAWN class can be found further in this document. -- -- ==== -- --- # **API CHANGE HISTORY** +-- # Demo Missions -- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- ### [SPAWN Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPA%20-%20Spawning) -- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. +-- ### [SPAWN Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPA%20-%20Spawning) +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) -- --- Hereby the change log: +-- ==== -- --- 2017-03-14: SPAWN:**InitKeepUnitNames()** added. --- 2017-03-14: SPAWN:**InitRandomizePosition( RandomizePosition, OuterRadious, InnerRadius )** added. +-- # YouTube Channel -- --- 2017-02-04: SPAWN:InitUnControlled( **UnControlled** ) replaces SPAWN:InitUnControlled(). --- --- 2017-01-24: SPAWN:**InitAIOnOff( AIOnOff )** added. --- --- 2017-01-24: SPAWN:**InitAIOn()** added. --- --- 2017-01-24: SPAWN:**InitAIOff()** added. --- --- 2016-08-15: SPAWN:**InitCleanUp**( SpawnCleanUpInterval ) replaces SPAWN:_CleanUp_( SpawnCleanUpInterval ). --- --- 2016-08-15: SPAWN:**InitRandomizeZones( SpawnZones )** added. --- --- 2016-08-14: SPAWN:**OnSpawnGroup**( SpawnCallBackFunction, ... ) replaces SPAWN:_SpawnFunction_( SpawnCallBackFunction, ... ). --- --- 2016-08-14: SPAWN.SpawnInZone( Zone, __RandomizeGroup__, SpawnIndex ) replaces SpawnInZone( Zone, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ). --- --- 2016-08-14: SPAWN.SpawnFromVec3( Vec3, SpawnIndex ) replaces SpawnFromVec3( Vec3, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ): --- --- 2016-08-14: SPAWN.SpawnFromVec2( Vec2, SpawnIndex ) replaces SpawnFromVec2( Vec2, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ): --- --- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromUnit( SpawnUnit, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ): --- --- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromStatic( SpawnStatic, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ): --- --- 2016-08-14: SPAWN.**InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )** added: --- --- 2016-08-14: SPAWN.**Init**Limit( SpawnMaxUnitsAlive, SpawnMaxGroups ) replaces SPAWN._Limit_( SpawnMaxUnitsAlive, SpawnMaxGroups ): --- --- 2016-08-14: SPAWN.**Init**Array( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) replaces SPAWN._Array_( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ). --- --- 2016-08-14: SPAWN.**Init**RandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) replaces SPAWN._RandomizeRoute_( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ). --- --- 2016-08-14: SPAWN.**Init**RandomizeTemplate( SpawnTemplatePrefixTable ) replaces SPAWN._RandomizeTemplate_( SpawnTemplatePrefixTable ). --- --- 2016-08-14: SPAWN.**Init**UnControlled() replaces SPAWN._UnControlled_(). +-- ### [SPAWN YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL) -- -- === -- @@ -173,11 +37,11 @@ -- -- @module Spawn +----BASE:TraceClass("SPAWN") --- SPAWN Class -- @type SPAWN --- @extends Core.Base#BASE -- @field ClassName -- @field #string SpawnTemplatePrefix -- @field #string SpawnAliasPrefix @@ -186,6 +50,214 @@ -- @field #number SpawnIndex -- @field #number MaxAliveGroups -- @field #SPAWN.SpawnZoneTable SpawnZoneTable +-- @extends Core.Base#BASE + + +--- # SPAWN class, extends @{Base#BASE} +-- +-- The SPAWN class allows to spawn dynamically new groups. +-- Each SPAWN object needs to be have a related **template group** setup in the Mission Editor (ME), +-- which is a normal group with the **Late Activation** flag set. +-- This template group will never be activated in your mission. +-- SPAWN uses that **template group** to reference to all the characteristics +-- (air, ground, livery, unit composition, formation, skill level etc) of each new group to be spawned. +-- +-- Therefore, when creating a SPAWN object, the @{#SPAWN.New} and @{#SPAWN.NewWithAlias} require +-- **the name of the template group** to be given as a string to those constructor methods. +-- +-- Initialization settings can be applied on the SPAWN object, +-- which modify the behaviour or the way groups are spawned. +-- These initialization methods have the prefix **Init**. +-- There are also spawn methods with the prefix **Spawn** and will spawn new groups in various ways. +-- +-- ### IMPORTANT! The methods with prefix **Init** must be used before any methods with prefix **Spawn** method are used, or unexpected results may appear!!! +-- +-- Because SPAWN can spawn multiple groups of a template group, +-- SPAWN has an **internal index** that keeps track +-- which was the latest group that was spawned. +-- +-- **Limits** can be set on how many groups can be spawn in each SPAWN object, +-- using the method @{#SPAWN.InitLimit}. SPAWN has 2 kind of limits: +-- +-- * The maximum amount of @{Unit}s that can be **alive** at the same time... +-- * The maximum amount of @{Group}s that can be **spawned**... This is more of a **resource**-type of limit. +-- +-- When new groups get spawned using the **Spawn** methods, +-- it will be evaluated whether any limits have been reached. +-- When no spawn limit is reached, a new group will be created by the spawning methods, +-- and the internal index will be increased with 1. +-- +-- These limits ensure that your mission does not accidentally get flooded with spawned groups. +-- Additionally, it also guarantees that independent of the group composition, +-- at any time, the most optimal amount of groups are alive in your mission. +-- For example, if your template group has a group composition of 10 units, and you specify a limit of 100 units alive at the same time, +-- with unlimited resources = :InitLimit( 100, 0 ) and 10 groups are alive, but two groups have only one unit alive in the group, +-- then a sequent Spawn(Scheduled) will allow a new group to be spawned!!! +-- +-- ### IMPORTANT!! If a limit has been reached, it is possible that a **Spawn** method returns **nil**, meaning, no @{Group} had been spawned!!! +-- +-- Spawned groups get **the same name** as the name of the template group. +-- Spawned units in those groups keep _by default_ **the same name** as the name of the template group. +-- However, because multiple groups and units are created from the template group, +-- a suffix is added to each spawned group and unit. +-- +-- Newly spawned groups will get the following naming structure at run-time: +-- +-- 1. Spawned groups will have the name _GroupName_#_nnn_, where _GroupName_ is the name of the **template group**, +-- and _nnn_ is a **counter from 0 to 999**. +-- 2. Spawned units will have the name _GroupName_#_nnn_-_uu_, +-- where _uu_ is a **counter from 0 to 99** for each new spawned unit belonging to the group. +-- +-- That being said, there is a way to keep the same unit names! +-- The method @{#SPAWN.InitKeepUnitNames}() will keep the same unit names as defined within the template group, thus: +-- +-- 3. Spawned units will have the name _UnitName_#_nnn_-_uu_, +-- where _UnitName_ is the **unit name as defined in the template group*, +-- and _uu_ is a **counter from 0 to 99** for each new spawned unit belonging to the group. +-- +-- Some **additional notes that need to be considered!!**: +-- +-- * templates are actually groups defined within the mission editor, with the flag "Late Activation" set. +-- As such, these groups are never used within the mission, but are used by the @{#SPAWN} module. +-- * It is important to defined BEFORE you spawn new groups, +-- a proper initialization of the SPAWN instance is done with the options you want to use. +-- * When designing a mission, NEVER name groups using a "#" within the name of the group Spawn template(s), +-- or the SPAWN module logic won't work anymore. +-- +-- ## SPAWN construction methods +-- +-- Create a new SPAWN object with the @{#SPAWN.New}() or the @{#SPAWN.NewWithAlias}() methods: +-- +-- * @{#SPAWN.New}(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition). +-- * @{#SPAWN.NewWithAlias}(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition), and gives each spawned @{Group} an different name. +-- +-- It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned. +-- The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons. +-- So in principle, the group list will contain all parameters and configurations after initialization, and when groups get actually spawned, this spawning can be done quickly and efficient. +-- +-- ## SPAWN **Init**ialization methods +-- +-- A spawn object will behave differently based on the usage of **initialization** methods, which all start with the **Init** prefix: +-- +-- ### Unit Names +-- +-- * @{#SPAWN.InitKeepUnitNames}(): Keeps the unit names as defined within the mission editor, but note that anything after a # mark is ignored, and any spaces before and after the resulting name are removed. IMPORTANT! This method MUST be the first used after :New !!! +-- +-- ### Route randomization +-- +-- * @{#SPAWN.InitRandomizeRoute}(): Randomize the routes of spawned groups, and for air groups also optionally the height. +-- +-- ### Group composition randomization +-- +-- * @{#SPAWN.InitRandomizeTemplate}(): Randomize the group templates so that when a new group is spawned, a random group template is selected from one of the templates defined. +-- +-- ### Uncontrolled +-- +-- * @{#SPAWN.InitUnControlled}(): Spawn plane groups uncontrolled. +-- +-- ### Array formation +-- +-- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a batallion in an array. +-- +-- ### Position randomization +-- +-- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens. +-- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Unit}s in the @{Group} that is spawned within a **radius band**, given an Outer and Inner radius. +-- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Zone}s that are declared using this function. Each zone can be given a probability factor. +-- +-- ### Enable / Disable AI when spawning a new @{Group} +-- +-- * @{#SPAWN.InitAIOn}(): Turns the AI On when spawning the new @{Group} object. +-- * @{#SPAWN.InitAIOff}(): Turns the AI Off when spawning the new @{Group} object. +-- * @{#SPAWN.InitAIOnOff}(): Turns the AI On or Off when spawning the new @{Group} object. +-- +-- ### Limit scheduled spawning +-- +-- * @{#SPAWN.InitLimit}(): Limits the amount of groups that can be alive at the same time and that can be dynamically spawned. +-- +-- ### Delay initial scheduled spawn +-- +-- * @{#SPAWN.InitDelayOnOff}(): Turns the inital delay On/Off when scheduled spawning the first @{Group} object. +-- * @{#SPAWN.InitDelayOn}(): Turns the inital delay On when scheduled spawning the first @{Group} object. +-- * @{#SPAWN.InitDelayOff}(): Turns the inital delay Off when scheduled spawning the first @{Group} object. +-- +-- ### Repeat spawned @{Group}s upon landing +-- +-- * @{#SPAWN.InitRepeat}() or @{#SPAWN.InitRepeatOnLanding}(): This method is used to re-spawn automatically the same group after it has landed. +-- * @{#SPAWN.InitRepeatOnEngineShutDown}(): This method is used to re-spawn automatically the same group after it has landed and it shuts down the engines at the ramp. +-- +-- +-- ## SPAWN **Spawn** methods +-- +-- Groups can be spawned at different times and methods: +-- +-- ### **Single** spawning methods +-- +-- * @{#SPAWN.Spawn}(): Spawn one new group based on the last spawned index. +-- * @{#SPAWN.ReSpawn}(): Re-spawn a group based on a given index. +-- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air). +-- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ). +-- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}. +-- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}. +-- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}. +-- +-- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object. +-- You can use the @{GROUP} object to do further actions with the DCSGroup. +-- +-- ### **Scheduled** spawning methods +-- +-- * @{#SPAWN.SpawnScheduled}(): Spawn groups at scheduled but randomized intervals. +-- * @{#SPAWN.SpawnScheduledStart}(): Start or continue to spawn groups at scheduled time intervals. +-- * @{#SPAWN.SpawnScheduledStop}(): Stop the spawning of groups at scheduled time intervals. +-- +-- +-- +-- ## Retrieve alive GROUPs spawned by the SPAWN object +-- +-- The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution. +-- Every time a SPAWN object spawns a new GROUP object, a reference to the GROUP object is added to an internal table of GROUPS. +-- SPAWN provides methods to iterate through that internal GROUP object reference table: +-- +-- * @{#SPAWN.GetFirstAliveGroup}(): Will find the first alive GROUP it has spawned, and return the alive GROUP object and the first Index where the first alive GROUP object has been found. +-- * @{#SPAWN.GetNextAliveGroup}(): Will find the next alive GROUP object from a given Index, and return a reference to the alive GROUP object and the next Index where the alive GROUP has been found. +-- * @{#SPAWN.GetLastAliveGroup}(): Will find the last alive GROUP object, and will return a reference to the last live GROUP object and the last Index where the last alive GROUP object has been found. +-- +-- You can use the methods @{#SPAWN.GetFirstAliveGroup}() and sequently @{#SPAWN.GetNextAliveGroup}() to iterate through the alive GROUPS within the SPAWN object, and to actions... See the respective methods for an example. +-- The method @{#SPAWN.GetGroupFromIndex}() will return the GROUP object reference from the given Index, dead or alive... +-- +-- ## Spawned cleaning of inactive groups +-- +-- Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive. +-- In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't, +-- and it may occur that no new groups are or can be spawned as limits are reached. +-- To prevent this, a @{#SPAWN.InitCleanUp}() initialization method has been defined that will silently monitor the status of each spawned group. +-- Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time. +-- There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"... +-- In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically. +-- This models AI that has succesfully returned to their airbase, to restart their combat activities. +-- Check the @{#SPAWN.InitCleanUp}() for further info. +-- +-- ## Catch the @{Group} Spawn Event in a callback function! +-- +-- When using the @{#SPAWN.SpawnScheduled)() method, new @{Group}s are created following the spawn time interval parameters. +-- When a new @{Group} is spawned, you maybe want to execute actions with that group spawned at the spawn event. +-- The SPAWN class supports this functionality through the method @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ), +-- which takes a function as a parameter that you can define locally. +-- Whenever a new @{Group} is spawned, the given function is called, and the @{Group} that was just spawned, is given as a parameter. +-- As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned @{Group} object. +-- A coding example is provided at the description of the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method. +-- +-- ## Delay the initial spawning +-- +-- When using the @{#SPAWN.SpawnScheduled)() method, the default behaviour of this method will be that it will spawn the initial (first) @{Group} +-- immediately when :SpawnScheduled() is initiated. The methods @{#SPAWN.InitDelayOnOff}() and @{#SPAWN.InitDelayOn}() can be used to +-- activate a delay before the first @{Group} is spawned. For completeness, a method @{#SPAWN.InitDelayOff}() is also available, that +-- can be used to switch off the initial delay. Because there is no delay by default, this method would only be used when a +-- @{#SPAWN.SpawnScheduledStop}() ; @{#SPAWN.SpawnScheduledStart}() sequence would have been used. +-- +-- +-- @field #SPAWN SPAWN +-- SPAWN = { ClassName = "SPAWN", SpawnTemplatePrefix = nil, @@ -193,6 +265,14 @@ SPAWN = { } +--- Enumerator for spawns at airbases +-- @type SPAWN.Takeoff +-- @extends Wrapper.Group#GROUP.Takeoff + +--- @field #SPAWN.Takeoff Takeoff +SPAWN.Takeoff = GROUP.Takeoff + + --- @type SPAWN.SpawnZoneTable -- @list SpawnZone @@ -227,6 +307,8 @@ function SPAWN:New( SpawnTemplatePrefix ) self.AIOnOff = true -- The AI is on by default when spawning a group. self.SpawnUnControlled = false self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. + self.DelayOnOff = false -- No intial delay when spawning the first group. + self.Grouping = nil -- No grouping self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else @@ -270,6 +352,8 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) self.AIOnOff = true -- The AI is on by default when spawning a group. self.SpawnUnControlled = false self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. + self.DelayOnOff = false -- No intial delay when spawning the first group. + self.Grouping = nil self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else @@ -436,6 +520,20 @@ function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable ) return self end +--- When spawning a new group, make the grouping of the units according the InitGrouping setting. +-- @param #SPAWN self +-- @param #number Grouping Indicates the maximum amount of units in the group. +-- @return #SPAWN +function SPAWN:InitGrouping( Grouping ) -- R2.2 + self:F( { self.SpawnTemplatePrefix, Grouping } ) + + self.SpawnGrouping = Grouping + + return self +end + + + --TODO: Add example. --- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types. -- @param #SPAWN self @@ -626,6 +724,36 @@ do -- AI methods end -- AI methods +do -- Delay methods + --- Turns the Delay On or Off for the first @{Group} scheduled spawning. + -- The default value is that for scheduled spawning, there is an initial delay when spawning the first @{Group}. + -- @param #SPAWN self + -- @param #boolean DelayOnOff A value of true sets the Delay On, a value of false sets the Delay Off. + -- @return #SPAWN The SPAWN object + function SPAWN:InitDelayOnOff( DelayOnOff ) + + self.DelayOnOff = DelayOnOff + return self + end + + --- Turns the Delay On for the @{Group} when spawning. + -- @param #SPAWN self + -- @return #SPAWN The SPAWN object + function SPAWN:InitDelayOn() + + return self:InitDelayOnOff( true ) + end + + --- Turns the Delay Off for the @{Group} when spawning. + -- @param #SPAWN self + -- @return #SPAWN The SPAWN object + function SPAWN:InitDelayOff() + + return self:InitDelayOnOff( false ) + end + +end -- Delay methods + --- Will spawn a group based on the internal index. -- Note: Uses @{DATABASE} module defined in MOOSE. -- @param #SPAWN self @@ -669,6 +797,8 @@ function SPAWN:ReSpawn( SpawnIndex ) SpawnGroup:ReSpawnFunction() end + SpawnGroup:ResetEvents() + return SpawnGroup end @@ -750,7 +880,10 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) -- If there is a SpawnFunction hook defined, call it. if self.SpawnFunctionHook then - self.SpawnFunctionHook( self.SpawnGroups[self.SpawnIndex].Group, unpack( self.SpawnFunctionArguments ) ) + -- delay calling this for .1 seconds so that it hopefully comes after the BIRTH event of the group. + self.SpawnHookScheduler = SCHEDULER:New() + self.SpawnHookScheduler:Schedule( nil, self.SpawnFunctionHook, { self.SpawnGroups[self.SpawnIndex].Group, unpack( self.SpawnFunctionArguments)}, 0.1 ) + -- self.SpawnFunctionHook( self.SpawnGroups[self.SpawnIndex].Group, unpack( self.SpawnFunctionArguments ) ) end -- TODO: Need to fix this by putting an "R" in the name of the group when the group repeats. --if self.Repeat then @@ -787,7 +920,11 @@ function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation ) self:F( { SpawnTime, SpawnTimeVariation } ) if SpawnTime ~= nil and SpawnTimeVariation ~= nil then - self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, 1, SpawnTime, SpawnTimeVariation ) + local InitialDelay = 0 + if self.DelayOnOff == true then + InitialDelay = math.random( SpawnTime - SpawnTime * SpawnTimeVariation, SpawnTime + SpawnTime * SpawnTimeVariation ) + end + self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, InitialDelay, SpawnTime, SpawnTimeVariation ) end return self @@ -795,17 +932,23 @@ end --- Will re-start the spawning scheduler. -- Note: This method is only required to be called when the schedule was stopped. +-- @param #SPAWN self +-- @return #SPAWN function SPAWN:SpawnScheduleStart() self:F( { self.SpawnTemplatePrefix } ) self.SpawnScheduler:Start() + return self end --- Will stop the scheduled spawning scheduler. +-- @param #SPAWN self +-- @return #SPAWN function SPAWN:SpawnScheduleStop() self:F( { self.SpawnTemplatePrefix } ) self.SpawnScheduler:Stop() + return self end @@ -839,6 +982,73 @@ function SPAWN:OnSpawnGroup( SpawnCallBackFunction, ... ) return self end +--- Will spawn a group at an airbase. +-- This method is mostly advisable to be used if you want to simulate spawning units at an airbase. +-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. +-- You can use the returned group to further define the route to be followed. +-- @param #SPAWN self +-- @param Wrapper.Airbase#AIRBASE Airbase The @{Airbase} where to spawn the group. +-- @param #SPAWN.Takeoff Takeoff (optional) The location and takeoff method. Default is Hot. +-- @param #number TakeoffAltitude (optional) The altitude above the ground. +-- @return Wrapper.Group#GROUP that was spawned. +-- @return #nil Nothing was spawned. +function SPAWN:SpawnAtAirbase( Airbase, Takeoff, TakeoffAltitude ) -- R2.2 + self:E( { self.SpawnTemplatePrefix, Airbase, Takeoff, TakeoffAltitude } ) + + local PointVec3 = Airbase:GetPointVec3() + self:T2(PointVec3) + + Takeoff = Takeoff or SPAWN.Takeoff.Hot + + if self:_GetSpawnIndex( self.SpawnIndex + 1 ) then + + local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate + + if SpawnTemplate then + + self:T( { "Current point of ", self.SpawnTemplatePrefix, Airbase } ) + + -- Translate the position of the Group Template to the Vec3. + for UnitID = 1, #SpawnTemplate.units do + self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) + local UnitTemplate = SpawnTemplate.units[UnitID] + local SX = UnitTemplate.x + local SY = UnitTemplate.y + local BX = SpawnTemplate.route.points[1].x + local BY = SpawnTemplate.route.points[1].y + local TX = PointVec3.x + ( SX - BX ) + local TY = PointVec3.z + ( SY - BY ) + SpawnTemplate.units[UnitID].x = TX + SpawnTemplate.units[UnitID].y = TY + if Takeoff == GROUP.Takeoff.Air then + SpawnTemplate.units[UnitID].alt = PointVec3.y + ( TakeoffAltitude or 200 ) + else + SpawnTemplate.units[UnitID].alt = PointVec3.y + 10 + end + self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) + end + + SpawnTemplate.route.points[1].x = PointVec3.x + SpawnTemplate.route.points[1].y = PointVec3.z + if Takeoff == GROUP.Takeoff.Air then + SpawnTemplate.route.points[1].alt = PointVec3.y + ( TakeoffAltitude or 200 ) + else + SpawnTemplate.route.points[1].alt = PointVec3.y + 10 + SpawnTemplate.route.points[1].airdromeId = Airbase:GetID() + end + SpawnTemplate.route.points[1].type = GROUPTEMPLATE.Takeoff[Takeoff] + + SpawnTemplate.x = PointVec3.x + SpawnTemplate.y = PointVec3.z + + return self:SpawnWithIndex( self.SpawnIndex ) + end + end + + return nil +end + + --- Will spawn a group from a Vec3 in 3D space. -- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes. @@ -926,7 +1136,7 @@ end function SPAWN:SpawnFromUnit( HostUnit, SpawnIndex ) self:F( { self.SpawnTemplatePrefix, HostUnit, SpawnIndex } ) - if HostUnit and HostUnit:IsAlive() then -- and HostUnit:getUnit(1):inAir() == false then + if HostUnit and HostUnit:IsAlive() ~= nil then -- and HostUnit:getUnit(1):inAir() == false then return self:SpawnFromVec3( HostUnit:GetVec3(), SpawnIndex ) end @@ -994,6 +1204,19 @@ function SPAWN:InitUnControlled( UnControlled ) end +--- Get the Coordinate of the Group that is Late Activated as the template for the SPAWN object. +-- @param #SPAWN self +-- @return Core.Point#COORDINATE The Coordinate +function SPAWN:GetCoordinate() + + local LateGroup = GROUP:FindByName( self.SpawnTemplatePrefix ) + if LateGroup then + return LateGroup:GetCoordinate() + end + + return nil +end + --- Will return the SpawnGroupName either with with a specific count number or without any count. -- @param #SPAWN self @@ -1252,7 +1475,7 @@ end -- @param #string SpawnTemplatePrefix -- @param #number SpawnIndex -- @return #SPAWN self -function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) +function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) --R2.2 self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) local SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) @@ -1267,6 +1490,23 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) SpawnTemplate.visible = false end + if self.SpawnGrouping then + local UnitAmount = #SpawnTemplate.units + self:F( { UnitAmount = UnitAmount, SpawnGrouping = self.SpawnGrouping } ) + if UnitAmount > self.SpawnGrouping then + for UnitID = self.SpawnGrouping + 1, UnitAmount do + SpawnTemplate.units[UnitID] = nil + end + else + if UnitAmount < self.SpawnGrouping then + for UnitID = UnitAmount + 1, self.SpawnGrouping do + SpawnTemplate.units[UnitID] = UTILS.DeepCopy( SpawnTemplate.units[1] ) + SpawnTemplate.units[UnitID].unitId = nil + end + end + end + end + if self.SpawnInitKeepUnitNames == false then for UnitID = 1, #SpawnTemplate.units do SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID ) @@ -1480,11 +1720,13 @@ function SPAWN:_OnBirth( EventData ) if SpawnGroup then local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self.AliveUnits = self.AliveUnits + 1 - self:T( "Alive Units: " .. self.AliveUnits ) - end + if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! + self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + self.AliveUnits = self.AliveUnits + 1 + self:T( "Alive Units: " .. self.AliveUnits ) + end + end end end @@ -1501,11 +1743,13 @@ function SPAWN:_OnDeadOrCrash( EventData ) if SpawnGroup then local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "Dead event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self.AliveUnits = self.AliveUnits - 1 - self:T( "Alive Units: " .. self.AliveUnits ) - end + if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! + self:T( { "Dead event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + self.AliveUnits = self.AliveUnits - 1 + self:T( "Alive Units: " .. self.AliveUnits ) + end + end end end @@ -1519,10 +1763,12 @@ function SPAWN:_OnTakeOff( EventData ) local SpawnGroup = EventData.IniGroup if SpawnGroup then local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "TakeOff event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self:T( "self.Landed = false" ) - SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", false ) + if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! + self:T( { "TakeOff event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + self:T( "self.Landed = false" ) + SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", false ) + end end end end @@ -1537,16 +1783,18 @@ function SPAWN:_OnLand( EventData ) local SpawnGroup = EventData.IniGroup if SpawnGroup then local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "Land event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - -- TODO: Check if this is the last unit of the group that lands. - SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", true ) - if self.RepeatOnLanding then - local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) - self:T( { "Landed:", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) - self:ReSpawn( SpawnGroupIndex ) - end - end + if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! + self:T( { "Land event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + -- TODO: Check if this is the last unit of the group that lands. + SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", true ) + if self.RepeatOnLanding then + local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) + self:T( { "Landed:", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) + self:ReSpawn( SpawnGroupIndex ) + end + end + end end end @@ -1561,16 +1809,18 @@ function SPAWN:_OnEngineShutDown( EventData ) local SpawnGroup = EventData.IniGroup if SpawnGroup then local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "EngineShutdown event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - -- todo: test if on the runway - local Landed = SpawnGroup:GetState( SpawnGroup, "Spawn_Landed" ) - if Landed and self.RepeatOnEngineShutDown then - local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) - self:T( { "EngineShutDown: ", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) - self:ReSpawn( SpawnGroupIndex ) - end - end + if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! + self:T( { "EngineShutdown event: " .. EventPrefix } ) + if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then + -- todo: test if on the runway + local Landed = SpawnGroup:GetState( SpawnGroup, "Spawn_Landed" ) + if Landed and self.RepeatOnEngineShutDown then + local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) + self:T( { "EngineShutDown: ", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) + self:ReSpawn( SpawnGroupIndex ) + end + end + end end end diff --git a/Moose Development/Moose/Moose.lua b/Moose Development/Moose/Moose.lua index fe1d1e2bf..25308e705 100644 --- a/Moose Development/Moose/Moose.lua +++ b/Moose Development/Moose/Moose.lua @@ -1,69 +1,3 @@ ---- The main include file for the MOOSE system. --- Test of permissions - ---- Core Routines -Include.File( "Utilities/Routines" ) -Include.File( "Utilities/Utils" ) - ---- Core Classes -Include.File( "Core/Base" ) -Include.File( "Core/Scheduler" ) -Include.File( "Core/ScheduleDispatcher") -Include.File( "Core/Event" ) -Include.File( "Core/Menu" ) -Include.File( "Core/Zone" ) -Include.File( "Core/Database" ) -Include.File( "Core/Set" ) -Include.File( "Core/Point" ) -Include.File( "Core/Message" ) -Include.File( "Core/Fsm" ) -Include.File( "Core/Radio" ) - ---- Wrapper Classes -Include.File( "Wrapper/Object" ) -Include.File( "Wrapper/Identifiable" ) -Include.File( "Wrapper/Positionable" ) -Include.File( "Wrapper/Controllable" ) -Include.File( "Wrapper/Group" ) -Include.File( "Wrapper/Unit" ) -Include.File( "Wrapper/Client" ) -Include.File( "Wrapper/Static" ) -Include.File( "Wrapper/Airbase" ) -Include.File( "Wrapper/Scenery" ) - ---- Functional Classes -Include.File( "Functional/Scoring" ) -Include.File( "Functional/CleanUp" ) -Include.File( "Functional/Spawn" ) -Include.File( "Functional/Movement" ) -Include.File( "Functional/Sead" ) -Include.File( "Functional/Escort" ) -Include.File( "Functional/MissileTrainer" ) -Include.File( "Functional/AirbasePolice" ) -Include.File( "Functional/Detection" ) - ---- AI Classes -Include.File( "AI/AI_Balancer" ) -Include.File( "AI/AI_Patrol" ) -Include.File( "AI/AI_Cap" ) -Include.File( "AI/AI_Cas" ) -Include.File( "AI/AI_Cargo" ) - ---- Actions -Include.File( "Actions/Act_Assign" ) -Include.File( "Actions/Act_Route" ) -Include.File( "Actions/Act_Account" ) -Include.File( "Actions/Act_Assist" ) - ---- Task Handling Classes -Include.File( "Tasking/CommandCenter" ) -Include.File( "Tasking/Mission" ) -Include.File( "Tasking/Task" ) -Include.File( "Tasking/DetectionManager" ) -Include.File( "Tasking/Task_A2G_Dispatcher") -Include.File( "Tasking/Task_A2G" ) - - -- The order of the declarations is important here. Don't touch it. --- Declare the event dispatcher based on the EVENT class @@ -73,7 +7,7 @@ _EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT _SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.Timer#SCHEDULEDISPATCHER --- Declare the main database object, which is used internally by the MOOSE classes. -_DATABASE = DATABASE:New() -- Database#DATABASE - +_DATABASE = DATABASE:New() -- Core.Database#DATABASE +_SETTINGS = SETTINGS:Set() diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 09ad2ab23..5fbefd0df 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -1,50 +1,19 @@ ---- A COMMANDCENTER is the owner of multiple missions within MOOSE. +--- **Tasking** -- A COMMANDCENTER is the owner of multiple missions within MOOSE. -- A COMMANDCENTER governs multiple missions, the tasking and the reporting. +-- +-- === +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- -- @module CommandCenter ---- The REPORT class --- @type REPORT --- @extends Core.Base#BASE -REPORT = { - ClassName = "REPORT", -} ---- Create a new REPORT. --- @param #REPORT self --- @param #string Title --- @return #REPORT -function REPORT:New( Title ) - - local self = BASE:Inherit( self, BASE:New() ) - - self.Report = {} - if Title then - self.Report[#self.Report+1] = Title - end - - return self -end - ---- Add a new line to a REPORT. --- @param #REPORT self --- @param #string Text --- @return #REPORT -function REPORT:Add( Text ) - self.Report[#self.Report+1] = Text - return self.Report[#self.Report] -end - ---- Produces the text of the report, taking into account an optional delimeter, which is \n by default. --- @param #REPORT self --- @param #string Delimiter (optional) A delimiter text. --- @return #string The report text. -function REPORT:Text( Delimiter ) - Delimiter = Delimiter or "\n" - local ReportText = table.concat( self.Report, Delimiter ) or "" - return ReportText -end --- The COMMANDCENTER class -- @type COMMANDCENTER @@ -52,13 +21,64 @@ end -- @field Dcs.DCSCoalitionWrapper.Object#coalition CommandCenterCoalition -- @list Missions -- @extends Core.Base#BASE + + +--- # COMMANDCENTER class, extends @{Base#BASE} +-- +-- The COMMANDCENTER class governs multiple missions, the tasking and the reporting. +-- +-- The commandcenter communicates important messages between the various groups of human players executing tasks in missions. +-- +-- ## COMMANDCENTER constructor +-- +-- * @{#COMMANDCENTER.New}(): Creates a new COMMANDCENTER object. +-- +-- ## Mission Management +-- +-- * @{#COMMANDCENTER.AddMission}(): Adds a mission to the commandcenter control. +-- * @{#COMMANDCENTER.RemoveMission}(): Removes a mission to the commandcenter control. +-- * @{#COMMANDCENTER.GetMissions}(): Retrieves the missions table controlled by the commandcenter. +-- +-- ## Reference Zones +-- +-- Command Centers may be aware of certain Reference Zones within the battleground. These Reference Zones can refer to +-- known areas, recognizable buildings or sites, or any other point of interest. +-- Command Centers will use these Reference Zones to help pilots with defining coordinates in terms of navigation +-- during the WWII era. +-- The Reference Zones are related to the WWII mode that the Command Center will operate in. +-- Use the method @{#COMMANDCENTER.SetModeWWII}() to set the mode of communication to the WWII mode. +-- +-- In WWII mode, the Command Center will receive detected targets, and will select for each target the closest +-- nearby Reference Zone. This allows pilots to navigate easier through the battle field readying for combat. +-- +-- The Reference Zones need to be set by the Mission Designer in the Mission Editor. +-- Reference Zones are set by normal trigger zones. One can color the zones in a specific color, +-- and the radius of the zones doesn't matter, only the point is important. Place the center of these Reference Zones at +-- specific scenery objects or points of interest (like cities, rivers, hills, crossing etc). +-- The trigger zones indicating a Reference Zone need to follow a specific syntax. +-- The name of each trigger zone expressing a Reference Zone need to start with a classification name of the object, +-- followed by a #, followed by a symbolic name of the Reference Zone. +-- A few examples: +-- +-- * A church at Tskinvali would be indicated as: *Church#Tskinvali* +-- * A train station near Kobuleti would be indicated as: *Station#Kobuleti* +-- +-- The COMMANDCENTER class contains a method to indicate which trigger zones need to be used as Reference Zones. +-- This is done by using the method @{#COMMANDCENTER.SetReferenceZones}(). +-- For the moment, only one Reference Zone class can be specified, but in the future, more classes will become possible. +-- +-- @field #COMMANDCENTER COMMANDCENTER = { ClassName = "COMMANDCENTER", CommandCenterName = "", CommandCenterCoalition = nil, CommandCenterPositionable = nil, Name = "", + ReferencePoints = {}, + ReferenceNames = {}, + CommunicationMode = "80", } + --- The constructor takes an IDENTIFIABLE as the HQ command center. -- @param #COMMANDCENTER self -- @param Wrapper.Positionable#POSITIONABLE CommandCenterPositionable @@ -81,17 +101,18 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) if EventData.IniObjectCategory == 1 then local EventGroup = GROUP:Find( EventData.IniDCSGroup ) if EventGroup and self:HasGroup( EventGroup ) then - local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu ) - local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup ) - local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) + local MenuReporting = MENU_GROUP:New( EventGroup, "Missions Reports", self.CommandCenterMenu ) + local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Status Report", MenuReporting, self.ReportMissionsStatus, self, EventGroup ) + local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Players Report", MenuReporting, self.ReportMissionsPlayers, self, EventGroup ) self:ReportSummary( EventGroup ) - end - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! - Mission:JoinUnit( PlayerUnit, PlayerGroup ) - Mission:ReportDetails() + local PlayerUnit = EventData.IniUnit + for MissionID, Mission in pairs( self:GetMissions() ) do + local Mission = Mission -- Tasking.Mission#MISSION + local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! + Mission:JoinUnit( PlayerUnit, PlayerGroup ) + end + self:SetMenu() + _DATABASE:PlayerSettingsMenu( PlayerUnit ) end end @@ -112,7 +133,22 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) local Mission = Mission -- Tasking.Mission#MISSION local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! Mission:JoinUnit( PlayerUnit, PlayerGroup ) - Mission:ReportDetails() + end + self:SetMenu() + end + ) + + -- Handle when a player leaves a slot and goes back to spectators ... + -- The PlayerUnit will be UnAssigned from the Task. + -- When there is no Unit left running the Task, the Task goes into Abort... + self:HandleEvent( EVENTS.MissionEnd, + --- @param #TASK self + -- @param Core.Event#EVENTDATA EventData + function( self, EventData ) + local PlayerUnit = EventData.IniUnit + for MissionID, Mission in pairs( self:GetMissions() ) do + local Mission = Mission -- Tasking.Mission#MISSION + Mission:Stop() end end ) @@ -127,7 +163,9 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) local PlayerUnit = EventData.IniUnit for MissionID, Mission in pairs( self:GetMissions() ) do local Mission = Mission -- Tasking.Mission#MISSION - Mission:AbortUnit( PlayerUnit ) + if Mission:IsENGAGED() then + Mission:AbortUnit( PlayerUnit ) + end end end ) @@ -141,10 +179,17 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) function( self, EventData ) local PlayerUnit = EventData.IniUnit for MissionID, Mission in pairs( self:GetMissions() ) do - Mission:CrashUnit( PlayerUnit ) + local Mission = Mission -- Tasking.Mission#MISSION + if Mission:IsENGAGED() then + Mission:CrashUnit( PlayerUnit ) + end end end ) + + self:SetMenu() + + _SETTINGS:SetSystemMenu( CommandCenterPositionable ) return self end @@ -195,6 +240,66 @@ function COMMANDCENTER:RemoveMission( Mission ) return Mission end +--- Set special Reference Zones known by the Command Center to guide airborne pilots during WWII. +-- +-- These Reference Zones are normal trigger zones, with a special naming. +-- The Reference Zones need to be set by the Mission Designer in the Mission Editor. +-- Reference Zones are set by normal trigger zones. One can color the zones in a specific color, +-- and the radius of the zones doesn't matter, only the center of the zone is important. Place the center of these Reference Zones at +-- specific scenery objects or points of interest (like cities, rivers, hills, crossing etc). +-- The trigger zones indicating a Reference Zone need to follow a specific syntax. +-- The name of each trigger zone expressing a Reference Zone need to start with a classification name of the object, +-- followed by a #, followed by a symbolic name of the Reference Zone. +-- A few examples: +-- +-- * A church at Tskinvali would be indicated as: *Church#Tskinvali* +-- * A train station near Kobuleti would be indicated as: *Station#Kobuleti* +-- +-- Taking the above example, this is how this method would be used: +-- +-- CC:SetReferenceZones( "Church" ) +-- CC:SetReferenceZones( "Station" ) +-- +-- +-- @param #COMMANDCENTER self +-- @param #string ReferenceZonePrefix The name before the #-mark indicating the class of the Reference Zones. +-- @return #COMMANDCENTER +function COMMANDCENTER:SetReferenceZones( ReferenceZonePrefix ) + local MatchPattern = "(.*)#(.*)" + self:F( { MatchPattern = MatchPattern } ) + for ReferenceZoneName in pairs( _DATABASE.ZONENAMES ) do + local ZoneName, ReferenceName = string.match( ReferenceZoneName, MatchPattern ) + self:F( { ZoneName = ZoneName, ReferenceName = ReferenceName } ) + if ZoneName and ReferenceName and ZoneName == ReferenceZonePrefix then + self.ReferencePoints[ReferenceZoneName] = ZONE:New( ReferenceZoneName ) + self.ReferenceNames[ReferenceZoneName] = ReferenceName + end + end + return self +end + +--- Set the commandcenter operations in WWII mode +-- This will disable LL, MGRS, BRA, BULLS navigatin messages sent by the Command Center, +-- and will be replaced by a navigation using Reference Zones. +-- It will also disable the settings at the settings menu for these. +-- @param #COMMANDCENTER self +-- @return #COMMANDCENTER +function COMMANDCENTER:SetModeWWII() + self.CommunicationMode = "WWII" + return self +end + + +--- Returns if the commandcenter operations is in WWII mode +-- @param #COMMANDCENTER self +-- @return #boolean true if in WWII mode. +function COMMANDCENTER:IsModeWWII() + return self.CommunicationMode == "WWII" +end + + + + --- Sets the menu structure of the Missions governed by the HQ command center. -- @param #COMMANDCENTER self function COMMANDCENTER:SetMenu() @@ -203,12 +308,12 @@ function COMMANDCENTER:SetMenu() self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "Command Center (" .. self:GetName() .. ")" ) local MenuTime = timer.getTime() - for MissionID, Mission in pairs( self:GetMissions() ) do + for MissionID, Mission in pairs( self:GetMissions() or {} ) do local Mission = Mission -- Tasking.Mission#MISSION Mission:SetMenu( MenuTime ) end - for MissionID, Mission in pairs( self:GetMissions() ) do + for MissionID, Mission in pairs( self:GetMissions() or {} ) do local Mission = Mission -- Tasking.Mission#MISSION Mission:RemoveMenu( MenuTime ) end @@ -219,7 +324,6 @@ end -- @param #COMMANDCENTER self -- @return Core.Menu#MENU_COALITION function COMMANDCENTER:GetMenu() - self:F() return self.CommandCenterMenu end @@ -255,12 +359,9 @@ end -- @param #string Message -- @param Wrapper.Group#GROUP TaskGroup -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. -function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name ) +function COMMANDCENTER:MessageToGroup( Message, TaskGroup ) - local Prefix = "@ Group" - Prefix = Prefix .. ( Name and " (" .. Name .. "): " or '' ) - Message = Prefix .. Message - self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() ) + self:GetPositionable():MessageToGroup( Message , 15, TaskGroup, self:GetName() ) end @@ -270,7 +371,8 @@ function COMMANDCENTER:MessageToCoalition( Message ) local CCCoalition = self:GetPositionable():GetCoalition() --TODO: Fix coalition bug! - self:GetPositionable():MessageToCoalition( Message, 20, CCCoalition, self:GetName() ) + + self:GetPositionable():MessageToCoalition( Message, 15, CCCoalition ) end @@ -278,18 +380,37 @@ end --- Report the status of all MISSIONs to a GROUP. -- Each Mission is listed, with an indication how many Tasks are still to be completed. -- @param #COMMANDCENTER self -function COMMANDCENTER:ReportSummary( ReportGroup ) +function COMMANDCENTER:ReportMissionsStatus( ReportGroup ) + self:E( ReportGroup ) + + local Report = REPORT:New() + + Report:Add( "Status report of all missions." ) + + for MissionID, Mission in pairs( self.Missions ) do + local Mission = Mission -- Tasking.Mission#MISSION + Report:Add( " - " .. Mission:ReportStatus() ) + end + + self:MessageToGroup( Report:Text(), ReportGroup ) +end + +--- Report the players of all MISSIONs to a GROUP. +-- Each Mission is listed, with an indication how many Tasks are still to be completed. +-- @param #COMMANDCENTER self +function COMMANDCENTER:ReportMissionsPlayers( ReportGroup ) self:E( ReportGroup ) local Report = REPORT:New() + Report:Add( "Players active in all missions." ) + for MissionID, Mission in pairs( self.Missions ) do local Mission = Mission -- Tasking.Mission#MISSION - Report:Add( " - " .. Mission:ReportOverview() ) + Report:Add( " - " .. Mission:ReportPlayers() ) end - self:GetPositionable():MessageToGroup( Report:Text(), 30, ReportGroup ) - + self:MessageToGroup( Report:Text(), ReportGroup ) end --- Report the status of a Task to a Group. @@ -305,6 +426,6 @@ function COMMANDCENTER:ReportDetails( ReportGroup, Task ) Report:Add( " - " .. Mission:ReportDetails() ) end - self:GetPositionable():MessageToGroup( Report:Text(), 30, ReportGroup ) + self:MessageToGroup( Report:Text(), ReportGroup ) end diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index 7601701a0..8e9010a3e 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -15,7 +15,7 @@ -- --------------------------------- -- Derived DETECTION_MANAGER classes will reports detected units using the method @{DetectionManager#DETECTION_MANAGER.ReportDetected}(). This method implements polymorphic behaviour. -- --- The time interval in seconds of the reporting can be changed using the methods @{DetectionManager#DETECTION_MANAGER.SetReportInterval}(). +-- The time interval in seconds of the reporting can be changed using the methods @{DetectionManager#DETECTION_MANAGER.SetRefreshTimeInterval}(). -- To control how long a reporting message is displayed, use @{DetectionManager#DETECTION_MANAGER.SetReportDisplayTime}(). -- Derived classes need to implement the method @{DetectionManager#DETECTION_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report. -- @@ -70,13 +70,67 @@ do -- DETECTION MANAGER self:SetStartState( "Stopped" ) self:AddTransition( "Stopped", "Start", "Started" ) + + --- Start Handler OnBefore for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] OnBeforeStart + -- @param #DETECTION_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Start Handler OnAfter for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] OnAfterStart + -- @param #DETECTION_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Start Trigger for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] Start + -- @param #DETECTION_MANAGER self + + --- Start Asynchronous Trigger for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] __Start + -- @param #DETECTION_MANAGER self + -- @param #number Delay + + + self:AddTransition( "Started", "Stop", "Stopped" ) + + --- Stop Handler OnBefore for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] OnBeforeStop + -- @param #DETECTION_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Stop Handler OnAfter for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] OnAfterStop + -- @param #DETECTION_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Stop Trigger for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] Stop + -- @param #DETECTION_MANAGER self + + --- Stop Asynchronous Trigger for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] __Stop + -- @param #DETECTION_MANAGER self + -- @param #number Delay + + self:AddTransition( "Started", "Report", "Started" ) - self:SetReportInterval( 30 ) + self:SetRefreshTimeInterval( 30 ) self:SetReportDisplayTime( 25 ) - Detection:__Start( 1 ) + self:E( { Detection = Detection } ) + Detection:__Start( 3 ) return self end @@ -89,19 +143,19 @@ do -- DETECTION MANAGER self:E( "onafterReport" ) - self:__Report( -self._ReportInterval ) + self:__Report( -self._RefreshTimeInterval ) self:ProcessDetected( self.Detection ) end --- Set the reporting time interval. -- @param #DETECTION_MANAGER self - -- @param #number ReportInterval The interval in seconds when a report needs to be done. + -- @param #number RefreshTimeInterval The interval in seconds when a report needs to be done. -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:SetReportInterval( ReportInterval ) + function DETECTION_MANAGER:SetRefreshTimeInterval( RefreshTimeInterval ) self:F2() - self._ReportInterval = ReportInterval + self._RefreshTimeInterval = RefreshTimeInterval end diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 10e194ff2..8565078f2 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -1,5 +1,13 @@ ---- A MISSION is the main owner of a Mission orchestration within MOOSE . The Mission framework orchestrates @{CLIENT}s, @{TASK}s, @{STAGE}s etc. --- A @{CLIENT} needs to be registered within the @{MISSION} through the function @{AddClient}. A @{TASK} needs to be registered within the @{MISSION} through the function @{AddTask}. +--- **Tasking** -- A MISSION is the main owner of a Mission orchestration within MOOSE. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- === +-- -- @module Mission --- The MISSION class @@ -12,6 +20,7 @@ MISSION = { ClassName = "MISSION", Name = "", MissionStatus = "PENDING", + AssignedGroups = {}, } --- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. @@ -26,35 +35,48 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi local self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM - self:SetStartState( "Idle" ) + self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) - self:AddTransition( "Idle", "Start", "Ongoing" ) + self.CommandCenter = CommandCenter + CommandCenter:AddMission( self ) - --- OnLeave Transition Handler for State Idle. - -- @function [parent=#MISSION] OnLeaveIdle + self.Name = MissionName + self.MissionPriority = MissionPriority + self.MissionBriefing = MissionBriefing + self.MissionCoalition = MissionCoalition + + self.Tasks = {} + self.PlayerNames = {} -- These are the players that achieved progress in the mission. + + self:SetStartState( "IDLE" ) + + self:AddTransition( "IDLE", "Start", "ENGAGED" ) + + --- OnLeave Transition Handler for State IDLE. + -- @function [parent=#MISSION] OnLeaveIDLE -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. -- @return #boolean Return false to cancel Transition. - --- OnEnter Transition Handler for State Idle. - -- @function [parent=#MISSION] OnEnterIdle + --- OnEnter Transition Handler for State IDLE. + -- @function [parent=#MISSION] OnEnterIDLE -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. - --- OnLeave Transition Handler for State Ongoing. - -- @function [parent=#MISSION] OnLeaveOngoing + --- OnLeave Transition Handler for State ENGAGED. + -- @function [parent=#MISSION] OnLeaveENGAGED -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. -- @return #boolean Return false to cancel Transition. - --- OnEnter Transition Handler for State Ongoing. - -- @function [parent=#MISSION] OnEnterOngoing + --- OnEnter Transition Handler for State ENGAGED. + -- @function [parent=#MISSION] OnEnterENGAGED -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. @@ -84,18 +106,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi -- @param #MISSION self -- @param #number Delay The delay in seconds. - self:AddTransition( "Ongoing", "Stop", "Idle" ) + self:AddTransition( "ENGAGED", "Stop", "IDLE" ) - --- OnLeave Transition Handler for State Idle. - -- @function [parent=#MISSION] OnLeaveIdle + --- OnLeave Transition Handler for State IDLE. + -- @function [parent=#MISSION] OnLeaveIDLE -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. -- @return #boolean Return false to cancel Transition. - --- OnEnter Transition Handler for State Idle. - -- @function [parent=#MISSION] OnEnterIdle + --- OnEnter Transition Handler for State IDLE. + -- @function [parent=#MISSION] OnEnterIDLE -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. @@ -125,18 +147,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi -- @param #MISSION self -- @param #number Delay The delay in seconds. - self:AddTransition( "Ongoing", "Complete", "Completed" ) + self:AddTransition( "ENGAGED", "Complete", "COMPLETED" ) - --- OnLeave Transition Handler for State Completed. - -- @function [parent=#MISSION] OnLeaveCompleted + --- OnLeave Transition Handler for State COMPLETED. + -- @function [parent=#MISSION] OnLeaveCOMPLETED -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. -- @return #boolean Return false to cancel Transition. - --- OnEnter Transition Handler for State Completed. - -- @function [parent=#MISSION] OnEnterCompleted + --- OnEnter Transition Handler for State COMPLETED. + -- @function [parent=#MISSION] OnEnterCOMPLETED -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. @@ -166,18 +188,18 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi -- @param #MISSION self -- @param #number Delay The delay in seconds. - self:AddTransition( "*", "Fail", "Failed" ) + self:AddTransition( "*", "Fail", "FAILED" ) - --- OnLeave Transition Handler for State Failed. - -- @function [parent=#MISSION] OnLeaveFailed + --- OnLeave Transition Handler for State FAILED. + -- @function [parent=#MISSION] OnLeaveFAILED -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. -- @return #boolean Return false to cancel Transition. - --- OnEnter Transition Handler for State Failed. - -- @function [parent=#MISSION] OnEnterFailed + --- OnEnter Transition Handler for State FAILED. + -- @function [parent=#MISSION] OnEnterFAILED -- @param #MISSION self -- @param #string From The From State string. -- @param #string Event The Event string. @@ -207,56 +229,56 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi -- @param #MISSION self -- @param #number Delay The delay in seconds. - self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) - self.CommandCenter = CommandCenter - CommandCenter:AddMission( self ) + self:AddTransition( "*", "MissionGoals", "*" ) + + --- MissionGoals Handler OnBefore for MISSION + -- @function [parent=#MISSION] OnBeforeMissionGoals + -- @param #MISSION self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- MissionGoals Handler OnAfter for MISSION + -- @function [parent=#MISSION] OnAfterMissionGoals + -- @param #MISSION self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- MissionGoals Trigger for MISSION + -- @function [parent=#MISSION] MissionGoals + -- @param #MISSION self + + --- MissionGoals Asynchronous Trigger for MISSION + -- @function [parent=#MISSION] __MissionGoals + -- @param #MISSION self + -- @param #number Delay - self.Name = MissionName - self.MissionPriority = MissionPriority - self.MissionBriefing = MissionBriefing - self.MissionCoalition = MissionCoalition - - self.Tasks = {} - -- Private implementations - + CommandCenter:SetMenu() return self end --- FSM function for a MISSION --- @param #MISSION self --- @param #string From --- @param #string Event --- @param #string To -function MISSION:onbeforeComplete( From, Event, To ) - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if not Task:IsStateSuccess() and not Task:IsStateFailed() and not Task:IsStateAborted() and not Task:IsStateCancelled() then - return false -- Mission cannot be completed. Other Tasks are still active. - end - end - return true -- Allow Mission completion. -end -- FSM function for a MISSION -- @param #MISSION self -- @param #string From -- @param #string Event -- @param #string To -function MISSION:onenterCompleted( From, Event, To ) +function MISSION:onenterCOMPLETED( From, Event, To ) - self:GetCommandCenter():MessageToCoalition( "Mission " .. self:GetName() .. " has been completed! Good job guys!" ) + self:GetCommandCenter():MessageToCoalition( self:GetName() .. " has been completed! Good job guys!" ) end --- Gets the mission name. -- @param #MISSION self -- @return #MISSION self function MISSION:GetName() - return self.Name + return string.format( 'Mission "%s (%s)"', self.Name, self.MissionPriority ) end --- Add a Unit to join the Mission. @@ -288,19 +310,17 @@ end -- If the Unit is part of a Task in the Mission, true is returned. -- @param #MISSION self -- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player joining the Mission. --- @return #boolean true if Unit is part of a Task in the Mission. +-- @return #MISSION function MISSION:AbortUnit( PlayerUnit ) self:F( { PlayerUnit = PlayerUnit } ) - local PlayerUnitRemoved = false - for TaskID, Task in pairs( self:GetTasks() ) do - if Task:AbortUnit( PlayerUnit ) then - PlayerUnitRemoved = true - end + local Task = Task -- Tasking.Task#TASK + local PlayerGroup = PlayerUnit:GetGroup() + Task:AbortGroup( PlayerGroup ) end - return PlayerUnitRemoved + return self end --- Handles a crash of a PlayerUnit from the Mission. @@ -309,19 +329,17 @@ end -- If the Unit is part of a Task in the Mission, true is returned. -- @param #MISSION self -- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player crashing. --- @return #boolean true if Unit is part of a Task in the Mission. +-- @return #MISSION function MISSION:CrashUnit( PlayerUnit ) self:F( { PlayerUnit = PlayerUnit } ) - local PlayerUnitRemoved = false - for TaskID, Task in pairs( self:GetTasks() ) do - if Task:CrashUnit( PlayerUnit ) then - PlayerUnitRemoved = true - end + local Task = Task -- Tasking.Task#TASK + local PlayerGroup = PlayerUnit:GetGroup() + Task:CrashGroup( PlayerGroup ) end - return PlayerUnitRemoved + return self end --- Add a scoring to the mission. @@ -365,7 +383,7 @@ end -- @param #MISSION self -- @param #number MenuTime function MISSION:SetMenu( MenuTime ) - self:F() + self:F( { self:GetName(), MenuTime } ) for _, TaskData in pairs( self:GetTasks() ) do local Task = TaskData -- Tasking.Task#TASK @@ -377,7 +395,7 @@ end -- @param #MISSION self -- @param #number MenuTime function MISSION:RemoveMenu( MenuTime ) - self:F() + self:F( { self:GetName(), MenuTime } ) for _, Task in pairs( self:GetTasks() ) do local Task = Task -- Tasking.Task#TASK @@ -386,6 +404,59 @@ function MISSION:RemoveMenu( MenuTime ) end + +do -- Group Assignment + + --- Returns if the @{Mission} is assigned to the Group. + -- @param #MISSION self + -- @param Wrapper.Group#GROUP MissionGroup + -- @return #boolean + function MISSION:IsGroupAssigned( MissionGroup ) + + local MissionGroupName = MissionGroup:GetName() + + if self.AssignedGroups[MissionGroupName] == MissionGroup then + self:T( { "Mission is assigned to:", MissionGroup:GetName() } ) + return true + end + + self:T( { "Mission is not assigned to:", MissionGroup:GetName() } ) + return false + end + + + --- Set @{Group} assigned to the @{Mission}. + -- @param #MISSION self + -- @param Wrapper.Group#GROUP MissionGroup + -- @return #MISSION + function MISSION:SetGroupAssigned( MissionGroup ) + + local MissionName = self:GetName() + local MissionGroupName = MissionGroup:GetName() + + self.AssignedGroups[MissionGroupName] = MissionGroup + self:E( string.format( "Mission %s is assigned to %s", MissionName, MissionGroupName ) ) + + return self + end + + --- Clear the @{Group} assignment from the @{Mission}. + -- @param #MISSION self + -- @param Wrapper.Group#GROUP MissionGroup + -- @return #MISSION + function MISSION:ClearGroupAssignment( MissionGroup ) + + local MissionName = self:GetName() + local MissionGroupName = MissionGroup:GetName() + + self.AssignedGroups[MissionGroupName] = nil + --self:E( string.format( "Mission %s is unassigned to %s", MissionName, MissionGroupName ) ) + + return self + end + +end + --- Gets the COMMANDCENTER. -- @param #MISSION self -- @return Tasking.CommandCenter#COMMANDCENTER @@ -404,21 +475,59 @@ function MISSION:RemoveTaskMenu( Task ) end ---- Gets the mission menu for the coalition. +--- Gets the root mission menu for the TaskGroup. -- @param #MISSION self --- @param Wrapper.Group#GROUP TaskGroup -- @return Core.Menu#MENU_COALITION self -function MISSION:GetMenu( TaskGroup ) +function MISSION:GetRootMenu( TaskGroup ) -- R2.2 local CommandCenter = self:GetCommandCenter() local CommandCenterMenu = CommandCenter:GetMenu() local MissionName = self:GetName() - local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) + --local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) - return MissionMenu + self.MissionMenu = self.MissionMenu or MENU_COALITION:New( self.MissionCoalition, self:GetName(), CommandCenterMenu ) + + return self.MissionMenu end +--- Gets the mission menu for the TaskGroup. +-- @param #MISSION self +-- @return Core.Menu#MENU_COALITION self +function MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure + + local CommandCenter = self:GetCommandCenter() + local CommandCenterMenu = CommandCenter:GetMenu() + + local MissionName = self:GetName() + --local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) + + self.MissionGroupMenu = self.MissionGroupMenu or {} + self.MissionGroupMenu[TaskGroup] = self.MissionGroupMenu[TaskGroup] or {} + + local GroupMenu = self.MissionGroupMenu[TaskGroup] + + self.MissionMenu = self.MissionMenu or MENU_COALITION:New( self.MissionCoalition, self:GetName(), CommandCenterMenu ) + + GroupMenu.BriefingMenu = GroupMenu.BriefingMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Mission Briefing", self.MissionMenu, self.MenuReportBriefing, self, TaskGroup ) + + GroupMenu.TaskReportsMenu = GroupMenu.TaskReportsMenu or MENU_GROUP:New( TaskGroup, "Task Reports", self.MissionMenu ) + GroupMenu.ReportTasksMenu = GroupMenu.ReportTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksSummary, self, TaskGroup ) + GroupMenu.ReportPlannedTasksMenu = GroupMenu.ReportPlannedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Planned Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Planned" ) + GroupMenu.ReportAssignedTasksMenu = GroupMenu.ReportAssignedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Assigned Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Assigned" ) + GroupMenu.ReportSuccessTasksMenu = GroupMenu.ReportSuccessTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Successful Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Success" ) + GroupMenu.ReportFailedTasksMenu = GroupMenu.ReportFailedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Failed Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Failed" ) + GroupMenu.ReportHeldTasksMenu = GroupMenu.ReportHeldTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Held Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Hold" ) + + GroupMenu.PlayerReportsMenu = GroupMenu.PlayerReportsMenu or MENU_GROUP:New( TaskGroup, "Statistics Reports", self.MissionMenu ) + GroupMenu.ReportMissionHistory = GroupMenu.ReportPlayersHistory or MENU_GROUP_COMMAND:New( TaskGroup, "Report Mission Progress", GroupMenu.PlayerReportsMenu, self.MenuReportPlayersProgress, self, TaskGroup ) + GroupMenu.ReportPlayersPerTaskMenu = GroupMenu.ReportPlayersPerTaskMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Players per Task", GroupMenu.PlayerReportsMenu, self.MenuReportPlayersPerTask, self, TaskGroup ) + + return self.MissionMenu +end + + + --- Get the TASK identified by the TaskNumber from the Mission. This function is useful in GoalFunctions. -- @param #string TaskName The Name of the @{Task} within the @{Mission}. @@ -490,39 +599,39 @@ function MISSION:GetNextTaskID( Task ) return self.Tasks[TaskName].n end ---- Is the @{Mission} **Completed**. +--- Is the @{Mission} **COMPLETED**. -- @param #MISSION self -- @return #boolean -function MISSION:IsCompleted() - return self:Is( "Completed" ) +function MISSION:IsCOMPLETED() + return self:Is( "COMPLETED" ) end ---- Is the @{Mission} **Idle**. +--- Is the @{Mission} **IDLE**. -- @param #MISSION self -- @return #boolean -function MISSION:IsIdle() - return self:Is( "Idle" ) +function MISSION:IsIDLE() + return self:Is( "IDLE" ) end ---- Is the @{Mission} **Ongoing**. +--- Is the @{Mission} **ENGAGED**. -- @param #MISSION self -- @return #boolean -function MISSION:IsOngoing() - return self:Is( "Ongoing" ) +function MISSION:IsENGAGED() + return self:Is( "ENGAGED" ) end ---- Is the @{Mission} **Failed**. +--- Is the @{Mission} **FAILED**. -- @param #MISSION self -- @return #boolean -function MISSION:IsFailed() - return self:Is( "Failed" ) +function MISSION:IsFAILED() + return self:Is( "FAILED" ) end ---- Is the @{Mission} **Hold**. +--- Is the @{Mission} **HOLD**. -- @param #MISSION self -- @return #boolean -function MISSION:IsHold() - return self:Is( "Hold" ) +function MISSION:IsHOLD() + return self:Is( "HOLD" ) end --- Validates if the Mission has a Group @@ -542,19 +651,9 @@ function MISSION:HasGroup( TaskGroup ) return Has end ---- Create a summary report of the Mission (one line). --- @param #MISSION self --- @return #string -function MISSION:ReportSummary() - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetName() - - -- Determine the status of the mission. - local Status = self:GetState() - +--- @param #MISSION self +-- @return #number +function MISSION:GetTasksRemaining() -- Determine how many tasks are remaining. local TasksRemaining = 0 for TaskID, Task in pairs( self:GetTasks() ) do @@ -564,16 +663,39 @@ function MISSION:ReportSummary() TasksRemaining = TasksRemaining + 1 end end - - Report:Add( "Mission " .. Name .. " - " .. Status .. " - " .. TasksRemaining .. " tasks remaining." ) - - return Report:Text() + return TasksRemaining end ---- Create a overview report of the Mission (multiple lines). +--- @param #MISSION self +-- @return #number +function MISSION:GetTaskTypes() + -- Determine how many tasks are remaining. + local TaskTypeList = {} + local TasksRemaining = 0 + for TaskID, Task in pairs( self:GetTasks() ) do + local Task = Task -- Tasking.Task#TASK + local TaskType = Task:GetType() + TaskTypeList[TaskType] = TaskType + end + return TaskTypeList +end + + +function MISSION:AddPlayerName( PlayerName ) + self.PlayerNames = self.PlayerNames or {} + self.PlayerNames[PlayerName] = PlayerName + return self +end + +function MISSION:GetPlayerNames() + return self.PlayerNames +end + + +--- Create a briefing report of the Mission. -- @param #MISSION self -- @return #string -function MISSION:ReportOverview() +function MISSION:ReportBriefing() local Report = REPORT:New() @@ -581,15 +703,213 @@ function MISSION:ReportOverview() local Name = self:GetName() -- Determine the status of the mission. - local Status = self:GetState() + local Status = "<" .. self:GetState() .. ">" + + Report:Add( string.format( '%s - %s - Mission Briefing Report', Name, Status ) ) - Report:Add( "Mission " .. Name .. " - State '" .. Status .. "'" ) + Report:Add( self.MissionBriefing ) + + return Report:Text() +end + + +--- Create a status report of the Mission. +-- This reports provides a one liner of the mission status. It indicates how many players and how many Tasks. +-- +-- Mission "" - Status "" +-- - Task Types: , +-- - Planned Tasks (xp) +-- - Assigned Tasks(xp) +-- - Success Tasks (xp) +-- - Hold Tasks (xp) +-- - Cancelled Tasks (xp) +-- - Aborted Tasks (xp) +-- - Failed Tasks (xp) +-- +-- @param #MISSION self +-- @return #string +function MISSION:ReportStatus() + + local Report = REPORT:New() + + -- List the name of the mission. + local Name = self:GetName() + + -- Determine the status of the mission. + local Status = "<" .. self:GetState() .. ">" + + Report:Add( string.format( '%s - Status "%s"', Name, Status ) ) + + local TaskTypes = self:GetTaskTypes() + + Report:Add( string.format( " - Task Types: %s", table.concat(TaskTypes, ", " ) ) ) + + local TaskStatusList = { "Planned", "Assigned", "Success", "Hold", "Cancelled", "Aborted", "Failed" } + + for TaskStatusID, TaskStatus in pairs( TaskStatusList ) do + local TaskCount = 0 + local TaskPlayerCount = 0 + -- Determine how many tasks are remaining. + for TaskID, Task in pairs( self:GetTasks() ) do + local Task = Task -- Tasking.Task#TASK + if Task:Is( TaskStatus ) then + TaskCount = TaskCount + 1 + TaskPlayerCount = TaskPlayerCount + Task:GetPlayerCount() + end + end + if TaskCount > 0 then + Report:Add( string.format( " - %02d %s Tasks (%dp)", TaskCount, TaskStatus, TaskPlayerCount ) ) + end + end + + return Report:Text() +end + + +--- Create an active player report of the Mission. +-- This reports provides a one liner of the mission status. It indicates how many players and how many Tasks. +-- +-- Mission "" - - Active Players Report +-- - Player ": Task , Task +-- - Player : Task , Task +-- - .. +-- +-- @param #MISSION self +-- @return #string +function MISSION:ReportPlayersPerTask( ReportGroup ) + + local Report = REPORT:New() + + -- List the name of the mission. + local Name = self:GetName() + + -- Determine the status of the mission. + local Status = "<" .. self:GetState() .. ">" + + Report:Add( string.format( '%s - %s - Players per Task Report', Name, Status ) ) + + local PlayerList = {} -- Determine how many tasks are remaining. - local TasksRemaining = 0 for TaskID, Task in pairs( self:GetTasks() ) do local Task = Task -- Tasking.Task#TASK - Report:Add( "- " .. Task:ReportSummary() ) + local PlayerNames = Task:GetPlayerNames() + for PlayerName, PlayerGroup in pairs( PlayerNames ) do + PlayerList[PlayerName] = Task:GetName() + end + + end + + for PlayerName, TaskName in pairs( PlayerList ) do + Report:Add( string.format( ' - Player (%s): Task "%s"', PlayerName, TaskName ) ) + end + + return Report:Text() +end + +--- Create an Mission Progress report of the Mission. +-- This reports provides a one liner per player of the mission achievements per task. +-- +-- Mission "" - - Active Players Report +-- - Player : Task : +-- - Player : Task : +-- - .. +-- +-- @param #MISSION self +-- @return #string +function MISSION:ReportPlayersProgress( ReportGroup ) + + local Report = REPORT:New() + + -- List the name of the mission. + local Name = self:GetName() + + -- Determine the status of the mission. + local Status = "<" .. self:GetState() .. ">" + + Report:Add( string.format( '%s - %s - Players per Task Progress Report', Name, Status ) ) + + local PlayerList = {} + + -- Determine how many tasks are remaining. + for TaskID, Task in pairs( self:GetTasks() ) do + local Task = Task -- Tasking.Task#TASK + local TaskGoalTotal = Task:GetGoalTotal() or 0 + local TaskName = Task:GetName() + PlayerList[TaskName] = PlayerList[TaskName] or {} + if TaskGoalTotal ~= 0 then + local PlayerNames = self:GetPlayerNames() + for PlayerName, PlayerData in pairs( PlayerNames ) do + PlayerList[TaskName][PlayerName] = string.format( 'Player (%s): Task "%s": %d%%', PlayerName, TaskName, Task:GetPlayerProgress( PlayerName ) * 100 / TaskGoalTotal ) + end + else + PlayerList[TaskName]["_"] = string.format( 'Player (---): Task "%s": %d%%', TaskName, 0 ) + end + + end + + for TaskName, TaskData in pairs( PlayerList ) do + for PlayerName, TaskText in pairs( TaskData ) do + Report:Add( string.format( ' - %s', TaskText ) ) + end + end + + return Report:Text() +end + + +--- Create a summary report of the Mission (one line). +-- @param #MISSION self +-- @param Wrapper.Group#GROUP ReportGroup +-- @return #string +function MISSION:ReportSummary( ReportGroup ) + + local Report = REPORT:New() + + -- List the name of the mission. + local Name = self:GetName() + + -- Determine the status of the mission. + local Status = "<" .. self:GetState() .. ">" + + Report:Add( string.format( '%s - %s - Task Overview Report', Name, Status ) ) + + -- Determine how many tasks are remaining. + for TaskID, Task in UTILS.spairs( self:GetTasks(), function( t, a, b ) return t[a]:ReportOrder( ReportGroup ) < t[b]:ReportOrder( ReportGroup ) end ) do + local Task = Task -- Tasking.Task#TASK + Report:Add( "- " .. Task:ReportSummary( ReportGroup ) ) + end + + return Report:Text() +end + +--- Create a overview report of the Mission (multiple lines). +-- @param #MISSION self +-- @return #string +function MISSION:ReportOverview( ReportGroup, TaskStatus ) + + local Report = REPORT:New() + + -- List the name of the mission. + local Name = self:GetName() + + -- Determine the status of the mission. + local Status = "<" .. self:GetState() .. ">" + + Report:Add( string.format( '%s - %s - %s Tasks Report', Name, Status, TaskStatus ) ) + + -- Determine how many tasks are remaining. + local Tasks = 0 + for TaskID, Task in UTILS.spairs( self:GetTasks(), function( t, a, b ) return t[a]:ReportOrder( ReportGroup ) < t[b]:ReportOrder( ReportGroup ) end ) do + local Task = Task -- Tasking.Task#TASK + if Task:Is( TaskStatus ) then + Report:Add( string.rep( "-", 140 ) ) + Report:Add( " - " .. Task:ReportOverview( ReportGroup ) ) + end + Tasks = Tasks + 1 + if Tasks >= 8 then + break + end end return Report:Text() @@ -598,7 +918,7 @@ end --- Create a detailed report of the Mission, listing all the details of the Task. -- @param #MISSION self -- @return #string -function MISSION:ReportDetails() +function MISSION:ReportDetails( ReportGroup ) local Report = REPORT:New() @@ -606,15 +926,15 @@ function MISSION:ReportDetails() local Name = self:GetName() -- Determine the status of the mission. - local Status = self:GetState() + local Status = "<" .. self:GetState() .. ">" - Report:Add( "Mission " .. Name .. " - State '" .. Status .. "'" ) + Report:Add( string.format( '%s - %s - Task Detailed Report', Name, Status ) ) -- Determine how many tasks are remaining. local TasksRemaining = 0 for TaskID, Task in pairs( self:GetTasks() ) do local Task = Task -- Tasking.Task#TASK - Report:Add( Task:ReportDetails() ) + Report:Add( Task:ReportDetails( ReportGroup ) ) end return Report:Text() @@ -627,9 +947,65 @@ end -- Tasks = Mission:GetTasks() -- env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" ) function MISSION:GetTasks() - self:F() return self.Tasks end - + +--- Reports the briefing. +-- @param #MISSION self +-- @param Wrapper.Group#GROUP ReportGroup The group to which the report needs to be sent. +function MISSION:MenuReportBriefing( ReportGroup ) + + local Report = self:ReportBriefing() + + self:GetCommandCenter():MessageToGroup( Report, ReportGroup ) +end + + + +--- Report the task summary. +-- @param #MISSION self +-- @param Wrapper.Group#GROUP ReportGroup +function MISSION:MenuReportTasksSummary( ReportGroup ) + + local Report = self:ReportSummary( ReportGroup ) + + self:GetCommandCenter():MessageToGroup( Report, ReportGroup ) +end + + + + +--- @param #MISSION self +-- @param #string TaskStatus The status +-- @param Wrapper.Group#GROUP ReportGroup +function MISSION:MenuReportTasksPerStatus( ReportGroup, TaskStatus ) + + local Report = self:ReportOverview( ReportGroup, TaskStatus ) + + self:GetCommandCenter():MessageToGroup( Report, ReportGroup ) +end + + +--- @param #MISSION self +-- @param Wrapper.Group#GROUP ReportGroup +function MISSION:MenuReportPlayersPerTask( ReportGroup ) + + local Report = self:ReportPlayersPerTask() + + self:GetCommandCenter():MessageToGroup( Report, ReportGroup ) +end + +--- @param #MISSION self +-- @param Wrapper.Group#GROUP ReportGroup +function MISSION:MenuReportPlayersProgress( ReportGroup ) + + local Report = self:ReportPlayersProgress() + + self:GetCommandCenter():MessageToGroup( Report, ReportGroup ) +end + + + + diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 0f3931b1c..89c0e0bcd 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -1,9 +1,29 @@ ---- This module contains the TASK class. +--- **Tasking** -- This module contains the TASK class, the main engine to run human taskings. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- +-- @module Task + +--- @type TASK +-- @field Core.Scheduler#SCHEDULER TaskScheduler +-- @field Tasking.Mission#MISSION Mission +-- @field Core.Set#SET_GROUP SetGroup The Set of Groups assigned to the Task +-- @field Core.Fsm#FSM_PROCESS FsmTemplate +-- @field Tasking.Mission#MISSION Mission +-- @field Tasking.CommandCenter#COMMANDCENTER CommandCenter +-- @extends Core.Fsm#FSM_TASK + +--- +-- # TASK class, extends @{Base#BASE} +-- +-- ## The TASK class implements the methods for task orchestration within MOOSE. -- --- 1) @{#TASK} class, extends @{Base#BASE} --- ============================================ --- 1.1) The @{#TASK} class implements the methods for task orchestration within MOOSE. --- ---------------------------------------------------------------------------------------- -- The class provides a couple of methods to: -- -- * @{#TASK.AssignToGroup}():Assign a task to a group (of players). @@ -16,8 +36,8 @@ -- * @{#TASK.UnAssignFromUnit}(): Unassign the task from a unit. -- * @{#TASK.SetTimeOut}(): Set timer in seconds before task gets cancelled if not assigned. -- --- 1.2) Set and enquire task status (beyond the task state machine processing). --- ---------------------------------------------------------------------------- +-- ## 1.2) Set and enquire task status (beyond the task state machine processing). +-- -- A task needs to implement as a minimum the following task states: -- -- * **Success**: Expresses the successful execution and finalization of the task. @@ -35,30 +55,17 @@ -- The status of tasks can be set by the methods **State** followed by the task status. An example is `StateAssigned()`. -- The status of tasks can be enquired by the methods **IsState** followed by the task status name. An example is `if IsStateAssigned() then`. -- --- 1.3) Add scoring when reaching a certain task status: --- ----------------------------------------------------- +-- ## 1.3) Add scoring when reaching a certain task status: +-- -- Upon reaching a certain task status in a task, additional scoring can be given. If the Mission has a scoring system attached, the scores will be added to the mission scoring. -- Use the method @{#TASK.AddScore}() to add scores when a status is reached. -- --- 1.4) Task briefing: --- ------------------- +-- ## 1.4) Task briefing: +-- -- A task briefing can be given that is shown to the player when he is assigned to the task. -- --- === +-- @field #TASK TASK -- --- ### Authors: FlightControl - Design and Programming --- --- @module Task - ---- The TASK class --- @type TASK --- @field Core.Scheduler#SCHEDULER TaskScheduler --- @field Tasking.Mission#MISSION Mission --- @field Core.Set#SET_GROUP SetGroup The Set of Groups assigned to the Task --- @field Core.Fsm#FSM_PROCESS FsmTemplate --- @field Tasking.Mission#MISSION Mission --- @field Tasking.CommandCenter#COMMANDCENTER CommandCenter --- @extends Core.Fsm#FSM_TASK TASK = { ClassName = "TASK", TaskScheduler = nil, @@ -72,6 +79,7 @@ TASK = { Mission = nil, CommandCenter = nil, TimeOut = 0, + AssignedGroups = {}, } --- FSM PlayerAborted event handler prototype for TASK. @@ -150,17 +158,48 @@ TASK = { -- @param #string TaskName The name of the Task -- @param #string TaskType The type of the Task -- @return #TASK self -function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) +function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing ) - local self = BASE:Inherit( self, FSM_TASK:New() ) -- Core.Fsm#FSM_TASK + local self = BASE:Inherit( self, FSM_TASK:New() ) -- Tasking.Task#TASK self:SetStartState( "Planned" ) self:AddTransition( "Planned", "Assign", "Assigned" ) self:AddTransition( "Assigned", "AssignUnit", "Assigned" ) self:AddTransition( "Assigned", "Success", "Success" ) + self:AddTransition( "Assigned", "Hold", "Hold" ) self:AddTransition( "Assigned", "Fail", "Failed" ) self:AddTransition( "Assigned", "Abort", "Aborted" ) self:AddTransition( "Assigned", "Cancel", "Cancelled" ) + self:AddTransition( "Assigned", "Goal", "*" ) + + --- Goal Handler OnBefore for TASK + -- @function [parent=#TASK] OnBeforeGoal + -- @param #TASK self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Goal Handler OnAfter for TASK + -- @function [parent=#TASK] OnAfterGoal + -- @param #TASK self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Goal Trigger for TASK + -- @function [parent=#TASK] Goal + -- @param #TASK self + + --- Goal Asynchronous Trigger for TASK + -- @function [parent=#TASK] __Goal + -- @param #TASK self + -- @param #number Delay + + + self:AddTransition( "*", "PlayerCrashed", "*" ) self:AddTransition( "*", "PlayerAborted", "*" ) self:AddTransition( "*", "PlayerDead", "*" ) @@ -181,10 +220,13 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) self:SetName( TaskName ) self:SetID( Mission:GetNextTaskID( self ) ) -- The Mission orchestrates the task sequences .. - self.TaskBriefing = "You are invited for the task: " .. self.TaskName .. "." + self:SetBriefing( TaskBriefing ) self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() + self.TaskInfo = {} + + self.TaskProgress = {} return self end @@ -210,7 +252,7 @@ function TASK:SetUnitProcess( FsmTemplate ) end --- Add a PlayerUnit to join the Task. --- For each Group within the Task, the Unit is check if it can join the Task. +-- For each Group within the Task, the Unit is checked if it can join the Task. -- If the Unit was not part of the Task, false is returned. -- If the Unit is part of the Task, true is returned. -- @param #TASK self @@ -230,13 +272,13 @@ function TASK:JoinUnit( PlayerUnit, PlayerGroup ) -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is added to the Task. -- If the PlayerGroup is not assigned to the Task, the menu needs to be set. In that case, the PlayerUnit will become the GroupPlayer leader. if self:IsStatePlanned() or self:IsStateReplanned() then - self:SetMenuForGroup( PlayerGroup ) + --self:SetMenuForGroup( PlayerGroup ) --self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) end if self:IsStateAssigned() then - local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) - self:E( { IsAssignedToGroup = IsAssignedToGroup } ) - if IsAssignedToGroup then + local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) + self:E( { IsGroupAssigned = IsGroupAssigned } ) + if IsGroupAssigned then self:AssignToUnit( PlayerUnit ) self:MessageToGroups( PlayerUnit:GetPlayerName() .. " joined Task " .. self:GetName() ) end @@ -251,14 +293,11 @@ end -- If the Unit is part of the Task, true is returned. -- @param #TASK self -- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. --- @return #boolean true if Unit is part of the Task. -function TASK:AbortUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - local PlayerUnitAborted = false +-- @return #TASK +function TASK:AbortGroup( PlayerGroup ) + self:F( { PlayerGroup = PlayerGroup } ) local PlayerGroups = self:GetGroups() - local PlayerGroup = PlayerUnit:GetGroup() -- Is the PlayerGroup part of the PlayerGroups? if PlayerGroups:IsIncludeObject( PlayerGroup ) then @@ -266,23 +305,39 @@ function TASK:AbortUnit( PlayerUnit ) -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. if self:IsStateAssigned() then - local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) - self:E( { IsAssignedToGroup = IsAssignedToGroup } ) - if IsAssignedToGroup then - self:UnAssignFromUnit( PlayerUnit ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " aborted Task " .. self:GetName() ) - self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) - if #PlayerGroup:GetUnits() == 1 then - self:UnAssignFromGroup( PlayerGroup ) - PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) - self:RemoveMenuForGroup( PlayerGroup ) + local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) + self:E( { IsGroupAssigned = IsGroupAssigned } ) + if IsGroupAssigned then + local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName() + --self:MessageToGroups( PlayerName .. " aborted Task " .. self:GetName() ) + self:UnAssignFromGroup( PlayerGroup ) + --self:Abort() + + -- Now check if the task needs to go to hold... + -- It will go to hold, if there are no players in the mission... + + PlayerGroups:Flush() + local IsRemaining = false + for GroupName, AssignedGroup in pairs( PlayerGroups:GetSet() or {} ) do + if self:IsGroupAssigned( AssignedGroup ) == true then + IsRemaining = true + self:F( { Task = self:GetName(), IsRemaining = IsRemaining } ) + break + end end - self:Abort() + + self:F( { Task = self:GetName(), IsRemaining = IsRemaining } ) + if IsRemaining == false then + self:Abort() + end + + self:PlayerAborted( PlayerGroup:GetUnit(1) ) end + end end - return PlayerUnitAborted + return self end --- A PlayerUnit crashed in a Task. Abort the Player. @@ -290,14 +345,11 @@ end -- If the Unit is part of the Task, true is returned. -- @param #TASK self -- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. --- @return #boolean true if Unit is part of the Task. -function TASK:CrashUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - local PlayerUnitCrashed = false +-- @return #TASK +function TASK:CrashGroup( PlayerGroup ) + self:F( { PlayerGroup = PlayerGroup } ) local PlayerGroups = self:GetGroups() - local PlayerGroup = PlayerUnit:GetGroup() -- Is the PlayerGroup part of the PlayerGroups? if PlayerGroups:IsIncludeObject( PlayerGroup ) then @@ -305,22 +357,38 @@ function TASK:CrashUnit( PlayerUnit ) -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. if self:IsStateAssigned() then - local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) - self:E( { IsAssignedToGroup = IsAssignedToGroup } ) - if IsAssignedToGroup then - self:UnAssignFromUnit( PlayerUnit ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " crashed in Task " .. self:GetName() ) - self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) - if #PlayerGroup:GetUnits() == 1 then - PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) - self:RemoveMenuForGroup( PlayerGroup ) + local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) + self:E( { IsGroupAssigned = IsGroupAssigned } ) + if IsGroupAssigned then + local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName() + self:MessageToGroups( PlayerName .. " crashed! " ) + self:UnAssignFromGroup( PlayerGroup ) + + -- Now check if the task needs to go to hold... + -- It will go to hold, if there are no players in the mission... + + PlayerGroups:Flush() + local IsRemaining = false + for GroupName, AssignedGroup in pairs( PlayerGroups:GetSet() or {} ) do + if self:IsGroupAssigned( AssignedGroup ) == true then + IsRemaining = true + self:F( { Task = self:GetName(), IsRemaining = IsRemaining } ) + break + end end - self:PlayerCrashed( PlayerUnit ) + + self:F( { Task = self:GetName(), IsRemaining = IsRemaining } ) + if IsRemaining == false then + self:Abort() + end + + self:PlayerCrashed( PlayerGroup:GetUnit(1) ) end + end end - return PlayerUnitCrashed + return self end @@ -341,46 +409,157 @@ function TASK:GetGroups() return self.SetGroup end +do -- Group Assignment - ---- Assign the @{Task} to a @{Group}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @return #TASK -function TASK:AssignToGroup( TaskGroup ) - self:F2( TaskGroup:GetName() ) + --- Returns if the @{Task} is assigned to the Group. + -- @param #TASK self + -- @param Wrapper.Group#GROUP TaskGroup + -- @return #boolean + function TASK:IsGroupAssigned( TaskGroup ) - local TaskGroupName = TaskGroup:GetName() - - TaskGroup:SetState( TaskGroup, "Assigned", self ) - - local Mission = self:GetMission() - local MissionMenu = Mission:GetMenu( TaskGroup ) - MissionMenu:RemoveSubMenus() - - --self:RemoveMenuForGroup( TaskGroup ) - self:SetAssignedMenuForGroup( TaskGroup ) - - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - self:E(PlayerName) - if PlayerName ~= nil or PlayerName ~= "" then - self:AssignToUnit( TaskUnit ) + local TaskGroupName = TaskGroup:GetName() + + if self.AssignedGroups[TaskGroupName] then + self:T( { "Task is assigned to:", TaskGroup:GetName() } ) + return true end + + self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) + return false + end + + + --- Set @{Group} assigned to the @{Task}. + -- @param #TASK self + -- @param Wrapper.Group#GROUP TaskGroup + -- @return #TASK + function TASK:SetGroupAssigned( TaskGroup ) + + local TaskName = self:GetName() + local TaskGroupName = TaskGroup:GetName() + + self.AssignedGroups[TaskGroupName] = TaskGroup + self:E( string.format( "Task %s is assigned to %s", TaskName, TaskGroupName ) ) + + -- Set the group to be assigned at mission level. This allows to decide the menu options on mission level for this group. + self:GetMission():SetGroupAssigned( TaskGroup ) + + local SetAssignedGroups = self:GetGroups() + +-- SetAssignedGroups:ForEachGroup( +-- function( AssignedGroup ) +-- if self:IsGroupAssigned(AssignedGroup) then +-- self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is assigned to group %s.", TaskName, TaskGroupName ), AssignedGroup ) +-- else +-- self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is assigned to your group.", TaskName ), AssignedGroup ) +-- end +-- end +-- ) + + return self + end + + --- Clear the @{Group} assignment from the @{Task}. + -- @param #TASK self + -- @param Wrapper.Group#GROUP TaskGroup + -- @return #TASK + function TASK:ClearGroupAssignment( TaskGroup ) + + local TaskName = self:GetName() + local TaskGroupName = TaskGroup:GetName() + + self.AssignedGroups[TaskGroupName] = nil + --self:E( string.format( "Task %s is unassigned to %s", TaskName, TaskGroupName ) ) + + -- Set the group to be assigned at mission level. This allows to decide the menu options on mission level for this group. + self:GetMission():ClearGroupAssignment( TaskGroup ) + + local SetAssignedGroups = self:GetGroups() + + SetAssignedGroups:ForEachGroup( + function( AssignedGroup ) + if self:IsGroupAssigned(AssignedGroup) then + --self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from group %s.", TaskName, TaskGroupName ), AssignedGroup ) + else + --self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from your group.", TaskName ), AssignedGroup ) + end + end + ) + + return self end - return self end +do -- Group Assignment + + --- Assign the @{Task} to a @{Group}. + -- @param #TASK self + -- @param Wrapper.Group#GROUP TaskGroup + -- @return #TASK + function TASK:AssignToGroup( TaskGroup ) + self:F( TaskGroup:GetName() ) + + local TaskGroupName = TaskGroup:GetName() + local Mission = self:GetMission() + local CommandCenter = Mission:GetCommandCenter() + + self:SetGroupAssigned( TaskGroup ) + + local TaskUnits = TaskGroup:GetUnits() + for UnitID, UnitData in pairs( TaskUnits ) do + local TaskUnit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = TaskUnit:GetPlayerName() + self:E(PlayerName) + if PlayerName ~= nil and PlayerName ~= "" then + self:AssignToUnit( TaskUnit ) + CommandCenter:MessageToGroup( + string.format( 'Task "%s": Briefing for player (%s):\n%s', + self:GetName(), + PlayerName, + self:GetBriefing() + ), TaskGroup + ) + end + end + + CommandCenter:SetMenu() + + return self + end + + --- UnAssign the @{Task} from a @{Group}. + -- @param #TASK self + -- @param Wrapper.Group#GROUP TaskGroup + function TASK:UnAssignFromGroup( TaskGroup ) + self:F2( { TaskGroup = TaskGroup:GetName() } ) + + self:ClearGroupAssignment( TaskGroup ) + + local TaskUnits = TaskGroup:GetUnits() + for UnitID, UnitData in pairs( TaskUnits ) do + local TaskUnit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = TaskUnit:GetPlayerName() + if PlayerName ~= nil and PlayerName ~= "" then -- Only remove units that have players! + self:UnAssignFromUnit( TaskUnit ) + end + end + + local Mission = self:GetMission() + local CommandCenter = Mission:GetCommandCenter() + CommandCenter:SetMenu() + end +end + + --- -- @param #TASK self -- @param Wrapper.Group#GROUP FindGroup -- @return #boolean function TASK:HasGroup( FindGroup ) - return self:GetGroups():IsIncludeObject( FindGroup ) + local SetAttackGroup = self:GetGroups() + return SetAttackGroup:FindGroup(FindGroup) end @@ -395,7 +574,6 @@ function TASK:AssignToUnit( TaskUnit ) -- Assign a new FsmUnit to TaskUnit. local FsmUnit = self:SetStateMachine( TaskUnit, FsmTemplate:Copy( TaskUnit, self ) ) -- Core.Fsm#FSM_PROCESS - self:E({"Address FsmUnit", tostring( FsmUnit ) } ) FsmUnit:SetStartState( "Planned" ) @@ -449,7 +627,7 @@ function TASK:SendBriefingToAssignedGroups() for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if self:IsAssignedToGroup( TaskGroup ) then + if self:IsGroupAssigned( TaskGroup ) then TaskGroup:Message( self.TaskBriefing, 60 ) end end @@ -462,50 +640,14 @@ function TASK:UnAssignFromGroups() self:F2() for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - self:UnAssignFromGroup( TaskGroup ) - end -end - ---- UnAssign the @{Task} from a @{Group}. --- @param #TASK self -function TASK:UnAssignFromGroup( TaskGroup ) - self:F2( { TaskGroup } ) - - TaskGroup:SetState( TaskGroup, "Assigned", nil ) - - self:RemoveAssignedMenuForGroup( TaskGroup ) - - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - if PlayerName ~= nil or PlayerName ~= "" then - self:UnAssignFromUnit( TaskUnit ) + if self:IsGroupAssigned(TaskGroup) then + self:UnAssignFromGroup( TaskGroup ) end end end ---- Returns if the @{Task} is assigned to the Group. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @return #boolean -function TASK:IsAssignedToGroup( TaskGroup ) - - local TaskGroupName = TaskGroup:GetName() - - if self:IsStateAssigned() then - if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then - self:T( { "Task is assigned to:", TaskGroup:GetName() } ) - return true - end - end - - self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) - return false -end - --- Returns if the @{Task} has still alive and assigned Units. -- @param #TASK self -- @return #boolean @@ -514,7 +656,7 @@ function TASK:HasAliveUnits() for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do if self:IsStateAssigned() then - if self:IsAssignedToGroup( TaskGroup ) then + if self:IsGroupAssigned( TaskGroup ) then for TaskUnitID, TaskUnit in pairs( TaskGroup:GetUnits() ) do if TaskUnit:IsAlive() then self:T( { HasAliveUnits = true } ) @@ -533,14 +675,19 @@ end -- @param #TASK self -- @param #number MenuTime -- @return #TASK -function TASK:SetMenu( MenuTime ) - self:F() +function TASK:SetMenu( MenuTime ) --R2.1 Mission Reports and Task Reports added. Fixes issue #424. + self:F( { self:GetName(), MenuTime } ) - self.SetGroup:Flush() + --self.SetGroup:Flush() for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do - local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP + local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then - if self:IsStatePlanned() or self:IsStateReplanned() then + + -- Set Mission Menus + + local Mission = self:GetMission() + local MissionMenu = Mission:GetMenu( TaskGroup ) + if MissionMenu then self:SetMenuForGroup( TaskGroup, MenuTime ) end end @@ -555,10 +702,9 @@ end -- @return #TASK function TASK:SetMenuForGroup( TaskGroup, MenuTime ) - if not TaskGroup:GetState( TaskGroup, "Assigned" ) then - self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName(), MenuTime ) - else - if not self:IsAssignedToGroup( TaskGroup ) then + if self:IsStatePlanned() or self:IsStateAssigned() then + self:SetPlannedMenuForGroup( TaskGroup, MenuTime ) + if self:IsGroupAssigned( TaskGroup ) then self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) end end @@ -571,22 +717,37 @@ end -- @param #string MenuText The menu text. -- @param #number MenuTime -- @return #TASK self -function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime ) - self:E( TaskGroup:GetName() ) +function TASK:SetPlannedMenuForGroup( TaskGroup, MenuTime ) + self:F( TaskGroup:GetName() ) local Mission = self:GetMission() local MissionName = Mission:GetName() local CommandCenter = Mission:GetCommandCenter() local CommandCenterMenu = CommandCenter:GetMenu() - local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) - - - local MissionMenu = Mission:GetMenu( TaskGroup ) - local TaskType = self:GetType() - local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ):SetTime( MenuTime ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ):SetTime( MenuTime ):SetRemoveParent( true ) +-- local TaskThreatLevel = self.TaskInfo["ThreatLevel"] +-- local TaskThreatLevelString = TaskThreatLevel and " [" .. string.rep( "â– ", TaskThreatLevel ) .. "]" or " []" + local TaskPlayerCount = self:GetPlayerCount() + local TaskPlayerString = string.format( " (%dp)", TaskPlayerCount ) + local TaskText = string.format( "%s%s", self:GetName(), TaskPlayerString ) --, TaskThreatLevelString ) + local TaskName = string.format( "%s", self:GetName() ) + + local MissionMenu = Mission:GetMenu( TaskGroup ) + --local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) + + --local MissionMenu = Mission:GetMenu( TaskGroup ) + + self.MenuPlanned = self.MenuPlanned or {} + self.MenuPlanned[TaskGroup] = MENU_GROUP:New( TaskGroup, "Join Planned Task", MissionMenu, Mission.MenuReportTasksPerStatus, Mission, TaskGroup, "Planned" ):SetTime( MenuTime ):SetTag( "Tasking" ) + local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, self.MenuPlanned[TaskGroup] ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true ) + local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskText, TaskTypeMenu ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true ) + local ReportTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Status" ), TaskTypeMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true ) + + if not Mission:IsGroupAssigned( TaskGroup ) then + self:F( { "Replacing Join Task menu" } ) + local JoinTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Join Task" ), TaskTypeMenu, self.MenuAssignToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true ) + end return self end @@ -597,15 +758,29 @@ end -- @param #number MenuTime -- @return #TASK self function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) - self:E( TaskGroup:GetName() ) + self:F( { TaskGroup:GetName(), MenuTime } ) local Mission = self:GetMission() + local MissionName = Mission:GetName() + local CommandCenter = Mission:GetCommandCenter() + local CommandCenterMenu = CommandCenter:GetMenu() + + local TaskType = self:GetType() +-- local TaskThreatLevel = self.TaskInfo["ThreatLevel"] +-- local TaskThreatLevelString = TaskThreatLevel and " [" .. string.rep( "â– ", TaskThreatLevel ) .. "]" or " []" + local TaskPlayerCount = self:GetPlayerCount() + local TaskPlayerString = string.format( " (%dp)", TaskPlayerCount ) + local TaskText = string.format( "%s%s", self:GetName(), TaskPlayerString ) --, TaskThreatLevelString ) + local TaskName = string.format( "%s", self:GetName() ) + local MissionMenu = Mission:GetMenu( TaskGroup ) +-- local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) +-- local MissionMenu = Mission:GetMenu( TaskGroup ) - self:E( { MissionMenu = MissionMenu } ) - - local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ) + self.MenuAssigned = self.MenuAssigned or {} + self.MenuAssigned[TaskGroup] = MENU_GROUP:New( TaskGroup, string.format( "Assigned Task %s", TaskName ), MissionMenu ):SetTime( MenuTime ):SetTag( "Tasking" ) + local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Status" ), self.MenuAssigned[TaskGroup], self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true ) + local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Abort Group from Task" ), self.MenuAssigned[TaskGroup], self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true ) return self end @@ -615,15 +790,11 @@ end -- @param #number MenuTime -- @return #TASK function TASK:RemoveMenu( MenuTime ) - self:F() + self:F( { self:GetName(), MenuTime } ) for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do local TaskGroup = TaskGroup -- Wrapper.Group#GROUP - if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then - if not self:IsAssignedToGroup( TaskGroup ) then - self:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) - end - end + self:RefreshMenus( TaskGroup, MenuTime ) end end @@ -633,24 +804,29 @@ end -- @param Wrapper.Group#GROUP TaskGroup -- @param #number MenuTime -- @return #TASK self -function TASK:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) - self:F() +function TASK:RefreshMenus( TaskGroup, MenuTime ) + self:F( { TaskGroup:GetName(), MenuTime } ) local Mission = self:GetMission() local MissionName = Mission:GetName() - + local CommandCenter = Mission:GetCommandCenter() + local CommandCenterMenu = CommandCenter:GetMenu() + local MissionMenu = Mission:GetMenu( TaskGroup ) + + local TaskName = self:GetName() + self.MenuPlanned = self.MenuPlanned or {} + local PlannedMenu = self.MenuPlanned[TaskGroup] - if MissionMenu then - local TaskType = self:GetType() - local TypeMenu = MissionMenu:GetMenu( TaskType ) - - if TypeMenu then - local TaskMenu = TypeMenu:GetMenu( self:GetTaskName() ) - if TaskMenu then - TaskMenu:Remove( MenuTime ) - end - end + self.MenuAssigned = self.MenuAssigned or {} + local AssignedMenu = self.MenuAssigned[TaskGroup] + + if PlannedMenu then + PlannedMenu:Remove( MenuTime , "Tasking") + end + + if AssignedMenu then + AssignedMenu:Remove( MenuTime, "Tasking" ) end end @@ -674,11 +850,10 @@ function TASK:RemoveAssignedMenuForGroup( TaskGroup ) end -function TASK.MenuAssignToGroup( MenuParam ) +--- @param #TASK self +-- @param Wrapper.Group#GROUP TaskGroup +function TASK:MenuAssignToGroup( TaskGroup ) - local self = MenuParam.self - local TaskGroup = MenuParam.TaskGroup - self:E( "Assigned menu selected") self:AssignToGroup( TaskGroup ) @@ -688,7 +863,7 @@ end -- @param #TASK self function TASK:MenuTaskStatus( TaskGroup ) - local ReportText = self:ReportDetails() + local ReportText = self:ReportDetails( TaskGroup ) self:T( ReportText ) self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup ) @@ -699,7 +874,7 @@ end -- @param #TASK self function TASK:MenuTaskAbort( TaskGroup ) - self:Abort() + self:AbortGroup( TaskGroup ) end @@ -711,6 +886,13 @@ function TASK:GetTaskName() return self.TaskName end +--- Returns the @{Task} briefing. +-- @param #TASK self +-- @return #string Task briefing. +function TASK:GetTaskBriefing() + return self.TaskBriefing +end + @@ -768,19 +950,25 @@ end -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self function TASK:RemoveStateMachine( TaskUnit ) - self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) + self:F( { TaskUnit = TaskUnit:GetName(), HasFsm = ( self.Fsm[TaskUnit] ~= nil ) } ) - self:E( self.Fsm ) - for TaskUnitT, Fsm in pairs( self.Fsm ) do - self:E( TaskUnitT ) + --self:E( self.Fsm ) + --for TaskUnitT, Fsm in pairs( self.Fsm ) do + --local Fsm = Fsm -- Core.Fsm#FSM_PROCESS + --self:E( TaskUnitT ) + --self.Fsm[TaskUnit] = nil + --end + + if self.Fsm[TaskUnit] then + self.Fsm[TaskUnit]:Remove() + self.Fsm[TaskUnit] = nil end - - self.Fsm[TaskUnit] = nil collectgarbage() self:E( "Garbage Collected, Processes should be finalized now ...") end + --- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit @@ -832,6 +1020,19 @@ function TASK:SetType( TaskType ) self.TaskType = TaskType end +--- Sets the Information on the Task +-- @param #TASK self +-- @param #string TaskInfo The key and title of the task information. +-- @param #string TaskInfoText The Task info text. +-- @param #number TaskInfoOrder The ordering, a number between 0 and 99. +function TASK:SetInfo( TaskInfo, TaskInfoText, TaskInfoOrder ) + + self.TaskInfo = self.TaskInfo or {} + self.TaskInfo[TaskInfo] = self.TaskInfo[TaskInfo] or {} + self.TaskInfo[TaskInfo].TaskInfoText = TaskInfoText + self.TaskInfo[TaskInfo].TaskInfoOrder = TaskInfoOrder +end + --- Gets the Type of the Task -- @param #TASK self -- @return #string TaskType @@ -969,10 +1170,18 @@ end -- @param #string TaskBriefing -- @return #TASK self function TASK:SetBriefing( TaskBriefing ) + self:E(TaskBriefing) self.TaskBriefing = TaskBriefing return self end +--- Gets the @{Task} briefing. +-- @param #TASK self +-- @return #string The briefing text. +function TASK:GetBriefing() + return self.TaskBriefing +end + @@ -983,16 +1192,29 @@ end -- @param #string To function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName ) - self:E( { "Task Assigned", self.Dispatcher } ) - - self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned to your group." ) - - if self.Dispatcher then - self:E( "Firing Assign event " ) - self.Dispatcher:Assign( self, PlayerUnit, PlayerName ) + + --- This test is required, because the state transition will be fired also when the state does not change in case of an event. + if From ~= "Assigned" then + self:E( { From, Event, To, PlayerUnit:GetName(), PlayerName } ) + + self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is assigned." ) + + -- Set the total Progress to be achieved. + + self:SetGoalTotal() -- Polymorphic to set the initial goal total! + + if self.Dispatcher then + self:E( "Firing Assign event " ) + self.Dispatcher:Assign( self, PlayerUnit, PlayerName ) + end + + self:GetMission():__Start( 1 ) + + -- When the task is assigned, the task goal needs to be checked of the derived classes. + self:__Goal( -10 ) -- Polymorphic + + self:SetMenu() end - - self:GetMission():__Start( 1 ) end @@ -1008,7 +1230,7 @@ function TASK:onenterSuccess( From, Event, To ) self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is successful! Good job!" ) self:UnAssignFromGroups() - self:GetMission():__Complete( 1 ) + self:GetMission():__MissionGoals( 1 ) end @@ -1021,12 +1243,30 @@ end function TASK:onenterAborted( From, Event, To ) self:E( "Task Aborted" ) + + if From ~= "Aborted" then + self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has been aborted! Task may be replanned." ) + self:__Replan( 5 ) + self:SetMenu() + end + +end - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has been aborted! Task may be replanned." ) +--- FSM function for a TASK +-- @param #TASK self +-- @param #string From +-- @param #string Event +-- @param #string To +function TASK:onenterCancelled( From, Event, To ) + + self:E( "Task Cancelled" ) - self:UnAssignFromGroups() + if From ~= "Cancelled" then + self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has been cancelled! The tactical situation has changed." ) + self:UnAssignFromGroups() + self:SetMenu() + end - self:__Replan( 5 ) end --- FSM function for a TASK @@ -1066,7 +1306,7 @@ end function TASK:onstatechange( From, Event, To ) if self:IsTrace() then - MESSAGE:New( "@ Task " .. self.TaskName .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() + --MESSAGE:New( "@ Task " .. self.TaskName .. " : " .. From .. " changed to " .. To .. " by " .. Event, 2 ):ToAll() end if self.Scores[To] then @@ -1103,7 +1343,7 @@ function TASK:onbeforeTimeOut( From, Event, To ) return false end -do -- Dispatcher +do -- Links --- Set dispatcher of a task -- @param #TASK self @@ -1113,6 +1353,19 @@ do -- Dispatcher self.Dispatcher = Dispatcher end + --- Set detection of a task + -- @param #TASK self + -- @param Function.Detection#DETECTION_BASE Detection + -- @param #number DetectedItemIndex + -- @return #TASK + function TASK:SetDetection( Detection, DetectedItemIndex ) + + self:E({DetectedItemIndex,Detection}) + + self.Detection = Detection + self.DetectedItemIndex = DetectedItemIndex + end + end do -- Reporting @@ -1120,52 +1373,249 @@ do -- Reporting --- Create a summary report of the Task. -- List the Task Name and Status -- @param #TASK self +-- @param Wrapper.Group#GROUP ReportGroup -- @return #string -function TASK:ReportSummary() +function TASK:ReportSummary( ReportGroup ) local Report = REPORT:New() -- List the name of the Task. - local Name = self:GetName() + Report:Add( self:GetName() ) -- Determine the status of the Task. - local State = self:GetState() + Report:Add( "State: <" .. self:GetState() .. ">" ) - Report:Add( "Task " .. Name .. " - State '" .. State ) + if self.TaskInfo["Coordinates"] then + local TaskInfoIDText = string.format( "%s: ", "Coordinate" ) + local TaskCoord = self.TaskInfo["Coordinates"].TaskInfoText -- Core.Point#COORDINATE + Report:Add( TaskInfoIDText .. TaskCoord:ToString( ReportGroup, nil, self ) ) + end + + return Report:Text( ', ' ) +end +--- Create an overiew report of the Task. +-- List the Task Name and Status +-- @param #TASK self +-- @return #string +function TASK:ReportOverview( ReportGroup ) + + self:UpdateTaskInfo() + + -- List the name of the Task. + local TaskName = self:GetName() + local Report = REPORT:New() + + local Line = 0 + local LineReport = REPORT:New() + + for TaskInfoID, TaskInfo in UTILS.spairs( self.TaskInfo, function( t, a, b ) return t[a].TaskInfoOrder < t[b].TaskInfoOrder end ) do + + self:F( { TaskInfo = TaskInfo } ) + + if Line < math.floor( TaskInfo.TaskInfoOrder / 10 ) then + if Line ~= 0 then + Report:AddIndent( LineReport:Text( ", " ) ) + else + Report:Add( TaskName .. ", " .. LineReport:Text( ", " ) ) + end + LineReport = REPORT:New() + Line = math.floor( TaskInfo.TaskInfoOrder / 10 ) + end + + local TaskInfoIDText = string.format( "%s: ", TaskInfoID ) + + if type( TaskInfo.TaskInfoText ) == "string" then + LineReport:Add( TaskInfoIDText .. TaskInfo.TaskInfoText ) + elseif type(TaskInfo) == "table" then + if TaskInfoID == "Coordinates" then + local ToCoordinate = TaskInfo.TaskInfoText -- Core.Point#COORDINATE + --Report:Add( TaskInfoIDText ) + LineReport:Add( TaskInfoIDText .. ToCoordinate:ToString( ReportGroup, nil, self ) ) + --Report:AddIndent( ToCoordinate:ToStringBULLS( ReportGroup:GetCoalition() ) ) + else + end + end + end + + Report:AddIndent( LineReport:Text( ", " ) ) + return Report:Text() end +--- Create a count of the players in the Task. +-- @param #TASK self +-- @return #number The total number of players in the task. +function TASK:GetPlayerCount() --R2.1 Get a count of the players. + + local PlayerCount = 0 + + -- Loop each Unit active in the Task, and find Player Names. + for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do + local PlayerGroup = PlayerGroup -- Wrapper.Group#GROUP + if self:IsGroupAssigned( PlayerGroup ) then + local PlayerNames = PlayerGroup:GetPlayerNames() + PlayerCount = PlayerCount + #PlayerNames + end + end + + return PlayerCount +end + + +--- Create a list of the players in the Task. +-- @param #TASK self +-- @return #map<#string,Wrapper.Group#GROUP> A map of the players +function TASK:GetPlayerNames() --R2.1 Get a map of the players. + + local PlayerNameMap = {} + + -- Loop each Unit active in the Task, and find Player Names. + for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do + local PlayerGroup = PlayerGroup -- Wrapper.Group#GROUP + if self:IsGroupAssigned( PlayerGroup ) then + local PlayerNames = PlayerGroup:GetPlayerNames() + for PlayerNameID, PlayerName in pairs( PlayerNames ) do + PlayerNameMap[PlayerName] = PlayerGroup + end + end + end + + return PlayerNameMap +end + --- Create a detailed report of the Task. -- List the Task Status, and the Players assigned to the Task. -- @param #TASK self +-- @param Wrapper.Group#GROUP TaskGroup -- @return #string -function TASK:ReportDetails() +function TASK:ReportDetails( ReportGroup ) - local Report = REPORT:New() + self:UpdateTaskInfo() + + local Report = REPORT:New():SetIndent( 3 ) -- List the name of the Task. local Name = self:GetName() -- Determine the status of the Task. - local State = self:GetState() - + local Status = "<" .. self:GetState() .. ">" + -- Loop each Unit active in the Task, and find Player Names. - local PlayerNames = {} - local PlayerReport = REPORT:New( " - Players:" ) - for PlayerGroupID, PlayerGroupData in pairs( self:GetGroups():GetSet() ) do - local PlayerGroup = PlayerGroupData -- Wrapper.Group#GROUP - PlayerNames = PlayerGroup:GetPlayerNames() - if PlayerNames then - PlayerReport:Add( " -- Group " .. PlayerGroup:GetCallsign() .. ": " .. table.concat( PlayerNames, ", " ) ) + local PlayerNames = self:GetPlayerNames() + + local PlayerReport = REPORT:New() + for PlayerName, PlayerGroup in pairs( PlayerNames ) do + PlayerReport:Add( "Group " .. PlayerGroup:GetCallsign() .. ": " .. PlayerName ) + end + local Players = PlayerReport:Text() + + Report:Add( "Task: " .. Name .. " - " .. Status .. " - Detailed Report" ) + Report:Add( " - Players:" ) + Report:AddIndent( Players ) + + for TaskInfoID, TaskInfo in pairs( self.TaskInfo, function( t, a, b ) return t[a].TaskInfoOrder < t[b].TaskInfoOrder end ) do + + local TaskInfoIDText = string.format( " - %s: ", TaskInfoID ) + + if type( TaskInfo.TaskInfoText ) == "string" then + Report:Add( TaskInfoIDText .. TaskInfo.TaskInfoText ) + elseif type(TaskInfo) == "table" then + if TaskInfoID == "Coordinates" then + local FromCoordinate = ReportGroup:GetUnit(1):GetCoordinate() + local ToCoordinate = TaskInfo.TaskInfoText -- Core.Point#COORDINATE + Report:Add( TaskInfoIDText ) + Report:AddIndent( ToCoordinate:ToStringBRA( FromCoordinate ) .. ", " .. TaskInfo.TaskInfoText:ToStringAspect( FromCoordinate ) ) + Report:AddIndent( ToCoordinate:ToStringBULLS( ReportGroup:GetCoalition() ) ) + else + end end + end - -- Loop each Process in the Task, and find Reporting Details. - Report:Add( string.format( " - Task %s\n -- State '%s'\n%s", Name, State, PlayerReport:Text() ) ) return Report:Text() end end -- Reporting + + +do -- Additional Task Scoring and Task Progress + + --- Add Task Progress for a Player Name + -- @param #TASK self + -- @param #string PlayerName The name of the player. + -- @param #string ProgressText The text that explains the Progress achieved. + -- @param #number ProgressTime The time the progress was achieved. + -- @oaram #number ProgressPoints The amount of points of magnitude granted. This will determine the shared Mission Success scoring. + -- @return #TASK + function TASK:AddProgress( PlayerName, ProgressText, ProgressTime, ProgressPoints ) + self.TaskProgress = self.TaskProgress or {} + self.TaskProgress[ProgressTime] = self.TaskProgress[ProgressTime] or {} + self.TaskProgress[ProgressTime].PlayerName = PlayerName + self.TaskProgress[ProgressTime].ProgressText = ProgressText + self.TaskProgress[ProgressTime].ProgressPoints = ProgressPoints + self:GetMission():AddPlayerName( PlayerName ) + return self + end + + function TASK:GetPlayerProgress( PlayerName ) + local ProgressPlayer = 0 + for ProgressTime, ProgressData in pairs( self.TaskProgress ) do + if PlayerName == ProgressData.PlayerName then + ProgressPlayer = ProgressPlayer + ProgressData.ProgressPoints + end + end + return ProgressPlayer + end + + --- Set a score when progress has been made by the player. + -- @param #TASK self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points to be granted when task process has been achieved. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK + function TASK:SetScoreOnProgress( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountPlayer", "Player " .. PlayerName .. " has achieved progress.", Score ) + + return self + end + + --- Set a score when all the targets in scope of the A2A attack, have been destroyed. + -- @param #TASK self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK + function TASK:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Success", "The task is a success!", Score ) + + return self + end + + --- Set a penalty when the A2A attack has failed. + -- @param #TASK self + -- @param #string PlayerName The name of the player. + -- @param #number Penalty The penalty in points, must be a negative value! + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK + function TASK:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) + self:F( { PlayerName, Penalty, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Failed", "The task is a failure!", Penalty ) + + return self + end + +end diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua new file mode 100644 index 000000000..a40424276 --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -0,0 +1,735 @@ +--- **Tasking** - The TASK_A2A models tasks for players in Air to Air engagements. +-- +-- ![Banner Image](..\Presentations\TASK_A2A\Dia1.JPG) +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- +-- @module Task_A2A + +do -- TASK_A2A + + --- The TASK_A2A class + -- @type TASK_A2A + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + + --- # TASK_A2A class, extends @{Task#TASK} + -- + -- The TASK_A2A class defines Air To Air tasks for a @{Set} of Target Units, + -- based on the tasking capabilities defined in @{Task#TASK}. + -- The TASK_A2A is implemented using a @{Fsm#FSM_TASK}, and has the following statuses: + -- + -- * **None**: Start of the process + -- * **Planned**: The A2A task is planned. + -- * **Assigned**: The A2A task is assigned to a @{Group#GROUP}. + -- * **Success**: The A2A task is successfully completed. + -- * **Failed**: The A2A task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. + -- + -- # 1.1) Set the scoring of achievements in an A2A attack. + -- + -- Scoring or penalties can be given in the following circumstances: + -- + -- * @{#TASK_A2A.SetScoreOnDestroy}(): Set a score when a target in scope of the A2A attack, has been destroyed. + -- * @{#TASK_A2A.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2A attack, have been destroyed. + -- * @{#TASK_A2A.SetPenaltyOnFailed}(): Set a penalty when the A2A attack has failed. + -- + -- @field #TASK_A2A + TASK_A2A = { + ClassName = "TASK_A2A", + } + + --- Instantiates a new TASK_A2A. + -- @param #TASK_A2A self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetAttack The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Set#SET_UNIT UnitSetTargets + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @return #TASK_A2A self + function TASK_A2A:New( Mission, SetAttack, TaskName, TargetSetUnit, TaskType, TaskBriefing ) + local self = BASE:Inherit( self, TASK:New( Mission, SetAttack, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2A + self:F() + + self.TargetSetUnit = TargetSetUnit + self.TaskType = TaskType + + local Fsm = self:GetUnitProcess() + + + Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } ) + + Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) + Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) + Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) + + Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" ) + + Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" ) + Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" ) + + Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} ) + Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" ) + Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} ) + Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} ) + Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" ) + +-- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) +-- Fsm:AddTransition( "Accounted", "Success", "Success" ) + Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) + Fsm:AddTransition( "Failed", "Fail", "Failed" ) + + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2A#TASK_A2A Task + function Fsm:onafterRouteToRendezVous( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.RendezVousSetUnit + + if Task:GetRendezVousZone( TaskUnit ) then + self:__RouteToRendezVousZone( 0.1 ) + else + if Task:GetRendezVousCoordinate( TaskUnit ) then + self:__RouteToRendezVousPoint( 0.1 ) + else + self:__ArriveAtRendezVous( 0.1 ) + end + end + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task#TASK_A2A Task + function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.TargetSetUnit + + self:__Engage( 0.1 ) + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task#TASK_A2A Task + function Fsm:onafterEngage( TaskUnit, Task ) + self:E( { self } ) + self:__Account( 0.1 ) + self:__RouteToTarget(0.1 ) + self:__RouteToTargets( -10 ) + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2A#TASK_A2A Task + function Fsm:onafterRouteToTarget( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.TargetSetUnit + + if Task:GetTargetZone( TaskUnit ) then + self:__RouteToTargetZone( 0.1 ) + else + local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT + if TargetUnit then + local Coordinate = TargetUnit:GetCoordinate() + self:T( { TargetCoordinate = Coordinate, Coordinate:GetX(), Coordinate:GetAlt(), Coordinate:GetZ() } ) + Task:SetTargetCoordinate( TargetUnit:GetCoordinate(), TaskUnit ) + end + self:__RouteToTargetPoint( 0.1 ) + end + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2A#TASK_A2A Task + function Fsm:onafterRouteToTargets( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT + if TargetUnit then + Task:SetTargetCoordinate( TargetUnit:GetCoordinate(), TaskUnit ) + end + self:__RouteToTargets( -10 ) + end + + return self + + end + + --- @param #TASK_A2A self + function TASK_A2A:GetPlannedMenuText() + return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" + end + + --- @param #TASK_A2A self + -- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. + -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2A:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + ActRouteRendezVous:SetCoordinate( RendezVousCoordinate ) + ActRouteRendezVous:SetRange( RendezVousRange ) + end + + --- @param #TASK_A2A self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. + -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. + function TASK_A2A:GetRendezVousCoordinate( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange() + end + + + + --- @param #TASK_A2A self + -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2A:SetRendezVousZone( RendezVousZone, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + ActRouteRendezVous:SetZone( RendezVousZone ) + end + + --- @param #TASK_A2A self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map. + function TASK_A2A:GetRendezVousZone( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + return ActRouteRendezVous:GetZone() + end + + --- @param #TASK_A2A self + -- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2A:SetTargetCoordinate( TargetCoordinate, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + ActRouteTarget:SetCoordinate( TargetCoordinate ) + end + + + --- @param #TASK_A2A self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map. + function TASK_A2A:GetTargetCoordinate( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + return ActRouteTarget:GetCoordinate() + end + + + --- @param #TASK_A2A self + -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. + -- @param Wrapper.Unit#UNIT TaskUnit + function TASK_A2A:SetTargetZone( TargetZone, Altitude, Heading, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + ActRouteTarget:SetZone( TargetZone, Altitude, Heading ) + end + + + --- @param #TASK_A2A self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. + function TASK_A2A:GetTargetZone( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + return ActRouteTarget:GetZone() + end + + function TASK_A2A:SetGoalTotal() + + self.GoalTotal = self.TargetSetUnit:Count() + end + + function TASK_A2A:GetGoalTotal() + + return self.GoalTotal + end + + + +end + + +do -- TASK_A2A_INTERCEPT + + --- The TASK_A2A_INTERCEPT class + -- @type TASK_A2A_INTERCEPT + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + + --- # TASK_A2A_INTERCEPT class, extends @{Task_A2A#TASK_A2A} + -- + -- The TASK_A2A_INTERCEPT class defines an intercept task for a human player to be executed. + -- When enemy planes need to be intercepted by human players, use this task type to urgen the players to get out there! + -- + -- The TASK_A2A_INTERCEPT is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks + -- based on detected airborne enemy targets intruding friendly airspace. + -- + -- The task is defined for a @{Mission#MISSION}, where a friendly @{Set#SET_GROUP} consisting of GROUPs with one human players each, is intercepting the targets. + -- The task is given a name and a briefing, that is used in the menu structure and in the reporting. + -- + -- @field #TASK_A2A_INTERCEPT + TASK_A2A_INTERCEPT = { + ClassName = "TASK_A2A_INTERCEPT", + } + + + + --- Instantiates a new TASK_A2A_INTERCEPT. + -- @param #TASK_A2A_INTERCEPT self + -- @param Tasking.Mission#MISSION Mission + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Core.Set#SET_UNIT TargetSetUnit + -- @param #string TaskBriefing The briefing of the task. + -- @return #TASK_A2A_INTERCEPT + function TASK_A2A_INTERCEPT:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) + local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "INTERCEPT", TaskBriefing ) ) -- #TASK_A2A_INTERCEPT + self:F() + + Mission:AddTask( self ) + + --TODO: Add BR, Altitude, type of planes... + + self:SetBriefing( + TaskBriefing or + "Intercept incoming intruders.\n" + ) + + self:UpdateTaskInfo() + + return self + end + + function TASK_A2A_INTERCEPT:UpdateTaskInfo() + + local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate() + self:SetInfo( "Coordinates", TargetCoordinate, 0 ) + + self:SetInfo( "Threat", "[" .. string.rep( "â– ", self.Detection and self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex ) or self.TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 ) + + if self.Detection then + local DetectedItemsCount = self.TargetSetUnit:Count() + local ReportTypes = REPORT:New() + local TargetTypes = {} + for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do + local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit ) + if not TargetTypes[TargetType] then + TargetTypes[TargetType] = TargetType + ReportTypes:Add( TargetType ) + end + end + self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 ) + else + local DetectedItemsCount = self.TargetSetUnit:Count() + local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames() + self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 ) + end + + end + + + --- @param #TASK_A2A_INTERCEPT self + -- @param Wrapper.Group#GROUP ReportGroup + function TASK_A2A_INTERCEPT:ReportOrder( ReportGroup ) + self:F( { TaskInfo = self.TaskInfo } ) + local Coordinate = self.TaskInfo.Coordinates.TaskInfoText + local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) + + return Distance + end + + + --- @param #TASK_A2A_INTERCEPT self + function TASK_A2A_INTERCEPT:onafterGoal( TaskUnit, From, Event, To ) + local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT + + if TargetSetUnit:Count() == 0 then + self:Success() + end + + self:__Goal( -10 ) + end + + --- Set a score when a target in scope of the A2A attack, has been destroyed . + -- @param #TASK_A2A_INTERCEPT self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points to be granted when task process has been achieved. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2A_INTERCEPT + function TASK_A2A_INTERCEPT:SetScoreOnProgress( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has intercepted a target.", Score ) + + return self + end + + --- Set a score when all the targets in scope of the A2A attack, have been destroyed. + -- @param #TASK_A2A_INTERCEPT self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2A_INTERCEPT + function TASK_A2A_INTERCEPT:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Success", "All targets have been successfully intercepted!", Score ) + + return self + end + + --- Set a penalty when the A2A attack has failed. + -- @param #TASK_A2A_INTERCEPT self + -- @param #string PlayerName The name of the player. + -- @param #number Penalty The penalty in points, must be a negative value! + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2A_INTERCEPT + function TASK_A2A_INTERCEPT:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) + self:F( { PlayerName, Penalty, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Failed", "The intercept has failed!", Penalty ) + + return self + end + + +end + + +do -- TASK_A2A_SWEEP + + --- The TASK_A2A_SWEEP class + -- @type TASK_A2A_SWEEP + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + + --- # TASK_A2A_SWEEP class, extends @{Task_A2A#TASK_A2A} + -- + -- The TASK_A2A_SWEEP class defines a sweep task for a human player to be executed. + -- A sweep task needs to be given when targets were detected but somehow the detection was lost. + -- Most likely, these enemy planes are hidden in the mountains or are flying under radar. + -- These enemy planes need to be sweeped by human players, and use this task type to urge the players to get out there and find those enemy fighters. + -- + -- The TASK_A2A_SWEEP is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create sweep tasks + -- based on detected airborne enemy targets intruding friendly airspace, for which the detection has been lost for more than 60 seconds. + -- + -- The task is defined for a @{Mission#MISSION}, where a friendly @{Set#SET_GROUP} consisting of GROUPs with one human players each, is sweeping the targets. + -- The task is given a name and a briefing, that is used in the menu structure and in the reporting. + -- + -- @field #TASK_A2A_SWEEP + TASK_A2A_SWEEP = { + ClassName = "TASK_A2A_SWEEP", + } + + + + --- Instantiates a new TASK_A2A_SWEEP. + -- @param #TASK_A2A_SWEEP self + -- @param Tasking.Mission#MISSION Mission + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Core.Set#SET_UNIT TargetSetUnit + -- @param #string TaskBriefing The briefing of the task. + -- @return #TASK_A2A_SWEEP self + function TASK_A2A_SWEEP:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) + local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "SWEEP", TaskBriefing ) ) -- #TASK_A2A_SWEEP + self:F() + + Mission:AddTask( self ) + + --TODO: Add BR, Altitude, type of planes... + + self:SetBriefing( + TaskBriefing or + "Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n" + ) + + self:UpdateTaskInfo() + + return self + end + + + function TASK_A2A_SWEEP:UpdateTaskInfo() + + local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate() + self:SetInfo( "Coordinates", TargetCoordinate, 0 ) + + self:SetInfo( "Assumed Threat", "[" .. string.rep( "â– ", self.Detection and self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex ) or self.TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 ) + + if self.Detection then + local DetectedItemsCount = self.TargetSetUnit:Count() + local ReportTypes = REPORT:New() + local TargetTypes = {} + for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do + local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit ) + if not TargetTypes[TargetType] then + TargetTypes[TargetType] = TargetType + ReportTypes:Add( TargetType ) + end + end + self:SetInfo( "Lost Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 ) + else + local DetectedItemsCount = self.TargetSetUnit:Count() + local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames() + self:SetInfo( "Lost Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 ) + end + + end + + + function TASK_A2A_SWEEP:ReportOrder( ReportGroup ) + local Coordinate = self.TaskInfo.Coordinates.TaskInfoText + local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) + + return Distance + end + + --- @param #TASK_A2A_SWEEP self + function TASK_A2A_SWEEP:onafterGoal( TaskUnit, From, Event, To ) + local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT + + if TargetSetUnit:Count() == 0 then + self:Success() + end + + self:__Goal( -10 ) + end + + --- Set a score when a target in scope of the A2A attack, has been destroyed . + -- @param #TASK_A2A_SWEEP self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points to be granted when task process has been achieved. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2A_SWEEP + function TASK_A2A_SWEEP:SetScoreOnProgress( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has sweeped a target.", Score ) + + return self + end + + --- Set a score when all the targets in scope of the A2A attack, have been destroyed. + -- @param #TASK_A2A_SWEEP self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2A_SWEEP + function TASK_A2A_SWEEP:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Success", "All targets have been successfully sweeped!", Score ) + + return self + end + + --- Set a penalty when the A2A attack has failed. + -- @param #TASK_A2A_SWEEP self + -- @param #string PlayerName The name of the player. + -- @param #number Penalty The penalty in points, must be a negative value! + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2A_SWEEP + function TASK_A2A_SWEEP:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) + self:F( { PlayerName, Penalty, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Failed", "The sweep has failed!", Penalty ) + + return self + end + +end + + +do -- TASK_A2A_ENGAGE + + --- The TASK_A2A_ENGAGE class + -- @type TASK_A2A_ENGAGE + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + + --- # TASK_A2A_ENGAGE class, extends @{Task_A2A#TASK_A2A} + -- + -- The TASK_A2A_ENGAGE class defines an engage task for a human player to be executed. + -- When enemy planes are close to human players, use this task type is used urge the players to get out there! + -- + -- The TASK_A2A_ENGAGE is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create engage tasks + -- based on detected airborne enemy targets intruding friendly airspace. + -- + -- The task is defined for a @{Mission#MISSION}, where a friendly @{Set#SET_GROUP} consisting of GROUPs with one human players each, is engaging the targets. + -- The task is given a name and a briefing, that is used in the menu structure and in the reporting. + -- + -- @field #TASK_A2A_ENGAGE + TASK_A2A_ENGAGE = { + ClassName = "TASK_A2A_ENGAGE", + } + + + + --- Instantiates a new TASK_A2A_ENGAGE. + -- @param #TASK_A2A_ENGAGE self + -- @param Tasking.Mission#MISSION Mission + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Core.Set#SET_UNIT TargetSetUnit + -- @param #string TaskBriefing The briefing of the task. + -- @return #TASK_A2A_ENGAGE self + function TASK_A2A_ENGAGE:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) + local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "ENGAGE", TaskBriefing ) ) -- #TASK_A2A_ENGAGE + self:F() + + Mission:AddTask( self ) + + --TODO: Add BR, Altitude, type of planes... + + self:SetBriefing( + TaskBriefing or + "Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n" + ) + + self:UpdateTaskInfo() + + return self + end + + + function TASK_A2A_ENGAGE:UpdateTaskInfo() + + local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate() + self:SetInfo( "Coordinates", TargetCoordinate, 0 ) + + self:SetInfo( "Threat", "[" .. string.rep( "â– ", self.Detection and self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex ) or self.TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 ) + + if self.Detection then + local DetectedItemsCount = self.TargetSetUnit:Count() + local ReportTypes = REPORT:New() + local TargetTypes = {} + for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do + local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit ) + if not TargetTypes[TargetType] then + TargetTypes[TargetType] = TargetType + ReportTypes:Add( TargetType ) + end + end + self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 ) + else + local DetectedItemsCount = self.TargetSetUnit:Count() + local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames() + self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 ) + end + + end + + function TASK_A2A_ENGAGE:ReportOrder( ReportGroup ) + local Coordinate = self.TaskInfo.Coordinates.TaskInfoText + local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) + + return Distance + end + + --- @param #TASK_A2A_ENGAGE self + function TASK_A2A_ENGAGE:onafterGoal( TaskUnit, From, Event, To ) + local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT + + if TargetSetUnit:Count() == 0 then + self:Success() + end + + self:__Goal( -10 ) + end + + --- Set a score when a target in scope of the A2A attack, has been destroyed . + -- @param #TASK_A2A_ENGAGE self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points to be granted when task process has been achieved. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2A_ENGAGE + function TASK_A2A_ENGAGE:SetScoreOnProgress( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has engaged and destroyed a target.", Score ) + + return self + end + + --- Set a score when all the targets in scope of the A2A attack, have been destroyed. + -- @param #TASK_A2A_ENGAGE self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2A_ENGAGE + function TASK_A2A_ENGAGE:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Success", "All targets have been successfully engaged!", Score ) + + return self + end + + --- Set a penalty when the A2A attack has failed. + -- @param #TASK_A2A_ENGAGE self + -- @param #string PlayerName The name of the player. + -- @param #number Penalty The penalty in points, must be a negative value! + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2A_ENGAGE + function TASK_A2A_ENGAGE:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) + self:F( { PlayerName, Penalty, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Failed", "The target engagement has failed!", Penalty ) + + return self + end + +end + diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua new file mode 100644 index 000000000..0167c71f9 --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -0,0 +1,598 @@ +--- **Tasking** - The TASK_A2A_DISPATCHER creates and manages player TASK_A2A tasks based on detected targets. +-- +-- The @{#TASK_A2A_DISPATCHER} classes implement the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- +-- @module Task_A2A_Dispatcher + +do -- TASK_A2A_DISPATCHER + + --- TASK_A2A_DISPATCHER class. + -- @type TASK_A2A_DISPATCHER + -- @extends Tasking.DetectionManager#DETECTION_MANAGER + + --- # TASK_A2A_DISPATCHER class, extends @{Tasking#DETECTION_MANAGER} + -- + -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia1.JPG) + -- + -- The @{#TASK_A2A_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups. + -- + -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia3.JPG) + -- + -- The EWR will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. + -- Find a summary below describing for which situation a task type is created: + -- + -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia9.JPG) + -- + -- * **INTERCEPT Task**: Is created when the target is known, is detected and within a danger zone, and there is no friendly airborne in range. + -- * **SWEEP Task**: Is created when the target is unknown, was detected and the last position is only known, and within a danger zone, and there is no friendly airborne in range. + -- * **ENGAGE Task**: Is created when the target is known, is detected and within a danger zone, and there is a friendly airborne in range, that will receive this task. + -- + -- ## 1. TASK\_A2A\_DISPATCHER constructor: + -- + -- The @{#TASK_A2A_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance. + -- + -- ### 1.1. Define or set the **Mission**: + -- + -- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter. + -- + -- local HQ = GROUP:FindByName( "HQ", "Bravo" ) + -- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + -- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED ) + -- + -- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission. + -- Create the MISSION object, and hook it under the command center. + -- + -- ### 1.2. Build a set of the groups seated by human players: + -- + -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia6.JPG) + -- + -- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into. + -- + -- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart() + -- + -- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission. + -- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available. + -- + -- ### 1.3. Define the **EWR network**: + -- + -- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third parameter. + -- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy. + -- + -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia5.JPG) + -- + -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. + -- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). + -- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. + -- The position of these units is very important as they need to provide enough coverage + -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. + -- + -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia7.JPG) + -- + -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. + -- For example if they are a long way forward and can detect enemy planes on the ground and taking off + -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. + -- Having the radars further back will mean a slower escalation because fewer targets will be detected and + -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. + -- It all depends on what the desired effect is. + -- + -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class. + -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, + -- increasing or decreasing the radar coverage of the Early Warning System. + -- + -- See the following example to setup an EWR network containing EWR stations and AWACS. + -- + -- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart() + -- + -- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 ) + -- EWRDetection:SetFriendliesRange( 10000 ) + -- EWRDetection:SetRefreshTimeInterval(30) + -- + -- -- Setup the A2A dispatcher, and initialize it. + -- A2ADispatcher = TASK_A2A_DISPATCHER:New( Mission, AttackGroups, EWRDetection ) + -- + -- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**. + -- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set. + -- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set. + -- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is choosen, which is 6km. + -- The **EWRDetection** object is then passed to the @{#TASK_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism. + -- + -- ### 2. Define the detected **target grouping radius**: + -- + -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia8.JPG) + -- + -- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed. + -- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. + -- Fast planes like in the 80s, need a larger radius than WWII planes. + -- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft. + -- + -- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate + -- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small! + -- + -- ## 3. Set the **Engage radius**: + -- + -- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission. + -- + -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia11.JPG) + -- + -- So, if there is a target area detected and reported, + -- then any friendlies that are airborne near this target area, + -- will be commanded to (re-)engage that target when available (if no other tasks were commanded). + -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, + -- will be considered to receive the command to engage that target area. + -- You need to evaluate the value of this parameter carefully. + -- If too small, more intercept missions may be triggered upon detected target areas. + -- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. + -- + -- ## 4. Set **Scoring** and **Messages**: + -- + -- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Task} dispatched by the TASK\_A2A\_DISPATCHER. + -- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_, + -- when the player reaches certain achievements in the task. + -- + -- The prototype to handle the **Assign** event needs to be developed as follows: + -- + -- TaskDispatcher = TASK_A2A_DISPATCHER:New( ... ) + -- + -- --- @param #TaskDispatcher self + -- -- @param #string From Contains the name of the state from where the Event was triggered. + -- -- @param #string Event Contains the name of the event that was triggered. In this case Assign. + -- -- @param #string To Contains the name of the state that will be transitioned to. + -- -- @param Tasking.Task_A2A#TASK_A2A Task The Task object, which is any derived object from TASK_A2A. + -- -- @param Wrapper.Unit#UNIT TaskUnit The Unit or Client that contains the Player. + -- -- @param #string PlayerName The name of the Player that joined the TaskUnit. + -- function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) + -- Task:SetScoreOnProgress( PlayerName, 20, TaskUnit ) + -- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit ) + -- Task:SetScoreOnFail( PlayerName, -100, TaskUnit ) + -- end + -- + -- The **OnAfterAssign** method (function) is added to the TaskDispatcher object. + -- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher. + -- So, this method will be called only **ONCE** when a player joins a unit in scope of the task. + -- + -- The TASK class implements various methods to additional **set scoring** for player achievements: + -- + -- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task. + -- Examples of **task progress** can be destroying units, arriving at zones etc. + -- + -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state. + -- This means the **task has been successfully completed**. + -- + -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state. + -- This means the **task has not been successfully completed**, and the scores must be given with a negative value! + -- + -- @field #TASK_A2A_DISPATCHER + TASK_A2A_DISPATCHER = { + ClassName = "TASK_A2A_DISPATCHER", + Mission = nil, + Detection = nil, + Tasks = {}, + SweepZones = {}, + } + + + --- TASK_A2A_DISPATCHER constructor. + -- @param #TASK_A2A_DISPATCHER self + -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. + -- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. + -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. + -- @return #TASK_A2A_DISPATCHER self + function TASK_A2A_DISPATCHER:New( Mission, SetGroup, Detection ) + + -- Inherits from DETECTION_MANAGER + local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2A_DISPATCHER + + self.Detection = Detection + self.Mission = Mission + + + -- TODO: Check detection through radar. + self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER ) + self.Detection:InitDetectRadar( true ) + self.Detection:SetRefreshTimeInterval( 30 ) + + self:AddTransition( "Started", "Assign", "Started" ) + + --- OnAfter Transition Handler for Event Assign. + -- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign + -- @param #TASK_A2A_DISPATCHER self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Tasking.Task_A2A#TASK_A2A Task + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #string PlayerName + + self:__Start( 5 ) + + return self + end + + + --- Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission. + -- So, if there is a target area detected and reported, + -- then any friendlies that are airborne near this target area, + -- will be commanded to (re-)engage that target when available (if no other tasks were commanded). + -- An ENGAGE task will be created for those pilots. + -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, + -- will be considered to receive the command to engage that target area. + -- You need to evaluate the value of this parameter carefully. + -- If too small, more intercept missions may be triggered upon detected target areas. + -- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. + -- @param #TASK_A2A_DISPATCHER self + -- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target. + -- @return #TASK_A2A_DISPATCHER + -- @usage + -- + -- -- Set 50km as the radius to engage any target by airborne friendlies. + -- TaskA2ADispatcher:SetEngageRadius( 50000 ) + -- + -- -- Set 100km as the radius to engage any target by airborne friendlies. + -- TaskA2ADispatcher:SetEngageRadius() -- 100000 is the default value. + -- + function TASK_A2A_DISPATCHER:SetEngageRadius( EngageRadius ) + + self.Detection:SetFriendliesRange( EngageRadius or 100000 ) + + return self + end + + + + --- Creates an INTERCEPT task when there are targets for it. + -- @param #TASK_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem + -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. + function TASK_A2A_DISPATCHER:EvaluateINTERCEPT( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + -- Check if there is at least one UNIT in the DetectedSet is visible. + + if DetectedItem.IsDetected == true then + + -- Here we're doing something advanced... We're copying the DetectedSet. + local TargetSetUnit = SET_UNIT:New() + TargetSetUnit:SetDatabase( DetectedSet ) + TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. + + return TargetSetUnit + end + + return nil + end + + + --- Creates an SWEEP task when there are targets for it. + -- @param #TASK_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem + -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. + function TASK_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + + if DetectedItem.IsDetected == false then + + -- Here we're doing something advanced... We're copying the DetectedSet. + local TargetSetUnit = SET_UNIT:New() + TargetSetUnit:SetDatabase( DetectedSet ) + TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. + + return TargetSetUnit + end + + return nil + end + + + --- Creates an ENGAGE task when there are human friendlies airborne near the targets. + -- @param #TASK_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem + -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. + function TASK_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem ) + + + -- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone. + if PlayersCount > 0 and DetectedItem.IsDetected == true then + + -- Here we're doing something advanced... We're copying the DetectedSet. + local TargetSetUnit = SET_UNIT:New() + TargetSetUnit:SetDatabase( DetectedSet ) + TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. + + return TargetSetUnit + end + + return nil + end + + + + + --- Evaluates the removal of the Task from the Mission. + -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". + -- @param #TASK_A2A_DISPATCHER self + -- @param Tasking.Mission#MISSION Mission + -- @param Tasking.Task#TASK Task + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. + -- @param #boolean DetectedItemID + -- @param #boolean DetectedItemChange + -- @return Tasking.Task#TASK + function TASK_A2A_DISPATCHER:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, DetectedItemIndex, DetectedItemChanged ) + + if Task then + + if Task:IsStatePlanned() then + local TaskName = Task:GetName() + local TaskType = TaskName:match( "(%u+)%.%d+" ) + + self:T2( { TaskType = TaskType } ) + + local Remove = false + + local IsPlayers = Detection:IsPlayersNearBy( DetectedItem ) + if TaskType == "ENGAGE" then + if IsPlayers == false then + Remove = true + end + end + + if TaskType == "INTERCEPT" then + if IsPlayers == true then + Remove = true + end + if DetectedItem.IsDetected == false then + Remove = true + end + end + + if TaskType == "SWEEP" then + if DetectedItem.IsDetected == true then + Remove = true + end + end + + local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT + --DetectedSet:Flush() + --self:E( { DetectedSetCount = DetectedSet:Count() } ) + if DetectedSet:Count() == 0 then + Remove = true + end + + if DetectedItemChanged == true or Remove then + Task = self:RemoveTask( DetectedItemIndex ) + end + end + end + + return Task + end + + --- Calculates which friendlies are nearby the area + -- @param #TASK_A2A_DISPATCHER self + -- @param DetectedItem + -- @return #number, Core.CommandCenter#REPORT + function TASK_A2A_DISPATCHER:GetFriendliesNearBy( DetectedItem ) + + local DetectedSet = DetectedItem.Set + local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem ) + + local FriendlyTypes = {} + local FriendliesCount = 0 + + if FriendlyUnitsNearBy then + local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G() + for FriendlyUnitName, FriendlyUnitData in pairs( FriendlyUnitsNearBy ) do + local FriendlyUnit = FriendlyUnitData -- Wrapper.Unit#UNIT + if FriendlyUnit:IsAirPlane() then + local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel() + FriendliesCount = FriendliesCount + 1 + local FriendlyType = FriendlyUnit:GetTypeName() + FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and ( FriendlyTypes[FriendlyType] + 1 ) or 1 + if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then + end + end + end + + end + + --self:E( { FriendliesCount = FriendliesCount } ) + + local FriendlyTypesReport = REPORT:New() + + if FriendliesCount > 0 then + for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do + FriendlyTypesReport:Add( string.format("%d of %s", FriendlyTypeCount, FriendlyType ) ) + end + else + FriendlyTypesReport:Add( "-" ) + end + + + return FriendliesCount, FriendlyTypesReport + end + + --- Calculates which HUMAN friendlies are nearby the area + -- @param #TASK_A2A_DISPATCHER self + -- @param DetectedItem + -- @return #number, Core.CommandCenter#REPORT + function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem ) + + local DetectedSet = DetectedItem.Set + local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem ) + + local PlayerTypes = {} + local PlayersCount = 0 + + if PlayersNearBy then + local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G() + for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do + local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT + local PlayerName = PlayerUnit:GetPlayerName() + --self:E( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } ) + if PlayerUnit:IsAirPlane() and PlayerName ~= nil then + local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel() + PlayersCount = PlayersCount + 1 + local PlayerType = PlayerUnit:GetTypeName() + PlayerTypes[PlayerName] = PlayerType + if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then + end + end + end + + end + + --self:E( { PlayersCount = PlayersCount } ) + + local PlayerTypesReport = REPORT:New() + + if PlayersCount > 0 then + for PlayerName, PlayerType in pairs( PlayerTypes ) do + PlayerTypesReport:Add( string.format('"%s" in %s', PlayerName, PlayerType ) ) + end + else + PlayerTypesReport:Add( "-" ) + end + + + return PlayersCount, PlayerTypesReport + end + + function TASK_A2A_DISPATCHER:RemoveTask( TaskIndex ) + self.Mission:RemoveTask( self.Tasks[TaskIndex] ) + self.Tasks[TaskIndex] = nil + end + + + --- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}. + -- @param #TASK_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. + -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. + function TASK_A2A_DISPATCHER:ProcessDetected( Detection ) + self:E() + + local AreaMsg = {} + local TaskMsg = {} + local ChangeMsg = {} + + local Mission = self.Mission + + if Mission:IsIDLE() or Mission:IsENGAGED() then + + local TaskReport = REPORT:New() + + -- Checking the task queue for the dispatcher, and removing any obsolete task! + for TaskIndex, TaskData in pairs( self.Tasks ) do + local Task = TaskData -- Tasking.Task#TASK + if Task:IsStatePlanned() then + local DetectedItem = Detection:GetDetectedItem( TaskIndex ) + if not DetectedItem then + local TaskText = Task:GetName() + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2A task %s for %s removed.", TaskText, Mission:GetName() ), TaskGroup ) + end + Task = self:RemoveTask( TaskIndex ) + end + end + end + + -- Now that all obsolete tasks are removed, loop through the detected targets. + for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do + + local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedCount = DetectedSet:Count() + local DetectedZone = DetectedItem.Zone + --self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) + --DetectedSet:Flush() + + local DetectedID = DetectedItem.ID + local TaskIndex = DetectedItem.Index + local DetectedItemChanged = DetectedItem.Changed + + local Task = self.Tasks[TaskIndex] + Task = self:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, TaskIndex, DetectedItemChanged ) -- Task will be removed if it is planned and changed. + + -- Evaluate INTERCEPT + if not Task and DetectedCount > 0 then + local TargetSetUnit = self:EvaluateENGAGE( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed... + if TargetSetUnit then + Task = TASK_A2A_ENGAGE:New( Mission, self.SetGroup, string.format( "ENGAGE.%03d", DetectedID ), TargetSetUnit ) + Task:SetDetection( Detection, TaskIndex ) + else + local TargetSetUnit = self:EvaluateINTERCEPT( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed... + if TargetSetUnit then + Task = TASK_A2A_INTERCEPT:New( Mission, self.SetGroup, string.format( "INTERCEPT.%03d", DetectedID ), TargetSetUnit ) + Task:SetDetection( Detection, TaskIndex ) + else + local TargetSetUnit = self:EvaluateSWEEP( DetectedItem ) -- Returns a SetUnit + if TargetSetUnit then + Task = TASK_A2A_SWEEP:New( Mission, self.SetGroup, string.format( "SWEEP.%03d", DetectedID ), TargetSetUnit ) + Task:SetDetection( Detection, TaskIndex ) + end + end + end + + if Task then + self.Tasks[TaskIndex] = Task + Task:SetTargetZone( DetectedZone, DetectedItem.Coordinate.y, DetectedItem.Coordinate.Heading ) + Task:SetDispatcher( self ) + Mission:AddTask( Task ) + + TaskReport:Add( Task:GetName() ) + else + self:E("This should not happen") + end + + end + + if Task then + local FriendliesCount, FriendliesReport = self:GetFriendliesNearBy( DetectedItem ) + Task:SetInfo( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ), 30 ) + local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem ) + Task:SetInfo( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ), 31 ) + end + + -- OK, so the tasking has been done, now delete the changes reported for the area. + Detection:AcceptChanges( DetectedItem ) + end + + -- TODO set menus using the HQ coordinator + Mission:GetCommandCenter():SetMenu() + + local TaskText = TaskReport:Text(", ") + + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then + Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetName(), TaskText ), TaskGroup ) + end + end + + end + + return true + end + +end diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 662ab0d2b..9760326c4 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -2,67 +2,13 @@ -- -- ![Banner Image](..\Presentations\TASK_A2G\Dia1.JPG) -- +-- ==== -- --- # 1) @{Task_A2G#TASK_A2G} class, extends @{Task#TASK} +-- ### Author: **Sven Van de Velde (FlightControl)** -- --- The @{#TASK_A2G} class defines Air To Ground tasks for a @{Set} of Target Units, --- based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_A2G is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: --- --- * **None**: Start of the process --- * **Planned**: The A2G task is planned. --- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}. --- * **Success**: The A2G task is successfully completed. --- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. --- --- # 1.1) Set the scoring of achievements in an A2G attack. --- --- Scoring or penalties can be given in the following circumstances: --- --- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed. --- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed. --- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed. --- --- # 2) @{Task_A2G#TASK_SEAD} class, extends @{Task_A2G#TASK_A2G} --- --- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units. --- --- === --- --- # 3) @{Task_A2G#TASK_CAS} class, extends @{Task_A2G#TASK_A2G} --- --- The @{#TASK_CAS} class defines a CAS task for a @{Set} of Target Units. --- --- === --- --- # 4) @{Task_A2G#TASK_BAI} class, extends @{Task_A2G#TASK_A2G} --- --- The @{#TASK_BAI} class defines a BAI task for a @{Set} of Target Units. +-- ### Contributions: -- -- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-09: Revised version. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[WingThor]**: Concept, Advice & Testing. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. -- -- @module Task_A2G @@ -72,6 +18,28 @@ do -- TASK_A2G -- @type TASK_A2G -- @field Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK + + --- # TASK_A2G class, extends @{Task#TASK} + -- + -- The TASK_A2G class defines Air To Ground tasks for a @{Set} of Target Units, + -- based on the tasking capabilities defined in @{Task#TASK}. + -- The TASK_A2G is implemented using a @{Fsm#FSM_TASK}, and has the following statuses: + -- + -- * **None**: Start of the process + -- * **Planned**: The A2G task is planned. + -- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}. + -- * **Success**: The A2G task is successfully completed. + -- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. + -- + -- ## Set the scoring of achievements in an A2G attack. + -- + -- Scoring or penalties can be given in the following circumstances: + -- + -- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed. + -- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed. + -- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed. + -- + -- @field #TASK_A2G TASK_A2G = { ClassName = "TASK_A2G", } @@ -86,14 +54,12 @@ do -- TASK_A2G -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. -- @return #TASK_A2G self - function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- Tasking.Task#TASK_A2G + function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType, TaskBriefing ) + local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2G self:F() self.TargetSetUnit = TargetSetUnit self.TaskType = TaskType - - Mission:AddTask( self ) local Fsm = self:GetUnitProcess() @@ -109,14 +75,14 @@ do -- TASK_A2G Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" ) Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" ) - Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, self.TaskType ), { Accounted = "Success" } ) + Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} ) Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" ) Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} ) Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} ) Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" ) - Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) - Fsm:AddTransition( "Accounted", "Success", "Success" ) + --Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) + --Fsm:AddTransition( "Accounted", "Success", "Success" ) Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Failed", "Fail", "Failed" ) @@ -132,7 +98,7 @@ do -- TASK_A2G if Task:GetRendezVousZone( TaskUnit ) then self:__RouteToRendezVousZone( 0.1 ) else - if Task:GetRendezVousPointVec2( TaskUnit ) then + if Task:GetRendezVousCoordinate( TaskUnit ) then self:__RouteToRendezVousPoint( 0.1 ) else self:__ArriveAtRendezVous( 0.1 ) @@ -175,9 +141,9 @@ do -- TASK_A2G else local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT if TargetUnit then - local PointVec2 = TargetUnit:GetPointVec2() - self:T( { TargetPointVec2 = PointVec2, PointVec2:GetX(), PointVec2:GetAlt(), PointVec2:GetZ() } ) - Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit ) + local Coordinate = TargetUnit:GetCoordinate() + self:T( { TargetCoordinate = Coordinate, Coordinate:GetX(), Coordinate:GetY(), Coordinate:GetZ() } ) + Task:SetTargetCoordinate( TargetUnit:GetCoordinate(), TaskUnit ) end self:__RouteToTargetPoint( 0.1 ) end @@ -191,7 +157,7 @@ do -- TASK_A2G self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT if TargetUnit then - Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit ) + Task:SetTargetCoordinate( TargetUnit:GetCoordinate(), TaskUnit ) end self:__RouteToTargets( -10 ) end @@ -199,6 +165,15 @@ do -- TASK_A2G return self end + + --- @param #TASK_A2G self + -- @param Core.Set#SET_UNIT TargetSetUnit The set of targets. + function TASK_A2G:SetTargetSetUnit( TargetSetUnit ) + + self.TargetSetUnit = TargetSetUnit + end + + --- @param #TASK_A2G self function TASK_A2G:GetPlannedMenuText() @@ -206,28 +181,28 @@ do -- TASK_A2G end --- @param #TASK_A2G self - -- @param Core.Point#POINT_VEC2 RendezVousPointVec2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map. + -- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_A2G:SetRendezVousPointVec2( RendezVousPointVec2, RendezVousRange, TaskUnit ) + function TASK_A2G:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - ActRouteRendezVous:SetPointVec2( RendezVousPointVec2 ) + ActRouteRendezVous:SetCoordinate( RendezVousCoordinate ) ActRouteRendezVous:SetRange( RendezVousRange ) end --- @param #TASK_A2G self -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Point#POINT_VEC2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map. + -- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. - function TASK_A2G:GetRendezVousPointVec2( TaskUnit ) + function TASK_A2G:GetRendezVousCoordinate( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - return ActRouteRendezVous:GetPointVec2(), ActRouteRendezVous:GetRange() + return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange() end @@ -255,26 +230,26 @@ do -- TASK_A2G end --- @param #TASK_A2G self - -- @param Core.Point#POINT_VEC2 TargetPointVec2 The PointVec2 object where the Target is located on the map. + -- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_A2G:SetTargetPointVec2( TargetPointVec2, TaskUnit ) + function TASK_A2G:SetTargetCoordinate( TargetCoordinate, TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - ActRouteTarget:SetPointVec2( TargetPointVec2 ) + ActRouteTarget:SetCoordinate( TargetCoordinate ) end --- @param #TASK_A2G self -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Point#POINT_VEC2 The PointVec2 object where the Target is located on the map. - function TASK_A2G:GetTargetPointVec2( TaskUnit ) + -- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map. + function TASK_A2G:GetTargetCoordinate( TaskUnit ) local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - return ActRouteTarget:GetPointVec2() + return ActRouteTarget:GetCoordinate() end @@ -301,141 +276,444 @@ do -- TASK_A2G return ActRouteTarget:GetZone() end + function TASK_A2G:SetGoalTotal() + + self.GoalTotal = self.TargetSetUnit:Count() + end + + function TASK_A2G:GetGoalTotal() + + return self.GoalTotal + end + +end + + +do -- TASK_A2G_SEAD + + --- The TASK_A2G_SEAD class + -- @type TASK_A2G_SEAD + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + + --- # TASK_A2G_SEAD class, extends @{Task_A2G#TASK_A2G} + -- + -- The TASK_A2G_SEAD class defines an Suppression or Extermination of Air Defenses task for a human player to be executed. + -- These tasks are important to be executed as they will help to achieve air superiority at the vicinity. + -- + -- The TASK_A2G_SEAD is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks + -- based on detected enemy ground targets. + -- + -- @field #TASK_A2G_SEAD + TASK_A2G_SEAD = { + ClassName = "TASK_A2G_SEAD", + } + + --- Instantiates a new TASK_A2G_SEAD. + -- @param #TASK_A2G_SEAD self + -- @param Tasking.Mission#MISSION Mission + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Core.Set#SET_UNIT TargetSetUnit + -- @param #string TaskBriefing The briefing of the task. + -- @return #TASK_A2G_SEAD self + function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_A2G_SEAD + self:F() + + Mission:AddTask( self ) + + self:SetBriefing( + TaskBriefing or + "Execute a Suppression of Enemy Air Defenses.\n" + ) + + self:UpdateTaskInfo() + + return self + end + + function TASK_A2G_SEAD:UpdateTaskInfo() + + + local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate() + self:SetInfo( "Coordinates", TargetCoordinate, 0 ) + + self:SetInfo( "Threat", "[" .. string.rep( "â– ", self.Detection and self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex ) or self.TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 ) + + if self.Detection then + local DetectedItemsCount = self.TargetSetUnit:Count() + local ReportTypes = REPORT:New() + local TargetTypes = {} + for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do + local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit ) + if not TargetTypes[TargetType] then + TargetTypes[TargetType] = TargetType + ReportTypes:Add( TargetType ) + end + end + self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 ) + else + local DetectedItemsCount = self.TargetSetUnit:Count() + local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames() + self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 ) + end + + end + + function TASK_A2G_SEAD:ReportOrder( ReportGroup ) + local Coordinate = self.TaskInfo.Coordinates.TaskInfoText + local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) + + return Distance + end + + + --- @param #TASK_A2G_SEAD self + function TASK_A2G_SEAD:onafterGoal( TaskUnit, From, Event, To ) + local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT + + if TargetSetUnit:Count() == 0 then + self:Success() + end + + self:__Goal( -10 ) + end + --- Set a score when a target in scope of the A2G attack, has been destroyed . - -- @param #TASK_A2G self - -- @param #string Text The text to display to the player, when the target has been destroyed. - -- @param #number Score The score in points. + -- @param #TASK_A2G_SEAD self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points to be granted when task process has been achieved. -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G - function TASK_A2G:SetScoreOnDestroy( Text, Score, TaskUnit ) - self:F( { Text, Score, TaskUnit } ) + -- @return #TASK_A2G_SEAD + function TASK_A2G_SEAD:SetScoreOnProgress( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) local ProcessUnit = self:GetUnitProcess( TaskUnit ) - ProcessUnit:AddScoreProcess( "Engaging", "Account", "Account", Text, Score ) + ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has SEADed a target.", Score ) return self end --- Set a score when all the targets in scope of the A2G attack, have been destroyed. - -- @param #TASK_A2G self - -- @param #string Text The text to display to the player, when all targets hav been destroyed. + -- @param #TASK_A2G_SEAD self + -- @param #string PlayerName The name of the player. -- @param #number Score The score in points. -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G - function TASK_A2G:SetScoreOnSuccess( Text, Score, TaskUnit ) - self:F( { Text, Score, TaskUnit } ) + -- @return #TASK_A2G_SEAD + function TASK_A2G_SEAD:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) local ProcessUnit = self:GetUnitProcess( TaskUnit ) - ProcessUnit:AddScore( "Success", Text, Score ) + ProcessUnit:AddScore( "Success", "All radar emitting targets have been successfully SEADed!", Score ) return self end --- Set a penalty when the A2G attack has failed. - -- @param #TASK_A2G self - -- @param #string Text The text to display to the player, when the A2G attack has failed. - -- @param #number Penalty The penalty in points. + -- @param #TASK_A2G_SEAD self + -- @param #string PlayerName The name of the player. + -- @param #number Penalty The penalty in points, must be a negative value! -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G - function TASK_A2G:SetPenaltyOnFailed( Text, Penalty, TaskUnit ) - self:F( { Text, Score, TaskUnit } ) + -- @return #TASK_A2G_SEAD + function TASK_A2G_SEAD:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) + self:F( { PlayerName, Penalty, TaskUnit } ) local ProcessUnit = self:GetUnitProcess( TaskUnit ) - ProcessUnit:AddScore( "Failed", Text, Penalty ) + ProcessUnit:AddScore( "Failed", "The SEADing has failed!", Penalty ) return self end - -end - - -do -- TASK_SEAD - - --- The TASK_SEAD class - -- @type TASK_SEAD - -- @field Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - TASK_SEAD = { - ClassName = "TASK_SEAD", - } - - --- Instantiates a new TASK_SEAD. - -- @param #TASK_SEAD self - -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. - -- @return #TASK_SEAD self - function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit ) - local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD" ) ) -- #TASK_SEAD - self:F() - - return self - end end -do -- TASK_BAI +do -- TASK_A2G_BAI - --- The TASK_BAI class - -- @type TASK_BAI + --- The TASK_A2G_BAI class + -- @type TASK_A2G_BAI -- @field Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - TASK_BAI = { - ClassName = "TASK_BAI", + + --- # TASK_A2G_BAI class, extends @{Task_A2G#TASK_A2G} + -- + -- The TASK_A2G_BAI class defines an Battlefield Air Interdiction task for a human player to be executed. + -- These tasks are more strategic in nature and are most of the time further away from friendly forces. + -- BAI tasks can also be used to express the abscence of friendly forces near the vicinity. + -- + -- The TASK_A2G_BAI is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create BAI tasks + -- based on detected enemy ground targets. + -- + -- @field #TASK_A2G_BAI + TASK_A2G_BAI = { + ClassName = "TASK_A2G_BAI", } - --- Instantiates a new TASK_BAI. - -- @param #TASK_BAI self + --- Instantiates a new TASK_A2G_BAI. + -- @param #TASK_A2G_BAI self -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. - -- @return #TASK_BAI self - function TASK_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit ) - local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI" ) ) -- #TASK_BAI + -- @param Core.Set#SET_UNIT TargetSetUnit + -- @param #string TaskBriefing The briefing of the task. + -- @return #TASK_A2G_BAI self + function TASK_A2G_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI", TaskBriefing ) ) -- #TASK_A2G_BAI self:F() + Mission:AddTask( self ) + + self:SetBriefing( + TaskBriefing or + "Execute a Battlefield Air Interdiction of a group of enemy targets.\n" + ) + + self:UpdateTaskInfo() + return self - end + end + + function TASK_A2G_BAI:UpdateTaskInfo() + + self:E({self.Detection, self.DetectedItemIndex}) + + local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate() + self:SetInfo( "Coordinates", TargetCoordinate, 0 ) + + self:SetInfo( "Threat", "[" .. string.rep( "â– ", self.Detection and self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex ) or self.TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 ) + + if self.Detection then + local DetectedItemsCount = self.TargetSetUnit:Count() + local ReportTypes = REPORT:New() + local TargetTypes = {} + for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do + local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit ) + if not TargetTypes[TargetType] then + TargetTypes[TargetType] = TargetType + ReportTypes:Add( TargetType ) + end + end + self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 ) + else + local DetectedItemsCount = self.TargetSetUnit:Count() + local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames() + self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 ) + end + + end + + + function TASK_A2G_BAI:ReportOrder( ReportGroup ) + local Coordinate = self.TaskInfo.Coordinates.TaskInfoText + local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) + + return Distance + end + + + --- @param #TASK_A2G_BAI self + function TASK_A2G_BAI:onafterGoal( TaskUnit, From, Event, To ) + local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT + + if TargetSetUnit:Count() == 0 then + self:Success() + end + + self:__Goal( -10 ) + end + + --- Set a score when a target in scope of the A2G attack, has been destroyed . + -- @param #TASK_A2G_BAI self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points to be granted when task process has been achieved. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G_BAI + function TASK_A2G_BAI:SetScoreOnProgress( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Battlefield Air Interdiction (BAI).", Score ) + + return self + end + + --- Set a score when all the targets in scope of the A2G attack, have been destroyed. + -- @param #TASK_A2G_BAI self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G_BAI + function TASK_A2G_BAI:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Battlefield Air Interdiction (BAI) is a success!", Score ) + + return self + end + + --- Set a penalty when the A2G attack has failed. + -- @param #TASK_A2G_BAI self + -- @param #string PlayerName The name of the player. + -- @param #number Penalty The penalty in points, must be a negative value! + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G_BAI + function TASK_A2G_BAI:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) + self:F( { PlayerName, Penalty, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Failed", "The Battlefield Air Interdiction (BAI) has failed!", Penalty ) + + return self + end + end -do -- TASK_CAS +do -- TASK_A2G_CAS - --- The TASK_CAS class - -- @type TASK_CAS + --- The TASK_A2G_CAS class + -- @type TASK_A2G_CAS -- @field Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - TASK_CAS = { - ClassName = "TASK_CAS", + + --- # TASK_A2G_CAS class, extends @{Task_A2G#TASK_A2G} + -- + -- The TASK_A2G_CAS class defines an Close Air Support task for a human player to be executed. + -- Friendly forces will be in the vicinity within 6km from the enemy. + -- + -- The TASK_A2G_CAS is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create CAS tasks + -- based on detected enemy ground targets. + -- + -- @field #TASK_A2G_CAS + TASK_A2G_CAS = { + ClassName = "TASK_A2G_CAS", } - --- Instantiates a new TASK_CAS. - -- @param #TASK_CAS self + --- Instantiates a new TASK_A2G_CAS. + -- @param #TASK_A2G_CAS self -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. - -- @return #TASK_CAS self - function TASK_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit ) - local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS" ) ) -- #TASK_CAS + -- @param Core.Set#SET_UNIT TargetSetUnit + -- @param #string TaskBriefing The briefing of the task. + -- @return #TASK_A2G_CAS self + function TASK_A2G_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) + local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS", TaskBriefing ) ) -- #TASK_A2G_CAS self:F() + Mission:AddTask( self ) + + self:SetBriefing( + TaskBriefing or + "Execute a Close Air Support for a group of enemy targets.\n" .. + "Beware of friendlies at the vicinity!\n" + ) + + self:UpdateTaskInfo() + return self end + + function TASK_A2G_CAS:UpdateTaskInfo() + + local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate() + self:SetInfo( "Coordinates", TargetCoordinate, 0 ) + + self:SetInfo( "Threat", "[" .. string.rep( "â– ", self.Detection and self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex ) or self.TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 ) + + if self.Detection then + local DetectedItemsCount = self.TargetSetUnit:Count() + local ReportTypes = REPORT:New() + local TargetTypes = {} + for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do + local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit ) + if not TargetTypes[TargetType] then + TargetTypes[TargetType] = TargetType + ReportTypes:Add( TargetType ) + end + end + self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 ) + else + local DetectedItemsCount = self.TargetSetUnit:Count() + local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames() + self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 ) + end + + end + + function TASK_A2G_CAS:ReportOrder( ReportGroup ) + local Coordinate = self.TaskInfo.Coordinates.TaskInfoText + local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) + + return Distance + end + + + --- @param #TASK_A2G_CAS self + function TASK_A2G_CAS:onafterGoal( TaskUnit, From, Event, To ) + local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT + + if TargetSetUnit:Count() == 0 then + self:Success() + end + + self:__Goal( -10 ) + end + + --- Set a score when a target in scope of the A2G attack, has been destroyed . + -- @param #TASK_A2G_CAS self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points to be granted when task process has been achieved. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G_CAS + function TASK_A2G_CAS:SetScoreOnProgress( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Close Air Support (CAS).", Score ) + + return self + end + + --- Set a score when all the targets in scope of the A2G attack, have been destroyed. + -- @param #TASK_A2G_CAS self + -- @param #string PlayerName The name of the player. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G_CAS + function TASK_A2G_CAS:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) + self:F( { PlayerName, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Close Air Support (CAS) was a success!", Score ) + + return self + end + + --- Set a penalty when the A2G attack has failed. + -- @param #TASK_A2G_CAS self + -- @param #string PlayerName The name of the player. + -- @param #number Penalty The penalty in points, must be a negative value! + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2G_CAS + function TASK_A2G_CAS:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) + self:F( { PlayerName, Penalty, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Failed", "The Close Air Support (CAS) has failed!", Penalty ) + + return self + end + end diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua index a30c335ba..8857bb4ce 100644 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -1,45 +1,12 @@ --- **Tasking** - The TASK_A2G_DISPATCHER creates and manages player TASK_A2G tasks based on detected targets. -- --- === +-- ==== -- --- # 1) @{#TASK_A2G_DISPATCHER} class, extends @{#DETECTION_MANAGER} +-- ### Author: **Sven Van de Velde (FlightControl)** -- --- The @{#TASK_A2G_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups). --- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. --- Find a summary below describing for which situation a task type is created: +-- ### Contributions: -- --- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter. --- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter. --- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars. --- --- Other task types will follow... --- --- 3.1) TASK_A2G_DISPATCHER constructor: --- -------------------------------------- --- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK_A2G_DISPATCHER instance. --- --- === --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-09: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. +-- ==== -- -- @module Task_A2G_Dispatcher @@ -51,10 +18,29 @@ do -- TASK_A2G_DISPATCHER -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. -- @field Tasking.Mission#MISSION Mission -- @extends Tasking.DetectionManager#DETECTION_MANAGER + + --- # TASK_A2G_DISPATCHE} class, extends @{#DETECTION_MANAGER} + -- + -- The TASK_A2G_DISPATCHER class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups). + -- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. + -- Find a summary below describing for which situation a task type is created: + -- + -- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter. + -- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter. + -- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars. + -- + -- Other task types will follow... + -- + -- ## TASK_A2G_DISPATCHER constructor + -- + -- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK_A2G_DISPATCHER instance. + -- + -- @field #TASK_A2G_DISPATCHER TASK_A2G_DISPATCHER = { ClassName = "TASK_A2G_DISPATCHER", Mission = nil, Detection = nil, + Tasks = {}, } @@ -72,6 +58,9 @@ do -- TASK_A2G_DISPATCHER self.Detection = Detection self.Mission = Mission + self.Detection:FilterCategories( Unit.Category.GROUND_UNIT, Unit.Category.SHIP ) + self.Detection:FilterFriendliesCategory( Unit.Category.GROUND_UNIT ) + self:AddTransition( "Started", "Assign", "Started" ) --- OnAfter Transition Handler for Event Assign. @@ -93,7 +82,7 @@ do -- TASK_A2G_DISPATCHER --- Creates a SEAD task when there are targets for it. -- @param #TASK_A2G_DISPATCHER self -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem - -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem ) self:F( { DetectedItem.ItemID } ) @@ -121,7 +110,8 @@ do -- TASK_A2G_DISPATCHER --- Creates a CAS task when there are targets for it. -- @param #TASK_A2G_DISPATCHER self -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem - -- @return Tasking.Task#TASK + -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem ) self:F( { DetectedItem.ItemID } ) @@ -132,8 +122,9 @@ do -- TASK_A2G_DISPATCHER -- Determine if the set has radar targets. If it does, construct a SEAD task. local GroundUnitCount = DetectedSet:HasGroundUnits() local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) + local RadarCount = DetectedSet:HasSEAD() - if GroundUnitCount > 0 and FriendliesNearBy == true then + if RadarCount == 0 and GroundUnitCount > 0 and FriendliesNearBy == true then -- Copy the Set local TargetSetUnit = SET_UNIT:New() @@ -149,7 +140,8 @@ do -- TASK_A2G_DISPATCHER --- Creates a BAI task when there are targets for it. -- @param #TASK_A2G_DISPATCHER self -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem - -- @return Tasking.Task#TASK + -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition ) self:F( { DetectedItem.ItemID } ) @@ -160,8 +152,9 @@ do -- TASK_A2G_DISPATCHER -- Determine if the set has radar targets. If it does, construct a SEAD task. local GroundUnitCount = DetectedSet:HasGroundUnits() local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) + local RadarCount = DetectedSet:HasSEAD() - if GroundUnitCount > 0 and FriendliesNearBy == false then + if RadarCount == 0 and GroundUnitCount > 0 and FriendliesNearBy == false then -- Copy the Set local TargetSetUnit = SET_UNIT:New() @@ -174,19 +167,26 @@ do -- TASK_A2G_DISPATCHER return nil end + + function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex ) + self.Mission:RemoveTask( self.Tasks[TaskIndex] ) + self.Tasks[TaskIndex] = nil + end + --- Evaluates the removal of the Task from the Mission. -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". -- @param #TASK_A2G_DISPATCHER self -- @param Tasking.Mission#MISSION Mission -- @param Tasking.Task#TASK Task - -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem + -- @param #boolean DetectedItemID + -- @param #boolean DetectedItemChange -- @return Tasking.Task#TASK - function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItem ) + function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged ) if Task then - if Task:IsStatePlanned() and DetectedItem.Changed == true then - self:E( "Removing Tasking: " .. Task:GetTaskName() ) - Task = Mission:RemoveTask( Task ) + if ( Task:IsStatePlanned() and DetectedItemChanged == true ) or Task:IsStateCancelled() then + --self:E( "Removing Tasking: " .. Task:GetTaskName() ) + self:RemoveTask( TaskIndex ) end end @@ -206,94 +206,195 @@ do -- TASK_A2G_DISPATCHER local ChangeMsg = {} local Mission = self.Mission - local ReportSEAD = REPORT:New( "- SEAD Tasks:") - local ReportCAS = REPORT:New( "- CAS Tasks:") - local ReportBAI = REPORT:New( "- BAI Tasks:") - local ReportChanges = REPORT:New( " - Changes:" ) - - --- First we need to the detected targets. - for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do - local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem - local DetectedSet = DetectedItem.Set -- Functional.Detection#DETECTION_BASE.DetectedSet - local DetectedZone = DetectedItem.Zone - self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) - DetectedSet:Flush() - - local ItemID = DetectedItem.ItemID - - -- Evaluate SEAD Tasking - local SEADTask = Mission:GetTask( string.format( "SEAD.%03d", ItemID ) ) - SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedItem ) - if not SEADTask then - local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - local Task = TASK_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", ItemID ), TargetSetUnit ) - Task:SetTargetZone( DetectedZone ) - Task:SetDispatcher( self ) - SEADTask = Mission:AddTask( Task ) + if Mission:IsIDLE() or Mission:IsENGAGED() then + + local TaskReport = REPORT:New() + + -- Checking the task queue for the dispatcher, and removing any obsolete task! + for TaskIndex, TaskData in pairs( self.Tasks ) do + local Task = TaskData -- Tasking.Task#TASK + if Task:IsStatePlanned() then + local DetectedItem = Detection:GetDetectedItem( TaskIndex ) + if not DetectedItem then + local TaskText = Task:GetName() + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetName() ), TaskGroup ) + end + Task = self:RemoveTask( TaskIndex ) + Mission:RemoveTask( Task ) + self.Tasks[TaskIndex] = nil + end end - end - if SEADTask and SEADTask:IsStatePlanned() then - ReportSEAD:Add( string.format( " - %s.%02d - %s", "SEAD", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) end - -- Evaluate CAS Tasking - local CASTask = Mission:GetTask( string.format( "CAS.%03d", ItemID ) ) - CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedItem ) - if not CASTask then - local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - local Task = TASK_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", ItemID ), TargetSetUnit ) - Task:SetTargetZone( DetectedZone ) - Task:SetDispatcher( self ) - CASTask = Mission:AddTask( Task ) - end - end - if CASTask and CASTask:IsStatePlanned() then - ReportCAS:Add( string.format( " - %s.%02d - %s", "CAS", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) - end - - -- Evaluate BAI Tasking - local BAITask = Mission:GetTask( string.format( "BAI.%03d", ItemID ) ) - BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedItem ) - if not BAITask then - local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - local Task = TASK_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", ItemID ), TargetSetUnit ) - Task:SetTargetZone( DetectedZone ) - Task:SetDispatcher( self ) - BAITask = Mission:AddTask( Task ) - end - end - if BAITask and BAITask:IsStatePlanned() then - ReportBAI:Add( string.format( " - %s.%02d - %s", "BAI", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) - end + --- First we need to the detected targets. + for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do - - -- Loop through the changes ... - local ChangeText = Detection:GetChangeText( DetectedItem ) - ReportChanges:Add( ChangeText ) + local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedZone = DetectedItem.Zone + --self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) + --DetectedSet:Flush() - - -- OK, so the tasking has been done, now delete the changes reported for the area. - Detection:AcceptChanges( DetectedItem ) - - end + local DetectedItemID = DetectedItem.ID + local TaskIndex = DetectedItem.Index + local DetectedItemChanged = DetectedItem.Changed + + self:E( { DetectedItemChanged = DetectedItemChanged, DetectedItemID = DetectedItemID, TaskIndex = TaskIndex } ) + + local Task = self.Tasks[TaskIndex] -- Tasking.Task_A2G#TASK_A2G + + if Task then + -- If there is a Task and the task was assigned, then we check if the task was changed ... If it was, we need to reevaluate the targets. + if Task:IsStateAssigned() then + if DetectedItemChanged == true then -- The detection has changed, thus a new TargetSet is to be evaluated and set + local TargetsReport = REPORT:New() + local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... + if TargetSetUnit then + if Task:IsInstanceOf( TASK_A2G_SEAD ) then + Task:SetTargetSetUnit( TargetSetUnit ) + Task:UpdateTaskInfo() + TargetsReport:Add( Detection:GetChangeText( DetectedItem ) ) + else + Task:Cancel() + end + else + local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed... + if TargetSetUnit then + if Task:IsInstanceOf( TASK_A2G_CAS ) then + Task:SetTargetSetUnit( TargetSetUnit ) + Task:SetDetection( Detection, TaskIndex ) + Task:UpdateTaskInfo() + TargetsReport:Add( Detection:GetChangeText( DetectedItem ) ) + else + Task:Cancel() + Task = self:RemoveTask( TaskIndex ) + end + else + local TargetSetUnit = self:EvaluateBAI( DetectedItem ) -- Returns a SetUnit if there are targets to be BAIed... + if TargetSetUnit then + if Task:IsInstanceOf( TASK_A2G_BAI ) then + Task:SetTargetSetUnit( TargetSetUnit ) + Task:SetDetection( Detection, TaskIndex ) + Task:UpdateTaskInfo() + TargetsReport:Add( Detection:GetChangeText( DetectedItem ) ) + else + Task:Cancel() + Task = self:RemoveTask( TaskIndex ) + end + end + end + end + + -- Now we send to each group the changes, if any. + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + local TargetsText = TargetsReport:Text(", ") + if ( Mission:IsGroupAssigned(TaskGroup) ) and TargetsText ~= "" then + Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup ) + end + end + end + end + end + + if Task then + if Task:IsStatePlanned() then + if DetectedItemChanged == true then -- The detection has changed, thus a new TargetSet is to be evaluated and set + if Task:IsInstanceOf( TASK_A2G_SEAD ) then + local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... + if TargetSetUnit then + Task:SetTargetSetUnit( TargetSetUnit ) + Task:UpdateTaskInfo() + else + Task:Cancel() + Task = self:RemoveTask( TaskIndex ) + end + else + if Task:IsInstanceOf( TASK_A2G_CAS ) then + local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed... + if TargetSetUnit then + Task:SetTargetSetUnit( TargetSetUnit ) + Task:SetDetection( Detection, TaskIndex ) + Task:UpdateTaskInfo() + else + Task:Cancel() + Task = self:RemoveTask( TaskIndex ) + end + else + if Task:IsInstanceOf( TASK_A2G_BAI ) then + local TargetSetUnit = self:EvaluateBAI( DetectedItem ) -- Returns a SetUnit if there are targets to be BAIed... + if TargetSetUnit then + Task:SetTargetSetUnit( TargetSetUnit ) + Task:SetDetection( Detection, TaskIndex ) + Task:UpdateTaskInfo() + else + Task:Cancel() + Task = self:RemoveTask( TaskIndex ) + end + else + Task:Cancel() + Task = self:RemoveTask( TaskIndex ) + end + end + end + end + end + end + + -- Evaluate SEAD + if not Task then + local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... + if TargetSetUnit then + Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit ) + Task:SetDetection( Detection, TaskIndex ) + end + + -- Evaluate CAS + if not Task then + local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed... + if TargetSetUnit then + Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit ) + Task:SetDetection( Detection, TaskIndex ) + end + + -- Evaluate BAI + if not Task then + local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed... + if TargetSetUnit then + Task = TASK_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit ) + Task:SetDetection( Detection, TaskIndex ) + end + end + end + + if Task then + self.Tasks[TaskIndex] = Task + Task:SetTargetZone( DetectedZone ) + Task:SetDispatcher( self ) + Mission:AddTask( Task ) - -- TODO set menus using the HQ coordinator - Mission:GetCommandCenter():SetMenu() - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if not TaskGroup:GetState( TaskGroup, "Assigned" ) then - Mission:GetCommandCenter():MessageToGroup( - string.format( "HQ Reporting - Planned tasks for mission '%s':\n%s\n", - self.Mission:GetName(), - string.format( "%s\n\n%s\n\n%s\n\n%s", ReportSEAD:Text(), ReportCAS:Text(), ReportBAI:Text(), ReportChanges:Text() - ) - ), TaskGroup - ) + TaskReport:Add( Task:GetName() ) + else + self:E("This should not happen") + end + end + + + -- OK, so the tasking has been done, now delete the changes reported for the area. + Detection:AcceptChanges( DetectedItem ) end + + -- TODO set menus using the HQ coordinator + Mission:GetCommandCenter():SetMenu() + + local TaskText = TaskReport:Text(", ") + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then + Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetName(), TaskText ), TaskGroup ) + end + end + end return true diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua new file mode 100644 index 000000000..812691fcd --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -0,0 +1,982 @@ +--- **Tasking** -- The TASK_CARGO models tasks for players to transport @{Cargo}. +-- +-- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG) +-- +-- ==== +-- +-- The Moose framework provides various CARGO classes that allow DCS phisical or logical objects to be transported or sling loaded by Carriers. +-- The CARGO_ classes, as part of the moose core, are able to Board, Load, UnBoard and UnLoad cargo between Carrier units. +-- +-- This collection of classes in this module define tasks for human players to handle these cargo objects. +-- Cargo can be transported, picked-up, deployed and sling-loaded from and to other places. +-- +-- The following classes are important to consider: +-- +-- * @{#TASK_CARGO_TRANSPORT}: Defines a task for a human player to transport a set of cargo between various zones. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- +-- @module Task_Cargo + +do -- TASK_CARGO + + --- @type TASK_CARGO + -- @extends Tasking.Task#TASK + + --- + -- # TASK_CARGO class, extends @{Task#TASK} + -- + -- ## A flexible tasking system + -- + -- The TASK_CARGO classes provide you with a flexible tasking sytem, + -- that allows you to transport cargo of various types between various locations + -- and various dedicated deployment zones. + -- + -- The cargo in scope of the TASK_CARGO classes must be explicitly given, and is of type SET_CARGO. + -- The SET_CARGO contains a collection of CARGO objects that must be handled by the players in the mission. + -- + -- + -- ## Task execution experience from the player perspective + -- + -- A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J). + -- The player needs to accept the task from the task overview list within the mission, using the radio menus. + -- + -- Once the TASK_CARGO is assigned to the player and accepted by the player, the player will obtain + -- an extra **Cargo Handling Radio Menu** that contains the CARGO objects that need to be transported. + -- + -- Each CARGO object has a certain state: + -- + -- * **UnLoaded**: The CARGO is located within the battlefield. It may still need to be transported. + -- * **Loaded**: The CARGO is loaded within a Carrier. This can be your air unit, or another air unit, or even a vehicle. + -- * **Boarding**: The CARGO is running or moving towards your Carrier for loading. + -- * **UnBoarding**: The CARGO is driving or jumping out of your Carrier and moves to a location in the Deployment Zone. + -- + -- Cargo must be transported towards different **Deployment @{Zone}s**. + -- + -- The Cargo Handling Radio Menu system allows to execute **various actions** to handle the cargo. + -- In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed. + -- Depending on the location of your Carrier unit, the menu options will vary. + -- + -- + -- ## Cargo Pickup and Boarding + -- + -- For cargo boarding, a cargo can only execute the boarding actions if it is within the foreseen **Reporting Range**. + -- Therefore, it is important that you steer your Carrier within the Reporting Range, + -- so that boarding actions can be executed on the cargo. + -- To Pickup and Board cargo, the following menu items will be shown in your carrier radio menu: + -- + -- ### Board Cargo + -- + -- If your Carrier is within the Reporting Range of the cargo, it will allow to pickup the cargo by selecting this menu option. + -- Depending on the Cargo type, the cargo will either move to your Carrier or you will receive instructions how to handle the cargo + -- pickup. If the cargo moves to your carrier, it will indicate the boarding status. + -- Note that multiple units need to board your Carrier, so it is required to await the full boarding process. + -- Once the cargo is fully boarded within your Carrier, you will be notified of this. + -- + -- Note that for airborne Carriers, it is required to land first before the Boarding process can be initiated. + -- If during boarding the Carrier gets airborne, the boarding process will be cancelled. + -- + -- ## Pickup Cargo + -- + -- If your Carrier is not within the Reporting Range of the cargo, the HQ will guide you to its location. + -- Routing information is shown in flight that directs you to the cargo within Reporting Range. + -- Upon arrival, the Cargo will contact you and further instructions will be given. + -- When your Carrier is airborne, you will receive instructions to land your Carrier. + -- The action will not be completed until you've landed your Carrier. + -- + -- + -- ## Cargo Deploy and UnBoarding + -- + -- Various Deployment Zones can be foreseen in the scope of the Cargo transportation. Each deployment zone can be of varying @{Zone} type. + -- The Cargo Handling Radio Menu provides with menu options to execute an action to steer your Carrier to a specific Zone. + -- + -- ### UnBoard Cargo + -- + -- If your Carrier is already within a Deployment Zone, + -- then the Cargo Handling Radio Menu allows to **UnBoard** a specific cargo that is + -- loaded within your Carrier group into the Deployment Zone. + -- Note that the Unboarding process takes a while, as the cargo units (infantry or vehicles) must unload from your Carrier. + -- Ensure that you stay at the position or stay on the ground while Unboarding. + -- If any unforeseen manoeuvre is done by the Carrier, then the Unboarding will be cancelled. + -- + -- ### Deploy Cargo + -- + -- If your Carrier is not within a Deployment Zone, you'll need to fly towards one. + -- Fortunately, the Cargo Handling Radio Menu provides you with menu options to select a specific Deployment Zone to fly towards. + -- Once a Deployment Zone has been selected, your Carrier will receive routing information from HQ towards the Deployment Zone center. + -- Upon arrival, the HQ will provide you with further instructions. + -- When your Carrier is airborne, you will receive instructions to land your Carrier. + -- The action will not be completed until you've landed your Carrier! + -- + -- ## Handle TASK_CARGO Events ... + -- + -- The TASK_CARGO classes define @{Cargo} transport tasks, + -- based on the tasking capabilities defined in @{Task#TASK}. + -- + -- ### Specific TASK_CARGO Events + -- + -- Specific Cargo Handling event can be captured, that allow to trigger specific actions! + -- + -- * **Boarded**: Triggered when the Cargo has been Boarded into your Carrier. + -- * **UnBoarded**: Triggered when the cargo has been Unboarded from your Carrier and has arrived at the Deployment Zone. + -- + -- ### Standard TASK_CARGO Events + -- + -- The TASK_CARGO is implemented using a @{Statemachine#FSM_TASK}, and has the following standard statuses: + -- + -- * **None**: Start of the process. + -- * **Planned**: The cargo task is planned. + -- * **Assigned**: The cargo task is assigned to a @{Group#GROUP}. + -- * **Success**: The cargo task is successfully completed. + -- * **Failed**: The cargo task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. + -- + -- === + -- + -- @field #TASK_CARGO + -- + TASK_CARGO = { + ClassName = "TASK_CARGO", + } + + --- Instantiates a new TASK_CARGO. + -- @param #TASK_CARGO self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. + -- @param #string TaskType The type of Cargo task. + -- @param #string TaskBriefing The Cargo Task briefing. + -- @return #TASK_CARGO self + function TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, TaskType, TaskBriefing ) + local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- #TASK_CARGO + self:F( {Mission, SetGroup, TaskName, SetCargo, TaskType}) + + self.SetCargo = SetCargo + self.TaskType = TaskType + self.SmokeColor = SMOKECOLOR.Red + + self.CargoItemCount = {} -- Map of Carriers having a cargo item count to check the cargo loading limits. + self.CargoLimit = 2 + + self.DeployZones = {} -- setmetatable( {}, { __mode = "v" } ) -- weak table on value + + + local Fsm = self:GetUnitProcess() + + Fsm:SetStartState( "Planned" ) + + Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } ) + + Fsm:AddTransition( { "Assigned", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded", "Landed", "Boarding" }, "SelectAction", "*" ) + + Fsm:AddTransition( "*", "RouteToPickup", "RoutingToPickup" ) + Fsm:AddProcess ( "RoutingToPickup", "RouteToPickupPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtPickup", Cancelled = "CancelRouteToPickup" } ) + Fsm:AddTransition( "Arrived", "ArriveAtPickup", "ArrivedAtPickup" ) + Fsm:AddTransition( "Cancelled", "CancelRouteToPickup", "WaitingForCommand" ) + + Fsm:AddTransition( "*", "RouteToDeploy", "RoutingToDeploy" ) + Fsm:AddProcess ( "RoutingToDeploy", "RouteToDeployZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtDeploy", Cancelled = "CancelRouteToDeploy" } ) + Fsm:AddTransition( "Arrived", "ArriveAtDeploy", "ArrivedAtDeploy" ) + Fsm:AddTransition( "Cancelled", "CancelRouteToDeploy", "WaitingForCommand" ) + + Fsm:AddTransition( { "ArrivedAtPickup", "ArrivedAtDeploy", "Landing" }, "Land", "Landing" ) + Fsm:AddTransition( "Landing", "Landed", "Landed" ) + + Fsm:AddTransition( "*", "PrepareBoarding", "AwaitBoarding" ) + Fsm:AddTransition( "AwaitBoarding", "Board", "Boarding" ) + Fsm:AddTransition( "Boarding", "Boarded", "Boarded" ) + + Fsm:AddTransition( "*", "PrepareUnBoarding", "AwaitUnBoarding" ) + Fsm:AddTransition( "AwaitUnBoarding", "UnBoard", "UnBoarding" ) + Fsm:AddTransition( "UnBoarding", "UnBoarded", "UnBoarded" ) + + + Fsm:AddTransition( "Deployed", "Success", "Success" ) + Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) + Fsm:AddTransition( "Failed", "Fail", "Failed" ) + + + --- + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_CARGO#TASK_CARGO Task + function Fsm:onafterSelectAction( TaskUnit, Task ) + + local TaskUnitName = TaskUnit:GetName() + + self:E( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) + + local MenuTime = timer.getTime() + + TaskUnit.Menu = MENU_GROUP:New( TaskUnit:GetGroup(), Task:GetName() .. " @ " .. TaskUnit:GetName() ):SetTime( MenuTime ) + + local CargoItemCount = TaskUnit:CargoItemCount() + + --Task:GetMission():GetCommandCenter():MessageToGroup( "Cargo in carrier: " .. CargoItemCount, TaskUnit:GetGroup() ) + + + Task.SetCargo:ForEachCargo( + + --- @param Core.Cargo#CARGO Cargo + function( Cargo ) + + if Cargo:IsAlive() then + +-- if Task:is( "RoutingToPickup" ) then +-- MENU_GROUP_COMMAND:New( +-- TaskUnit:GetGroup(), +-- "Cancel Route " .. Cargo.Name, +-- TaskUnit.Menu, +-- self.MenuRouteToPickupCancel, +-- self, +-- Cargo +-- ):SetTime(MenuTime) +-- end + + + + if Cargo:IsUnLoaded() then + if CargoItemCount < Task.CargoLimit then + if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + local NotInDeployZones = true + for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do + if Cargo:IsInZone( DeployZone ) then + NotInDeployZones = false + end + end + if NotInDeployZones then + if not TaskUnit:InAir() then + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Board cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime) + end + end + else + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Pickup cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime) + end + end + end + + if Cargo:IsLoaded() then + if not TaskUnit:InAir() then + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unboard cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnBoardCargo, self, Cargo ):SetTime(MenuTime) + end + -- Deployzones are optional zones that can be selected to request routing information. + for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do + if not Cargo:IsInZone( DeployZone ) then + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Deploy cargo at " .. DeployZoneName, TaskUnit.Menu, self.MenuRouteToDeploy, self, DeployZone ):SetTime(MenuTime) + end + end + end + end + + end + ) + + TaskUnit.Menu:Remove( MenuTime ) + + + self:__SelectAction( -15 ) + + end + + + --- + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:OnLeaveWaitingForCommand( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + TaskUnit.Menu:Remove() + end + + function Fsm:MenuBoardCargo( Cargo ) + self:__PrepareBoarding( 1.0, Cargo ) + end + + function Fsm:MenuUnBoardCargo( Cargo, DeployZone ) + self:__PrepareUnBoarding( 1.0, Cargo, DeployZone ) + end + + function Fsm:MenuRouteToPickup( Cargo ) + self:__RouteToPickup( 1.0, Cargo ) + end + + function Fsm:MenuRouteToDeploy( DeployZone ) + self:__RouteToDeploy( 1.0, DeployZone ) + end + + + + --- + --#TASK_CAROG_TRANSPORT self + --#Wrapper.Unit#UNIT + + + --- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + -- @param From + -- @param Event + -- @param To + -- @param Core.Cargo#CARGO Cargo + function Fsm:onafterRouteToPickup( TaskUnit, Task, From, Event, To, Cargo ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + if Cargo:IsAlive() then + self.Cargo = Cargo -- Core.Cargo#CARGO + Task:SetCargoPickup( self.Cargo, TaskUnit ) + self:__RouteToPickupPoint( -0.1 ) + end + + end + + + + --- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:onafterArriveAtPickup( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + if self.Cargo:IsAlive() then + self.Cargo:Smoke( Task:GetSmokeColor(), 15 ) + if TaskUnit:IsAir() then + Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() ) + self:__Land( -0.1, "Pickup" ) + else + self:__SelectAction( -0.1 ) + end + end + end + + + --- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:onafterCancelRouteToPickup( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + self:__SelectAction( -0.1 ) + end + + + --- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + function Fsm:onafterRouteToDeploy( TaskUnit, Task, From, Event, To, DeployZone ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + self:E( DeployZone ) + self.DeployZone = DeployZone + Task:SetDeployZone( self.DeployZone, TaskUnit ) + self:__RouteToDeployZone( -0.1 ) + end + + + --- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:onafterArriveAtDeploy( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + if TaskUnit:IsAir() then + Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() ) + self:__Land( -0.1, "Deploy" ) + else + self:__SelectAction( -0.1 ) + end + end + + + --- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:onafterCancelRouteToDeploy( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + self:__SelectAction( -0.1 ) + end + + + + --- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:onafterLand( TaskUnit, Task, From, Event, To, Action ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + if self.Cargo:IsAlive() then + if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if TaskUnit:InAir() then + self:__Land( -10, Action ) + else + Task:GetMission():GetCommandCenter():MessageToGroup( "Landed ...", TaskUnit:GetGroup() ) + self:__Landed( -0.1, Action ) + end + else + if Action == "Pickup" then + self:__RouteToPickupZone( -0.1 ) + else + self:__RouteToDeployZone( -0.1 ) + end + end + end + end + + --- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:onafterLanded( TaskUnit, Task, From, Event, To, Action ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + if self.Cargo:IsAlive() then + if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if TaskUnit:InAir() then + self:__Land( -0.1, Action ) + else + self:__SelectAction( -0.1 ) + end + else + if Action == "Pickup" then + self:__RouteToPickupZone( -0.1 ) + else + self:__RouteToDeployZone( -0.1 ) + end + end + end + end + + --- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:onafterPrepareBoarding( TaskUnit, Task, From, Event, To, Cargo ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + if Cargo and Cargo:IsAlive() then + self.Cargo = Cargo -- Core.Cargo#CARGO_GROUP + self:__Board( -0.1 ) + end + end + + --- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:onafterBoard( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + function self.Cargo:OnEnterLoaded( From, Event, To, TaskUnit, TaskProcess ) + self:E({From, Event, To, TaskUnit, TaskProcess }) + TaskProcess:__Boarded( 0.1 ) + end + + if self.Cargo:IsAlive() then + if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if TaskUnit:InAir() then + --- ABORT the boarding. Split group if any and go back to select action. + else + self.Cargo:MessageToGroup( "Boarding ...", TaskUnit:GetGroup() ) + self.Cargo:Board( TaskUnit, 20, self ) + end + else + --self:__ArriveAtCargo( -0.1 ) + end + end + end + + + --- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:onafterBoarded( TaskUnit, Task ) + + local TaskUnitName = TaskUnit:GetName() + self:E( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) + + self.Cargo:MessageToGroup( "Boarded ...", TaskUnit:GetGroup() ) + + TaskUnit:AddCargo( self.Cargo ) + + self:__SelectAction( 1 ) + + -- TODO:I need to find a more decent solution for this. + Task:E( { CargoPickedUp = Task.CargoPickedUp } ) + if self.Cargo:IsAlive() then + if Task.CargoPickedUp then + Task:CargoPickedUp( TaskUnit, self.Cargo ) + end + end + + end + + + --- + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + -- @param From + -- @param Event + -- @param To + -- @param Cargo + -- @param Core.Zone#ZONE_BASE DeployZone + function Fsm:onafterPrepareUnBoarding( TaskUnit, Task, From, Event, To, Cargo ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID(), From, Event, To, Cargo } ) + + self.Cargo = Cargo + self.DeployZone = nil + + -- Check if the Cargo is at a deployzone... If it is, provide it as a parameter! + if Cargo:IsAlive() then + for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do + if Cargo:IsInZone( DeployZone ) then + self.DeployZone = DeployZone -- Core.Zone#ZONE_BASE + break + end + end + self:__UnBoard( -0.1, Cargo, self.DeployZone ) + end + end + + --- + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + -- @param From + -- @param Event + -- @param To + -- @param Cargo + -- @param Core.Zone#ZONE_BASE DeployZone + function Fsm:onafterUnBoard( TaskUnit, Task, From, Event, To, Cargo, DeployZone ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID(), From, Event, To, Cargo, DeployZone } ) + + function self.Cargo:OnEnterUnLoaded( From, Event, To, DeployZone, TaskProcess ) + self:E({From, Event, To, DeployZone, TaskProcess }) + TaskProcess:__UnBoarded( -0.1 ) + end + + if self.Cargo:IsAlive() then + self.Cargo:MessageToGroup( "UnBoarding ...", TaskUnit:GetGroup() ) + if DeployZone then + self.Cargo:UnBoard( DeployZone:GetPointVec2(), 400, self ) + else + self.Cargo:UnBoard( TaskUnit:GetPointVec2():AddX(60), 400, self ) + end + end + end + + + --- + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:onafterUnBoarded( TaskUnit, Task ) + + local TaskUnitName = TaskUnit:GetName() + self:E( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) + + self.Cargo:MessageToGroup( "UnBoarded ...", TaskUnit:GetGroup() ) + + TaskUnit:RemoveCargo( self.Cargo ) + + local NotInDeployZones = true + for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do + if self.Cargo:IsInZone( DeployZone ) then + NotInDeployZones = false + end + end + + if NotInDeployZones == false then + self.Cargo:SetDeployed( true ) + end + + -- TODO:I need to find a more decent solution for this. + Task:E( { CargoDeployed = Task.CargoDeployed and "true" or "false" } ) + Task:E( { CargoIsAlive = self.Cargo:IsAlive() and "true" or "false" } ) + if self.Cargo:IsAlive() then + if Task.CargoDeployed then + Task:CargoDeployed( TaskUnit, self.Cargo, self.DeployZone ) + end + end + + self:__SelectAction( 1 ) + end + + + return self + + end + + + --- Set a limit on the amount of cargo items that can be loaded into the Carriers. + -- @param #TASK_CARGO self + -- @param CargoLimit Specifies a number of cargo items that can be loaded in the helicopter. + -- @return #TASK_CARGO + function TASK_CARGO:SetCargoLimit( CargoLimit ) + self.CargoLimit = CargoLimit + return self + end + + + ---@param Color Might be SMOKECOLOR.Blue, SMOKECOLOR.Red SMOKECOLOR.Orange, SMOKECOLOR.White or SMOKECOLOR.Green + function TASK_CARGO:SetSmokeColor(SmokeColor) + -- Makes sure Coloe is set + if SmokeColor == nil then + self.SmokeColor = SMOKECOLOR.Red -- Make sure a default color is exist + + elseif type(SmokeColor) == "number" then + self:F2(SmokeColor) + if SmokeColor > 0 and SmokeColor <=5 then -- Make sure number is within ragne, assuming first enum is one + self.SmokeColor = SMOKECOLOR.SmokeColor + end + end + end + + --@return SmokeColor + function TASK_CARGO:GetSmokeColor() + return self.SmokeColor + end + + --- @param #TASK_CARGO self + function TASK_CARGO:GetPlannedMenuText() + return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" + end + + --- @param #TASK_CARGO self + -- @return Core.Set#SET_CARGO The Cargo Set. + function TASK_CARGO:GetCargoSet() + + return self.SetCargo + end + + --- @param #TASK_CARGO self + -- @return #list The Deployment Zones. + function TASK_CARGO:GetDeployZones() + + return self.DeployZones + end + + --- @param #TASK_CARGO self + -- @param AI.AI_Cargo#AI_CARGO Cargo The cargo. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_CARGO + function TASK_CARGO:SetCargoPickup( Cargo, TaskUnit ) + + self:F({Cargo, TaskUnit}) + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteCargo = ProcessUnit:GetProcess( "RoutingToPickup", "RouteToPickupPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + ActRouteCargo:Reset() + ActRouteCargo:SetCoordinate( Cargo:GetCoordinate() ) + ActRouteCargo:SetRange( Cargo:GetBoardingRange() ) + ActRouteCargo:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Cargo " .. Cargo:GetName(), TaskUnit.Menu ) + ActRouteCargo:Start() + return self + end + + + --- @param #TASK_CARGO self + -- @param Core.Zone#ZONE DeployZone + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_CARGO + function TASK_CARGO:SetDeployZone( DeployZone, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteDeployZone = ProcessUnit:GetProcess( "RoutingToDeploy", "RouteToDeployZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + ActRouteDeployZone:Reset() + ActRouteDeployZone:SetZone( DeployZone ) + ActRouteDeployZone:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Deploy Zone" .. DeployZone:GetName(), TaskUnit.Menu ) + ActRouteDeployZone:Start() + return self + end + + + --- @param #TASK_CARGO self + -- @param Core.Zone#ZONE DeployZone + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_CARGO + function TASK_CARGO:AddDeployZone( DeployZone, TaskUnit ) + + self.DeployZones[DeployZone:GetName()] = DeployZone + + return self + end + + --- @param #TASK_CARGO self + -- @param Core.Zone#ZONE DeployZone + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_CARGO + function TASK_CARGO:RemoveDeployZone( DeployZone, TaskUnit ) + + self.DeployZones[DeployZone:GetName()] = nil + + return self + end + + --- @param #TASK_CARGO self + -- @param @list DeployZones + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_CARGO + function TASK_CARGO:SetDeployZones( DeployZones, TaskUnit ) + + for DeployZoneID, DeployZone in pairs( DeployZones ) do + self.DeployZones[DeployZone:GetName()] = DeployZone + end + + return self + end + + + + --- @param #TASK_CARGO self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. + function TASK_CARGO:GetTargetZone( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + return ActRouteTarget:GetZone() + end + + --- Set a score when a target in scope of the A2G attack, has been destroyed . + -- @param #TASK_CARGO self + -- @param #string Text The text to display to the player, when the target has been destroyed. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_CARGO + function TASK_CARGO:SetScoreOnProgress( Text, Score, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScoreProcess( "Engaging", "Account", "Account", Text, Score ) + + return self + end + + --- Set a score when all the targets in scope of the A2G attack, have been destroyed. + -- @param #TASK_CARGO self + -- @param #string Text The text to display to the player, when all targets hav been destroyed. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_CARGO + function TASK_CARGO:SetScoreOnSuccess( Text, Score, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Success", Text, Score ) + + return self + end + + --- Set a penalty when the A2G attack has failed. + -- @param #TASK_CARGO self + -- @param #string Text The text to display to the player, when the A2G attack has failed. + -- @param #number Penalty The penalty in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_CARGO + function TASK_CARGO:SetScoreOnFail( Text, Penalty, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Failed", Text, Penalty ) + + return self + end + + function TASK_CARGO:SetGoalTotal() + + self.GoalTotal = self.SetCargo:Count() + end + + function TASK_CARGO:GetGoalTotal() + + return self.GoalTotal + end + + +end + + +do -- TASK_CARGO_TRANSPORT + + --- The TASK_CARGO_TRANSPORT class + -- @type TASK_CARGO_TRANSPORT + -- @extends #TASK_CARGO + TASK_CARGO_TRANSPORT = { + ClassName = "TASK_CARGO_TRANSPORT", + } + + --- Instantiates a new TASK_CARGO_TRANSPORT. + -- @param #TASK_CARGO_TRANSPORT self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. + -- @param #string TaskBriefing The Cargo Task briefing. + -- @return #TASK_CARGO_TRANSPORT self + function TASK_CARGO_TRANSPORT:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing ) + local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport", TaskBriefing ) ) -- #TASK_CARGO_TRANSPORT + self:F() + + Mission:AddTask( self ) + + + -- Events + + self:AddTransition( "*", "CargoPickedUp", "*" ) + self:AddTransition( "*", "CargoDeployed", "*" ) + + self:E( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } ) + + --- OnBefore Transition Handler for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoPickedUp + -- @param #TASK_CARGO_TRANSPORT self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoPickedUp + -- @param #TASK_CARGO_TRANSPORT self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- Synchronous Event Trigger for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_TRANSPORT] CargoPickedUp + -- @param #TASK_CARGO_TRANSPORT self + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- Asynchronous Event Trigger for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_TRANSPORT] __CargoPickedUp + -- @param #TASK_CARGO_TRANSPORT self + -- @param #number Delay The delay in seconds. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- OnBefore Transition Handler for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoDeployed + -- @param #TASK_CARGO_TRANSPORT self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoDeployed + -- @param #TASK_CARGO_TRANSPORT self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + --- Synchronous Event Trigger for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_TRANSPORT] CargoDeployed + -- @param #TASK_CARGO_TRANSPORT self + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + --- Asynchronous Event Trigger for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_TRANSPORT] __CargoDeployed + -- @param #TASK_CARGO_TRANSPORT self + -- @param #number Delay The delay in seconds. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + local Fsm = self:GetUnitProcess() + + local CargoReport = REPORT:New( "Transport Cargo. The following cargo needs to be transported including initial positions:") + + SetCargo:ForEachCargo( + --- @param Core.Cargo#CARGO Cargo + function( Cargo ) + local CargoType = Cargo:GetType() + local CargoName = Cargo:GetName() + local CargoCoordinate = Cargo:GetCoordinate() + CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) ) + end + ) + + self:SetBriefing( + TaskBriefing or + CargoReport:Text() + ) + + + return self + end + + function TASK_CARGO_TRANSPORT:ReportOrder( ReportGroup ) + + return true + end + + + --- + -- @param #TASK_CARGO_TRANSPORT self + -- @return #boolean + function TASK_CARGO_TRANSPORT:IsAllCargoTransported() + + local CargoSet = self:GetCargoSet() + local Set = CargoSet:GetSet() + + local DeployZones = self:GetDeployZones() + + local CargoDeployed = true + + -- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ). + for CargoID, CargoData in pairs( Set ) do + local Cargo = CargoData -- Core.Cargo#CARGO + + if Cargo:IsDeployed() then + + -- Loop the DeployZones set for the TASK_CARGO_TRANSPORT. + for DeployZoneID, DeployZone in pairs( DeployZones ) do + + -- If there is a Cargo not in one of DeployZones, then not all Cargo is deployed. + self:T( { Cargo.CargoObject } ) + if Cargo:IsInZone( DeployZone ) == false then + CargoDeployed = false + end + end + else + CargoDeployed = false + end + end + + return CargoDeployed + end + + --- @param #TASK_CARGO_TRANSPORT self + function TASK_CARGO_TRANSPORT:onafterGoal( TaskUnit, From, Event, To ) + local CargoSet = self.CargoSet + + if self:IsAllCargoTransported() then + self:Success() + end + + self:__Goal( -10 ) + end + +end + diff --git a/Moose Development/Moose/Utilities/StatHandler.lua b/Moose Development/Moose/Utilities/StatHandler.lua deleted file mode 100644 index 1e3f2b29f..000000000 --- a/Moose Development/Moose/Utilities/StatHandler.lua +++ /dev/null @@ -1,246 +0,0 @@ ---- Provides a logging of statistics in a running DCS Mission. --- @script eStatHandler - - - - ---Handler table -local eStatHandler = {} -local _StatRunID - ---Neccessary tables for string instead of integers -SETCoalition = -{ - [1] = "red", - [2] = "blue", -} - -SETGroupCat = -{ - [1] = "AIRPLANE", - [2] = "HELICOPTER", - [3] = "GROUND", - [4] = "SHIP", - [5] = "STRUCTURE", -} - -SETWeaponCatName = -{ - [0] = "SHELL", - [1] = "MISSILE", - [2] = "ROCKET", - [3] = "BOMB", - } - - wEvent = { - "S_EVENT_SHOT", - "S_EVENT_HIT", - "S_EVENT_TAKEOFF", - "S_EVENT_LAND", - "S_EVENT_CRASH", - "S_EVENT_EJECTION", - "S_EVENT_REFUELING", - "S_EVENT_DEAD", - "S_EVENT_PILOT_DEAD", - "S_EVENT_BASE_CAPTURED", - "S_EVENT_MISSION_START", - "S_EVENT_MISSION_END", - "S_EVENT_TOOK_CONTROL", - "S_EVENT_REFUELING_STOP", - "S_EVENT_BIRTH", - "S_EVENT_HUMAN_FAILURE", - "S_EVENT_ENGINE_STARTUP", - "S_EVENT_ENGINE_SHUTDOWN", - "S_EVENT_PLAYER_ENTER_UNIT", - "S_EVENT_PLAYER_LEAVE_UNIT", - "S_EVENT_PLAYER_COMMENT", - "S_EVENT_SHOOTING_START", - "S_EVENT_SHOOTING_END", - "S_EVENT_MAX", - } - -statEventsTable = {} - - -function SecondsToClock(sSeconds) -local nSeconds = sSeconds - if nSeconds == 0 then - --return nil; - return "00:00:00"; - else - nHours = string.format("%02.f", math.floor(nSeconds/3600)); - nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60))); - nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60)); - return nHours..":"..nMins..":"..nSecs - end -end - - -function eStatHandler:onEvent(e) - local InitID_ = "" - local InitName = "" - local WorldEvent = wEvent[e.id] - local InitCoa = "" - local InitGroupCat = "" - local InitType = "" - local InitPlayer = "" - local eWeaponCat = "" - local eWeaponName = "" - local TargID_ = "" - local TargName = "" - local TargType = "" - local TargPlayer = "" - local TargCoa = "" - local TargGroupCat = "" - - if e.initiator and Object.getCategory(e.initiator) == Object.Category.UNIT then - --Initiator variables - local InitGroup = e.initiator:getGroup() - InitID_ = e.initiator.id_ - if e.initiator:getName() then - InitName = e.initiator:getName() - end - if InitGroup:getCoalition() then - InitCoa = SETCoalition[InitGroup:getCoalition()] - end - if InitGroup:getCategory() then - InitGroupCat = SETGroupCat[InitGroup:getCategory() + 1] - end - InitType = e.initiator:getTypeName() - - --Get initiator player name or AI if NIL - if e.initiator:getPlayerName() == nil then - InitPlayer = "AI" - else - InitPlayer = e.initiator:getPlayerName() - end - else - if e.initiator then - local InitGroup = e.initiator:getGroup() - InitID_ = e.initiator.id_ - if e.initiator:getName() then - InitName = e.initiator:getName() - end - InitCoa = SETCoalition[InitGroup:getCoalition()] - InitGroupCat = SETGroupCat[InitGroup:getCategory() + 1] - InitType = e.initiator:getTypeName() - - --Get initiator player name or AI if NIL - if e.initiator:getPlayerName() == nil then - InitPlayer = "AI" - else - InitPlayer = e.initiator:getPlayerName() - end - end - end - - --Weapon variables - if e.weapon == nil then - eWeaponCat = "" - eWeaponName = "" - else - local eWeaponDesc = e.weapon:getDesc() - eWeaponCat = SETWeaponCatName[eWeaponDesc.category] - eWeaponName = eWeaponDesc.displayName - end - - --Target variables - if e.target == nil then - TargID_ = "" - TargName = "" - TargType = "" - TargPlayer = "" - TargCoa = "" - TargGroupCat = "" - elseif Object.getCategory(e.target) == Object.Category.UNIT then - local TargGroup = e.target:getGroup() - TargID_ = e.target.id_ - if e.target:getName() then - TargName = e.target:getName() - end - TargType = e.target:getTypeName() - TargCoa = SETCoalition[TargGroup:getCoalition()] - TargGroupCat = SETGroupCat[TargGroup:getCategory() + 1] - - --Get target player name or AI if NIL - if not e.target:getPlayerName() then - TargPlayer = "AI" - else - TargPlayer = e.target:getPlayerName() - end - else - TargType = e.target:getTypeName() - TargID_ = "" - TargName = "" - TargPlayer = "" - TargCoa = "" - TargGroupCat = "" - end - - --write events to table - statEventsTable[#statEventsTable + 1] = - { - [1] = _StatRunID, - [2] = SecondsToClock(timer.getTime()), - [3] = WorldEvent, - [4] = InitID_, - [5] = InitName, - [6] = InitCoa, - [7] = InitGroupCat, - [8] = InitType, - [9] = InitPlayer, - [10] = eWeaponCat, - [11] = eWeaponName, - [12] = TargID_, - [13] = TargName, - [14] = TargCoa, - [15] = TargGroupCat, - [16] = TargType, - [17] = TargPlayer, - } - env.info( 'Event: ' .. _StatRunID .. '~ ' .. SecondsToClock(timer.getTime()) .. '~ ' .. WorldEvent .. '~ ' .. InitID_ .. '~ ' .. InitName .. '~ ' .. InitCoa .. '~ ' .. InitGroupCat .. '~ ' .. InitType .. '~ ' .. InitPlayer .. - '~ ' .. eWeaponCat .. '~ ' .. eWeaponName .. '~ ' .. TargID_ .. '~ ' .. TargName .. '~ ' .. TargCoa .. '~ ' .. TargGroupCat .. '~ ' .. TargType .. '~ ' .. TargPlayer ) -end - - - - -do - - local StatFile,err - - - function StatOpen() - local fdir = lfs.writedir() .. [[Logs\]] .. "Events_" .. os.date( "%Y-%m-%d_%H-%M-%S" ) .. ".csv" - StatFile,err = io.open(fdir,"w+") - if not StatFile then - local errmsg = 'Error: No Logs folder found in the User\\Saved Games\\DCS\\Logs directory...' .. 'Save_stat . sample: C:\\Users\\youname\\Saved Games\\DCS\\Logs' - trigger.action.outText(errmsg, 10) - return print(err) - end - StatFile:write("RunID~Time~Event~Initiator ID~Initiator Name~Initiator Coalition~Initiator Group Category~Initiator Type~Initiator Player~Weapon Category~Weapon Name~Target ID~Target Name~Target Coalition~Target Group Category~Target Type~Target Player\n") - - _StatRunID = os.date("%y-%m-%d_%H-%M-%S") - routines.scheduleFunction( StatSave, { }, timer.getTime() + 1, 1) - end - - function StatSave() - --write statistic information to file - for Index, eDetails in ipairs(statEventsTable) do - for eInfoName, eInfoData in ipairs(eDetails) do - StatFile:write(eInfoData.."~") - end - StatFile:write("\n") - end - statEventsTable = {} - end - - function StatClose() - StatFile:close() - end - -end - -world.addEventHandler(eStatHandler) -StatOpen() - diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 3cf7c34ec..dc292d942 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -33,6 +33,69 @@ FLARECOLOR = trigger.flareColor -- #FLARECOLOR -- @type UTILS UTILS = {} +--- Function to infer instance of an object +-- +-- ### Examples: +-- +-- * UTILS.IsInstanceOf( 'some text', 'string' ) will return true +-- * UTILS.IsInstanceOf( some_function, 'function' ) will return true +-- * UTILS.IsInstanceOf( 10, 'number' ) will return true +-- * UTILS.IsInstanceOf( false, 'boolean' ) will return true +-- * UTILS.IsInstanceOf( nil, 'nil' ) will return true +-- +-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', ZONE ) will return true +-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', 'ZONE' ) will return true +-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', 'zone' ) will return true +-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', 'BASE' ) will return true +-- +-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', 'GROUP' ) will return false +-- +-- +-- @param object is the object to be evaluated +-- @param className is the name of the class to evaluate (can be either a string or a Moose class) +-- @return #boolean +UTILS.IsInstanceOf = function( object, className ) + -- Is className NOT a string ? + if not type( className ) == 'string' then + + -- Is className a Moose class ? + if type( className ) == 'table' and className.IsInstanceOf ~= nil then + + -- Get the name of the Moose class as a string + className = className.ClassName + + -- className is neither a string nor a Moose class, throw an error + else + + -- I'm not sure if this should take advantage of MOOSE logging function, or throw an error for pcall + local err_str = 'className parameter should be a string; parameter received: '..type( className ) + self:E( err_str ) + return false + -- error( err_str ) + + end + end + + -- Is the object a Moose class instance ? + if type( object ) == 'table' and object.IsInstanceOf ~= nil then + + -- Use the IsInstanceOf method of the BASE class + return object:IsInstanceOf( className ) + else + + -- If the object is not an instance of a Moose class, evaluate against lua basic data types + local basicDataTypes = { 'string', 'number', 'function', 'boolean', 'nil', 'table' } + for _, basicDataType in ipairs( basicDataTypes ) do + if className == basicDataType then + return type( object ) == basicDataType + end + end + end + + -- Check failed + return false +end + --from http://lua-users.org/wiki/CopyTable UTILS.DeepCopy = function(object) @@ -183,6 +246,10 @@ UTILS.KnotsToMps = function(knots) return knots*1852/3600 end +UTILS.KnotsToKmph = function(knots) + return knots* 1.852 +end + UTILS.KmphToMps = function(kmph) return kmph/3.6 end @@ -238,12 +305,13 @@ UTILS.tostringLL = function( lat, lon, acc, DMS) end local secFrmtStr -- create the formatting string for the seconds place - if acc <= 0 then -- no decimal place. - secFrmtStr = '%02d' - else - local width = 3 + acc -- 01.310 - that's a width of 6, for example. - secFrmtStr = '%0' .. width .. '.' .. acc .. 'f' - end + secFrmtStr = '%02d' +-- if acc <= 0 then -- no decimal place. +-- secFrmtStr = '%02d' +-- else +-- local width = 3 + acc -- 01.310 - that's a width of 6, for example. +-- secFrmtStr = '%0' .. width .. '.' .. acc .. 'f' +-- end return string.format('%02d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' ' .. string.format('%02d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi @@ -276,6 +344,16 @@ UTILS.tostringLL = function( lat, lon, acc, DMS) end end +-- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5. +UTILS.tostringMGRS = function(MGRS, acc) --R2.1 + if acc == 0 then + return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph + else + return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', UTILS.Round(MGRS.Easting/(10^(5-acc)), 0)) + .. ' ' .. string.format('%0' .. acc .. 'd', UTILS.Round(MGRS.Northing/(10^(5-acc)), 0)) + end +end + --- From http://lua-users.org/wiki/SimpleRound -- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place @@ -293,3 +371,27 @@ function UTILS.DoString( s ) return false, err end end + +-- Here is a customized version of pairs, which I called spairs because it iterates over the table in a sorted order. +function UTILS.spairs( t, order ) + -- collect the keys + local keys = {} + for k in pairs(t) do keys[#keys+1] = k end + + -- if order function given, sort by it by passing the table and keys a, b, + -- otherwise just sort the keys + if order then + table.sort(keys, function(a,b) return order(t, a, b) end) + else + table.sort(keys) + end + + -- return the iterator function + local i = 0 + return function() + i = i + 1 + if keys[i] then + return keys[i], t[keys[i]] + end + end +end diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index a73189948..fab9998b3 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -1,17 +1,28 @@ ---- This module contains the AIRBASE classes. +--- **Wrapper** -- AIRBASE is a wrapper class to handle the DCS Airbase objects. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: -- -- === -- --- 1) @{Airbase#AIRBASE} class, extends @{Positionable#POSITIONABLE} --- ================================================================= --- The @{AIRBASE} class is a wrapper class to handle the DCS Airbase objects: +-- @module Airbase + + +--- @type AIRBASE +-- @extends Wrapper.Positionable#POSITIONABLE + +--- # AIRBASE class, extends @{Positionable#POSITIONABLE} +-- +-- AIRBASE is a wrapper class to handle the DCS Airbase objects: -- -- * Support all DCS Airbase APIs. -- * Enhance with Airbase specific APIs not in the DCS Airbase API set. -- --- --- 1.1) AIRBASE reference methods --- ------------------------------ +-- ## AIRBASE reference methods +-- -- For each DCS Airbase object alive within a running mission, a AIRBASE wrapper object (instance) will be created within the _@{DATABASE} object. -- This is done at the beginning of the mission (when the mission starts). -- @@ -29,27 +40,14 @@ -- -- IMPORTANT: ONE SHOULD NEVER SANATIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil). -- --- 1.2) DCS AIRBASE APIs --- --------------------- +-- ## DCS Airbase APIs +-- -- The DCS Airbase APIs are used extensively within MOOSE. The AIRBASE class has for each DCS Airbase API a corresponding method. -- To be able to distinguish easily in your code the difference between a AIRBASE API call and a DCS Airbase API call, -- the first letter of the method is also capitalized. So, by example, the DCS Airbase method @{DCSWrapper.Airbase#Airbase.getName}() -- is implemented in the AIRBASE class as @{#AIRBASE.GetName}(). -- --- More functions will be added --- ---------------------------- --- During the MOOSE development, more functions will be added. --- --- @module Airbase --- @author FlightControl - - - - - ---- The AIRBASE class --- @type AIRBASE --- @extends Wrapper.Positionable#POSITIONABLE +-- @field #AIRBASE AIRBASE AIRBASE = { ClassName="AIRBASE", CategoryName = { @@ -59,6 +57,88 @@ AIRBASE = { }, } +--- @field Caucasus +AIRBASE.Caucasus = { + ["Gelendzhik"] = "Gelendzhik", + ["Krasnodar_Pashkovsky"] = "Krasnodar-Pashkovsky", + ["Sukhumi_Babushara"] = "Sukhumi-Babushara", + ["Gudauta"] = "Gudauta", + ["Batumi"] = "Batumi", + ["Senaki_Kolkhi"] = "Senaki-Kolkhi", + ["Kobuleti"] = "Kobuleti", + ["Kutaisi"] = "Kutaisi", + ["Tbilisi_Lochini"] = "Tbilisi-Lochini", + ["Soganlug"] = "Soganlug", + ["Vaziani"] = "Vaziani", + ["Anapa_Vityazevo"] = "Anapa-Vityazevo", + ["Krasnodar_Center"] = "Krasnodar-Center", + ["Novorossiysk"] = "Novorossiysk", + ["Krymsk"] = "Krymsk", + ["Maykop_Khanskaya"] = "Maykop-Khanskaya", + ["Sochi_Adler"] = "Sochi-Adler", + ["Mineralnye_Vody"] = "Mineralnye Vody", + ["Nalchik"] = "Nalchik", + ["Mozdok"] = "Mozdok", + ["Beslan"] = "Beslan", + } + +--- @field Nevada +AIRBASE.Nevada = { + ["Creech_AFB"] = "Creech AFB", + ["Groom_Lake_AFB"] = "Groom Lake AFB", + ["McCarran_International_Airport"] = "McCarran International Airport", + ["Nellis_AFB"] = "Nellis AFB", + ["Beatty_Airport"] = "Beatty Airport", + ["Boulder_City_Airport"] = "Boulder City Airport", + ["Echo_Bay"] = "Echo Bay", + ["Henderson_Executive_Airport"] = "Henderson Executive Airport", + ["Jean_Airport"] = "Jean Airport", + ["Laughlin_Airport"] = "Laughlin Airport", + ["Lincoln_County"] = "Lincoln County", + ["Mellan_Airstrip"] = "Mellan Airstrip", + ["Mesquite"] = "Mesquite", + ["Mina_Airport_3Q0"] = "Mina Airport 3Q0", + ["North_Las_Vegas"] = "North Las Vegas", + ["Pahute_Mesa_Airstrip"] = "Pahute Mesa Airstrip", + ["Tonopah_Airport"] = "Tonopah Airport", + ["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range Airfield", + } + +--- @field Normandy +AIRBASE.Normandy = { + ["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont", + ["Lignerolles"] = "Lignerolles", + ["Cretteville"] = "Cretteville", + ["Maupertus"] = "Maupertus", + ["Brucheville"] = "Brucheville", + ["Meautis"] = "Meautis", + ["Cricqueville_en_Bessin"] = "Cricqueville-en-Bessin", + ["Lessay"] = "Lessay", + ["Sainte_Laurent_sur_Mer"] = "Sainte-Laurent-sur-Mer", + ["Biniville"] = "Biniville", + ["Cardonville"] = "Cardonville", + ["Deux_Jumeaux"] = "Deux Jumeaux", + ["Chippelle"] = "Chippelle", + ["Beuzeville"] = "Beuzeville", + ["Azeville"] = "Azeville", + ["Picauville"] = "Picauville", + ["Le_Molay"] = "Le Molay", + ["Longues_sur_Mer"] = "Longues-sur-Mer", + ["Carpiquet"] = "Carpiquet", + ["Bazenville"] = "Bazenville", + ["Sainte_Croix_sur_Mer"] = "Sainte-Croix-sur-Mer", + ["Beny_sur_Mer"] = "Beny-sur-Mer", + ["Rucqueville"] = "Rucqueville", + ["Sommervieu"] = "Sommervieu", + ["Lantheuil"] = "Lantheuil", + ["Evreux"] = "Evreux", + ["Chailey"] = "Chailey", + ["Needs_Oar_Point"] = "Needs Oar Point", + ["Funtington"] = "Funtington", + ["Tangmere"] = "Tangmere", + ["Ford"] = "Ford", + } + -- Registration. --- Create a new AIRBASE from DCSAirbase. @@ -69,6 +149,7 @@ function AIRBASE:Register( AirbaseName ) local self = BASE:Inherit( self, POSITIONABLE:New( AirbaseName ) ) self.AirbaseName = AirbaseName + self.AirbaseZone = ZONE_RADIUS:New( AirbaseName, self:GetVec2(), 8000 ) return self end @@ -105,5 +186,12 @@ function AIRBASE:GetDCSObject() return nil end +--- Get the airbase zone. +-- @param #AIRBASE self +-- @return Core.Zone#ZONE_RADIUS The zone radius of the airbase. +function AIRBASE:GetZone() + return self.AirbaseZone +end + diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index afab71f1d..74cd6b741 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -1,10 +1,26 @@ ---- This module contains the CLIENT class. +--- **Wrapper** -- CLIENT wraps DCS Unit objects acting as a __Client__ or __Player__ within a mission. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- === +-- +-- @module Client + + +--- The CLIENT class +-- @type CLIENT +-- @extends Wrapper.Unit#UNIT + + +--- # CLIENT class, extends @{Unit#UNIT} -- --- 1) @{Client#CLIENT} class, extends @{Unit#UNIT} --- =============================================== -- Clients are those **Units** defined within the Mission Editor that have the skillset defined as __Client__ or __Player__. -- Note that clients are NOT the same as Units, they are NOT necessarily alive. --- The @{Client#CLIENT} class is a wrapper class to handle the DCS Unit objects that have the skillset defined as __Client__ or __Player__: +-- The CLIENT class is a wrapper class to handle the DCS Unit objects that have the skillset defined as __Client__ or __Player__: -- -- * Wraps the DCS Unit objects with skill level set to Player or Client. -- * Support all DCS Unit APIs. @@ -15,8 +31,8 @@ -- -- Clients are being used by the @{MISSION} class to follow players and register their successes. -- --- 1.1) CLIENT reference methods --- ----------------------------- +-- ## CLIENT reference methods +-- -- For each DCS Unit having skill level Player or Client, a CLIENT wrapper object (instance) will be created within the _@{DATABASE} object. -- This is done at the beginning of the mission (when the mission starts). -- @@ -32,13 +48,9 @@ -- * @{#CLIENT.Find}(): Find a CLIENT instance from the _DATABASE object using a DCS Unit object. -- * @{#CLIENT.FindByName}(): Find a CLIENT instance from the _DATABASE object using a DCS Unit name. -- --- IMPORTANT: ONE SHOULD NEVER SANATIZE these CLIENT OBJECT REFERENCES! (make the CLIENT object references nil). +-- **IMPORTANT: ONE SHOULD NEVER SANATIZE these CLIENT OBJECT REFERENCES! (make the CLIENT object references nil).** -- --- @module Client - ---- The CLIENT class --- @type CLIENT --- @extends Wrapper.Unit#UNIT +-- @field #CLIENT CLIENT = { ONBOARDSIDE = { NONE = 0, diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index b81cda295..8d3a037a1 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1,29 +1,48 @@ ---- This module contains the CONTROLLABLE class. +--- **Wrapper** -- CONTROLLABLE is an intermediate class wrapping Group and Unit classes "controllers". -- --- 1) @{Controllable#CONTROLLABLE} class, extends @{Positionable#POSITIONABLE} --- =========================================================== --- The @{Controllable#CONTROLLABLE} class is a wrapper class to handle the DCS Controllable objects: +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- === +-- +-- @module Controllable + + + +--- @type CONTROLLABLE +-- @extends Wrapper.Positionable#POSITIONABLE +-- @field Dcs.DCSWrapper.Controllable#Controllable DCSControllable The DCS controllable class. +-- @field #string ControllableName The name of the controllable. + + + +--- # CONTROLLABLE class, extends @{Positionable#POSITIONABLE} +-- +-- CONTROLLABLE is a wrapper class to handle the "DCS Controllable objects", which are Groups and Units: -- -- * Support all DCS Controllable APIs. -- * Enhance with Controllable specific APIs not in the DCS Controllable API set. -- * Handle local Controllable Controller. -- * Manage the "state" of the DCS Controllable. -- --- 1.1) CONTROLLABLE constructor --- ----------------------------- +-- ## CONTROLLABLE constructor +-- -- The CONTROLLABLE class provides the following functions to construct a CONTROLLABLE instance: -- -- * @{#CONTROLLABLE.New}(): Create a CONTROLLABLE instance. -- --- 1.2) CONTROLLABLE task methods --- ------------------------------ +-- ## CONTROLLABLE Task methods +-- -- Several controllable task methods are available that help you to prepare tasks. -- These methods return a string consisting of the task description, which can then be given to either a @{Controllable#CONTROLLABLE.PushTask} or @{Controllable#SetTask} method to assign the task to the CONTROLLABLE. -- Tasks are specific for the category of the CONTROLLABLE, more specific, for AIR, GROUND or AIR and GROUND. -- Each task description where applicable indicates for which controllable category the task is valid. -- There are 2 main subdivisions of tasks: Assigned tasks and EnRoute tasks. -- --- ### 1.2.1) Assigned task methods +-- ### Task assignment -- -- Assigned task methods make the controllable execute the task where the location of the (possible) targets of the task are known before being detected. -- This is different from the EnRoute tasks, where the targets of the task need to be detected before the task can be executed. @@ -54,19 +73,20 @@ -- * @{#CONTROLLABLE.TaskRouteToZone}: (AIR + GROUND) Route the controllable to a given zone. -- * @{#CONTROLLABLE.TaskReturnToBase}: (AIR) Route the controllable to an airbase. -- --- ### 1.2.2) EnRoute task methods +-- ### EnRoute assignment -- -- EnRoute tasks require the targets of the task need to be detected by the controllable (using its sensors) before the task can be executed: -- -- * @{#CONTROLLABLE.EnRouteTaskAWACS}: (AIR) Aircraft will act as an AWACS for friendly units (will provide them with information about contacts). No parameters. -- * @{#CONTROLLABLE.EnRouteTaskEngageControllable}: (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now; it just allows the unit/controllable to engage the target controllable as well as other assigned targets. -- * @{#CONTROLLABLE.EnRouteTaskEngageTargets}: (AIR) Engaging targets of defined types. +-- * @{#CONTROLLABLE.EnRouteTaskEngageTargetsInZone}: (AIR) Engaging a targets of defined types at circle-shaped zone. -- * @{#CONTROLLABLE.EnRouteTaskEWR}: (AIR) Attack the Unit. -- * @{#CONTROLLABLE.EnRouteTaskFAC}: (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose a targets (enemy ground controllable) around as well as other assigned targets. -- * @{#CONTROLLABLE.EnRouteTaskFAC_EngageControllable}: (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose the target (enemy ground controllable) as well as other assigned targets. -- * @{#CONTROLLABLE.EnRouteTaskTanker}: (AIR) Aircraft will act as a tanker for friendly units. No parameters. -- --- ### 1.2.3) Preparation task methods +-- ### Task preparation -- -- There are certain task methods that allow to tailor the task behaviour: -- @@ -75,24 +95,48 @@ -- * @{#CONTROLLABLE.TaskCondition}: Return a condition section for a controlled task. -- * @{#CONTROLLABLE.TaskControlled}: Return a Controlled Task taking a Task and a TaskCondition. -- --- ### 1.2.4) Obtain the mission from controllable templates +-- ### Call a function as a Task +-- +-- A function can be called which is part of a Task. The method @{#CONTROLLABLE.TaskFunction}() prepares +-- a Task that can call a GLOBAL function from within the Controller execution. +-- This method can also be used to **embed a function call when a certain waypoint has been reached**. +-- See below the **Tasks at Waypoints** section. +-- +-- Demonstration Mission: [GRP-502 - Route at waypoint to random point](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/GRP - Group Commands/GRP-502 - Route at waypoint to random point) +-- +-- ### Tasks at Waypoints +-- +-- Special Task methods are available to set tasks at certain waypoints. +-- The method @{#CONTROLLABLE.SetTaskAtWaypoint}() helps preparing a Route, embedding a Task at the Waypoint of the Route. +-- +-- This creates a Task element, with an action to call a function as part of a Wrapped Task. +-- +-- ### Obtain the mission from controllable templates -- -- Controllable templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a controllable and assign it to another: -- -- * @{#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. -- --- 1.3) CONTROLLABLE Command methods --- -------------------------- +-- ## CONTROLLABLE Command methods +-- -- Controllable **command methods** prepare the execution of commands using the @{#CONTROLLABLE.SetCommand} method: -- -- * @{#CONTROLLABLE.CommandDoScript}: Do Script command. -- * @{#CONTROLLABLE.CommandSwitchWayPoint}: Perform a switch waypoint command. -- --- 1.4) CONTROLLABLE Option methods --- ------------------------- +-- ## Routing of Controllables +-- +-- Different routing methods exist to route GROUPs and UNITs to different locations: +-- +-- * @{#CONTROLLABLE.Route}(): Make the Controllable to follow a given route. +-- * @{#CONTROLLABLE.RouteGroundTo}(): Make the GROUND Controllable to drive towards a specific coordinate. +-- * @{#CONTROLLABLE.RouteAirTo}(): Make the AIR Controllable to fly towards a specific coordinate. +-- +-- ## Option methods +-- -- Controllable **Option methods** change the behaviour of the Controllable while being alive. -- --- ### 1.4.1) Rule of Engagement: +-- ### Rule of Engagement: -- -- * @{#CONTROLLABLE.OptionROEWeaponFree} -- * @{#CONTROLLABLE.OptionROEOpenFire} @@ -106,7 +150,7 @@ -- * @{#CONTROLLABLE.OptionROEReturnFirePossible} -- * @{#CONTROLLABLE.OptionROEEvadeFirePossible} -- --- ### 1.4.2) Rule on thread: +-- ### Rule on thread: -- -- * @{#CONTROLLABLE.OptionROTNoReaction} -- * @{#CONTROLLABLE.OptionROTPassiveDefense} @@ -120,15 +164,7 @@ -- * @{#CONTROLLABLE.OptionROTEvadeFirePossible} -- * @{#CONTROLLABLE.OptionROTVerticalPossible} -- --- === --- --- @module Controllable - ---- The CONTROLLABLE class --- @type CONTROLLABLE --- @extends Wrapper.Positionable#POSITIONABLE --- @field Dcs.DCSWrapper.Controllable#Controllable DCSControllable The DCS controllable class. --- @field #string ControllableName The name of the controllable. +-- @field #CONTROLLABLE CONTROLLABLE = { ClassName = "CONTROLLABLE", ControllableName = "", @@ -140,7 +176,7 @@ CONTROLLABLE = { -- @param Dcs.DCSWrapper.Controllable#Controllable ControllableName The DCS Controllable name -- @return #CONTROLLABLE self function CONTROLLABLE:New( ControllableName ) - local self = BASE:Inherit( self, POSITIONABLE:New( ControllableName ) ) + local self = BASE:Inherit( self, POSITIONABLE:New( ControllableName ) ) -- #CONTROLLABLE self:F2( ControllableName ) self.ControllableName = ControllableName @@ -154,12 +190,10 @@ end -- @param #CONTROLLABLE self -- @return Dcs.DCSController#Controller function CONTROLLABLE:_GetController() - self:F2( { self.ControllableName } ) local DCSControllable = self:GetDCSObject() if DCSControllable then local ControllableController = DCSControllable:getController() - self:T3( ControllableController ) return ControllableController end @@ -218,6 +252,46 @@ function CONTROLLABLE:GetLife() return nil end +--- Returns the initial health. +-- @param #CONTROLLABLE self +-- @return #number The controllable health value (unit or group average). +-- @return #nil The controllable is not existing or alive. +function CONTROLLABLE:GetLife0() + self:F2( self.ControllableName ) + + local DCSControllable = self:GetDCSObject() + + if DCSControllable then + local UnitLife = 0 + local Units = self:GetUnits() + if #Units == 1 then + local Unit = Units[1] -- Wrapper.Unit#UNIT + UnitLife = Unit:GetLife0() + else + local UnitLifeTotal = 0 + for UnitID, Unit in pairs( Units ) do + local Unit = Unit -- Wrapper.Unit#UNIT + UnitLifeTotal = UnitLifeTotal + Unit:GetLife0() + end + UnitLife = UnitLifeTotal / #Units + end + return UnitLife + end + + return nil +end + +--- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. +-- This method returns nil to ensure polymorphic behaviour! This method needs to be overridden by GROUP or UNIT. +-- @param #CONTROLLABLE self +-- @return #nil The CONTROLLABLE is not existing or alive. +function CONTROLLABLE:GetFuel() + self:F( self.ControllableName ) + + return nil +end + + -- Tasks @@ -288,23 +362,26 @@ end -- @param #CONTROLLABLE self -- @return Wrapper.Controllable#CONTROLLABLE self function CONTROLLABLE:SetTask( DCSTask, WaitTime ) - self:F2( { DCSTask } ) + self:F2( { DCSTask = DCSTask } ) local DCSControllable = self:GetDCSObject() if DCSControllable then - local Controller = self:_GetController() - self:T3( Controller ) -- When a controllable SPAWNs, it takes about a second to get the controllable in the simulator. Setting tasks to unspawned controllables provides unexpected results. -- Therefore we schedule the functions to set the mission and options for the Controllable. -- Controller.setTask( Controller, DCSTask ) - if not WaitTime then + local function SetTask( Controller, DCSTask ) + local Controller = self:_GetController() Controller:setTask( DCSTask ) + end + + if not WaitTime or WaitTime == 0 then + SetTask( DCSTask ) else - self.TaskScheduler:Schedule( Controller, Controller.setTask, { DCSTask }, WaitTime ) + self.TaskScheduler:Schedule( self, SetTask, { DCSTask }, WaitTime ) end return self @@ -313,6 +390,24 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime ) return nil end +--- Checking the Task Queue of the controllable. Returns false if no task is on the queue. true if there is a task. +-- @param #CONTROLLABLE self +-- @return Wrapper.Controllable#CONTROLLABLE self +function CONTROLLABLE:HasTask() --R2.2 + + local HasTaskResult = false + + local DCSControllable = self:GetDCSObject() + + if DCSControllable then + + local Controller = self:_GetController() + HasTaskResult = Controller:hasTask() + end + + return HasTaskResult +end + --- Return a condition section for a controlled task. -- @param #CONTROLLABLE self @@ -377,7 +472,7 @@ function CONTROLLABLE:TaskCombo( DCSTasks ) } for TaskID, Task in ipairs( DCSTasks ) do - self:E( Task ) + self:T( Task ) end self:T3( { DCSTaskCombo } ) @@ -392,11 +487,11 @@ function CONTROLLABLE:TaskWrappedAction( DCSCommand, Index ) self:F2( { DCSCommand } ) local DCSTaskWrappedAction - + DCSTaskWrappedAction = { id = "WrappedAction", enabled = true, - number = Index, + number = Index or 1, auto = false, params = { action = DCSCommand, @@ -407,6 +502,22 @@ function CONTROLLABLE:TaskWrappedAction( DCSCommand, Index ) return DCSTaskWrappedAction end +--- Set a Task at a Waypoint using a Route list. +-- @param #CONTROLLABLE self +-- @param #table Waypoint The Waypoint! +-- @param Dcs.DCSTasking.Task#Task Task The Task structure to be executed! +-- @return Dcs.DCSTasking.Task#Task +function CONTROLLABLE:SetTaskWaypoint( Waypoint, Task ) + + Waypoint.task = self:TaskCombo( { Task } ) + + self:T3( { Waypoint.task } ) + return Waypoint.task +end + + + + --- Executes a command action -- @param #CONTROLLABLE self -- @param Dcs.DCSCommand#Command DCSCommand @@ -576,7 +687,7 @@ function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, Att } } - self:E( DCSTask ) + self:T3( DCSTask ) return DCSTask end @@ -585,36 +696,64 @@ end --- (AIR) Delivering weapon at the point on the ground. -- @param #CONTROLLABLE self -- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. +-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. -- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) Desired quantity of passes. The parameter is not the same in AttackGroup and AttackUnit tasks. +-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. +-- @param #number Altitude (optional) The altitude from where to attack. +-- @param #number WeaponType (optional) The WeaponType. -- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskBombing( Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack ) - self:F2( { self.ControllableName, Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack } ) - --- Bombing = { --- id = 'Bombing', --- params = { --- point = Vec2, --- weaponType = number, --- expend = enum AI.Task.WeaponExpend, --- attackQty = number, --- direction = Azimuth, --- controllableAttack = boolean, --- } --- } +function CONTROLLABLE:TaskBombing( Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType ) + self:F2( { self.ControllableName, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType } ) local DCSTask - DCSTask = { id = 'Bombing', + DCSTask = { + id = 'Bombing', params = { - point = Vec2, - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - direction = Direction, - controllableAttack = ControllableAttack, + point = Vec2, + groupAttack = GroupAttack or false, + expend = WeaponExpend or "Auto", + attackQtyLimit = AttackQty and true or false, + attackQty = AttackQty, + directionEnabled = Direction and true or false, + direction = Direction, + altitudeEnabled = Altitude and true or false, + altitude = Altitude or 30, + weaponType = WeaponType, + }, + }, + + self:T3( { DCSTask } ) + return DCSTask +end + +--- (AIR) Attacking the map object (building, structure, e.t.c). +-- @param #CONTROLLABLE self +-- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. +-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. +-- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. +-- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param #number Altitude (optional) The altitude from where to attack. +-- @param #number WeaponType (optional) The WeaponType. +-- @return Dcs.DCSTasking.Task#Task The DCS task structure. +function CONTROLLABLE:TaskAttackMapObject( Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType ) + self:F2( { self.ControllableName, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType } ) + + local DCSTask + DCSTask = { + id = 'AttackMapObject', + params = { + point = Vec2, + groupAttack = GroupAttack or false, + expend = WeaponExpend or "Auto", + attackQtyLimit = AttackQty and true or false, + attackQty = AttackQty, + directionEnabled = Direction and true or false, + direction = Direction, + altitudeEnabled = Altitude and true or false, + altitude = Altitude or 30, + weaponType = WeaponType, }, }, @@ -622,6 +761,7 @@ function CONTROLLABLE:TaskBombing( Vec2, WeaponType, WeaponExpend, AttackQty, Di return DCSTask end + --- (AIR) Orbit at a specified position at a specified alititude during a specified duration with a specified speed. -- @param #CONTROLLABLE self -- @param Dcs.DCSTypes#Vec2 Point The point to hold the position. @@ -700,45 +840,6 @@ end ---- (AIR) Attacking the map object (building, structure, e.t.c). --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the point the map object is closest to. The distance between the point and the map object must not be greater than 2000 meters. Object id is not used here because Mission Editor doesn't support map object identificators. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackMapObject( Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack ) - self:F2( { self.ControllableName, Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack } ) - --- AttackMapObject = { --- id = 'AttackMapObject', --- params = { --- point = Vec2, --- weaponType = number, --- expend = enum AI.Task.WeaponExpend, --- attackQty = number, --- direction = Azimuth, --- controllableAttack = boolean, --- } --- } - - local DCSTask - DCSTask = { id = 'AttackMapObject', - params = { - point = Vec2, - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - direction = Direction, - controllableAttack = ControllableAttack, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end --- (AIR) Delivering weapon on the runway. @@ -1098,7 +1199,7 @@ end -- @param Dcs.DCSTypes#AttributeNameArray TargetTypes Array of target categories allowed to engage. -- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. -- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskEngageTargets( Vec2, Radius, TargetTypes, Priority ) +function CONTROLLABLE:EnRouteTaskEngageTargetsInZone( Vec2, Radius, TargetTypes, Priority ) self:F2( { self.ControllableName, Vec2, Radius, TargetTypes, Priority } ) -- EngageTargetsInZone = { @@ -1433,6 +1534,84 @@ function CONTROLLABLE:TaskEmbarkToTransport( Point, Radius ) return DCSTask end +--- This creates a Task element, with an action to call a function as part of a Wrapped Task. +-- This Task can then be embedded at a Waypoint by calling the method @{#CONTROLLABLE.SetTaskAtWaypoint}. +-- @param #CONTROLLABLE self +-- @param #string FunctionString The function name embedded as a string that will be called. +-- @param ... The variable arguments passed to the function when called! These arguments can be of any type! +-- @return #CONTROLLABLE +-- @usage +-- +-- local ZoneList = { +-- ZONE:New( "ZONE1" ), +-- ZONE:New( "ZONE2" ), +-- ZONE:New( "ZONE3" ), +-- ZONE:New( "ZONE4" ), +-- ZONE:New( "ZONE5" ) +-- } +-- +-- GroundGroup = GROUP:FindByName( "Vehicle" ) +-- +-- --- @param Wrapper.Group#GROUP GroundGroup +-- function RouteToZone( Vehicle, ZoneRoute ) +-- +-- local Route = {} +-- +-- Vehicle:E( { ZoneRoute = ZoneRoute } ) +-- +-- Vehicle:MessageToAll( "Moving to zone " .. ZoneRoute:GetName(), 10 ) +-- +-- -- Get the current coordinate of the Vehicle +-- local FromCoord = Vehicle:GetCoordinate() +-- +-- -- Select a random Zone and get the Coordinate of the new Zone. +-- local RandomZone = ZoneList[ math.random( 1, #ZoneList ) ] -- Core.Zone#ZONE +-- local ToCoord = RandomZone:GetCoordinate() +-- +-- -- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task +-- Route[#Route+1] = FromCoord:WaypointGround( 72 ) +-- Route[#Route+1] = ToCoord:WaypointGround( 60, "Vee" ) +-- +-- local TaskRouteToZone = Vehicle:TaskFunction( "RouteToZone", RandomZone ) +-- +-- Vehicle:SetTaskAtWaypoint( Route, #Route, TaskRouteToZone ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. +-- +-- Vehicle:Route( Route, math.random( 10, 20 ) ) -- Move after a random seconds to the Route. See the Route method for details. +-- +-- end +-- +-- RouteToZone( GroundGroup, ZoneList[1] ) +-- +function CONTROLLABLE:TaskFunction( FunctionString, ... ) + self:F2( { FunctionString, arg } ) + + local DCSTask + + local DCSScript = {} + DCSScript[#DCSScript+1] = "local MissionControllable = GROUP:Find( ... ) " + + if arg and arg.n > 0 then + local ArgumentKey = '_' .. tostring( arg ):match("table: (.*)") + self:SetState( self, ArgumentKey, arg ) + DCSScript[#DCSScript+1] = "local Arguments = MissionControllable:GetState( MissionControllable, '" .. ArgumentKey .. "' ) " + --DCSScript[#DCSScript+1] = "MissionControllable:ClearState( MissionControllable, '" .. ArgumentKey .. "' ) " + DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable, unpack( Arguments ) )" + else + DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable )" + end + + DCSTask = self:TaskWrappedAction( + self:CommandDoScript( + table.concat( DCSScript ) + ) + ) + + self:T( DCSTask ) + + return DCSTask + +end + --- (AIR + GROUND) Return a mission task from a mission template. @@ -1573,19 +1752,16 @@ end --- Make the controllable to follow a given route. -- @param #CONTROLLABLE self --- @param #table GoPoints A table of Route Points. --- @return #CONTROLLABLE self -function CONTROLLABLE:Route( GoPoints ) - self:F2( GoPoints ) +-- @param #table Route A table of Route Points. +-- @param #number DelaySeconds Wait for the specified seconds before executing the Route. +-- @return #CONTROLLABLE The CONTROLLABLE. +function CONTROLLABLE:Route( Route, DelaySeconds ) + self:F2( Route ) local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Points = routines.utils.deepCopy( GoPoints ) - local MissionTask = { id = 'Mission', params = { route = { points = Points, }, }, } - local Controller = self:_GetController() - --Controller.setTask( Controller, MissionTask ) - self.TaskScheduler:Schedule( Controller, Controller.setTask, { MissionTask }, 1 ) + local RouteTask = self:TaskRoute( Route ) -- Create a RouteTask, that will route the CONTROLLABLE to the Route. + self:SetTask( RouteTask, DelaySeconds or 1 ) -- Execute the RouteTask after the specified seconds (default is 1). return self end @@ -1593,6 +1769,47 @@ function CONTROLLABLE:Route( GoPoints ) end +--- Make the GROUND Controllable to drive towards a specific point. +-- @param #CONTROLLABLE self +-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. +-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. +-- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right". +-- @param #number DelaySeconds Wait for the specified seconds before executing the Route. +-- @return #CONTROLLABLE The CONTROLLABLE. +function CONTROLLABLE:RouteGroundTo( ToCoordinate, Speed, Formation, DelaySeconds ) + + local FromCoordinate = self:GetCoordinate() + + local FromWP = FromCoordinate:WaypointGround() + local ToWP = ToCoordinate:WaypointGround( Speed, Formation ) + + self:Route( { FromWP, ToWP }, DelaySeconds ) + + return self +end + + +--- Make the AIR Controllable fly towards a specific point. +-- @param #CONTROLLABLE self +-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. +-- @param Core.Point#COORDINATE.RoutePointAltType AltType The altitude type. +-- @param Core.Point#COORDINATE.RoutePointType Type The route point type. +-- @param Core.Point#COORDINATE.RoutePointAction Action The route point action. +-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. +-- @param #number DelaySeconds Wait for the specified seconds before executing the Route. +-- @return #CONTROLLABLE The CONTROLLABLE. +function CONTROLLABLE:RouteAirTo( ToCoordinate, AltType, Type, Action, Speed, DelaySeconds ) + + local FromCoordinate = self:GetCoordinate() + local FromWP = FromCoordinate:WaypointAir() + + local ToWP = ToCoordinate:WaypointAir( AltType, Type, Action, Speed ) + + self:Route( { FromWP, ToWP }, DelaySeconds ) + + return self +end + --- (AIR + GROUND) Route the controllable to a given zone. -- The controllable final destination point can be randomized. @@ -1657,6 +1874,59 @@ function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation ) return nil end +--- (GROUND) Route the controllable to a given Vec2. +-- A speed can be given in km/h. +-- A given formation can be given. +-- @param #CONTROLLABLE self +-- @param #Vec2 Vec2 The Vec2 where to route to. +-- @param #number Speed The speed. +-- @param Base#FORMATION Formation The formation string. +function CONTROLLABLE:TaskRouteToVec2( Vec2, Speed, Formation ) + + local DCSControllable = self:GetDCSObject() + + if DCSControllable then + + local ControllablePoint = self:GetVec2() + + local PointFrom = {} + PointFrom.x = ControllablePoint.x + PointFrom.y = ControllablePoint.y + PointFrom.type = "Turning Point" + PointFrom.action = Formation or "Cone" + PointFrom.speed = 20 / 1.6 + + + local PointTo = {} + + PointTo.x = Vec2.x + PointTo.y = Vec2.y + PointTo.type = "Turning Point" + + if Formation then + PointTo.action = Formation + else + PointTo.action = "Cone" + end + + if Speed then + PointTo.speed = Speed + else + PointTo.speed = 60 / 3.6 + end + + local Points = { PointFrom, PointTo } + + self:T3( Points ) + + self:Route( Points ) + + return self + end + + return nil +end + -- Commands @@ -1774,6 +2044,7 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad local DetectionRWR = ( DetectRWR and DetectRWR == true ) and Controller.Detection.RWR or nil local DetectionDLINK = ( DetectDLINK and DetectDLINK == true ) and Controller.Detection.DLINK or nil + self:T( { DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK } ) return self:_GetController():getDetectedTargets( DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK ) end @@ -1781,21 +2052,25 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad return nil end -function CONTROLLABLE:IsTargetDetected( DCSObject ) +function CONTROLLABLE:IsTargetDetected( DCSObject, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK ) self:F2( self.ControllableName ) local DCSControllable = self:GetDCSObject() + if DCSControllable then + local DetectionVisual = ( DetectVisual and DetectVisual == true ) and Controller.Detection.VISUAL or nil + local DetectionOptical = ( DetectOptical and DetectOptical == true ) and Controller.Detection.OPTICAL or nil + local DetectionRadar = ( DetectRadar and DetectRadar == true ) and Controller.Detection.RADAR or nil + local DetectionIRST = ( DetectIRST and DetectIRST == true ) and Controller.Detection.IRST or nil + local DetectionRWR = ( DetectRWR and DetectRWR == true ) and Controller.Detection.RWR or nil + local DetectionDLINK = ( DetectDLINK and DetectDLINK == true ) and Controller.Detection.DLINK or nil + + local Controller = self:_GetController() + local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity - = self:_GetController().isTargetDetected( self:_GetController(), DCSObject, - Controller.Detection.VISUAL, - Controller.Detection.OPTIC, - Controller.Detection.RADAR, - Controller.Detection.IRST, - Controller.Detection.RWR, - Controller.Detection.DLINK - ) + = Controller:isTargetDetected( DCSObject, DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK ) + return TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity end @@ -2123,6 +2398,57 @@ function CONTROLLABLE:OptionROTVertical() return nil end + +--- Set RTB on bingo fuel. +-- @param #CONTROLLABLE self +-- @param #boolean RTB true if RTB on bingo fuel (default), false if no RTB on bingo fuel. +-- Warning! When you switch this option off, the airborne group will continue to fly until all fuel has been consumed, and will crash. +-- @return #CONTROLLABLE self +function CONTROLLABLE:OptionRTBBingoFuel( RTB ) --R2.2 + self:F2( { self.ControllableName } ) + + RTB = RTB or true + + local DCSControllable = self:GetDCSObject() + if DCSControllable then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.RTB_ON_BINGO, RTB ) + end + + return self + end + + return nil +end + + +--- Set RTB on ammo. +-- @param #CONTROLLABLE self +-- @param #boolean WeaponsFlag Weapons.flag enumerator. +-- @return #CONTROLLABLE self +function CONTROLLABLE:OptionRTBAmmo( WeaponsFlag ) + self:F2( { self.ControllableName } ) + + local DCSControllable = self:GetDCSObject() + if DCSControllable then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.GROUND.id.RTB_ON_OUT_OF_AMMO, WeaponsFlag ) + end + + return self + end + + return nil +end + + + + + --- Retrieve the controllable mission and allow to place function hooks within the mission waypoint plan. -- Use the method @{Controllable#CONTROLLABLE:WayPointFunction} to define the hook functions for specific waypoints. -- Use the method @{Controllable@CONTROLLABLE:WayPointExecute) to start the execution of the new mission plan. @@ -2165,37 +2491,11 @@ function CONTROLLABLE:WayPointFunction( WayPoint, WayPointIndex, WayPointFunctio self:F2( { WayPoint, WayPointIndex, WayPointFunction } ) table.insert( self.WayPoints[WayPoint].task.params.tasks, WayPointIndex ) - self.WayPoints[WayPoint].task.params.tasks[WayPointIndex] = self:TaskFunction( WayPoint, WayPointIndex, WayPointFunction, arg ) + self.WayPoints[WayPoint].task.params.tasks[WayPointIndex] = self:TaskFunction( WayPointFunction, arg ) return self end -function CONTROLLABLE:TaskFunction( WayPoint, WayPointIndex, FunctionString, FunctionArguments ) - self:F2( { WayPoint, WayPointIndex, FunctionString, FunctionArguments } ) - - local DCSTask - - local DCSScript = {} - DCSScript[#DCSScript+1] = "local MissionControllable = GROUP:Find( ... ) " - - if FunctionArguments and #FunctionArguments > 0 then - DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable, " .. table.concat( FunctionArguments, "," ) .. ")" - else - DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable )" - end - - DCSTask = self:TaskWrappedAction( - self:CommandDoScript( - table.concat( DCSScript ) - ), WayPointIndex - ) - - self:T3( DCSTask ) - - return DCSTask - -end - --- Executes the WayPoint plan. -- The function gets a WayPoint parameter, that you can use to restart the mission at a specific WayPoint. -- Note that when the WayPoint parameter is used, the new start mission waypoint of the controllable will be 1! @@ -2222,4 +2522,32 @@ function CONTROLLABLE:WayPointExecute( WayPoint, WaitTime ) return self end +--- Returns if the Controllable contains AirPlanes. +-- @param #CONTROLLABLE self +-- @return #boolean true if Controllable contains AirPlanes. +function CONTROLLABLE:IsAirPlane() + self:F2() + + local DCSObject = self:GetDCSObject() + + if DCSObject then + local Category = DCSObject:getDesc().category + return Category == Unit.Category.AIRPLANE + end + + return nil +end + +function CONTROLLABLE:GetSize() + + local DCSObject = self:GetDCSObject() + + if DCSObject then + return 1 + else + return 0 + end +end + + -- Message APIs \ No newline at end of file diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index c905231b4..886da067d 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1,4 +1,4 @@ ---- **Wrapper** -- GROUP is a wrapper class for the DCS Class Group. +--- **Wrapper** -- GROUP wraps the DCS Class Group objects. -- -- === -- @@ -15,45 +15,22 @@ -- -- ==== -- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-26: GROUP:**RouteRTB( RTBAirbase, Speed )** added. --- --- 2017-03-07: GROUP:**HandleEvent( Event, EventFunction )** added. --- 2017-03-07: GROUP:**UnHandleEvent( Event )** added. --- --- 2017-01-24: GROUP:**SetAIOnOff( AIOnOff )** added. --- --- 2017-01-24: GROUP:**SetAIOn()** added. --- --- 2017-01-24: GROUP:**SetAIOff()** added. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** +-- ### Author: **Sven Van de Velde (FlightControl)** -- -- ### Contributions: -- -- * [**Entropy**](https://forums.eagle.ru/member.php?u=111471), **Afinegan**: Came up with the requirement for AIOnOff(). -- --- ### Authors: --- --- * **FlightControl**: Design & Programming +-- ==== -- -- @module Group --- @author FlightControl + --- @type GROUP -- @extends Wrapper.Controllable#CONTROLLABLE -- @field #string GroupName The name of the group. + --- -- # GROUP class, extends @{Controllable#CONTROLLABLE} -- @@ -114,6 +91,25 @@ GROUP = { ClassName = "GROUP", } + +--- Enumerator for location at airbases +-- @type GROUP.Takeoff +GROUP.Takeoff = { + Air = 1, + Runway = 2, + Hot = 3, + Cold = 4, +} + +GROUPTEMPLATE = {} + +GROUPTEMPLATE.Takeoff = { + [GROUP.Takeoff.Air] = "Turning Point", + [GROUP.Takeoff.Runway] = "TakeOff", + [GROUP.Takeoff.Hot] = "TakeOffParkingHot", + [GROUP.Takeoff.Cold] = "TakeOffParking", +} + --- Create a new GROUP from a DCSGroup -- @param #GROUP self -- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name @@ -319,6 +315,7 @@ function GROUP:GetUnit( UnitNumber ) local DCSGroup = self:GetDCSObject() if DCSGroup then + local DCSUnit = DCSGroup:getUnit( UnitNumber ) local UnitFound = UNIT:Find( DCSGroup:getUnit( UnitNumber ) ) self:T2( UnitFound ) return UnitFound @@ -356,8 +353,13 @@ function GROUP:GetSize() if DCSGroup then local GroupSize = DCSGroup:getSize() - self:T3( GroupSize ) - return GroupSize + + if GroupSize then + self:T3( GroupSize ) + return GroupSize + else + return 0 + end end return nil @@ -425,6 +427,24 @@ function GROUP:GetTypeName() return nil end +--- Gets the player name of the group. +-- @param #GROUP self +-- @return #string The player name of the group. +function GROUP:GetPlayerName() + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSObject() + + if DCSGroup then + local PlayerName = DCSGroup:getUnit(1):getPlayerName() + self:T3( PlayerName ) + return( PlayerName ) + end + + return nil +end + + --- Gets the CallSign of the first DCS Unit of the DCS Group. -- @param #GROUP self -- @return #string The CallSign of the first DCS Unit of the DCS Group. @@ -484,6 +504,24 @@ function GROUP:GetPointVec2() return nil end +--- Returns a COORDINATE object indicating the point of the first UNIT of the GROUP within the mission. +-- @param Wrapper.Group#GROUP self +-- @return Core.Point#COORDINATE The COORDINATE of the GROUP. +function GROUP:GetCoordinate() + self:F2( self.PositionableName ) + + local FirstUnit = self:GetUnit(1) + + if FirstUnit then + local FirstUnitCoordinate = FirstUnit:GetCoordinate() + self:T3(FirstUnitCoordinate) + return FirstUnitCoordinate + end + + return nil +end + + --- Returns a random @{DCSTypes#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. -- @param #GROUP self -- @param #number Radius @@ -526,6 +564,32 @@ function GROUP:GetHeading() end +--- Returns relative amount of fuel (from 0.0 to 1.0) the group has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. +-- @param #GROUP self +-- @return #number The relative amount of fuel (from 0.0 to 1.0). +-- @return #nil The GROUP is not existing or alive. +function GROUP:GetFuel() + self:F( self.ControllableName ) + + local DCSControllable = self:GetDCSObject() + + if DCSControllable then + local GroupSize = self:GetSize() + local TotalFuel = 0 + for UnitID, UnitData in pairs( self:GetUnits() ) do + local Unit = UnitData -- Wrapper.Unit#UNIT + local UnitFuel = Unit:GetFuel() + self:F( { Fuel = UnitFuel } ) + TotalFuel = TotalFuel + UnitFuel + end + local GroupFuel = TotalFuel / GroupSize + return GroupFuel + end + + return 0 +end + + do -- Is Zone methods --- Returns true if all units of the group are within a @{Zone}. @@ -535,6 +599,8 @@ do -- Is Zone methods function GROUP:IsCompletelyInZone( Zone ) self:F2( { self.GroupName, Zone } ) + if not self:IsAlive() then return false end + for UnitID, UnitData in pairs( self:GetUnits() ) do local Unit = UnitData -- Wrapper.Unit#UNIT if Zone:IsVec3InZone( Unit:GetVec3() ) then @@ -549,27 +615,40 @@ end --- Returns true if some units of the group are within a @{Zone}. -- @param #GROUP self -- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE} +-- @return #boolean Returns true if the Group is partially within the @{Zone#ZONE_BASE} function GROUP:IsPartlyInZone( Zone ) self:F2( { self.GroupName, Zone } ) + local IsOneUnitInZone = false + local IsOneUnitOutsideZone = false + + if not self:IsAlive() then return false end + for UnitID, UnitData in pairs( self:GetUnits() ) do local Unit = UnitData -- Wrapper.Unit#UNIT if Zone:IsVec3InZone( Unit:GetVec3() ) then - return true + IsOneUnitInZone = true + else + IsOneUnitOutsideZone = true end end - return false + if IsOneUnitInZone and IsOneUnitOutsideZone then + return true + else + return false + end end --- Returns true if none of the group units of the group are within a @{Zone}. -- @param #GROUP self -- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE} +-- @return #boolean Returns true if the Group is not within the @{Zone#ZONE_BASE} function GROUP:IsNotInZone( Zone ) self:F2( { self.GroupName, Zone } ) + if not self:IsAlive() then return true end + for UnitID, UnitData in pairs( self:GetUnits() ) do local Unit = UnitData -- Wrapper.Unit#UNIT if Zone:IsVec3InZone( Unit:GetVec3() ) then @@ -580,6 +659,26 @@ function GROUP:IsNotInZone( Zone ) return true end +--- Returns the number of UNITs that are in the @{Zone} +-- @param #GROUP self +-- @param Core.Zone#ZONE_BASE Zone The zone to test. +-- @return #number The number of UNITs that are in the @{Zone} +function GROUP:CountInZone( Zone ) + self:F2( {self.GroupName, Zone} ) + local Count = 0 + + if not self:IsAlive() then return Count end + + for UnitID, UnitData in pairs( self:GetUnits() ) do + local Unit = UnitData -- Wrapper.Unit#UNIT + if Zone:IsVec3InZone( Unit:GetVec3() ) then + Count = Count + 1 + end + end + + return Count +end + --- Returns if the group is of an air category. -- If the group is a helicopter or a plane, then this method will return true, otherwise false. -- @param #GROUP self @@ -803,29 +902,35 @@ end -- @param #table Template The template of the Group retrieved with GROUP:GetTemplate() function GROUP:Respawn( Template ) - local Vec3 = self:GetVec3() - Template.x = Vec3.x - Template.y = Vec3.z - --Template.x = nil - --Template.y = nil - - self:E( #Template.units ) - for UnitID, UnitData in pairs( self:GetUnits() ) do - local GroupUnit = UnitData -- Wrapper.Unit#UNIT - self:E( GroupUnit:GetName() ) - if GroupUnit:IsAlive() then - local GroupUnitVec3 = GroupUnit:GetVec3() - local GroupUnitHeading = GroupUnit:GetHeading() - Template.units[UnitID].alt = GroupUnitVec3.y - Template.units[UnitID].x = GroupUnitVec3.x - Template.units[UnitID].y = GroupUnitVec3.z - Template.units[UnitID].heading = GroupUnitHeading - self:E( { UnitID, Template.units[UnitID], Template.units[UnitID] } ) + if self:IsAlive() then + local Vec3 = self:GetVec3() + Template.x = Vec3.x + Template.y = Vec3.z + --Template.x = nil + --Template.y = nil + + self:E( #Template.units ) + for UnitID, UnitData in pairs( self:GetUnits() ) do + local GroupUnit = UnitData -- Wrapper.Unit#UNIT + self:E( GroupUnit:GetName() ) + if GroupUnit:IsAlive() then + local GroupUnitVec3 = GroupUnit:GetVec3() + local GroupUnitHeading = GroupUnit:GetHeading() + Template.units[UnitID].alt = GroupUnitVec3.y + Template.units[UnitID].x = GroupUnitVec3.x + Template.units[UnitID].y = GroupUnitVec3.z + Template.units[UnitID].heading = GroupUnitHeading + self:E( { UnitID, Template.units[UnitID], Template.units[UnitID] } ) + end end + end self:Destroy() _DATABASE:Spawn( Template ) + + self:ResetEvents() + end --- Returns the group template from the @{DATABASE} (_DATABASE object). @@ -1011,7 +1116,7 @@ do -- Route methods local PointTo = {} local AirbasePointVec2 = RTBAirbase:GetPointVec2() - local AirbaseAirPoint = AirbasePointVec2:RoutePointAir( + local AirbaseAirPoint = AirbasePointVec2:WaypointAir( POINT_VEC3.RoutePointAltType.BARO, "Land", "Landing", @@ -1069,7 +1174,21 @@ do -- Event Handling -- @return #GROUP function GROUP:UnHandleEvent( Event ) - self:EventDispatcher():RemoveForGroup( self:GetName(), self, Event ) + self:EventDispatcher():RemoveEvent( self, Event ) + + return self + end + + --- Reset the subscriptions. + -- @param #GROUP self + -- @return #GROUP + function GROUP:ResetEvents() + + self:EventDispatcher():Reset( self ) + + for UnitID, UnitData in pairs( self:GetUnits() ) do + UnitData:ResetEvents() + end return self end @@ -1084,7 +1203,7 @@ do -- Players -- @return #nil The group has no players function GROUP:GetPlayerNames() - local PlayerNames = nil + local PlayerNames = {} local Units = self:GetUnits() for UnitID, UnitData in pairs( Units ) do @@ -1096,8 +1215,100 @@ do -- Players end end - self:F( PlayerNames ) + self:F2( PlayerNames ) return PlayerNames end -end \ No newline at end of file +end + +--do -- Smoke +-- +----- Signal a flare at the position of the GROUP. +---- @param #GROUP self +---- @param Utilities.Utils#FLARECOLOR FlareColor +--function GROUP:Flare( FlareColor ) +-- self:F2() +-- trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 ) +--end +-- +----- Signal a white flare at the position of the GROUP. +---- @param #GROUP self +--function GROUP:FlareWhite() +-- self:F2() +-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 ) +--end +-- +----- Signal a yellow flare at the position of the GROUP. +---- @param #GROUP self +--function GROUP:FlareYellow() +-- self:F2() +-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 ) +--end +-- +----- Signal a green flare at the position of the GROUP. +---- @param #GROUP self +--function GROUP:FlareGreen() +-- self:F2() +-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 ) +--end +-- +----- Signal a red flare at the position of the GROUP. +---- @param #GROUP self +--function GROUP:FlareRed() +-- self:F2() +-- local Vec3 = self:GetVec3() +-- if Vec3 then +-- trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 ) +-- end +--end +-- +----- Smoke the GROUP. +---- @param #GROUP self +--function GROUP:Smoke( SmokeColor, Range ) +-- self:F2() +-- if Range then +-- trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor ) +-- else +-- trigger.action.smoke( self:GetVec3(), SmokeColor ) +-- end +-- +--end +-- +----- Smoke the GROUP Green. +---- @param #GROUP self +--function GROUP:SmokeGreen() +-- self:F2() +-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green ) +--end +-- +----- Smoke the GROUP Red. +---- @param #GROUP self +--function GROUP:SmokeRed() +-- self:F2() +-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red ) +--end +-- +----- Smoke the GROUP White. +---- @param #GROUP self +--function GROUP:SmokeWhite() +-- self:F2() +-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White ) +--end +-- +----- Smoke the GROUP Orange. +---- @param #GROUP self +--function GROUP:SmokeOrange() +-- self:F2() +-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange ) +--end +-- +----- Smoke the GROUP Blue. +---- @param #GROUP self +--function GROUP:SmokeBlue() +-- self:F2() +-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue ) +--end +-- +-- +-- +--end \ No newline at end of file diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index 08a22b044..cd7243f4b 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -1,39 +1,34 @@ ---- This module contains the IDENTIFIABLE class. +--- **Wrapper** -- IDENTIFIABLE is an intermediate class wrapping DCS Object class derived Objects. -- --- 1) @{#IDENTIFIABLE} class, extends @{Object#OBJECT} --- =============================================================== --- The @{#IDENTIFIABLE} class is a wrapper class to handle the DCS Identifiable objects: +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- +-- @module Identifiable + +--- @type IDENTIFIABLE +-- @extends Wrapper.Object#OBJECT +-- @field #string IdentifiableName The name of the identifiable. + +--- # IDENTIFIABLE class, extends @{Object#OBJECT} +-- +-- The IDENTIFIABLE class is a wrapper class to handle the DCS Identifiable objects: -- -- * Support all DCS Identifiable APIs. -- * Enhance with Identifiable specific APIs not in the DCS Identifiable API set. -- * Manage the "state" of the DCS Identifiable. -- --- 1.1) IDENTIFIABLE constructor: --- ------------------------------ +-- ## IDENTIFIABLE constructor +-- -- The IDENTIFIABLE class provides the following functions to construct a IDENTIFIABLE instance: -- -- * @{#IDENTIFIABLE.New}(): Create a IDENTIFIABLE instance. -- --- 1.2) IDENTIFIABLE methods: --- -------------------------- --- The following methods can be used to identify an identifiable object: --- --- * @{#IDENTIFIABLE.GetName}(): Returns the name of the Identifiable. --- * @{#IDENTIFIABLE.IsAlive}(): Returns if the Identifiable is alive. --- * @{#IDENTIFIABLE.GetTypeName}(): Returns the type name of the Identifiable. --- * @{#IDENTIFIABLE.GetCoalition}(): Returns the coalition of the Identifiable. --- * @{#IDENTIFIABLE.GetCountry}(): Returns the country of the Identifiable. --- * @{#IDENTIFIABLE.GetDesc}(): Returns the descriptor structure of the Identifiable. --- --- --- === --- --- @module Identifiable - ---- The IDENTIFIABLE class --- @type IDENTIFIABLE --- @extends Wrapper.Object#OBJECT --- @field #string IdentifiableName The name of the identifiable. +-- @field #IDENTIFIABLE IDENTIFIABLE = { ClassName = "IDENTIFIABLE", IdentifiableName = "", @@ -88,15 +83,8 @@ end function IDENTIFIABLE:GetName() self:F2( self.IdentifiableName ) - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableName = self.IdentifiableName - return IdentifiableName - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil + local IdentifiableName = self.IdentifiableName + return IdentifiableName end diff --git a/Moose Development/Moose/Wrapper/Object.lua b/Moose Development/Moose/Wrapper/Object.lua index 6641c80fb..5e4a236f1 100644 --- a/Moose Development/Moose/Wrapper/Object.lua +++ b/Moose Development/Moose/Wrapper/Object.lua @@ -1,33 +1,36 @@ ---- This module contains the OBJECT class. +--- **Wrapper** -- OBJECT wraps the DCS Object derived objects. -- --- 1) @{Object#OBJECT} class, extends @{Base#BASE} --- =========================================================== --- The @{Object#OBJECT} class is a wrapper class to handle the DCS Object objects: --- --- * Support all DCS Object APIs. --- * Enhance with Object specific APIs not in the DCS Object API set. --- * Manage the "state" of the DCS Object. --- --- 1.1) OBJECT constructor: --- ------------------------------ --- The OBJECT class provides the following functions to construct a OBJECT instance: --- --- * @{Object#OBJECT.New}(): Create a OBJECT instance. --- --- 1.2) OBJECT methods: --- -------------------------- --- The following methods can be used to identify an Object object: +-- ==== -- --- * @{Object#OBJECT.GetID}(): Returns the ID of the Object object. +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: -- -- === -- -- @module Object ---- The OBJECT class --- @type OBJECT + +--- @type OBJECT -- @extends Core.Base#BASE -- @field #string ObjectName The name of the Object. + + +--- # OBJECT class, extends @{Base#BASE} +-- +-- OBJECT handles the DCS Object objects: +-- +-- * Support all DCS Object APIs. +-- * Enhance with Object specific APIs not in the DCS Object API set. +-- * Manage the "state" of the DCS Object. +-- +-- ## OBJECT constructor: +-- +-- The OBJECT class provides the following functions to construct a OBJECT instance: +-- +-- * @{Object#OBJECT.New}(): Create a OBJECT instance. +-- +-- @field #OBJECT OBJECT = { ClassName = "OBJECT", ObjectName = "", diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index e6bd95cd3..1005dde1b 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -1,39 +1,60 @@ ---- This module contains the POSITIONABLE class. +--- **Wrapper** -- POSITIONABLE wraps DCS classes that are "positionable". -- --- 1) @{Positionable#POSITIONABLE} class, extends @{Identifiable#IDENTIFIABLE} --- =========================================================== --- The @{Positionable#POSITIONABLE} class is a wrapper class to handle the POSITIONABLE objects: +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- +-- @module Positionable + +--- @type POSITIONABLE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-) +-- @extends Wrapper.Identifiable#IDENTIFIABLE + +--- @type POSITIONABLE +-- @extends Wrapper.Identifiable#IDENTIFIABLE + + +--- # POSITIONABLE class, extends @{Identifiable#IDENTIFIABLE} +-- +-- The POSITIONABLE class is a wrapper class to handle the POSITIONABLE objects: -- -- * Support all DCS APIs. -- * Enhance with POSITIONABLE specific APIs not in the DCS API set. -- * Manage the "state" of the POSITIONABLE. -- --- 1.1) POSITIONABLE constructor: --- ------------------------------ +-- ## POSITIONABLE constructor +-- -- The POSITIONABLE class provides the following functions to construct a POSITIONABLE instance: -- --- * @{Positionable#POSITIONABLE.New}(): Create a POSITIONABLE instance. --- --- 1.2) POSITIONABLE methods: --- -------------------------- --- The following methods can be used to identify an measurable object: +-- * @{#POSITIONABLE.New}(): Create a POSITIONABLE instance. -- --- * @{Positionable#POSITIONABLE.GetID}(): Returns the ID of the measurable object. --- * @{Positionable#POSITIONABLE.GetName}(): Returns the name of the measurable object. +-- ## Get the current speed -- --- === +-- There are 3 methods that can be used to determine the speed. +-- Use @{#POSITIONABLE.GetVelocityKMH}() to retrieve the current speed in km/h. Use @{#POSITIONABLE.GetVelocityMPS}() to retrieve the speed in meters per second. +-- The method @{#POSITIONABLE.GetVelocity}() returns the speed vector (a Vec3). -- --- @module Positionable - ---- The POSITIONABLE class --- @type POSITIONABLE --- @extends Wrapper.Identifiable#IDENTIFIABLE --- @field #string PositionableName The name of the measurable. +-- ## Get the current altitude +-- +-- Altitude can be retrieved using the method @{#POSITIONABLE.GetHeight}() and returns the current altitude in meters from the orthonormal plane. +-- +-- +-- @field #POSITIONABLE POSITIONABLE = { ClassName = "POSITIONABLE", PositionableName = "", } +--- @field #POSITIONABLE.__ +POSITIONABLE.__ = {} + +--- @field #POSITIONABLE.__.Cargo +POSITIONABLE.__.Cargo = {} + + --- A DCSPositionable -- @type DCSPositionable -- @field id_ The ID of the controllable in DCS @@ -132,6 +153,27 @@ function POSITIONABLE:GetPointVec3() return nil end +--- Returns a COORDINATE object indicating the point in 3D of the POSITIONABLE within the mission. +-- @param Wrapper.Positionable#POSITIONABLE self +-- @return Core.Point#COORDINATE The COORDINATE of the POSITIONABLE. +function POSITIONABLE:GetCoordinate() + self:F2( self.PositionableName ) + + local DCSPositionable = self:GetDCSObject() + + if DCSPositionable then + local PositionableVec3 = self:GetPositionVec3() + + local PositionableCoordinate = COORDINATE:NewFromVec3( PositionableVec3 ) + PositionableCoordinate:SetHeading( self:GetHeading() ) + + self:T2( PositionableCoordinate ) + return PositionableCoordinate + end + + return nil +end + --- Returns a random @{DCSTypes#Vec3} vector within a range, indicating the point in 3D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self @@ -184,6 +226,28 @@ function POSITIONABLE:GetVec3() return nil end + +--- Get the bounding box of the underlying POSITIONABLE DCS Object. +-- @param #POSITIONABLE self +-- @return Dcs.DCSTypes#Distance The bounding box of the POSITIONABLE. +-- @return #nil The POSITIONABLE is not existing or alive. +function POSITIONABLE:GetBoundingBox() --R2.1 + self:F2() + + local DCSPositionable = self:GetDCSObject() + + if DCSPositionable then + local PositionableDesc = DCSPositionable:getDesc() --Dcs.DCSTypes#Desc + if PositionableDesc then + local PositionableBox = PositionableDesc.box + return PositionableBox + end + end + + return nil +end + + --- Returns the altitude of the POSITIONABLE. -- @param Wrapper.Positionable#POSITIONABLE self -- @return Dcs.DCSTypes#Distance The altitude of the POSITIONABLE. @@ -280,10 +344,32 @@ function POSITIONABLE:GetVelocity() return nil end + +--- Returns the POSITIONABLE height in meters. +-- @param Wrapper.Positionable#POSITIONABLE self +-- @return Dcs.DCSTypes#Vec3 The height of the positionable. +-- @return #nil The POSITIONABLE is not existing or alive. +function POSITIONABLE:GetHeight() --R2.1 + self:F2( self.PositionableName ) + + local DCSPositionable = self:GetDCSObject() + + if DCSPositionable then + local PositionablePosition = DCSPositionable:getPosition() + if PositionablePosition then + local PositionableHeight = PositionablePosition.p.y + self:T2( PositionableHeight ) + return PositionableHeight + end + end + + return nil +end + + --- Returns the POSITIONABLE velocity in km/h. -- @param Wrapper.Positionable#POSITIONABLE self -- @return #number The velocity in km/h --- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetVelocityKMH() self:F2( self.PositionableName ) @@ -297,21 +383,59 @@ function POSITIONABLE:GetVelocityKMH() return Velocity end + return 0 +end + +--- Returns the POSITIONABLE velocity in meters per second. +-- @param Wrapper.Positionable#POSITIONABLE self +-- @return #number The velocity in meters per second. +function POSITIONABLE:GetVelocityMPS() + self:F2( self.PositionableName ) + + local DCSPositionable = self:GetDCSObject() + + if DCSPositionable then + local VelocityVec3 = self:GetVelocity() + local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec + self:T3( Velocity ) + return Velocity + end + + return 0 +end + + +--- Returns the message text with the callsign embedded (if there is one). +-- @param #POSITIONABLE self +-- @param #string Message The message text +-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. +-- @return #string The message text +function POSITIONABLE:GetMessageText( Message, Name ) --R2.1 added + + local DCSObject = self:GetDCSObject() + if DCSObject then + Name = Name and ( " (" .. Name .. ")" ) or "" + local Callsign = string.format( "[%s]", self:GetCallsign() ~= "" and self:GetCallsign() or self:GetName() ) + local MessageText = Callsign .. Name .. ": " .. Message + return MessageText + end + return nil end + --- Returns a message with the callsign embedded (if there is one). -- @param #POSITIONABLE self -- @param #string Message The message text -- @param Dcs.DCSTypes#Duration Duration The duration of the message. -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @return Core.Message#MESSAGE -function POSITIONABLE:GetMessage( Message, Duration, Name ) +function POSITIONABLE:GetMessage( Message, Duration, Name ) --R2.1 changed callsign and name and using GetMessageText local DCSObject = self:GetDCSObject() if DCSObject then - Name = Name or self:GetTypeName() - return MESSAGE:New( Message, Duration, self:GetCallsign() .. " (" .. Name .. ")" ) + local MessageText = self:GetMessageText( Message, Name ) + return MESSAGE:New( MessageText, Duration ) end return nil @@ -340,12 +464,19 @@ end -- @param #string Message The message text -- @param Dcs.DCSTYpes#Duration Duration The duration of the message. -- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name ) +function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition ) self:F2( { Message, Duration } ) + local Name = "" + local DCSObject = self:GetDCSObject() if DCSObject then + if MessageCoalition == coalition.side.BLUE then + Name = "Blue coalition" + end + if MessageCoalition == coalition.side.RED then + Name = "Red coalition" + end self:GetMessage( Message, Duration, Name ):ToCoalition( MessageCoalition ) end @@ -425,6 +556,30 @@ function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name ) return nil end +--- Send a message to a @{Set#SET_GROUP}. +-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. +-- @param #POSITIONABLE self +-- @param #string Message The message text +-- @param Dcs.DCSTypes#Duration Duration The duration of the message. +-- @param Core.Set#SET_GROUP MessageSetGroup The SET_GROUP collection receiving the message. +-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. +function POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Name ) --R2.1 + self:F2( { Message, Duration } ) + + local DCSObject = self:GetDCSObject() + if DCSObject then + if DCSObject:isExist() then + MessageSetGroup:ForEachGroup( + function( MessageGroup ) + self:GetMessage( Message, Duration, Name ):ToGroup( MessageGroup ) + end + ) + end + end + + return nil +end + --- Send a message to the players in the @{Group}. -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self @@ -446,7 +601,220 @@ end -- Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message -- @param #POSITIONABLE self -- @return #RADIO Radio -function POSITIONABLE:GetRadio() +function POSITIONABLE:GetRadio() --R2.1 self:F2(self) return RADIO:New(self) end + +--- Create a @{Radio#BEACON}, to allow this POSITIONABLE to broadcast beacon signals +-- @param #POSITIONABLE self +-- @return #RADIO Radio +function POSITIONABLE:GetBeacon() --R2.1 + self:F2(self) + return BEACON:New(self) +end + +--- Start Lasing a POSITIONABLE +-- @param #POSITIONABLE self +-- @param #POSITIONABLE Target +-- @param #number LaserCode +-- @param #number Duration +-- @return Core.Spot#SPOT +function POSITIONABLE:LaseUnit( Target, LaserCode, Duration ) --R2.1 + self:F2() + + LaserCode = LaserCode or math.random( 1000, 9999 ) + + local RecceDcsUnit = self:GetDCSObject() + local TargetVec3 = Target:GetVec3() + + self:E("bulding spot") + self.Spot = SPOT:New( self ) -- Core.Spot#SPOT + self.Spot:LaseOn( Target, LaserCode, Duration) + self.LaserCode = LaserCode + + return self.Spot + +end + +--- Stop Lasing a POSITIONABLE +-- @param #POSITIONABLE self +-- @return #POSITIONABLE +function POSITIONABLE:LaseOff() --R2.1 + self:F2() + + if self.Spot then + self.Spot:LaseOff() + self.Spot = nil + end + + return self +end + +--- Check if the POSITIONABLE is lasing a target +-- @param #POSITIONABLE self +-- @return #boolean true if it is lasing a target +function POSITIONABLE:IsLasing() --R2.1 + self:F2() + + local Lasing = false + + if self.Spot then + Lasing = self.Spot:IsLasing() + end + + return Lasing +end + +--- Get the Spot +-- @param #POSITIONABLE self +-- @return Core.Spot#SPOT The Spot +function POSITIONABLE:GetSpot() --R2.1 + + return self.Spot +end + +--- Get the last assigned laser code +-- @param #POSITIONABLE self +-- @return #number The laser code +function POSITIONABLE:GetLaserCode() --R2.1 + + return self.LaserCode +end + +--- Add cargo. +-- @param #POSITIONABLE self +-- @param Core.Cargo#CARGO Cargo +-- @return #POSITIONABLE +function POSITIONABLE:AddCargo( Cargo ) + self.__.Cargo[Cargo] = Cargo + return self +end + +--- Remove cargo. +-- @param #POSITIONABLE self +-- @param Core.Cargo#CARGO Cargo +-- @return #POSITIONABLE +function POSITIONABLE:RemoveCargo( Cargo ) + self.__.Cargo[Cargo] = nil + return self +end + +--- Returns if carrier has given cargo. +-- @param #POSITIONABLE self +-- @return Core.Cargo#CARGO Cargo +function POSITIONABLE:HasCargo( Cargo ) + return self.__.Cargo[Cargo] +end + +--- Clear all cargo. +-- @param #POSITIONABLE self +function POSITIONABLE:ClearCargo() + self.__.Cargo = {} +end + +--- Get cargo item count. +-- @param #POSITIONABLE self +-- @return Core.Cargo#CARGO Cargo +function POSITIONABLE:CargoItemCount() + local ItemCount = 0 + for CargoName, Cargo in pairs( self.__.Cargo ) do + ItemCount = ItemCount + Cargo:GetCount() + end + return ItemCount +end + +--- Signal a flare at the position of the POSITIONABLE. +-- @param #POSITIONABLE self +-- @param Utilities.Utils#FLARECOLOR FlareColor +function POSITIONABLE:Flare( FlareColor ) + self:F2() + trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 ) +end + +--- Signal a white flare at the position of the POSITIONABLE. +-- @param #POSITIONABLE self +function POSITIONABLE:FlareWhite() + self:F2() + trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 ) +end + +--- Signal a yellow flare at the position of the POSITIONABLE. +-- @param #POSITIONABLE self +function POSITIONABLE:FlareYellow() + self:F2() + trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 ) +end + +--- Signal a green flare at the position of the POSITIONABLE. +-- @param #POSITIONABLE self +function POSITIONABLE:FlareGreen() + self:F2() + trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 ) +end + +--- Signal a red flare at the position of the POSITIONABLE. +-- @param #POSITIONABLE self +function POSITIONABLE:FlareRed() + self:F2() + local Vec3 = self:GetVec3() + if Vec3 then + trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 ) + end +end + +--- Smoke the POSITIONABLE. +-- @param #POSITIONABLE self +-- @param Utilities.Utils#SMOKECOLOR SmokeColor The color to smoke to positionable. +-- @param #number Range The range in meters to randomize the smoking around the positionable. +-- @param #number AddHeight The height in meters to add to the altitude of the positionable. +function POSITIONABLE:Smoke( SmokeColor, Range, AddHeight ) + self:F2() + if Range then + local Vec3 = self:GetRandomVec3( Range ) + Vec3.y = Vec3.y + AddHeight or 0 + trigger.action.smoke( Vec3, SmokeColor ) + else + local Vec3 = self:GetVec3() + Vec3.y = Vec3.y + AddHeight or 0 + trigger.action.smoke( self:GetVec3(), SmokeColor ) + end + +end + +--- Smoke the POSITIONABLE Green. +-- @param #POSITIONABLE self +function POSITIONABLE:SmokeGreen() + self:F2() + trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green ) +end + +--- Smoke the POSITIONABLE Red. +-- @param #POSITIONABLE self +function POSITIONABLE:SmokeRed() + self:F2() + trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red ) +end + +--- Smoke the POSITIONABLE White. +-- @param #POSITIONABLE self +function POSITIONABLE:SmokeWhite() + self:F2() + trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White ) +end + +--- Smoke the POSITIONABLE Orange. +-- @param #POSITIONABLE self +function POSITIONABLE:SmokeOrange() + self:F2() + trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange ) +end + +--- Smoke the POSITIONABLE Blue. +-- @param #POSITIONABLE self +function POSITIONABLE:SmokeBlue() + self:F2() + trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue ) +end + + diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index c697d250e..d0d5215dd 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -1,22 +1,31 @@ ---- This module contains the SCENERY class. +--- **Wrapper** -- SCENERY models scenery within the DCS simulator. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- +-- @module Scenery + + + +--- @type SCENERY +-- @extends Wrapper.Positionable#POSITIONABLE + + +--- # SCENERY class, extends @{Positionable#POSITIONABLE} -- --- 1) @{Scenery#SCENERY} class, extends @{Positionable#POSITIONABLE} --- =============================================================== -- Scenery objects are defined on the map. -- The @{Scenery#SCENERY} class is a wrapper class to handle the DCS Scenery objects: -- -- * Wraps the DCS Scenery objects. -- * Support all DCS Scenery APIs. -- * Enhance with Scenery specific APIs not in the DCS API set. --- --- @module Scenery --- @author FlightControl - - - ---- The SCENERY class --- @type SCENERY --- @extends Wrapper.Positionable#POSITIONABLE +-- +-- @field #SCENERY SCENERY = { ClassName = "SCENERY", } diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index 0bedb8918..cc1e1f74c 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -1,7 +1,21 @@ ---- This module contains the STATIC class. +--- **Wrapper** -- STATIC wraps the DCS StaticObject class. +-- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- +-- @module Static + + +--- @type STATIC +-- @extends Wrapper.Positionable#POSITIONABLE + +--- # STATIC class, extends @{Positionable#POSITIONABLE} -- --- 1) @{Static#STATIC} class, extends @{Positionable#POSITIONABLE} --- =============================================================== -- Statics are **Static Units** defined within the Mission Editor. -- Note that Statics are almost the same as Units, but they don't have a controller. -- The @{Static#STATIC} class is a wrapper class to handle the DCS Static objects: @@ -10,8 +24,8 @@ -- * Support all DCS Static APIs. -- * Enhance with Static specific APIs not in the DCS API set. -- --- 1.1) STATIC reference methods --- ----------------------------- +-- ## STATIC reference methods +-- -- For each DCS Static will have a STATIC wrapper object (instance) within the _@{DATABASE} object. -- This is done at the beginning of the mission (when the mission starts). -- @@ -28,17 +42,7 @@ -- -- IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil). -- --- @module Static --- @author FlightControl - - - - - - ---- The STATIC class --- @type STATIC --- @extends Wrapper.Positionable#POSITIONABLE +-- @field #STATIC STATIC = { ClassName = "STATIC", } diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 68be9a981..7f9a34ed2 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -9,8 +9,17 @@ -- * Handle local Unit Controller. -- * Manage the "state" of the DCS Unit. -- +-- ==== +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- +-- ### Contributions: +-- +-- ==== +-- -- @module Unit + --- @type UNIT -- @extends Wrapper.Controllable#CONTROLLABLE @@ -64,13 +73,18 @@ -- -- The UNIT class contains methods to test the location or proximity against zones or other objects. -- --- ### Zones +-- ### Zones range -- -- To test whether the Unit is within a **zone**, use the @{#UNIT.IsInZone}() or the @{#UNIT.IsNotInZone}() methods. Any zone can be tested on, but the zone must be derived from @{Zone#ZONE_BASE}. -- --- ### Units +-- ### Unit range +-- +-- * Test if another DCS Unit is within a given radius of the current DCS Unit, use the @{#UNIT.OtherUnitInRadius}() method. +-- +-- ## Test Line of Sight +-- +-- * Use the @{#UNIT.IsLOS}() method to check if the given unit is within line of sight. -- --- Test if another DCS Unit is within a given radius of the current DCS Unit, use the @{#UNIT.OtherUnitInRadius}() method. -- -- @field #UNIT UNIT UNIT = { @@ -310,7 +324,8 @@ function UNIT:GetPlayerName() return PlayerName end - return nil + return nil + end --- Returns the unit's number in the group. @@ -471,12 +486,12 @@ function UNIT:GetRadar() return nil, nil end ---- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. +--- Returns relative amount of fuel (from 0.0 to 1.0) the UNIT has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. -- @param #UNIT self -- @return #number The relative amount of fuel (from 0.0 to 1.0). -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetFuel() - self:F2( self.UnitName ) + self:F( self.UnitName ) local DCSUnit = self:GetDCSObject() @@ -521,7 +536,7 @@ function UNIT:GetLife() return UnitLife end - return nil + return -1 end --- Returns the Unit's initial health. @@ -538,7 +553,7 @@ function UNIT:GetLife0() return UnitLife0 end - return nil + return 0 end --- Returns the category name of the #UNIT. @@ -583,122 +598,128 @@ end -- @param #UNIT self function UNIT:GetThreatLevel() - local Attributes = self:GetDesc().attributes - self:T( Attributes ) local ThreatLevel = 0 local ThreatText = "" - if self:IsGround() then + local Descriptor = self:GetDesc() - self:T( "Ground" ) + if Descriptor then - local ThreatLevels = { - "Unarmed", - "Infantry", - "Old Tanks & APCs", - "Tanks & IFVs without ATGM", - "Tanks & IFV with ATGM", - "Modern Tanks", - "AAA", - "IR Guided SAMs", - "SR SAMs", - "MR SAMs", - "LR SAMs" - } + local Attributes = Descriptor.attributes + self:T( Attributes ) + + if self:IsGround() then + self:T( "Ground" ) - if Attributes["LR SAM"] then ThreatLevel = 10 - elseif Attributes["MR SAM"] then ThreatLevel = 9 - elseif Attributes["SR SAM"] and - not Attributes["IR Guided SAM"] then ThreatLevel = 8 - elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and - Attributes["IR Guided SAM"] then ThreatLevel = 7 - elseif Attributes["AAA"] then ThreatLevel = 6 - elseif Attributes["Modern Tanks"] then ThreatLevel = 5 - elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and - Attributes["ATGM"] then ThreatLevel = 4 - elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and - not Attributes["ATGM"] then ThreatLevel = 3 - elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2 - elseif Attributes["Infantry"] then ThreatLevel = 1 + local ThreatLevels = { + "Unarmed", + "Infantry", + "Old Tanks & APCs", + "Tanks & IFVs without ATGM", + "Tanks & IFV with ATGM", + "Modern Tanks", + "AAA", + "IR Guided SAMs", + "SR SAMs", + "MR SAMs", + "LR SAMs" + } + + + if Attributes["LR SAM"] then ThreatLevel = 10 + elseif Attributes["MR SAM"] then ThreatLevel = 9 + elseif Attributes["SR SAM"] and + not Attributes["IR Guided SAM"] then ThreatLevel = 8 + elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and + Attributes["IR Guided SAM"] then ThreatLevel = 7 + elseif Attributes["AAA"] then ThreatLevel = 6 + elseif Attributes["Modern Tanks"] then ThreatLevel = 5 + elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and + Attributes["ATGM"] then ThreatLevel = 4 + elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and + not Attributes["ATGM"] then ThreatLevel = 3 + elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2 + elseif Attributes["Infantry"] then ThreatLevel = 1 + end + + ThreatText = ThreatLevels[ThreatLevel+1] end - ThreatText = ThreatLevels[ThreatLevel+1] - end - - if self:IsAir() then - - self:T( "Air" ) - - local ThreatLevels = { - "Unarmed", - "Tanker", - "AWACS", - "Transport Helicpter", - "UAV", - "Bomber", - "Strategic Bomber", - "Attack Helicopter", - "Interceptor", - "Multirole Fighter", - "Fighter" - } + if self:IsAir() then - - if Attributes["Fighters"] then ThreatLevel = 10 - elseif Attributes["Multirole fighters"] then ThreatLevel = 9 - elseif Attributes["Battleplanes"] then ThreatLevel = 8 - elseif Attributes["Attack helicopters"] then ThreatLevel = 7 - elseif Attributes["Strategic bombers"] then ThreatLevel = 6 - elseif Attributes["Bombers"] then ThreatLevel = 5 - elseif Attributes["UAVs"] then ThreatLevel = 4 - elseif Attributes["Transport helicopters"] then ThreatLevel = 3 - elseif Attributes["AWACS"] then ThreatLevel = 2 - elseif Attributes["Tankers"] then ThreatLevel = 1 + self:T( "Air" ) + + local ThreatLevels = { + "Unarmed", + "Tanker", + "AWACS", + "Transport Helicopter", + "UAV", + "Bomber", + "Strategic Bomber", + "Attack Helicopter", + "Battleplane", + "Multirole Fighter", + "Fighter" + } + + + if Attributes["Fighters"] then ThreatLevel = 10 + elseif Attributes["Multirole fighters"] then ThreatLevel = 9 + elseif Attributes["Battleplanes"] then ThreatLevel = 8 + elseif Attributes["Attack helicopters"] then ThreatLevel = 7 + elseif Attributes["Strategic bombers"] then ThreatLevel = 6 + elseif Attributes["Bombers"] then ThreatLevel = 5 + elseif Attributes["UAVs"] then ThreatLevel = 4 + elseif Attributes["Transport helicopters"] then ThreatLevel = 3 + elseif Attributes["AWACS"] then ThreatLevel = 2 + elseif Attributes["Tankers"] then ThreatLevel = 1 + end + + ThreatText = ThreatLevels[ThreatLevel+1] end - - ThreatText = ThreatLevels[ThreatLevel+1] - end - - if self:IsShip() then - - self:T( "Ship" ) - ---["Aircraft Carriers"] = {"Heavy armed ships",}, ---["Cruisers"] = {"Heavy armed ships",}, ---["Destroyers"] = {"Heavy armed ships",}, ---["Frigates"] = {"Heavy armed ships",}, ---["Corvettes"] = {"Heavy armed ships",}, ---["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",}, ---["Light armed ships"] = {"Armed ships","NonArmoredUnits"}, ---["Armed ships"] = {"Ships"}, ---["Unarmed ships"] = {"Ships","HeavyArmoredUnits",}, - - local ThreatLevels = { - "Unarmed ship", - "Light armed ships", - "Corvettes", - "", - "Frigates", - "", - "Cruiser", - "", - "Destroyer", - "", - "Aircraft Carrier" - } + if self:IsShip() then + + self:T( "Ship" ) + + --["Aircraft Carriers"] = {"Heavy armed ships",}, + --["Cruisers"] = {"Heavy armed ships",}, + --["Destroyers"] = {"Heavy armed ships",}, + --["Frigates"] = {"Heavy armed ships",}, + --["Corvettes"] = {"Heavy armed ships",}, + --["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",}, + --["Light armed ships"] = {"Armed ships","NonArmoredUnits"}, + --["Armed ships"] = {"Ships"}, + --["Unarmed ships"] = {"Ships","HeavyArmoredUnits",}, - if Attributes["Aircraft Carriers"] then ThreatLevel = 10 - elseif Attributes["Destroyers"] then ThreatLevel = 8 - elseif Attributes["Cruisers"] then ThreatLevel = 6 - elseif Attributes["Frigates"] then ThreatLevel = 4 - elseif Attributes["Corvettes"] then ThreatLevel = 2 - elseif Attributes["Light armed ships"] then ThreatLevel = 1 + local ThreatLevels = { + "Unarmed ship", + "Light armed ships", + "Corvettes", + "", + "Frigates", + "", + "Cruiser", + "", + "Destroyer", + "", + "Aircraft Carrier" + } + + + if Attributes["Aircraft Carriers"] then ThreatLevel = 10 + elseif Attributes["Destroyers"] then ThreatLevel = 8 + elseif Attributes["Cruisers"] then ThreatLevel = 6 + elseif Attributes["Frigates"] then ThreatLevel = 4 + elseif Attributes["Corvettes"] then ThreatLevel = 2 + elseif Attributes["Light armed ships"] then ThreatLevel = 1 + end + + ThreatText = ThreatLevels[ThreatLevel+1] end - - ThreatText = ThreatLevels[ThreatLevel+1] end self:T2( ThreatLevel ) @@ -719,7 +740,7 @@ function UNIT:IsInZone( Zone ) if self:IsAlive() then local IsInZone = Zone:IsVec3InZone( self:GetVec3() ) - self:T( { IsInZone } ) + self:T2( { IsInZone } ) return IsInZone end @@ -773,91 +794,7 @@ end ---- Signal a flare at the position of the UNIT. --- @param #UNIT self --- @param Utilities.Utils#FLARECOLOR FlareColor -function UNIT:Flare( FlareColor ) - self:F2() - trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 ) -end ---- Signal a white flare at the position of the UNIT. --- @param #UNIT self -function UNIT:FlareWhite() - self:F2() - trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 ) -end - ---- Signal a yellow flare at the position of the UNIT. --- @param #UNIT self -function UNIT:FlareYellow() - self:F2() - trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 ) -end - ---- Signal a green flare at the position of the UNIT. --- @param #UNIT self -function UNIT:FlareGreen() - self:F2() - trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 ) -end - ---- Signal a red flare at the position of the UNIT. --- @param #UNIT self -function UNIT:FlareRed() - self:F2() - local Vec3 = self:GetVec3() - if Vec3 then - trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 ) - end -end - ---- Smoke the UNIT. --- @param #UNIT self -function UNIT:Smoke( SmokeColor, Range ) - self:F2() - if Range then - trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor ) - else - trigger.action.smoke( self:GetVec3(), SmokeColor ) - end - -end - ---- Smoke the UNIT Green. --- @param #UNIT self -function UNIT:SmokeGreen() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green ) -end - ---- Smoke the UNIT Red. --- @param #UNIT self -function UNIT:SmokeRed() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red ) -end - ---- Smoke the UNIT White. --- @param #UNIT self -function UNIT:SmokeWhite() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White ) -end - ---- Smoke the UNIT Orange. --- @param #UNIT self -function UNIT:SmokeOrange() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange ) -end - ---- Smoke the UNIT Blue. --- @param #UNIT self -function UNIT:SmokeBlue() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue ) -end -- Is methods @@ -990,5 +927,42 @@ do -- Event Handling return self end + + --- Reset the subscriptions. + -- @param #UNIT self + -- @return #UNIT + function UNIT:ResetEvents() + + self:EventDispatcher():Reset( self ) + + return self + end + +end + +do -- Detection + + --- Returns if a unit is detecting the TargetUnit. + -- @param #UNIT self + -- @param #UNIT TargetUnit + -- @return #boolean true If the TargetUnit is detected by the unit, otherwise false. + function UNIT:IsDetected( TargetUnit ) --R2.1 + + local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity = self:IsTargetDetected( TargetUnit:GetDCSObject() ) + + return TargetIsDetected + end + + --- Returns if a unit has Line of Sight (LOS) with the TargetUnit. + -- @param #UNIT self + -- @param #UNIT TargetUnit + -- @return #boolean true If the TargetUnit has LOS with the unit, otherwise false. + function UNIT:IsLOS( TargetUnit ) --R2.1 + + local IsLOS = self:GetPointVec3():IsLOS( TargetUnit:GetPointVec3() ) + + return IsLOS + end + end \ No newline at end of file diff --git a/Moose Logo/Avatars/MOOSE_Avatar01.png b/Moose Logo/Avatars/MOOSE_Avatar01.png deleted file mode 100644 index ad84f48b9..000000000 Binary files a/Moose Logo/Avatars/MOOSE_Avatar01.png and /dev/null differ diff --git a/Moose Logo/Avatars/MOOSE_Avatar02.png b/Moose Logo/Avatars/MOOSE_Avatar02.png deleted file mode 100644 index 28bb5c54d..000000000 Binary files a/Moose Logo/Avatars/MOOSE_Avatar02.png and /dev/null differ diff --git a/Moose Logo/Avatars/MOOSE_Avatar03.png b/Moose Logo/Avatars/MOOSE_Avatar03.png deleted file mode 100644 index 8908ff5fb..000000000 Binary files a/Moose Logo/Avatars/MOOSE_Avatar03.png and /dev/null differ diff --git a/Moose Logo/Avatars/MOOSE_Avatar04.png b/Moose Logo/Avatars/MOOSE_Avatar04.png deleted file mode 100644 index 6bad84249..000000000 Binary files a/Moose Logo/Avatars/MOOSE_Avatar04.png and /dev/null differ diff --git a/Moose Logo/Avatars/MOOSE_Avatar05.png b/Moose Logo/Avatars/MOOSE_Avatar05.png deleted file mode 100644 index d34782171..000000000 Binary files a/Moose Logo/Avatars/MOOSE_Avatar05.png and /dev/null differ diff --git a/Moose Logo/Avatars/MOOSE_Avatar06.png b/Moose Logo/Avatars/MOOSE_Avatar06.png deleted file mode 100644 index 1c989c1f9..000000000 Binary files a/Moose Logo/Avatars/MOOSE_Avatar06.png and /dev/null differ diff --git a/Moose Logo/Avatars/MOOSE_Avatar07.png b/Moose Logo/Avatars/MOOSE_Avatar07.png deleted file mode 100644 index ded4c17be..000000000 Binary files a/Moose Logo/Avatars/MOOSE_Avatar07.png and /dev/null differ diff --git a/Moose Logo/Desktop/MOOSE_Desktop_01.jpg b/Moose Logo/Desktop/MOOSE_Desktop_01.jpg deleted file mode 100644 index 8bb72872a..000000000 Binary files a/Moose Logo/Desktop/MOOSE_Desktop_01.jpg and /dev/null differ diff --git a/Moose Logo/Desktop/MOOSE_Desktop_03.jpg b/Moose Logo/Desktop/MOOSE_Desktop_03.jpg deleted file mode 100644 index 64b14c442..000000000 Binary files a/Moose Logo/Desktop/MOOSE_Desktop_03.jpg and /dev/null differ diff --git a/Moose Logo/Desktop/MOOSE_Desktop_04.jpg b/Moose Logo/Desktop/MOOSE_Desktop_04.jpg deleted file mode 100644 index 7315385a6..000000000 Binary files a/Moose Logo/Desktop/MOOSE_Desktop_04.jpg and /dev/null differ diff --git a/Moose Logo/Desktop/MOOSE_Desktop_06.jpg b/Moose Logo/Desktop/MOOSE_Desktop_06.jpg deleted file mode 100644 index 6a4d05ebf..000000000 Binary files a/Moose Logo/Desktop/MOOSE_Desktop_06.jpg and /dev/null differ diff --git a/Moose Logo/Desktop/MOOSE_Desktop_07.jpg b/Moose Logo/Desktop/MOOSE_Desktop_07.jpg deleted file mode 100644 index 6856142b5..000000000 Binary files a/Moose Logo/Desktop/MOOSE_Desktop_07.jpg and /dev/null differ diff --git a/Moose Logo/Desktop/MOOSE_Desktop_08.jpg b/Moose Logo/Desktop/MOOSE_Desktop_08.jpg deleted file mode 100644 index 382cad100..000000000 Binary files a/Moose Logo/Desktop/MOOSE_Desktop_08.jpg and /dev/null differ diff --git a/Moose Logo/Desktop/MOOSE_Desktop_09.jpg b/Moose Logo/Desktop/MOOSE_Desktop_09.jpg deleted file mode 100644 index 236156572..000000000 Binary files a/Moose Logo/Desktop/MOOSE_Desktop_09.jpg and /dev/null differ diff --git a/Moose Logo/MOOSE Brand Book 1.0.pdf b/Moose Logo/MOOSE Brand Book 1.0.pdf deleted file mode 100644 index 1a203d330..000000000 Binary files a/Moose Logo/MOOSE Brand Book 1.0.pdf and /dev/null differ diff --git a/Moose Logo/MOOSE_Logo_Primary_Black.eps b/Moose Logo/MOOSE_Logo_Primary_Black.eps deleted file mode 100644 index ed72da324..000000000 Binary files a/Moose Logo/MOOSE_Logo_Primary_Black.eps and /dev/null differ diff --git a/Moose Logo/MOOSE_Logo_Primary_Black.png b/Moose Logo/MOOSE_Logo_Primary_Black.png deleted file mode 100644 index f61cc767d..000000000 Binary files a/Moose Logo/MOOSE_Logo_Primary_Black.png and /dev/null differ diff --git a/Moose Logo/MOOSE_Logo_Primary_Color.eps b/Moose Logo/MOOSE_Logo_Primary_Color.eps deleted file mode 100644 index 78f8266af..000000000 Binary files a/Moose Logo/MOOSE_Logo_Primary_Color.eps and /dev/null differ diff --git a/Moose Logo/MOOSE_Logo_Primary_Color.png b/Moose Logo/MOOSE_Logo_Primary_Color.png deleted file mode 100644 index 7801dd4af..000000000 Binary files a/Moose Logo/MOOSE_Logo_Primary_Color.png and /dev/null differ diff --git a/Moose Logo/MOOSE_Logo_Primary_White.eps b/Moose Logo/MOOSE_Logo_Primary_White.eps deleted file mode 100644 index 230579805..000000000 Binary files a/Moose Logo/MOOSE_Logo_Primary_White.eps and /dev/null differ diff --git a/Moose Logo/MOOSE_Logo_Primary_White.png b/Moose Logo/MOOSE_Logo_Primary_White.png deleted file mode 100644 index 0413d98d6..000000000 Binary files a/Moose Logo/MOOSE_Logo_Primary_White.png and /dev/null differ diff --git a/Moose Logo/MOOSE_Logo_Secondary_Black.eps b/Moose Logo/MOOSE_Logo_Secondary_Black.eps deleted file mode 100644 index 396d62af2..000000000 Binary files a/Moose Logo/MOOSE_Logo_Secondary_Black.eps and /dev/null differ diff --git a/Moose Logo/MOOSE_Logo_Secondary_Black.png b/Moose Logo/MOOSE_Logo_Secondary_Black.png deleted file mode 100644 index 749ca200f..000000000 Binary files a/Moose Logo/MOOSE_Logo_Secondary_Black.png and /dev/null differ diff --git a/Moose Logo/MOOSE_Logo_Secondary_Color.eps b/Moose Logo/MOOSE_Logo_Secondary_Color.eps deleted file mode 100644 index 82090bd70..000000000 Binary files a/Moose Logo/MOOSE_Logo_Secondary_Color.eps and /dev/null differ diff --git a/Moose Logo/MOOSE_Logo_Secondary_Color.png b/Moose Logo/MOOSE_Logo_Secondary_Color.png deleted file mode 100644 index f2a397caa..000000000 Binary files a/Moose Logo/MOOSE_Logo_Secondary_Color.png and /dev/null differ diff --git a/Moose Logo/MOOSE_Logo_Secondary_White.eps b/Moose Logo/MOOSE_Logo_Secondary_White.eps deleted file mode 100644 index 44971970a..000000000 Binary files a/Moose Logo/MOOSE_Logo_Secondary_White.eps and /dev/null differ diff --git a/Moose Logo/MOOSE_Logo_Secondary_White.png b/Moose Logo/MOOSE_Logo_Secondary_White.png deleted file mode 100644 index 3b50c2abf..000000000 Binary files a/Moose Logo/MOOSE_Logo_Secondary_White.png and /dev/null differ diff --git a/Moose Logo/MOOSE_Symbol_Black.eps b/Moose Logo/MOOSE_Symbol_Black.eps deleted file mode 100644 index f3bcdf259..000000000 Binary files a/Moose Logo/MOOSE_Symbol_Black.eps and /dev/null differ diff --git a/Moose Logo/MOOSE_Symbol_Black.png b/Moose Logo/MOOSE_Symbol_Black.png deleted file mode 100644 index 630369bdb..000000000 Binary files a/Moose Logo/MOOSE_Symbol_Black.png and /dev/null differ diff --git a/Moose Logo/MOOSE_Symbol_Color.eps b/Moose Logo/MOOSE_Symbol_Color.eps deleted file mode 100644 index 471d62172..000000000 Binary files a/Moose Logo/MOOSE_Symbol_Color.eps and /dev/null differ diff --git a/Moose Logo/MOOSE_Symbol_Color.png b/Moose Logo/MOOSE_Symbol_Color.png deleted file mode 100644 index 62ad5fcd4..000000000 Binary files a/Moose Logo/MOOSE_Symbol_Color.png and /dev/null differ diff --git a/Moose Logo/MOOSE_Symbol_White.eps b/Moose Logo/MOOSE_Symbol_White.eps deleted file mode 100644 index 73a1cfe24..000000000 Binary files a/Moose Logo/MOOSE_Symbol_White.eps and /dev/null differ diff --git a/Moose Logo/MOOSE_Symbol_White.png b/Moose Logo/MOOSE_Symbol_White.png deleted file mode 100644 index 5492ff470..000000000 Binary files a/Moose Logo/MOOSE_Symbol_White.png and /dev/null differ diff --git a/Moose Logo/Moose_ED_Signature_Black.png b/Moose Logo/Moose_ED_Signature_Black.png deleted file mode 100644 index 12f3e789d..000000000 Binary files a/Moose Logo/Moose_ED_Signature_Black.png and /dev/null differ diff --git a/Moose Logo/Moose_ED_Signature_Color.png b/Moose Logo/Moose_ED_Signature_Color.png deleted file mode 100644 index 371d7eca8..000000000 Binary files a/Moose Logo/Moose_ED_Signature_Color.png and /dev/null differ diff --git a/Moose Logo/logo.txt b/Moose Logo/logo.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/Moose Mission Setup/Moose Create Dynamic/Moose_Dynamic_Loader.lua b/Moose Mission Setup/Moose Create Dynamic/Moose_Dynamic_Loader.lua index f0bd89564..c0f9b8985 100644 --- a/Moose Mission Setup/Moose Create Dynamic/Moose_Dynamic_Loader.lua +++ b/Moose Mission Setup/Moose Create Dynamic/Moose_Dynamic_Loader.lua @@ -1,26 +1,21 @@ local base = _G -Include = {} +__Moose = {} -Include.File = function( IncludeFile ) - if not Include.Files[ IncludeFile ] then - Include.Files[IncludeFile] = IncludeFile - env.info( "Include:" .. IncludeFile .. " from " .. Include.ProgramPath ) - local f = assert( base.loadfile( Include.ProgramPath .. IncludeFile .. ".lua" ) ) +__Moose.Include = function( IncludeFile ) + if not __Moose.Includes[ IncludeFile ] then + __Moose.Includes[IncludeFile] = IncludeFile + local f = assert( base.loadfile( __Moose.ProgramPath .. IncludeFile ) ) if f == nil then - error ("Could not load MOOSE file " .. IncludeFile .. ".lua" ) + error ("Moose: Could not load Moose file " .. IncludeFile ) else - env.info( "Include:" .. IncludeFile .. " loaded from " .. Include.ProgramPath ) + env.info( "Moose: " .. IncludeFile .. " dynamically loaded from " .. __Moose.ProgramPath ) return f() end end end -Include.ProgramPath = "Scripts/Moose/" +__Moose.ProgramPath = "Scripts/Moose/" -env.info( "Include.ProgramPath = " .. Include.ProgramPath) - -Include.Files = {} - -Include.File( "Moose" ) +__Moose.Includes = {} diff --git a/Moose Mission Setup/Moose Create Dynamic/Moose_Trace_On.lua b/Moose Mission Setup/Moose Create Dynamic/Moose_Trace_On.lua deleted file mode 100644 index 02de8a92e..000000000 --- a/Moose Mission Setup/Moose Create Dynamic/Moose_Trace_On.lua +++ /dev/null @@ -1,2 +0,0 @@ - -BASE:TraceOnOff( true ) diff --git a/Moose Mission Setup/Moose Create Static/Moose_Static_Loader.lua b/Moose Mission Setup/Moose Create Static/Moose_Static_Loader.lua index 90d2bcf13..8b1378917 100644 --- a/Moose Mission Setup/Moose Create Static/Moose_Static_Loader.lua +++ b/Moose Mission Setup/Moose Create Static/Moose_Static_Loader.lua @@ -1,7 +1 @@ -local base = _G - -Include = {} -Include.Files = {} -Include.File = function( IncludeFile ) -end diff --git a/Moose Mission Setup/Moose Create Static/Moose_Trace_Off.lua b/Moose Mission Setup/Moose Create Static/Moose_Trace_Off.lua deleted file mode 100644 index 876ccca23..000000000 --- a/Moose Mission Setup/Moose Create Static/Moose_Trace_Off.lua +++ /dev/null @@ -1,2 +0,0 @@ - -BASE:TraceOnOff( false ) diff --git a/Moose Mission Setup/Moose Mission Template/Moose_Mission_Template.miz b/Moose Mission Setup/Moose Mission Template/Moose_Mission_Template.miz deleted file mode 100644 index 5a3142fba..000000000 Binary files a/Moose Mission Setup/Moose Mission Template/Moose_Mission_Template.miz and /dev/null differ diff --git a/Moose Mission Setup/Moose Mission Update/7-zip.chm b/Moose Mission Setup/Moose Mission Update/7-zip.chm deleted file mode 100644 index 68f152ccb..000000000 Binary files a/Moose Mission Setup/Moose Mission Update/7-zip.chm and /dev/null differ diff --git a/Moose Mission Setup/Moose Mission Update/7-zip.dll b/Moose Mission Setup/Moose Mission Update/7-zip.dll deleted file mode 100644 index 6f0bd373f..000000000 Binary files a/Moose Mission Setup/Moose Mission Update/7-zip.dll and /dev/null differ diff --git a/Moose Mission Setup/Moose Mission Update/7-zip32.dll b/Moose Mission Setup/Moose Mission Update/7-zip32.dll deleted file mode 100644 index 1ea1da918..000000000 Binary files a/Moose Mission Setup/Moose Mission Update/7-zip32.dll and /dev/null differ diff --git a/Moose Mission Setup/Moose Mission Update/7z.dll b/Moose Mission Setup/Moose Mission Update/7z.dll deleted file mode 100644 index 450acaf6f..000000000 Binary files a/Moose Mission Setup/Moose Mission Update/7z.dll and /dev/null differ diff --git a/Moose Mission Setup/Moose Mission Update/7z.exe b/Moose Mission Setup/Moose Mission Update/7z.exe deleted file mode 100644 index e444ddf5e..000000000 Binary files a/Moose Mission Setup/Moose Mission Update/7z.exe and /dev/null differ diff --git a/Moose Mission Setup/Moose Mission Update/7z.sfx b/Moose Mission Setup/Moose Mission Update/7z.sfx deleted file mode 100644 index acf79e62e..000000000 Binary files a/Moose Mission Setup/Moose Mission Update/7z.sfx and /dev/null differ diff --git a/Moose Mission Setup/Moose Mission Update/7zCon.sfx b/Moose Mission Setup/Moose Mission Update/7zCon.sfx deleted file mode 100644 index 531e1778a..000000000 Binary files a/Moose Mission Setup/Moose Mission Update/7zCon.sfx and /dev/null differ diff --git a/Moose Mission Setup/Moose Mission Update/7zFM.exe b/Moose Mission Setup/Moose Mission Update/7zFM.exe deleted file mode 100644 index 89e81f9c0..000000000 Binary files a/Moose Mission Setup/Moose Mission Update/7zFM.exe and /dev/null differ diff --git a/Moose Mission Setup/Moose Mission Update/7zG.exe b/Moose Mission Setup/Moose Mission Update/7zG.exe deleted file mode 100644 index 4ceeb0f68..000000000 Binary files a/Moose Mission Setup/Moose Mission Update/7zG.exe and /dev/null differ diff --git a/Moose Mission Setup/Moose Mission Update/Moose_Update_Missions.bat b/Moose Mission Setup/Moose Mission Update/Moose_Update_Missions.bat index 5ccbaab56..82b6c3406 100644 --- a/Moose Mission Setup/Moose Mission Update/Moose_Update_Missions.bat +++ b/Moose Mission Setup/Moose Mission Update/Moose_Update_Missions.bat @@ -8,16 +8,16 @@ echo Path to Mission Files: %1 rem For /R %1 %%G IN (*.miz) do 7z u "%%G" "l10n\DEFAULT\Moose.lua" For /R %1 %%M IN (*.miz) do ( echo off - cd + cd > NUL: echo "Mission: %%M" mkdir Temp cd Temp mkdir l10n mkdir l10n\DEFAULT - copy ..\..\Moose.lua l10n\DEFAULT - copy "%%~pM%%~nM.lua" l10n\DEFAULT\*.* - dir l10n\DEFAULT - 7z -bb0 u "%%M" "l10n\DEFAULT\*.lua" + copy ..\..\Moose.lua l10n\DEFAULT > NUL: + copy "%%~pM%%~nM.lua" l10n\DEFAULT\*.* > NUL: + rem dir l10n\DEFAULT + "%~dp0..\..\Utils\7-Zip\7z" -bb0 u "%%M" "l10n\DEFAULT\*.lua" > NUL: cd .. rmdir /S /Q Temp ) \ No newline at end of file diff --git a/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua b/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua index 0135a9cc2..3b2b611ed 100644 --- a/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua +++ b/Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua @@ -1,35940 +1,31 @@ -env.info( '*** MOOSE STATIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170328_1316' ) +env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' ) +env.info( 'Moose Generation Timestamp: 20170328_0728' ) + local base = _G Include = {} -Include.Files = {} + Include.File = function( IncludeFile ) -end - ---- Various routines --- @module routines --- @author Flightcontrol - -env.setErrorMessageBoxEnabled(false) - ---- Extract of MIST functions. --- @author Grimes - -routines = {} - - --- don't change these -routines.majorVersion = 3 -routines.minorVersion = 3 -routines.build = 22 - ------------------------------------------------------------------------------------------------------------------ - ----------------------------------------------------------------------------------------------- --- Utils- conversion, Lua utils, etc. -routines.utils = {} - ---from http://lua-users.org/wiki/CopyTable -routines.utils.deepCopy = function(object) - local lookup_table = {} - local function _copy(object) - if type(object) ~= "table" then - return object - elseif lookup_table[object] then - return lookup_table[object] - end - local new_table = {} - lookup_table[object] = new_table - for index, value in pairs(object) do - new_table[_copy(index)] = _copy(value) - end - return setmetatable(new_table, getmetatable(object)) - end - local objectreturn = _copy(object) - return objectreturn -end - - --- porting in Slmod's serialize_slmod2 -routines.utils.oneLineSerialize = function(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function - - lookup_table = {} - - local function _Serialize( tbl ) - - if type(tbl) == 'table' then --function only works for tables! - - if lookup_table[tbl] then - return lookup_table[object] - end - - local tbl_str = {} - - lookup_table[tbl] = tbl_str - - tbl_str[#tbl_str + 1] = '{' - - for ind,val in pairs(tbl) do -- serialize its fields - local ind_str = {} - if type(ind) == "number" then - ind_str[#ind_str + 1] = '[' - ind_str[#ind_str + 1] = tostring(ind) - ind_str[#ind_str + 1] = ']=' - else --must be a string - ind_str[#ind_str + 1] = '[' - ind_str[#ind_str + 1] = routines.utils.basicSerialize(ind) - ind_str[#ind_str + 1] = ']=' - end - - local val_str = {} - if ((type(val) == 'number') or (type(val) == 'boolean')) then - val_str[#val_str + 1] = tostring(val) - val_str[#val_str + 1] = ',' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'string' then - val_str[#val_str + 1] = routines.utils.basicSerialize(val) - val_str[#val_str + 1] = ',' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'nil' then -- won't ever happen, right? - val_str[#val_str + 1] = 'nil,' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'table' then - if ind == "__index" then - -- tbl_str[#tbl_str + 1] = "__index" - -- tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - else - - val_str[#val_str + 1] = _Serialize(val) - val_str[#val_str + 1] = ',' --I think this is right, I just added it - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - end - elseif type(val) == 'function' then - -- tbl_str[#tbl_str + 1] = "function " .. tostring(ind) - -- tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - else --- env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) --- env.info( debug.traceback() ) - end - - end - tbl_str[#tbl_str + 1] = '}' - return table.concat(tbl_str) + if not Include.Files[ IncludeFile ] then + Include.Files[IncludeFile] = IncludeFile + env.info( "Include:" .. IncludeFile .. " from " .. Include.ProgramPath ) + local f = assert( base.loadfile( Include.ProgramPath .. IncludeFile .. ".lua" ) ) + if f == nil then + error ("Could not load MOOSE file " .. IncludeFile .. ".lua" ) else - return tostring(tbl) + env.info( "Include:" .. IncludeFile .. " loaded from " .. Include.ProgramPath ) + return f() end end - - local objectreturn = _Serialize(tbl) - return objectreturn end ---porting in Slmod's "safestring" basic serialize -routines.utils.basicSerialize = function(s) - if s == nil then - return "\"\"" - else - if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then - return tostring(s) - elseif type(s) == 'string' then - s = string.format('%q', s) - return s - end - end -end - - -routines.utils.toDegree = function(angle) - return angle*180/math.pi -end - -routines.utils.toRadian = function(angle) - return angle*math.pi/180 -end - -routines.utils.metersToNM = function(meters) - return meters/1852 -end - -routines.utils.metersToFeet = function(meters) - return meters/0.3048 -end - -routines.utils.NMToMeters = function(NM) - return NM*1852 -end - -routines.utils.feetToMeters = function(feet) - return feet*0.3048 -end - -routines.utils.mpsToKnots = function(mps) - return mps*3600/1852 -end - -routines.utils.mpsToKmph = function(mps) - return mps*3.6 -end - -routines.utils.knotsToMps = function(knots) - return knots*1852/3600 -end - -routines.utils.kmphToMps = function(kmph) - return kmph/3.6 -end - -function routines.utils.makeVec2(Vec3) - if Vec3.z then - return {x = Vec3.x, y = Vec3.z} - else - return {x = Vec3.x, y = Vec3.y} -- it was actually already vec2. - end -end - -function routines.utils.makeVec3(Vec2, y) - if not Vec2.z then - if not y then - y = 0 - end - return {x = Vec2.x, y = y, z = Vec2.y} - else - return {x = Vec2.x, y = Vec2.y, z = Vec2.z} -- it was already Vec3, actually. - end -end - -function routines.utils.makeVec3GL(Vec2, offset) - local adj = offset or 0 - - if not Vec2.z then - return {x = Vec2.x, y = (land.getHeight(Vec2) + adj), z = Vec2.y} - else - return {x = Vec2.x, y = (land.getHeight({x = Vec2.x, y = Vec2.z}) + adj), z = Vec2.z} - end -end - -routines.utils.zoneToVec3 = function(zone) - local new = {} - if type(zone) == 'table' and zone.point then - new.x = zone.point.x - new.y = zone.point.y - new.z = zone.point.z - return new - elseif type(zone) == 'string' then - zone = trigger.misc.getZone(zone) - if zone then - new.x = zone.point.x - new.y = zone.point.y - new.z = zone.point.z - return new - end - end -end - --- gets heading-error corrected direction from point along vector vec. -function routines.utils.getDir(vec, point) - local dir = math.atan2(vec.z, vec.x) - dir = dir + routines.getNorthCorrection(point) - if dir < 0 then - dir = dir + 2*math.pi -- put dir in range of 0 to 2*pi - end - return dir -end - --- gets distance in meters between two points (2 dimensional) -function routines.utils.get2DDist(point1, point2) - point1 = routines.utils.makeVec3(point1) - point2 = routines.utils.makeVec3(point2) - return routines.vec.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z}) -end - --- gets distance in meters between two points (3 dimensional) -function routines.utils.get3DDist(point1, point2) - return routines.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) -end - - - - - ---3D Vector manipulation -routines.vec = {} - -routines.vec.add = function(vec1, vec2) - return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} -end - -routines.vec.sub = function(vec1, vec2) - return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} -end - -routines.vec.scalarMult = function(vec, mult) - return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} -end - -routines.vec.scalar_mult = routines.vec.scalarMult - -routines.vec.dp = function(vec1, vec2) - return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z -end - -routines.vec.cp = function(vec1, vec2) - return { x = vec1.y*vec2.z - vec1.z*vec2.y, y = vec1.z*vec2.x - vec1.x*vec2.z, z = vec1.x*vec2.y - vec1.y*vec2.x} -end - -routines.vec.mag = function(vec) - return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 -end - -routines.vec.getUnitVec = function(vec) - local mag = routines.vec.mag(vec) - return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } -end - -routines.vec.rotateVec2 = function(vec2, theta) - return { x = vec2.x*math.cos(theta) - vec2.y*math.sin(theta), y = vec2.x*math.sin(theta) + vec2.y*math.cos(theta)} -end ---------------------------------------------------------------------------------------------------------------------------- - - - - --- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5. -routines.tostringMGRS = function(MGRS, acc) - if acc == 0 then - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph - else - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', routines.utils.round(MGRS.Easting/(10^(5-acc)), 0)) - .. ' ' .. string.format('%0' .. acc .. 'd', routines.utils.round(MGRS.Northing/(10^(5-acc)), 0)) - end -end - ---[[acc: -in DM: decimal point of minutes. -In DMS: decimal point of seconds. -position after the decimal of the least significant digit: -So: -42.32 - acc of 2. -]] -routines.tostringLL = function(lat, lon, acc, DMS) - - local latHemi, lonHemi - if lat > 0 then - latHemi = 'N' - else - latHemi = 'S' - end - - if lon > 0 then - lonHemi = 'E' - else - lonHemi = 'W' - end - - lat = math.abs(lat) - lon = math.abs(lon) - - local latDeg = math.floor(lat) - local latMin = (lat - latDeg)*60 - - local lonDeg = math.floor(lon) - local lonMin = (lon - lonDeg)*60 - - if DMS then -- degrees, minutes, and seconds. - local oldLatMin = latMin - latMin = math.floor(latMin) - local latSec = routines.utils.round((oldLatMin - latMin)*60, acc) - - local oldLonMin = lonMin - lonMin = math.floor(lonMin) - local lonSec = routines.utils.round((oldLonMin - lonMin)*60, acc) - - if latSec == 60 then - latSec = 0 - latMin = latMin + 1 - end - - if lonSec == 60 then - lonSec = 0 - lonMin = lonMin + 1 - end - - local secFrmtStr -- create the formatting string for the seconds place - if acc <= 0 then -- no decimal place. - secFrmtStr = '%02d' - else - local width = 3 + acc -- 01.310 - that's a width of 6, for example. - secFrmtStr = '%0' .. width .. '.' .. acc .. 'f' - end - - return string.format('%02d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' ' - .. string.format('%02d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi - - else -- degrees, decimal minutes. - latMin = routines.utils.round(latMin, acc) - lonMin = routines.utils.round(lonMin, acc) - - if latMin == 60 then - latMin = 0 - latDeg = latDeg + 1 - end - - if lonMin == 60 then - lonMin = 0 - lonDeg = lonDeg + 1 - end - - local minFrmtStr -- create the formatting string for the minutes place - if acc <= 0 then -- no decimal place. - minFrmtStr = '%02d' - else - local width = 3 + acc -- 01.310 - that's a width of 6, for example. - minFrmtStr = '%0' .. width .. '.' .. acc .. 'f' - end - - return string.format('%02d', latDeg) .. ' ' .. string.format(minFrmtStr, latMin) .. '\'' .. latHemi .. ' ' - .. string.format('%02d', lonDeg) .. ' ' .. string.format(minFrmtStr, lonMin) .. '\'' .. lonHemi - - end -end - ---[[ required: az - radian - required: dist - meters - optional: alt - meters (set to false or nil if you don't want to use it). - optional: metric - set true to get dist and alt in km and m. - precision will always be nearest degree and NM or km.]] -routines.tostringBR = function(az, dist, alt, metric) - az = routines.utils.round(routines.utils.toDegree(az), 0) - - if metric then - dist = routines.utils.round(dist/1000, 2) - else - dist = routines.utils.round(routines.utils.metersToNM(dist), 2) - end - - local s = string.format('%03d', az) .. ' for ' .. dist - - if alt then - if metric then - s = s .. ' at ' .. routines.utils.round(alt, 0) - else - s = s .. ' at ' .. routines.utils.round(routines.utils.metersToFeet(alt), 0) - end - end - return s -end - -routines.getNorthCorrection = function(point) --gets the correction needed for true north - if not point.z then --Vec2; convert to Vec3 - point.z = point.y - point.y = 0 - end - local lat, lon = coord.LOtoLL(point) - local north_posit = coord.LLtoLO(lat + 1, lon) - return math.atan2(north_posit.z - point.z, north_posit.x - point.x) -end - - -do - local idNum = 0 - - --Simplified event handler - routines.addEventHandler = function(f) --id is optional! - local handler = {} - idNum = idNum + 1 - handler.id = idNum - handler.f = f - handler.onEvent = function(self, event) - self.f(event) - end - world.addEventHandler(handler) - end - - routines.removeEventHandler = function(id) - for key, handler in pairs(world.eventHandlers) do - if handler.id and handler.id == id then - world.eventHandlers[key] = nil - return true - end - end - return false - end -end - --- need to return a Vec3 or Vec2? -function routines.getRandPointInCircle(point, radius, innerRadius) - local theta = 2*math.pi*math.random() - local rad = math.random() + math.random() - if rad > 1 then - rad = 2 - rad - end - - local radMult - if innerRadius and innerRadius <= radius then - radMult = (radius - innerRadius)*rad + innerRadius - else - radMult = radius*rad - end - - if not point.z then --might as well work with vec2/3 - point.z = point.y - end - - local rndCoord - if radius > 0 then - rndCoord = {x = math.cos(theta)*radMult + point.x, y = math.sin(theta)*radMult + point.z} - else - rndCoord = {x = point.x, y = point.z} - end - return rndCoord -end - -routines.goRoute = function(group, path) - local misTask = { - id = 'Mission', - params = { - route = { - points = routines.utils.deepCopy(path), - }, - }, - } - if type(group) == 'string' then - group = Group.getByName(group) - end - local groupCon = group:getController() - if groupCon then - groupCon:setTask(misTask) - return true - end - - Controller.setTask(groupCon, misTask) - return false -end - - --- Useful atomic functions from mist, ported. - -routines.ground = {} -routines.fixedWing = {} -routines.heli = {} - -routines.ground.buildWP = function(point, overRideForm, overRideSpeed) - - local wp = {} - wp.x = point.x - - if point.z then - wp.y = point.z - else - wp.y = point.y - end - local form, speed - - if point.speed and not overRideSpeed then - wp.speed = point.speed - elseif type(overRideSpeed) == 'number' then - wp.speed = overRideSpeed - else - wp.speed = routines.utils.kmphToMps(20) - end - - if point.form and not overRideForm then - form = point.form - else - form = overRideForm - end - - if not form then - wp.action = 'Cone' - else - form = string.lower(form) - if form == 'off_road' or form == 'off road' then - wp.action = 'Off Road' - elseif form == 'on_road' or form == 'on road' then - wp.action = 'On Road' - elseif form == 'rank' or form == 'line_abrest' or form == 'line abrest' or form == 'lineabrest'then - wp.action = 'Rank' - elseif form == 'cone' then - wp.action = 'Cone' - elseif form == 'diamond' then - wp.action = 'Diamond' - elseif form == 'vee' then - wp.action = 'Vee' - elseif form == 'echelon_left' or form == 'echelon left' or form == 'echelonl' then - wp.action = 'EchelonL' - elseif form == 'echelon_right' or form == 'echelon right' or form == 'echelonr' then - wp.action = 'EchelonR' - else - wp.action = 'Cone' -- if nothing matched - end - end - - wp.type = 'Turning Point' - - return wp - -end - -routines.fixedWing.buildWP = function(point, WPtype, speed, alt, altType) - - local wp = {} - wp.x = point.x - - if point.z then - wp.y = point.z - else - wp.y = point.y - end - - if alt and type(alt) == 'number' then - wp.alt = alt - else - wp.alt = 2000 - end - - if altType then - altType = string.lower(altType) - if altType == 'radio' or 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or 'asl' then - wp.alt_type = 'BARO' - end - else - wp.alt_type = 'RADIO' - end - - if point.speed then - speed = point.speed - end - - if point.type then - WPtype = point.type - end - - if not speed then - wp.speed = routines.utils.kmphToMps(500) - else - wp.speed = speed - end - - if not WPtype then - wp.action = 'Turning Point' - else - WPtype = string.lower(WPtype) - if WPtype == 'flyover' or WPtype == 'fly over' or WPtype == 'fly_over' then - wp.action = 'Fly Over Point' - elseif WPtype == 'turningpoint' or WPtype == 'turning point' or WPtype == 'turning_point' then - wp.action = 'Turning Point' - else - wp.action = 'Turning Point' - end - end - - wp.type = 'Turning Point' - return wp -end - -routines.heli.buildWP = function(point, WPtype, speed, alt, altType) - - local wp = {} - wp.x = point.x - - if point.z then - wp.y = point.z - else - wp.y = point.y - end - - if alt and type(alt) == 'number' then - wp.alt = alt - else - wp.alt = 500 - end - - if altType then - altType = string.lower(altType) - if altType == 'radio' or 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or 'asl' then - wp.alt_type = 'BARO' - end - else - wp.alt_type = 'RADIO' - end - - if point.speed then - speed = point.speed - end - - if point.type then - WPtype = point.type - end - - if not speed then - wp.speed = routines.utils.kmphToMps(200) - else - wp.speed = speed - end - - if not WPtype then - wp.action = 'Turning Point' - else - WPtype = string.lower(WPtype) - if WPtype == 'flyover' or WPtype == 'fly over' or WPtype == 'fly_over' then - wp.action = 'Fly Over Point' - elseif WPtype == 'turningpoint' or WPtype == 'turning point' or WPtype == 'turning_point' then - wp.action = 'Turning Point' - else - wp.action = 'Turning Point' - end - end - - wp.type = 'Turning Point' - return wp -end - -routines.groupToRandomPoint = function(vars) - local group = vars.group --Required - local point = vars.point --required - local radius = vars.radius or 0 - local innerRadius = vars.innerRadius - local form = vars.form or 'Cone' - local heading = vars.heading or math.random()*2*math.pi - local headingDegrees = vars.headingDegrees - local speed = vars.speed or routines.utils.kmphToMps(20) - - - local useRoads - if not vars.disableRoads then - useRoads = true - else - useRoads = false - end - - local path = {} - - if headingDegrees then - heading = headingDegrees*math.pi/180 - end - - if heading >= 2*math.pi then - heading = heading - 2*math.pi - end - - local rndCoord = routines.getRandPointInCircle(point, radius, innerRadius) - - local offset = {} - local posStart = routines.getLeadPos(group) - - offset.x = routines.utils.round(math.sin(heading - (math.pi/2)) * 50 + rndCoord.x, 3) - offset.z = routines.utils.round(math.cos(heading + (math.pi/2)) * 50 + rndCoord.y, 3) - path[#path + 1] = routines.ground.buildWP(posStart, form, speed) - - - if useRoads == true and ((point.x - posStart.x)^2 + (point.z - posStart.z)^2)^0.5 > radius * 1.3 then - path[#path + 1] = routines.ground.buildWP({['x'] = posStart.x + 11, ['z'] = posStart.z + 11}, 'off_road', speed) - path[#path + 1] = routines.ground.buildWP(posStart, 'on_road', speed) - path[#path + 1] = routines.ground.buildWP(offset, 'on_road', speed) - else - path[#path + 1] = routines.ground.buildWP({['x'] = posStart.x + 25, ['z'] = posStart.z + 25}, form, speed) - end - - path[#path + 1] = routines.ground.buildWP(offset, form, speed) - path[#path + 1] = routines.ground.buildWP(rndCoord, form, speed) - - routines.goRoute(group, path) - - return -end - -routines.groupRandomDistSelf = function(gpData, dist, form, heading, speed) - local pos = routines.getLeadPos(gpData) - local fakeZone = {} - fakeZone.radius = dist or math.random(300, 1000) - fakeZone.point = {x = pos.x, y, pos.y, z = pos.z} - routines.groupToRandomZone(gpData, fakeZone, form, heading, speed) - - return -end - -routines.groupToRandomZone = function(gpData, zone, form, heading, speed) - if type(gpData) == 'string' then - gpData = Group.getByName(gpData) - end - - if type(zone) == 'string' then - zone = trigger.misc.getZone(zone) - elseif type(zone) == 'table' and not zone.radius then - zone = trigger.misc.getZone(zone[math.random(1, #zone)]) - end - - if speed then - speed = routines.utils.kmphToMps(speed) - end - - local vars = {} - vars.group = gpData - vars.radius = zone.radius - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.point = routines.utils.zoneToVec3(zone) - - routines.groupToRandomPoint(vars) - - return -end - -routines.isTerrainValid = function(coord, terrainTypes) -- vec2/3 and enum or table of acceptable terrain types - if coord.z then - coord.y = coord.z - end - local typeConverted = {} - - if type(terrainTypes) == 'string' then -- if its a string it does this check - for constId, constData in pairs(land.SurfaceType) do - if string.lower(constId) == string.lower(terrainTypes) or string.lower(constData) == string.lower(terrainTypes) then - table.insert(typeConverted, constId) - end - end - elseif type(terrainTypes) == 'table' then -- if its a table it does this check - for typeId, typeData in pairs(terrainTypes) do - for constId, constData in pairs(land.SurfaceType) do - if string.lower(constId) == string.lower(typeData) or string.lower(constData) == string.lower(typeId) then - table.insert(typeConverted, constId) - end - end - end - end - for validIndex, validData in pairs(typeConverted) do - if land.getSurfaceType(coord) == land.SurfaceType[validData] then - return true - end - end - return false -end - -routines.groupToPoint = function(gpData, point, form, heading, speed, useRoads) - if type(point) == 'string' then - point = trigger.misc.getZone(point) - end - if speed then - speed = routines.utils.kmphToMps(speed) - end - - local vars = {} - vars.group = gpData - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.disableRoads = useRoads - vars.point = routines.utils.zoneToVec3(point) - routines.groupToRandomPoint(vars) - - return -end - - -routines.getLeadPos = function(group) - if type(group) == 'string' then -- group name - group = Group.getByName(group) - end - - local units = group:getUnits() - - local leader = units[1] - if not leader then -- SHOULD be good, but if there is a bug, this code future-proofs it then. - local lowestInd = math.huge - for ind, unit in pairs(units) do - if ind < lowestInd then - lowestInd = ind - leader = unit - end - end - end - if leader and Unit.isExist(leader) then -- maybe a little too paranoid now... - return leader:getPosition().p - end -end - ---[[ vars for routines.getMGRSString: -vars.units - table of unit names (NOT unitNameTable- maybe this should change). -vars.acc - integer between 0 and 5, inclusive -]] -routines.getMGRSString = function(vars) - local units = vars.units - local acc = vars.acc or 5 - local avgPos = routines.getAvgPos(units) - if avgPos then - return routines.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(avgPos)), acc) - end -end - ---[[ vars for routines.getLLString -vars.units - table of unit names (NOT unitNameTable- maybe this should change). -vars.acc - integer, number of numbers after decimal place -vars.DMS - if true, output in degrees, minutes, seconds. Otherwise, output in degrees, minutes. - - -]] -routines.getLLString = function(vars) - local units = vars.units - local acc = vars.acc or 3 - local DMS = vars.DMS - local avgPos = routines.getAvgPos(units) - if avgPos then - local lat, lon = coord.LOtoLL(avgPos) - return routines.tostringLL(lat, lon, acc, DMS) - end -end - ---[[ -vars.zone - table of a zone name. -vars.ref - vec3 ref point, maybe overload for vec2 as well? -vars.alt - boolean, if used, includes altitude in string -vars.metric - boolean, gives distance in km instead of NM. -]] -routines.getBRStringZone = function(vars) - local zone = trigger.misc.getZone( vars.zone ) - local ref = routines.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. - local alt = vars.alt - local metric = vars.metric - if zone then - local vec = {x = zone.point.x - ref.x, y = zone.point.y - ref.y, z = zone.point.z - ref.z} - local dir = routines.utils.getDir(vec, ref) - local dist = routines.utils.get2DDist(zone.point, ref) - if alt then - alt = zone.y - end - return routines.tostringBR(dir, dist, alt, metric) - else - env.info( 'routines.getBRStringZone: error: zone is nil' ) - end -end - ---[[ -vars.units- table of unit names (NOT unitNameTable- maybe this should change). -vars.ref - vec3 ref point, maybe overload for vec2 as well? -vars.alt - boolean, if used, includes altitude in string -vars.metric - boolean, gives distance in km instead of NM. -]] -routines.getBRString = function(vars) - local units = vars.units - local ref = routines.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. - local alt = vars.alt - local metric = vars.metric - local avgPos = routines.getAvgPos(units) - if avgPos then - local vec = {x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z} - local dir = routines.utils.getDir(vec, ref) - local dist = routines.utils.get2DDist(avgPos, ref) - if alt then - alt = avgPos.y - end - return routines.tostringBR(dir, dist, alt, metric) - end -end - - --- Returns the Vec3 coordinates of the average position of the concentration of units most in the heading direction. ---[[ vars for routines.getLeadingPos: -vars.units - table of unit names -vars.heading - direction -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees -]] -routines.getLeadingPos = function(vars) - local units = vars.units - local heading = vars.heading - local radius = vars.radius - if vars.headingDegrees then - heading = routines.utils.toRadian(vars.headingDegrees) - end - - local unitPosTbl = {} - for i = 1, #units do - local unit = Unit.getByName(units[i]) - if unit and unit:isExist() then - unitPosTbl[#unitPosTbl + 1] = unit:getPosition().p - end - end - if #unitPosTbl > 0 then -- one more more units found. - -- first, find the unit most in the heading direction - local maxPos = -math.huge - - local maxPosInd -- maxPos - the furthest in direction defined by heading; maxPosInd = - for i = 1, #unitPosTbl do - local rotatedVec2 = routines.vec.rotateVec2(routines.utils.makeVec2(unitPosTbl[i]), heading) - if (not maxPos) or maxPos < rotatedVec2.x then - maxPos = rotatedVec2.x - maxPosInd = i - end - end - - --now, get all the units around this unit... - local avgPos - if radius then - local maxUnitPos = unitPosTbl[maxPosInd] - local avgx, avgy, avgz, totNum = 0, 0, 0, 0 - for i = 1, #unitPosTbl do - if routines.utils.get2DDist(maxUnitPos, unitPosTbl[i]) <= radius then - avgx = avgx + unitPosTbl[i].x - avgy = avgy + unitPosTbl[i].y - avgz = avgz + unitPosTbl[i].z - totNum = totNum + 1 - end - end - avgPos = { x = avgx/totNum, y = avgy/totNum, z = avgz/totNum} - else - avgPos = unitPosTbl[maxPosInd] - end - - return avgPos - end -end - - ---[[ vars for routines.getLeadingMGRSString: -vars.units - table of unit names -vars.heading - direction -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees -vars.acc - number, 0 to 5. -]] -routines.getLeadingMGRSString = function(vars) - local pos = routines.getLeadingPos(vars) - if pos then - local acc = vars.acc or 5 - return routines.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(pos)), acc) - end -end - ---[[ vars for routines.getLeadingLLString: -vars.units - table of unit names -vars.heading - direction, number -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees -vars.acc - number of digits after decimal point (can be negative) -vars.DMS - boolean, true if you want DMS. -]] -routines.getLeadingLLString = function(vars) - local pos = routines.getLeadingPos(vars) - if pos then - local acc = vars.acc or 3 - local DMS = vars.DMS - local lat, lon = coord.LOtoLL(pos) - return routines.tostringLL(lat, lon, acc, DMS) - end -end - - - ---[[ vars for routines.getLeadingBRString: -vars.units - table of unit names -vars.heading - direction, number -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees -vars.metric - boolean, if true, use km instead of NM. -vars.alt - boolean, if true, include altitude. -vars.ref - vec3/vec2 reference point. -]] -routines.getLeadingBRString = function(vars) - local pos = routines.getLeadingPos(vars) - if pos then - local ref = vars.ref - local alt = vars.alt - local metric = vars.metric - - local vec = {x = pos.x - ref.x, y = pos.y - ref.y, z = pos.z - ref.z} - local dir = routines.utils.getDir(vec, ref) - local dist = routines.utils.get2DDist(pos, ref) - if alt then - alt = pos.y - end - return routines.tostringBR(dir, dist, alt, metric) - end -end - ---[[ vars for routines.message.add - vars.text = 'Hello World' - vars.displayTime = 20 - vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} - -]] - ---[[ vars for routines.msgMGRS -vars.units - table of unit names (NOT unitNameTable- maybe this should change). -vars.acc - integer between 0 and 5, inclusive -vars.text - text in the message -vars.displayTime - self explanatory -vars.msgFor - scope -]] -routines.msgMGRS = function(vars) - local units = vars.units - local acc = vars.acc - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getMGRSString{units = units, acc = acc} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } -end - ---[[ vars for routines.msgLL -vars.units - table of unit names (NOT unitNameTable- maybe this should change) (Yes). -vars.acc - integer, number of numbers after decimal place -vars.DMS - if true, output in degrees, minutes, seconds. Otherwise, output in degrees, minutes. -vars.text - text in the message -vars.displayTime - self explanatory -vars.msgFor - scope -]] -routines.msgLL = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local acc = vars.acc - local DMS = vars.DMS - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getLLString{units = units, acc = acc, DMS = DMS} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - -end - - ---[[ -vars.units- table of unit names (NOT unitNameTable- maybe this should change). -vars.ref - vec3 ref point, maybe overload for vec2 as well? -vars.alt - boolean, if used, includes altitude in string -vars.metric - boolean, gives distance in km instead of NM. -vars.text - text of the message -vars.displayTime -vars.msgFor - scope -]] -routines.msgBR = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local ref = vars.ref -- vec2/vec3 will be handled in routines.getBRString - local alt = vars.alt - local metric = vars.metric - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getBRString{units = units, ref = ref, alt = alt, metric = metric} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - -end - - --------------------------------------------------------------------------------------------- --- basically, just sub-types of routines.msgBR... saves folks the work of getting the ref point. ---[[ -vars.units- table of unit names (NOT unitNameTable- maybe this should change). -vars.ref - string red, blue -vars.alt - boolean, if used, includes altitude in string -vars.metric - boolean, gives distance in km instead of NM. -vars.text - text of the message -vars.displayTime -vars.msgFor - scope -]] -routines.msgBullseye = function(vars) - if string.lower(vars.ref) == 'red' then - vars.ref = routines.DBs.missionData.bullseye.red - routines.msgBR(vars) - elseif string.lower(vars.ref) == 'blue' then - vars.ref = routines.DBs.missionData.bullseye.blue - routines.msgBR(vars) - end -end - ---[[ -vars.units- table of unit names (NOT unitNameTable- maybe this should change). -vars.ref - unit name of reference point -vars.alt - boolean, if used, includes altitude in string -vars.metric - boolean, gives distance in km instead of NM. -vars.text - text of the message -vars.displayTime -vars.msgFor - scope -]] - -routines.msgBRA = function(vars) - if Unit.getByName(vars.ref) then - vars.ref = Unit.getByName(vars.ref):getPosition().p - if not vars.alt then - vars.alt = true - end - routines.msgBR(vars) - end -end --------------------------------------------------------------------------------------------- - ---[[ vars for routines.msgLeadingMGRS: -vars.units - table of unit names -vars.heading - direction -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees (optional) -vars.acc - number, 0 to 5. -vars.text - text of the message -vars.displayTime -vars.msgFor - scope -]] -routines.msgLeadingMGRS = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local heading = vars.heading - local radius = vars.radius - local headingDegrees = vars.headingDegrees - local acc = vars.acc - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - - -end ---[[ vars for routines.msgLeadingLL: -vars.units - table of unit names -vars.heading - direction, number -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees (optional) -vars.acc - number of digits after decimal point (can be negative) -vars.DMS - boolean, true if you want DMS. (optional) -vars.text - text of the message -vars.displayTime -vars.msgFor - scope -]] -routines.msgLeadingLL = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local heading = vars.heading - local radius = vars.radius - local headingDegrees = vars.headingDegrees - local acc = vars.acc - local DMS = vars.DMS - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - -end - ---[[ -vars.units - table of unit names -vars.heading - direction, number -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees (optional) -vars.metric - boolean, if true, use km instead of NM. (optional) -vars.alt - boolean, if true, include altitude. (optional) -vars.ref - vec3/vec2 reference point. -vars.text - text of the message -vars.displayTime -vars.msgFor - scope -]] -routines.msgLeadingBR = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local heading = vars.heading - local radius = vars.radius - local headingDegrees = vars.headingDegrees - local metric = vars.metric - local alt = vars.alt - local ref = vars.ref -- vec2/vec3 will be handled in routines.getBRString - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } -end - - -function spairs(t, order) - -- collect the keys - local keys = {} - for k in pairs(t) do keys[#keys+1] = k end - - -- if order function given, sort by it by passing the table and keys a, b, - -- otherwise just sort the keys - if order then - table.sort(keys, function(a,b) return order(t, a, b) end) - else - table.sort(keys) - end - - -- return the iterator function - local i = 0 - return function() - i = i + 1 - if keys[i] then - return keys[i], t[keys[i]] - end - end -end - - -function routines.IsPartOfGroupInZones( CargoGroup, LandingZones ) ---trace.f() - - local CurrentZoneID = nil - - if CargoGroup then - local CargoUnits = CargoGroup:getUnits() - for CargoUnitID, CargoUnit in pairs( CargoUnits ) do - if CargoUnit and CargoUnit:getLife() >= 1.0 then - CurrentZoneID = routines.IsUnitInZones( CargoUnit, LandingZones ) - if CurrentZoneID then - break - end - end - end - end - ---trace.r( "", "", { CurrentZoneID } ) - return CurrentZoneID -end - - - -function routines.IsUnitInZones( TransportUnit, LandingZones ) ---trace.f("", "routines.IsUnitInZones" ) - - local TransportZoneResult = nil - local TransportZonePos = nil - local TransportZone = nil - - -- fill-up some local variables to support further calculations to determine location of units within the zone. - if TransportUnit then - local TransportUnitPos = TransportUnit:getPosition().p - if type( LandingZones ) == "table" then - for LandingZoneID, LandingZoneName in pairs( LandingZones ) do - TransportZone = trigger.misc.getZone( LandingZoneName ) - if TransportZone then - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then - TransportZoneResult = LandingZoneID - break - end - end - end - else - TransportZone = trigger.misc.getZone( LandingZones ) - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then - TransportZoneResult = 1 - end - end - if TransportZoneResult then - --trace.i( "routines", "TransportZone:" .. TransportZoneResult ) - else - --trace.i( "routines", "TransportZone:nil logic" ) - end - return TransportZoneResult - else - --trace.i( "routines", "TransportZone:nil hard" ) - return nil - end -end - -function routines.IsUnitNearZonesRadius( TransportUnit, LandingZones, ZoneRadius ) ---trace.f("", "routines.IsUnitInZones" ) - - local TransportZoneResult = nil - local TransportZonePos = nil - local TransportZone = nil - - -- fill-up some local variables to support further calculations to determine location of units within the zone. - if TransportUnit then - local TransportUnitPos = TransportUnit:getPosition().p - if type( LandingZones ) == "table" then - for LandingZoneID, LandingZoneName in pairs( LandingZones ) do - TransportZone = trigger.misc.getZone( LandingZoneName ) - if TransportZone then - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= ZoneRadius ) then - TransportZoneResult = LandingZoneID - break - end - end - end - else - TransportZone = trigger.misc.getZone( LandingZones ) - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= ZoneRadius ) then - TransportZoneResult = 1 - end - end - if TransportZoneResult then - --trace.i( "routines", "TransportZone:" .. TransportZoneResult ) - else - --trace.i( "routines", "TransportZone:nil logic" ) - end - return TransportZoneResult - else - --trace.i( "routines", "TransportZone:nil hard" ) - return nil - end -end - - -function routines.IsStaticInZones( TransportStatic, LandingZones ) ---trace.f() - - local TransportZoneResult = nil - local TransportZonePos = nil - local TransportZone = nil - - -- fill-up some local variables to support further calculations to determine location of units within the zone. - local TransportStaticPos = TransportStatic:getPosition().p - if type( LandingZones ) == "table" then - for LandingZoneID, LandingZoneName in pairs( LandingZones ) do - TransportZone = trigger.misc.getZone( LandingZoneName ) - if TransportZone then - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportStaticPos.x - TransportZonePos.x)^2 + (TransportStaticPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then - TransportZoneResult = LandingZoneID - break - end - end - end - else - TransportZone = trigger.misc.getZone( LandingZones ) - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportStaticPos.x - TransportZonePos.x)^2 + (TransportStaticPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then - TransportZoneResult = 1 - end - end - ---trace.r( "", "", { TransportZoneResult } ) - return TransportZoneResult -end - - -function routines.IsUnitInRadius( CargoUnit, ReferencePosition, Radius ) ---trace.f() - - local Valid = true - - -- fill-up some local variables to support further calculations to determine location of units within the zone. - local CargoPos = CargoUnit:getPosition().p - local ReferenceP = ReferencePosition.p - - if (((CargoPos.x - ReferenceP.x)^2 + (CargoPos.z - ReferenceP.z)^2)^0.5 <= Radius) then - else - Valid = false - end - - return Valid -end - -function routines.IsPartOfGroupInRadius( CargoGroup, ReferencePosition, Radius ) ---trace.f() - - local Valid = true - - Valid = routines.ValidateGroup( CargoGroup, "CargoGroup", Valid ) - - -- fill-up some local variables to support further calculations to determine location of units within the zone - local CargoUnits = CargoGroup:getUnits() - for CargoUnitId, CargoUnit in pairs( CargoUnits ) do - local CargoUnitPos = CargoUnit:getPosition().p --- env.info( 'routines.IsPartOfGroupInRadius: CargoUnitPos.x = ' .. CargoUnitPos.x .. ' CargoUnitPos.z = ' .. CargoUnitPos.z ) - local ReferenceP = ReferencePosition.p --- env.info( 'routines.IsPartOfGroupInRadius: ReferenceGroupPos.x = ' .. ReferenceGroupPos.x .. ' ReferenceGroupPos.z = ' .. ReferenceGroupPos.z ) - - if ((( CargoUnitPos.x - ReferenceP.x)^2 + (CargoUnitPos.z - ReferenceP.z)^2)^0.5 <= Radius) then - else - Valid = false - break - end - end - - return Valid -end - - -function routines.ValidateString( Variable, VariableName, Valid ) ---trace.f() - - if type( Variable ) == "string" then - if Variable == "" then - error( "routines.ValidateString: error: " .. VariableName .. " must be filled out!" ) - Valid = false - end - else - error( "routines.ValidateString: error: " .. VariableName .. " is not a string." ) - Valid = false - end - ---trace.r( "", "", { Valid } ) - return Valid -end - -function routines.ValidateNumber( Variable, VariableName, Valid ) ---trace.f() - - if type( Variable ) == "number" then - else - error( "routines.ValidateNumber: error: " .. VariableName .. " is not a number." ) - Valid = false - end - ---trace.r( "", "", { Valid } ) - return Valid - -end - -function routines.ValidateGroup( Variable, VariableName, Valid ) ---trace.f() - - if Variable == nil then - error( "routines.ValidateGroup: error: " .. VariableName .. " is a nil value!" ) - Valid = false - end - ---trace.r( "", "", { Valid } ) - return Valid -end - -function routines.ValidateZone( LandingZones, VariableName, Valid ) ---trace.f() - - if LandingZones == nil then - error( "routines.ValidateGroup: error: " .. VariableName .. " is a nil value!" ) - Valid = false - end - - if type( LandingZones ) == "table" then - for LandingZoneID, LandingZoneName in pairs( LandingZones ) do - if trigger.misc.getZone( LandingZoneName ) == nil then - error( "routines.ValidateGroup: error: Zone " .. LandingZoneName .. " does not exist!" ) - Valid = false - break - end - end - else - if trigger.misc.getZone( LandingZones ) == nil then - error( "routines.ValidateGroup: error: Zone " .. LandingZones .. " does not exist!" ) - Valid = false - end - end - ---trace.r( "", "", { Valid } ) - return Valid -end - -function routines.ValidateEnumeration( Variable, VariableName, Enum, Valid ) ---trace.f() - - local ValidVariable = false - - for EnumId, EnumData in pairs( Enum ) do - if Variable == EnumData then - ValidVariable = true - break - end - end - - if ValidVariable then - else - error( 'TransportValidateEnum: " .. VariableName .. " is not a valid type.' .. Variable ) - Valid = false - end - ---trace.r( "", "", { Valid } ) - return Valid -end - -function routines.getGroupRoute(groupIdent, task) -- same as getGroupPoints but returns speed and formation type along with vec2 of point} - -- refactor to search by groupId and allow groupId and groupName as inputs - local gpId = groupIdent - if type(groupIdent) == 'string' and not tonumber(groupIdent) then - gpId = _DATABASE.Templates.Groups[groupIdent].groupId - end - - for coa_name, coa_data in pairs(env.mission.coalition) do - if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - for obj_type_name, obj_type_data in pairs(cntry_data) do - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - for group_num, group_data in pairs(obj_type_data.group) do - if group_data and group_data.groupId == gpId then -- this is the group we are looking for - if group_data.route and group_data.route.points and #group_data.route.points > 0 then - local points = {} - - for point_num, point in pairs(group_data.route.points) do - local routeData = {} - if not point.point then - routeData.x = point.x - routeData.y = point.y - else - routeData.point = point.point --it's possible that the ME could move to the point = Vec2 notation. - end - routeData.form = point.action - routeData.speed = point.speed - routeData.alt = point.alt - routeData.alt_type = point.alt_type - routeData.airdromeId = point.airdromeId - routeData.helipadId = point.helipadId - routeData.type = point.type - routeData.action = point.action - if task then - routeData.task = point.task - end - points[point_num] = routeData - end - - return points - end - return - end --if group_data and group_data.name and group_data.name == 'groupname' - end --for group_num, group_data in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do - end --for cntry_id, cntry_data in pairs(coa_data.country) do - end --if coa_data.country then --there is a country table - end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then - end --for coa_name, coa_data in pairs(mission.coalition) do -end - -routines.ground.patrolRoute = function(vars) - - - local tempRoute = {} - local useRoute = {} - local gpData = vars.gpData - if type(gpData) == 'string' then - gpData = Group.getByName(gpData) - end - - local useGroupRoute - if not vars.useGroupRoute then - useGroupRoute = vars.gpData - else - useGroupRoute = vars.useGroupRoute - end - local routeProvided = false - if not vars.route then - if useGroupRoute then - tempRoute = routines.getGroupRoute(useGroupRoute) - end - else - useRoute = vars.route - local posStart = routines.getLeadPos(gpData) - useRoute[1] = routines.ground.buildWP(posStart, useRoute[1].action, useRoute[1].speed) - routeProvided = true - end - - - local overRideSpeed = vars.speed or 'default' - local pType = vars.pType - local offRoadForm = vars.offRoadForm or 'default' - local onRoadForm = vars.onRoadForm or 'default' - - if routeProvided == false and #tempRoute > 0 then - local posStart = routines.getLeadPos(gpData) - - - useRoute[#useRoute + 1] = routines.ground.buildWP(posStart, offRoadForm, overRideSpeed) - for i = 1, #tempRoute do - local tempForm = tempRoute[i].action - local tempSpeed = tempRoute[i].speed - - if offRoadForm == 'default' then - tempForm = tempRoute[i].action - end - if onRoadForm == 'default' then - onRoadForm = 'On Road' - end - if (string.lower(tempRoute[i].action) == 'on road' or string.lower(tempRoute[i].action) == 'onroad' or string.lower(tempRoute[i].action) == 'on_road') then - tempForm = onRoadForm - else - tempForm = offRoadForm - end - - if type(overRideSpeed) == 'number' then - tempSpeed = overRideSpeed - end - - - useRoute[#useRoute + 1] = routines.ground.buildWP(tempRoute[i], tempForm, tempSpeed) - end - - if pType and string.lower(pType) == 'doubleback' then - local curRoute = routines.utils.deepCopy(useRoute) - for i = #curRoute, 2, -1 do - useRoute[#useRoute + 1] = routines.ground.buildWP(curRoute[i], curRoute[i].action, curRoute[i].speed) - end - end - - useRoute[1].action = useRoute[#useRoute].action -- make it so the first WP matches the last WP - end - - local cTask3 = {} - local newPatrol = {} - newPatrol.route = useRoute - newPatrol.gpData = gpData:getName() - cTask3[#cTask3 + 1] = 'routines.ground.patrolRoute(' - cTask3[#cTask3 + 1] = routines.utils.oneLineSerialize(newPatrol) - cTask3[#cTask3 + 1] = ')' - cTask3 = table.concat(cTask3) - local tempTask = { - id = 'WrappedAction', - params = { - action = { - id = 'Script', - params = { - command = cTask3, - - }, - }, - }, - } - - - useRoute[#useRoute].task = tempTask - routines.goRoute(gpData, useRoute) - - return -end - -routines.ground.patrol = function(gpData, pType, form, speed) - local vars = {} - - if type(gpData) == 'table' and gpData:getName() then - gpData = gpData:getName() - end - - vars.useGroupRoute = gpData - vars.gpData = gpData - vars.pType = pType - vars.offRoadForm = form - vars.speed = speed - - routines.ground.patrolRoute(vars) - - return -end - -function routines.GetUnitHeight( CheckUnit ) ---trace.f( "routines" ) - - local UnitPoint = CheckUnit:getPoint() - local UnitPosition = { x = UnitPoint.x, y = UnitPoint.z } - local UnitHeight = UnitPoint.y - - local LandHeight = land.getHeight( UnitPosition ) - - --env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) - - --trace.f( "routines", "Unit Height = " .. UnitHeight - LandHeight ) - - return UnitHeight - LandHeight - -end - - - -Su34Status = { status = {} } -boardMsgRed = { statusMsg = "" } -boardMsgAll = { timeMsg = "" } -SpawnSettings = {} -Su34MenuPath = {} -Su34Menus = 0 - - -function Su34AttackCarlVinson(groupName) ---trace.menu("", "Su34AttackCarlVinson") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34.getController(groupSu34) - local groupCarlVinson = Group.getByName("US Carl Vinson #001") - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - if groupCarlVinson ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupCarlVinson:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true}}) - end - Su34Status.status[groupName] = 1 - MessageToRed( string.format('%s: ',groupName) .. 'Attacking carrier Carl Vinson. ', 10, 'RedStatus' .. groupName ) -end - -function Su34AttackWest(groupName) ---trace.f("","Su34AttackWest") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34.getController(groupSu34) - local groupShipWest1 = Group.getByName("US Ship West #001") - local groupShipWest2 = Group.getByName("US Ship West #002") - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - if groupShipWest1 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipWest1:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true}}) - end - if groupShipWest2 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipWest2:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true}}) - end - Su34Status.status[groupName] = 2 - MessageToRed( string.format('%s: ',groupName) .. 'Attacking invading ships in the west. ', 10, 'RedStatus' .. groupName ) -end - -function Su34AttackNorth(groupName) ---trace.menu("","Su34AttackNorth") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34.getController(groupSu34) - local groupShipNorth1 = Group.getByName("US Ship North #001") - local groupShipNorth2 = Group.getByName("US Ship North #002") - local groupShipNorth3 = Group.getByName("US Ship North #003") - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - if groupShipNorth1 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipNorth1:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false}}) - end - if groupShipNorth2 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipNorth2:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false}}) - end - if groupShipNorth3 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipNorth3:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false}}) - end - Su34Status.status[groupName] = 3 - MessageToRed( string.format('%s: ',groupName) .. 'Attacking invading ships in the north. ', 10, 'RedStatus' .. groupName ) -end - -function Su34Orbit(groupName) ---trace.menu("","Su34Orbit") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34:getController() - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - controllerSu34:pushTask( {id = 'ControlledTask', params = { task = { id = 'Orbit', params = { pattern = AI.Task.OrbitPattern.RACE_TRACK } }, stopCondition = { duration = 600 } } } ) - Su34Status.status[groupName] = 4 - MessageToRed( string.format('%s: ',groupName) .. 'In orbit and awaiting further instructions. ', 10, 'RedStatus' .. groupName ) -end - -function Su34TakeOff(groupName) ---trace.menu("","Su34TakeOff") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34:getController() - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) - Su34Status.status[groupName] = 8 - MessageToRed( string.format('%s: ',groupName) .. 'Take-Off. ', 10, 'RedStatus' .. groupName ) -end - -function Su34Hold(groupName) ---trace.menu("","Su34Hold") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34:getController() - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) - Su34Status.status[groupName] = 5 - MessageToRed( string.format('%s: ',groupName) .. 'Holding Weapons. ', 10, 'RedStatus' .. groupName ) -end - -function Su34RTB(groupName) ---trace.menu("","Su34RTB") - Su34Status.status[groupName] = 6 - MessageToRed( string.format('%s: ',groupName) .. 'Return to Krasnodar. ', 10, 'RedStatus' .. groupName ) -end - -function Su34Destroyed(groupName) ---trace.menu("","Su34Destroyed") - Su34Status.status[groupName] = 7 - MessageToRed( string.format('%s: ',groupName) .. 'Destroyed. ', 30, 'RedStatus' .. groupName ) -end - -function GroupAlive( groupName ) ---trace.menu("","GroupAlive") - local groupTest = Group.getByName( groupName ) - - local groupExists = false - - if groupTest then - groupExists = groupTest:isExist() - end - - --trace.r( "", "", { groupExists } ) - return groupExists -end - -function Su34IsDead() ---trace.f() - -end - -function Su34OverviewStatus() ---trace.menu("","Su34OverviewStatus") - local msg = "" - local currentStatus = 0 - local Exists = false - - for groupName, currentStatus in pairs(Su34Status.status) do - - env.info(('Su34 Overview Status: GroupName = ' .. groupName )) - Alive = GroupAlive( groupName ) - - if Alive then - if currentStatus == 1 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Attacking carrier Carl Vinson. " - elseif currentStatus == 2 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Attacking supporting ships in the west. " - elseif currentStatus == 3 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Attacking invading ships in the north. " - elseif currentStatus == 4 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "In orbit and awaiting further instructions. " - elseif currentStatus == 5 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Holding Weapons. " - elseif currentStatus == 6 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Return to Krasnodar. " - elseif currentStatus == 7 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Destroyed. " - elseif currentStatus == 8 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Take-Off. " - end - else - if currentStatus == 7 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Destroyed. " - else - Su34Destroyed(groupName) - end - end - end - - boardMsgRed.statusMsg = msg -end - - -function UpdateBoardMsg() ---trace.f() - Su34OverviewStatus() - MessageToRed( boardMsgRed.statusMsg, 15, 'RedStatus' ) -end - -function MusicReset( flg ) ---trace.f() - trigger.action.setUserFlag(95,flg) -end - -function PlaneActivate(groupNameFormat, flg) ---trace.f() - local groupName = groupNameFormat .. string.format("#%03d", trigger.misc.getUserFlag(flg)) - --trigger.action.outText(groupName,10) - trigger.action.activateGroup(Group.getByName(groupName)) -end - -function Su34Menu(groupName) ---trace.f() - - --env.info(( 'Su34Menu(' .. groupName .. ')' )) - local groupSu34 = Group.getByName( groupName ) - - if Su34Status.status[groupName] == 1 or - Su34Status.status[groupName] == 2 or - Su34Status.status[groupName] == 3 or - Su34Status.status[groupName] == 4 or - Su34Status.status[groupName] == 5 then - if Su34MenuPath[groupName] == nil then - if planeMenuPath == nil then - planeMenuPath = missionCommands.addSubMenuForCoalition( - coalition.side.RED, - "SU-34 anti-ship flights", - nil - ) - end - Su34MenuPath[groupName] = missionCommands.addSubMenuForCoalition( - coalition.side.RED, - "Flight " .. groupName, - planeMenuPath - ) - - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Attack carrier Carl Vinson", - Su34MenuPath[groupName], - Su34AttackCarlVinson, - groupName - ) - - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Attack ships in the west", - Su34MenuPath[groupName], - Su34AttackWest, - groupName - ) - - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Attack ships in the north", - Su34MenuPath[groupName], - Su34AttackNorth, - groupName - ) - - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Hold position and await instructions", - Su34MenuPath[groupName], - Su34Orbit, - groupName - ) - - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Report status", - Su34MenuPath[groupName], - Su34OverviewStatus - ) - end - else - if Su34MenuPath[groupName] then - missionCommands.removeItemForCoalition(coalition.side.RED, Su34MenuPath[groupName]) - end - end -end - ---- Obsolete function, but kept to rework in framework. - -function ChooseInfantry ( TeleportPrefixTable, TeleportMax ) ---trace.f("Spawn") - --env.info(( 'ChooseInfantry: ' )) - - TeleportPrefixTableCount = #TeleportPrefixTable - TeleportPrefixTableIndex = math.random( 1, TeleportPrefixTableCount ) - - --env.info(( 'ChooseInfantry: TeleportPrefixTableIndex = ' .. TeleportPrefixTableIndex .. ' TeleportPrefixTableCount = ' .. TeleportPrefixTableCount .. ' TeleportMax = ' .. TeleportMax )) - - local TeleportFound = false - local TeleportLoop = true - local Index = TeleportPrefixTableIndex - local TeleportPrefix = '' - - while TeleportLoop do - TeleportPrefix = TeleportPrefixTable[Index] - if SpawnSettings[TeleportPrefix] then - if SpawnSettings[TeleportPrefix]['SpawnCount'] - 1 < TeleportMax then - SpawnSettings[TeleportPrefix]['SpawnCount'] = SpawnSettings[TeleportPrefix]['SpawnCount'] + 1 - TeleportFound = true - else - TeleportFound = false - end - else - SpawnSettings[TeleportPrefix] = {} - SpawnSettings[TeleportPrefix]['SpawnCount'] = 0 - TeleportFound = true - end - if TeleportFound then - TeleportLoop = false - else - if Index < TeleportPrefixTableCount then - Index = Index + 1 - else - TeleportLoop = false - end - end - --env.info(( 'ChooseInfantry: Loop 1 - TeleportPrefix = ' .. TeleportPrefix .. ' Index = ' .. Index )) - end - - if TeleportFound == false then - TeleportLoop = true - Index = 1 - while TeleportLoop do - TeleportPrefix = TeleportPrefixTable[Index] - if SpawnSettings[TeleportPrefix] then - if SpawnSettings[TeleportPrefix]['SpawnCount'] - 1 < TeleportMax then - SpawnSettings[TeleportPrefix]['SpawnCount'] = SpawnSettings[TeleportPrefix]['SpawnCount'] + 1 - TeleportFound = true - else - TeleportFound = false - end - else - SpawnSettings[TeleportPrefix] = {} - SpawnSettings[TeleportPrefix]['SpawnCount'] = 0 - TeleportFound = true - end - if TeleportFound then - TeleportLoop = false - else - if Index < TeleportPrefixTableIndex then - Index = Index + 1 - else - TeleportLoop = false - end - end - --env.info(( 'ChooseInfantry: Loop 2 - TeleportPrefix = ' .. TeleportPrefix .. ' Index = ' .. Index )) - end - end - - local TeleportGroupName = '' - if TeleportFound == true then - TeleportGroupName = TeleportPrefix .. string.format("#%03d", SpawnSettings[TeleportPrefix]['SpawnCount'] ) - else - TeleportGroupName = '' - end - - --env.info(('ChooseInfantry: TeleportGroupName = ' .. TeleportGroupName )) - --env.info(('ChooseInfantry: return')) - - return TeleportGroupName -end - -SpawnedInfantry = 0 - -function LandCarrier ( CarrierGroup, LandingZonePrefix ) ---trace.f() - --env.info(( 'LandCarrier: ' )) - --env.info(( 'LandCarrier: CarrierGroup = ' .. CarrierGroup:getName() )) - --env.info(( 'LandCarrier: LandingZone = ' .. LandingZonePrefix )) - - local controllerGroup = CarrierGroup:getController() - - local LandingZone = trigger.misc.getZone(LandingZonePrefix) - local LandingZonePos = {} - LandingZonePos.x = LandingZone.point.x + math.random(LandingZone.radius * -1, LandingZone.radius) - LandingZonePos.y = LandingZone.point.z + math.random(LandingZone.radius * -1, LandingZone.radius) - - controllerGroup:pushTask( { id = 'Land', params = { point = LandingZonePos, durationFlag = true, duration = 10 } } ) - - --env.info(( 'LandCarrier: end' )) -end - -EscortCount = 0 -function EscortCarrier ( CarrierGroup, EscortPrefix, EscortLastWayPoint, EscortEngagementDistanceMax, EscortTargetTypes ) ---trace.f() - --env.info(( 'EscortCarrier: ' )) - --env.info(( 'EscortCarrier: CarrierGroup = ' .. CarrierGroup:getName() )) - --env.info(( 'EscortCarrier: EscortPrefix = ' .. EscortPrefix )) - - local CarrierName = CarrierGroup:getName() - - local EscortMission = {} - local CarrierMission = {} - - local EscortMission = SpawnMissionGroup( EscortPrefix ) - local CarrierMission = SpawnMissionGroup( CarrierGroup:getName() ) - - if EscortMission ~= nil and CarrierMission ~= nil then - - EscortCount = EscortCount + 1 - EscortMissionName = string.format( EscortPrefix .. '#Escort %s', CarrierName ) - EscortMission.name = EscortMissionName - EscortMission.groupId = nil - EscortMission.lateActivation = false - EscortMission.taskSelected = false - - local EscortUnits = #EscortMission.units - for u = 1, EscortUnits do - EscortMission.units[u].name = string.format( EscortPrefix .. '#Escort %s %02d', CarrierName, u ) - EscortMission.units[u].unitId = nil - end - - - EscortMission.route.points[1].task = { id = "ComboTask", - params = - { - tasks = - { - [1] = - { - enabled = true, - auto = false, - id = "Escort", - number = 1, - params = - { - lastWptIndexFlagChangedManually = false, - groupId = CarrierGroup:getID(), - lastWptIndex = nil, - lastWptIndexFlag = false, - engagementDistMax = EscortEngagementDistanceMax, - targetTypes = EscortTargetTypes, - pos = - { - y = 20, - x = 20, - z = 0, - } -- end of ["pos"] - } -- end of ["params"] - } -- end of [1] - } -- end of ["tasks"] - } -- end of ["params"] - } -- end of ["task"] - - SpawnGroupAdd( EscortPrefix, EscortMission ) - - end -end - -function SendMessageToCarrier( CarrierGroup, CarrierMessage ) ---trace.f() - - if CarrierGroup ~= nil then - MessageToGroup( CarrierGroup, CarrierMessage, 30, 'Carrier/' .. CarrierGroup:getName() ) - end - -end - -function MessageToGroup( MsgGroup, MsgText, MsgTime, MsgName ) ---trace.f() - - if type(MsgGroup) == 'string' then - --env.info( 'MessageToGroup: Converted MsgGroup string "' .. MsgGroup .. '" into a Group structure.' ) - MsgGroup = Group.getByName( MsgGroup ) - end - - if MsgGroup ~= nil then - local MsgTable = {} - MsgTable.text = MsgText - MsgTable.displayTime = MsgTime - MsgTable.msgFor = { units = { MsgGroup:getUnits()[1]:getName() } } - MsgTable.name = MsgName - --routines.message.add( MsgTable ) - --env.info(('MessageToGroup: Message sent to ' .. MsgGroup:getUnits()[1]:getName() .. ' -> ' .. MsgText )) - end -end - -function MessageToUnit( UnitName, MsgText, MsgTime, MsgName ) ---trace.f() - - if UnitName ~= nil then - local MsgTable = {} - MsgTable.text = MsgText - MsgTable.displayTime = MsgTime - MsgTable.msgFor = { units = { UnitName } } - MsgTable.name = MsgName - --routines.message.add( MsgTable ) - end -end - -function MessageToAll( MsgText, MsgTime, MsgName ) ---trace.f() - - MESSAGE:New( MsgText, MsgTime, "Message" ):ToCoalition( coalition.side.RED ):ToCoalition( coalition.side.BLUE ) -end - -function MessageToRed( MsgText, MsgTime, MsgName ) ---trace.f() - - MESSAGE:New( MsgText, MsgTime, "To Red Coalition" ):ToCoalition( coalition.side.RED ) -end - -function MessageToBlue( MsgText, MsgTime, MsgName ) ---trace.f() - - MESSAGE:New( MsgText, MsgTime, "To Blue Coalition" ):ToCoalition( coalition.side.RED ) -end - -function getCarrierHeight( CarrierGroup ) ---trace.f() - - if CarrierGroup ~= nil then - if table.getn(CarrierGroup:getUnits()) == 1 then - local CarrierUnit = CarrierGroup:getUnits()[1] - local CurrentPoint = CarrierUnit:getPoint() - - local CurrentPosition = { x = CurrentPoint.x, y = CurrentPoint.z } - local CarrierHeight = CurrentPoint.y - - local LandHeight = land.getHeight( CurrentPosition ) - - --env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) - - return CarrierHeight - LandHeight - else - return 999999 - end - else - return 999999 - end - -end - -function GetUnitHeight( CheckUnit ) ---trace.f() - - local UnitPoint = CheckUnit:getPoint() - local UnitPosition = { x = CurrentPoint.x, y = CurrentPoint.z } - local UnitHeight = CurrentPoint.y - - local LandHeight = land.getHeight( CurrentPosition ) - - --env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) - - return UnitHeight - LandHeight - -end - - -_MusicTable = {} -_MusicTable.Files = {} -_MusicTable.Queue = {} -_MusicTable.FileCnt = 0 - - -function MusicRegister( SndRef, SndFile, SndTime ) ---trace.f() - - env.info(( 'MusicRegister: SndRef = ' .. SndRef )) - env.info(( 'MusicRegister: SndFile = ' .. SndFile )) - env.info(( 'MusicRegister: SndTime = ' .. SndTime )) - - - _MusicTable.FileCnt = _MusicTable.FileCnt + 1 - - _MusicTable.Files[_MusicTable.FileCnt] = {} - _MusicTable.Files[_MusicTable.FileCnt].Ref = SndRef - _MusicTable.Files[_MusicTable.FileCnt].File = SndFile - _MusicTable.Files[_MusicTable.FileCnt].Time = SndTime - - if not _MusicTable.Function then - _MusicTable.Function = routines.scheduleFunction( MusicScheduler, { }, timer.getTime() + 10, 10) - end - -end - -function MusicToPlayer( SndRef, PlayerName, SndContinue ) ---trace.f() - - --env.info(( 'MusicToPlayer: SndRef = ' .. SndRef )) - - local PlayerUnits = AlivePlayerUnits() - for PlayerUnitIdx, PlayerUnit in pairs(PlayerUnits) do - local PlayerUnitName = PlayerUnit:getPlayerName() - --env.info(( 'MusicToPlayer: PlayerUnitName = ' .. PlayerUnitName )) - if PlayerName == PlayerUnitName then - PlayerGroup = PlayerUnit:getGroup() - if PlayerGroup then - --env.info(( 'MusicToPlayer: PlayerGroup = ' .. PlayerGroup:getName() )) - MusicToGroup( SndRef, PlayerGroup, SndContinue ) - end - break - end - end - - --env.info(( 'MusicToPlayer: end' )) - -end - -function MusicToGroup( SndRef, SndGroup, SndContinue ) ---trace.f() - - --env.info(( 'MusicToGroup: SndRef = ' .. SndRef )) - - if SndGroup ~= nil then - if _MusicTable and _MusicTable.FileCnt > 0 then - if SndGroup:isExist() then - if MusicCanStart(SndGroup:getUnit(1):getPlayerName()) then - --env.info(( 'MusicToGroup: OK for Sound.' )) - local SndIdx = 0 - if SndRef == '' then - --env.info(( 'MusicToGroup: SndRef as empty. Queueing at random.' )) - SndIdx = math.random( 1, _MusicTable.FileCnt ) - else - for SndIdx = 1, _MusicTable.FileCnt do - if _MusicTable.Files[SndIdx].Ref == SndRef then - break - end - end - end - --env.info(( 'MusicToGroup: SndIdx = ' .. SndIdx )) - --env.info(( 'MusicToGroup: Queueing Music ' .. _MusicTable.Files[SndIdx].File .. ' for Group ' .. SndGroup:getID() )) - trigger.action.outSoundForGroup( SndGroup:getID(), _MusicTable.Files[SndIdx].File ) - MessageToGroup( SndGroup, 'Playing ' .. _MusicTable.Files[SndIdx].File, 15, 'Music-' .. SndGroup:getUnit(1):getPlayerName() ) - - local SndQueueRef = SndGroup:getUnit(1):getPlayerName() - if _MusicTable.Queue[SndQueueRef] == nil then - _MusicTable.Queue[SndQueueRef] = {} - end - _MusicTable.Queue[SndQueueRef].Start = timer.getTime() - _MusicTable.Queue[SndQueueRef].PlayerName = SndGroup:getUnit(1):getPlayerName() - _MusicTable.Queue[SndQueueRef].Group = SndGroup - _MusicTable.Queue[SndQueueRef].ID = SndGroup:getID() - _MusicTable.Queue[SndQueueRef].Ref = SndIdx - _MusicTable.Queue[SndQueueRef].Continue = SndContinue - _MusicTable.Queue[SndQueueRef].Type = Group - end - end - end - end -end - -function MusicCanStart(PlayerName) ---trace.f() - - --env.info(( 'MusicCanStart:' )) - - local MusicOut = false - - if _MusicTable['Queue'] ~= nil and _MusicTable.FileCnt > 0 then - --env.info(( 'MusicCanStart: PlayerName = ' .. PlayerName )) - local PlayerFound = false - local MusicStart = 0 - local MusicTime = 0 - for SndQueueIdx, SndQueue in pairs( _MusicTable.Queue ) do - if SndQueue.PlayerName == PlayerName then - PlayerFound = true - MusicStart = SndQueue.Start - MusicTime = _MusicTable.Files[SndQueue.Ref].Time - break - end - end - if PlayerFound then - --env.info(( 'MusicCanStart: MusicStart = ' .. MusicStart )) - --env.info(( 'MusicCanStart: MusicTime = ' .. MusicTime )) - --env.info(( 'MusicCanStart: timer.getTime() = ' .. timer.getTime() )) - - if MusicStart + MusicTime <= timer.getTime() then - MusicOut = true - end - else - MusicOut = true - end - end - - if MusicOut then - --env.info(( 'MusicCanStart: true' )) - else - --env.info(( 'MusicCanStart: false' )) - end - - return MusicOut -end - -function MusicScheduler() ---trace.scheduled("", "MusicScheduler") - - --env.info(( 'MusicScheduler:' )) - if _MusicTable['Queue'] ~= nil and _MusicTable.FileCnt > 0 then - --env.info(( 'MusicScheduler: Walking Sound Queue.')) - for SndQueueIdx, SndQueue in pairs( _MusicTable.Queue ) do - if SndQueue.Continue then - if MusicCanStart(SndQueue.PlayerName) then - --env.info(('MusicScheduler: MusicToGroup')) - MusicToPlayer( '', SndQueue.PlayerName, true ) - end - end - end - end - -end - - -env.info(( 'Init: Scripts Loaded v1.1' )) - ---- This module contains derived utilities taken from the MIST framework, --- which are excellent tools to be reused in an OO environment!. --- --- ### Authors: --- --- * Grimes : Design & Programming of the MIST framework. --- --- ### Contributions: --- --- * FlightControl : Rework to OO framework --- --- @module Utils - - ---- @type SMOKECOLOR --- @field Green --- @field Red --- @field White --- @field Orange --- @field Blue - -SMOKECOLOR = trigger.smokeColor -- #SMOKECOLOR - ---- @type FLARECOLOR --- @field Green --- @field Red --- @field White --- @field Yellow - -FLARECOLOR = trigger.flareColor -- #FLARECOLOR - ---- Utilities static class. --- @type UTILS -UTILS = {} - - ---from http://lua-users.org/wiki/CopyTable -UTILS.DeepCopy = function(object) - local lookup_table = {} - local function _copy(object) - if type(object) ~= "table" then - return object - elseif lookup_table[object] then - return lookup_table[object] - end - local new_table = {} - lookup_table[object] = new_table - for index, value in pairs(object) do - new_table[_copy(index)] = _copy(value) - end - return setmetatable(new_table, getmetatable(object)) - end - local objectreturn = _copy(object) - return objectreturn -end - - --- porting in Slmod's serialize_slmod2 -UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function - - lookup_table = {} - - local function _Serialize( tbl ) - - if type(tbl) == 'table' then --function only works for tables! - - if lookup_table[tbl] then - return lookup_table[object] - end - - local tbl_str = {} - - lookup_table[tbl] = tbl_str - - tbl_str[#tbl_str + 1] = '{' - - for ind,val in pairs(tbl) do -- serialize its fields - local ind_str = {} - if type(ind) == "number" then - ind_str[#ind_str + 1] = '[' - ind_str[#ind_str + 1] = tostring(ind) - ind_str[#ind_str + 1] = ']=' - else --must be a string - ind_str[#ind_str + 1] = '[' - ind_str[#ind_str + 1] = routines.utils.basicSerialize(ind) - ind_str[#ind_str + 1] = ']=' - end - - local val_str = {} - if ((type(val) == 'number') or (type(val) == 'boolean')) then - val_str[#val_str + 1] = tostring(val) - val_str[#val_str + 1] = ',' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'string' then - val_str[#val_str + 1] = routines.utils.basicSerialize(val) - val_str[#val_str + 1] = ',' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'nil' then -- won't ever happen, right? - val_str[#val_str + 1] = 'nil,' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'table' then - if ind == "__index" then - -- tbl_str[#tbl_str + 1] = "__index" - -- tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - else - - val_str[#val_str + 1] = _Serialize(val) - val_str[#val_str + 1] = ',' --I think this is right, I just added it - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - end - elseif type(val) == 'function' then - tbl_str[#tbl_str + 1] = "f() " .. tostring(ind) - tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - else - env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) - env.info( debug.traceback() ) - end - - end - tbl_str[#tbl_str + 1] = '}' - return table.concat(tbl_str) - else - return tostring(tbl) - end - end - - local objectreturn = _Serialize(tbl) - return objectreturn -end - ---porting in Slmod's "safestring" basic serialize -UTILS.BasicSerialize = function(s) - if s == nil then - return "\"\"" - else - if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then - return tostring(s) - elseif type(s) == 'string' then - s = string.format('%q', s) - return s - end - end -end - - -UTILS.ToDegree = function(angle) - return angle*180/math.pi -end - -UTILS.ToRadian = function(angle) - return angle*math.pi/180 -end - -UTILS.MetersToNM = function(meters) - return meters/1852 -end - -UTILS.MetersToFeet = function(meters) - return meters/0.3048 -end - -UTILS.NMToMeters = function(NM) - return NM*1852 -end - -UTILS.FeetToMeters = function(feet) - return feet*0.3048 -end - -UTILS.MpsToKnots = function(mps) - return mps*3600/1852 -end - -UTILS.MpsToKmph = function(mps) - return mps*3.6 -end - -UTILS.KnotsToMps = function(knots) - return knots*1852/3600 -end - -UTILS.KmphToMps = function(kmph) - return kmph/3.6 -end - ---[[acc: -in DM: decimal point of minutes. -In DMS: decimal point of seconds. -position after the decimal of the least significant digit: -So: -42.32 - acc of 2. -]] -UTILS.tostringLL = function( lat, lon, acc, DMS) - - local latHemi, lonHemi - if lat > 0 then - latHemi = 'N' - else - latHemi = 'S' - end - - if lon > 0 then - lonHemi = 'E' - else - lonHemi = 'W' - end - - lat = math.abs(lat) - lon = math.abs(lon) - - local latDeg = math.floor(lat) - local latMin = (lat - latDeg)*60 - - local lonDeg = math.floor(lon) - local lonMin = (lon - lonDeg)*60 - - if DMS then -- degrees, minutes, and seconds. - local oldLatMin = latMin - latMin = math.floor(latMin) - local latSec = UTILS.Round((oldLatMin - latMin)*60, acc) - - local oldLonMin = lonMin - lonMin = math.floor(lonMin) - local lonSec = UTILS.Round((oldLonMin - lonMin)*60, acc) - - if latSec == 60 then - latSec = 0 - latMin = latMin + 1 - end - - if lonSec == 60 then - lonSec = 0 - lonMin = lonMin + 1 - end - - local secFrmtStr -- create the formatting string for the seconds place - if acc <= 0 then -- no decimal place. - secFrmtStr = '%02d' - else - local width = 3 + acc -- 01.310 - that's a width of 6, for example. - secFrmtStr = '%0' .. width .. '.' .. acc .. 'f' - end - - return string.format('%02d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' ' - .. string.format('%02d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi - - else -- degrees, decimal minutes. - latMin = UTILS.Round(latMin, acc) - lonMin = UTILS.Round(lonMin, acc) - - if latMin == 60 then - latMin = 0 - latDeg = latDeg + 1 - end - - if lonMin == 60 then - lonMin = 0 - lonDeg = lonDeg + 1 - end - - local minFrmtStr -- create the formatting string for the minutes place - if acc <= 0 then -- no decimal place. - minFrmtStr = '%02d' - else - local width = 3 + acc -- 01.310 - that's a width of 6, for example. - minFrmtStr = '%0' .. width .. '.' .. acc .. 'f' - end - - return string.format('%02d', latDeg) .. ' ' .. string.format(minFrmtStr, latMin) .. '\'' .. latHemi .. ' ' - .. string.format('%02d', lonDeg) .. ' ' .. string.format(minFrmtStr, lonMin) .. '\'' .. lonHemi - - end -end - - ---- From http://lua-users.org/wiki/SimpleRound --- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place -function UTILS.Round( num, idp ) - local mult = 10 ^ ( idp or 0 ) - return math.floor( num * mult + 0.5 ) / mult -end - --- porting in Slmod's dostring -function UTILS.DoString( s ) - local f, err = loadstring( s ) - if f then - return true, f() - else - return false, err - end -end ---- **Core** - BASE forms **the basis of the MOOSE framework**. Each class within the MOOSE framework derives from BASE. --- --- ![Banner Image](..\Presentations\BASE\Dia1.JPG) --- --- === --- --- The @{#BASE} class is the core root class from where every other class in moose is derived. --- --- === --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) --- YYYY-MM-DD: CLASS:**NewFunction( Params )** added --- --- Hereby the change log: --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * None. --- --- ### Authors: --- --- * **FlightControl**: Design & Programming --- --- @module Base - - - -local _TraceOnOff = true -local _TraceLevel = 1 -local _TraceAll = false -local _TraceClass = {} -local _TraceClassMethod = {} - -local _ClassID = 0 - ---- @type BASE --- @field ClassName The name of the class. --- @field ClassID The ID number of the class. --- @field ClassNameAndID The name of the class concatenated with the ID number of the class. - ---- # 1) #BASE class --- --- All classes within the MOOSE framework are derived from the BASE class. --- --- BASE provides facilities for : --- --- * The construction and inheritance of MOOSE classes. --- * The class naming and numbering system. --- * The class hierarchy search system. --- * The tracing of information or objects during mission execution for debuggin purposes. --- * The subscription to DCS events for event handling in MOOSE objects. --- --- Note: The BASE class is an abstract class and is not meant to be used directly. --- --- ## 1.1) BASE constructor --- --- Any class derived from BASE, will use the @{Base#BASE.New} constructor embedded in the @{Base#BASE.Inherit} method. --- See an example at the @{Base#BASE.New} method how this is done. --- --- ## 1.2) Trace information for debugging --- --- The BASE class contains trace methods to trace progress within a mission execution of a certain object. --- These trace methods are inherited by each MOOSE class interiting BASE, soeach object created from derived class from BASE can use the tracing methods to trace its execution. --- --- Any type of information can be passed to these tracing methods. See the following examples: --- --- self:E( "Hello" ) --- --- Result in the word "Hello" in the dcs.log. --- --- local Array = { 1, nil, "h", { "a","b" }, "x" } --- self:E( Array ) --- --- Results with the text [1]=1,[3]="h",[4]={[1]="a",[2]="b"},[5]="x"} in the dcs.log. --- --- local Object1 = "Object1" --- local Object2 = 3 --- local Object3 = { Object 1, Object 2 } --- self:E( { Object1, Object2, Object3 } ) --- --- Results with the text [1]={[1]="Object",[2]=3,[3]={[1]="Object",[2]=3}} in the dcs.log. --- --- local SpawnObject = SPAWN:New( "Plane" ) --- local GroupObject = GROUP:FindByName( "Group" ) --- self:E( { Spawn = SpawnObject, Group = GroupObject } ) --- --- Results with the text [1]={Spawn={....),Group={...}} in the dcs.log. --- --- Below a more detailed explanation of the different method types for tracing. --- --- ### 1.2.1) Tracing methods categories --- --- There are basically 3 types of tracing methods available: --- --- * @{#BASE.F}: Used to trace the entrance of a function and its given parameters. An F is indicated at column 44 in the DCS.log file. --- * @{#BASE.T}: Used to trace further logic within a function giving optional variables or parameters. A T is indicated at column 44 in the DCS.log file. --- * @{#BASE.E}: Used to always trace information giving optional variables or parameters. An E is indicated at column 44 in the DCS.log file. --- --- ### 1.2.2) Tracing levels --- --- There are 3 tracing levels within MOOSE. --- These tracing levels were defined to avoid bulks of tracing to be generated by lots of objects. --- --- As such, the F and T methods have additional variants to trace level 2 and 3 respectively: --- --- * @{#BASE.F2}: Trace the beginning of a function and its given parameters with tracing level 2. --- * @{#BASE.F3}: Trace the beginning of a function and its given parameters with tracing level 3. --- * @{#BASE.T2}: Trace further logic within a function giving optional variables or parameters with tracing level 2. --- * @{#BASE.T3}: Trace further logic within a function giving optional variables or parameters with tracing level 3. --- --- ### 1.2.3) Trace activation. --- --- Tracing can be activated in several ways: --- --- * Switch tracing on or off through the @{#BASE.TraceOnOff}() method. --- * Activate all tracing through the @{#BASE.TraceAll}() method. --- * Activate only the tracing of a certain class (name) through the @{#BASE.TraceClass}() method. --- * Activate only the tracing of a certain method of a certain class through the @{#BASE.TraceClassMethod}() method. --- * Activate only the tracing of a certain level through the @{#BASE.TraceLevel}() method. --- --- ### 1.2.4) Check if tracing is on. --- --- The method @{#BASE.IsTrace}() will validate if tracing is activated or not. --- --- ## 1.3 DCS simulator Event Handling --- --- The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator, --- and handled through lua scripting. MOOSE provides an encapsulation to handle these events more efficiently. --- --- ### 1.3.1 Subscribe / Unsubscribe to DCS Events --- --- At first, the mission designer will need to **Subscribe** to a specific DCS event for the class. --- So, when the DCS event occurs, the class will be notified of that event. --- There are two methods which you use to subscribe to or unsubscribe from an event. --- --- * @{#BASE.HandleEvent}(): Subscribe to a DCS Event. --- * @{#BASE.UnHandleEvent}(): Unsubscribe from a DCS Event. --- --- ### 1.3.2 Event Handling of DCS Events --- --- Once the class is subscribed to the event, an **Event Handling** method on the object or class needs to be written that will be called --- when the DCS event occurs. The Event Handling method receives an @{Event#EVENTDATA} structure, which contains a lot of information --- about the event that occurred. --- --- Find below an example of the prototype how to write an event handling function for two units: --- --- local Tank1 = UNIT:FindByName( "Tank A" ) --- local Tank2 = UNIT:FindByName( "Tank B" ) --- --- -- Here we subscribe to the Dead events. So, if one of these tanks dies, the Tank1 or Tank2 objects will be notified. --- Tank1:HandleEvent( EVENTS.Dead ) --- Tank2:HandleEvent( EVENTS.Dead ) --- --- --- This function is an Event Handling function that will be called when Tank1 is Dead. --- -- @param Wrapper.Unit#UNIT self --- -- @param Core.Event#EVENTDATA EventData --- function Tank1:OnEventDead( EventData ) --- --- self:SmokeGreen() --- end --- --- --- This function is an Event Handling function that will be called when Tank2 is Dead. --- -- @param Wrapper.Unit#UNIT self --- -- @param Core.Event#EVENTDATA EventData --- function Tank2:OnEventDead( EventData ) --- --- self:SmokeBlue() --- end --- --- --- --- See the @{Event} module for more information about event handling. --- --- ## 1.4) Class identification methods --- --- BASE provides methods to get more information of each object: --- --- * @{#BASE.GetClassID}(): Gets the ID (number) of the object. Each object created is assigned a number, that is incremented by one. --- * @{#BASE.GetClassName}(): Gets the name of the object, which is the name of the class the object was instantiated from. --- * @{#BASE.GetClassNameAndID}(): Gets the name and ID of the object. --- --- ## 1.5) All objects derived from BASE can have "States" --- --- A mechanism is in place in MOOSE, that allows to let the objects administer **states**. --- States are essentially properties of objects, which are identified by a **Key** and a **Value**. --- --- The method @{#BASE.SetState}() can be used to set a Value with a reference Key to the object. --- To **read or retrieve** a state Value based on a Key, use the @{#BASE.GetState} method. --- --- These two methods provide a very handy way to keep state at long lasting processes. --- Values can be stored within the objects, and later retrieved or changed when needed. --- There is one other important thing to note, the @{#BASE.SetState}() and @{#BASE.GetState} methods --- receive as the **first parameter the object for which the state needs to be set**. --- Thus, if the state is to be set for the same object as the object for which the method is used, then provide the same --- object name to the method. --- --- ## 1.10) Inheritance --- --- The following methods are available to implement inheritance --- --- * @{#BASE.Inherit}: Inherits from a class. --- * @{#BASE.GetParent}: Returns the parent object from the object it is handling, or nil if there is no parent object. --- --- === --- --- @field #BASE BASE --- -BASE = { - ClassName = "BASE", - ClassID = 0, - _Private = {}, - Events = {}, - States = {} -} - ---- The Formation Class --- @type FORMATION --- @field Cone A cone formation. -FORMATION = { - Cone = "Cone" -} - - - ---- BASE constructor. --- --- This is an example how to use the BASE:New() constructor in a new class definition when inheriting from BASE. --- --- function EVENT:New() --- local self = BASE:Inherit( self, BASE:New() ) -- #EVENT --- return self --- end --- --- @param #BASE self --- @return #BASE -function BASE:New() - local self = routines.utils.deepCopy( self ) -- Create a new self instance - local MetaTable = {} - setmetatable( self, MetaTable ) - self.__index = self - _ClassID = _ClassID + 1 - self.ClassID = _ClassID - - - return self -end - -function BASE:_Destructor() - --self:E("_Destructor") - - --self:EventRemoveAll() -end - - --- THIS IS WHY WE NEED LUA 5.2 ... -function BASE:_SetDestructor() - - -- TODO: Okay, this is really technical... - -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak... - -- Therefore, I am parking this logic until I've properly discussed all this with the community. - - local proxy = newproxy(true) - local proxyMeta = getmetatable(proxy) - - proxyMeta.__gc = function () - env.info("In __gc for " .. self:GetClassNameAndID() ) - if self._Destructor then - self:_Destructor() - end - end - - -- keep the userdata from newproxy reachable until the object - -- table is about to be garbage-collected - then the __gc hook - -- will be invoked and the destructor called - rawset( self, '__proxy', proxy ) - -end - ---- This is the worker method to inherit from a parent class. --- @param #BASE self --- @param Child is the Child class that inherits. --- @param #BASE Parent is the Parent class that the Child inherits from. --- @return #BASE Child -function BASE:Inherit( Child, Parent ) - local Child = routines.utils.deepCopy( Child ) - --local Parent = routines.utils.deepCopy( Parent ) - --local Parent = Parent - if Child ~= nil then - setmetatable( Child, Parent ) - Child.__index = Child - - --Child:_SetDestructor() - end - --self:T( 'Inherited from ' .. Parent.ClassName ) - return Child -end - ---- This is the worker method to retrieve the Parent class. --- Note that the Parent class must be passed to call the parent class method. --- --- self:GetParent(self):ParentMethod() --- --- --- @param #BASE self --- @param #BASE Child is the Child class from which the Parent class needs to be retrieved. --- @return #BASE -function BASE:GetParent( Child ) - local Parent = getmetatable( Child ) --- env.info('Inherited class of ' .. Child.ClassName .. ' is ' .. Parent.ClassName ) - return Parent -end - ---- Get the ClassName + ClassID of the class instance. --- The ClassName + ClassID is formatted as '%s#%09d'. --- @param #BASE self --- @return #string The ClassName + ClassID of the class instance. -function BASE:GetClassNameAndID() - return string.format( '%s#%09d', self.ClassName, self.ClassID ) -end - ---- Get the ClassName of the class instance. --- @param #BASE self --- @return #string The ClassName of the class instance. -function BASE:GetClassName() - return self.ClassName -end - ---- Get the ClassID of the class instance. --- @param #BASE self --- @return #string The ClassID of the class instance. -function BASE:GetClassID() - return self.ClassID -end - -do -- Event Handling - - --- Returns the event dispatcher - -- @param #BASE self - -- @return Core.Event#EVENT - function BASE:EventDispatcher() - - return _EVENTDISPATCHER - end - - - --- Get the Class @{Event} processing Priority. - -- The Event processing Priority is a number from 1 to 10, - -- reflecting the order of the classes subscribed to the Event to be processed. - -- @param #BASE self - -- @return #number The @{Event} processing Priority. - function BASE:GetEventPriority() - return self._Private.EventPriority or 5 - end - - --- Set the Class @{Event} processing Priority. - -- The Event processing Priority is a number from 1 to 10, - -- reflecting the order of the classes subscribed to the Event to be processed. - -- @param #BASE self - -- @param #number EventPriority The @{Event} processing Priority. - -- @return self - function BASE:SetEventPriority( EventPriority ) - self._Private.EventPriority = EventPriority - end - - --- Remove all subscribed events - -- @param #BASE self - -- @return #BASE - function BASE:EventRemoveAll() - - self:EventDispatcher():RemoveAll( self ) - - return self - end - - --- Subscribe to a DCS Event. - -- @param #BASE self - -- @param Core.Event#EVENTS Event - -- @param #function EventFunction (optional) The function to be called when the event occurs for the unit. - -- @return #BASE - function BASE:HandleEvent( Event, EventFunction ) - - self:EventDispatcher():OnEventGeneric( EventFunction, self, Event ) - - return self - end - - --- UnSubscribe to a DCS event. - -- @param #BASE self - -- @param Core.Event#EVENTS Event - -- @return #BASE - function BASE:UnHandleEvent( Event ) - - self:EventDispatcher():Remove( self, Event ) - - return self - end - - -- Event handling function prototypes - - --- Occurs whenever any unit in a mission fires a weapon. But not any machine gun or autocannon based weapon, those are handled by EVENT.ShootingStart. - -- @function [parent=#BASE] OnEventShot - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs whenever an object is hit by a weapon. - -- initiator : The unit object the fired the weapon - -- weapon: Weapon object that hit the target - -- target: The Object that was hit. - -- @function [parent=#BASE] OnEventHit - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when an aircraft takes off from an airbase, farp, or ship. - -- initiator : The unit that tookoff - -- place: Object from where the AI took-off from. Can be an Airbase Object, FARP, or Ships - -- @function [parent=#BASE] OnEventTakeoff - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when an aircraft lands at an airbase, farp or ship - -- initiator : The unit that has landed - -- place: Object that the unit landed on. Can be an Airbase Object, FARP, or Ships - -- @function [parent=#BASE] OnEventLand - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any aircraft crashes into the ground and is completely destroyed. - -- initiator : The unit that has crashed - -- @function [parent=#BASE] OnEventCrash - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when a pilot ejects from an aircraft - -- initiator : The unit that has ejected - -- @function [parent=#BASE] OnEventEjection - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when an aircraft connects with a tanker and begins taking on fuel. - -- initiator : The unit that is receiving fuel. - -- @function [parent=#BASE] OnEventRefueling - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when an object is completely destroyed. - -- initiator : The unit that is was destroyed. - -- @function [parent=#BASE] OnEvent - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when the pilot of an aircraft is killed. Can occur either if the player is alive and crashes or if a weapon kills the pilot without completely destroying the plane. - -- initiator : The unit that the pilot has died in. - -- @function [parent=#BASE] OnEventPilotDead - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when a ground unit captures either an airbase or a farp. - -- initiator : The unit that captured the base - -- place: The airbase that was captured, can be a FARP or Airbase. When calling place:getCoalition() the faction will already be the new owning faction. - -- @function [parent=#BASE] OnEventBaseCaptured - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when a mission starts - -- @function [parent=#BASE] OnEventMissionStart - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when a mission ends - -- @function [parent=#BASE] OnEventMissionEnd - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when an aircraft is finished taking fuel. - -- initiator : The unit that was receiving fuel. - -- @function [parent=#BASE] OnEventRefuelingStop - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any object is spawned into the mission. - -- initiator : The unit that was spawned - -- @function [parent=#BASE] OnEventBirth - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any system fails on a human controlled aircraft. - -- initiator : The unit that had the failure - -- @function [parent=#BASE] OnEventHumanFailure - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any aircraft starts its engines. - -- initiator : The unit that is starting its engines. - -- @function [parent=#BASE] OnEventEngineStartup - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any aircraft shuts down its engines. - -- initiator : The unit that is stopping its engines. - -- @function [parent=#BASE] OnEventEngineShutdown - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any player assumes direct control of a unit. - -- initiator : The unit that is being taken control of. - -- @function [parent=#BASE] OnEventPlayerEnterUnit - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any player relieves control of a unit to the AI. - -- initiator : The unit that the player left. - -- @function [parent=#BASE] OnEventPlayerLeaveUnit - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any unit begins firing a weapon that has a high rate of fire. Most common with aircraft cannons (GAU-8), autocannons, and machine guns. - -- initiator : The unit that is doing the shooing. - -- target: The unit that is being targeted. - -- @function [parent=#BASE] OnEventShootingStart - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any unit stops firing its weapon. Event will always correspond with a shooting start event. - -- initiator : The unit that was doing the shooing. - -- @function [parent=#BASE] OnEventShootingEnd - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - -end - - ---- Creation of a Birth Event. --- @param #BASE self --- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. --- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event. --- @param #string IniUnitName The initiating unit name. --- @param place --- @param subplace -function BASE:CreateEventBirth( EventTime, Initiator, IniUnitName, place, subplace ) - self:F( { EventTime, Initiator, IniUnitName, place, subplace } ) - - local Event = { - id = world.event.S_EVENT_BIRTH, - time = EventTime, - initiator = Initiator, - IniUnitName = IniUnitName, - place = place, - subplace = subplace - } - - world.onEvent( Event ) -end - ---- Creation of a Crash Event. --- @param #BASE self --- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. --- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event. -function BASE:CreateEventCrash( EventTime, Initiator ) - self:F( { EventTime, Initiator } ) - - local Event = { - id = world.event.S_EVENT_CRASH, - time = EventTime, - initiator = Initiator, - } - - world.onEvent( Event ) -end - --- TODO: Complete Dcs.DCSTypes#Event structure. ---- The main event handling function... This function captures all events generated for the class. --- @param #BASE self --- @param Dcs.DCSTypes#Event event -function BASE:onEvent(event) - --self:F( { BaseEventCodes[event.id], event } ) - - if self then - for EventID, EventObject in pairs( self.Events ) do - if EventObject.EventEnabled then - --env.info( 'onEvent Table EventObject.Self = ' .. tostring(EventObject.Self) ) - --env.info( 'onEvent event.id = ' .. tostring(event.id) ) - --env.info( 'onEvent EventObject.Event = ' .. tostring(EventObject.Event) ) - if event.id == EventObject.Event then - if self == EventObject.Self then - if event.initiator and event.initiator:isExist() then - event.IniUnitName = event.initiator:getName() - end - if event.target and event.target:isExist() then - event.TgtUnitName = event.target:getName() - end - --self:T( { BaseEventCodes[event.id], event } ) - --EventObject.EventFunction( self, event ) - end - end - end - end - end -end - ---- Set a state or property of the Object given a Key and a Value. --- Note that if the Object is destroyed, nillified or garbage collected, then the Values and Keys will also be gone. --- @param #BASE self --- @param Object The object that will hold the Value set by the Key. --- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type! --- @param Value The value to is stored in the object. --- @return The Value set. --- @return #nil The Key was not found and thus the Value could not be retrieved. -function BASE:SetState( Object, Key, Value ) - - local ClassNameAndID = Object:GetClassNameAndID() - - self.States[ClassNameAndID] = self.States[ClassNameAndID] or {} - self.States[ClassNameAndID][Key] = Value - self:T2( { ClassNameAndID, Key, Value } ) - - return self.States[ClassNameAndID][Key] -end - - ---- Get a Value given a Key from the Object. --- Note that if the Object is destroyed, nillified or garbage collected, then the Values and Keys will also be gone. --- @param #BASE self --- @param Object The object that holds the Value set by the Key. --- @param Key The key that is used to retrieve the value. Note that the key can be a #string, but it can also be any other type! --- @param Value The value to is stored in the Object. --- @return The Value retrieved. -function BASE:GetState( Object, Key ) - - local ClassNameAndID = Object:GetClassNameAndID() - - if self.States[ClassNameAndID] then - local Value = self.States[ClassNameAndID][Key] or false - self:T2( { ClassNameAndID, Key, Value } ) - return Value - end - - return nil -end - -function BASE:ClearState( Object, StateName ) - - local ClassNameAndID = Object:GetClassNameAndID() - if self.States[ClassNameAndID] then - self.States[ClassNameAndID][StateName] = nil - end -end - --- Trace section - --- Log a trace (only shown when trace is on) --- TODO: Make trace function using variable parameters. - ---- Set trace on or off --- Note that when trace is off, no debug statement is performed, increasing performance! --- When Moose is loaded statically, (as one file), tracing is switched off by default. --- So tracing must be switched on manually in your mission if you are using Moose statically. --- When moose is loading dynamically (for moose class development), tracing is switched on by default. --- @param #BASE self --- @param #boolean TraceOnOff Switch the tracing on or off. --- @usage --- -- Switch the tracing On --- BASE:TraceOnOff( true ) --- --- -- Switch the tracing Off --- BASE:TraceOnOff( false ) -function BASE:TraceOnOff( TraceOnOff ) - _TraceOnOff = TraceOnOff -end - - ---- Enquires if tracing is on (for the class). --- @param #BASE self --- @return #boolean -function BASE:IsTrace() - - if debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then - return true - else - return false - end -end - ---- Set trace level --- @param #BASE self --- @param #number Level -function BASE:TraceLevel( Level ) - _TraceLevel = Level - self:E( "Tracing level " .. Level ) -end - ---- Trace all methods in MOOSE --- @param #BASE self --- @param #boolean TraceAll true = trace all methods in MOOSE. -function BASE:TraceAll( TraceAll ) - - _TraceAll = TraceAll - - if _TraceAll then - self:E( "Tracing all methods in MOOSE " ) - else - self:E( "Switched off tracing all methods in MOOSE" ) - end -end - ---- Set tracing for a class --- @param #BASE self --- @param #string Class -function BASE:TraceClass( Class ) - _TraceClass[Class] = true - _TraceClassMethod[Class] = {} - self:E( "Tracing class " .. Class ) -end - ---- Set tracing for a specific method of class --- @param #BASE self --- @param #string Class --- @param #string Method -function BASE:TraceClassMethod( Class, Method ) - if not _TraceClassMethod[Class] then - _TraceClassMethod[Class] = {} - _TraceClassMethod[Class].Method = {} - end - _TraceClassMethod[Class].Method[Method] = true - self:E( "Tracing method " .. Method .. " of class " .. Class ) -end - ---- Trace a function call. This function is private. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam ) - - if debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then - - local DebugInfoCurrent = DebugInfoCurrentParam and DebugInfoCurrentParam or debug.getinfo( 2, "nl" ) - local DebugInfoFrom = DebugInfoFromParam and DebugInfoFromParam or debug.getinfo( 3, "l" ) - - local Function = "function" - if DebugInfoCurrent.name then - Function = DebugInfoCurrent.name - end - - if _TraceAll == true or _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName].Method[Function] then - local LineCurrent = 0 - if DebugInfoCurrent.currentline then - LineCurrent = DebugInfoCurrent.currentline - end - local LineFrom = 0 - if DebugInfoFrom then - LineFrom = DebugInfoFrom.currentline - end - env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) ) - end - end -end - ---- Trace a function call. Must be at the beginning of the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:F( Arguments ) - - if debug and _TraceOnOff then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - if _TraceLevel >= 1 then - self:_F( Arguments, DebugInfoCurrent, DebugInfoFrom ) - end - end -end - - ---- Trace a function call level 2. Must be at the beginning of the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:F2( Arguments ) - - if debug and _TraceOnOff then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - if _TraceLevel >= 2 then - self:_F( Arguments, DebugInfoCurrent, DebugInfoFrom ) - end - end -end - ---- Trace a function call level 3. Must be at the beginning of the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:F3( Arguments ) - - if debug and _TraceOnOff then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - if _TraceLevel >= 3 then - self:_F( Arguments, DebugInfoCurrent, DebugInfoFrom ) - end - end -end - ---- Trace a function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam ) - - if debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then - - local DebugInfoCurrent = DebugInfoCurrentParam and DebugInfoCurrentParam or debug.getinfo( 2, "nl" ) - local DebugInfoFrom = DebugInfoFromParam and DebugInfoFromParam or debug.getinfo( 3, "l" ) - - local Function = "function" - if DebugInfoCurrent.name then - Function = DebugInfoCurrent.name - end - - if _TraceAll == true or _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName].Method[Function] then - local LineCurrent = 0 - if DebugInfoCurrent.currentline then - LineCurrent = DebugInfoCurrent.currentline - end - local LineFrom = 0 - if DebugInfoFrom then - LineFrom = DebugInfoFrom.currentline - end - env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s" , LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) ) - end - end -end - ---- Trace a function logic level 1. Can be anywhere within the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:T( Arguments ) - - if debug and _TraceOnOff then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - if _TraceLevel >= 1 then - self:_T( Arguments, DebugInfoCurrent, DebugInfoFrom ) - end - end -end - - ---- Trace a function logic level 2. Can be anywhere within the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:T2( Arguments ) - - if debug and _TraceOnOff then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - if _TraceLevel >= 2 then - self:_T( Arguments, DebugInfoCurrent, DebugInfoFrom ) - end - end -end - ---- Trace a function logic level 3. Can be anywhere within the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:T3( Arguments ) - - if debug and _TraceOnOff then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - if _TraceLevel >= 3 then - self:_T( Arguments, DebugInfoCurrent, DebugInfoFrom ) - end - end -end - ---- Log an exception which will be traced always. Can be anywhere within the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:E( Arguments ) - - if debug then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - local Function = "function" - if DebugInfoCurrent.name then - Function = DebugInfoCurrent.name - end - - local LineCurrent = DebugInfoCurrent.currentline - local LineFrom = -1 - if DebugInfoFrom then - LineFrom = DebugInfoFrom.currentline - end - - env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) ) - end - -end - - - ---- **Core** - SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**. --- --- ![Banner Image](..\Presentations\SCHEDULER\Dia1.JPG) --- --- === --- --- # 1) @{Scheduler#SCHEDULER} class, extends @{Base#BASE} --- --- The @{Scheduler#SCHEDULER} class creates schedule. --- --- ## 1.1) SCHEDULER constructor --- --- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters: --- --- * @{Scheduler#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection. --- * @{Scheduler#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection. --- * @{Scheduler#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters. --- * @{Scheduler#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters. --- --- ## 1.2) SCHEDULER timer stopping and (re-)starting. --- --- The SCHEDULER can be stopped and restarted with the following methods: --- --- * @{Scheduler#SCHEDULER.Start}(): (Re-)Start the schedules within the SCHEDULER object. If a CallID is provided to :Start(), only the schedule referenced by CallID will be (re-)started. --- * @{Scheduler#SCHEDULER.Stop}(): Stop the schedules within the SCHEDULER object. If a CallID is provided to :Stop(), then only the schedule referenced by CallID will be stopped. --- --- ## 1.3) Create a new schedule --- --- With @{Scheduler#SCHEDULER.Schedule}() a new time event can be scheduled. This function is used by the :New() constructor when a new schedule is planned. --- --- === --- --- ### Contributions: --- --- * FlightControl : Concept & Testing --- --- ### Authors: --- --- * FlightControl : Design & Programming --- --- ### Test Missions: --- --- * SCH - Scheduler --- --- === --- --- @module Scheduler - - ---- The SCHEDULER class --- @type SCHEDULER --- @field #number ScheduleID the ID of the scheduler. --- @extends Core.Base#BASE -SCHEDULER = { - ClassName = "SCHEDULER", - Schedules = {}, -} - ---- SCHEDULER constructor. --- @param #SCHEDULER self --- @param #table SchedulerObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference. --- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments. --- @param #table SchedulerArguments Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }. --- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called. --- @param #number Repeat Specifies the interval in seconds when the scheduler will call the event function. --- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat. --- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped. --- @return #SCHEDULER self. --- @return #number The ScheduleID of the planned schedule. -function SCHEDULER:New( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop ) - local self = BASE:Inherit( self, BASE:New() ) - self:F2( { Start, Repeat, RandomizeFactor, Stop } ) - - local ScheduleID = nil - - self.MasterObject = SchedulerObject - - if SchedulerFunction then - ScheduleID = self:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop ) - end - - return self, ScheduleID -end - ---function SCHEDULER:_Destructor() --- --self:E("_Destructor") --- --- _SCHEDULEDISPATCHER:RemoveSchedule( self.CallID ) ---end - ---- Schedule a new time event. Note that the schedule will only take place if the scheduler is *started*. Even for a single schedule event, the scheduler needs to be started also. --- @param #SCHEDULER self --- @param #table SchedulerObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference. --- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments. --- @param #table SchedulerArguments Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }. --- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called. --- @param #number Repeat Specifies the interval in seconds when the scheduler will call the event function. --- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat. --- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped. --- @return #number The ScheduleID of the planned schedule. -function SCHEDULER:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop ) - self:F2( { Start, Repeat, RandomizeFactor, Stop } ) - self:T3( { SchedulerArguments } ) - - local ObjectName = "-" - if SchedulerObject and SchedulerObject.ClassName and SchedulerObject.ClassID then - ObjectName = SchedulerObject.ClassName .. SchedulerObject.ClassID - end - self:F3( { "Schedule :", ObjectName, tostring( SchedulerObject ), Start, Repeat, RandomizeFactor, Stop } ) - self.SchedulerObject = SchedulerObject - - local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( - self, - SchedulerFunction, - SchedulerArguments, - Start, - Repeat, - RandomizeFactor, - Stop - ) - - self.Schedules[#self.Schedules+1] = ScheduleID - - return ScheduleID -end - ---- (Re-)Starts the schedules or a specific schedule if a valid ScheduleID is provided. --- @param #SCHEDULER self --- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule. -function SCHEDULER:Start( ScheduleID ) - self:F3( { ScheduleID } ) - - _SCHEDULEDISPATCHER:Start( self, ScheduleID ) -end - ---- Stops the schedules or a specific schedule if a valid ScheduleID is provided. --- @param #SCHEDULER self --- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule. -function SCHEDULER:Stop( ScheduleID ) - self:F3( { ScheduleID } ) - - _SCHEDULEDISPATCHER:Stop( self, ScheduleID ) -end - ---- Removes a specific schedule if a valid ScheduleID is provided. --- @param #SCHEDULER self --- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule. -function SCHEDULER:Remove( ScheduleID ) - self:F3( { ScheduleID } ) - - _SCHEDULEDISPATCHER:Remove( self, ScheduleID ) -end - ---- Clears all pending schedules. --- @param #SCHEDULER self -function SCHEDULER:Clear() - self:F3( ) - - _SCHEDULEDISPATCHER:Clear( self ) -end - - - - - - - - - - - - - - ---- This module defines the SCHEDULEDISPATCHER class, which is used by a central object called _SCHEDULEDISPATCHER. --- --- === --- --- Takes care of the creation and dispatching of scheduled functions for SCHEDULER objects. --- --- This class is tricky and needs some thorought explanation. --- SCHEDULE classes are used to schedule functions for objects, or as persistent objects. --- The SCHEDULEDISPATCHER class ensures that: --- --- - Scheduled functions are planned according the SCHEDULER object parameters. --- - Scheduled functions are repeated when requested, according the SCHEDULER object parameters. --- - Scheduled functions are automatically removed when the schedule is finished, according the SCHEDULER object parameters. --- --- The SCHEDULEDISPATCHER class will manage SCHEDULER object in memory during garbage collection: --- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER --- object is _persistent_ within memory. --- - When a SCHEDULER object *is* attached to another object, then the SCHEDULER object is _not persistent_ within memory after a garbage collection! --- The none persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collectged, when the parent object is also desroyed or nillified and garbage collected. --- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object, --- these will not be executed anymore when the SCHEDULER object has been destroyed. --- --- The SCHEDULEDISPATCHER allows multiple scheduled functions to be planned and executed for one SCHEDULER object. --- The SCHEDULER object therefore keeps a table of "CallID's", which are returned after each planning of a new scheduled function by the SCHEDULEDISPATCHER. --- The SCHEDULER object plans new scheduled functions through the @{Scheduler#SCHEDULER.Schedule}() method. --- The Schedule() method returns the CallID that is the reference ID for each planned schedule. --- --- === --- --- === --- --- ### Contributions: - --- ### Authors: FlightControl : Design & Programming --- --- @module ScheduleDispatcher - ---- The SCHEDULEDISPATCHER structure --- @type SCHEDULEDISPATCHER -SCHEDULEDISPATCHER = { - ClassName = "SCHEDULEDISPATCHER", - CallID = 0, -} - -function SCHEDULEDISPATCHER:New() - local self = BASE:Inherit( self, BASE:New() ) - self:F3() - return self -end - ---- Add a Schedule to the ScheduleDispatcher. --- The development of this method was really tidy. --- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is nillified. --- Nothing of this code should be modified without testing it thoroughly. --- @param #SCHEDULEDISPATCHER self --- @param Core.Scheduler#SCHEDULER Scheduler -function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop ) - self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop } ) - - self.CallID = self.CallID + 1 - - -- Initialize the ObjectSchedulers array, which is a weakly coupled table. - -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.PersistentSchedulers = self.PersistentSchedulers or {} - - -- Initialize the ObjectSchedulers array, which is a weakly coupled table. - -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) -- or {} - - if Scheduler.MasterObject then - self.ObjectSchedulers[self.CallID] = Scheduler - self:F3( { CallID = self.CallID, ObjectScheduler = tostring(self.ObjectSchedulers[self.CallID]), MasterObject = tostring(Scheduler.MasterObject) } ) - else - self.PersistentSchedulers[self.CallID] = Scheduler - self:F3( { CallID = self.CallID, PersistentScheduler = self.PersistentSchedulers[self.CallID] } ) - end - - self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } ) - self.Schedule[Scheduler] = self.Schedule[Scheduler] or {} - self.Schedule[Scheduler][self.CallID] = {} - self.Schedule[Scheduler][self.CallID].Function = ScheduleFunction - self.Schedule[Scheduler][self.CallID].Arguments = ScheduleArguments - self.Schedule[Scheduler][self.CallID].StartTime = timer.getTime() + ( Start or 0 ) - self.Schedule[Scheduler][self.CallID].Start = Start + .1 - self.Schedule[Scheduler][self.CallID].Repeat = Repeat - self.Schedule[Scheduler][self.CallID].Randomize = Randomize - self.Schedule[Scheduler][self.CallID].Stop = Stop - - self:T3( self.Schedule[Scheduler][self.CallID] ) - - self.Schedule[Scheduler][self.CallID].CallHandler = function( CallID ) - self:F2( CallID ) - - local ErrorHandler = function( errmsg ) - env.info( "Error in timer function: " .. errmsg ) - if debug ~= nil then - env.info( debug.traceback() ) - end - return errmsg - end - - local Scheduler = self.ObjectSchedulers[CallID] - if not Scheduler then - Scheduler = self.PersistentSchedulers[CallID] - end - - self:T3( { Scheduler = Scheduler } ) - - if Scheduler then - - local Schedule = self.Schedule[Scheduler][CallID] - - self:T3( { Schedule = Schedule } ) - - local ScheduleObject = Scheduler.SchedulerObject - --local ScheduleObjectName = Scheduler.SchedulerObject:GetNameAndClassID() - local ScheduleFunction = Schedule.Function - local ScheduleArguments = Schedule.Arguments - local Start = Schedule.Start - local Repeat = Schedule.Repeat or 0 - local Randomize = Schedule.Randomize or 0 - local Stop = Schedule.Stop or 0 - local ScheduleID = Schedule.ScheduleID - - local Status, Result - if ScheduleObject then - local function Timer() - return ScheduleFunction( ScheduleObject, unpack( ScheduleArguments ) ) - end - Status, Result = xpcall( Timer, ErrorHandler ) - else - local function Timer() - return ScheduleFunction( unpack( ScheduleArguments ) ) - end - Status, Result = xpcall( Timer, ErrorHandler ) - end - - local CurrentTime = timer.getTime() - local StartTime = CurrentTime + Start - - if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then - if Repeat ~= 0 and ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) then - local ScheduleTime = - CurrentTime + - Repeat + - math.random( - - ( Randomize * Repeat / 2 ), - ( Randomize * Repeat / 2 ) - ) + - 0.01 - self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } ) - return ScheduleTime -- returns the next time the function needs to be called. - else - self:Stop( Scheduler, CallID ) - end - else - self:Stop( Scheduler, CallID ) - end - else - self:E( "Scheduled obscolete call for CallID: " .. CallID ) - end - - return nil - end - - self:Start( Scheduler, self.CallID ) - - return self.CallID -end - -function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID ) - self:F2( { Remove = CallID, Scheduler = Scheduler } ) - - if CallID then - self:Stop( Scheduler, CallID ) - self.Schedule[Scheduler][CallID] = nil - end -end - -function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) - self:F2( { Start = CallID, Scheduler = Scheduler } ) - - if CallID then - local Schedule = self.Schedule[Scheduler] - -- Only start when there is no ScheduleID defined! - -- This prevents to "Start" the scheduler twice with the same CallID... - if not Schedule[CallID].ScheduleID then - Schedule[CallID].ScheduleID = timer.scheduleFunction( - Schedule[CallID].CallHandler, - CallID, - timer.getTime() + Schedule[CallID].Start - ) - end - else - for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do - self:Start( Scheduler, CallID ) -- Recursive - end - end -end - -function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) - self:F2( { Stop = CallID, Scheduler = Scheduler } ) - - if CallID then - local Schedule = self.Schedule[Scheduler] - -- Only stop when there is a ScheduleID defined for the CallID. - -- So, when the scheduler was stopped before, do nothing. - if Schedule[CallID].ScheduleID then - timer.removeFunction( Schedule[CallID].ScheduleID ) - Schedule[CallID].ScheduleID = nil - end - else - for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do - self:Stop( Scheduler, CallID ) -- Recursive - end - end -end - -function SCHEDULEDISPATCHER:Clear( Scheduler ) - self:F2( { Scheduler = Scheduler } ) - - for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do - self:Stop( Scheduler, CallID ) -- Recursive - end -end - - - ---- **Core** - EVENT models DCS **event dispatching** using a **publish-subscribe** model. --- --- ![Banner Image](..\Presentations\EVENT\Dia1.JPG) --- --- === --- --- # 1) Event Handling Overview --- --- ![Objects](..\Presentations\EVENT\Dia2.JPG) --- --- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc. --- This module provides a mechanism to dispatch those events occuring within your running mission, to the different objects orchestrating your mission. --- --- ![Objects](..\Presentations\EVENT\Dia3.JPG) --- --- Objects can subscribe to different events. The Event dispatcher will publish the received DCS events to the subscribed MOOSE objects, in a specified order. --- In this way, the subscribed MOOSE objects are kept in sync with your evolving running mission. --- --- ## 1.1) Event Dispatching --- --- ![Objects](..\Presentations\EVENT\Dia4.JPG) --- --- The _EVENTDISPATCHER object is automatically created within MOOSE, --- and handles the dispatching of DCS Events occurring --- in the simulator to the subscribed objects --- in the correct processing order. --- --- ![Objects](..\Presentations\EVENT\Dia5.JPG) --- --- There are 5 levels of kind of objects that the _EVENTDISPATCHER services: --- --- * _DATABASE object: The core of the MOOSE objects. Any object that is created, deleted or updated, is done in this database. --- * SET_ derived classes: Subsets of the _DATABASE object. These subsets are updated by the _EVENTDISPATCHER as the second priority. --- * UNIT objects: UNIT objects can subscribe to DCS events. Each DCS event will be directly published to teh subscribed UNIT object. --- * GROUP objects: GROUP objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed GROUP object. --- * Any other object: Various other objects can subscribe to DCS events. Each DCS event triggered will be published to each subscribed object. --- --- ![Objects](..\Presentations\EVENT\Dia6.JPG) --- --- For most DCS events, the above order of updating will be followed. --- --- ![Objects](..\Presentations\EVENT\Dia7.JPG) --- --- But for some DCS events, the publishing order is reversed. This is due to the fact that objects need to be **erased** instead of added. --- --- ## 1.2) Event Handling --- --- ![Objects](..\Presentations\EVENT\Dia8.JPG) --- --- The actual event subscribing and handling is not facilitated through the _EVENTDISPATCHER, but it is done through the @{BASE} class, @{UNIT} class and @{GROUP} class. --- The _EVENTDISPATCHER is a component that is quietly working in the background of MOOSE. --- --- ![Objects](..\Presentations\EVENT\Dia9.JPG) --- --- The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator, --- and handled through lua scripting. MOOSE provides an encapsulation to handle these events more efficiently. --- --- ### 1.2.1 Subscribe / Unsubscribe to DCS Events --- --- At first, the mission designer will need to **Subscribe** to a specific DCS event for the class. --- So, when the DCS event occurs, the class will be notified of that event. --- There are two functions which you use to subscribe to or unsubscribe from an event. --- --- * @{Base#BASE.HandleEvent}(): Subscribe to a DCS Event. --- * @{Base#BASE.UnHandleEvent}(): Unsubscribe from a DCS Event. --- --- Note that for a UNIT, the event will be handled **for that UNIT only**! --- Note that for a GROUP, the event will be handled **for all the UNITs in that GROUP only**! --- --- For all objects of other classes, the subscribed events will be handled for **all UNITs within the Mission**! --- So if a UNIT within the mission has the subscribed event for that object, --- then the object event handler will receive the event for that UNIT! --- --- ### 1.3.2 Event Handling of DCS Events --- --- Once the class is subscribed to the event, an **Event Handling** method on the object or class needs to be written that will be called --- when the DCS event occurs. The Event Handling method receives an @{Event#EVENTDATA} structure, which contains a lot of information --- about the event that occurred. --- --- Find below an example of the prototype how to write an event handling function for two units: --- --- local Tank1 = UNIT:FindByName( "Tank A" ) --- local Tank2 = UNIT:FindByName( "Tank B" ) --- --- -- Here we subscribe to the Dead events. So, if one of these tanks dies, the Tank1 or Tank2 objects will be notified. --- Tank1:HandleEvent( EVENTS.Dead ) --- Tank2:HandleEvent( EVENTS.Dead ) --- --- --- This function is an Event Handling function that will be called when Tank1 is Dead. --- -- @param Wrapper.Unit#UNIT self --- -- @param Core.Event#EVENTDATA EventData --- function Tank1:OnEventDead( EventData ) --- --- self:SmokeGreen() --- end --- --- --- This function is an Event Handling function that will be called when Tank2 is Dead. --- -- @param Wrapper.Unit#UNIT self --- -- @param Core.Event#EVENTDATA EventData --- function Tank2:OnEventDead( EventData ) --- --- self:SmokeBlue() --- end --- --- ### 1.3.3 Event Handling methods that are automatically called upon subscribed DCS events --- --- ![Objects](..\Presentations\EVENT\Dia10.JPG) --- --- The following list outlines which EVENTS item in the structure corresponds to which Event Handling method. --- Always ensure that your event handling methods align with the events being subscribed to, or nothing will be executed. --- --- # 2) EVENTS type --- --- The EVENTS structure contains names for all the different DCS events that objects can subscribe to using the --- @{Base#BASE.HandleEvent}() method. --- --- # 3) EVENTDATA type --- --- The @{Event#EVENTDATA} structure contains all the fields that are populated with event information before --- an Event Handler method is being called by the event dispatcher. --- The Event Handler received the EVENTDATA object as a parameter, and can be used to investigate further the different events. --- There are basically 4 main categories of information stored in the EVENTDATA structure: --- --- * Initiator Unit data: Several fields documenting the initiator unit related to the event. --- * Target Unit data: Several fields documenting the target unit related to the event. --- * Weapon data: Certain events populate weapon information. --- * Place data: Certain events populate place information. --- --- --- This function is an Event Handling function that will be called when Tank1 is Dead. --- -- EventData is an EVENTDATA structure. --- -- We use the EventData.IniUnit to smoke the tank Green. --- -- @param Wrapper.Unit#UNIT self --- -- @param Core.Event#EVENTDATA EventData --- function Tank1:OnEventDead( EventData ) --- --- EventData.IniUnit:SmokeGreen() --- end --- --- --- Find below an overview which events populate which information categories: --- --- ![Objects](..\Presentations\EVENT\Dia14.JPG) --- --- **IMPORTANT NOTE:** Some events can involve not just UNIT objects, but also STATIC objects!!! --- In that case the initiator or target unit fields will refer to a STATIC object! --- In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated. --- The fields **IniObjectCategory** and **TgtObjectCategory** contain the indicator which **kind of object is involved** in the event. --- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory. --- Example code snippet: --- --- if Event.IniObjectCategory == Object.Category.UNIT then --- ... --- end --- if Event.IniObjectCategory == Object.Category.STATIC then --- ... --- end --- --- When a static object is involved in the event, the Group and Player fields won't be populated. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) --- YYYY-MM-DD: CLASS:**NewFunction( Params )** added --- --- Hereby the change log: --- --- * 2017-03-07: Added the correct event dispatching in case the event is subscribed by a GROUP. --- --- * 2017-02-07: Did a complete revision of the Event Handing API and underlying mechanisms. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- ### Authors: --- --- * [**FlightControl**](https://forums.eagle.ru/member.php?u=89536): Design & Programming & documentation. --- --- @module Event - - ---- The EVENT structure --- @type EVENT --- @field #EVENT.Events Events --- @extends Core.Base#BASE -EVENT = { - ClassName = "EVENT", - ClassID = 0, -} - ---- The different types of events supported by MOOSE. --- Use this structure to subscribe to events using the @{Base#BASE.HandleEvent}() method. --- @type EVENTS -EVENTS = { - Shot = world.event.S_EVENT_SHOT, - Hit = world.event.S_EVENT_HIT, - Takeoff = world.event.S_EVENT_TAKEOFF, - Land = world.event.S_EVENT_LAND, - Crash = world.event.S_EVENT_CRASH, - Ejection = world.event.S_EVENT_EJECTION, - Refueling = world.event.S_EVENT_REFUELING, - Dead = world.event.S_EVENT_DEAD, - PilotDead = world.event.S_EVENT_PILOT_DEAD, - BaseCaptured = world.event.S_EVENT_BASE_CAPTURED, - MissionStart = world.event.S_EVENT_MISSION_START, - MissionEnd = world.event.S_EVENT_MISSION_END, - TookControl = world.event.S_EVENT_TOOK_CONTROL, - RefuelingStop = world.event.S_EVENT_REFUELING_STOP, - Birth = world.event.S_EVENT_BIRTH, - HumanFailure = world.event.S_EVENT_HUMAN_FAILURE, - EngineStartup = world.event.S_EVENT_ENGINE_STARTUP, - EngineShutdown = world.event.S_EVENT_ENGINE_SHUTDOWN, - PlayerEnterUnit = world.event.S_EVENT_PLAYER_ENTER_UNIT, - PlayerLeaveUnit = world.event.S_EVENT_PLAYER_LEAVE_UNIT, - PlayerComment = world.event.S_EVENT_PLAYER_COMMENT, - ShootingStart = world.event.S_EVENT_SHOOTING_START, - ShootingEnd = world.event.S_EVENT_SHOOTING_END, -} - ---- The Event structure --- Note that at the beginning of each field description, there is an indication which field will be populated depending on the object type involved in the Event: --- --- * A (Object.Category.)UNIT : A UNIT object type is involved in the Event. --- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event.µ --- --- @type EVENTDATA --- @field #number id The identifier of the event. --- --- @field Dcs.DCSUnit#Unit initiator (UNIT/STATIC/SCENERY) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. --- @field Dcs.DCSObject#Object.Category IniObjectCategory (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). --- @field Dcs.DCSUnit#Unit IniDCSUnit (UNIT/STATIC) The initiating @{DCSUnit#Unit} or @{DCSStaticObject#StaticObject}. --- @field #string IniDCSUnitName (UNIT/STATIC) The initiating Unit name. --- @field Wrapper.Unit#UNIT IniUnit (UNIT/STATIC) The initiating MOOSE wrapper @{Unit#UNIT} of the initiator Unit object. --- @field #string IniUnitName (UNIT/STATIC) The initiating UNIT name (same as IniDCSUnitName). --- @field Dcs.DCSGroup#Group IniDCSGroup (UNIT) The initiating {DCSGroup#Group}. --- @field #string IniDCSGroupName (UNIT) The initiating Group name. --- @field Wrapper.Group#GROUP IniGroup (UNIT) The initiating MOOSE wrapper @{Group#GROUP} of the initiator Group object. --- @field #string IniGroupName UNIT) The initiating GROUP name (same as IniDCSGroupName). --- @field #string IniPlayerName (UNIT) The name of the initiating player in case the Unit is a client or player slot. --- @field Dcs.DCScoalition#coalition.side IniCoalition (UNIT) The coalition of the initiator. --- @field Dcs.DCSUnit#Unit.Category IniCategory (UNIT) The category of the initiator. --- @field #string IniTypeName (UNIT) The type name of the initiator. --- --- @field Dcs.DCSUnit#Unit target (UNIT/STATIC) The target @{Dcs.DCSUnit#Unit} or @{DCSStaticObject#StaticObject}. --- @field Dcs.DCSObject#Object.Category TgtObjectCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). --- @field Dcs.DCSUnit#Unit TgtDCSUnit (UNIT/STATIC) The target @{DCSUnit#Unit} or @{DCSStaticObject#StaticObject}. --- @field #string TgtDCSUnitName (UNIT/STATIC) The target Unit name. --- @field Wrapper.Unit#UNIT TgtUnit (UNIT/STATIC) The target MOOSE wrapper @{Unit#UNIT} of the target Unit object. --- @field #string TgtUnitName (UNIT/STATIC) The target UNIT name (same as TgtDCSUnitName). --- @field Dcs.DCSGroup#Group TgtDCSGroup (UNIT) The target {DCSGroup#Group}. --- @field #string TgtDCSGroupName (UNIT) The target Group name. --- @field Wrapper.Group#GROUP TgtGroup (UNIT) The target MOOSE wrapper @{Group#GROUP} of the target Group object. --- @field #string TgtGroupName (UNIT) The target GROUP name (same as TgtDCSGroupName). --- @field #string TgtPlayerName (UNIT) The name of the target player in case the Unit is a client or player slot. --- @field Dcs.DCScoalition#coalition.side TgtCoalition (UNIT) The coalition of the target. --- @field Dcs.DCSUnit#Unit.Category TgtCategory (UNIT) The category of the target. --- @field #string TgtTypeName (UNIT) The type name of the target. --- --- @field weapon The weapon used during the event. --- @field Weapon --- @field WeaponName --- @field WeaponTgtDCSUnit - - -local _EVENTMETA = { - [world.event.S_EVENT_SHOT] = { - Order = 1, - Event = "OnEventShot", - Text = "S_EVENT_SHOT" - }, - [world.event.S_EVENT_HIT] = { - Order = 1, - Event = "OnEventHit", - Text = "S_EVENT_HIT" - }, - [world.event.S_EVENT_TAKEOFF] = { - Order = 1, - Event = "OnEventTakeoff", - Text = "S_EVENT_TAKEOFF" - }, - [world.event.S_EVENT_LAND] = { - Order = 1, - Event = "OnEventLand", - Text = "S_EVENT_LAND" - }, - [world.event.S_EVENT_CRASH] = { - Order = -1, - Event = "OnEventCrash", - Text = "S_EVENT_CRASH" - }, - [world.event.S_EVENT_EJECTION] = { - Order = 1, - Event = "OnEventEjection", - Text = "S_EVENT_EJECTION" - }, - [world.event.S_EVENT_REFUELING] = { - Order = 1, - Event = "OnEventRefueling", - Text = "S_EVENT_REFUELING" - }, - [world.event.S_EVENT_DEAD] = { - Order = -1, - Event = "OnEventDead", - Text = "S_EVENT_DEAD" - }, - [world.event.S_EVENT_PILOT_DEAD] = { - Order = 1, - Event = "OnEventPilotDead", - Text = "S_EVENT_PILOT_DEAD" - }, - [world.event.S_EVENT_BASE_CAPTURED] = { - Order = 1, - Event = "OnEventBaseCaptured", - Text = "S_EVENT_BASE_CAPTURED" - }, - [world.event.S_EVENT_MISSION_START] = { - Order = 1, - Event = "OnEventMissionStart", - Text = "S_EVENT_MISSION_START" - }, - [world.event.S_EVENT_MISSION_END] = { - Order = 1, - Event = "OnEventMissionEnd", - Text = "S_EVENT_MISSION_END" - }, - [world.event.S_EVENT_TOOK_CONTROL] = { - Order = 1, - Event = "OnEventTookControl", - Text = "S_EVENT_TOOK_CONTROL" - }, - [world.event.S_EVENT_REFUELING_STOP] = { - Order = 1, - Event = "OnEventRefuelingStop", - Text = "S_EVENT_REFUELING_STOP" - }, - [world.event.S_EVENT_BIRTH] = { - Order = 1, - Event = "OnEventBirth", - Text = "S_EVENT_BIRTH" - }, - [world.event.S_EVENT_HUMAN_FAILURE] = { - Order = 1, - Event = "OnEventHumanFailure", - Text = "S_EVENT_HUMAN_FAILURE" - }, - [world.event.S_EVENT_ENGINE_STARTUP] = { - Order = 1, - Event = "OnEventEngineStartup", - Text = "S_EVENT_ENGINE_STARTUP" - }, - [world.event.S_EVENT_ENGINE_SHUTDOWN] = { - Order = 1, - Event = "OnEventEngineShutdown", - Text = "S_EVENT_ENGINE_SHUTDOWN" - }, - [world.event.S_EVENT_PLAYER_ENTER_UNIT] = { - Order = 1, - Event = "OnEventPlayerEnterUnit", - Text = "S_EVENT_PLAYER_ENTER_UNIT" - }, - [world.event.S_EVENT_PLAYER_LEAVE_UNIT] = { - Order = -1, - Event = "OnEventPlayerLeaveUnit", - Text = "S_EVENT_PLAYER_LEAVE_UNIT" - }, - [world.event.S_EVENT_PLAYER_COMMENT] = { - Order = 1, - Event = "OnEventPlayerComment", - Text = "S_EVENT_PLAYER_COMMENT" - }, - [world.event.S_EVENT_SHOOTING_START] = { - Order = 1, - Event = "OnEventShootingStart", - Text = "S_EVENT_SHOOTING_START" - }, - [world.event.S_EVENT_SHOOTING_END] = { - Order = 1, - Event = "OnEventShootingEnd", - Text = "S_EVENT_SHOOTING_END" - }, -} - - ---- The Events structure --- @type EVENT.Events --- @field #number IniUnit - -function EVENT:New() - local self = BASE:Inherit( self, BASE:New() ) - self:F2() - self.EventHandler = world.addEventHandler( self ) - return self -end - -function EVENT:EventText( EventID ) - - local EventText = _EVENTMETA[EventID].Text - - return EventText -end - - ---- Initializes the Events structure for the event --- @param #EVENT self --- @param Dcs.DCSWorld#world.event EventID --- @param Core.Base#BASE EventClass --- @return #EVENT.Events -function EVENT:Init( EventID, EventClass ) - self:F3( { _EVENTMETA[EventID].Text, EventClass } ) - - if not self.Events[EventID] then - -- Create a WEAK table to ensure that the garbage collector is cleaning the event links when the object usage is cleaned. - self.Events[EventID] = setmetatable( {}, { __mode = "k" } ) - end - - -- Each event has a subtable of EventClasses, ordered by EventPriority. - local EventPriority = EventClass:GetEventPriority() - if not self.Events[EventID][EventPriority] then - self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } ) - end - - if not self.Events[EventID][EventPriority][EventClass] then - self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "v" } ) - end - return self.Events[EventID][EventPriority][EventClass] -end - ---- Removes an Events entry --- @param #EVENT self --- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param Dcs.DCSWorld#world.event EventID --- @return #EVENT.Events -function EVENT:Remove( EventClass, EventID ) - self:F3( { EventClass, _EVENTMETA[EventID].Text } ) - - local EventClass = EventClass - local EventPriority = EventClass:GetEventPriority() - self.Events[EventID][EventPriority][EventClass] = nil -end - ---- Removes an Events entry for a UNIT. --- @param #EVENT self --- @param #string UnitName The name of the UNIT. --- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param Dcs.DCSWorld#world.event EventID --- @return #EVENT.Events -function EVENT:RemoveForUnit( UnitName, EventClass, EventID ) - self:F3( { EventClass, _EVENTMETA[EventID].Text } ) - - local EventClass = EventClass - local EventPriority = EventClass:GetEventPriority() - local Event = self.Events[EventID][EventPriority][EventClass] - Event.EventUnit[UnitName] = nil -end - ---- Removes an Events entry for a GROUP. --- @param #EVENT self --- @param #string GroupName The name of the GROUP. --- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param Dcs.DCSWorld#world.event EventID --- @return #EVENT.Events -function EVENT:RemoveForGroup( GroupName, EventClass, EventID ) - self:F3( { EventClass, _EVENTMETA[EventID].Text } ) - - local EventClass = EventClass - local EventPriority = EventClass:GetEventPriority() - local Event = self.Events[EventID][EventPriority][EventClass] - Event.EventGroup[GroupName] = nil -end - ---- Clears all event subscriptions for a @{Base#BASE} derived object. --- @param #EVENT self --- @param Core.Base#BASE EventObject -function EVENT:RemoveAll( EventObject ) - self:F3( { EventObject:GetClassNameAndID() } ) - - local EventClass = EventObject:GetClassNameAndID() - local EventPriority = EventClass:GetEventPriority() - for EventID, EventData in pairs( self.Events ) do - self.Events[EventID][EventPriority][EventClass] = nil - end -end - - - ---- Create an OnDead event handler for a group --- @param #EVENT self --- @param #table EventTemplate --- @param #function EventFunction The function to be called when the event occurs for the unit. --- @param EventClass The instance of the class for which the event is. --- @param #function OnEventFunction --- @return #EVENT -function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID ) - self:F2( EventTemplate.name ) - - for EventUnitID, EventUnit in pairs( EventTemplate.units ) do - self:OnEventForUnit( EventUnit.name, EventFunction, EventClass, EventID ) - end - return self -end - ---- Set a new listener for an S_EVENT_X event independent from a unit or a weapon. --- @param #EVENT self --- @param #function EventFunction The function to be called when the event occurs for the unit. --- @param Core.Base#BASE EventClass The self instance of the class for which the event is captured. When the event happens, the event process will be called in this class provided. --- @param EventID --- @return #EVENT -function EVENT:OnEventGeneric( EventFunction, EventClass, EventID ) - self:F2( { EventID } ) - - local EventData = self:Init( EventID, EventClass ) - EventData.EventFunction = EventFunction - EventData.EventClass = EventClass - - return self -end - - ---- Set a new listener for an S_EVENT_X event for a UNIT. --- @param #EVENT self --- @param #string UnitName The name of the UNIT. --- @param #function EventFunction The function to be called when the event occurs for the GROUP. --- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param EventID --- @return #EVENT -function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID ) - self:F2( UnitName ) - - local EventData = self:Init( EventID, EventClass ) - if not EventData.EventUnit then - EventData.EventUnit = {} - end - EventData.EventUnit[UnitName] = {} - EventData.EventUnit[UnitName].EventFunction = EventFunction - EventData.EventUnit[UnitName].EventClass = EventClass - return self -end - ---- Set a new listener for an S_EVENT_X event for a GROUP. --- @param #EVENT self --- @param #string GroupName The name of the GROUP. --- @param #function EventFunction The function to be called when the event occurs for the GROUP. --- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param EventID --- @return #EVENT -function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID ) - self:F2( GroupName ) - - local Event = self:Init( EventID, EventClass ) - if not Event.EventGroup then - Event.EventGroup = {} - end - Event.EventGroup[GroupName] = {} - Event.EventGroup[GroupName].EventFunction = EventFunction - Event.EventGroup[GroupName].EventClass = EventClass - return self -end - -do -- OnBirth - - --- Create an OnBirth event handler for a group - -- @param #EVENT self - -- @param Wrapper.Group#GROUP EventGroup - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass ) - self:F2( EventTemplate.name ) - - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Birth ) - - return self - end - -end - -do -- OnCrash - - --- Create an OnCrash event handler for a group - -- @param #EVENT self - -- @param Wrapper.Group#GROUP EventGroup - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventClass ) - self:F2( EventTemplate.name ) - - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Crash ) - - return self - end - -end - -do -- OnDead - - --- Create an OnDead event handler for a group - -- @param #EVENT self - -- @param Wrapper.Group#GROUP EventGroup - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass ) - self:F2( EventTemplate.name ) - - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Dead ) - - return self - end - -end - - -do -- OnLand - --- Create an OnLand event handler for a group - -- @param #EVENT self - -- @param #table EventTemplate - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass ) - self:F2( EventTemplate.name ) - - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Land ) - - return self - end - -end - -do -- OnTakeOff - --- Create an OnTakeOff event handler for a group - -- @param #EVENT self - -- @param #table EventTemplate - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass ) - self:F2( EventTemplate.name ) - - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Takeoff ) - - return self - end - -end - -do -- OnEngineShutDown - - --- Create an OnDead event handler for a group - -- @param #EVENT self - -- @param #table EventTemplate - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventClass ) - self:F2( EventTemplate.name ) - - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.EngineShutdown ) - - return self - end - -end - - ---- @param #EVENT self --- @param #EVENTDATA Event -function EVENT:onEvent( Event ) - - local ErrorHandler = function( errmsg ) - - env.info( "Error in SCHEDULER function:" .. errmsg ) - if debug ~= nil then - env.info( debug.traceback() ) - end - - return errmsg - end - - self:E( _EVENTMETA[Event.id].Text, Event ) - - if self and self.Events and self.Events[Event.id] then - - - if Event.initiator then - - Event.IniObjectCategory = Event.initiator:getCategory() - - if Event.IniObjectCategory == Object.Category.UNIT then - Event.IniDCSUnit = Event.initiator - Event.IniDCSUnitName = Event.IniDCSUnit:getName() - Event.IniUnitName = Event.IniDCSUnitName - Event.IniDCSGroup = Event.IniDCSUnit:getGroup() - Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName ) - if not Event.IniUnit then - -- Unit can be a CLIENT. Most likely this will be the case ... - Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true ) - end - Event.IniDCSGroupName = "" - if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then - Event.IniDCSGroupName = Event.IniDCSGroup:getName() - Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName ) - if Event.IniGroup then - Event.IniGroupName = Event.IniDCSGroupName - end - end - Event.IniPlayerName = Event.IniDCSUnit:getPlayerName() - Event.IniCoalition = Event.IniDCSUnit:getCoalition() - Event.IniTypeName = Event.IniDCSUnit:getTypeName() - Event.IniCategory = Event.IniDCSUnit:getDesc().category - end - - if Event.IniObjectCategory == Object.Category.STATIC then - Event.IniDCSUnit = Event.initiator - Event.IniDCSUnitName = Event.IniDCSUnit:getName() - Event.IniUnitName = Event.IniDCSUnitName - Event.IniUnit = STATIC:FindByName( Event.IniDCSUnitName, false ) - Event.IniCoalition = Event.IniDCSUnit:getCoalition() - Event.IniCategory = Event.IniDCSUnit:getDesc().category - Event.IniTypeName = Event.IniDCSUnit:getTypeName() - end - - if Event.IniObjectCategory == Object.Category.SCENERY then - Event.IniDCSUnit = Event.initiator - Event.IniDCSUnitName = Event.IniDCSUnit:getName() - Event.IniUnitName = Event.IniDCSUnitName - Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator ) - Event.IniCategory = Event.IniDCSUnit:getDesc().category - Event.IniTypeName = Event.IniDCSUnit:getTypeName() - end - end - - if Event.target then - - Event.TgtObjectCategory = Event.target:getCategory() - - if Event.TgtObjectCategory == Object.Category.UNIT then - Event.TgtDCSUnit = Event.target - Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup() - Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() - Event.TgtUnitName = Event.TgtDCSUnitName - Event.TgtUnit = UNIT:FindByName( Event.TgtDCSUnitName ) - Event.TgtDCSGroupName = "" - if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then - Event.TgtDCSGroupName = Event.TgtDCSGroup:getName() - Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) - if Event.TgtGroup then - Event.TgtGroupName = Event.TgtDCSGroupName - end - end - Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName() - Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() - Event.TgtCategory = Event.TgtDCSUnit:getDesc().category - Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() - end - - if Event.TgtObjectCategory == Object.Category.STATIC then - Event.TgtDCSUnit = Event.target - Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() - Event.TgtUnitName = Event.TgtDCSUnitName - Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName ) - Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() - Event.TgtCategory = Event.TgtDCSUnit:getDesc().category - Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() - end - - if Event.TgtObjectCategory == Object.Category.SCENERY then - Event.TgtDCSUnit = Event.target - Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() - Event.TgtUnitName = Event.TgtDCSUnitName - Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target ) - Event.TgtCategory = Event.TgtDCSUnit:getDesc().category - Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() - end - end - - if Event.weapon then - Event.Weapon = Event.weapon - Event.WeaponName = Event.Weapon:getTypeName() - Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit! - Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName() - Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition() - Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category - Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() - --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() - end - - local PriorityOrder = _EVENTMETA[Event.id].Order - local PriorityBegin = PriorityOrder == -1 and 5 or 1 - local PriorityEnd = PriorityOrder == -1 and 1 or 5 - - if Event.IniObjectCategory ~= 3 then - self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) - end - - for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do - - if self.Events[Event.id][EventPriority] then - - -- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called. - for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do - - Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName ) - Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) - - -- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT. - if ( Event.IniDCSUnitName and EventData.EventUnit and EventData.EventUnit[Event.IniDCSUnitName] ) or - ( Event.TgtDCSUnitName and EventData.EventUnit and EventData.EventUnit[Event.TgtDCSUnitName] ) then - - if EventData.EventUnit[Event.IniDCSUnitName] then - - -- First test if a EventFunction is Set, otherwise search for the default function - if EventData.EventUnit[Event.IniDCSUnitName].EventFunction then - - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event ) - end, ErrorHandler ) - - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event ) - end, ErrorHandler ) - end - end - end - - if EventData.EventUnit[Event.TgtDCSUnitName] then - - -- First test if a EventFunction is Set, otherwise search for the default function - if EventData.EventUnit[Event.TgtDCSUnitName].EventFunction then - - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventData.EventUnit[Event.TgtDCSUnitName].EventFunction( EventClass, Event ) - end, ErrorHandler ) - - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event ) - end, ErrorHandler ) - end - end - end - - else - - -- If the EventData is for a GROUP, the call directly the EventClass EventFunction for the UNIT in that GROUP. - if ( Event.IniDCSUnitName and Event.IniDCSGroupName and Event.IniGroupName and EventData.EventGroup and EventData.EventGroup[Event.IniGroupName] ) or - ( Event.TgtDCSUnitName and Event.TgtDCSGroupName and Event.TgtGroupName and EventData.EventGroup and EventData.EventGroup[Event.TgtGroupName] ) then - - if EventData.EventGroup[Event.IniGroupName] then - -- First test if a EventFunction is Set, otherwise search for the default function - if EventData.EventGroup[Event.IniGroupName].EventFunction then - - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event ) - end, ErrorHandler ) - - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event ) - end, ErrorHandler ) - end - end - end - - if EventData.EventGroup[Event.TgtGroupName] then - if EventData.EventGroup[Event.TgtGroupName].EventFunction then - - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event ) - end, ErrorHandler ) - - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event ) - end, ErrorHandler ) - end - end - end - - else - - -- If the EventData is not bound to a specific unit, then call the EventClass EventFunction. - -- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon. - if (Event.IniDCSUnit or Event.WeaponUNIT) and not EventData.EventUnit then - - if EventClass == EventData.EventClass then - - -- First test if a EventFunction is Set, otherwise search for the default function - if EventData.EventFunction then - - -- There is an EventFunction defined, so call the EventFunction. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - end - local Result, Value = xpcall( - function() - return EventData.EventFunction( EventClass, Event ) - end, ErrorHandler ) - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - end - - local Result, Value = xpcall( - function() - local Result, Value = EventFunction( EventClass, Event ) - return Result, Value - end, ErrorHandler ) - end - end - end - end - end - end - end - end - end - else - self:E( { _EVENTMETA[Event.id].Text, Event } ) - end - - Event = nil -end - ---- The EVENTHANDLER structure --- @type EVENTHANDLER --- @extends Core.Base#BASE -EVENTHANDLER = { - ClassName = "EVENTHANDLER", - ClassID = 0, -} - ---- The EVENTHANDLER constructor --- @param #EVENTHANDLER self --- @return #EVENTHANDLER -function EVENTHANDLER:New() - self = BASE:Inherit( self, BASE:New() ) -- #EVENTHANDLER - return self -end ---- **Core** -- MENU_ classes model the definition of **hierarchical menu structures** and **commands for players** within a mission. --- --- === --- --- DCS Menus can be managed using the MENU classes. --- The advantage of using MENU classes is that it hides the complexity of dealing with menu management in more advanced scanerios where you need to --- set menus and later remove them, and later set them again. You'll find while using use normal DCS scripting functions, that setting and removing --- menus is not a easy feat if you have complex menu hierarchies defined. --- Using the MOOSE menu classes, the removal and refreshing of menus are nicely being handled within these classes, and becomes much more easy. --- On top, MOOSE implements **variable parameter** passing for command menus. --- --- There are basically two different MENU class types that you need to use: --- --- ### To manage **main menus**, the classes begin with **MENU_**: --- --- * @{Menu#MENU_MISSION}: Manages main menus for whole mission file. --- * @{Menu#MENU_COALITION}: Manages main menus for whole coalition. --- * @{Menu#MENU_GROUP}: Manages main menus for GROUPs. --- * @{Menu#MENU_CLIENT}: Manages main menus for CLIENTs. This manages menus for units with the skill level "Client". --- --- ### To manage **command menus**, which are menus that allow the player to issue **functions**, the classes begin with **MENU_COMMAND_**: --- --- * @{Menu#MENU_MISSION_COMMAND}: Manages command menus for whole mission file. --- * @{Menu#MENU_COALITION_COMMAND}: Manages command menus for whole coalition. --- * @{Menu#MENU_GROUP_COMMAND}: Manages command menus for GROUPs. --- * @{Menu#MENU_CLIENT_COMMAND}: Manages command menus for CLIENTs. This manages menus for units with the skill level "Client". --- --- === --- --- The above menus classes **are derived** from 2 main **abstract** classes defined within the MOOSE framework (so don't use these): --- --- 1) MENU_ BASE abstract base classes (don't use them) --- ==================================================== --- The underlying base menu classes are **NOT** to be used within your missions. --- These are simply abstract base classes defining a couple of fields that are used by the --- derived MENU_ classes to manage menus. --- --- 1.1) @{#MENU_BASE} class, extends @{Base#BASE} --- -------------------------------------------------- --- The @{#MENU_BASE} class defines the main MENU class where other MENU classes are derived from. --- --- 1.2) @{#MENU_COMMAND_BASE} class, extends @{Base#BASE} --- ---------------------------------------------------------- --- The @{#MENU_COMMAND_BASE} class defines the main MENU class where other MENU COMMAND_ classes are derived from, in order to set commands. --- --- === --- --- **The next menus define the MENU classes that you can use within your missions.** --- --- 2) MENU MISSION classes --- ====================== --- The underlying classes manage the menus for a complete mission file. --- --- 2.1) @{#MENU_MISSION} class, extends @{Menu#MENU_BASE} --- --------------------------------------------------------- --- The @{Menu#MENU_MISSION} class manages the main menus for a complete mission. --- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}. --- --- 2.2) @{#MENU_MISSION_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE} --- ------------------------------------------------------------------------- --- The @{Menu#MENU_MISSION_COMMAND} class manages the command menus for a complete mission, which allow players to execute functions during mission execution. --- You can add menus with the @{#MENU_MISSION_COMMAND.New} method, which constructs a MENU_MISSION_COMMAND object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION_COMMAND.Remove}. --- --- === --- --- 3) MENU COALITION classes --- ========================= --- The underlying classes manage the menus for whole coalitions. --- --- 3.1) @{#MENU_COALITION} class, extends @{Menu#MENU_BASE} --- ------------------------------------------------------------ --- The @{Menu#MENU_COALITION} class manages the main menus for coalitions. --- You can add menus with the @{#MENU_COALITION.New} method, which constructs a MENU_COALITION object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION.Remove}. --- --- 3.2) @{Menu#MENU_COALITION_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE} --- ---------------------------------------------------------------------------- --- The @{Menu#MENU_COALITION_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. --- You can add menus with the @{#MENU_COALITION_COMMAND.New} method, which constructs a MENU_COALITION_COMMAND object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION_COMMAND.Remove}. --- --- === --- --- 4) MENU GROUP classes --- ===================== --- The underlying classes manage the menus for groups. Note that groups can be inactive, alive or can be destroyed. --- --- 4.1) @{Menu#MENU_GROUP} class, extends @{Menu#MENU_BASE} --- -------------------------------------------------------- --- The @{Menu#MENU_GROUP} class manages the main menus for coalitions. --- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}. --- --- 4.2) @{Menu#MENU_GROUP_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE} --- ------------------------------------------------------------------------ --- The @{Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. --- You can add menus with the @{#MENU_GROUP_COMMAND.New} method, which constructs a MENU_GROUP_COMMAND object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP_COMMAND.Remove}. --- --- === --- --- 5) MENU CLIENT classes --- ====================== --- The underlying classes manage the menus for units with skill level client or player. --- --- 5.1) @{Menu#MENU_CLIENT} class, extends @{Menu#MENU_BASE} --- --------------------------------------------------------- --- The @{Menu#MENU_CLIENT} class manages the main menus for coalitions. --- You can add menus with the @{#MENU_CLIENT.New} method, which constructs a MENU_CLIENT object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT.Remove}. --- --- 5.2) @{Menu#MENU_CLIENT_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE} --- ------------------------------------------------------------------------- --- The @{Menu#MENU_CLIENT_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. --- You can add menus with the @{#MENU_CLIENT_COMMAND.New} method, which constructs a MENU_CLIENT_COMMAND object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT_COMMAND.Remove}. --- --- === --- --- ### Contributions: - --- ### Authors: FlightControl : Design & Programming --- --- @module Menu - - -do -- MENU_BASE - - --- The MENU_BASE class - -- @type MENU_BASE - -- @extends Base#BASE - MENU_BASE = { - ClassName = "MENU_BASE", - MenuPath = nil, - MenuText = "", - MenuParentPath = nil - } - - --- Consructor - -- @param #MENU_BASE - -- @return #MENU_BASE - function MENU_BASE:New( MenuText, ParentMenu ) - - local MenuParentPath = {} - if ParentMenu ~= nil then - MenuParentPath = ParentMenu.MenuPath - end - - local self = BASE:Inherit( self, BASE:New() ) - - self.MenuPath = nil - self.MenuText = MenuText - self.MenuParentPath = MenuParentPath - self.Menus = {} - self.MenuCount = 0 - self.MenuRemoveParent = false - self.MenuTime = timer.getTime() - - return self - end - - --- Gets a @{Menu} from a parent @{Menu} - -- @param #MENU_BASE self - -- @param #string MenuText The text of the child menu. - -- @return #MENU_BASE - function MENU_BASE:GetMenu( MenuText ) - self:F( { self.Menus, MenuText } ) - return self.Menus[MenuText] - end - - --- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}. - -- @param #MENU_BASE self - -- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}. - -- @return #MENU_BASE - function MENU_BASE:SetRemoveParent( RemoveParent ) - self:F( { RemoveParent } ) - self.MenuRemoveParent = RemoveParent - return self - end - - - --- Sets a time stamp for later prevention of menu removal. - -- @param #MENU_BASE self - -- @param MenuTime - -- @return #MENU_BASE - function MENU_BASE:SetTime( MenuTime ) - self.MenuTime = MenuTime - return self - end - -end - -do -- MENU_COMMAND_BASE - - --- The MENU_COMMAND_BASE class - -- @type MENU_COMMAND_BASE - -- @field #function MenuCallHandler - -- @extends Core.Menu#MENU_BASE - MENU_COMMAND_BASE = { - ClassName = "MENU_COMMAND_BASE", - CommandMenuFunction = nil, - CommandMenuArgument = nil, - MenuCallHandler = nil, - } - - --- Constructor - -- @param #MENU_COMMAND_BASE - -- @return #MENU_COMMAND_BASE - function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments ) - - local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) - - self.CommandMenuFunction = CommandMenuFunction - self.MenuCallHandler = function( CommandMenuArguments ) - self.CommandMenuFunction( unpack( CommandMenuArguments ) ) - end - - return self - end - -end - - -do -- MENU_MISSION - - --- The MENU_MISSION class - -- @type MENU_MISSION - -- @extends Core.Menu#MENU_BASE - MENU_MISSION = { - ClassName = "MENU_MISSION" - } - - --- MENU_MISSION constructor. Creates a new MENU_MISSION object and creates the menu for a complete mission file. - -- @param #MENU_MISSION self - -- @param #string MenuText The text for the menu. - -- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other). - -- @return #MENU_MISSION - function MENU_MISSION:New( MenuText, ParentMenu ) - - local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) - - self:F( { MenuText, ParentMenu } ) - - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self.Menus = {} - - self:T( { MenuText } ) - - self.MenuPath = missionCommands.addSubMenu( MenuText, self.MenuParentPath ) - - self:T( { self.MenuPath } ) - - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self - end - - return self - end - - --- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept! - -- @param #MENU_MISSION self - -- @return #MENU_MISSION - function MENU_MISSION:RemoveSubMenus() - self:F( self.MenuPath ) - - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() - end - - end - - --- Removes the main menu and the sub menus recursively of this MENU_MISSION. - -- @param #MENU_MISSION self - -- @return #nil - function MENU_MISSION:Remove() - self:F( self.MenuPath ) - - self:RemoveSubMenus() - missionCommands.removeItem( self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil - end - - return nil - end - -end - -do -- MENU_MISSION_COMMAND - - --- The MENU_MISSION_COMMAND class - -- @type MENU_MISSION_COMMAND - -- @extends Core.Menu#MENU_COMMAND_BASE - MENU_MISSION_COMMAND = { - ClassName = "MENU_MISSION_COMMAND" - } - - --- MENU_MISSION constructor. Creates a new radio command item for a complete mission file, which can invoke a function with parameters. - -- @param #MENU_MISSION_COMMAND self - -- @param #string MenuText The text for the menu. - -- @param Menu#MENU_MISSION ParentMenu The parent menu. - -- @param CommandMenuFunction A function that is called when the menu key is pressed. - -- @param CommandMenuArgument An argument for the function. There can only be ONE argument given. So multiple arguments must be wrapped into a table. See the below example how to do this. - -- @return #MENU_MISSION_COMMAND self - function MENU_MISSION_COMMAND:New( MenuText, ParentMenu, CommandMenuFunction, ... ) - - local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) - - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self:T( { MenuText, CommandMenuFunction, arg } ) - - - self.MenuPath = missionCommands.addCommand( MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) - - ParentMenu.Menus[self.MenuPath] = self - - return self - end - - --- Removes a radio command item for a coalition - -- @param #MENU_MISSION_COMMAND self - -- @return #nil - function MENU_MISSION_COMMAND:Remove() - self:F( self.MenuPath ) - - missionCommands.removeItem( self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil - end - return nil - end - -end - - - -do -- MENU_COALITION - - --- The MENU_COALITION class - -- @type MENU_COALITION - -- @extends Core.Menu#MENU_BASE - -- @usage - -- -- This demo creates a menu structure for the planes within the red coalition. - -- -- To test, join the planes, then look at the other radio menus (Option F10). - -- -- Then switch planes and check if the menu is still there. - -- - -- local Plane1 = CLIENT:FindByName( "Plane 1" ) - -- local Plane2 = CLIENT:FindByName( "Plane 2" ) - -- - -- - -- -- This would create a menu for the red coalition under the main DCS "Others" menu. - -- local MenuCoalitionRed = MENU_COALITION:New( coalition.side.RED, "Manage Menus" ) - -- - -- - -- local function ShowStatus( StatusText, Coalition ) - -- - -- MESSAGE:New( Coalition, 15 ):ToRed() - -- Plane1:Message( StatusText, 15 ) - -- Plane2:Message( StatusText, 15 ) - -- end - -- - -- local MenuStatus -- Menu#MENU_COALITION - -- local MenuStatusShow -- Menu#MENU_COALITION_COMMAND - -- - -- local function RemoveStatusMenu() - -- MenuStatus:Remove() - -- end - -- - -- local function AddStatusMenu() - -- - -- -- This would create a menu for the red coalition under the MenuCoalitionRed menu object. - -- MenuStatus = MENU_COALITION:New( coalition.side.RED, "Status for Planes" ) - -- MenuStatusShow = MENU_COALITION_COMMAND:New( coalition.side.RED, "Show Status", MenuStatus, ShowStatus, "Status of planes is ok!", "Message to Red Coalition" ) - -- end - -- - -- local MenuAdd = MENU_COALITION_COMMAND:New( coalition.side.RED, "Add Status Menu", MenuCoalitionRed, AddStatusMenu ) - -- local MenuRemove = MENU_COALITION_COMMAND:New( coalition.side.RED, "Remove Status Menu", MenuCoalitionRed, RemoveStatusMenu ) - MENU_COALITION = { - ClassName = "MENU_COALITION" - } - - --- MENU_COALITION constructor. Creates a new MENU_COALITION object and creates the menu for a complete coalition. - -- @param #MENU_COALITION self - -- @param Dcs.DCSCoalition#coalition.side Coalition The coalition owning the menu. - -- @param #string MenuText The text for the menu. - -- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other). - -- @return #MENU_COALITION self - function MENU_COALITION:New( Coalition, MenuText, ParentMenu ) - - local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) - - self:F( { Coalition, MenuText, ParentMenu } ) - - self.Coalition = Coalition - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self.Menus = {} - - self:T( { MenuText } ) - - self.MenuPath = missionCommands.addSubMenuForCoalition( Coalition, MenuText, self.MenuParentPath ) - - self:T( { self.MenuPath } ) - - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self - end - - return self - end - - --- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept! - -- @param #MENU_COALITION self - -- @return #MENU_COALITION - function MENU_COALITION:RemoveSubMenus() - self:F( self.MenuPath ) - - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() - end - - end - - --- Removes the main menu and the sub menus recursively of this MENU_COALITION. - -- @param #MENU_COALITION self - -- @return #nil - function MENU_COALITION:Remove() - self:F( self.MenuPath ) - - self:RemoveSubMenus() - missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil - end - - return nil - end - -end - -do -- MENU_COALITION_COMMAND - - --- The MENU_COALITION_COMMAND class - -- @type MENU_COALITION_COMMAND - -- @extends Core.Menu#MENU_COMMAND_BASE - MENU_COALITION_COMMAND = { - ClassName = "MENU_COALITION_COMMAND" - } - - --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. - -- @param #MENU_COALITION_COMMAND self - -- @param Dcs.DCSCoalition#coalition.side Coalition The coalition owning the menu. - -- @param #string MenuText The text for the menu. - -- @param Menu#MENU_COALITION ParentMenu The parent menu. - -- @param CommandMenuFunction A function that is called when the menu key is pressed. - -- @param CommandMenuArgument An argument for the function. There can only be ONE argument given. So multiple arguments must be wrapped into a table. See the below example how to do this. - -- @return #MENU_COALITION_COMMAND - function MENU_COALITION_COMMAND:New( Coalition, MenuText, ParentMenu, CommandMenuFunction, ... ) - - local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) - - self.MenuCoalition = Coalition - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self:T( { MenuText, CommandMenuFunction, arg } ) - - - self.MenuPath = missionCommands.addCommandForCoalition( self.MenuCoalition, MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) - - ParentMenu.Menus[self.MenuPath] = self - - return self - end - - --- Removes a radio command item for a coalition - -- @param #MENU_COALITION_COMMAND self - -- @return #nil - function MENU_COALITION_COMMAND:Remove() - self:F( self.MenuPath ) - - missionCommands.removeItemForCoalition( self.MenuCoalition, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil - end - return nil - end - -end - -do -- MENU_CLIENT - - -- This local variable is used to cache the menus registered under clients. - -- Menus don't dissapear when clients are destroyed and restarted. - -- So every menu for a client created must be tracked so that program logic accidentally does not create - -- the same menus twice during initialization logic. - -- These menu classes are handling this logic with this variable. - local _MENUCLIENTS = {} - - --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. - -- @type MENU_CLIENT - -- @extends Core.Menu#MENU_BASE - -- @usage - -- -- This demo creates a menu structure for the two clients of planes. - -- -- Each client will receive a different menu structure. - -- -- To test, join the planes, then look at the other radio menus (Option F10). - -- -- Then switch planes and check if the menu is still there. - -- -- And play with the Add and Remove menu options. - -- - -- -- Note that in multi player, this will only work after the DCS clients bug is solved. - -- - -- local function ShowStatus( PlaneClient, StatusText, Coalition ) - -- - -- MESSAGE:New( Coalition, 15 ):ToRed() - -- PlaneClient:Message( StatusText, 15 ) - -- end - -- - -- local MenuStatus = {} - -- - -- local function RemoveStatusMenu( MenuClient ) - -- local MenuClientName = MenuClient:GetName() - -- MenuStatus[MenuClientName]:Remove() - -- end - -- - -- --- @param Wrapper.Client#CLIENT MenuClient - -- local function AddStatusMenu( MenuClient ) - -- local MenuClientName = MenuClient:GetName() - -- -- This would create a menu for the red coalition under the MenuCoalitionRed menu object. - -- MenuStatus[MenuClientName] = MENU_CLIENT:New( MenuClient, "Status for Planes" ) - -- MENU_CLIENT_COMMAND:New( MenuClient, "Show Status", MenuStatus[MenuClientName], ShowStatus, MenuClient, "Status of planes is ok!", "Message to Red Coalition" ) - -- end - -- - -- SCHEDULER:New( nil, - -- function() - -- local PlaneClient = CLIENT:FindByName( "Plane 1" ) - -- if PlaneClient and PlaneClient:IsAlive() then - -- local MenuManage = MENU_CLIENT:New( PlaneClient, "Manage Menus" ) - -- MENU_CLIENT_COMMAND:New( PlaneClient, "Add Status Menu Plane 1", MenuManage, AddStatusMenu, PlaneClient ) - -- MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 1", MenuManage, RemoveStatusMenu, PlaneClient ) - -- end - -- end, {}, 10, 10 ) - -- - -- SCHEDULER:New( nil, - -- function() - -- local PlaneClient = CLIENT:FindByName( "Plane 2" ) - -- if PlaneClient and PlaneClient:IsAlive() then - -- local MenuManage = MENU_CLIENT:New( PlaneClient, "Manage Menus" ) - -- MENU_CLIENT_COMMAND:New( PlaneClient, "Add Status Menu Plane 2", MenuManage, AddStatusMenu, PlaneClient ) - -- MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneClient ) - -- end - -- end, {}, 10, 10 ) - MENU_CLIENT = { - ClassName = "MENU_CLIENT" - } - - --- MENU_CLIENT constructor. Creates a new radio menu item for a client. - -- @param #MENU_CLIENT self - -- @param Wrapper.Client#CLIENT Client The Client owning the menu. - -- @param #string MenuText The text for the menu. - -- @param #table ParentMenu The parent menu. - -- @return #MENU_CLIENT self - function MENU_CLIENT:New( Client, MenuText, ParentMenu ) - - -- Arrange meta tables - local MenuParentPath = {} - if ParentMenu ~= nil then - MenuParentPath = ParentMenu.MenuPath - end - - local self = BASE:Inherit( self, MENU_BASE:New( MenuText, MenuParentPath ) ) - self:F( { Client, MenuText, ParentMenu } ) - - self.MenuClient = Client - self.MenuClientGroupID = Client:GetClientGroupID() - self.MenuParentPath = MenuParentPath - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self.Menus = {} - - if not _MENUCLIENTS[self.MenuClientGroupID] then - _MENUCLIENTS[self.MenuClientGroupID] = {} - end - - local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] - - self:T( { Client:GetClientGroupName(), MenuPath[table.concat(MenuParentPath)], MenuParentPath, MenuText } ) - - local MenuPathID = table.concat(MenuParentPath) .. "/" .. MenuText - if MenuPath[MenuPathID] then - missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] ) - end - - self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath ) - MenuPath[MenuPathID] = self.MenuPath - - self:T( { Client:GetClientGroupName(), self.MenuPath } ) - - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self - end - return self - end - - --- Removes the sub menus recursively of this @{#MENU_CLIENT}. - -- @param #MENU_CLIENT self - -- @return #MENU_CLIENT self - function MENU_CLIENT:RemoveSubMenus() - self:F( self.MenuPath ) - - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() - end - - end - - --- Removes the sub menus recursively of this MENU_CLIENT. - -- @param #MENU_CLIENT self - -- @return #nil - function MENU_CLIENT:Remove() - self:F( self.MenuPath ) - - self:RemoveSubMenus() - - if not _MENUCLIENTS[self.MenuClientGroupID] then - _MENUCLIENTS[self.MenuClientGroupID] = {} - end - - local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] - - if MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] then - MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] = nil - end - - missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), self.MenuPath ) - self.ParentMenu.Menus[self.MenuPath] = nil - return nil - end - - - --- The MENU_CLIENT_COMMAND class - -- @type MENU_CLIENT_COMMAND - -- @extends Core.Menu#MENU_COMMAND - MENU_CLIENT_COMMAND = { - ClassName = "MENU_CLIENT_COMMAND" - } - - --- MENU_CLIENT_COMMAND constructor. Creates a new radio command item for a client, which can invoke a function with parameters. - -- @param #MENU_CLIENT_COMMAND self - -- @param Wrapper.Client#CLIENT Client The Client owning the menu. - -- @param #string MenuText The text for the menu. - -- @param #MENU_BASE ParentMenu The parent menu. - -- @param CommandMenuFunction A function that is called when the menu key is pressed. - -- @return Menu#MENU_CLIENT_COMMAND self - function MENU_CLIENT_COMMAND:New( Client, MenuText, ParentMenu, CommandMenuFunction, ... ) - - -- Arrange meta tables - - local MenuParentPath = {} - if ParentMenu ~= nil then - MenuParentPath = ParentMenu.MenuPath - end - - local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, MenuParentPath, CommandMenuFunction, arg ) ) -- Menu#MENU_CLIENT_COMMAND - - self.MenuClient = Client - self.MenuClientGroupID = Client:GetClientGroupID() - self.MenuParentPath = MenuParentPath - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - if not _MENUCLIENTS[self.MenuClientGroupID] then - _MENUCLIENTS[self.MenuClientGroupID] = {} - end - - local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] - - self:T( { Client:GetClientGroupName(), MenuPath[table.concat(MenuParentPath)], MenuParentPath, MenuText, CommandMenuFunction, arg } ) - - local MenuPathID = table.concat(MenuParentPath) .. "/" .. MenuText - if MenuPath[MenuPathID] then - missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] ) - end - - self.MenuPath = missionCommands.addCommandForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath, self.MenuCallHandler, arg ) - MenuPath[MenuPathID] = self.MenuPath - - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self - end - - return self - end - - --- Removes a menu structure for a client. - -- @param #MENU_CLIENT_COMMAND self - -- @return #nil - function MENU_CLIENT_COMMAND:Remove() - self:F( self.MenuPath ) - - if not _MENUCLIENTS[self.MenuClientGroupID] then - _MENUCLIENTS[self.MenuClientGroupID] = {} - end - - local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] - - if MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] then - MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] = nil - end - - missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), self.MenuPath ) - self.ParentMenu.Menus[self.MenuPath] = nil - return nil - end -end - ---- MENU_GROUP - -do - -- This local variable is used to cache the menus registered under groups. - -- Menus don't dissapear when groups for players are destroyed and restarted. - -- So every menu for a client created must be tracked so that program logic accidentally does not create. - -- the same menus twice during initialization logic. - -- These menu classes are handling this logic with this variable. - local _MENUGROUPS = {} - - --- The MENU_GROUP class - -- @type MENU_GROUP - -- @extends Core.Menu#MENU_BASE - -- @usage - -- -- This demo creates a menu structure for the two groups of planes. - -- -- Each group will receive a different menu structure. - -- -- To test, join the planes, then look at the other radio menus (Option F10). - -- -- Then switch planes and check if the menu is still there. - -- -- And play with the Add and Remove menu options. - -- - -- -- Note that in multi player, this will only work after the DCS groups bug is solved. - -- - -- local function ShowStatus( PlaneGroup, StatusText, Coalition ) - -- - -- MESSAGE:New( Coalition, 15 ):ToRed() - -- PlaneGroup:Message( StatusText, 15 ) - -- end - -- - -- local MenuStatus = {} - -- - -- local function RemoveStatusMenu( MenuGroup ) - -- local MenuGroupName = MenuGroup:GetName() - -- MenuStatus[MenuGroupName]:Remove() - -- end - -- - -- --- @param Wrapper.Group#GROUP MenuGroup - -- local function AddStatusMenu( MenuGroup ) - -- local MenuGroupName = MenuGroup:GetName() - -- -- This would create a menu for the red coalition under the MenuCoalitionRed menu object. - -- MenuStatus[MenuGroupName] = MENU_GROUP:New( MenuGroup, "Status for Planes" ) - -- MENU_GROUP_COMMAND:New( MenuGroup, "Show Status", MenuStatus[MenuGroupName], ShowStatus, MenuGroup, "Status of planes is ok!", "Message to Red Coalition" ) - -- end - -- - -- SCHEDULER:New( nil, - -- function() - -- local PlaneGroup = GROUP:FindByName( "Plane 1" ) - -- if PlaneGroup and PlaneGroup:IsAlive() then - -- local MenuManage = MENU_GROUP:New( PlaneGroup, "Manage Menus" ) - -- MENU_GROUP_COMMAND:New( PlaneGroup, "Add Status Menu Plane 1", MenuManage, AddStatusMenu, PlaneGroup ) - -- MENU_GROUP_COMMAND:New( PlaneGroup, "Remove Status Menu Plane 1", MenuManage, RemoveStatusMenu, PlaneGroup ) - -- end - -- end, {}, 10, 10 ) - -- - -- SCHEDULER:New( nil, - -- function() - -- local PlaneGroup = GROUP:FindByName( "Plane 2" ) - -- if PlaneGroup and PlaneGroup:IsAlive() then - -- local MenuManage = MENU_GROUP:New( PlaneGroup, "Manage Menus" ) - -- MENU_GROUP_COMMAND:New( PlaneGroup, "Add Status Menu Plane 2", MenuManage, AddStatusMenu, PlaneGroup ) - -- MENU_GROUP_COMMAND:New( PlaneGroup, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneGroup ) - -- end - -- end, {}, 10, 10 ) - -- - MENU_GROUP = { - ClassName = "MENU_GROUP" - } - - --- MENU_GROUP constructor. Creates a new radio menu item for a group. - -- @param #MENU_GROUP self - -- @param Wrapper.Group#GROUP MenuGroup The Group owning the menu. - -- @param #string MenuText The text for the menu. - -- @param #table ParentMenu The parent menu. - -- @return #MENU_GROUP self - function MENU_GROUP:New( MenuGroup, MenuText, ParentMenu ) - - -- Determine if the menu was not already created and already visible at the group. - -- If it is visible, then return the cached self, otherwise, create self and cache it. - - MenuGroup._Menus = MenuGroup._Menus or {} - local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText - if MenuGroup._Menus[Path] then - self = MenuGroup._Menus[Path] - else - self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) - MenuGroup._Menus[Path] = self - - self.MenuGroup = MenuGroup - self.Path = Path - self.MenuGroupID = MenuGroup:GetID() - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self:T( { "Adding Menu ", MenuText, self.MenuParentPath } ) - self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuGroupID, MenuText, self.MenuParentPath ) - - if self.ParentMenu and self.ParentMenu.Menus then - self.ParentMenu.Menus[MenuText] = self - self:F( { self.ParentMenu.Menus, MenuText } ) - self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 - end - end - - --self:F( { MenuGroup:GetName(), MenuText, ParentMenu.MenuPath } ) - - return self - end - - --- Removes the sub menus recursively of this MENU_GROUP. - -- @param #MENU_GROUP self - -- @param MenuTime - -- @return #MENU_GROUP self - function MENU_GROUP:RemoveSubMenus( MenuTime ) - self:F2( { self.MenuPath, MenuTime, self.MenuTime } ) - - self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } ) - for MenuText, Menu in pairs( self.Menus ) do - Menu:Remove( MenuTime ) - end - - end - - - --- Removes the main menu and sub menus recursively of this MENU_GROUP. - -- @param #MENU_GROUP self - -- @param MenuTime - -- @return #nil - function MENU_GROUP:Remove( MenuTime ) - self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - - self:RemoveSubMenus( MenuTime ) - - if not MenuTime or self.MenuTime ~= MenuTime then - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] - - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuText] = nil - self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 - if self.ParentMenu.MenuCount == 0 then - if self.MenuRemoveParent == true then - self:T( "Removing Parent Menu " ) - self.ParentMenu:Remove() - end - end - end - self:T( { "Removing Group Menu:", self.MenuGroup:GetName(), self.MenuGroup._Menus[self.Path].Path } ) - self.MenuGroup._Menus[self.Path] = nil - self = nil - end - end - - return nil - end - - - --- The MENU_GROUP_COMMAND class - -- @type MENU_GROUP_COMMAND - -- @extends Core.Menu#MENU_BASE - MENU_GROUP_COMMAND = { - ClassName = "MENU_GROUP_COMMAND" - } - - --- Creates a new radio command item for a group - -- @param #MENU_GROUP_COMMAND self - -- @param Wrapper.Group#GROUP MenuGroup The Group owning the menu. - -- @param MenuText The text for the menu. - -- @param ParentMenu The parent menu. - -- @param CommandMenuFunction A function that is called when the menu key is pressed. - -- @param CommandMenuArgument An argument for the function. - -- @return #MENU_GROUP_COMMAND - function MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, CommandMenuFunction, ... ) - - MenuGroup._Menus = MenuGroup._Menus or {} - local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText - if MenuGroup._Menus[Path] then - self = MenuGroup._Menus[Path] - self:T( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } ) - else - self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) - MenuGroup._Menus[Path] = self - - self.Path = Path - self.MenuGroup = MenuGroup - self.MenuGroupID = MenuGroup:GetID() - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self:T( { "Adding Group Command Menu:", MenuGroup:GetName(), MenuText, self.MenuParentPath } ) - self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) - - if self.ParentMenu and self.ParentMenu.Menus then - self.ParentMenu.Menus[MenuText] = self - self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 - self:F( { ParentMenu.Menus, MenuText } ) - end - end - - return self - end - - --- Removes a menu structure for a group. - -- @param #MENU_GROUP_COMMAND self - -- @param MenuTime - -- @return #nil - function MENU_GROUP_COMMAND:Remove( MenuTime ) - self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - - if not MenuTime or self.MenuTime ~= MenuTime then - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] - - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - self:T( { "Removing Group Command Menu:", self.MenuGroup:GetName(), self.MenuText, self.Path, self.MenuGroup._Menus[self.Path].Path } ) - - self.ParentMenu.Menus[self.MenuText] = nil - self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 - if self.ParentMenu.MenuCount == 0 then - if self.MenuRemoveParent == true then - self:T( "Removing Parent Menu " ) - self.ParentMenu:Remove() - end - end - - self.MenuGroup._Menus[self.Path] = nil - self = nil - end - end - - return nil - end - -end - ---- **Core** - ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**. --- --- ![Banner Image](..\Presentations\ZONE\Dia1.JPG) --- --- === --- --- There are essentially two core functions that zones accomodate: --- --- * Test if an object is within the zone boundaries. --- * Provide the zone behaviour. Some zones are static, while others are moveable. --- --- The object classes are using the zone classes to test the zone boundaries, which can take various forms: --- --- * Test if completely within the zone. --- * Test if partly within the zone (for @{Group#GROUP} objects). --- * Test if not in the zone. --- * Distance to the nearest intersecting point of the zone. --- * Distance to the center of the zone. --- * ... --- --- Each of these ZONE classes have a zone name, and specific parameters defining the zone type: --- --- * @{#ZONE_BASE}: The ZONE_BASE class defining the base for all other zone classes. --- * @{#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius. --- * @{#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor. --- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Unit#UNIT} with a radius. --- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. --- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- --- === --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-02-28: ZONE\_BASE:**IsVec2InZone()** replaces ZONE\_BASE:_IsPointVec2InZone()_. --- 2017-02-28: ZONE\_BASE:**IsVec3InZone()** replaces ZONE\_BASE:_IsPointVec3InZone()_. --- 2017-02-28: ZONE\_RADIUS:**IsVec2InZone()** replaces ZONE\_RADIUS:_IsPointVec2InZone()_. --- 2017-02-28: ZONE\_RADIUS:**IsVec3InZone()** replaces ZONE\_RADIUS:_IsPointVec3InZone()_. --- 2017-02-28: ZONE\_POLYGON:**IsVec2InZone()** replaces ZONE\_POLYGON:_IsPointVec2InZone()_. --- 2017-02-28: ZONE\_POLYGON:**IsVec3InZone()** replaces ZONE\_POLYGON:_IsPointVec3InZone()_. --- --- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec2()** added. --- --- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec3()** added. --- --- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec3( inner, outer )** added. --- --- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec2( inner, outer )** added. --- --- 2016-08-15: ZONE\_BASE:**GetName()** added. --- --- 2016-08-15: ZONE\_BASE:**SetZoneProbability( ZoneProbability )** added. --- --- 2016-08-15: ZONE\_BASE:**GetZoneProbability()** added. --- --- 2016-08-15: ZONE\_BASE:**GetZoneMaybe()** added. --- --- === --- --- @module Zone - - ---- The ZONE_BASE class --- @type ZONE_BASE --- @field #string ZoneName Name of the zone. --- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. --- @extends Core.Base#BASE - - ---- # 1) ZONE_BASE class, extends @{Base#BASE} --- --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- --- ## 1.1) Each zone has a name: --- --- * @{#ZONE_BASE.GetName}(): Returns the name of the zone. --- --- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: --- --- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone. --- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone. --- --- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones: --- --- * @{#ZONE_BASE.SetRandomizeProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% ) --- * @{#ZONE_BASE.GetRandomizeProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% ) --- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate. --- --- ## 1.4) A zone manages Vectors: --- --- * @{#ZONE_BASE.GetVec2}(): Returns the @{DCSTypes#Vec2} coordinate of the zone. --- * @{#ZONE_BASE.GetRandomVec2}(): Define a random @{DCSTypes#Vec2} within the zone. --- --- ## 1.5) A zone has a bounding square: --- --- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone. --- --- ## 1.6) A zone can be marked: --- --- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color. --- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color. --- --- === --- @field #ZONE_BASE ZONE_BASE -ZONE_BASE = { - ClassName = "ZONE_BASE", - ZoneName = "", - ZoneProbability = 1, - } - - ---- The ZONE_BASE.BoundingSquare --- @type ZONE_BASE.BoundingSquare --- @field Dcs.DCSTypes#Distance x1 The lower x coordinate (left down) --- @field Dcs.DCSTypes#Distance y1 The lower y coordinate (left down) --- @field Dcs.DCSTypes#Distance x2 The higher x coordinate (right up) --- @field Dcs.DCSTypes#Distance y2 The higher y coordinate (right up) - - ---- ZONE_BASE constructor --- @param #ZONE_BASE self --- @param #string ZoneName Name of the zone. --- @return #ZONE_BASE self -function ZONE_BASE:New( ZoneName ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( ZoneName ) - - self.ZoneName = ZoneName - - return self -end - ---- Returns the name of the zone. --- @param #ZONE_BASE self --- @return #string The name of the zone. -function ZONE_BASE:GetName() - self:F2() - - return self.ZoneName -end ---- Returns if a location is within the zone. --- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. --- @return #boolean true if the location is within the zone. -function ZONE_BASE:IsVec2InZone( Vec2 ) - self:F2( Vec2 ) - - return false -end - ---- Returns if a point is within the zone. --- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. --- @return #boolean true if the point is within the zone. -function ZONE_BASE:IsVec3InZone( Vec3 ) - self:F2( Vec3 ) - - local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) - - return InZone -end - ---- Returns the @{DCSTypes#Vec2} coordinate of the zone. --- @param #ZONE_BASE self --- @return #nil. -function ZONE_BASE:GetVec2() - self:F2( self.ZoneName ) - - return nil -end - ---- Returns a @{Point#POINT_VEC2} of the zone. --- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Core.Point#POINT_VEC2 The PointVec2 of the zone. -function ZONE_BASE:GetPointVec2() - self:F2( self.ZoneName ) - - local Vec2 = self:GetVec2() - - local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) - - self:T2( { PointVec2 } ) - - return PointVec2 -end - - ---- Returns the @{DCSTypes#Vec3} of the zone. --- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Dcs.DCSTypes#Vec3 The Vec3 of the zone. -function ZONE_BASE:GetVec3( Height ) - self:F2( self.ZoneName ) - - Height = Height or 0 - - local Vec2 = self:GetVec2() - - local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } - - self:T2( { Vec3 } ) - - return Vec3 -end - ---- Returns a @{Point#POINT_VEC3} of the zone. --- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Core.Point#POINT_VEC3 The PointVec3 of the zone. -function ZONE_BASE:GetPointVec3( Height ) - self:F2( self.ZoneName ) - - local Vec3 = self:GetVec3( Height ) - - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - - self:T2( { PointVec3 } ) - - return PointVec3 -end - - ---- Define a random @{DCSTypes#Vec2} within the zone. --- @param #ZONE_BASE self --- @return Dcs.DCSTypes#Vec2 The Vec2 coordinates. -function ZONE_BASE:GetRandomVec2() - return nil -end - ---- Define a random @{Point#POINT_VEC2} within the zone. --- @param #ZONE_BASE self --- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. -function ZONE_BASE:GetRandomPointVec2() - return nil -end - ---- Define a random @{Point#POINT_VEC3} within the zone. --- @param #ZONE_BASE self --- @return Core.Point#POINT_VEC3 The PointVec3 coordinates. -function ZONE_BASE:GetRandomPointVec3() - return nil -end - ---- Get the bounding square the zone. --- @param #ZONE_BASE self --- @return #nil The bounding square. -function ZONE_BASE:GetBoundingSquare() - --return { x1 = 0, y1 = 0, x2 = 0, y2 = 0 } - return nil -end - ---- Bound the zone boundaries with a tires. --- @param #ZONE_BASE self -function ZONE_BASE:BoundZone() - self:F2() - -end - ---- Smokes the zone boundaries in a color. --- @param #ZONE_BASE self --- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. -function ZONE_BASE:SmokeZone( SmokeColor ) - self:F2( SmokeColor ) - -end - ---- Set the randomization probability of a zone to be selected. --- @param #ZONE_BASE self --- @param ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. -function ZONE_BASE:SetZoneProbability( ZoneProbability ) - self:F2( ZoneProbability ) - - self.ZoneProbability = ZoneProbability or 1 - return self -end - ---- Get the randomization probability of a zone to be selected. --- @param #ZONE_BASE self --- @return #number A value between 0 and 1. 0 = 0% and 1 = 100% probability. -function ZONE_BASE:GetZoneProbability() - self:F2() - - return self.ZoneProbability -end - ---- Get the zone taking into account the randomization probability of a zone to be selected. --- @param #ZONE_BASE self --- @return #ZONE_BASE The zone is selected taking into account the randomization probability factor. --- @return #nil The zone is not selected taking into account the randomization probability factor. -function ZONE_BASE:GetZoneMaybe() - self:F2() - - local Randomization = math.random() - if Randomization <= self.ZoneProbability then - return self - else - return nil - end -end - - ---- The ZONE_RADIUS class, defined by a zone name, a location and a radius. --- @type ZONE_RADIUS --- @field Dcs.DCSTypes#Vec2 Vec2 The current location of the zone. --- @field Dcs.DCSTypes#Distance Radius The radius of the zone. --- @extends Core.Zone#ZONE_BASE - ---- # 2) @{Zone#ZONE_RADIUS} class, extends @{Zone#ZONE_BASE} --- --- The ZONE_RADIUS class defined by a zone name, a location and a radius. --- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties. --- --- ## 2.1) @{Zone#ZONE_RADIUS} constructor --- --- * @{#ZONE_RADIUS.New}(): Constructor. --- --- ## 2.2) Manage the radius of the zone --- --- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone. --- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone. --- --- ## 2.3) Manage the location of the zone --- --- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone. --- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone. --- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter. --- --- ## 2.4) Zone point randomization --- --- Various functions exist to find random points within the zone. --- --- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone. --- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone. --- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight. --- --- === --- --- @field #ZONE_RADIUS ZONE_RADIUS --- -ZONE_RADIUS = { - ClassName="ZONE_RADIUS", - } - ---- Constructor of @{#ZONE_RADIUS}, taking the zone name, the zone location and a radius. --- @param #ZONE_RADIUS self --- @param #string ZoneName Name of the zone. --- @param Dcs.DCSTypes#Vec2 Vec2 The location of the zone. --- @param Dcs.DCSTypes#Distance Radius The radius of the zone. --- @return #ZONE_RADIUS self -function ZONE_RADIUS:New( ZoneName, Vec2, Radius ) - local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS - self:F( { ZoneName, Vec2, Radius } ) - - self.Radius = Radius - self.Vec2 = Vec2 - - return self -end - ---- Bounds the zone with tires. --- @param #ZONE_RADIUS self --- @param #number Points (optional) The amount of points in the circle. --- @param #boolean UnBound If true the tyres will be destroyed. --- @return #ZONE_RADIUS self -function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) - - local Point = {} - local Vec2 = self:GetVec2() - - Points = Points and Points or 360 - - local Angle - local RadialBase = math.pi*2 - - -- - for Angle = 0, 360, (360 / Points ) do - local Radial = Angle * RadialBase / 360 - Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() - Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() - - local CountryName = _DATABASE.COUNTRY_NAME[CountryID] - - local Tire = { - ["country"] = CountryName, - ["category"] = "Fortifications", - ["canCargo"] = false, - ["shape_name"] = "H-tyre_B_WF", - ["type"] = "Black_Tyre_WF", - --["unitId"] = Angle + 10000, - ["y"] = Point.y, - ["x"] = Point.x, - ["name"] = string.format( "%s-Tire #%0d", self:GetName(), Angle ), - ["heading"] = 0, - } -- end of ["group"] - - local Group = coalition.addStaticObject( CountryID, Tire ) - if UnBound and UnBound == true then - Group:destroy() - end - end - - return self -end - - ---- Smokes the zone boundaries in a color. --- @param #ZONE_RADIUS self --- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. --- @param #number Points (optional) The amount of points in the circle. --- @return #ZONE_RADIUS self -function ZONE_RADIUS:SmokeZone( SmokeColor, Points ) - self:F2( SmokeColor ) - - local Point = {} - local Vec2 = self:GetVec2() - - Points = Points and Points or 360 - - local Angle - local RadialBase = math.pi*2 - - for Angle = 0, 360, 360 / Points do - local Radial = Angle * RadialBase / 360 - Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() - Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() - POINT_VEC2:New( Point.x, Point.y ):Smoke( SmokeColor ) - end - - return self -end - - ---- Flares the zone boundaries in a color. --- @param #ZONE_RADIUS self --- @param Utilities.Utils#FLARECOLOR FlareColor The flare color. --- @param #number Points (optional) The amount of points in the circle. --- @param Dcs.DCSTypes#Azimuth Azimuth (optional) Azimuth The azimuth of the flare. --- @return #ZONE_RADIUS self -function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth ) - self:F2( { FlareColor, Azimuth } ) - - local Point = {} - local Vec2 = self:GetVec2() - - Points = Points and Points or 360 - - local Angle - local RadialBase = math.pi*2 - - for Angle = 0, 360, 360 / Points do - local Radial = Angle * RadialBase / 360 - Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() - Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() - POINT_VEC2:New( Point.x, Point.y ):Flare( FlareColor, Azimuth ) - end - - return self -end - ---- Returns the radius of the zone. --- @param #ZONE_RADIUS self --- @return Dcs.DCSTypes#Distance The radius of the zone. -function ZONE_RADIUS:GetRadius() - self:F2( self.ZoneName ) - - self:T2( { self.Radius } ) - - return self.Radius -end - ---- Sets the radius of the zone. --- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Distance Radius The radius of the zone. --- @return Dcs.DCSTypes#Distance The radius of the zone. -function ZONE_RADIUS:SetRadius( Radius ) - self:F2( self.ZoneName ) - - self.Radius = Radius - self:T2( { self.Radius } ) - - return self.Radius -end - ---- Returns the @{DCSTypes#Vec2} of the zone. --- @param #ZONE_RADIUS self --- @return Dcs.DCSTypes#Vec2 The location of the zone. -function ZONE_RADIUS:GetVec2() - self:F2( self.ZoneName ) - - self:T2( { self.Vec2 } ) - - return self.Vec2 -end - ---- Sets the @{DCSTypes#Vec2} of the zone. --- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Vec2 Vec2 The new location of the zone. --- @return Dcs.DCSTypes#Vec2 The new location of the zone. -function ZONE_RADIUS:SetVec2( Vec2 ) - self:F2( self.ZoneName ) - - self.Vec2 = Vec2 - - self:T2( { self.Vec2 } ) - - return self.Vec2 -end - ---- Returns the @{DCSTypes#Vec3} of the ZONE_RADIUS. --- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Dcs.DCSTypes#Vec3 The point of the zone. -function ZONE_RADIUS:GetVec3( Height ) - self:F2( { self.ZoneName, Height } ) - - Height = Height or 0 - local Vec2 = self:GetVec2() - - local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } - - self:T2( { Vec3 } ) - - return Vec3 -end - - ---- Returns if a location is within the zone. --- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. --- @return #boolean true if the location is within the zone. -function ZONE_RADIUS:IsVec2InZone( Vec2 ) - self:F2( Vec2 ) - - local ZoneVec2 = self:GetVec2() - - if ZoneVec2 then - if (( Vec2.x - ZoneVec2.x )^2 + ( Vec2.y - ZoneVec2.y ) ^2 ) ^ 0.5 <= self:GetRadius() then - return true - end - end - - return false -end - ---- Returns if a point is within the zone. --- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. --- @return #boolean true if the point is within the zone. -function ZONE_RADIUS:IsVec3InZone( Vec3 ) - self:F2( Vec3 ) - - local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) - - return InZone -end - ---- Returns a random Vec2 location within the zone. --- @param #ZONE_RADIUS self --- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. --- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. --- @return Dcs.DCSTypes#Vec2 The random location within the zone. -function ZONE_RADIUS:GetRandomVec2( inner, outer ) - self:F( self.ZoneName, inner, outer ) - - local Point = {} - local Vec2 = self:GetVec2() - local _inner = inner or 0 - local _outer = outer or self:GetRadius() - - local angle = math.random() * math.pi * 2; - Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer); - Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer); - - self:T( { Point } ) - - return Point -end - ---- Returns a @{Point#POINT_VEC2} object reflecting a random 2D location within the zone. --- @param #ZONE_RADIUS self --- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. --- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. --- @return Core.Point#POINT_VEC2 The @{Point#POINT_VEC2} object reflecting the random 3D location within the zone. -function ZONE_RADIUS:GetRandomPointVec2( inner, outer ) - self:F( self.ZoneName, inner, outer ) - - local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() ) - - self:T3( { PointVec2 } ) - - return PointVec2 -end - ---- Returns a @{Point#POINT_VEC3} object reflecting a random 3D location within the zone. --- @param #ZONE_RADIUS self --- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. --- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. --- @return Core.Point#POINT_VEC3 The @{Point#POINT_VEC3} object reflecting the random 3D location within the zone. -function ZONE_RADIUS:GetRandomPointVec3( inner, outer ) - self:F( self.ZoneName, inner, outer ) - - local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2() ) - - self:T3( { PointVec3 } ) - - return PointVec3 -end - - - --- @type ZONE --- @extends Core.Zone#ZONE_RADIUS - - ---- # 3) ZONE class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE class, defined by the zone name as defined within the Mission Editor. --- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- @field #ZONE ZONE --- -ZONE = { - ClassName="ZONE", - } - - ---- Constructor of ZONE, taking the zone name. --- @param #ZONE self --- @param #string ZoneName The name of the zone as defined within the mission editor. --- @return #ZONE -function ZONE:New( ZoneName ) - - local Zone = trigger.misc.getZone( ZoneName ) - - if not Zone then - error( "Zone " .. ZoneName .. " does not exist." ) - return nil - end - - local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, { x = Zone.point.x, y = Zone.point.z }, Zone.radius ) ) - self:F( ZoneName ) - - self.Zone = Zone - - return self -end - - ---- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. --- @type ZONE_UNIT --- @field Wrapper.Unit#UNIT ZoneUNIT --- @extends Core.Zone#ZONE_RADIUS - ---- # 4) #ZONE_UNIT class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. --- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- @field #ZONE_UNIT ZONE_UNIT --- -ZONE_UNIT = { - ClassName="ZONE_UNIT", - } - ---- Constructor to create a ZONE_UNIT instance, taking the zone name, a zone unit and a radius. --- @param #ZONE_UNIT self --- @param #string ZoneName Name of the zone. --- @param Wrapper.Unit#UNIT ZoneUNIT The unit as the center of the zone. --- @param Dcs.DCSTypes#Distance Radius The radius of the zone. --- @return #ZONE_UNIT self -function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius ) - local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneUNIT:GetVec2(), Radius ) ) - self:F( { ZoneName, ZoneUNIT:GetVec2(), Radius } ) - - self.ZoneUNIT = ZoneUNIT - self.LastVec2 = ZoneUNIT:GetVec2() - - return self -end - - ---- Returns the current location of the @{Unit#UNIT}. --- @param #ZONE_UNIT self --- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Unit#UNIT}location. -function ZONE_UNIT:GetVec2() - self:F( self.ZoneName ) - - local ZoneVec2 = self.ZoneUNIT:GetVec2() - if ZoneVec2 then - self.LastVec2 = ZoneVec2 - return ZoneVec2 - else - return self.LastVec2 - end - - self:T( { ZoneVec2 } ) - - return nil -end - ---- Returns a random location within the zone. --- @param #ZONE_UNIT self --- @return Dcs.DCSTypes#Vec2 The random location within the zone. -function ZONE_UNIT:GetRandomVec2() - self:F( self.ZoneName ) - - local RandomVec2 = {} - local Vec2 = self.ZoneUNIT:GetVec2() - - if not Vec2 then - Vec2 = self.LastVec2 - end - - local angle = math.random() * math.pi*2; - RandomVec2.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius(); - RandomVec2.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius(); - - self:T( { RandomVec2 } ) - - return RandomVec2 -end - ---- Returns the @{DCSTypes#Vec3} of the ZONE_UNIT. --- @param #ZONE_UNIT self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Dcs.DCSTypes#Vec3 The point of the zone. -function ZONE_UNIT:GetVec3( Height ) - self:F2( self.ZoneName ) - - Height = Height or 0 - - local Vec2 = self:GetVec2() - - local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } - - self:T2( { Vec3 } ) - - return Vec3 -end - ---- @type ZONE_GROUP --- @field Wrapper.Group#GROUP ZoneGROUP --- @extends Core.Zone#ZONE_RADIUS - - ---- # 5) #ZONE_GROUP class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- @field #ZONE_GROUP ZONE_GROUP --- -ZONE_GROUP = { - ClassName="ZONE_GROUP", - } - ---- Constructor to create a ZONE_GROUP instance, taking the zone name, a zone @{Group#GROUP} and a radius. --- @param #ZONE_GROUP self --- @param #string ZoneName Name of the zone. --- @param Wrapper.Group#GROUP ZoneGROUP The @{Group} as the center of the zone. --- @param Dcs.DCSTypes#Distance Radius The radius of the zone. --- @return #ZONE_GROUP self -function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius ) - local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneGROUP:GetVec2(), Radius ) ) - self:F( { ZoneName, ZoneGROUP:GetVec2(), Radius } ) - - self.ZoneGROUP = ZoneGROUP - - return self -end - - ---- Returns the current location of the @{Group}. --- @param #ZONE_GROUP self --- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Group} location. -function ZONE_GROUP:GetVec2() - self:F( self.ZoneName ) - - local ZoneVec2 = self.ZoneGROUP:GetVec2() - - self:T( { ZoneVec2 } ) - - return ZoneVec2 -end - ---- Returns a random location within the zone of the @{Group}. --- @param #ZONE_GROUP self --- @return Dcs.DCSTypes#Vec2 The random location of the zone based on the @{Group} location. -function ZONE_GROUP:GetRandomVec2() - self:F( self.ZoneName ) - - local Point = {} - local Vec2 = self.ZoneGROUP:GetVec2() - - local angle = math.random() * math.pi*2; - Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius(); - Point.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius(); - - self:T( { Point } ) - - return Point -end - - - ---- @type ZONE_POLYGON_BASE --- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCSTypes#Vec2}. --- @extends Core.Zone#ZONE_BASE - - ---- # 6) ZONE_POLYGON_BASE class, extends @{Zone#ZONE_BASE} --- --- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- --- ## 6.1) Zone point randomization --- --- Various functions exist to find random points within the zone. --- --- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone. --- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone. --- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. --- --- === --- --- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE --- -ZONE_POLYGON_BASE = { - ClassName="ZONE_POLYGON_BASE", - } - ---- A points array. --- @type ZONE_POLYGON_BASE.ListVec2 --- @list - ---- Constructor to create a ZONE_POLYGON_BASE instance, taking the zone name and an array of @{DCSTypes#Vec2}, forming a polygon. --- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected. --- @param #ZONE_POLYGON_BASE self --- @param #string ZoneName Name of the zone. --- @param #ZONE_POLYGON_BASE.ListVec2 PointsArray An array of @{DCSTypes#Vec2}, forming a polygon.. --- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:New( ZoneName, PointsArray ) - local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) - self:F( { ZoneName, PointsArray } ) - - local i = 0 - - self.Polygon = {} - - for i = 1, #PointsArray do - self.Polygon[i] = {} - self.Polygon[i].x = PointsArray[i].x - self.Polygon[i].y = PointsArray[i].y - end - - return self -end - ---- Flush polygon coordinates as a table in DCS.log. --- @param #ZONE_POLYGON_BASE self --- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:Flush() - self:F2() - - self:E( { Polygon = self.ZoneName, Coordinates = self.Polygon } ) - - return self -end - ---- Smokes the zone boundaries in a color. --- @param #ZONE_POLYGON_BASE self --- @param #boolean UnBound If true, the tyres will be destroyed. --- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:BoundZone( UnBound ) - - local i - local j - local Segments = 10 - - i = 1 - j = #self.Polygon - - while i <= #self.Polygon do - self:T( { i, j, self.Polygon[i], self.Polygon[j] } ) - - local DeltaX = self.Polygon[j].x - self.Polygon[i].x - local DeltaY = self.Polygon[j].y - self.Polygon[i].y - - for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line. - local PointX = self.Polygon[i].x + ( Segment * DeltaX / Segments ) - local PointY = self.Polygon[i].y + ( Segment * DeltaY / Segments ) - local Tire = { - ["country"] = "USA", - ["category"] = "Fortifications", - ["canCargo"] = false, - ["shape_name"] = "H-tyre_B_WF", - ["type"] = "Black_Tyre_WF", - ["y"] = PointY, - ["x"] = PointX, - ["name"] = string.format( "%s-Tire #%0d", self:GetName(), ((i - 1) * Segments) + Segment ), - ["heading"] = 0, - } -- end of ["group"] - - local Group = coalition.addStaticObject( country.id.USA, Tire ) - if UnBound and UnBound == true then - Group:destroy() - end - - end - j = i - i = i + 1 - end - - return self -end - - - ---- Smokes the zone boundaries in a color. --- @param #ZONE_POLYGON_BASE self --- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. --- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:SmokeZone( SmokeColor ) - self:F2( SmokeColor ) - - local i - local j - local Segments = 10 - - i = 1 - j = #self.Polygon - - while i <= #self.Polygon do - self:T( { i, j, self.Polygon[i], self.Polygon[j] } ) - - local DeltaX = self.Polygon[j].x - self.Polygon[i].x - local DeltaY = self.Polygon[j].y - self.Polygon[i].y - - for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line. - local PointX = self.Polygon[i].x + ( Segment * DeltaX / Segments ) - local PointY = self.Polygon[i].y + ( Segment * DeltaY / Segments ) - POINT_VEC2:New( PointX, PointY ):Smoke( SmokeColor ) - end - j = i - i = i + 1 - end - - return self -end - - - - ---- Returns if a location is within the zone. --- Source learned and taken from: https://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html --- @param #ZONE_POLYGON_BASE self --- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. --- @return #boolean true if the location is within the zone. -function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) - self:F2( Vec2 ) - - local Next - local Prev - local InPolygon = false - - Next = 1 - Prev = #self.Polygon - - while Next <= #self.Polygon do - self:T( { Next, Prev, self.Polygon[Next], self.Polygon[Prev] } ) - if ( ( ( self.Polygon[Next].y > Vec2.y ) ~= ( self.Polygon[Prev].y > Vec2.y ) ) and - ( Vec2.x < ( self.Polygon[Prev].x - self.Polygon[Next].x ) * ( Vec2.y - self.Polygon[Next].y ) / ( self.Polygon[Prev].y - self.Polygon[Next].y ) + self.Polygon[Next].x ) - ) then - InPolygon = not InPolygon - end - self:T2( { InPolygon = InPolygon } ) - Prev = Next - Next = Next + 1 - end - - self:T( { InPolygon = InPolygon } ) - return InPolygon -end - ---- Define a random @{DCSTypes#Vec2} within the zone. --- @param #ZONE_POLYGON_BASE self --- @return Dcs.DCSTypes#Vec2 The Vec2 coordinate. -function ZONE_POLYGON_BASE:GetRandomVec2() - self:F2() - - --- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way... - local Vec2Found = false - local Vec2 - local BS = self:GetBoundingSquare() - - self:T2( BS ) - - while Vec2Found == false do - Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) } - self:T2( Vec2 ) - if self:IsVec2InZone( Vec2 ) then - Vec2Found = true - end - end - - self:T2( Vec2 ) - - return Vec2 -end - ---- Return a @{Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. --- @param #ZONE_POLYGON_BASE self --- @return @{Point#POINT_VEC2} -function ZONE_POLYGON_BASE:GetRandomPointVec2() - self:F2() - - local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() ) - - self:T2( PointVec2 ) - - return PointVec2 -end - ---- Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. --- @param #ZONE_POLYGON_BASE self --- @return @{Point#POINT_VEC3} -function ZONE_POLYGON_BASE:GetRandomPointVec3() - self:F2() - - local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2() ) - - self:T2( PointVec3 ) - - return PointVec3 -end - - ---- Get the bounding square the zone. --- @param #ZONE_POLYGON_BASE self --- @return #ZONE_POLYGON_BASE.BoundingSquare The bounding square. -function ZONE_POLYGON_BASE:GetBoundingSquare() - - local x1 = self.Polygon[1].x - local y1 = self.Polygon[1].y - local x2 = self.Polygon[1].x - local y2 = self.Polygon[1].y - - for i = 2, #self.Polygon do - self:T2( { self.Polygon[i], x1, y1, x2, y2 } ) - x1 = ( x1 > self.Polygon[i].x ) and self.Polygon[i].x or x1 - x2 = ( x2 < self.Polygon[i].x ) and self.Polygon[i].x or x2 - y1 = ( y1 > self.Polygon[i].y ) and self.Polygon[i].y or y1 - y2 = ( y2 < self.Polygon[i].y ) and self.Polygon[i].y or y2 - - end - - return { x1 = x1, y1 = y1, x2 = x2, y2 = y2 } -end - - ---- @type ZONE_POLYGON --- @extends Core.Zone#ZONE_POLYGON_BASE - - ---- # 7) ZONE_POLYGON class, extends @{Zone#ZONE_POLYGON_BASE} --- --- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- @field #ZONE_POLYGON ZONE_POLYGON --- -ZONE_POLYGON = { - ClassName="ZONE_POLYGON", - } - ---- Constructor to create a ZONE_POLYGON instance, taking the zone name and the name of the @{Group#GROUP} defined within the Mission Editor. --- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON. --- @param #ZONE_POLYGON self --- @param #string ZoneName Name of the zone. --- @param Wrapper.Group#GROUP ZoneGroup The GROUP waypoints as defined within the Mission Editor define the polygon shape. --- @return #ZONE_POLYGON self -function ZONE_POLYGON:New( ZoneName, ZoneGroup ) - - local GroupPoints = ZoneGroup:GetTaskRoute() - - local self = BASE:Inherit( self, ZONE_POLYGON_BASE:New( ZoneName, GroupPoints ) ) - self:F( { ZoneName, ZoneGroup, self.Polygon } ) - - return self -end - ---- This module contains the DATABASE class, managing the database of mission objects. --- --- ==== --- --- 1) @{#DATABASE} class, extends @{Base#BASE} --- =================================================== --- Mission designers can use the DATABASE class to refer to: --- --- * UNITS --- * GROUPS --- * CLIENTS --- * AIRPORTS --- * PLAYERSJOINED --- * PLAYERS --- --- On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor. --- --- Moose will automatically create one instance of the DATABASE class into the **global** object _DATABASE. --- Moose refers to _DATABASE within the framework extensively, but you can also refer to the _DATABASE object within your missions if required. --- --- 1.1) DATABASE iterators --- ----------------------- --- You can iterate the database with the available iterator methods. --- The iterator methods will walk the DATABASE set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the DATABASE: --- --- * @{#DATABASE.ForEachUnit}: Calls a function for each @{UNIT} it finds within the DATABASE. --- * @{#DATABASE.ForEachGroup}: Calls a function for each @{GROUP} it finds within the DATABASE. --- * @{#DATABASE.ForEachPlayer}: Calls a function for each alive player it finds within the DATABASE. --- * @{#DATABASE.ForEachPlayerJoined}: Calls a function for each joined player it finds within the DATABASE. --- * @{#DATABASE.ForEachClient}: Calls a function for each @{CLIENT} it finds within the DATABASE. --- * @{#DATABASE.ForEachClientAlive}: Calls a function for each alive @{CLIENT} it finds within the DATABASE. --- --- === --- --- @module Database --- @author FlightControl - ---- DATABASE class --- @type DATABASE --- @extends Core.Base#BASE -DATABASE = { - ClassName = "DATABASE", - Templates = { - Units = {}, - Groups = {}, - ClientsByName = {}, - ClientsByID = {}, - }, - UNITS = {}, - STATICS = {}, - GROUPS = {}, - PLAYERS = {}, - PLAYERSJOINED = {}, - CLIENTS = {}, - AIRBASES = {}, - COUNTRY_ID = {}, - COUNTRY_NAME = {}, - NavPoints = {}, -} - -local _DATABASECoalition = - { - [1] = "Red", - [2] = "Blue", - } - -local _DATABASECategory = - { - ["plane"] = Unit.Category.AIRPLANE, - ["helicopter"] = Unit.Category.HELICOPTER, - ["vehicle"] = Unit.Category.GROUND_UNIT, - ["ship"] = Unit.Category.SHIP, - ["static"] = Unit.Category.STRUCTURE, - } - - ---- Creates a new DATABASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #DATABASE self --- @return #DATABASE --- @usage --- -- Define a new DATABASE Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE. --- DBObject = DATABASE:New() -function DATABASE:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) - - self:SetEventPriority( 1 ) - - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - - -- Follow alive players and clients - self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) - - self:_RegisterTemplates() - self:_RegisterGroupsAndUnits() - self:_RegisterClients() - self:_RegisterStatics() - self:_RegisterPlayers() - self:_RegisterAirbases() - - return self -end - ---- Finds a Unit based on the Unit Name. --- @param #DATABASE self --- @param #string UnitName --- @return Wrapper.Unit#UNIT The found Unit. -function DATABASE:FindUnit( UnitName ) - - local UnitFound = self.UNITS[UnitName] - return UnitFound -end - - ---- Adds a Unit based on the Unit Name in the DATABASE. --- @param #DATABASE self -function DATABASE:AddUnit( DCSUnitName ) - - if not self.UNITS[DCSUnitName] then - local UnitRegister = UNIT:Register( DCSUnitName ) - self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName ) - end - - return self.UNITS[DCSUnitName] -end - - ---- Deletes a Unit from the DATABASE based on the Unit Name. --- @param #DATABASE self -function DATABASE:DeleteUnit( DCSUnitName ) - - --self.UNITS[DCSUnitName] = nil -end - ---- Adds a Static based on the Static Name in the DATABASE. --- @param #DATABASE self -function DATABASE:AddStatic( DCSStaticName ) - - if not self.STATICS[DCSStaticName] then - self.STATICS[DCSStaticName] = STATIC:Register( DCSStaticName ) - end -end - - ---- Deletes a Static from the DATABASE based on the Static Name. --- @param #DATABASE self -function DATABASE:DeleteStatic( DCSStaticName ) - - --self.STATICS[DCSStaticName] = nil -end - ---- Finds a STATIC based on the StaticName. --- @param #DATABASE self --- @param #string StaticName --- @return Wrapper.Static#STATIC The found STATIC. -function DATABASE:FindStatic( StaticName ) - - local StaticFound = self.STATICS[StaticName] - return StaticFound -end - ---- Adds a Airbase based on the Airbase Name in the DATABASE. --- @param #DATABASE self -function DATABASE:AddAirbase( DCSAirbaseName ) - - if not self.AIRBASES[DCSAirbaseName] then - self.AIRBASES[DCSAirbaseName] = AIRBASE:Register( DCSAirbaseName ) - end -end - - ---- Deletes a Airbase from the DATABASE based on the Airbase Name. --- @param #DATABASE self -function DATABASE:DeleteAirbase( DCSAirbaseName ) - - --self.AIRBASES[DCSAirbaseName] = nil -end - ---- Finds a AIRBASE based on the AirbaseName. --- @param #DATABASE self --- @param #string AirbaseName --- @return Wrapper.Airbase#AIRBASE The found AIRBASE. -function DATABASE:FindAirbase( AirbaseName ) - - local AirbaseFound = self.AIRBASES[AirbaseName] - return AirbaseFound -end - - ---- Finds a CLIENT based on the ClientName. --- @param #DATABASE self --- @param #string ClientName --- @return Wrapper.Client#CLIENT The found CLIENT. -function DATABASE:FindClient( ClientName ) - - local ClientFound = self.CLIENTS[ClientName] - return ClientFound -end - - ---- Adds a CLIENT based on the ClientName in the DATABASE. --- @param #DATABASE self -function DATABASE:AddClient( ClientName ) - - if not self.CLIENTS[ClientName] then - self.CLIENTS[ClientName] = CLIENT:Register( ClientName ) - end - - return self.CLIENTS[ClientName] -end - - ---- Finds a GROUP based on the GroupName. --- @param #DATABASE self --- @param #string GroupName --- @return Wrapper.Group#GROUP The found GROUP. -function DATABASE:FindGroup( GroupName ) - - local GroupFound = self.GROUPS[GroupName] - return GroupFound -end - - ---- Adds a GROUP based on the GroupName in the DATABASE. --- @param #DATABASE self -function DATABASE:AddGroup( GroupName ) - - if not self.GROUPS[GroupName] then - self:E( { "Add GROUP:", GroupName } ) - self.GROUPS[GroupName] = GROUP:Register( GroupName ) - end - - return self.GROUPS[GroupName] -end - ---- Adds a player based on the Player Name in the DATABASE. --- @param #DATABASE self -function DATABASE:AddPlayer( UnitName, PlayerName ) - - if PlayerName then - self:E( { "Add player for unit:", UnitName, PlayerName } ) - self.PLAYERS[PlayerName] = self:FindUnit( UnitName ) - self.PLAYERSJOINED[PlayerName] = PlayerName - end -end - ---- Deletes a player from the DATABASE based on the Player Name. --- @param #DATABASE self -function DATABASE:DeletePlayer( PlayerName ) - - if PlayerName then - self:E( { "Clean player:", PlayerName } ) - self.PLAYERS[PlayerName] = nil - end -end - - ---- Instantiate new Groups within the DCSRTE. --- This method expects EXACTLY the same structure as a structure within the ME, and needs 2 additional fields defined: --- SpawnCountryID, SpawnCategoryID --- This method is used by the SPAWN class. --- @param #DATABASE self --- @param #table SpawnTemplate --- @return #DATABASE self -function DATABASE:Spawn( SpawnTemplate ) - self:F( SpawnTemplate.name ) - - self:T( { SpawnTemplate.SpawnCountryID, SpawnTemplate.SpawnCategoryID } ) - - -- Copy the spawn variables of the template in temporary storage, nullify, and restore the spawn variables. - local SpawnCoalitionID = SpawnTemplate.CoalitionID - local SpawnCountryID = SpawnTemplate.CountryID - local SpawnCategoryID = SpawnTemplate.CategoryID - - -- Nullify - SpawnTemplate.CoalitionID = nil - SpawnTemplate.CountryID = nil - SpawnTemplate.CategoryID = nil - - self:_RegisterTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID ) - - self:T3( SpawnTemplate ) - coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate ) - - -- Restore - SpawnTemplate.CoalitionID = SpawnCoalitionID - SpawnTemplate.CountryID = SpawnCountryID - SpawnTemplate.CategoryID = SpawnCategoryID - - local SpawnGroup = self:AddGroup( SpawnTemplate.name ) - return SpawnGroup -end - ---- Set a status to a Group within the Database, this to check crossing events for example. -function DATABASE:SetStatusGroup( GroupName, Status ) - self:F2( Status ) - - self.Templates.Groups[GroupName].Status = Status -end - ---- Get a status to a Group within the Database, this to check crossing events for example. -function DATABASE:GetStatusGroup( GroupName ) - self:F2( Status ) - - if self.Templates.Groups[GroupName] then - return self.Templates.Groups[GroupName].Status - else - return "" - end -end - ---- Private method that registers new Group Templates within the DATABASE Object. --- @param #DATABASE self --- @param #table GroupTemplate --- @return #DATABASE self -function DATABASE:_RegisterTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID ) - - local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name) - - local TraceTable = {} - - if not self.Templates.Groups[GroupTemplateName] then - self.Templates.Groups[GroupTemplateName] = {} - self.Templates.Groups[GroupTemplateName].Status = nil - end - - -- Delete the spans from the route, it is not needed and takes memory. - if GroupTemplate.route and GroupTemplate.route.spans then - GroupTemplate.route.spans = nil - end - - GroupTemplate.CategoryID = CategoryID - GroupTemplate.CoalitionID = CoalitionID - GroupTemplate.CountryID = CountryID - - self.Templates.Groups[GroupTemplateName].GroupName = GroupTemplateName - self.Templates.Groups[GroupTemplateName].Template = GroupTemplate - self.Templates.Groups[GroupTemplateName].groupId = GroupTemplate.groupId - self.Templates.Groups[GroupTemplateName].UnitCount = #GroupTemplate.units - self.Templates.Groups[GroupTemplateName].Units = GroupTemplate.units - self.Templates.Groups[GroupTemplateName].CategoryID = CategoryID - self.Templates.Groups[GroupTemplateName].CoalitionID = CoalitionID - self.Templates.Groups[GroupTemplateName].CountryID = CountryID - - - TraceTable[#TraceTable+1] = "Group" - TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].GroupName - - TraceTable[#TraceTable+1] = "Coalition" - TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CoalitionID - TraceTable[#TraceTable+1] = "Category" - TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CategoryID - TraceTable[#TraceTable+1] = "Country" - TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CountryID - - TraceTable[#TraceTable+1] = "Units" - - for unit_num, UnitTemplate in pairs( GroupTemplate.units ) do - - UnitTemplate.name = env.getValueDictByKey(UnitTemplate.name) - - self.Templates.Units[UnitTemplate.name] = {} - self.Templates.Units[UnitTemplate.name].UnitName = UnitTemplate.name - self.Templates.Units[UnitTemplate.name].Template = UnitTemplate - self.Templates.Units[UnitTemplate.name].GroupName = GroupTemplateName - self.Templates.Units[UnitTemplate.name].GroupTemplate = GroupTemplate - self.Templates.Units[UnitTemplate.name].GroupId = GroupTemplate.groupId - self.Templates.Units[UnitTemplate.name].CategoryID = CategoryID - self.Templates.Units[UnitTemplate.name].CoalitionID = CoalitionID - self.Templates.Units[UnitTemplate.name].CountryID = CountryID - - if UnitTemplate.skill and (UnitTemplate.skill == "Client" or UnitTemplate.skill == "Player") then - self.Templates.ClientsByName[UnitTemplate.name] = UnitTemplate - self.Templates.ClientsByName[UnitTemplate.name].CategoryID = CategoryID - self.Templates.ClientsByName[UnitTemplate.name].CoalitionID = CoalitionID - self.Templates.ClientsByName[UnitTemplate.name].CountryID = CountryID - self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate - end - - TraceTable[#TraceTable+1] = self.Templates.Units[UnitTemplate.name].UnitName - end - - self:E( TraceTable ) -end - -function DATABASE:GetGroupTemplate( GroupName ) - local GroupTemplate = self.Templates.Groups[GroupName].Template - GroupTemplate.SpawnCoalitionID = self.Templates.Groups[GroupName].CoalitionID - GroupTemplate.SpawnCategoryID = self.Templates.Groups[GroupName].CategoryID - GroupTemplate.SpawnCountryID = self.Templates.Groups[GroupName].CountryID - return GroupTemplate -end - -function DATABASE:GetGroupNameFromUnitName( UnitName ) - return self.Templates.Units[UnitName].GroupName -end - -function DATABASE:GetGroupTemplateFromUnitName( UnitName ) - return self.Templates.Units[UnitName].GroupTemplate -end - -function DATABASE:GetCoalitionFromClientTemplate( ClientName ) - return self.Templates.ClientsByName[ClientName].CoalitionID -end - -function DATABASE:GetCategoryFromClientTemplate( ClientName ) - return self.Templates.ClientsByName[ClientName].CategoryID -end - -function DATABASE:GetCountryFromClientTemplate( ClientName ) - return self.Templates.ClientsByName[ClientName].CountryID -end - ---- Airbase - -function DATABASE:GetCoalitionFromAirbase( AirbaseName ) - return self.AIRBASES[AirbaseName]:GetCoalition() -end - -function DATABASE:GetCategoryFromAirbase( AirbaseName ) - return self.AIRBASES[AirbaseName]:GetCategory() -end - - - ---- Private method that registers all alive players in the mission. --- @param #DATABASE self --- @return #DATABASE self -function DATABASE:_RegisterPlayers() - - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - for UnitId, UnitData in pairs( CoalitionData ) do - self:T3( { "UnitData:", UnitData } ) - if UnitData and UnitData:isExist() then - local UnitName = UnitData:getName() - local PlayerName = UnitData:getPlayerName() - if not self.PLAYERS[PlayerName] then - self:E( { "Add player for unit:", UnitName, PlayerName } ) - self:AddPlayer( UnitName, PlayerName ) - end - end - end - end - - return self -end - - ---- Private method that registers all Groups and Units within in the mission. --- @param #DATABASE self --- @return #DATABASE self -function DATABASE:_RegisterGroupsAndUnits() - - local CoalitionsData = { GroupsRed = coalition.getGroups( coalition.side.RED ), GroupsBlue = coalition.getGroups( coalition.side.BLUE ) } - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - for DCSGroupId, DCSGroup in pairs( CoalitionData ) do - - if DCSGroup:isExist() then - local DCSGroupName = DCSGroup:getName() - - self:E( { "Register Group:", DCSGroupName } ) - self:AddGroup( DCSGroupName ) - - for DCSUnitId, DCSUnit in pairs( DCSGroup:getUnits() ) do - - local DCSUnitName = DCSUnit:getName() - self:E( { "Register Unit:", DCSUnitName } ) - self:AddUnit( DCSUnitName ) - end - else - self:E( { "Group does not exist: ", DCSGroup } ) - end - - end - end - - return self -end - ---- Private method that registers all Units of skill Client or Player within in the mission. --- @param #DATABASE self --- @return #DATABASE self -function DATABASE:_RegisterClients() - - for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do - self:E( { "Register Client:", ClientName } ) - self:AddClient( ClientName ) - end - - return self -end - ---- @param #DATABASE self -function DATABASE:_RegisterStatics() - - local CoalitionsData = { GroupsRed = coalition.getStaticObjects( coalition.side.RED ), GroupsBlue = coalition.getStaticObjects( coalition.side.BLUE ) } - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - for DCSStaticId, DCSStatic in pairs( CoalitionData ) do - - if DCSStatic:isExist() then - local DCSStaticName = DCSStatic:getName() - - self:E( { "Register Static:", DCSStaticName } ) - self:AddStatic( DCSStaticName ) - else - self:E( { "Static does not exist: ", DCSStatic } ) - end - end - end - - return self -end - ---- @param #DATABASE self -function DATABASE:_RegisterAirbases() - - local CoalitionsData = { AirbasesRed = coalition.getAirbases( coalition.side.RED ), AirbasesBlue = coalition.getAirbases( coalition.side.BLUE ), AirbasesNeutral = coalition.getAirbases( coalition.side.NEUTRAL ) } - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - for DCSAirbaseId, DCSAirbase in pairs( CoalitionData ) do - - local DCSAirbaseName = DCSAirbase:getName() - - self:E( { "Register Airbase:", DCSAirbaseName } ) - self:AddAirbase( DCSAirbaseName ) - end - end - - return self -end - - ---- Events - ---- Handles the OnBirth event for the alive units set. --- @param #DATABASE self --- @param Core.Event#EVENTDATA Event -function DATABASE:_EventOnBirth( Event ) - self:F2( { Event } ) - - if Event.IniDCSUnit then - if Event.IniObjectCategory == 3 then - self:AddStatic( Event.IniDCSUnitName ) - else - if Event.IniObjectCategory == 1 then - self:AddUnit( Event.IniDCSUnitName ) - self:AddGroup( Event.IniDCSGroupName ) - end - end - self:_EventOnPlayerEnterUnit( Event ) - end -end - - ---- Handles the OnDead or OnCrash event for alive units set. --- @param #DATABASE self --- @param Core.Event#EVENTDATA Event -function DATABASE:_EventOnDeadOrCrash( Event ) - self:F2( { Event } ) - - if Event.IniDCSUnit then - if Event.IniObjectCategory == 3 then - if self.STATICS[Event.IniDCSUnitName] then - self:DeleteStatic( Event.IniDCSUnitName ) - end - else - if Event.IniObjectCategory == 1 then - if self.UNITS[Event.IniDCSUnitName] then - self:DeleteUnit( Event.IniDCSUnitName ) - end - end - end - end -end - - ---- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied). --- @param #DATABASE self --- @param Core.Event#EVENTDATA Event -function DATABASE:_EventOnPlayerEnterUnit( Event ) - self:F2( { Event } ) - - if Event.IniUnit then - if Event.IniObjectCategory == 1 then - self:AddUnit( Event.IniDCSUnitName ) - self:AddGroup( Event.IniDCSGroupName ) - local PlayerName = Event.IniUnit:GetPlayerName() - if not self.PLAYERS[PlayerName] then - self:AddPlayer( Event.IniUnitName, PlayerName ) - end - end - end -end - - ---- Handles the OnPlayerLeaveUnit event to clean the active players table. --- @param #DATABASE self --- @param Core.Event#EVENTDATA Event -function DATABASE:_EventOnPlayerLeaveUnit( Event ) - self:F2( { Event } ) - - if Event.IniUnit then - if Event.IniObjectCategory == 1 then - local PlayerName = Event.IniUnit:GetPlayerName() - if self.PLAYERS[PlayerName] then - self:DeletePlayer( PlayerName ) - end - end - end -end - ---- Iterators - ---- Iterate the DATABASE and call an iterator function for the given set, providing the Object for each element within the set and optional parameters. --- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive player in the database. --- @return #DATABASE self -function DATABASE:ForEach( IteratorFunction, FinalizeFunction, arg, Set ) - self:F2( arg ) - - local function CoRoutine() - local Count = 0 - for ObjectID, Object in pairs( Set ) do - self:T2( Object ) - IteratorFunction( Object, unpack( arg ) ) - Count = Count + 1 --- if Count % 100 == 0 then --- coroutine.yield( false ) --- end - end - return true - end - --- local co = coroutine.create( CoRoutine ) - local co = CoRoutine - - local function Schedule() - --- local status, res = coroutine.resume( co ) - local status, res = co() - self:T3( { status, res } ) - - if status == false then - error( res ) - end - if res == false then - return true -- resume next time the loop - end - if FinalizeFunction then - FinalizeFunction( unpack( arg ) ) - end - return false - end - - local Scheduler = SCHEDULER:New( self, Schedule, {}, 0.001, 0.001, 0 ) - - return self -end - - ---- Iterate the DATABASE and call an iterator function for each **alive** UNIT, providing the UNIT and optional parameters. --- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the database. The function needs to accept a UNIT parameter. --- @return #DATABASE self -function DATABASE:ForEachUnit( IteratorFunction, FinalizeFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, FinalizeFunction, arg, self.UNITS ) - - return self -end - ---- Iterate the DATABASE and call an iterator function for each **alive** GROUP, providing the GROUP and optional parameters. --- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the database. The function needs to accept a GROUP parameter. --- @return #DATABASE self -function DATABASE:ForEachGroup( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.GROUPS ) - - return self -end - - ---- Iterate the DATABASE and call an iterator function for each **ALIVE** player, providing the player name and optional parameters. --- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an player in the database. The function needs to accept the player name. --- @return #DATABASE self -function DATABASE:ForEachPlayer( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.PLAYERS ) - - return self -end - - ---- Iterate the DATABASE and call an iterator function for each player who has joined the mission, providing the Unit of the player and optional parameters. --- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is was a player in the database. The function needs to accept a UNIT parameter. --- @return #DATABASE self -function DATABASE:ForEachPlayerJoined( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.PLAYERSJOINED ) - - return self -end - ---- Iterate the DATABASE and call an iterator function for each CLIENT, providing the CLIENT to the function and optional parameters. --- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive player in the database. The function needs to accept a CLIENT parameter. --- @return #DATABASE self -function DATABASE:ForEachClient( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.CLIENTS ) - - return self -end - - -function DATABASE:_RegisterTemplates() - self:F2() - - self.Navpoints = {} - self.UNITS = {} - --Build routines.db.units and self.Navpoints - for CoalitionName, coa_data in pairs(env.mission.coalition) do - - if (CoalitionName == 'red' or CoalitionName == 'blue') and type(coa_data) == 'table' then - --self.Units[coa_name] = {} - - local CoalitionSide = coalition.side[string.upper(CoalitionName)] - - ---------------------------------------------- - -- build nav points DB - self.Navpoints[CoalitionName] = {} - if coa_data.nav_points then --navpoints - for nav_ind, nav_data in pairs(coa_data.nav_points) do - - if type(nav_data) == 'table' then - self.Navpoints[CoalitionName][nav_ind] = routines.utils.deepCopy(nav_data) - - self.Navpoints[CoalitionName][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. - self.Navpoints[CoalitionName][nav_ind]['point'] = {} -- point is used by SSE, support it. - self.Navpoints[CoalitionName][nav_ind]['point']['x'] = nav_data.x - self.Navpoints[CoalitionName][nav_ind]['point']['y'] = 0 - self.Navpoints[CoalitionName][nav_ind]['point']['z'] = nav_data.y - end - end - end - ------------------------------------------------- - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - - local CountryName = string.upper(cntry_data.name) - local CountryID = cntry_data.id - - self.COUNTRY_ID[CountryName] = CountryID - self.COUNTRY_NAME[CountryID] = CountryName - - --self.Units[coa_name][countryName] = {} - --self.Units[coa_name][countryName]["countryId"] = cntry_data.id - - if type(cntry_data) == 'table' then --just making sure - - for obj_type_name, obj_type_data in pairs(cntry_data) do - - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then --should be an unncessary check - - local CategoryName = obj_type_name - - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - - --self.Units[coa_name][countryName][category] = {} - - for group_num, GroupTemplate in pairs(obj_type_data.group) do - - if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group - self:_RegisterTemplate( - GroupTemplate, - CoalitionSide, - _DATABASECategory[string.lower(CategoryName)], - CountryID - ) - end --if GroupTemplate and GroupTemplate.units then - end --for group_num, GroupTemplate in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do - end --if type(cntry_data) == 'table' then - end --for cntry_id, cntry_data in pairs(coa_data.country) do - end --if coa_data.country then --there is a country table - end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then - end --for coa_name, coa_data in pairs(mission.coalition) do - - return self -end - - - - ---- **Core** - SET_ classes define **collections** of objects to perform **bulk actions** and logically **group** objects. --- --- ![Banner Image](..\Presentations\SET\Dia1.JPG) --- --- === --- --- SET_ classes group objects of the same type into a collection, which is either: --- --- * Manually managed using the **:Add...()** or **:Remove...()** methods. The initial SET can be filtered with the **@{#SET_BASE.FilterOnce}()** method --- * Dynamically updated when new objects are created or objects are destroyed using the **@{#SET_BASE.FilterStart}()** method. --- --- Various types of SET_ classes are available: --- --- * @{#SET_UNIT}: Defines a colleciton of @{Unit}s filtered by filter criteria. --- * @{#SET_GROUP}: Defines a collection of @{Group}s filtered by filter criteria. --- * @{#SET_CLIENT}: Defines a collection of @{Client}s filterd by filter criteria. --- * @{#SET_AIRBASE}: Defines a collection of @{Airbase}s filtered by filter criteria. --- --- These classes are derived from @{#SET_BASE}, which contains the main methods to manage SETs. --- --- A multitude of other methods are available in SET_ classes that allow to: --- --- * Validate the presence of objects in the SET. --- * Trigger events when objects in the SET change a zone presence. --- --- ### Authors: --- --- * FlightControl : Design & Programming --- --- ### Contributions: --- --- --- @module Set - - ---- @type SET_BASE --- @field #table Filter --- @field #table Set --- @field #table List --- @field Core.Scheduler#SCHEDULER CallScheduler --- @extends Core.Base#BASE - - ---- # 1) SET_BASE class, extends @{Base#BASE} --- The @{Set#SET_BASE} class defines the core functions that define a collection of objects. --- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. --- In this way, large loops can be done while not blocking the simulator main processing loop. --- The default **"yield interval"** is after 10 objects processed. --- The default **"time interval"** is after 0.001 seconds. --- --- ## 1.1) Add or remove objects from the SET --- --- Some key core functions are @{Set#SET_BASE.Add} and @{Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. --- --- ## 1.2) Define the SET iterator **"yield interval"** and the **"time interval"** --- --- Modify the iterator intervals with the @{Set#SET_BASE.SetInteratorIntervals} method. --- You can set the **"yield interval"**, and the **"time interval"**. (See above). --- --- @field #SET_BASE SET_BASE -SET_BASE = { - ClassName = "SET_BASE", - Filter = {}, - Set = {}, - List = {}, - Index = {}, -} - - ---- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #SET_BASE self --- @return #SET_BASE --- @usage --- -- Define a new SET_BASE Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE. --- DBObject = SET_BASE:New() -function SET_BASE:New( Database ) - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) -- Core.Set#SET_BASE - - self.Database = Database - - self.YieldInterval = 10 - self.TimeInterval = 0.001 - - self.Set = {} - - self.List = {} - self.List.__index = self.List - self.List = setmetatable( { Count = 0 }, self.List ) - - self.Index = {} - - self.CallScheduler = SCHEDULER:New( self ) - - self:SetEventPriority( 2 ) - - return self -end - ---- Finds an @{Base#BASE} object based on the object Name. --- @param #SET_BASE self --- @param #string ObjectName --- @return Core.Base#BASE The Object found. -function SET_BASE:_Find( ObjectName ) - - local ObjectFound = self.Set[ObjectName] - return ObjectFound -end - - ---- Gets the Set. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:GetSet() - self:F2() - - return self.Set -end - ---- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using a given ObjectName as the index. --- @param #SET_BASE self --- @param #string ObjectName --- @param Core.Base#BASE Object --- @return Core.Base#BASE The added BASE Object. -function SET_BASE:Add( ObjectName, Object ) - self:F2( ObjectName ) - - local t = { _ = Object } - - if self.List.last then - self.List.last._next = t - t._prev = self.List.last - self.List.last = t - else - -- this is the first node - self.List.first = t - self.List.last = t - end - - self.List.Count = self.List.Count + 1 - - self.Set[ObjectName] = t._ - - table.insert( self.Index, ObjectName ) - -end - ---- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index. --- @param #SET_BASE self --- @param Wrapper.Object#OBJECT Object --- @return Core.Base#BASE The added BASE Object. -function SET_BASE:AddObject( Object ) - self:F2( Object.ObjectName ) - - self:T( Object.UnitName ) - self:T( Object.ObjectName ) - self:Add( Object.ObjectName, Object ) - -end - - - ---- Removes a @{Base#BASE} object from the @{Set#SET_BASE} and derived classes, based on the Object Name. --- @param #SET_BASE self --- @param #string ObjectName -function SET_BASE:Remove( ObjectName ) - self:F( ObjectName ) - - local t = self.Set[ObjectName] - - self:E( { ObjectName, t } ) - - if t then - if t._next then - if t._prev then - t._next._prev = t._prev - t._prev._next = t._next - else - -- this was the first node - t._next._prev = nil - self.List._first = t._next - end - elseif t._prev then - -- this was the last node - t._prev._next = nil - self.List._last = t._prev - else - -- this was the only node - self.List._first = nil - self.List._last = nil - end - - t._next = nil - t._prev = nil - self.List.Count = self.List.Count - 1 - - for Index, Key in ipairs( self.Index ) do - if Key == ObjectName then - table.remove( self.Index, Index ) - break - end - end - - self.Set[ObjectName] = nil - - end - -end - ---- Gets a @{Base#BASE} object from the @{Set#SET_BASE} and derived classes, based on the Object Name. --- @param #SET_BASE self --- @param #string ObjectName --- @return Core.Base#BASE -function SET_BASE:Get( ObjectName ) - self:F( ObjectName ) - - local t = self.Set[ObjectName] - - self:T3( { ObjectName, t } ) - - return t - -end - ---- Gets the first object from the @{Set#SET_BASE} and derived classes. --- @param #SET_BASE self --- @return Core.Base#BASE -function SET_BASE:GetFirst() - self:F() - - local ObjectName = self.Index[1] - local FirstObject = self.Set[ObjectName] - self:T3( { FirstObject } ) - return FirstObject -end - ---- Gets the last object from the @{Set#SET_BASE} and derived classes. --- @param #SET_BASE self --- @return Core.Base#BASE -function SET_BASE:GetLast() - self:F() - - local ObjectName = self.Index[#self.Index] - local LastObject = self.Set[ObjectName] - self:T3( { LastObject } ) - return LastObject -end - ---- Gets a random object from the @{Set#SET_BASE} and derived classes. --- @param #SET_BASE self --- @return Core.Base#BASE -function SET_BASE:GetRandom() - self:F() - - local RandomItem = self.Set[self.Index[math.random(#self.Index)]] - - self:T3( { RandomItem } ) - - return RandomItem -end - - ---- Retrieves the amount of objects in the @{Set#SET_BASE} and derived classes. --- @param #SET_BASE self --- @return #number Count -function SET_BASE:Count() - - return #self.Index or 0 -end - - - ---- Copies the Filter criteria from a given Set (for rebuilding a new Set based on an existing Set). --- @param #SET_BASE self --- @param #SET_BASE BaseSet --- @return #SET_BASE -function SET_BASE:SetDatabase( BaseSet ) - - -- Copy the filter criteria of the BaseSet - local OtherFilter = routines.utils.deepCopy( BaseSet.Filter ) - self.Filter = OtherFilter - - -- Now base the new Set on the BaseSet - self.Database = BaseSet:GetSet() - return self -end - - - ---- Define the SET iterator **"yield interval"** and the **"time interval"**. --- @param #SET_BASE self --- @param #number YieldInterval Sets the frequency when the iterator loop will yield after the number of objects processed. The default frequency is 10 objects processed. --- @param #number TimeInterval Sets the time in seconds when the main logic will resume the iterator loop. The default time is 0.001 seconds. --- @return #SET_BASE self -function SET_BASE:SetIteratorIntervals( YieldInterval, TimeInterval ) - - self.YieldInterval = YieldInterval - self.TimeInterval = TimeInterval - - return self -end - - ---- Filters for the defined collection. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:FilterOnce() - - for ObjectName, Object in pairs( self.Database ) do - - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) - end - end - - return self -end - ---- Starts the filtering for the defined collection. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:_FilterStart() - - for ObjectName, Object in pairs( self.Database ) do - - if self:IsIncludeObject( Object ) then - self:E( { "Adding Object:", ObjectName } ) - self:Add( ObjectName, Object ) - end - end - - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - - -- Follow alive players and clients - self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) - - - return self -end - ---- Stops the filtering for the defined collection. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:FilterStop() - - self:UnHandleEvent( EVENTS.Birth ) - self:UnHandleEvent( EVENTS.Dead ) - self:UnHandleEvent( EVENTS.Crash ) - - return self -end - ---- Iterate the SET_BASE while identifying the nearest object from a @{Point#POINT_VEC2}. --- @param #SET_BASE self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest object in the set. --- @return Core.Base#BASE The closest object. -function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 ) - self:F2( PointVec2 ) - - local NearestObject = nil - local ClosestDistance = nil - - for ObjectID, ObjectData in pairs( self.Set ) do - if NearestObject == nil then - NearestObject = ObjectData - ClosestDistance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) - else - local Distance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) - if Distance < ClosestDistance then - NearestObject = ObjectData - ClosestDistance = Distance - end - end - end - - return NearestObject -end - - - ------ Private method that registers all alive players in the mission. ----- @param #SET_BASE self ----- @return #SET_BASE self ---function SET_BASE:_RegisterPlayers() --- --- local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } --- for CoalitionId, CoalitionData in pairs( CoalitionsData ) do --- for UnitId, UnitData in pairs( CoalitionData ) do --- self:T3( { "UnitData:", UnitData } ) --- if UnitData and UnitData:isExist() then --- local UnitName = UnitData:getName() --- if not self.PlayersAlive[UnitName] then --- self:E( { "Add player for unit:", UnitName, UnitData:getPlayerName() } ) --- self.PlayersAlive[UnitName] = UnitData:getPlayerName() --- end --- end --- end --- end --- --- return self ---end - ---- Events - ---- Handles the OnBirth event for the Set. --- @param #SET_BASE self --- @param Core.Event#EVENTDATA Event -function SET_BASE:_EventOnBirth( Event ) - self:F3( { Event } ) - - if Event.IniDCSUnit then - local ObjectName, Object = self:AddInDatabase( Event ) - self:T3( ObjectName, Object ) - if Object and self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) - --self:_EventOnPlayerEnterUnit( Event ) - end - end -end - ---- Handles the OnDead or OnCrash event for alive units set. --- @param #SET_BASE self --- @param Core.Event#EVENTDATA Event -function SET_BASE:_EventOnDeadOrCrash( Event ) - self:F3( { Event } ) - - if Event.IniDCSUnit then - local ObjectName, Object = self:FindInDatabase( Event ) - if ObjectName and Object ~= nil then - self:Remove( ObjectName ) - end - end -end - ---- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied). --- @param #SET_BASE self --- @param Core.Event#EVENTDATA Event -function SET_BASE:_EventOnPlayerEnterUnit( Event ) - self:F3( { Event } ) - - if Event.IniDCSUnit then - local ObjectName, Object = self:AddInDatabase( Event ) - self:T3( ObjectName, Object ) - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) - --self:_EventOnPlayerEnterUnit( Event ) - end - end -end - ---- Handles the OnPlayerLeaveUnit event to clean the active players table. --- @param #SET_BASE self --- @param Core.Event#EVENTDATA Event -function SET_BASE:_EventOnPlayerLeaveUnit( Event ) - self:F3( { Event } ) - - local ObjectName = Event.IniDCSUnit - if Event.IniDCSUnit then - if Event.IniDCSGroup then - local GroupUnits = Event.IniDCSGroup:getUnits() - local PlayerCount = 0 - for _, DCSUnit in pairs( GroupUnits ) do - if DCSUnit ~= Event.IniDCSUnit then - if DCSUnit:getPlayer() ~= nil then - PlayerCount = PlayerCount + 1 - end - end - end - self:E(PlayerCount) - if PlayerCount == 0 then - self:Remove( Event.IniDCSGroupName ) - end - end - end -end - --- Iterators - ---- Iterate the SET_BASE and derived classes and call an iterator function for the given SET_BASE, providing the Object for each element within the set and optional parameters. --- @param #SET_BASE self --- @param #function IteratorFunction The function that will be called. --- @return #SET_BASE self -function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArguments ) - self:F3( arg ) - - Set = Set or self:GetSet() - arg = arg or {} - - local function CoRoutine() - local Count = 0 - for ObjectID, ObjectData in pairs( Set ) do - local Object = ObjectData - self:T3( Object ) - if Function then - if Function( unpack( FunctionArguments ), Object ) == true then - IteratorFunction( Object, unpack( arg ) ) - end - else - IteratorFunction( Object, unpack( arg ) ) - end - Count = Count + 1 --- if Count % self.YieldInterval == 0 then --- coroutine.yield( false ) --- end - end - return true - end - --- local co = coroutine.create( CoRoutine ) - local co = CoRoutine - - local function Schedule() - --- local status, res = coroutine.resume( co ) - local status, res = co() - self:T3( { status, res } ) - - if status == false then - error( res ) - end - if res == false then - return true -- resume next time the loop - end - - return false - end - - --self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) - Schedule() - - return self -end - - ------ Iterate the SET_BASE and call an interator function for each **alive** unit, providing the Unit and optional parameters. ----- @param #SET_BASE self ----- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter. ----- @return #SET_BASE self ---function SET_BASE:ForEachDCSUnitAlive( IteratorFunction, ... ) --- self:F3( arg ) --- --- self:ForEach( IteratorFunction, arg, self.DCSUnitsAlive ) --- --- return self ---end --- ------ Iterate the SET_BASE and call an interator function for each **alive** player, providing the Unit of the player and optional parameters. ----- @param #SET_BASE self ----- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter. ----- @return #SET_BASE self ---function SET_BASE:ForEachPlayer( IteratorFunction, ... ) --- self:F3( arg ) --- --- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) --- --- return self ---end --- --- ------ Iterate the SET_BASE and call an interator function for each client, providing the Client to the function and optional parameters. ----- @param #SET_BASE self ----- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter. ----- @return #SET_BASE self ---function SET_BASE:ForEachClient( IteratorFunction, ... ) --- self:F3( arg ) --- --- self:ForEach( IteratorFunction, arg, self.Clients ) --- --- return self ---end - - ---- Decides whether to include the Object --- @param #SET_BASE self --- @param #table Object --- @return #SET_BASE self -function SET_BASE:IsIncludeObject( Object ) - self:F3( Object ) - - return true -end - ---- Flushes the current SET_BASE contents in the log ... (for debugging reasons). --- @param #SET_BASE self --- @return #string A string with the names of the objects. -function SET_BASE:Flush() - self:F3() - - local ObjectNames = "" - for ObjectName, Object in pairs( self.Set ) do - ObjectNames = ObjectNames .. ObjectName .. ", " - end - self:E( { "Objects in Set:", ObjectNames } ) - - return ObjectNames -end - - ---- @type SET_GROUP --- @extends Core.Set#SET_BASE - ---- # 2) SET_GROUP class, extends @{Set#SET_BASE} --- --- Mission designers can use the @{Set#SET_GROUP} class to build sets of groups belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Starting with certain prefix strings. --- --- ## 2.1) SET_GROUP constructor --- --- Create a new SET_GROUP object with the @{#SET_GROUP.New} method: --- --- * @{#SET_GROUP.New}: Creates a new SET_GROUP object. --- --- ## 2.2) Add or Remove GROUP(s) from SET_GROUP --- --- GROUPS can be added and removed using the @{Set#SET_GROUP.AddGroupsByName} and @{Set#SET_GROUP.RemoveGroupsByName} respectively. --- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. --- --- ## 2.3) SET_GROUP filter criteria --- --- You can set filter criteria to define the set of groups within the SET_GROUP. --- Filter criteria are defined by: --- --- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s). --- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). --- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies). --- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s). --- --- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: --- --- * @{#SET_GROUP.FilterStart}: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects **dynamically**. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Zone#ZONE}. --- --- ## 2.4) SET_GROUP iterators --- --- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. --- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_GROUP: --- --- * @{#SET_GROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_GROUP. --- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupPartlyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- --- === --- @field #SET_GROUP SET_GROUP -SET_GROUP = { - ClassName = "SET_GROUP", - Filter = { - Coalitions = nil, - Categories = nil, - Countries = nil, - GroupPrefixes = nil, - }, - FilterMeta = { - Coalitions = { - red = coalition.side.RED, - blue = coalition.side.BLUE, - neutral = coalition.side.NEUTRAL, - }, - Categories = { - plane = Group.Category.AIRPLANE, - helicopter = Group.Category.HELICOPTER, - ground = Group.Category.GROUND_UNIT, - ship = Group.Category.SHIP, - structure = Group.Category.STRUCTURE, - }, - }, -} - - ---- Creates a new SET_GROUP object, building a set of groups belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #SET_GROUP self --- @return #SET_GROUP --- @usage --- -- Define a new SET_GROUP Object. This DBObject will contain a reference to all alive GROUPS. --- DBObject = SET_GROUP:New() -function SET_GROUP:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.GROUPS ) ) - - return self -end - ---- Add GROUP(s) to SET_GROUP. --- @param Core.Set#SET_GROUP self --- @param #string AddGroupNames A single name or an array of GROUP names. --- @return self -function SET_GROUP:AddGroupsByName( AddGroupNames ) - - local AddGroupNamesArray = ( type( AddGroupNames ) == "table" ) and AddGroupNames or { AddGroupNames } - - for AddGroupID, AddGroupName in pairs( AddGroupNamesArray ) do - self:Add( AddGroupName, GROUP:FindByName( AddGroupName ) ) - end - - return self -end - ---- Remove GROUP(s) from SET_GROUP. --- @param Core.Set#SET_GROUP self --- @param Wrapper.Group#GROUP RemoveGroupNames A single name or an array of GROUP names. --- @return self -function SET_GROUP:RemoveGroupsByName( RemoveGroupNames ) - - local RemoveGroupNamesArray = ( type( RemoveGroupNames ) == "table" ) and RemoveGroupNames or { RemoveGroupNames } - - for RemoveGroupID, RemoveGroupName in pairs( RemoveGroupNamesArray ) do - self:Remove( RemoveGroupName.GroupName ) - end - - return self -end - - - - ---- Finds a Group based on the Group Name. --- @param #SET_GROUP self --- @param #string GroupName --- @return Wrapper.Group#GROUP The found Group. -function SET_GROUP:FindGroup( GroupName ) - - local GroupFound = self.Set[GroupName] - return GroupFound -end - - - ---- Builds a set of groups of coalitions. --- Possible current coalitions are red, blue and neutral. --- @param #SET_GROUP self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #SET_GROUP self -function SET_GROUP:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self -end - - ---- Builds a set of groups out of categories. --- Possible current categories are plane, helicopter, ground, ship. --- @param #SET_GROUP self --- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". --- @return #SET_GROUP self -function SET_GROUP:FilterCategories( Categories ) - if not self.Filter.Categories then - self.Filter.Categories = {} - end - if type( Categories ) ~= "table" then - Categories = { Categories } - end - for CategoryID, Category in pairs( Categories ) do - self.Filter.Categories[Category] = Category - end - return self -end - ---- Builds a set of groups of defined countries. --- Possible current countries are those known within DCS world. --- @param #SET_GROUP self --- @param #string Countries Can take those country strings known within DCS world. --- @return #SET_GROUP self -function SET_GROUP:FilterCountries( Countries ) - if not self.Filter.Countries then - self.Filter.Countries = {} - end - if type( Countries ) ~= "table" then - Countries = { Countries } - end - for CountryID, Country in pairs( Countries ) do - self.Filter.Countries[Country] = Country - end - return self -end - - ---- Builds a set of groups of defined GROUP prefixes. --- All the groups starting with the given prefixes will be included within the set. --- @param #SET_GROUP self --- @param #string Prefixes The prefix of which the group name starts with. --- @return #SET_GROUP self -function SET_GROUP:FilterPrefixes( Prefixes ) - if not self.Filter.GroupPrefixes then - self.Filter.GroupPrefixes = {} - end - if type( Prefixes ) ~= "table" then - Prefixes = { Prefixes } - end - for PrefixID, Prefix in pairs( Prefixes ) do - self.Filter.GroupPrefixes[Prefix] = Prefix - end - return self -end - - ---- Starts the filtering. --- @param #SET_GROUP self --- @return #SET_GROUP self -function SET_GROUP:FilterStart() - - if _DATABASE then - self:_FilterStart() - end - - - - return self -end - ---- Handles the Database to check on an event (birth) that the Object was added in the Database. --- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! --- @param #SET_GROUP self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the GROUP --- @return #table The GROUP -function SET_GROUP:AddInDatabase( Event ) - self:F3( { Event } ) - - if Event.IniObjectCategory == 1 then - if not self.Database[Event.IniDCSGroupName] then - self.Database[Event.IniDCSGroupName] = GROUP:Register( Event.IniDCSGroupName ) - self:T3( self.Database[Event.IniDCSGroupName] ) - end - end - - return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName] -end - ---- Handles the Database to check on any event that Object exists in the Database. --- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! --- @param #SET_GROUP self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the GROUP --- @return #table The GROUP -function SET_GROUP:FindInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName] -end - ---- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP, providing the GROUP and optional parameters. --- @param #SET_GROUP self --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroup( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set ) - - return self -end - ---- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroupCompletelyInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsCompletelyInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroupPartlyInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsPartlyInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroupNotInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsNotInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - - ------ Iterate the SET_GROUP and call an interator function for each **alive** player, providing the Group of the player and optional parameters. ----- @param #SET_GROUP self ----- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter. ----- @return #SET_GROUP self ---function SET_GROUP:ForEachPlayer( IteratorFunction, ... ) --- self:F2( arg ) --- --- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) --- --- return self ---end --- --- ------ Iterate the SET_GROUP and call an interator function for each client, providing the Client to the function and optional parameters. ----- @param #SET_GROUP self ----- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter. ----- @return #SET_GROUP self ---function SET_GROUP:ForEachClient( IteratorFunction, ... ) --- self:F2( arg ) --- --- self:ForEach( IteratorFunction, arg, self.Clients ) --- --- return self ---end - - ---- --- @param #SET_GROUP self --- @param Wrapper.Group#GROUP MooseGroup --- @return #SET_GROUP self -function SET_GROUP:IsIncludeObject( MooseGroup ) - self:F2( MooseGroup ) - local MooseGroupInclude = true - - if self.Filter.Coalitions then - local MooseGroupCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - self:T3( { "Coalition:", MooseGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MooseGroup:GetCoalition() then - MooseGroupCoalition = true - end - end - MooseGroupInclude = MooseGroupInclude and MooseGroupCoalition - end - - if self.Filter.Categories then - local MooseGroupCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - self:T3( { "Category:", MooseGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MooseGroup:GetCategory() then - MooseGroupCategory = true - end - end - MooseGroupInclude = MooseGroupInclude and MooseGroupCategory - end - - if self.Filter.Countries then - local MooseGroupCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - self:T3( { "Country:", MooseGroup:GetCountry(), CountryName } ) - if country.id[CountryName] == MooseGroup:GetCountry() then - MooseGroupCountry = true - end - end - MooseGroupInclude = MooseGroupInclude and MooseGroupCountry - end - - if self.Filter.GroupPrefixes then - local MooseGroupPrefix = false - for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do - self:T3( { "Prefix:", string.find( MooseGroup:GetName(), GroupPrefix, 1 ), GroupPrefix } ) - if string.find( MooseGroup:GetName(), GroupPrefix, 1 ) then - MooseGroupPrefix = true - end - end - MooseGroupInclude = MooseGroupInclude and MooseGroupPrefix - end - - self:T2( MooseGroupInclude ) - return MooseGroupInclude -end - ---- @type SET_UNIT --- @extends Core.Set#SET_BASE - ---- # 3) SET_UNIT class, extends @{Set#SET_BASE} --- --- Mission designers can use the SET_UNIT class to build sets of units belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Unit types --- * Starting with certain prefix strings. --- --- ## 3.1) SET_UNIT constructor --- --- Create a new SET_UNIT object with the @{#SET_UNIT.New} method: --- --- * @{#SET_UNIT.New}: Creates a new SET_UNIT object. --- --- ## 3.2) Add or Remove UNIT(s) from SET_UNIT --- --- UNITs can be added and removed using the @{Set#SET_UNIT.AddUnitsByName} and @{Set#SET_UNIT.RemoveUnitsByName} respectively. --- These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT. --- --- ## 3.3) SET_UNIT filter criteria --- --- You can set filter criteria to define the set of units within the SET_UNIT. --- Filter criteria are defined by: --- --- * @{#SET_UNIT.FilterCoalitions}: Builds the SET_UNIT with the units belonging to the coalition(s). --- * @{#SET_UNIT.FilterCategories}: Builds the SET_UNIT with the units belonging to the category(ies). --- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s). --- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies). --- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units starting with the same prefix string(s). --- --- Once the filter criteria have been set for the SET_UNIT, you can start filtering using: --- --- * @{#SET_UNIT.FilterStart}: Starts the filtering of the units within the SET_UNIT. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Zone#ZONE}. --- --- ## 3.4) SET_UNIT iterators --- --- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. --- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_UNIT: --- --- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT. --- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- --- Planned iterators methods in development are (so these are not yet available): --- --- * @{#SET_UNIT.ForEachUnitInUnit}: Calls a function for each unit contained within the SET_UNIT. --- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. --- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function. --- --- ## 3.5 ) SET_UNIT atomic methods --- --- Various methods exist for a SET_UNIT to perform actions or calculations and retrieve results from the SET_UNIT: --- --- * @{#SET_UNIT.GetTypeNames}(): Retrieve the type names of the @{Unit}s in the SET, delimited by a comma. --- --- === --- @field #SET_UNIT SET_UNIT -SET_UNIT = { - ClassName = "SET_UNIT", - Units = {}, - Filter = { - Coalitions = nil, - Categories = nil, - Types = nil, - Countries = nil, - UnitPrefixes = nil, - }, - FilterMeta = { - Coalitions = { - red = coalition.side.RED, - blue = coalition.side.BLUE, - neutral = coalition.side.NEUTRAL, - }, - Categories = { - plane = Unit.Category.AIRPLANE, - helicopter = Unit.Category.HELICOPTER, - ground = Unit.Category.GROUND_UNIT, - ship = Unit.Category.SHIP, - structure = Unit.Category.STRUCTURE, - }, - }, -} - - ---- Creates a new SET_UNIT object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #SET_UNIT self --- @return #SET_UNIT --- @usage --- -- Define a new SET_UNIT Object. This DBObject will contain a reference to all alive Units. --- DBObject = SET_UNIT:New() -function SET_UNIT:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.UNITS ) ) - - return self -end - ---- Add UNIT(s) to SET_UNIT. --- @param #SET_UNIT self --- @param #string AddUnit A single UNIT. --- @return #SET_UNIT self -function SET_UNIT:AddUnit( AddUnit ) - self:F2( AddUnit:GetName() ) - - self:Add( AddUnit:GetName(), AddUnit ) - - return self -end - - ---- Add UNIT(s) to SET_UNIT. --- @param #SET_UNIT self --- @param #string AddUnitNames A single name or an array of UNIT names. --- @return #SET_UNIT self -function SET_UNIT:AddUnitsByName( AddUnitNames ) - - local AddUnitNamesArray = ( type( AddUnitNames ) == "table" ) and AddUnitNames or { AddUnitNames } - - self:T( AddUnitNamesArray ) - for AddUnitID, AddUnitName in pairs( AddUnitNamesArray ) do - self:Add( AddUnitName, UNIT:FindByName( AddUnitName ) ) - end - - return self -end - ---- Remove UNIT(s) from SET_UNIT. --- @param Core.Set#SET_UNIT self --- @param Wrapper.Unit#UNIT RemoveUnitNames A single name or an array of UNIT names. --- @return self -function SET_UNIT:RemoveUnitsByName( RemoveUnitNames ) - - local RemoveUnitNamesArray = ( type( RemoveUnitNames ) == "table" ) and RemoveUnitNames or { RemoveUnitNames } - - for RemoveUnitID, RemoveUnitName in pairs( RemoveUnitNamesArray ) do - self:Remove( RemoveUnitName ) - end - - return self -end - - ---- Finds a Unit based on the Unit Name. --- @param #SET_UNIT self --- @param #string UnitName --- @return Wrapper.Unit#UNIT The found Unit. -function SET_UNIT:FindUnit( UnitName ) - - local UnitFound = self.Set[UnitName] - return UnitFound -end - - - ---- Builds a set of units of coalitions. --- Possible current coalitions are red, blue and neutral. --- @param #SET_UNIT self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #SET_UNIT self -function SET_UNIT:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self -end - - ---- Builds a set of units out of categories. --- Possible current categories are plane, helicopter, ground, ship. --- @param #SET_UNIT self --- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". --- @return #SET_UNIT self -function SET_UNIT:FilterCategories( Categories ) - if not self.Filter.Categories then - self.Filter.Categories = {} - end - if type( Categories ) ~= "table" then - Categories = { Categories } - end - for CategoryID, Category in pairs( Categories ) do - self.Filter.Categories[Category] = Category - end - return self -end - - ---- Builds a set of units of defined unit types. --- Possible current types are those types known within DCS world. --- @param #SET_UNIT self --- @param #string Types Can take those type strings known within DCS world. --- @return #SET_UNIT self -function SET_UNIT:FilterTypes( Types ) - if not self.Filter.Types then - self.Filter.Types = {} - end - if type( Types ) ~= "table" then - Types = { Types } - end - for TypeID, Type in pairs( Types ) do - self.Filter.Types[Type] = Type - end - return self -end - - ---- Builds a set of units of defined countries. --- Possible current countries are those known within DCS world. --- @param #SET_UNIT self --- @param #string Countries Can take those country strings known within DCS world. --- @return #SET_UNIT self -function SET_UNIT:FilterCountries( Countries ) - if not self.Filter.Countries then - self.Filter.Countries = {} - end - if type( Countries ) ~= "table" then - Countries = { Countries } - end - for CountryID, Country in pairs( Countries ) do - self.Filter.Countries[Country] = Country - end - return self -end - - ---- Builds a set of units of defined unit prefixes. --- All the units starting with the given prefixes will be included within the set. --- @param #SET_UNIT self --- @param #string Prefixes The prefix of which the unit name starts with. --- @return #SET_UNIT self -function SET_UNIT:FilterPrefixes( Prefixes ) - if not self.Filter.UnitPrefixes then - self.Filter.UnitPrefixes = {} - end - if type( Prefixes ) ~= "table" then - Prefixes = { Prefixes } - end - for PrefixID, Prefix in pairs( Prefixes ) do - self.Filter.UnitPrefixes[Prefix] = Prefix - end - return self -end - ---- Builds a set of units having a radar of give types. --- All the units having a radar of a given type will be included within the set. --- @param #SET_UNIT self --- @param #table RadarTypes The radar types. --- @return #SET_UNIT self -function SET_UNIT:FilterHasRadar( RadarTypes ) - - self.Filter.RadarTypes = self.Filter.RadarTypes or {} - if type( RadarTypes ) ~= "table" then - RadarTypes = { RadarTypes } - end - for RadarTypeID, RadarType in pairs( RadarTypes ) do - self.Filter.RadarTypes[RadarType] = RadarType - end - return self -end - ---- Builds a set of SEADable units. --- @param #SET_UNIT self --- @return #SET_UNIT self -function SET_UNIT:FilterHasSEAD() - - self.Filter.SEAD = true - return self -end - - - ---- Starts the filtering. --- @param #SET_UNIT self --- @return #SET_UNIT self -function SET_UNIT:FilterStart() - - if _DATABASE then - self:_FilterStart() - end - - return self -end - ---- Handles the Database to check on an event (birth) that the Object was added in the Database. --- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! --- @param #SET_UNIT self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the UNIT --- @return #table The UNIT -function SET_UNIT:AddInDatabase( Event ) - self:F3( { Event } ) - - if Event.IniObjectCategory == 1 then - if not self.Database[Event.IniDCSUnitName] then - self.Database[Event.IniDCSUnitName] = UNIT:Register( Event.IniDCSUnitName ) - self:T3( self.Database[Event.IniDCSUnitName] ) - end - end - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Handles the Database to check on any event that Object exists in the Database. --- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! --- @param #SET_UNIT self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the UNIT --- @return #table The UNIT -function SET_UNIT:FindInDatabase( Event ) - self:E( { Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event } ) - - - return Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName] -end - ---- Iterate the SET_UNIT and call an interator function for each **alive** UNIT, providing the UNIT and optional parameters. --- @param #SET_UNIT self --- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. --- @return #SET_UNIT self -function SET_UNIT:ForEachUnit( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set ) - - return self -end - ---- Iterate the SET_UNIT and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. --- @param #SET_UNIT self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. --- @return #SET_UNIT self -function SET_UNIT:ForEachUnitCompletelyInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Unit#UNIT UnitObject - function( ZoneObject, UnitObject ) - if UnitObject:IsCompletelyInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- Iterate the SET_UNIT and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function. --- @param #SET_UNIT self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. --- @return #SET_UNIT self -function SET_UNIT:ForEachUnitNotInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Unit#UNIT UnitObject - function( ZoneObject, UnitObject ) - if UnitObject:IsNotInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- Returns map of unit types. --- @param #SET_UNIT self --- @return #map<#string,#number> A map of the unit types found. The key is the UnitTypeName and the value is the amount of unit types found. -function SET_UNIT:GetUnitTypes() - self:F2() - - local MT = {} -- Message Text - local UnitTypes = {} - - for UnitID, UnitData in pairs( self:GetSet() ) do - local TextUnit = UnitData -- Wrapper.Unit#UNIT - if TextUnit:IsAlive() then - local UnitType = TextUnit:GetTypeName() - - if not UnitTypes[UnitType] then - UnitTypes[UnitType] = 1 - else - UnitTypes[UnitType] = UnitTypes[UnitType] + 1 - end - end - end - - for UnitTypeID, UnitType in pairs( UnitTypes ) do - MT[#MT+1] = UnitType .. " of " .. UnitTypeID - end - - return UnitTypes -end - - ---- Returns a comma separated string of the unit types with a count in the @{Set}. --- @param #SET_UNIT self --- @return #string The unit types string -function SET_UNIT:GetUnitTypesText() - self:F2() - - local MT = {} -- Message Text - local UnitTypes = self:GetUnitTypes() - - for UnitTypeID, UnitType in pairs( UnitTypes ) do - MT[#MT+1] = UnitType .. " of " .. UnitTypeID - end - - return table.concat( MT, ", " ) -end - ---- Returns map of unit threat levels. --- @param #SET_UNIT self --- @return #table. -function SET_UNIT:GetUnitThreatLevels() - self:F2() - - local UnitThreatLevels = {} - - for UnitID, UnitData in pairs( self:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - if ThreatUnit:IsAlive() then - local UnitThreatLevel, UnitThreatLevelText = ThreatUnit:GetThreatLevel() - local ThreatUnitName = ThreatUnit:GetName() - - UnitThreatLevels[UnitThreatLevel] = UnitThreatLevels[UnitThreatLevel] or {} - UnitThreatLevels[UnitThreatLevel].UnitThreatLevelText = UnitThreatLevelText - UnitThreatLevels[UnitThreatLevel].Units = UnitThreatLevels[UnitThreatLevel].Units or {} - UnitThreatLevels[UnitThreatLevel].Units[ThreatUnitName] = ThreatUnit - end - end - - return UnitThreatLevels -end - ---- Calculate the maxium A2G threat level of the SET_UNIT. --- @param #SET_UNIT self -function SET_UNIT:CalculateThreatLevelA2G() - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( self:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end - - self:T3( MaxThreatLevelA2G ) - return MaxThreatLevelA2G - -end - - ---- Returns if the @{Set} has targets having a radar (of a given type). --- @param #SET_UNIT self --- @param Dcs.DCSWrapper.Unit#Unit.RadarType RadarType --- @return #number The amount of radars in the Set with the given type -function SET_UNIT:HasRadar( RadarType ) - self:F2( RadarType ) - - local RadarCount = 0 - for UnitID, UnitData in pairs( self:GetSet()) do - local UnitSensorTest = UnitData -- Wrapper.Unit#UNIT - local HasSensors - if RadarType then - HasSensors = UnitSensorTest:HasSensors( Unit.SensorType.RADAR, RadarType ) - else - HasSensors = UnitSensorTest:HasSensors( Unit.SensorType.RADAR ) - end - self:T3(HasSensors) - if HasSensors then - RadarCount = RadarCount + 1 - end - end - - return RadarCount -end - ---- Returns if the @{Set} has targets that can be SEADed. --- @param #SET_UNIT self --- @return #number The amount of SEADable units in the Set -function SET_UNIT:HasSEAD() - self:F2() - - local SEADCount = 0 - for UnitID, UnitData in pairs( self:GetSet()) do - local UnitSEAD = UnitData -- Wrapper.Unit#UNIT - if UnitSEAD:IsAlive() then - local UnitSEADAttributes = UnitSEAD:GetDesc().attributes - - local HasSEAD = UnitSEAD:HasSEAD() - - self:T3(HasSEAD) - if HasSEAD then - SEADCount = SEADCount + 1 - end - end - end - - return SEADCount -end - ---- Returns if the @{Set} has ground targets. --- @param #SET_UNIT self --- @return #number The amount of ground targets in the Set. -function SET_UNIT:HasGroundUnits() - self:F2() - - local GroundUnitCount = 0 - for UnitID, UnitData in pairs( self:GetSet()) do - local UnitTest = UnitData -- Wrapper.Unit#UNIT - if UnitTest:IsGround() then - GroundUnitCount = GroundUnitCount + 1 - end - end - - return GroundUnitCount -end - ---- Returns if the @{Set} has friendly ground units. --- @param #SET_UNIT self --- @return #number The amount of ground targets in the Set. -function SET_UNIT:HasFriendlyUnits( FriendlyCoalition ) - self:F2() - - local FriendlyUnitCount = 0 - for UnitID, UnitData in pairs( self:GetSet()) do - local UnitTest = UnitData -- Wrapper.Unit#UNIT - if UnitTest:IsFriendly( FriendlyCoalition ) then - FriendlyUnitCount = FriendlyUnitCount + 1 - end - end - - return FriendlyUnitCount -end - - - ------ Iterate the SET_UNIT and call an interator function for each **alive** player, providing the Unit of the player and optional parameters. ----- @param #SET_UNIT self ----- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter. ----- @return #SET_UNIT self ---function SET_UNIT:ForEachPlayer( IteratorFunction, ... ) --- self:F2( arg ) --- --- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) --- --- return self ---end --- --- ------ Iterate the SET_UNIT and call an interator function for each client, providing the Client to the function and optional parameters. ----- @param #SET_UNIT self ----- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter. ----- @return #SET_UNIT self ---function SET_UNIT:ForEachClient( IteratorFunction, ... ) --- self:F2( arg ) --- --- self:ForEach( IteratorFunction, arg, self.Clients ) --- --- return self ---end - - ---- --- @param #SET_UNIT self --- @param Wrapper.Unit#UNIT MUnit --- @return #SET_UNIT self -function SET_UNIT:IsIncludeObject( MUnit ) - self:F2( MUnit ) - local MUnitInclude = true - - if self.Filter.Coalitions then - local MUnitCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - self:T3( { "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MUnit:GetCoalition() then - MUnitCoalition = true - end - end - MUnitInclude = MUnitInclude and MUnitCoalition - end - - if self.Filter.Categories then - local MUnitCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - self:T3( { "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MUnit:GetDesc().category then - MUnitCategory = true - end - end - MUnitInclude = MUnitInclude and MUnitCategory - end - - if self.Filter.Types then - local MUnitType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - self:T3( { "Type:", MUnit:GetTypeName(), TypeName } ) - if TypeName == MUnit:GetTypeName() then - MUnitType = true - end - end - MUnitInclude = MUnitInclude and MUnitType - end - - if self.Filter.Countries then - local MUnitCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - self:T3( { "Country:", MUnit:GetCountry(), CountryName } ) - if country.id[CountryName] == MUnit:GetCountry() then - MUnitCountry = true - end - end - MUnitInclude = MUnitInclude and MUnitCountry - end - - if self.Filter.UnitPrefixes then - local MUnitPrefix = false - for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do - self:T3( { "Prefix:", string.find( MUnit:GetName(), UnitPrefix, 1 ), UnitPrefix } ) - if string.find( MUnit:GetName(), UnitPrefix, 1 ) then - MUnitPrefix = true - end - end - MUnitInclude = MUnitInclude and MUnitPrefix - end - - if self.Filter.RadarTypes then - local MUnitRadar = false - for RadarTypeID, RadarType in pairs( self.Filter.RadarTypes ) do - self:T3( { "Radar:", RadarType } ) - if MUnit:HasSensors( Unit.SensorType.RADAR, RadarType ) == true then - if MUnit:GetRadar() == true then -- This call is necessary to evaluate the SEAD capability. - self:T3( "RADAR Found" ) - end - MUnitRadar = true - end - end - MUnitInclude = MUnitInclude and MUnitRadar - end - - if self.Filter.SEAD then - local MUnitSEAD = false - if MUnit:HasSEAD() == true then - self:T3( "SEAD Found" ) - MUnitSEAD = true - end - MUnitInclude = MUnitInclude and MUnitSEAD - end - - self:T2( MUnitInclude ) - return MUnitInclude -end - - ---- Retrieve the type names of the @{Unit}s in the SET, delimited by an optional delimiter. --- @param #SET_UNIT self --- @param #string Delimiter (optional) The delimiter, which is default a comma. --- @return #string The types of the @{Unit}s delimited. -function SET_UNIT:GetTypeNames( Delimiter ) - - Delimiter = Delimiter or ", " - local TypeReport = REPORT:New() - local Types = {} - - for UnitName, UnitData in pairs( self:GetSet() ) do - - local Unit = UnitData -- Wrapper.Unit#UNIT - local UnitTypeName = Unit:GetTypeName() - - if not Types[UnitTypeName] then - Types[UnitTypeName] = UnitTypeName - TypeReport:Add( UnitTypeName ) - end - end - - return TypeReport:Text( Delimiter ) -end - - ---- SET_CLIENT - - ---- @type SET_CLIENT --- @extends Core.Set#SET_BASE - - - ---- # 4) SET_CLIENT class, extends @{Set#SET_BASE} --- --- Mission designers can use the @{Set#SET_CLIENT} class to build sets of units belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Client types --- * Starting with certain prefix strings. --- --- ## 4.1) SET_CLIENT constructor --- --- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method: --- --- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object. --- --- ## 4.2) Add or Remove CLIENT(s) from SET_CLIENT --- --- CLIENTs can be added and removed using the @{Set#SET_CLIENT.AddClientsByName} and @{Set#SET_CLIENT.RemoveClientsByName} respectively. --- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. --- --- ## 4.3) SET_CLIENT filter criteria --- --- You can set filter criteria to define the set of clients within the SET_CLIENT. --- Filter criteria are defined by: --- --- * @{#SET_CLIENT.FilterCoalitions}: Builds the SET_CLIENT with the clients belonging to the coalition(s). --- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies). --- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s). --- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). --- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s). --- --- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: --- --- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients within the SET_CLIENT. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Zone#ZONE}. --- --- ## 4.4) SET_CLIENT iterators --- --- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. --- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_CLIENT: --- --- * @{#SET_CLIENT.ForEachClient}: Calls a function for each alive client it finds within the SET_CLIENT. --- --- === --- @field #SET_CLIENT SET_CLIENT -SET_CLIENT = { - ClassName = "SET_CLIENT", - Clients = {}, - Filter = { - Coalitions = nil, - Categories = nil, - Types = nil, - Countries = nil, - ClientPrefixes = nil, - }, - FilterMeta = { - Coalitions = { - red = coalition.side.RED, - blue = coalition.side.BLUE, - neutral = coalition.side.NEUTRAL, - }, - Categories = { - plane = Unit.Category.AIRPLANE, - helicopter = Unit.Category.HELICOPTER, - ground = Unit.Category.GROUND_UNIT, - ship = Unit.Category.SHIP, - structure = Unit.Category.STRUCTURE, - }, - }, -} - - ---- Creates a new SET_CLIENT object, building a set of clients belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #SET_CLIENT self --- @return #SET_CLIENT --- @usage --- -- Define a new SET_CLIENT Object. This DBObject will contain a reference to all Clients. --- DBObject = SET_CLIENT:New() -function SET_CLIENT:New() - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CLIENTS ) ) - - return self -end - ---- Add CLIENT(s) to SET_CLIENT. --- @param Core.Set#SET_CLIENT self --- @param #string AddClientNames A single name or an array of CLIENT names. --- @return self -function SET_CLIENT:AddClientsByName( AddClientNames ) - - local AddClientNamesArray = ( type( AddClientNames ) == "table" ) and AddClientNames or { AddClientNames } - - for AddClientID, AddClientName in pairs( AddClientNamesArray ) do - self:Add( AddClientName, CLIENT:FindByName( AddClientName ) ) - end - - return self -end - ---- Remove CLIENT(s) from SET_CLIENT. --- @param Core.Set#SET_CLIENT self --- @param Wrapper.Client#CLIENT RemoveClientNames A single name or an array of CLIENT names. --- @return self -function SET_CLIENT:RemoveClientsByName( RemoveClientNames ) - - local RemoveClientNamesArray = ( type( RemoveClientNames ) == "table" ) and RemoveClientNames or { RemoveClientNames } - - for RemoveClientID, RemoveClientName in pairs( RemoveClientNamesArray ) do - self:Remove( RemoveClientName.ClientName ) - end - - return self -end - - ---- Finds a Client based on the Client Name. --- @param #SET_CLIENT self --- @param #string ClientName --- @return Wrapper.Client#CLIENT The found Client. -function SET_CLIENT:FindClient( ClientName ) - - local ClientFound = self.Set[ClientName] - return ClientFound -end - - - ---- Builds a set of clients of coalitions. --- Possible current coalitions are red, blue and neutral. --- @param #SET_CLIENT self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #SET_CLIENT self -function SET_CLIENT:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self -end - - ---- Builds a set of clients out of categories. --- Possible current categories are plane, helicopter, ground, ship. --- @param #SET_CLIENT self --- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". --- @return #SET_CLIENT self -function SET_CLIENT:FilterCategories( Categories ) - if not self.Filter.Categories then - self.Filter.Categories = {} - end - if type( Categories ) ~= "table" then - Categories = { Categories } - end - for CategoryID, Category in pairs( Categories ) do - self.Filter.Categories[Category] = Category - end - return self -end - - ---- Builds a set of clients of defined client types. --- Possible current types are those types known within DCS world. --- @param #SET_CLIENT self --- @param #string Types Can take those type strings known within DCS world. --- @return #SET_CLIENT self -function SET_CLIENT:FilterTypes( Types ) - if not self.Filter.Types then - self.Filter.Types = {} - end - if type( Types ) ~= "table" then - Types = { Types } - end - for TypeID, Type in pairs( Types ) do - self.Filter.Types[Type] = Type - end - return self -end - - ---- Builds a set of clients of defined countries. --- Possible current countries are those known within DCS world. --- @param #SET_CLIENT self --- @param #string Countries Can take those country strings known within DCS world. --- @return #SET_CLIENT self -function SET_CLIENT:FilterCountries( Countries ) - if not self.Filter.Countries then - self.Filter.Countries = {} - end - if type( Countries ) ~= "table" then - Countries = { Countries } - end - for CountryID, Country in pairs( Countries ) do - self.Filter.Countries[Country] = Country - end - return self -end - - ---- Builds a set of clients of defined client prefixes. --- All the clients starting with the given prefixes will be included within the set. --- @param #SET_CLIENT self --- @param #string Prefixes The prefix of which the client name starts with. --- @return #SET_CLIENT self -function SET_CLIENT:FilterPrefixes( Prefixes ) - if not self.Filter.ClientPrefixes then - self.Filter.ClientPrefixes = {} - end - if type( Prefixes ) ~= "table" then - Prefixes = { Prefixes } - end - for PrefixID, Prefix in pairs( Prefixes ) do - self.Filter.ClientPrefixes[Prefix] = Prefix - end - return self -end - - - - ---- Starts the filtering. --- @param #SET_CLIENT self --- @return #SET_CLIENT self -function SET_CLIENT:FilterStart() - - if _DATABASE then - self:_FilterStart() - end - - return self -end - ---- Handles the Database to check on an event (birth) that the Object was added in the Database. --- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! --- @param #SET_CLIENT self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the CLIENT --- @return #table The CLIENT -function SET_CLIENT:AddInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Handles the Database to check on any event that Object exists in the Database. --- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! --- @param #SET_CLIENT self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the CLIENT --- @return #table The CLIENT -function SET_CLIENT:FindInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Iterate the SET_CLIENT and call an interator function for each **alive** CLIENT, providing the CLIENT and optional parameters. --- @param #SET_CLIENT self --- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. --- @return #SET_CLIENT self -function SET_CLIENT:ForEachClient( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set ) - - return self -end - ---- Iterate the SET_CLIENT and call an iterator function for each **alive** CLIENT presence completely in a @{Zone}, providing the CLIENT and optional parameters to the called function. --- @param #SET_CLIENT self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. --- @return #SET_CLIENT self -function SET_CLIENT:ForEachClientInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- Iterate the SET_CLIENT and call an iterator function for each **alive** CLIENT presence not in a @{Zone}, providing the CLIENT and optional parameters to the called function. --- @param #SET_CLIENT self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. --- @return #SET_CLIENT self -function SET_CLIENT:ForEachClientNotInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsNotInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- --- @param #SET_CLIENT self --- @param Wrapper.Client#CLIENT MClient --- @return #SET_CLIENT self -function SET_CLIENT:IsIncludeObject( MClient ) - self:F2( MClient ) - - local MClientInclude = true - - if MClient then - local MClientName = MClient.UnitName - - if self.Filter.Coalitions then - local MClientCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName ) - self:T3( { "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == ClientCoalitionID then - MClientCoalition = true - end - end - self:T( { "Evaluated Coalition", MClientCoalition } ) - MClientInclude = MClientInclude and MClientCoalition - end - - if self.Filter.Categories then - local MClientCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) - self:T3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then - MClientCategory = true - end - end - self:T( { "Evaluated Category", MClientCategory } ) - MClientInclude = MClientInclude and MClientCategory - end - - if self.Filter.Types then - local MClientType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - self:T3( { "Type:", MClient:GetTypeName(), TypeName } ) - if TypeName == MClient:GetTypeName() then - MClientType = true - end - end - self:T( { "Evaluated Type", MClientType } ) - MClientInclude = MClientInclude and MClientType - end - - if self.Filter.Countries then - local MClientCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - local ClientCountryID = _DATABASE:GetCountryFromClientTemplate(MClientName) - self:T3( { "Country:", ClientCountryID, country.id[CountryName], CountryName } ) - if country.id[CountryName] and country.id[CountryName] == ClientCountryID then - MClientCountry = true - end - end - self:T( { "Evaluated Country", MClientCountry } ) - MClientInclude = MClientInclude and MClientCountry - end - - if self.Filter.ClientPrefixes then - local MClientPrefix = false - for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do - self:T3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } ) - if string.find( MClient.UnitName, ClientPrefix, 1 ) then - MClientPrefix = true - end - end - self:T( { "Evaluated Prefix", MClientPrefix } ) - MClientInclude = MClientInclude and MClientPrefix - end - end - - self:T2( MClientInclude ) - return MClientInclude -end - ---- @type SET_AIRBASE --- @extends Core.Set#SET_BASE - ---- # 5) SET_AIRBASE class, extends @{Set#SET_BASE} --- --- Mission designers can use the @{Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: --- --- * Coalitions --- --- ## 5.1) SET_AIRBASE constructor --- --- Create a new SET_AIRBASE object with the @{#SET_AIRBASE.New} method: --- --- * @{#SET_AIRBASE.New}: Creates a new SET_AIRBASE object. --- --- ## 5.2) Add or Remove AIRBASEs from SET_AIRBASE --- --- AIRBASEs can be added and removed using the @{Set#SET_AIRBASE.AddAirbasesByName} and @{Set#SET_AIRBASE.RemoveAirbasesByName} respectively. --- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE. --- --- ## 5.3) SET_AIRBASE filter criteria --- --- You can set filter criteria to define the set of clients within the SET_AIRBASE. --- Filter criteria are defined by: --- --- * @{#SET_AIRBASE.FilterCoalitions}: Builds the SET_AIRBASE with the airbases belonging to the coalition(s). --- --- Once the filter criteria have been set for the SET_AIRBASE, you can start filtering using: --- --- * @{#SET_AIRBASE.FilterStart}: Starts the filtering of the airbases within the SET_AIRBASE. --- --- ## 5.4) SET_AIRBASE iterators --- --- Once the filters have been defined and the SET_AIRBASE has been built, you can iterate the SET_AIRBASE with the available iterator methods. --- The iterator methods will walk the SET_AIRBASE set, and call for each airbase within the set a function that you provide. --- The following iterator methods are currently available within the SET_AIRBASE: --- --- * @{#SET_AIRBASE.ForEachAirbase}: Calls a function for each airbase it finds within the SET_AIRBASE. --- --- === --- @field #SET_AIRBASE SET_AIRBASE -SET_AIRBASE = { - ClassName = "SET_AIRBASE", - Airbases = {}, - Filter = { - Coalitions = nil, - }, - FilterMeta = { - Coalitions = { - red = coalition.side.RED, - blue = coalition.side.BLUE, - neutral = coalition.side.NEUTRAL, - }, - Categories = { - airdrome = Airbase.Category.AIRDROME, - helipad = Airbase.Category.HELIPAD, - ship = Airbase.Category.SHIP, - }, - }, -} - - ---- Creates a new SET_AIRBASE object, building a set of airbases belonging to a coalitions and categories. --- @param #SET_AIRBASE self --- @return #SET_AIRBASE self --- @usage --- -- Define a new SET_AIRBASE Object. The DatabaseSet will contain a reference to all Airbases. --- DatabaseSet = SET_AIRBASE:New() -function SET_AIRBASE:New() - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.AIRBASES ) ) - - return self -end - ---- Add AIRBASEs to SET_AIRBASE. --- @param Core.Set#SET_AIRBASE self --- @param #string AddAirbaseNames A single name or an array of AIRBASE names. --- @return self -function SET_AIRBASE:AddAirbasesByName( AddAirbaseNames ) - - local AddAirbaseNamesArray = ( type( AddAirbaseNames ) == "table" ) and AddAirbaseNames or { AddAirbaseNames } - - for AddAirbaseID, AddAirbaseName in pairs( AddAirbaseNamesArray ) do - self:Add( AddAirbaseName, AIRBASE:FindByName( AddAirbaseName ) ) - end - - return self -end - ---- Remove AIRBASEs from SET_AIRBASE. --- @param Core.Set#SET_AIRBASE self --- @param Wrapper.Airbase#AIRBASE RemoveAirbaseNames A single name or an array of AIRBASE names. --- @return self -function SET_AIRBASE:RemoveAirbasesByName( RemoveAirbaseNames ) - - local RemoveAirbaseNamesArray = ( type( RemoveAirbaseNames ) == "table" ) and RemoveAirbaseNames or { RemoveAirbaseNames } - - for RemoveAirbaseID, RemoveAirbaseName in pairs( RemoveAirbaseNamesArray ) do - self:Remove( RemoveAirbaseName.AirbaseName ) - end - - return self -end - - ---- Finds a Airbase based on the Airbase Name. --- @param #SET_AIRBASE self --- @param #string AirbaseName --- @return Wrapper.Airbase#AIRBASE The found Airbase. -function SET_AIRBASE:FindAirbase( AirbaseName ) - - local AirbaseFound = self.Set[AirbaseName] - return AirbaseFound -end - - - ---- Builds a set of airbases of coalitions. --- Possible current coalitions are red, blue and neutral. --- @param #SET_AIRBASE self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #SET_AIRBASE self -function SET_AIRBASE:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self -end - - ---- Builds a set of airbases out of categories. --- Possible current categories are plane, helicopter, ground, ship. --- @param #SET_AIRBASE self --- @param #string Categories Can take the following values: "airdrome", "helipad", "ship". --- @return #SET_AIRBASE self -function SET_AIRBASE:FilterCategories( Categories ) - if not self.Filter.Categories then - self.Filter.Categories = {} - end - if type( Categories ) ~= "table" then - Categories = { Categories } - end - for CategoryID, Category in pairs( Categories ) do - self.Filter.Categories[Category] = Category - end - return self -end - ---- Starts the filtering. --- @param #SET_AIRBASE self --- @return #SET_AIRBASE self -function SET_AIRBASE:FilterStart() - - if _DATABASE then - self:_FilterStart() - end - - return self -end - - ---- Handles the Database to check on an event (birth) that the Object was added in the Database. --- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! --- @param #SET_AIRBASE self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the AIRBASE --- @return #table The AIRBASE -function SET_AIRBASE:AddInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Handles the Database to check on any event that Object exists in the Database. --- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! --- @param #SET_AIRBASE self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the AIRBASE --- @return #table The AIRBASE -function SET_AIRBASE:FindInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Iterate the SET_AIRBASE and call an interator function for each AIRBASE, providing the AIRBASE and optional parameters. --- @param #SET_AIRBASE self --- @param #function IteratorFunction The function that will be called when there is an alive AIRBASE in the SET_AIRBASE. The function needs to accept a AIRBASE parameter. --- @return #SET_AIRBASE self -function SET_AIRBASE:ForEachAirbase( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set ) - - return self -end - ---- Iterate the SET_AIRBASE while identifying the nearest @{Airbase#AIRBASE} from a @{Point#POINT_VEC2}. --- @param #SET_AIRBASE self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest @{Airbase#AIRBASE}. --- @return Wrapper.Airbase#AIRBASE The closest @{Airbase#AIRBASE}. -function SET_AIRBASE:FindNearestAirbaseFromPointVec2( PointVec2 ) - self:F2( PointVec2 ) - - local NearestAirbase = self:FindNearestObjectFromPointVec2( PointVec2 ) - return NearestAirbase -end - - - ---- --- @param #SET_AIRBASE self --- @param Wrapper.Airbase#AIRBASE MAirbase --- @return #SET_AIRBASE self -function SET_AIRBASE:IsIncludeObject( MAirbase ) - self:F2( MAirbase ) - - local MAirbaseInclude = true - - if MAirbase then - local MAirbaseName = MAirbase:GetName() - - if self.Filter.Coalitions then - local MAirbaseCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - local AirbaseCoalitionID = _DATABASE:GetCoalitionFromAirbase( MAirbaseName ) - self:T3( { "Coalition:", AirbaseCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == AirbaseCoalitionID then - MAirbaseCoalition = true - end - end - self:T( { "Evaluated Coalition", MAirbaseCoalition } ) - MAirbaseInclude = MAirbaseInclude and MAirbaseCoalition - end - - if self.Filter.Categories then - local MAirbaseCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase( MAirbaseName ) - self:T3( { "Category:", AirbaseCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == AirbaseCategoryID then - MAirbaseCategory = true - end - end - self:T( { "Evaluated Category", MAirbaseCategory } ) - MAirbaseInclude = MAirbaseInclude and MAirbaseCategory - end - end - - self:T2( MAirbaseInclude ) - return MAirbaseInclude -end ---- **Core** - **POINT\_VEC** classes define an **extensive API** to **manage 3D points** in the simulation space. --- --- 1) @{Point#POINT_VEC3} class, extends @{Base#BASE} --- ================================================== --- The @{Point#POINT_VEC3} class defines a 3D point in the simulator. --- --- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts. --- In order to keep the credibility of the the author, I want to emphasize that the of the MIST framework was created by Grimes, who you can find on the Eagle Dynamics Forums. --- --- ## 1.1) POINT_VEC3 constructor --- --- A new POINT_VEC3 instance can be created with: --- --- * @{Point#POINT_VEC3.New}(): a 3D point. --- * @{Point#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}. --- --- ## 1.2) Manupulate the X, Y, Z coordinates of the point --- --- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate. --- Methods exist to manupulate these coordinates. --- --- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively. --- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value. --- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}() --- to add or substract a value from the current respective axis value. --- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example: --- --- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3() --- --- ## 1.3) Create waypoints for routes --- --- A POINT_VEC3 can prepare waypoints for Ground, Air and Naval groups to be embedded into a Route. --- --- --- ## 1.5) Smoke, flare, explode, illuminate --- --- At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods: --- --- ### 1.5.1) Smoke --- --- * @{#POINT_VEC3.Smoke}(): To smoke the point in a certain color. --- * @{#POINT_VEC3.SmokeBlue}(): To smoke the point in blue. --- * @{#POINT_VEC3.SmokeRed}(): To smoke the point in red. --- * @{#POINT_VEC3.SmokeOrange}(): To smoke the point in orange. --- * @{#POINT_VEC3.SmokeWhite}(): To smoke the point in white. --- * @{#POINT_VEC3.SmokeGreen}(): To smoke the point in green. --- --- ### 1.5.2) Flare --- --- * @{#POINT_VEC3.Flare}(): To flare the point in a certain color. --- * @{#POINT_VEC3.FlareRed}(): To flare the point in red. --- * @{#POINT_VEC3.FlareYellow}(): To flare the point in yellow. --- * @{#POINT_VEC3.FlareWhite}(): To flare the point in white. --- * @{#POINT_VEC3.FlareGreen}(): To flare the point in green. --- --- ### 1.5.3) Explode --- --- * @{#POINT_VEC3.Explosion}(): To explode the point with a certain intensity. --- --- ### 1.5.4) Illuminate --- --- * @{#POINT_VEC3.IlluminationBomb}(): To illuminate the point. --- --- --- 2) @{Point#POINT_VEC2} class, extends @{Point#POINT_VEC3} --- ========================================================= --- The @{Point#POINT_VEC2} class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified. --- --- 2.1) POINT_VEC2 constructor --- --------------------------- --- A new POINT_VEC2 instance can be created with: --- --- * @{Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter. --- * @{Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}. --- --- ## 1.2) Manupulate the X, Altitude, Y coordinates of the 2D point --- --- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate. --- Methods exist to manupulate these coordinates. --- --- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively. --- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value. --- The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods @{#POINT_VEC2.GetLat}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetLon}() respectively. --- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}() --- to add or substract a value from the current respective axis value. --- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example: --- --- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2() --- --- === --- --- **API CHANGE HISTORY** --- ====================== --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-03: POINT\_VEC3:**Explosion( ExplosionIntensity )** added. --- 2017-03-03: POINT\_VEC3:**IlluminationBomb()** added. --- --- 2017-02-18: POINT\_VEC3:**NewFromVec2( Vec2, LandHeightAdd )** added. --- --- 2016-08-12: POINT\_VEC3:**Translate( Distance, Angle )** added. --- --- 2016-08-06: Made PointVec3 and Vec3, PointVec2 and Vec2 terminology used in the code consistent. --- --- * Replaced method _Point_Vec3() to **Vec3**() where the code manages a Vec3. Replaced all references to the method. --- * Replaced method _Point_Vec2() to **Vec2**() where the code manages a Vec2. Replaced all references to the method. --- * Replaced method Random_Point_Vec3() to **RandomVec3**() where the code manages a Vec3. Replaced all references to the method. --- . --- === --- --- ### Authors: --- --- * FlightControl : Design & Programming --- --- ### Contributions: --- --- @module Point - ---- The POINT_VEC3 class --- @type POINT_VEC3 --- @field #number x The x coordinate in 3D space. --- @field #number y The y coordinate in 3D space. --- @field #number z The z coordiante in 3D space. --- @field Utilities.Utils#SMOKECOLOR SmokeColor --- @field Utilities.Utils#FLARECOLOR FlareColor --- @field #POINT_VEC3.RoutePointAltType RoutePointAltType --- @field #POINT_VEC3.RoutePointType RoutePointType --- @field #POINT_VEC3.RoutePointAction RoutePointAction --- @extends Core.Base#BASE -POINT_VEC3 = { - ClassName = "POINT_VEC3", - Metric = true, - RoutePointAltType = { - BARO = "BARO", - }, - RoutePointType = { - TakeOffParking = "TakeOffParking", - TurningPoint = "Turning Point", - }, - RoutePointAction = { - FromParkingArea = "From Parking Area", - TurningPoint = "Turning Point", - }, -} - ---- The POINT_VEC2 class --- @type POINT_VEC2 --- @field Dcs.DCSTypes#Distance x The x coordinate in meters. --- @field Dcs.DCSTypes#Distance y the y coordinate in meters. --- @extends Core.Point#POINT_VEC3 -POINT_VEC2 = { - ClassName = "POINT_VEC2", -} - - -do -- POINT_VEC3 - ---- RoutePoint AltTypes --- @type POINT_VEC3.RoutePointAltType --- @field BARO "BARO" - ---- RoutePoint Types --- @type POINT_VEC3.RoutePointType --- @field TakeOffParking "TakeOffParking" --- @field TurningPoint "Turning Point" - ---- RoutePoint Actions --- @type POINT_VEC3.RoutePointAction --- @field FromParkingArea "From Parking Area" --- @field TurningPoint "Turning Point" - --- Constructor. - ---- Create a new POINT_VEC3 object. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North. --- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing Upwards. --- @param Dcs.DCSTypes#Distance z The z coordinate of the Vec3 point, pointing to the Right. --- @return Core.Point#POINT_VEC3 self -function POINT_VEC3:New( x, y, z ) - - local self = BASE:Inherit( self, BASE:New() ) - self.x = x - self.y = y - self.z = z - - return self -end - ---- Create a new POINT_VEC3 object from Vec2 coordinates. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. --- @return Core.Point#POINT_VEC3 self -function POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd ) - - local LandHeight = land.getHeight( Vec2 ) - - LandHeightAdd = LandHeightAdd or 0 - LandHeight = LandHeight + LandHeightAdd - - self = self:New( Vec2.x, LandHeight, Vec2.y ) - - self:F2( self ) - - return self -end - ---- Create a new POINT_VEC3 object from Vec3 coordinates. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point. --- @return Core.Point#POINT_VEC3 self -function POINT_VEC3:NewFromVec3( Vec3 ) - - self = self:New( Vec3.x, Vec3.y, Vec3.z ) - self:F2( self ) - return self -end - - ---- Return the coordinates of the POINT_VEC3 in Vec3 format. --- @param #POINT_VEC3 self --- @return Dcs.DCSTypes#Vec3 The Vec3 coodinate. -function POINT_VEC3:GetVec3() - return { x = self.x, y = self.y, z = self.z } -end - ---- Return the coordinates of the POINT_VEC3 in Vec2 format. --- @param #POINT_VEC3 self --- @return Dcs.DCSTypes#Vec2 The Vec2 coodinate. -function POINT_VEC3:GetVec2() - return { x = self.x, y = self.z } -end - - ---- Return the x coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #number The x coodinate. -function POINT_VEC3:GetX() - return self.x -end - ---- Return the y coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #number The y coodinate. -function POINT_VEC3:GetY() - return self.y -end - ---- Return the z coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #number The z coodinate. -function POINT_VEC3:GetZ() - return self.z -end - ---- Set the x coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number x The x coordinate. --- @return #POINT_VEC3 -function POINT_VEC3:SetX( x ) - self.x = x - return self -end - ---- Set the y coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number y The y coordinate. --- @return #POINT_VEC3 -function POINT_VEC3:SetY( y ) - self.y = y - return self -end - ---- Set the z coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number z The z coordinate. --- @return #POINT_VEC3 -function POINT_VEC3:SetZ( z ) - self.z = z - return self -end - ---- Add to the x coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number x The x coordinate value to add to the current x coodinate. --- @return #POINT_VEC3 -function POINT_VEC3:AddX( x ) - self.x = self.x + x - return self -end - ---- Add to the y coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number y The y coordinate value to add to the current y coodinate. --- @return #POINT_VEC3 -function POINT_VEC3:AddY( y ) - self.y = self.y + y - return self -end - ---- Add to the z coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number z The z coordinate value to add to the current z coodinate. --- @return #POINT_VEC3 -function POINT_VEC3:AddZ( z ) - self.z = self.z +z - return self -end - ---- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance OuterRadius --- @param Dcs.DCSTypes#Distance InnerRadius --- @return Dcs.DCSTypes#Vec2 Vec2 -function POINT_VEC3:GetRandomVec2InRadius( OuterRadius, InnerRadius ) - self:F2( { OuterRadius, InnerRadius } ) - - local Theta = 2 * math.pi * math.random() - local Radials = math.random() + math.random() - if Radials > 1 then - Radials = 2 - Radials - end - - local RadialMultiplier - if InnerRadius and InnerRadius <= OuterRadius then - RadialMultiplier = ( OuterRadius - InnerRadius ) * Radials + InnerRadius - else - RadialMultiplier = OuterRadius * Radials - end - - local RandomVec2 - if OuterRadius > 0 then - RandomVec2 = { x = math.cos( Theta ) * RadialMultiplier + self:GetX(), y = math.sin( Theta ) * RadialMultiplier + self:GetZ() } - else - RandomVec2 = { x = self:GetX(), y = self:GetZ() } - end - - return RandomVec2 -end - ---- Return a random POINT_VEC2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance OuterRadius --- @param Dcs.DCSTypes#Distance InnerRadius --- @return #POINT_VEC2 -function POINT_VEC3:GetRandomPointVec2InRadius( OuterRadius, InnerRadius ) - self:F2( { OuterRadius, InnerRadius } ) - - return POINT_VEC2:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) ) -end - ---- Return a random Vec3 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance OuterRadius --- @param Dcs.DCSTypes#Distance InnerRadius --- @return Dcs.DCSTypes#Vec3 Vec3 -function POINT_VEC3:GetRandomVec3InRadius( OuterRadius, InnerRadius ) - - local RandomVec2 = self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) - local y = self:GetY() + math.random( InnerRadius, OuterRadius ) - local RandomVec3 = { x = RandomVec2.x, y = y, z = RandomVec2.y } - - return RandomVec3 -end - ---- Return a random POINT_VEC3 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance OuterRadius --- @param Dcs.DCSTypes#Distance InnerRadius --- @return #POINT_VEC3 -function POINT_VEC3:GetRandomPointVec3InRadius( OuterRadius, InnerRadius ) - - return POINT_VEC3:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) ) -end - - ---- Return a direction vector Vec3 from POINT_VEC3 to the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3. --- @return Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. -function POINT_VEC3:GetDirectionVec3( TargetPointVec3 ) - return { x = TargetPointVec3:GetX() - self:GetX(), y = TargetPointVec3:GetY() - self:GetY(), z = TargetPointVec3:GetZ() - self:GetZ() } -end - ---- Get a correction in radians of the real magnetic north of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #number CorrectionRadians The correction in radians. -function POINT_VEC3:GetNorthCorrectionRadians() - local TargetVec3 = self:GetVec3() - local lat, lon = coord.LOtoLL(TargetVec3) - local north_posit = coord.LLtoLO(lat + 1, lon) - return math.atan2( north_posit.z - TargetVec3.z, north_posit.x - TargetVec3.x ) -end - - ---- Return a direction in radians from the POINT_VEC3 using a direction vector in Vec3 format. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. --- @return #number DirectionRadians The direction in radians. -function POINT_VEC3:GetDirectionRadians( DirectionVec3 ) - local DirectionRadians = math.atan2( DirectionVec3.z, DirectionVec3.x ) - --DirectionRadians = DirectionRadians + self:GetNorthCorrectionRadians() - if DirectionRadians < 0 then - DirectionRadians = DirectionRadians + 2 * math.pi -- put dir in range of 0 to 2*pi ( the full circle ) - end - return DirectionRadians -end - ---- Return the 2D distance in meters between the target POINT_VEC3 and the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3. --- @return Dcs.DCSTypes#Distance Distance The distance in meters. -function POINT_VEC3:Get2DDistance( TargetPointVec3 ) - local TargetVec3 = TargetPointVec3:GetVec3() - local SourceVec3 = self:GetVec3() - return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5 -end - ---- Return the 3D distance in meters between the target POINT_VEC3 and the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3. --- @return Dcs.DCSTypes#Distance Distance The distance in meters. -function POINT_VEC3:Get3DDistance( TargetPointVec3 ) - local TargetVec3 = TargetPointVec3:GetVec3() - local SourceVec3 = self:GetVec3() - return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5 -end - ---- Provides a Bearing / Range string --- @param #POINT_VEC3 self --- @param #number AngleRadians The angle in randians --- @param #number Distance The distance --- @return #string The BR Text -function POINT_VEC3:ToStringBR( AngleRadians, Distance ) - - AngleRadians = UTILS.Round( UTILS.ToDegree( AngleRadians ), 0 ) - if self:IsMetric() then - Distance = UTILS.Round( Distance / 1000, 2 ) - else - Distance = UTILS.Round( UTILS.MetersToNM( Distance ), 2 ) - end - - local s = string.format( '%03d', AngleRadians ) .. ' for ' .. Distance - - s = s .. self:GetAltitudeText() -- When the POINT is a VEC2, there will be no altitude shown. - - return s -end - ---- Provides a Bearing / Range string --- @param #POINT_VEC3 self --- @param #number AngleRadians The angle in randians --- @param #number Distance The distance --- @return #string The BR Text -function POINT_VEC3:ToStringLL( acc, DMS ) - - acc = acc or 3 - local lat, lon = coord.LOtoLL( self:GetVec3() ) - return UTILS.tostringLL(lat, lon, acc, DMS) -end - ---- Return the altitude text of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #string Altitude text. -function POINT_VEC3:GetAltitudeText() - if self:IsMetric() then - return ' at ' .. UTILS.Round( self:GetY(), 0 ) - else - return ' at ' .. UTILS.Round( UTILS.MetersToFeet( self:GetY() ), 0 ) - end -end - ---- Return a BR string from a POINT_VEC3 to the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3. --- @return #string The BR text. -function POINT_VEC3:GetBRText( TargetPointVec3 ) - local DirectionVec3 = self:GetDirectionVec3( TargetPointVec3 ) - local AngleRadians = self:GetDirectionRadians( DirectionVec3 ) - local Distance = self:Get2DDistance( TargetPointVec3 ) - return self:ToStringBR( AngleRadians, Distance ) -end - ---- Sets the POINT_VEC3 metric or NM. --- @param #POINT_VEC3 self --- @param #boolean Metric true means metric, false means NM. -function POINT_VEC3:SetMetric( Metric ) - self.Metric = Metric -end - ---- Gets if the POINT_VEC3 is metric or NM. --- @param #POINT_VEC3 self --- @return #boolean Metric true means metric, false means NM. -function POINT_VEC3:IsMetric() - return self.Metric -end - ---- Add a Distance in meters from the POINT_VEC3 horizontal plane, with the given angle, and calculate the new POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters. --- @param Dcs.DCSTypes#Angle Angle The Angle in degrees. --- @return #POINT_VEC3 The new calculated POINT_VEC3. -function POINT_VEC3:Translate( Distance, Angle ) - local SX = self:GetX() - local SZ = self:GetZ() - local Radians = Angle / 180 * math.pi - local TX = Distance * math.cos( Radians ) + SX - local TZ = Distance * math.sin( Radians ) + SZ - - return POINT_VEC3:New( TX, self:GetY(), TZ ) -end - - - ---- Build an air type route point. --- @param #POINT_VEC3 self --- @param #POINT_VEC3.RoutePointAltType AltType The altitude type. --- @param #POINT_VEC3.RoutePointType Type The route point type. --- @param #POINT_VEC3.RoutePointAction Action The route point action. --- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. --- @param #boolean SpeedLocked true means the speed is locked. --- @return #table The route point. -function POINT_VEC3:RoutePointAir( AltType, Type, Action, Speed, SpeedLocked ) - self:F2( { AltType, Type, Action, Speed, SpeedLocked } ) - - local RoutePoint = {} - RoutePoint.x = self.x - RoutePoint.y = self.z - RoutePoint.alt = self.y - RoutePoint.alt_type = AltType - - RoutePoint.type = Type - RoutePoint.action = Action - - RoutePoint.speed = Speed / 3.6 - RoutePoint.speed_locked = true - --- ["task"] = --- { --- ["id"] = "ComboTask", --- ["params"] = --- { --- ["tasks"] = --- { --- }, -- end of ["tasks"] --- }, -- end of ["params"] --- }, -- end of ["task"] - - - RoutePoint.task = {} - RoutePoint.task.id = "ComboTask" - RoutePoint.task.params = {} - RoutePoint.task.params.tasks = {} - - - return RoutePoint -end - ---- Build an ground type route point. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Speed Speed Speed in km/h. --- @param #POINT_VEC3.RoutePointAction Formation The route point Formation. --- @return #table The route point. -function POINT_VEC3:RoutePointGround( Speed, Formation ) - self:F2( { Formation, Speed } ) - - local RoutePoint = {} - RoutePoint.x = self.x - RoutePoint.y = self.z - - RoutePoint.action = Formation or "" - - - RoutePoint.speed = Speed / 3.6 - RoutePoint.speed_locked = true - --- ["task"] = --- { --- ["id"] = "ComboTask", --- ["params"] = --- { --- ["tasks"] = --- { --- }, -- end of ["tasks"] --- }, -- end of ["params"] --- }, -- end of ["task"] - - - RoutePoint.task = {} - RoutePoint.task.id = "ComboTask" - RoutePoint.task.params = {} - RoutePoint.task.params.tasks = {} - - - return RoutePoint -end - ---- Creates an explosion at the point of a certain intensity. --- @param #POINT_VEC3 self --- @param #number ExplosionIntensity -function POINT_VEC3:Explosion( ExplosionIntensity ) - self:F2( { ExplosionIntensity } ) - trigger.action.explosion( self:GetVec3(), ExplosionIntensity ) -end - ---- Creates an illumination bomb at the point. --- @param #POINT_VEC3 self -function POINT_VEC3:IlluminationBomb() - self:F2() - trigger.action.illuminationBomb( self:GetVec3() ) -end - - ---- Smokes the point in a color. --- @param #POINT_VEC3 self --- @param Utilities.Utils#SMOKECOLOR SmokeColor -function POINT_VEC3:Smoke( SmokeColor ) - self:F2( { SmokeColor } ) - trigger.action.smoke( self:GetVec3(), SmokeColor ) -end - ---- Smoke the POINT_VEC3 Green. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeGreen() - self:F2() - self:Smoke( SMOKECOLOR.Green ) -end - ---- Smoke the POINT_VEC3 Red. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeRed() - self:F2() - self:Smoke( SMOKECOLOR.Red ) -end - ---- Smoke the POINT_VEC3 White. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeWhite() - self:F2() - self:Smoke( SMOKECOLOR.White ) -end - ---- Smoke the POINT_VEC3 Orange. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeOrange() - self:F2() - self:Smoke( SMOKECOLOR.Orange ) -end - ---- Smoke the POINT_VEC3 Blue. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeBlue() - self:F2() - self:Smoke( SMOKECOLOR.Blue ) -end - ---- Flares the point in a color. --- @param #POINT_VEC3 self --- @param Utilities.Utils#FLARECOLOR FlareColor --- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -function POINT_VEC3:Flare( FlareColor, Azimuth ) - self:F2( { FlareColor } ) - trigger.action.signalFlare( self:GetVec3(), FlareColor, Azimuth and Azimuth or 0 ) -end - ---- Flare the POINT_VEC3 White. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -function POINT_VEC3:FlareWhite( Azimuth ) - self:F2( Azimuth ) - self:Flare( FLARECOLOR.White, Azimuth ) -end - ---- Flare the POINT_VEC3 Yellow. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -function POINT_VEC3:FlareYellow( Azimuth ) - self:F2( Azimuth ) - self:Flare( FLARECOLOR.Yellow, Azimuth ) -end - ---- Flare the POINT_VEC3 Green. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -function POINT_VEC3:FlareGreen( Azimuth ) - self:F2( Azimuth ) - self:Flare( FLARECOLOR.Green, Azimuth ) -end - ---- Flare the POINT_VEC3 Red. --- @param #POINT_VEC3 self -function POINT_VEC3:FlareRed( Azimuth ) - self:F2( Azimuth ) - self:Flare( FLARECOLOR.Red, Azimuth ) -end - -end - -do -- POINT_VEC2 - - - ---- POINT_VEC2 constructor. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North. --- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing to the Right. --- @param Dcs.DCSTypes#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height. --- @return Core.Point#POINT_VEC2 -function POINT_VEC2:New( x, y, LandHeightAdd ) - - local LandHeight = land.getHeight( { ["x"] = x, ["y"] = y } ) - - LandHeightAdd = LandHeightAdd or 0 - LandHeight = LandHeight + LandHeightAdd - - self = BASE:Inherit( self, POINT_VEC3:New( x, LandHeight, y ) ) - self:F2( self ) - - return self -end - ---- Create a new POINT_VEC2 object from Vec2 coordinates. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. --- @return Core.Point#POINT_VEC2 self -function POINT_VEC2:NewFromVec2( Vec2, LandHeightAdd ) - - local LandHeight = land.getHeight( Vec2 ) - - LandHeightAdd = LandHeightAdd or 0 - LandHeight = LandHeight + LandHeightAdd - - self = BASE:Inherit( self, POINT_VEC3:New( Vec2.x, LandHeight, Vec2.y ) ) - self:F2( self ) - - return self -end - ---- Create a new POINT_VEC2 object from Vec3 coordinates. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point. --- @return Core.Point#POINT_VEC2 self -function POINT_VEC2:NewFromVec3( Vec3 ) - - local self = BASE:Inherit( self, BASE:New() ) - local Vec2 = { x = Vec3.x, y = Vec3.z } - - local LandHeight = land.getHeight( Vec2 ) - - self = BASE:Inherit( self, POINT_VEC3:New( Vec2.x, LandHeight, Vec2.y ) ) - self:F2( self ) - - return self -end - ---- Return the x coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @return #number The x coodinate. -function POINT_VEC2:GetX() - return self.x -end - ---- Return the y coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @return #number The y coodinate. -function POINT_VEC2:GetY() - return self.z -end - ---- Return the altitude (height) of the land at the POINT_VEC2. --- @param #POINT_VEC2 self --- @return #number The land altitude. -function POINT_VEC2:GetAlt() - return land.getHeight( { x = self.x, y = self.z } ) -end - ---- Return Return the Lat(itude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.x). --- @param #POINT_VEC2 self --- @return #number The x coodinate. -function POINT_VEC2:GetLat() - return self.x -end - ---- Return the Lon(gitude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.z). --- @param #POINT_VEC2 self --- @return #number The y coodinate. -function POINT_VEC2:GetLon() - return self.z -end - ---- Set the x coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number x The x coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:SetX( x ) - self.x = x - return self -end - ---- Set the y coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number y The y coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:SetY( y ) - self.z = y - return self -end - ---- Set the Lat(itude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.x). --- @param #POINT_VEC2 self --- @param #number x The x coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:SetLat( x ) - self.x = x - return self -end - ---- Set the altitude of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number Altitude The land altitude. If nothing (nil) is given, then the current land altitude is set. --- @return #POINT_VEC2 -function POINT_VEC2:SetAlt( Altitude ) - self.y = Altitude or land.getHeight( { x = self.x, y = self.z } ) - return self -end - ---- Set the Lon(gitude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.z). --- @param #POINT_VEC2 self --- @param #number y The y coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:SetLon( z ) - self.z = z - return self -end - ---- Add to the x coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number x The x coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:AddX( x ) - self.x = self.x + x - return self -end - ---- Add to the y coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number y The y coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:AddY( y ) - self.z = self.z + y - return self -end - ---- Add to the current land height an altitude. --- @param #POINT_VEC2 self --- @param #number Altitude The Altitude to add. If nothing (nil) is given, then the current land altitude is set. --- @return #POINT_VEC2 -function POINT_VEC2:AddAlt( Altitude ) - self.y = land.getHeight( { x = self.x, y = self.z } ) + Altitude or 0 - return self -end - - - ---- Calculate the distance from a reference @{#POINT_VEC2}. --- @param #POINT_VEC2 self --- @param #POINT_VEC2 PointVec2Reference The reference @{#POINT_VEC2}. --- @return Dcs.DCSTypes#Distance The distance from the reference @{#POINT_VEC2} in meters. -function POINT_VEC2:DistanceFromPointVec2( PointVec2Reference ) - self:F2( PointVec2Reference ) - - local Distance = ( ( PointVec2Reference:GetX() - self:GetX() ) ^ 2 + ( PointVec2Reference:GetY() - self:GetY() ) ^2 ) ^0.5 - - self:T2( Distance ) - return Distance -end - ---- Calculate the distance from a reference @{DCSTypes#Vec2}. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Vec2 Vec2Reference The reference @{DCSTypes#Vec2}. --- @return Dcs.DCSTypes#Distance The distance from the reference @{DCSTypes#Vec2} in meters. -function POINT_VEC2:DistanceFromVec2( Vec2Reference ) - self:F2( Vec2Reference ) - - local Distance = ( ( Vec2Reference.x - self:GetX() ) ^ 2 + ( Vec2Reference.y - self:GetY() ) ^2 ) ^0.5 - - self:T2( Distance ) - return Distance -end - - ---- Return no text for the altitude of the POINT_VEC2. --- @param #POINT_VEC2 self --- @return #string Empty string. -function POINT_VEC2:GetAltitudeText() - return '' -end - ---- Add a Distance in meters from the POINT_VEC2 orthonormal plane, with the given angle, and calculate the new POINT_VEC2. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters. --- @param Dcs.DCSTypes#Angle Angle The Angle in degrees. --- @return #POINT_VEC2 The new calculated POINT_VEC2. -function POINT_VEC2:Translate( Distance, Angle ) - local SX = self:GetX() - local SY = self:GetY() - local Radians = Angle / 180 * math.pi - local TX = Distance * math.cos( Radians ) + SX - local TY = Distance * math.sin( Radians ) + SY - - return POINT_VEC2:New( TX, TY ) -end - -end - - ---- **Core** - MESSAGE class takes are of the **real-time notifications** and **messages to players** during a simulation. --- --- ![Banner Image](..\Presentations\MESSAGE\Dia1.JPG) --- --- === --- --- # 1) @{Message#MESSAGE} class, extends @{Base#BASE} --- --- Message System to display Messages to Clients, Coalitions or All. --- Messages are shown on the display panel for an amount of seconds, and will then disappear. --- Messages can contain a category which is indicating the category of the message. --- --- ## 1.1) MESSAGE construction --- --- Messages are created with @{Message#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet. --- To send messages, you need to use the To functions. --- --- ## 1.2) Send messages to an audience --- --- Messages are sent: --- --- * To a @{Client} using @{Message#MESSAGE.ToClient}(). --- * To a @{Group} using @{Message#MESSAGE.ToGroup}() --- * To a coalition using @{Message#MESSAGE.ToCoalition}(). --- * To the red coalition using @{Message#MESSAGE.ToRed}(). --- * To the blue coalition using @{Message#MESSAGE.ToBlue}(). --- * To all Players using @{Message#MESSAGE.ToAll}(). --- --- ## 1.3) Send conditionally to an audience --- --- Messages can be sent conditionally to an audience (when a condition is true): --- --- * To all players using @{Message#MESSAGE.ToAllIf}(). --- * To a coalition using @{Message#MESSAGE.ToCoalitionIf}(). --- --- --- @module Message - ---- The MESSAGE class --- @type MESSAGE --- @extends Core.Base#BASE -MESSAGE = { - ClassName = "MESSAGE", - MessageCategory = 0, - MessageID = 0, -} - - ---- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients. --- @param self --- @param #string MessageText is the text of the Message. --- @param #number MessageDuration is a number in seconds of how long the MESSAGE should be shown on the display panel. --- @param #string MessageCategory (optional) is a string expressing the "category" of the Message. The category will be shown as the first text in the message followed by a ": ". --- @return #MESSAGE --- @usage --- -- Create a series of new Messages. --- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score". --- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win". --- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score". --- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score". --- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" ) --- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" ) --- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" ) --- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score") -function MESSAGE:New( MessageText, MessageDuration, MessageCategory ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( { MessageText, MessageDuration, MessageCategory } ) - - -- When no MessageCategory is given, we don't show it as a title... - if MessageCategory and MessageCategory ~= "" then - if MessageCategory:sub(-1) ~= "\n" then - self.MessageCategory = MessageCategory .. ": " - else - self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n" - end - else - self.MessageCategory = "" - end - - self.MessageDuration = MessageDuration or 5 - self.MessageTime = timer.getTime() - self.MessageText = MessageText - - self.MessageSent = false - self.MessageGroup = false - self.MessageCoalition = false - - return self -end - ---- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player". --- @param #MESSAGE self --- @param Wrapper.Client#CLIENT Client is the Group of the Client. --- @return #MESSAGE --- @usage --- -- Send the 2 messages created with the @{New} method to the Client Group. --- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1. --- ClientGroup = Group.getByName( "ClientGroup" ) --- --- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup ) --- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup ) --- or --- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup ) --- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup ) --- or --- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ) --- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ) --- MessageClient1:ToClient( ClientGroup ) --- MessageClient2:ToClient( ClientGroup ) -function MESSAGE:ToClient( Client ) - self:F( Client ) - - if Client and Client:GetClientGroupID() then - - local ClientGroupID = Client:GetClientGroupID() - self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) - trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration ) - end - - return self -end - ---- Sends a MESSAGE to a Group. --- @param #MESSAGE self --- @param Wrapper.Group#GROUP Group is the Group. --- @return #MESSAGE -function MESSAGE:ToGroup( Group ) - self:F( Group.GroupName ) - - if Group then - - self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) - trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration ) - end - - return self -end ---- Sends a MESSAGE to the Blue coalition. --- @param #MESSAGE self --- @return #MESSAGE --- @usage --- -- Send a message created with the @{New} method to the BLUE coalition. --- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue() --- or --- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue() --- or --- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) --- MessageBLUE:ToBlue() -function MESSAGE:ToBlue() - self:F() - - self:ToCoalition( coalition.side.BLUE ) - - return self -end - ---- Sends a MESSAGE to the Red Coalition. --- @param #MESSAGE self --- @return #MESSAGE --- @usage --- -- Send a message created with the @{New} method to the RED coalition. --- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed() --- or --- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed() --- or --- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) --- MessageRED:ToRed() -function MESSAGE:ToRed( ) - self:F() - - self:ToCoalition( coalition.side.RED ) - - return self -end - ---- Sends a MESSAGE to a Coalition. --- @param #MESSAGE self --- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}. --- @return #MESSAGE --- @usage --- -- Send a message created with the @{New} method to the RED coalition. --- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED ) --- or --- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED ) --- or --- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) --- MessageRED:ToCoalition( coalition.side.RED ) -function MESSAGE:ToCoalition( CoalitionSide ) - self:F( CoalitionSide ) - - if CoalitionSide then - self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) - trigger.action.outTextForCoalition( CoalitionSide, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration ) - end - - return self -end - ---- Sends a MESSAGE to a Coalition if the given Condition is true. --- @param #MESSAGE self --- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}. --- @return #MESSAGE -function MESSAGE:ToCoalitionIf( CoalitionSide, Condition ) - self:F( CoalitionSide ) - - if Condition and Condition == true then - self:ToCoalition( CoalitionSide ) - end - - return self -end - ---- Sends a MESSAGE to all players. --- @param #MESSAGE self --- @return #MESSAGE --- @usage --- -- Send a message created to all players. --- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll() --- or --- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll() --- or --- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ) --- MessageAll:ToAll() -function MESSAGE:ToAll() - self:F() - - self:ToCoalition( coalition.side.RED ) - self:ToCoalition( coalition.side.BLUE ) - - return self -end - - ---- Sends a MESSAGE to all players if the given Condition is true. --- @param #MESSAGE self --- @return #MESSAGE -function MESSAGE:ToAllIf( Condition ) - - if Condition and Condition == true then - self:ToCoalition( coalition.side.RED ) - self:ToCoalition( coalition.side.BLUE ) - end - - return self -end ---- **Core** - The **FSM** (**F**inite **S**tate **M**achine) class and derived **FSM\_** classes --- are design patterns allowing efficient (long-lasting) processes and workflows. --- --- ![Banner Image](..\Presentations\FSM\Dia1.JPG) --- --- === --- --- A FSM can only be in one of a finite number of states. --- The machine is in only one state at a time; the state it is in at any given time is called the **current state**. --- It can change from one state to another when initiated by an **__internal__ or __external__ triggering event**, which is called a **transition**. --- An **FSM implementation** is defined by **a list of its states**, **its initial state**, and **the triggering events** for **each possible transition**. --- An FSM implementation is composed out of **two parts**, a set of **state transition rules**, and an implementation set of **state transition handlers**, implementing those transitions. --- --- The FSM class supports a **hierarchical implementation of a Finite State Machine**, --- that is, it allows to **embed existing FSM implementations in a master FSM**. --- FSM hierarchies allow for efficient FSM re-use, **not having to re-invent the wheel every time again** when designing complex processes. --- --- ![Workflow Example](..\Presentations\FSM\Dia2.JPG) --- --- The above diagram shows a graphical representation of a FSM implementation for a **Task**, which guides a Human towards a Zone, --- orders him to destroy x targets and account the results. --- Other examples of ready made FSM could be: --- --- * route a plane to a zone flown by a human --- * detect targets by an AI and report to humans --- * account for destroyed targets by human players --- * handle AI infantry to deploy from or embark to a helicopter or airplane or vehicle --- * let an AI patrol a zone --- --- The **MOOSE framework** uses extensively the FSM class and derived FSM\_ classes, --- because **the goal of MOOSE is to simplify mission design complexity for mission building**. --- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes. --- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used, --- and tailored** by mission designers through **the implementation of Transition Handlers**. --- Each of these FSM implementation classes start either with: --- --- * an acronym **AI\_**, which indicates an FSM implementation directing **AI controlled** @{GROUP} and/or @{UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class. --- * an acronym **TASK\_**, which indicates an FSM implementation executing a @{TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class. --- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{TASK}, seated in a @{CLIENT} (slot) or a @{UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class. --- --- Detailed explanations and API specifics are further below clarified and FSM derived class specifics are described in those class documentation sections. --- --- ##__Dislaimer:__ --- The FSM class development is based on a finite state machine implementation made by Conroy Kyle. --- The state machine can be found on [github](https://github.com/kyleconroy/lua-state-machine) --- I've reworked this development (taken the concept), and created a **hierarchical state machine** out of it, embedded within the DCS simulator. --- Additionally, I've added extendability and created an API that allows seamless FSM implementation. --- --- The following derived classes are available in the MOOSE framework, that implement a specialised form of a FSM: --- --- * @{#FSM_TASK}: Models Finite State Machines for @{Task}s. --- * @{#FSM_PROCESS}: Models Finite State Machines for @{Task} actions, which control @{Client}s. --- * @{#FSM_CONTROLLABLE}: Models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s. --- * @{#FSM_SET}: Models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here --- for multiple objects or the position of the state machine in the process. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) --- YYYY-MM-DD: CLASS:**NewFunction( Params )** added --- --- Hereby the change log: --- --- * 2016-12-18: Released. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * [**Pikey**](https://forums.eagle.ru/member.php?u=62835): Review of documentation & advice for improvements. --- --- ### Authors: --- --- * [**FlightControl**](https://forums.eagle.ru/member.php?u=89536): Design & Programming & documentation. --- --- @module Fsm - -do -- FSM - - --- FSM class - --- @type FSM - -- @extends Core.Base#BASE - - - --- # 1) FSM class, extends @{Base#BASE} - -- - -- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG) - -- - -- The FSM class is the base class of all FSM\_ derived classes. It implements the main functionality to define and execute Finite State Machines. - -- The derived FSM\_ classes extend the Finite State Machine functionality to run a workflow process for a specific purpose or component. - -- - -- Finite State Machines have **Transition Rules**, **Transition Handlers** and **Event Triggers**. - -- - -- The **Transition Rules** define the "Process Flow Boundaries", that is, - -- the path that can be followed hopping from state to state upon triggered events. - -- If an event is triggered, and there is no valid path found for that event, - -- an error will be raised and the FSM will stop functioning. - -- - -- The **Transition Handlers** are special methods that can be defined by the mission designer, following a defined syntax. - -- If the FSM object finds a method of such a handler, then the method will be called by the FSM, passing specific parameters. - -- The method can then define its own custom logic to implement the FSM workflow, and to conduct other actions. - -- - -- The **Event Triggers** are methods that are defined by the FSM, which the mission designer can use to implement the workflow. - -- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine. - -- - -- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation. - -- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**. - -- - -- ## 1.1) FSM Linear Transitions - -- - -- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**. - -- The Lineair transition rule evaluation will always be done from the **current state** of the FSM. - -- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop. - -- - -- ### 1.1.1) FSM Transition Rules - -- - -- The FSM has transition rules that it follows and validates, as it walks the process. - -- These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event. - -- - -- The method @{#FSM.AddTransition}() specifies a new possible Transition Rule for the FSM. - -- - -- The initial state can be defined using the method @{#FSM.SetStartState}(). The default start state of an FSM is "None". - -- - -- Find below an example of a Linear Transition Rule definition for an FSM. - -- - -- local Fsm3Switch = FSM:New() -- #FsmDemo - -- FsmSwitch:SetStartState( "Off" ) - -- FsmSwitch:AddTransition( "Off", "SwitchOn", "On" ) - -- FsmSwitch:AddTransition( "Off", "SwitchMiddle", "Middle" ) - -- FsmSwitch:AddTransition( "On", "SwitchOff", "Off" ) - -- FsmSwitch:AddTransition( "Middle", "SwitchOff", "Off" ) - -- - -- The above code snippet models a 3-way switch Linear Transition: - -- - -- * It can be switched **On** by triggering event **SwitchOn**. - -- * It can be switched to the **Middle** position, by triggering event **SwitchMiddle**. - -- * It can be switched **Off** by triggering event **SwitchOff**. - -- * Note that once the Switch is **On** or **Middle**, it can only be switched **Off**. - -- - -- ### Some additional comments: - -- - -- Note that Linear Transition Rules **can be declared in a few variations**: - -- - -- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**. - -- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM. - -- - -- The below code snippet shows how the two last lines can be rewritten and consensed. - -- - -- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" ) - -- - -- ### 1.1.2) Transition Handling - -- - -- ![Transition Handlers](..\Presentations\FSM\Dia4.JPG) - -- - -- An FSM transitions in **4 moments** when an Event is being triggered and processed. - -- The mission designer can define for each moment specific logic within methods implementations following a defined API syntax. - -- These methods define the flow of the FSM process; because in those methods the FSM Internal Events will be triggered. - -- - -- * To handle **State** transition moments, create methods starting with OnLeave or OnEnter concatenated with the State name. - -- * To handle **Event** transition moments, create methods starting with OnBefore or OnAfter concatenated with the Event name. - -- - -- **The OnLeave and OnBefore transition methods may return false, which will cancel the transition!** - -- - -- Transition Handler methods need to follow the above specified naming convention, but are also passed parameters from the FSM. - -- These parameters are on the correct order: From, Event, To: - -- - -- * From = A string containing the From state. - -- * Event = A string containing the Event name that was triggered. - -- * To = A string containing the To state. - -- - -- On top, each of these methods can have a variable amount of parameters passed. See the example in section [1.1.3](#1.1.3\)-event-triggers). - -- - -- ### 1.1.3) Event Triggers - -- - -- ![Event Triggers](..\Presentations\FSM\Dia5.JPG) - -- - -- The FSM creates for each Event two **Event Trigger methods**. - -- There are two modes how Events can be triggered, which is **synchronous** and **asynchronous**: - -- - -- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**. - -- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__. - -- - -- The destinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time. - -- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact. - -- - -- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering. - -- - -- function FSM:OnAfterEvent( From, Event, To, Amount ) - -- self:T( { Amount = Amount } ) - -- end - -- - -- local Amount = 1 - -- FSM:__Event( 5, Amount ) - -- - -- Amount = Amount + 1 - -- FSM:Event( Text, Amount ) - -- - -- In this example, the **:OnAfterEvent**() Transition Handler implementation will get called when **Event** is being triggered. - -- Before we go into more detail, let's look at the last 4 lines of the example. - -- The last line triggers synchronously the **Event**, and passes Amount as a parameter. - -- The 3rd last line of the example triggers asynchronously **Event**. - -- Event will be processed after 5 seconds, and Amount is given as a parameter. - -- - -- The output of this little code fragment will be: - -- - -- * Amount = 2 - -- * Amount = 2 - -- - -- Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing! - -- - -- ### 1.1.4) Linear Transition Example - -- - -- This example is fully implemented in the MOOSE test mission on GITHUB: [FSM-100 - Transition Explanation](https://github.com/FlightControl-Master/MOOSE/blob/master/Moose%20Test%20Missions/FSM%20-%20Finite%20State%20Machine/FSM-100%20-%20Transition%20Explanation/FSM-100%20-%20Transition%20Explanation.lua) - -- - -- It models a unit standing still near Batumi, and flaring every 5 seconds while switching between a Green flare and a Red flare. - -- The purpose of this example is not to show how exciting flaring is, but it demonstrates how a Linear Transition FSM can be build. - -- Have a look at the source code. The source code is also further explained below in this section. - -- - -- The example creates a new FsmDemo object from class FSM. - -- It will set the start state of FsmDemo to state **Green**. - -- Two Linear Transition Rules are created, where upon the event **Switch**, - -- the FsmDemo will transition from state **Green** to **Red** and from **Red** back to **Green**. - -- - -- ![Transition Example](..\Presentations\FSM\Dia6.JPG) - -- - -- local FsmDemo = FSM:New() -- #FsmDemo - -- FsmDemo:SetStartState( "Green" ) - -- FsmDemo:AddTransition( "Green", "Switch", "Red" ) - -- FsmDemo:AddTransition( "Red", "Switch", "Green" ) - -- - -- In the above example, the FsmDemo could flare every 5 seconds a Green or a Red flare into the air. - -- The next code implements this through the event handling method **OnAfterSwitch**. - -- - -- ![Transition Flow](..\Presentations\FSM\Dia7.JPG) - -- - -- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) - -- self:T( { From, Event, To, FsmUnit } ) - -- - -- if From == "Green" then - -- FsmUnit:Flare(FLARECOLOR.Green) - -- else - -- if From == "Red" then - -- FsmUnit:Flare(FLARECOLOR.Red) - -- end - -- end - -- self:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. - -- end - -- - -- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the first Switch event to happen in 5 seconds. - -- - -- The OnAfterSwitch implements a loop. The last line of the code fragment triggers the Switch Event within 5 seconds. - -- Upon the event execution (after 5 seconds), the OnAfterSwitch method is called of FsmDemo (cfr. the double point notation!!! ":"). - -- The OnAfterSwitch method receives from the FSM the 3 transition parameter details ( From, Event, To ), - -- and one additional parameter that was given when the event was triggered, which is in this case the Unit that is used within OnSwitchAfter. - -- - -- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) - -- - -- For debugging reasons the received parameters are traced within the DCS.log. - -- - -- self:T( { From, Event, To, FsmUnit } ) - -- - -- The method will check if the From state received is either "Green" or "Red" and will flare the respective color from the FsmUnit. - -- - -- if From == "Green" then - -- FsmUnit:Flare(FLARECOLOR.Green) - -- else - -- if From == "Red" then - -- FsmUnit:Flare(FLARECOLOR.Red) - -- end - -- end - -- - -- It is important that the Switch event is again triggered, otherwise, the FsmDemo would stop working after having the first Event being handled. - -- - -- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. - -- - -- The below code fragment extends the FsmDemo, demonstrating multiple **From states declared as a table**, adding a **Linear Transition Rule**. - -- The new event **Stop** will cancel the Switching process. - -- The transition for event Stop can be executed if the current state of the FSM is either "Red" or "Green". - -- - -- local FsmDemo = FSM:New() -- #FsmDemo - -- FsmDemo:SetStartState( "Green" ) - -- FsmDemo:AddTransition( "Green", "Switch", "Red" ) - -- FsmDemo:AddTransition( "Red", "Switch", "Green" ) - -- FsmDemo:AddTransition( { "Red", "Green" }, "Stop", "Stopped" ) - -- - -- The transition for event Stop can also be simplified, as any current state of the FSM is valid. - -- - -- FsmDemo:AddTransition( "*", "Stop", "Stopped" ) - -- - -- So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped. - -- And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt. - -- - -- ## 1.5) FSM Hierarchical Transitions - -- - -- Hierarchical Transitions allow to re-use readily available and implemented FSMs. - -- This becomes in very useful for mission building, where mission designers build complex processes and workflows, - -- combining smaller FSMs to one single FSM. - -- - -- The FSM can embed **Sub-FSMs** that will execute and return **multiple possible Return (End) States**. - -- Depending upon **which state is returned**, the main FSM can continue the flow **triggering specific events**. - -- - -- The method @{#FSM.AddProcess}() adds a new Sub-FSM to the FSM. - -- - -- === - -- - -- @field #FSM FSM - -- - FSM = { - ClassName = "FSM", - } - - --- Creates a new FSM object. - -- @param #FSM self - -- @return #FSM - function FSM:New( FsmT ) - - -- Inherits from BASE - self = BASE:Inherit( self, BASE:New() ) - - self.options = options or {} - self.options.subs = self.options.subs or {} - self.current = self.options.initial or 'none' - self.Events = {} - self.subs = {} - self.endstates = {} - - self.Scores = {} - - self._StartState = "none" - self._Transitions = {} - self._Processes = {} - self._EndStates = {} - self._Scores = {} - self._EventSchedules = {} - - self.CallScheduler = SCHEDULER:New( self ) - - - return self - end - - - --- Sets the start state of the FSM. - -- @param #FSM self - -- @param #string State A string defining the start state. - function FSM:SetStartState( State ) - - self._StartState = State - self.current = State - end - - - --- Returns the start state of the FSM. - -- @param #FSM self - -- @return #string A string containing the start state. - function FSM:GetStartState() - - return self._StartState or {} - end - - --- Add a new transition rule to the FSM. - -- A transition rule defines when and if the FSM can transition from a state towards another state upon a triggered event. - -- @param #FSM self - -- @param #table From Can contain a string indicating the From state or a table of strings containing multiple From states. - -- @param #string Event The Event name. - -- @param #string To The To state. - function FSM:AddTransition( From, Event, To ) - - local Transition = {} - Transition.From = From - Transition.Event = Event - Transition.To = To - - self:T( Transition ) - - self._Transitions[Transition] = Transition - self:_eventmap( self.Events, Transition ) - end - - - --- Returns a table of the transition rules defined within the FSM. - -- @return #table - function FSM:GetTransitions() - - return self._Transitions or {} - end - - --- Set the default @{Process} template with key ProcessName providing the ProcessClass and the process object when it is assigned to a @{Controllable} by the task. - -- @param #FSM self - -- @param #table From Can contain a string indicating the From state or a table of strings containing multiple From states. - -- @param #string Event The Event name. - -- @param Core.Fsm#FSM_PROCESS Process An sub-process FSM. - -- @param #table ReturnEvents A table indicating for which returned events of the SubFSM which Event must be triggered in the FSM. - -- @return Core.Fsm#FSM_PROCESS The SubFSM. - function FSM:AddProcess( From, Event, Process, ReturnEvents ) - self:T( { From, Event, Process, ReturnEvents } ) - - local Sub = {} - Sub.From = From - Sub.Event = Event - Sub.fsm = Process - Sub.StartEvent = "Start" - Sub.ReturnEvents = ReturnEvents - - self._Processes[Sub] = Sub - - self:_submap( self.subs, Sub, nil ) - - self:AddTransition( From, Event, From ) - - return Process - end - - - --- Returns a table of the SubFSM rules defined within the FSM. - -- @return #table - function FSM:GetProcesses() - - return self._Processes or {} - end - - function FSM:GetProcess( From, Event ) - - for ProcessID, Process in pairs( self:GetProcesses() ) do - if Process.From == From and Process.Event == Event then - return Process.fsm - end - end - - error( "Sub-Process from state " .. From .. " with event " .. Event .. " not found!" ) - end - - --- Adds an End state. - function FSM:AddEndState( State ) - - self._EndStates[State] = State - self.endstates[State] = State - end - - --- Returns the End states. - function FSM:GetEndStates() - - return self._EndStates or {} - end - - - --- Adds a score for the FSM to be achieved. - -- @param #FSM self - -- @param #string State is the state of the process when the score needs to be given. (See the relevant state descriptions of the process). - -- @param #string ScoreText is a text describing the score that is given according the status. - -- @param #number Score is a number providing the score of the status. - -- @return #FSM self - function FSM:AddScore( State, ScoreText, Score ) - self:F( { State, ScoreText, Score } ) - - self._Scores[State] = self._Scores[State] or {} - self._Scores[State].ScoreText = ScoreText - self._Scores[State].Score = Score - - return self - end - - --- Adds a score for the FSM_PROCESS to be achieved. - -- @param #FSM self - -- @param #string From is the From State of the main process. - -- @param #string Event is the Event of the main process. - -- @param #string State is the state of the process when the score needs to be given. (See the relevant state descriptions of the process). - -- @param #string ScoreText is a text describing the score that is given according the status. - -- @param #number Score is a number providing the score of the status. - -- @return #FSM self - function FSM:AddScoreProcess( From, Event, State, ScoreText, Score ) - self:F( { From, Event, State, ScoreText, Score } ) - - local Process = self:GetProcess( From, Event ) - - Process._Scores[State] = Process._Scores[State] or {} - Process._Scores[State].ScoreText = ScoreText - Process._Scores[State].Score = Score - - self:T( Process._Scores ) - - return Process - end - - --- Returns a table with the scores defined. - function FSM:GetScores() - - return self._Scores or {} - end - - --- Returns a table with the Subs defined. - function FSM:GetSubs() - - return self.options.subs - end - - - function FSM:LoadCallBacks( CallBackTable ) - - for name, callback in pairs( CallBackTable or {} ) do - self[name] = callback - end - - end - - function FSM:_eventmap( Events, EventStructure ) - - local Event = EventStructure.Event - local __Event = "__" .. EventStructure.Event - self[Event] = self[Event] or self:_create_transition(Event) - self[__Event] = self[__Event] or self:_delayed_transition(Event) - self:T( "Added methods: " .. Event .. ", " .. __Event ) - Events[Event] = self.Events[Event] or { map = {} } - self:_add_to_map( Events[Event].map, EventStructure ) - - end - - function FSM:_submap( subs, sub, name ) - self:F( { sub = sub, name = name } ) - subs[sub.From] = subs[sub.From] or {} - subs[sub.From][sub.Event] = subs[sub.From][sub.Event] or {} - - -- Make the reference table weak. - -- setmetatable( subs[sub.From][sub.Event], { __mode = "k" } ) - - subs[sub.From][sub.Event][sub] = {} - subs[sub.From][sub.Event][sub].fsm = sub.fsm - subs[sub.From][sub.Event][sub].StartEvent = sub.StartEvent - subs[sub.From][sub.Event][sub].ReturnEvents = sub.ReturnEvents or {} -- these events need to be given to find the correct continue event ... if none given, the processing will stop. - subs[sub.From][sub.Event][sub].name = name - subs[sub.From][sub.Event][sub].fsmparent = self - end - - - function FSM:_call_handler( handler, params, EventName ) - - local ErrorHandler = function( errmsg ) - - env.info( "Error in SCHEDULER function:" .. errmsg ) - if debug ~= nil then - env.info( debug.traceback() ) - end - - return errmsg - end - if self[handler] then - self:T( "Calling " .. handler ) - self._EventSchedules[EventName] = nil - local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler ) - return Value - end - end - - function FSM._handler( self, EventName, ... ) - - local Can, to = self:can( EventName ) - - if to == "*" then - to = self.current - end - - if Can then - local from = self.current - local params = { from, EventName, to, ... } - - if self.Controllable then - self:T( "FSM Transition for " .. self.Controllable.ControllableName .. " :" .. self.current .. " --> " .. EventName .. " --> " .. to ) - else - self:T( "FSM Transition:" .. self.current .. " --> " .. EventName .. " --> " .. to ) - end - - if ( self:_call_handler("onbefore" .. EventName, params, EventName ) == false ) - or ( self:_call_handler("OnBefore" .. EventName, params, EventName ) == false ) - or ( self:_call_handler("onleave" .. from, params, EventName ) == false ) - or ( self:_call_handler("OnLeave" .. from, params, EventName ) == false ) then - self:T( "Cancel Transition" ) - return false - end - - self.current = to - - local execute = true - - local subtable = self:_gosub( from, EventName ) - for _, sub in pairs( subtable ) do - --if sub.nextevent then - -- self:F2( "nextevent = " .. sub.nextevent ) - -- self[sub.nextevent]( self ) - --end - self:T( "calling sub start event: " .. sub.StartEvent ) - sub.fsm.fsmparent = self - sub.fsm.ReturnEvents = sub.ReturnEvents - sub.fsm[sub.StartEvent]( sub.fsm ) - execute = false - end - - local fsmparent, Event = self:_isendstate( to ) - if fsmparent and Event then - self:F2( { "end state: ", fsmparent, Event } ) - self:_call_handler("onenter" .. to, params, EventName ) - self:_call_handler("OnEnter" .. to, params, EventName ) - self:_call_handler("onafter" .. EventName, params, EventName ) - self:_call_handler("OnAfter" .. EventName, params, EventName ) - self:_call_handler("onstatechange", params, EventName ) - fsmparent[Event]( fsmparent ) - execute = false - end - - if execute then - -- only execute the call if the From state is not equal to the To state! Otherwise this function should never execute! - --if from ~= to then - self:_call_handler("onenter" .. to, params, EventName ) - self:_call_handler("OnEnter" .. to, params, EventName ) - --end - - self:_call_handler("onafter" .. EventName, params, EventName ) - self:_call_handler("OnAfter" .. EventName, params, EventName ) - - self:_call_handler("onstatechange", params, EventName ) - end - else - self:T( "Cannot execute transition." ) - self:T( { From = self.current, Event = EventName, To = to, Can = Can } ) - end - - return nil - end - - function FSM:_delayed_transition( EventName ) - return function( self, DelaySeconds, ... ) - self:T2( "Delayed Event: " .. EventName ) - local CallID = 0 - if DelaySeconds ~= nil then - if DelaySeconds < 0 then -- Only call the event ONCE! - DelaySeconds = math.abs( DelaySeconds ) - if not self._EventSchedules[EventName] then - CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1 ) - self._EventSchedules[EventName] = CallID - else - -- reschedule - end - else - CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1 ) - end - else - error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." ) - end - self:T2( { CallID = CallID } ) - end - end - - function FSM:_create_transition( EventName ) - return function( self, ... ) return self._handler( self, EventName , ... ) end - end - - function FSM:_gosub( ParentFrom, ParentEvent ) - local fsmtable = {} - if self.subs[ParentFrom] and self.subs[ParentFrom][ParentEvent] then - self:T( { ParentFrom, ParentEvent, self.subs[ParentFrom], self.subs[ParentFrom][ParentEvent] } ) - return self.subs[ParentFrom][ParentEvent] - else - return {} - end - end - - function FSM:_isendstate( Current ) - local FSMParent = self.fsmparent - if FSMParent and self.endstates[Current] then - self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } ) - FSMParent.current = Current - local ParentFrom = FSMParent.current - self:T( ParentFrom ) - self:T( self.ReturnEvents ) - local Event = self.ReturnEvents[Current] - self:T( { ParentFrom, Event, self.ReturnEvents } ) - if Event then - return FSMParent, Event - else - self:T( { "Could not find parent event name for state ", ParentFrom } ) - end - end - - return nil - end - - function FSM:_add_to_map( Map, Event ) - self:F3( { Map, Event } ) - if type(Event.From) == 'string' then - Map[Event.From] = Event.To - else - for _, From in ipairs(Event.From) do - Map[From] = Event.To - end - end - self:T3( { Map, Event } ) - end - - function FSM:GetState() - return self.current - end - - - function FSM:Is( State ) - return self.current == State - end - - function FSM:is(state) - return self.current == state - end - - function FSM:can(e) - local Event = self.Events[e] - self:F3( { self.current, Event } ) - local To = Event and Event.map[self.current] or Event.map['*'] - return To ~= nil, To - end - - function FSM:cannot(e) - return not self:can(e) - end - -end - -do -- FSM_CONTROLLABLE - - --- @type FSM_CONTROLLABLE - -- @field Wrapper.Controllable#CONTROLLABLE Controllable - -- @extends Core.Fsm#FSM - - --- # FSM_CONTROLLABLE, extends @{#FSM} - -- - -- FSM_CONTROLLABLE class models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s. - -- - -- === - -- - -- @field #FSM_CONTROLLABLE FSM_CONTROLLABLE - -- - FSM_CONTROLLABLE = { - ClassName = "FSM_CONTROLLABLE", - } - - --- Creates a new FSM_CONTROLLABLE object. - -- @param #FSM_CONTROLLABLE self - -- @param #table FSMT Finite State Machine Table - -- @param Wrapper.Controllable#CONTROLLABLE Controllable (optional) The CONTROLLABLE object that the FSM_CONTROLLABLE governs. - -- @return #FSM_CONTROLLABLE - function FSM_CONTROLLABLE:New( FSMT, Controllable ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM:New( FSMT ) ) -- Core.Fsm#FSM_CONTROLLABLE - - if Controllable then - self:SetControllable( Controllable ) - end - - self:AddTransition( "*", "Stop", "Stopped" ) - - --- OnBefore Transition Handler for Event Stop. - -- @function [parent=#FSM_CONTROLLABLE] OnBeforeStop - -- @param #FSM_CONTROLLABLE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Stop. - -- @function [parent=#FSM_CONTROLLABLE] OnAfterStop - -- @param #FSM_CONTROLLABLE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Stop. - -- @function [parent=#FSM_CONTROLLABLE] Stop - -- @param #FSM_CONTROLLABLE self - - --- Asynchronous Event Trigger for Event Stop. - -- @function [parent=#FSM_CONTROLLABLE] __Stop - -- @param #FSM_CONTROLLABLE self - -- @param #number Delay The delay in seconds. - - --- OnLeave Transition Handler for State Stopped. - -- @function [parent=#FSM_CONTROLLABLE] OnLeaveStopped - -- @param #FSM_CONTROLLABLE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Stopped. - -- @function [parent=#FSM_CONTROLLABLE] OnEnterStopped - -- @param #FSM_CONTROLLABLE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - return self - end - - --- OnAfter Transition Handler for Event Stop. - -- @function [parent=#FSM_CONTROLLABLE] OnAfterStop - -- @param #FSM_CONTROLLABLE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - function FSM_CONTROLLABLE:OnAfterStop(Controllable,From,Event,To) - - -- Clear all pending schedules - self.CallScheduler:Clear() - end - - --- Sets the CONTROLLABLE object that the FSM_CONTROLLABLE governs. - -- @param #FSM_CONTROLLABLE self - -- @param Wrapper.Controllable#CONTROLLABLE FSMControllable - -- @return #FSM_CONTROLLABLE - function FSM_CONTROLLABLE:SetControllable( FSMControllable ) - self:F( FSMControllable ) - self.Controllable = FSMControllable - end - - --- Gets the CONTROLLABLE object that the FSM_CONTROLLABLE governs. - -- @param #FSM_CONTROLLABLE self - -- @return Wrapper.Controllable#CONTROLLABLE - function FSM_CONTROLLABLE:GetControllable() - return self.Controllable - end - - function FSM_CONTROLLABLE:_call_handler( handler, params, EventName ) - - local ErrorHandler = function( errmsg ) - - env.info( "Error in SCHEDULER function:" .. errmsg ) - if debug ~= nil then - env.info( debug.traceback() ) - end - - return errmsg - end - - if self[handler] then - self:F3( "Calling " .. handler ) - self._EventSchedules[EventName] = nil - local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, unpack( params ) ) end, ErrorHandler ) - return Value - --return self[handler]( self, self.Controllable, unpack( params ) ) - end - end - -end - -do -- FSM_PROCESS - - --- @type FSM_PROCESS - -- @field Tasking.Task#TASK Task - -- @extends Core.Fsm#FSM_CONTROLLABLE - - - --- # FSM_PROCESS, extends @{#FSM} - -- - -- FSM_PROCESS class models Finite State Machines for @{Task} actions, which control @{Client}s. - -- - -- === - -- - -- @field #FSM_PROCESS FSM_PROCESS - -- - FSM_PROCESS = { - ClassName = "FSM_PROCESS", - } - - --- Creates a new FSM_PROCESS object. - -- @param #FSM_PROCESS self - -- @return #FSM_PROCESS - function FSM_PROCESS:New( Controllable, Task ) - - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_PROCESS - - self:F( Controllable, Task ) - - self:Assign( Controllable, Task ) - - return self - end - - function FSM_PROCESS:Init( FsmProcess ) - self:T( "No Initialisation" ) - end - - function FSM_PROCESS:_call_handler( handler, params, EventName ) - - local ErrorHandler = function( errmsg ) - - env.info( "Error in FSM_PROCESS call handler:" .. errmsg ) - if debug ~= nil then - env.info( debug.traceback() ) - end - - return errmsg - end - - if self[handler] then - self:F3( "Calling " .. handler ) - self._EventSchedules[EventName] = nil - local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler ) - return Value - --return self[handler]( self, self.Controllable, unpack( params ) ) - end - end - - --- Creates a new FSM_PROCESS object based on this FSM_PROCESS. - -- @param #FSM_PROCESS self - -- @return #FSM_PROCESS - function FSM_PROCESS:Copy( Controllable, Task ) - self:T( { self:GetClassNameAndID() } ) - - - local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS - - NewFsm:Assign( Controllable, Task ) - - -- Polymorphic call to initialize the new FSM_PROCESS based on self FSM_PROCESS - NewFsm:Init( self ) - - -- Set Start State - NewFsm:SetStartState( self:GetStartState() ) - - -- Copy Transitions - for TransitionID, Transition in pairs( self:GetTransitions() ) do - NewFsm:AddTransition( Transition.From, Transition.Event, Transition.To ) - end - - -- Copy Processes - for ProcessID, Process in pairs( self:GetProcesses() ) do - self:E( { Process} ) - local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents ) - end - - -- Copy End States - for EndStateID, EndState in pairs( self:GetEndStates() ) do - self:T( EndState ) - NewFsm:AddEndState( EndState ) - end - - -- Copy the score tables - for ScoreID, Score in pairs( self:GetScores() ) do - self:T( Score ) - NewFsm:AddScore( ScoreID, Score.ScoreText, Score.Score ) - end - - return NewFsm - end - - --- Removes an FSM_PROCESS object. - -- @param #FSM_PROCESS self - -- @return #FSM_PROCESS - function FSM_PROCESS:Remove() - self:T( { self:GetClassNameAndID() } ) - - -- Copy Processes - for ProcessID, Process in pairs( self:GetProcesses() ) do - self:E( { Process} ) - Process.fsm:Remove() - Process.fsm = nil - end - - return self - end - - --- Sets the task of the process. - -- @param #FSM_PROCESS self - -- @param Tasking.Task#TASK Task - -- @return #FSM_PROCESS - function FSM_PROCESS:SetTask( Task ) - - self.Task = Task - - return self - end - - --- Gets the task of the process. - -- @param #FSM_PROCESS self - -- @return Tasking.Task#TASK - function FSM_PROCESS:GetTask() - - return self.Task - end - - --- Gets the mission of the process. - -- @param #FSM_PROCESS self - -- @return Tasking.Mission#MISSION - function FSM_PROCESS:GetMission() - - return self.Task.Mission - end - - --- Gets the mission of the process. - -- @param #FSM_PROCESS self - -- @return Tasking.CommandCenter#COMMANDCENTER - function FSM_PROCESS:GetCommandCenter() - - return self:GetTask():GetMission():GetCommandCenter() - end - --- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP. - - --- Send a message of the @{Task} to the Group of the Unit. --- @param #FSM_PROCESS self -function FSM_PROCESS:Message( Message ) - self:F( { Message = Message } ) - - local CC = self:GetCommandCenter() - local TaskGroup = self.Controllable:GetGroup() - - local PlayerName = self.Controllable:GetPlayerName() -- Only for a unit - PlayerName = PlayerName and " (" .. PlayerName .. ")" or "" -- If PlayerName is nil, then keep it nil, otherwise add brackets. - local Callsign = self.Controllable:GetCallsign() - local Prefix = Callsign and " @ " .. Callsign .. PlayerName or "" - - Message = Prefix .. ": " .. Message - CC:MessageToGroup( Message, TaskGroup ) -end - - - - - --- Assign the process to a @{Unit} and activate the process. - -- @param #FSM_PROCESS self - -- @param Task.Tasking#TASK Task - -- @param Wrapper.Unit#UNIT ProcessUnit - -- @return #FSM_PROCESS self - function FSM_PROCESS:Assign( ProcessUnit, Task ) - self:T( { Task, ProcessUnit } ) - - self:SetControllable( ProcessUnit ) - self:SetTask( Task ) - - --self.ProcessGroup = ProcessUnit:GetGroup() - - return self - end - - function FSM_PROCESS:onenterAssigned( ProcessUnit ) - self:T( "Assign" ) - - self.Task:Assign() - end - - function FSM_PROCESS:onenterFailed( ProcessUnit ) - self:T( "Failed" ) - - self.Task:Fail() - end - - function FSM_PROCESS:onenterSuccess( ProcessUnit ) - self:T( "Success" ) - - self.Task:Success() - end - - --- StateMachine callback function for a FSM_PROCESS - -- @param #FSM_PROCESS self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy ) - self:T( { ProcessUnit, From, Event, To, Dummy, self:IsTrace() } ) - - if self:IsTrace() then - MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() - end - - self:T( { Scores = self._Scores, To = To } ) - -- TODO: This needs to be reworked with a callback functions allocated within Task, and set within the mission script from the Task Objects... - if self._Scores[To] then - - local Task = self.Task - local Scoring = Task:GetScoring() - if Scoring then - Scoring:_AddMissionTaskScore( Task.Mission, ProcessUnit, self._Scores[To].ScoreText, self._Scores[To].Score ) - end - end - end - -end - -do -- FSM_TASK - - --- FSM_TASK class - -- @type FSM_TASK - -- @field Tasking.Task#TASK Task - -- @extends Core.Fsm#FSM - - --- # FSM_TASK, extends @{#FSM} - -- - -- FSM_TASK class models Finite State Machines for @{Task}s. - -- - -- === - -- - -- @field #FSM_TASK FSM_TASK - -- - FSM_TASK = { - ClassName = "FSM_TASK", - } - - --- Creates a new FSM_TASK object. - -- @param #FSM_TASK self - -- @param #table FSMT - -- @param Tasking.Task#TASK Task - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #FSM_TASK - function FSM_TASK:New( FSMT ) - - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( FSMT ) ) -- Core.Fsm#FSM_TASK - - self["onstatechange"] = self.OnStateChange - - return self - end - - function FSM_TASK:_call_handler( handler, params, EventName ) - if self[handler] then - self:T( "Calling " .. handler ) - self._EventSchedules[EventName] = nil - return self[handler]( self, unpack( params ) ) - end - end - -end -- FSM_TASK - -do -- FSM_SET - - --- FSM_SET class - -- @type FSM_SET - -- @field Core.Set#SET_BASE Set - -- @extends Core.Fsm#FSM - - - --- # FSM_SET, extends @{#FSM} - -- - -- FSM_SET class models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here - -- for multiple objects or the position of the state machine in the process. - -- - -- === - -- - -- @field #FSM_SET FSM_SET - -- - FSM_SET = { - ClassName = "FSM_SET", - } - - --- Creates a new FSM_SET object. - -- @param #FSM_SET self - -- @param #table FSMT Finite State Machine Table - -- @param Set_SET_BASE FSMSet (optional) The Set object that the FSM_SET governs. - -- @return #FSM_SET - function FSM_SET:New( FSMSet ) - - -- Inherits from BASE - self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM_SET - - if FSMSet then - self:Set( FSMSet ) - end - - return self - end - - --- Sets the SET_BASE object that the FSM_SET governs. - -- @param #FSM_SET self - -- @param Core.Set#SET_BASE FSMSet - -- @return #FSM_SET - function FSM_SET:Set( FSMSet ) - self:F( FSMSet ) - self.Set = FSMSet - end - - --- Gets the SET_BASE object that the FSM_SET governs. - -- @param #FSM_SET self - -- @return Core.Set#SET_BASE - function FSM_SET:Get() - return self.Controllable - end - - function FSM_SET:_call_handler( handler, params, EventName ) - if self[handler] then - self:T( "Calling " .. handler ) - self._EventSchedules[EventName] = nil - return self[handler]( self, self.Set, unpack( params ) ) - end - end - -end -- FSM_SET - ---- **Core** - The RADIO class is responsible for **transmitting radio communications**. --- --- --- bitmap --- --- === --- --- What are radio communications in DCS ? --- --- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM), --- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**. --- --- How to supply DCS my own Sound Files ? --- --- * Your sound files need to be encoded in **.ogg** or .wav, --- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings, --- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file), --- * For simplicty sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission. --- --- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Unit#UNIT} or a @{Group#GROUP} or by any other @{Positionable#POSITIONABLE} --- --- * If the transmitter is a @{Unit#UNIT} or a @{Group#GROUP}, DCS will set the power of the transmission automatically, --- * If the transmitter is any other @{Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped. --- --- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft, --- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below). --- If a FC3 airacraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. --- --- === --- --- ### Authors: Hugues "Grey_Echo" Bousquet --- --- @module Radio - ---- # 1) RADIO class, extends @{Base#BASE} --- --- ## 1.1) RADIO usage --- --- There are 3 steps to a successful radio transmission. --- --- * First, you need to **"add" a @{#RADIO} object** to your @{Positionable#POSITIONABLE}. This is done using the @{Positionable#POSITIONABLE.GetRadio}() function, --- * Then, you will **set the relevant parameters** to the transmission (see below), --- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{Positionable#POSITIONABLE.Broadcast}() function. --- --- Methods to set relevant parameters for both a @{Unit#UNIT} or a @{Group#GROUP} or any other @{Positionable#POSITIONABLE} --- --- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"), --- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission, --- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission. --- --- Additional Methods to set relevant parameters if the transmiter is a @{Unit#UNIT} or a @{Group#GROUP} --- --- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped, --- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration, --- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call --- --- Additional Methods to set relevant parameters if the transmiter is any other @{Wrapper.Positionable#POSITIONABLE} --- --- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts --- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call --- --- What is this power thing ? --- --- * If your transmission is sent by a @{Positionable#POSITIONABLE} other than a @{Unit#UNIT} or a @{Group#GROUP}, you can set the power of the antenna, --- * Otherwise, DCS sets it automatically, depending on what's available on your Unit, --- * If the player gets **too far** from the transmiter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**, --- * This an automated DCS calculation you have no say on, --- * For reference, a standard VOR station has a 100W antenna, a standard AA TACAN has a 120W antenna, and civilian ATC's antenna usually range between 300 and 500W, --- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission. --- --- @type RADIO --- @field Wrapper.Positionable#POSITIONABLE Positionable The transmiter --- @field #string FileName Name of the sound file --- @field #number Frequency Frequency of the transmission in Hz --- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM) --- @field #string Subtitle Subtitle of the transmission --- @field #number SubtitleDuration Duration of the Subtitle in seconds --- @field #number Power Power of the antenna is Watts --- @field #boolean Loop --- @extends Core.Base#BASE -RADIO = { - ClassName = "RADIO", - FileName = "", - Frequency = 0, - Modulation = radio.modulation.AM, - Subtitle = "", - SubtitleDuration = 0, - Power = 100, - Loop = 0, -} - ---- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast --- @param #RADIO self --- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. --- @return #RADIO Radio --- @return #nil If Positionable is invalid --- @usage --- -- If you want to create a RADIO, you probably should use @{Positionable#POSITIONABLE.GetRadio}() instead -function RADIO:New(Positionable) - local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO - - self:F(Positionable) - - if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid - self.Positionable = Positionable - return self - end - - self:E({"The passed positionable is invalid, no RADIO created", Positionable}) - return nil -end - ---- Check validity of the filename passed and sets RADIO.FileName --- @param #RADIO self --- @param #string FileName File name of the sound file (i.e. "Noise.ogg") --- @return #RADIO self -function RADIO:SetFileName(FileName) - self:F2(FileName) - - if type(FileName) == "string" then - if FileName:find(".ogg") or FileName:find(".wav") then - if not FileName:find("l10n/DEFAULT/") then - FileName = "l10n/DEFAULT/" .. FileName - end - self.FileName = FileName - return self - end - end - - self:E({"File name invalid. Maybe something wrong with the extension ?", self.FileName}) - return self -end - ---- Check validity of the frequency passed and sets RADIO.Frequency --- @param #RADIO self --- @param #number Frequency in MHz (Ranges allowed for radio transmissions in DCS : 30-88 / 108-152 / 225-400MHz) --- @return #RADIO self -function RADIO:SetFrequency(Frequency) - self:F2(Frequency) - if type(Frequency) == "number" then - -- If frequency is in range - if (Frequency >= 30 and Frequency < 88) or (Frequency >= 108 and Frequency < 152) or (Frequency >= 225 and Frequency < 400) then - self.Frequency = Frequency * 1000000 -- Conversion in Hz - -- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency - if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then - self.Positionable:SetCommand({ - id = "SetFrequency", - params = { - frequency = self.Frequency, - modulation = self.Modulation, - } - }) - end - return self - end - end - self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", self.Frequency}) - return self -end - ---- Check validity of the frequency passed and sets RADIO.Modulation --- @param #RADIO self --- @param #number Modulation either radio.modulation.AM or radio.modulation.FM --- @return #RADIO self -function RADIO:SetModulation(Modulation) - self:F2(Modulation) - if type(Modulation) == "number" then - if Modulation == radio.modulation.AM or Modulation == radio.modulation.FM then --TODO Maybe make this future proof if ED decides to add an other modulation ? - self.Modulation = Modulation - return self - end - end - self:E({"Modulation is invalid. Use DCS's enum radio.modulation. Modulation unchanged.", self.Modulation}) - return self -end - ---- Check validity of the power passed and sets RADIO.Power --- @param #RADIO self --- @param #number Power in W --- @return #RADIO self -function RADIO:SetPower(Power) - self:F2(Power) - if type(Power) == "number" then - self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that - return self - end - self:E({"Power is invalid. Power unchanged.", self.Power}) - return self -end - ---- Check validity of the loop passed and sets RADIO.Loop --- @param #RADIO self --- @param #boolean Loop --- @return #RADIO self --- @usage -function RADIO:SetLoop(Loop) - self:F2(Loop) - if type(Loop) == "boolean" then - self.Loop = Loop - return self - end - self:E({"Loop is invalid. Loop unchanged.", self.Loop}) - return self -end - ---- Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration --- @param #RADIO self --- @param #string Subtitle --- @param #number SubtitleDuration in s --- @return #RADIO self --- @usage --- -- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration -function RADIO:SetSubtitle(Subtitle, SubtitleDuration) - self:F2({Subtitle, SubtitleDuration}) - if type(Subtitle) == "string" then - self.Subtitle = Subtitle - else - self.Subtitle = "" - self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle}) - end - if type(SubtitleDuration) == "number" then - if math.floor(math.abs(SubtitleDuration)) == SubtitleDuration then - self.SubtitleDuration = SubtitleDuration - return self - end - end - self.SubtitleDuration = 0 - self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration}) -end - ---- Create a new transmission, that is to say, populate the RADIO with relevant data --- @param #RADIO self --- @param #string FileName --- @param #number Frequency in MHz --- @param #number Modulation either radio.modulation.AM or radio.modulation.FM --- @param #number Power in W --- @return #RADIO self --- @usage --- -- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP, --- but it will work with a UNIT or a GROUP anyway --- -- Only the RADIO and the Filename are mandatory -function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power) - self:F({FileName, Frequency, Modulation, Power}) - - self:SetFileName(FileName) - if Frequency then self:SetFrequency(Frequency) end - if Modulation then self:SetModulation(Modulation) end - if Power then self:SetPower(Power) end - - return self -end - - ---- Create a new transmission, that is to say, populate the RADIO with relevant data --- @param #RADIO self --- @param #string FileName --- @param #string Subtitle --- @param #number SubtitleDuration in s --- @param #number Frequency in MHz --- @param #number Modulation either radio.modulation.AM or radio.modulation.FM --- @param #boolean Loop --- @return #RADIO self --- @usage --- -- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP, --- but it will work for any POSITIONABLE --- -- Only the RADIO and the Filename are mandatory -function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop) - self:F({FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop}) - - self:SetFileName(FileName) - if Subtitle then self:SetSubtitle(Subtitle) end - if SubtitleDuration then self:SetSubtitleDuration(SubtitleDuration) end - if Frequency then self:SetFrequency(Frequency) end - if Modulation then self:SetModulation(Modulation) end - if Loop then self:SetLoop(Loop) end - - return self -end - ---- Actually Broadcast the transmission --- @param #RADIO self --- @return #RADIO self --- @usage --- -- The Radio has to be populated with the new transmission before broadcasting. --- -- Please use RADIO setters or either @{Radio#RADIO.NewGenericTransmission} or @{Radio#RADIO.NewUnitTransmission} --- -- This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE --- -- If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission() --- -- If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command --- -- If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored. --- -- If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration and Loop are ignored -function RADIO:Broadcast() - self:F() - -- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system - if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then - self:T2("Broadcasting from a UNIT or a GROUP") - self.Positionable:SetCommand({ - id = "TransmitMessage", - params = { - file = self.FileName, - duration = self.SubtitleDuration, - subtitle = self.Subtitle, - loop = self.Loop, - } - }) - else - -- If the POSITIONABLE is anything else, we revert to the general singleton function - self:T2("Broadcasting from a POSITIONABLE") - trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, false, self.Frequency, self.Power) - end - return self -end - ---- Stops a transmission --- @param #RADIO self --- @return #RADIO self --- @usage --- -- Especially usefull to stop the broadcast of looped transmissions --- -- Only works with broadcasts from UNIT or GROUP -function RADIO:StopBroadcast() - self:F() - -- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command - if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then - self.Positionable:SetCommand({ - id = "StopTransmission", - params = {} - }) - else - self:E("This broadcast can't be stopped. It's not looped either, so please wait for the end of the sound file playback") - end - return self -end--- This module contains the OBJECT class. --- --- 1) @{Object#OBJECT} class, extends @{Base#BASE} --- =========================================================== --- The @{Object#OBJECT} class is a wrapper class to handle the DCS Object objects: --- --- * Support all DCS Object APIs. --- * Enhance with Object specific APIs not in the DCS Object API set. --- * Manage the "state" of the DCS Object. --- --- 1.1) OBJECT constructor: --- ------------------------------ --- The OBJECT class provides the following functions to construct a OBJECT instance: --- --- * @{Object#OBJECT.New}(): Create a OBJECT instance. --- --- 1.2) OBJECT methods: --- -------------------------- --- The following methods can be used to identify an Object object: --- --- * @{Object#OBJECT.GetID}(): Returns the ID of the Object object. --- --- === --- --- @module Object - ---- The OBJECT class --- @type OBJECT --- @extends Core.Base#BASE --- @field #string ObjectName The name of the Object. -OBJECT = { - ClassName = "OBJECT", - ObjectName = "", -} - ---- A DCSObject --- @type DCSObject --- @field id_ The ID of the controllable in DCS - ---- Create a new OBJECT from a DCSObject --- @param #OBJECT self --- @param Dcs.DCSWrapper.Object#Object ObjectName The Object name --- @return #OBJECT self -function OBJECT:New( ObjectName, Test ) - local self = BASE:Inherit( self, BASE:New() ) - self:F2( ObjectName ) - self.ObjectName = ObjectName - - return self -end - - ---- Returns the unit's unique identifier. --- @param Wrapper.Object#OBJECT self --- @return Dcs.DCSWrapper.Object#Object.ID ObjectID --- @return #nil The DCS Object is not existing or alive. -function OBJECT:GetID() - self:F2( self.ObjectName ) - - local DCSObject = self:GetDCSObject() - - if DCSObject then - local ObjectID = DCSObject:getID() - return ObjectID - end - - return nil -end - ---- Destroys the OBJECT. --- @param #OBJECT self --- @return #nil The DCS Unit is not existing or alive. -function OBJECT:Destroy() - self:F2( self.ObjectName ) - - local DCSObject = self:GetDCSObject() - - if DCSObject then - - DCSObject:destroy() - end - - return nil -end - - - - ---- This module contains the IDENTIFIABLE class. --- --- 1) @{#IDENTIFIABLE} class, extends @{Object#OBJECT} --- =============================================================== --- The @{#IDENTIFIABLE} class is a wrapper class to handle the DCS Identifiable objects: --- --- * Support all DCS Identifiable APIs. --- * Enhance with Identifiable specific APIs not in the DCS Identifiable API set. --- * Manage the "state" of the DCS Identifiable. --- --- 1.1) IDENTIFIABLE constructor: --- ------------------------------ --- The IDENTIFIABLE class provides the following functions to construct a IDENTIFIABLE instance: --- --- * @{#IDENTIFIABLE.New}(): Create a IDENTIFIABLE instance. --- --- 1.2) IDENTIFIABLE methods: --- -------------------------- --- The following methods can be used to identify an identifiable object: --- --- * @{#IDENTIFIABLE.GetName}(): Returns the name of the Identifiable. --- * @{#IDENTIFIABLE.IsAlive}(): Returns if the Identifiable is alive. --- * @{#IDENTIFIABLE.GetTypeName}(): Returns the type name of the Identifiable. --- * @{#IDENTIFIABLE.GetCoalition}(): Returns the coalition of the Identifiable. --- * @{#IDENTIFIABLE.GetCountry}(): Returns the country of the Identifiable. --- * @{#IDENTIFIABLE.GetDesc}(): Returns the descriptor structure of the Identifiable. --- --- --- === --- --- @module Identifiable - ---- The IDENTIFIABLE class --- @type IDENTIFIABLE --- @extends Wrapper.Object#OBJECT --- @field #string IdentifiableName The name of the identifiable. -IDENTIFIABLE = { - ClassName = "IDENTIFIABLE", - IdentifiableName = "", -} - -local _CategoryName = { - [Unit.Category.AIRPLANE] = "Airplane", - [Unit.Category.HELICOPTER] = "Helicoper", - [Unit.Category.GROUND_UNIT] = "Ground Identifiable", - [Unit.Category.SHIP] = "Ship", - [Unit.Category.STRUCTURE] = "Structure", - } - ---- Create a new IDENTIFIABLE from a DCSIdentifiable --- @param #IDENTIFIABLE self --- @param Dcs.DCSWrapper.Identifiable#Identifiable IdentifiableName The DCS Identifiable name --- @return #IDENTIFIABLE self -function IDENTIFIABLE:New( IdentifiableName ) - local self = BASE:Inherit( self, OBJECT:New( IdentifiableName ) ) - self:F2( IdentifiableName ) - self.IdentifiableName = IdentifiableName - return self -end - ---- Returns if the Identifiable is alive. --- If the Identifiable is not alive, nil is returned. --- If the Identifiable is alive, true is returned. --- @param #IDENTIFIABLE self --- @return #boolean true if Identifiable is alive. --- @return #nil if the Identifiable is not existing or is not alive. -function IDENTIFIABLE:IsAlive() - self:F3( self.IdentifiableName ) - - local DCSIdentifiable = self:GetDCSObject() -- Dcs.DCSObject#Object - - if DCSIdentifiable then - local IdentifiableIsAlive = DCSIdentifiable:isExist() - return IdentifiableIsAlive - end - - return false -end - - - - ---- Returns DCS Identifiable object name. --- The function provides access to non-activated objects too. --- @param #IDENTIFIABLE self --- @return #string The name of the DCS Identifiable. --- @return #nil The DCS Identifiable is not existing or alive. -function IDENTIFIABLE:GetName() - self:F2( self.IdentifiableName ) - - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableName = self.IdentifiableName - return IdentifiableName - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil -end - - ---- Returns the type name of the DCS Identifiable. --- @param #IDENTIFIABLE self --- @return #string The type name of the DCS Identifiable. --- @return #nil The DCS Identifiable is not existing or alive. -function IDENTIFIABLE:GetTypeName() - self:F2( self.IdentifiableName ) - - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableTypeName = DCSIdentifiable:getTypeName() - self:T3( IdentifiableTypeName ) - return IdentifiableTypeName - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil -end - - ---- Returns category of the DCS Identifiable. --- @param #IDENTIFIABLE self --- @return Dcs.DCSWrapper.Object#Object.Category The category ID -function IDENTIFIABLE:GetCategory() - self:F2( self.ObjectName ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - local ObjectCategory = DCSObject:getCategory() - self:T3( ObjectCategory ) - return ObjectCategory - end - - return nil -end - - ---- Returns the DCS Identifiable category name as defined within the DCS Identifiable Descriptor. --- @param #IDENTIFIABLE self --- @return #string The DCS Identifiable Category Name -function IDENTIFIABLE:GetCategoryName() - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableCategoryName = _CategoryName[ self:GetDesc().category ] - return IdentifiableCategoryName - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil -end - ---- Returns coalition of the Identifiable. --- @param #IDENTIFIABLE self --- @return Dcs.DCSCoalitionWrapper.Object#coalition.side The side of the coalition. --- @return #nil The DCS Identifiable is not existing or alive. -function IDENTIFIABLE:GetCoalition() - self:F2( self.IdentifiableName ) - - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableCoalition = DCSIdentifiable:getCoalition() - self:T3( IdentifiableCoalition ) - return IdentifiableCoalition - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil -end - ---- Returns country of the Identifiable. --- @param #IDENTIFIABLE self --- @return Dcs.DCScountry#country.id The country identifier. --- @return #nil The DCS Identifiable is not existing or alive. -function IDENTIFIABLE:GetCountry() - self:F2( self.IdentifiableName ) - - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableCountry = DCSIdentifiable:getCountry() - self:T3( IdentifiableCountry ) - return IdentifiableCountry - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil -end - - - ---- Returns Identifiable descriptor. Descriptor type depends on Identifiable category. --- @param #IDENTIFIABLE self --- @return Dcs.DCSWrapper.Identifiable#Identifiable.Desc The Identifiable descriptor. --- @return #nil The DCS Identifiable is not existing or alive. -function IDENTIFIABLE:GetDesc() - self:F2( self.IdentifiableName ) - - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableDesc = DCSIdentifiable:getDesc() - self:T2( IdentifiableDesc ) - return IdentifiableDesc - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil -end - ---- Gets the CallSign of the IDENTIFIABLE, which is a blank by default. --- @param #IDENTIFIABLE self --- @return #string The CallSign of the IDENTIFIABLE. -function IDENTIFIABLE:GetCallsign() - return '' -end - - -function IDENTIFIABLE:GetThreatLevel() - - return 0, "Scenery" -end ---- This module contains the POSITIONABLE class. --- --- 1) @{Positionable#POSITIONABLE} class, extends @{Identifiable#IDENTIFIABLE} --- =========================================================== --- The @{Positionable#POSITIONABLE} class is a wrapper class to handle the POSITIONABLE objects: --- --- * Support all DCS APIs. --- * Enhance with POSITIONABLE specific APIs not in the DCS API set. --- * Manage the "state" of the POSITIONABLE. --- --- 1.1) POSITIONABLE constructor: --- ------------------------------ --- The POSITIONABLE class provides the following functions to construct a POSITIONABLE instance: --- --- * @{Positionable#POSITIONABLE.New}(): Create a POSITIONABLE instance. --- --- 1.2) POSITIONABLE methods: --- -------------------------- --- The following methods can be used to identify an measurable object: --- --- * @{Positionable#POSITIONABLE.GetID}(): Returns the ID of the measurable object. --- * @{Positionable#POSITIONABLE.GetName}(): Returns the name of the measurable object. --- --- === --- --- @module Positionable - ---- The POSITIONABLE class --- @type POSITIONABLE --- @extends Wrapper.Identifiable#IDENTIFIABLE --- @field #string PositionableName The name of the measurable. -POSITIONABLE = { - ClassName = "POSITIONABLE", - PositionableName = "", -} - ---- A DCSPositionable --- @type DCSPositionable --- @field id_ The ID of the controllable in DCS - ---- Create a new POSITIONABLE from a DCSPositionable --- @param #POSITIONABLE self --- @param Dcs.DCSWrapper.Positionable#Positionable PositionableName The POSITIONABLE name --- @return #POSITIONABLE self -function POSITIONABLE:New( PositionableName ) - local self = BASE:Inherit( self, IDENTIFIABLE:New( PositionableName ) ) - - self.PositionableName = PositionableName - return self -end - ---- Returns the @{DCSTypes#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Position The 3D position vectors of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetPositionVec3() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionablePosition = DCSPositionable:getPosition().p - self:T3( PositionablePosition ) - return PositionablePosition - end - - return nil -end - ---- Returns the @{DCSTypes#Vec2} vector indicating the point in 2D of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Vec2 The 2D point vector of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetVec2() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionableVec3 = DCSPositionable:getPosition().p - - local PositionableVec2 = {} - PositionableVec2.x = PositionableVec3.x - PositionableVec2.y = PositionableVec3.z - - self:T2( PositionableVec2 ) - return PositionableVec2 - end - - return nil -end - ---- Returns a POINT_VEC2 object indicating the point in 2D of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Core.Point#POINT_VEC2 The 2D point vector of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetPointVec2() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionableVec3 = DCSPositionable:getPosition().p - - local PositionablePointVec2 = POINT_VEC2:NewFromVec3( PositionableVec3 ) - - self:T2( PositionablePointVec2 ) - return PositionablePointVec2 - end - - return nil -end - ---- Returns a POINT_VEC3 object indicating the point in 3D of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Core.Point#POINT_VEC3 The 3D point vector of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetPointVec3() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionableVec3 = self:GetPositionVec3() - - local PositionablePointVec3 = POINT_VEC3:NewFromVec3( PositionableVec3 ) - - self:T2( PositionablePointVec3 ) - return PositionablePointVec3 - end - - return nil -end - - ---- Returns a random @{DCSTypes#Vec3} vector within a range, indicating the point in 3D of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @param #number Radius --- @return Dcs.DCSTypes#Vec3 The 3D point vector of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. --- @usage --- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP -function POSITIONABLE:GetRandomVec3( Radius ) - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionablePointVec3 = DCSPositionable:getPosition().p - - if Radius then - local PositionableRandomVec3 = {} - local angle = math.random() * math.pi*2; - PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius; - PositionableRandomVec3.y = PositionablePointVec3.y - PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius; - - self:T3( PositionableRandomVec3 ) - return PositionableRandomVec3 - else - self:E("Radius is nil, returning the PointVec3 of the POSITIONABLE", PositionablePointVec3) - return PositionablePointVec3 - end - end - - return nil -end - ---- Returns the @{DCSTypes#Vec3} vector indicating the 3D vector of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Vec3 The 3D point vector of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetVec3() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionableVec3 = DCSPositionable:getPosition().p - self:T3( PositionableVec3 ) - return PositionableVec3 - end - - return nil -end - ---- Returns the altitude of the POSITIONABLE. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Distance The altitude of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetAltitude() - self:F2() - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionablePointVec3 = DCSPositionable:getPoint() --Dcs.DCSTypes#Vec3 - return PositionablePointVec3.y - end - - return nil -end - ---- Returns if the Positionable is located above a runway. --- @param Wrapper.Positionable#POSITIONABLE self --- @return #boolean true if Positionable is above a runway. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:IsAboveRunway() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - - local Vec2 = self:GetVec2() - local SurfaceType = land.getSurfaceType( Vec2 ) - local IsAboveRunway = SurfaceType == land.SurfaceType.RUNWAY - - self:T2( IsAboveRunway ) - return IsAboveRunway - end - - return nil -end - - - ---- Returns the POSITIONABLE heading in degrees. --- @param Wrapper.Positionable#POSITIONABLE self --- @return #number The POSTIONABLE heading --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetHeading() - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - - local PositionablePosition = DCSPositionable:getPosition() - if PositionablePosition then - local PositionableHeading = math.atan2( PositionablePosition.x.z, PositionablePosition.x.x ) - if PositionableHeading < 0 then - PositionableHeading = PositionableHeading + 2 * math.pi - end - PositionableHeading = PositionableHeading * 180 / math.pi - self:T2( PositionableHeading ) - return PositionableHeading - end - end - - return nil -end - - ---- Returns true if the POSITIONABLE is in the air. --- Polymorphic, is overridden in GROUP and UNIT. --- @param Wrapper.Positionable#POSITIONABLE self --- @return #boolean true if in the air. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:InAir() - self:F2( self.PositionableName ) - - return nil -end - - ---- Returns the POSITIONABLE velocity vector. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Vec3 The velocity vector --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetVelocity() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionableVelocityVec3 = DCSPositionable:getVelocity() - self:T3( PositionableVelocityVec3 ) - return PositionableVelocityVec3 - end - - return nil -end - ---- Returns the POSITIONABLE velocity in km/h. --- @param Wrapper.Positionable#POSITIONABLE self --- @return #number The velocity in km/h --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetVelocityKMH() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local VelocityVec3 = self:GetVelocity() - local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec - local Velocity = Velocity * 3.6 -- now it is in km/h. - self:T3( Velocity ) - return Velocity - end - - return nil -end - ---- Returns a message with the callsign embedded (if there is one). --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. --- @return Core.Message#MESSAGE -function POSITIONABLE:GetMessage( Message, Duration, Name ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - Name = Name or self:GetTypeName() - return MESSAGE:New( Message, Duration, self:GetCallsign() .. " (" .. Name .. ")" ) - end - - return nil -end - ---- Send a message to all coalitions. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToAll( Message, Duration, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - self:GetMessage( Message, Duration, Name ):ToAll() - end - - return nil -end - ---- Send a message to a coalition. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTYpes#Duration Duration The duration of the message. --- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - self:GetMessage( Message, Duration, Name ):ToCoalition( MessageCoalition ) - end - - return nil -end - - ---- Send a message to the red coalition. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTYpes#Duration Duration The duration of the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToRed( Message, Duration, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - self:GetMessage( Message, Duration, Name ):ToRed() - end - - return nil -end - ---- Send a message to the blue coalition. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToBlue( Message, Duration, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - self:GetMessage( Message, Duration, Name ):ToBlue() - end - - return nil -end - ---- Send a message to a client. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. --- @param Wrapper.Client#CLIENT Client The client object receiving the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToClient( Message, Duration, Client, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - self:GetMessage( Message, Duration, Name ):ToClient( Client ) - end - - return nil -end - ---- Send a message to a @{Group}. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. --- @param Wrapper.Group#GROUP MessageGroup The GROUP object receiving the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - if DCSObject:isExist() then - self:GetMessage( Message, Duration, Name ):ToGroup( MessageGroup ) - end - end - - return nil -end - ---- Send a message to the players in the @{Group}. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:Message( Message, Duration, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - self:GetMessage( Message, Duration, Name ):ToGroup( self ) - end - - return nil -end - ---- Create a @{Radio#RADIO}, to allow radio transmission for this POSITIONABLE. --- Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message --- @param #POSITIONABLE self --- @return #RADIO Radio -function POSITIONABLE:GetRadio() - self:F2(self) - return RADIO:New(self) -end ---- This module contains the CONTROLLABLE class. --- --- 1) @{Controllable#CONTROLLABLE} class, extends @{Positionable#POSITIONABLE} --- =========================================================== --- The @{Controllable#CONTROLLABLE} class is a wrapper class to handle the DCS Controllable objects: --- --- * Support all DCS Controllable APIs. --- * Enhance with Controllable specific APIs not in the DCS Controllable API set. --- * Handle local Controllable Controller. --- * Manage the "state" of the DCS Controllable. --- --- 1.1) CONTROLLABLE constructor --- ----------------------------- --- The CONTROLLABLE class provides the following functions to construct a CONTROLLABLE instance: --- --- * @{#CONTROLLABLE.New}(): Create a CONTROLLABLE instance. --- --- 1.2) CONTROLLABLE task methods --- ------------------------------ --- Several controllable task methods are available that help you to prepare tasks. --- These methods return a string consisting of the task description, which can then be given to either a @{Controllable#CONTROLLABLE.PushTask} or @{Controllable#SetTask} method to assign the task to the CONTROLLABLE. --- Tasks are specific for the category of the CONTROLLABLE, more specific, for AIR, GROUND or AIR and GROUND. --- Each task description where applicable indicates for which controllable category the task is valid. --- There are 2 main subdivisions of tasks: Assigned tasks and EnRoute tasks. --- --- ### 1.2.1) Assigned task methods --- --- Assigned task methods make the controllable execute the task where the location of the (possible) targets of the task are known before being detected. --- This is different from the EnRoute tasks, where the targets of the task need to be detected before the task can be executed. --- --- Find below a list of the **assigned task** methods: --- --- * @{#CONTROLLABLE.TaskAttackGroup}: (AIR) Attack a Controllable. --- * @{#CONTROLLABLE.TaskAttackMapObject}: (AIR) Attacking the map object (building, structure, e.t.c). --- * @{#CONTROLLABLE.TaskAttackUnit}: (AIR) Attack the Unit. --- * @{#CONTROLLABLE.TaskBombing}: (AIR) Delivering weapon at the point on the ground. --- * @{#CONTROLLABLE.TaskBombingRunway}: (AIR) Delivering weapon on the runway. --- * @{#CONTROLLABLE.TaskEmbarking}: (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable. --- * @{#CONTROLLABLE.TaskEmbarkToTransport}: (GROUND) Embark to a Transport landed at a location. --- * @{#CONTROLLABLE.TaskEscort}: (AIR) Escort another airborne controllable. --- * @{#CONTROLLABLE.TaskFAC_AttackGroup}: (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction. --- * @{#CONTROLLABLE.TaskFireAtPoint}: (GROUND) Fire some or all ammunition at a VEC2 point. --- * @{#CONTROLLABLE.TaskFollow}: (AIR) Following another airborne controllable. --- * @{#CONTROLLABLE.TaskHold}: (GROUND) Hold ground controllable from moving. --- * @{#CONTROLLABLE.TaskHoldPosition}: (AIR) Hold position at the current position of the first unit of the controllable. --- * @{#CONTROLLABLE.TaskLand}: (AIR HELICOPTER) Landing at the ground. For helicopters only. --- * @{#CONTROLLABLE.TaskLandAtZone}: (AIR) Land the controllable at a @{Zone#ZONE_RADIUS). --- * @{#CONTROLLABLE.TaskOrbitCircle}: (AIR) Orbit at the current position of the first unit of the controllable at a specified alititude. --- * @{#CONTROLLABLE.TaskOrbitCircleAtVec2}: (AIR) Orbit at a specified position at a specified alititude during a specified duration with a specified speed. --- * @{#CONTROLLABLE.TaskRefueling}: (AIR) Refueling from the nearest tanker. No parameters. --- * @{#CONTROLLABLE.TaskRoute}: (AIR + GROUND) Return a Misson task to follow a given route defined by Points. --- * @{#CONTROLLABLE.TaskRouteToVec2}: (AIR + GROUND) Make the Controllable move to a given point. --- * @{#CONTROLLABLE.TaskRouteToVec3}: (AIR + GROUND) Make the Controllable move to a given point. --- * @{#CONTROLLABLE.TaskRouteToZone}: (AIR + GROUND) Route the controllable to a given zone. --- * @{#CONTROLLABLE.TaskReturnToBase}: (AIR) Route the controllable to an airbase. --- --- ### 1.2.2) EnRoute task methods --- --- EnRoute tasks require the targets of the task need to be detected by the controllable (using its sensors) before the task can be executed: --- --- * @{#CONTROLLABLE.EnRouteTaskAWACS}: (AIR) Aircraft will act as an AWACS for friendly units (will provide them with information about contacts). No parameters. --- * @{#CONTROLLABLE.EnRouteTaskEngageControllable}: (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now; it just allows the unit/controllable to engage the target controllable as well as other assigned targets. --- * @{#CONTROLLABLE.EnRouteTaskEngageTargets}: (AIR) Engaging targets of defined types. --- * @{#CONTROLLABLE.EnRouteTaskEWR}: (AIR) Attack the Unit. --- * @{#CONTROLLABLE.EnRouteTaskFAC}: (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose a targets (enemy ground controllable) around as well as other assigned targets. --- * @{#CONTROLLABLE.EnRouteTaskFAC_EngageControllable}: (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose the target (enemy ground controllable) as well as other assigned targets. --- * @{#CONTROLLABLE.EnRouteTaskTanker}: (AIR) Aircraft will act as a tanker for friendly units. No parameters. --- --- ### 1.2.3) Preparation task methods --- --- There are certain task methods that allow to tailor the task behaviour: --- --- * @{#CONTROLLABLE.TaskWrappedAction}: Return a WrappedAction Task taking a Command. --- * @{#CONTROLLABLE.TaskCombo}: Return a Combo Task taking an array of Tasks. --- * @{#CONTROLLABLE.TaskCondition}: Return a condition section for a controlled task. --- * @{#CONTROLLABLE.TaskControlled}: Return a Controlled Task taking a Task and a TaskCondition. --- --- ### 1.2.4) Obtain the mission from controllable templates --- --- Controllable templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a controllable and assign it to another: --- --- * @{#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. --- --- 1.3) CONTROLLABLE Command methods --- -------------------------- --- Controllable **command methods** prepare the execution of commands using the @{#CONTROLLABLE.SetCommand} method: --- --- * @{#CONTROLLABLE.CommandDoScript}: Do Script command. --- * @{#CONTROLLABLE.CommandSwitchWayPoint}: Perform a switch waypoint command. --- --- 1.4) CONTROLLABLE Option methods --- ------------------------- --- Controllable **Option methods** change the behaviour of the Controllable while being alive. --- --- ### 1.4.1) Rule of Engagement: --- --- * @{#CONTROLLABLE.OptionROEWeaponFree} --- * @{#CONTROLLABLE.OptionROEOpenFire} --- * @{#CONTROLLABLE.OptionROEReturnFire} --- * @{#CONTROLLABLE.OptionROEEvadeFire} --- --- To check whether an ROE option is valid for a specific controllable, use: --- --- * @{#CONTROLLABLE.OptionROEWeaponFreePossible} --- * @{#CONTROLLABLE.OptionROEOpenFirePossible} --- * @{#CONTROLLABLE.OptionROEReturnFirePossible} --- * @{#CONTROLLABLE.OptionROEEvadeFirePossible} --- --- ### 1.4.2) Rule on thread: --- --- * @{#CONTROLLABLE.OptionROTNoReaction} --- * @{#CONTROLLABLE.OptionROTPassiveDefense} --- * @{#CONTROLLABLE.OptionROTEvadeFire} --- * @{#CONTROLLABLE.OptionROTVertical} --- --- To test whether an ROT option is valid for a specific controllable, use: --- --- * @{#CONTROLLABLE.OptionROTNoReactionPossible} --- * @{#CONTROLLABLE.OptionROTPassiveDefensePossible} --- * @{#CONTROLLABLE.OptionROTEvadeFirePossible} --- * @{#CONTROLLABLE.OptionROTVerticalPossible} --- --- === --- --- @module Controllable - ---- The CONTROLLABLE class --- @type CONTROLLABLE --- @extends Wrapper.Positionable#POSITIONABLE --- @field Dcs.DCSWrapper.Controllable#Controllable DCSControllable The DCS controllable class. --- @field #string ControllableName The name of the controllable. -CONTROLLABLE = { - ClassName = "CONTROLLABLE", - ControllableName = "", - WayPointFunctions = {}, -} - ---- Create a new CONTROLLABLE from a DCSControllable --- @param #CONTROLLABLE self --- @param Dcs.DCSWrapper.Controllable#Controllable ControllableName The DCS Controllable name --- @return #CONTROLLABLE self -function CONTROLLABLE:New( ControllableName ) - local self = BASE:Inherit( self, POSITIONABLE:New( ControllableName ) ) - self:F2( ControllableName ) - self.ControllableName = ControllableName - - self.TaskScheduler = SCHEDULER:New( self ) - return self -end - --- DCS Controllable methods support. - ---- Get the controller for the CONTROLLABLE. --- @param #CONTROLLABLE self --- @return Dcs.DCSController#Controller -function CONTROLLABLE:_GetController() - self:F2( { self.ControllableName } ) - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local ControllableController = DCSControllable:getController() - self:T3( ControllableController ) - return ControllableController - end - - return nil -end - --- Get methods - ---- Returns the UNITs wrappers of the DCS Units of the Controllable (default is a GROUP). --- @param #CONTROLLABLE self --- @return #list The UNITs wrappers. -function CONTROLLABLE:GetUnits() - self:F2( { self.ControllableName } ) - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local DCSUnits = DCSControllable:getUnits() - local Units = {} - for Index, UnitData in pairs( DCSUnits ) do - Units[#Units+1] = UNIT:Find( UnitData ) - end - self:T3( Units ) - return Units - end - - return nil -end - - ---- Returns the health. Dead controllables have health <= 1.0. --- @param #CONTROLLABLE self --- @return #number The controllable health value (unit or group average). --- @return #nil The controllable is not existing or alive. -function CONTROLLABLE:GetLife() - self:F2( self.ControllableName ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local UnitLife = 0 - local Units = self:GetUnits() - if #Units == 1 then - local Unit = Units[1] -- Wrapper.Unit#UNIT - UnitLife = Unit:GetLife() - else - local UnitLifeTotal = 0 - for UnitID, Unit in pairs( Units ) do - local Unit = Unit -- Wrapper.Unit#UNIT - UnitLifeTotal = UnitLifeTotal + Unit:GetLife() - end - UnitLife = UnitLifeTotal / #Units - end - return UnitLife - end - - return nil -end - - - --- Tasks - ---- Clear all tasks from the controllable. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE -function CONTROLLABLE:ClearTasks() - self:F2() - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local Controller = self:_GetController() - Controller:resetTask() - return self - end - - return nil -end - - ---- Popping current Task from the controllable. --- @param #CONTROLLABLE self --- @return Wrapper.Controllable#CONTROLLABLE self -function CONTROLLABLE:PopCurrentTask() - self:F2() - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local Controller = self:_GetController() - Controller:popTask() - return self - end - - return nil -end - ---- Pushing Task on the queue from the controllable. --- @param #CONTROLLABLE self --- @return Wrapper.Controllable#CONTROLLABLE self -function CONTROLLABLE:PushTask( DCSTask, WaitTime ) - self:F2() - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local Controller = self:_GetController() - - -- When a controllable SPAWNs, it takes about a second to get the controllable in the simulator. Setting tasks to unspawned controllables provides unexpected results. - -- Therefore we schedule the functions to set the mission and options for the Controllable. - -- Controller:pushTask( DCSTask ) - - if WaitTime then - self.TaskScheduler:Schedule( Controller, Controller.pushTask, { DCSTask }, WaitTime ) - else - Controller:pushTask( DCSTask ) - end - - return self - end - - return nil -end - ---- Clearing the Task Queue and Setting the Task on the queue from the controllable. --- @param #CONTROLLABLE self --- @return Wrapper.Controllable#CONTROLLABLE self -function CONTROLLABLE:SetTask( DCSTask, WaitTime ) - self:F2( { DCSTask } ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - - local Controller = self:_GetController() - self:T3( Controller ) - - -- When a controllable SPAWNs, it takes about a second to get the controllable in the simulator. Setting tasks to unspawned controllables provides unexpected results. - -- Therefore we schedule the functions to set the mission and options for the Controllable. - -- Controller.setTask( Controller, DCSTask ) - - if not WaitTime then - Controller:setTask( DCSTask ) - else - self.TaskScheduler:Schedule( Controller, Controller.setTask, { DCSTask }, WaitTime ) - end - - return self - end - - return nil -end - - ---- Return a condition section for a controlled task. --- @param #CONTROLLABLE self --- @param Dcs.DCSTime#Time time --- @param #string userFlag --- @param #boolean userFlagValue --- @param #string condition --- @param Dcs.DCSTime#Time duration --- @param #number lastWayPoint --- return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:TaskCondition( time, userFlag, userFlagValue, condition, duration, lastWayPoint ) - self:F2( { time, userFlag, userFlagValue, condition, duration, lastWayPoint } ) - - local DCSStopCondition = {} - DCSStopCondition.time = time - DCSStopCondition.userFlag = userFlag - DCSStopCondition.userFlagValue = userFlagValue - DCSStopCondition.condition = condition - DCSStopCondition.duration = duration - DCSStopCondition.lastWayPoint = lastWayPoint - - self:T3( { DCSStopCondition } ) - return DCSStopCondition -end - ---- Return a Controlled Task taking a Task and a TaskCondition. --- @param #CONTROLLABLE self --- @param Dcs.DCSTasking.Task#Task DCSTask --- @param #DCSStopCondition DCSStopCondition --- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:TaskControlled( DCSTask, DCSStopCondition ) - self:F2( { DCSTask, DCSStopCondition } ) - - local DCSTaskControlled - - DCSTaskControlled = { - id = 'ControlledTask', - params = { - task = DCSTask, - stopCondition = DCSStopCondition - } - } - - self:T3( { DCSTaskControlled } ) - return DCSTaskControlled -end - ---- Return a Combo Task taking an array of Tasks. --- @param #CONTROLLABLE self --- @param Dcs.DCSTasking.Task#TaskArray DCSTasks Array of @{DCSTasking.Task#Task} --- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:TaskCombo( DCSTasks ) - self:F2( { DCSTasks } ) - - local DCSTaskCombo - - DCSTaskCombo = { - id = 'ComboTask', - params = { - tasks = DCSTasks - } - } - - for TaskID, Task in ipairs( DCSTasks ) do - self:E( Task ) - end - - self:T3( { DCSTaskCombo } ) - return DCSTaskCombo -end - ---- Return a WrappedAction Task taking a Command. --- @param #CONTROLLABLE self --- @param Dcs.DCSCommand#Command DCSCommand --- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:TaskWrappedAction( DCSCommand, Index ) - self:F2( { DCSCommand } ) - - local DCSTaskWrappedAction - - DCSTaskWrappedAction = { - id = "WrappedAction", - enabled = true, - number = Index, - auto = false, - params = { - action = DCSCommand, - }, - } - - self:T3( { DCSTaskWrappedAction } ) - return DCSTaskWrappedAction -end - ---- Executes a command action --- @param #CONTROLLABLE self --- @param Dcs.DCSCommand#Command DCSCommand --- @return #CONTROLLABLE self -function CONTROLLABLE:SetCommand( DCSCommand ) - self:F2( DCSCommand ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local Controller = self:_GetController() - Controller:setCommand( DCSCommand ) - return self - end - - return nil -end - ---- Perform a switch waypoint command --- @param #CONTROLLABLE self --- @param #number FromWayPoint --- @param #number ToWayPoint --- @return Dcs.DCSTasking.Task#Task --- @usage --- --- This test demonstrates the use(s) of the SwitchWayPoint method of the GROUP class. --- HeliGroup = GROUP:FindByName( "Helicopter" ) --- --- --- Route the helicopter back to the FARP after 60 seconds. --- -- We use the SCHEDULER class to do this. --- SCHEDULER:New( nil, --- function( HeliGroup ) --- local CommandRTB = HeliGroup:CommandSwitchWayPoint( 2, 8 ) --- HeliGroup:SetCommand( CommandRTB ) --- end, { HeliGroup }, 90 --- ) -function CONTROLLABLE:CommandSwitchWayPoint( FromWayPoint, ToWayPoint ) - self:F2( { FromWayPoint, ToWayPoint } ) - - local CommandSwitchWayPoint = { - id = 'SwitchWaypoint', - params = { - fromWaypointIndex = FromWayPoint, - goToWaypointIndex = ToWayPoint, - }, - } - - self:T3( { CommandSwitchWayPoint } ) - return CommandSwitchWayPoint -end - ---- Create a stop route command, which returns a string containing the command. --- Use the result in the method @{#CONTROLLABLE.SetCommand}(). --- A value of true will make the ground group stop, a value of false will make it continue. --- Note that this can only work on GROUP level, although individual UNITs can be commanded, the whole GROUP will react. --- --- Example missions: --- --- * GRP-310 --- --- @param #CONTROLLABLE self --- @param #boolean StopRoute true if the ground unit needs to stop, false if it needs to continue to move. --- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:CommandStopRoute( StopRoute ) - self:F2( { StopRoute } ) - - local CommandStopRoute = { - id = 'StopRoute', - params = { - value = StopRoute, - }, - } - - self:T3( { CommandStopRoute } ) - return CommandStopRoute -end - - --- TASKS FOR AIR CONTROLLABLES - - ---- (AIR) Attack a Controllable. --- @param #CONTROLLABLE self --- @param Wrapper.Controllable#CONTROLLABLE AttackGroup The Controllable to be attacked. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param Dcs.DCSTypes#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude. --- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit ) - self:F2( { self.ControllableName, AttackGroup, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit } ) - - -- AttackGroup = { - -- id = 'AttackGroup', - -- params = { - -- groupId = Group.ID, - -- weaponType = number, - -- expend = enum AI.Task.WeaponExpend, - -- attackQty = number, - -- directionEnabled = boolean, - -- direction = Azimuth, - -- altitudeEnabled = boolean, - -- altitude = Distance, - -- attackQtyLimit = boolean, - -- } - -- } - - local DirectionEnabled = nil - if Direction then - DirectionEnabled = true - end - - local AltitudeEnabled = nil - if Altitude then - AltitudeEnabled = true - end - - local DCSTask - DCSTask = { id = 'AttackGroup', - params = { - groupId = AttackGroup:GetID(), - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - directionEnabled = DirectionEnabled, - direction = Direction, - altitudeEnabled = AltitudeEnabled, - altitude = Altitude, - attackQtyLimit = AttackQtyLimit, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - ---- (AIR) Attack the Unit. --- @param #CONTROLLABLE self --- @param Wrapper.Unit#UNIT AttackUnit The UNIT. --- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #number Altitude (optional) The altitude from where to attack. --- @param #boolean Visible (optional) not a clue. --- @param #number WeaponType (optional) The WeaponType. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType ) - self:F2( { self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType } ) - - local DCSTask - DCSTask = { - id = 'AttackUnit', - params = { - unitId = AttackUnit:GetID(), - groupAttack = GroupAttack or false, - visible = Visible or false, - expend = WeaponExpend or "Auto", - directionEnabled = Direction and true or false, - direction = Direction, - altitudeEnabled = Altitude and true or false, - altitude = Altitude or 30, - attackQtyLimit = AttackQty and true or false, - attackQty = AttackQty, - weaponType = WeaponType - } - } - - self:E( DCSTask ) - - return DCSTask -end - - ---- (AIR) Delivering weapon at the point on the ground. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) Desired quantity of passes. The parameter is not the same in AttackGroup and AttackUnit tasks. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskBombing( Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack ) - self:F2( { self.ControllableName, Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack } ) - --- Bombing = { --- id = 'Bombing', --- params = { --- point = Vec2, --- weaponType = number, --- expend = enum AI.Task.WeaponExpend, --- attackQty = number, --- direction = Azimuth, --- controllableAttack = boolean, --- } --- } - - local DCSTask - DCSTask = { id = 'Bombing', - params = { - point = Vec2, - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - direction = Direction, - controllableAttack = ControllableAttack, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - ---- (AIR) Orbit at a specified position at a specified alititude during a specified duration with a specified speed. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Point The point to hold the position. --- @param #number Altitude The altitude to hold the position. --- @param #number Speed The speed flying when holding the position. --- @return #CONTROLLABLE self -function CONTROLLABLE:TaskOrbitCircleAtVec2( Point, Altitude, Speed ) - self:F2( { self.ControllableName, Point, Altitude, Speed } ) - - -- pattern = enum AI.Task.OribtPattern, - -- point = Vec2, - -- point2 = Vec2, - -- speed = Distance, - -- altitude = Distance - - local LandHeight = land.getHeight( Point ) - - self:T3( { LandHeight } ) - - local DCSTask = { id = 'Orbit', - params = { pattern = AI.Task.OrbitPattern.CIRCLE, - point = Point, - speed = Speed, - altitude = Altitude + LandHeight - } - } - - - -- local AITask = { id = 'ControlledTask', - -- params = { task = { id = 'Orbit', - -- params = { pattern = AI.Task.OrbitPattern.CIRCLE, - -- point = Point, - -- speed = Speed, - -- altitude = Altitude + LandHeight - -- } - -- }, - -- stopCondition = { duration = Duration - -- } - -- } - -- } - -- ) - - return DCSTask -end - ---- (AIR) Orbit at the current position of the first unit of the controllable at a specified alititude. --- @param #CONTROLLABLE self --- @param #number Altitude The altitude to hold the position. --- @param #number Speed The speed flying when holding the position. --- @return #CONTROLLABLE self -function CONTROLLABLE:TaskOrbitCircle( Altitude, Speed ) - self:F2( { self.ControllableName, Altitude, Speed } ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local ControllablePoint = self:GetVec2() - return self:TaskOrbitCircleAtVec2( ControllablePoint, Altitude, Speed ) - end - - return nil -end - - - ---- (AIR) Hold position at the current position of the first unit of the controllable. --- @param #CONTROLLABLE self --- @param #number Duration The maximum duration in seconds to hold the position. --- @return #CONTROLLABLE self -function CONTROLLABLE:TaskHoldPosition() - self:F2( { self.ControllableName } ) - - return self:TaskOrbitCircle( 30, 10 ) -end - - - - ---- (AIR) Attacking the map object (building, structure, e.t.c). --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the point the map object is closest to. The distance between the point and the map object must not be greater than 2000 meters. Object id is not used here because Mission Editor doesn't support map object identificators. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackMapObject( Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack ) - self:F2( { self.ControllableName, Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack } ) - --- AttackMapObject = { --- id = 'AttackMapObject', --- params = { --- point = Vec2, --- weaponType = number, --- expend = enum AI.Task.WeaponExpend, --- attackQty = number, --- direction = Azimuth, --- controllableAttack = boolean, --- } --- } - - local DCSTask - DCSTask = { id = 'AttackMapObject', - params = { - point = Vec2, - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - direction = Direction, - controllableAttack = ControllableAttack, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR) Delivering weapon on the runway. --- @param #CONTROLLABLE self --- @param Wrapper.Airbase#AIRBASE Airbase Airbase to attack. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskBombingRunway( Airbase, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack ) - self:F2( { self.ControllableName, Airbase, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack } ) - --- BombingRunway = { --- id = 'BombingRunway', --- params = { --- runwayId = AirdromeId, --- weaponType = number, --- expend = enum AI.Task.WeaponExpend, --- attackQty = number, --- direction = Azimuth, --- controllableAttack = boolean, --- } --- } - - local DCSTask - DCSTask = { id = 'BombingRunway', - params = { - point = Airbase:GetID(), - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - direction = Direction, - controllableAttack = ControllableAttack, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR) Refueling from the nearest tanker. No parameters. --- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskRefueling() - self:F2( { self.ControllableName } ) - --- Refueling = { --- id = 'Refueling', --- params = {} --- } - - local DCSTask - DCSTask = { id = 'Refueling', - params = { - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR HELICOPTER) Landing at the ground. For helicopters only. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Point The point where to land. --- @param #number Duration The duration in seconds to stay on the ground. --- @return #CONTROLLABLE self -function CONTROLLABLE:TaskLandAtVec2( Point, Duration ) - self:F2( { self.ControllableName, Point, Duration } ) - --- Land = { --- id= 'Land', --- params = { --- point = Vec2, --- durationFlag = boolean, --- duration = Time --- } --- } - - local DCSTask - if Duration and Duration > 0 then - DCSTask = { id = 'Land', - params = { - point = Point, - durationFlag = true, - duration = Duration, - }, - } - else - DCSTask = { id = 'Land', - params = { - point = Point, - durationFlag = false, - }, - } - end - - self:T3( DCSTask ) - return DCSTask -end - ---- (AIR) Land the controllable at a @{Zone#ZONE_RADIUS). --- @param #CONTROLLABLE self --- @param Core.Zone#ZONE Zone The zone where to land. --- @param #number Duration The duration in seconds to stay on the ground. --- @return #CONTROLLABLE self -function CONTROLLABLE:TaskLandAtZone( Zone, Duration, RandomPoint ) - self:F2( { self.ControllableName, Zone, Duration, RandomPoint } ) - - local Point - if RandomPoint then - Point = Zone:GetRandomVec2() - else - Point = Zone:GetVec2() - end - - local DCSTask = self:TaskLandAtVec2( Point, Duration ) - - self:T3( DCSTask ) - return DCSTask -end - - - ---- (AIR) Following another airborne controllable. --- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders. --- If another controllable is on land the unit / controllable will orbit around. --- @param #CONTROLLABLE self --- @param Wrapper.Controllable#CONTROLLABLE FollowControllable The controllable to be followed. --- @param Dcs.DCSTypes#Vec3 Vec3 Position of the unit / lead unit of the controllable relative lead unit of another controllable in frame reference oriented by course of lead unit of another controllable. If another controllable is on land the unit / controllable will orbit around. --- @param #number LastWaypointIndex Detach waypoint of another controllable. Once reached the unit / controllable Follow task is finished. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskFollow( FollowControllable, Vec3, LastWaypointIndex ) - self:F2( { self.ControllableName, FollowControllable, Vec3, LastWaypointIndex } ) - --- Follow = { --- id = 'Follow', --- params = { --- groupId = Group.ID, --- pos = Vec3, --- lastWptIndexFlag = boolean, --- lastWptIndex = number --- } --- } - - local LastWaypointIndexFlag = false - if LastWaypointIndex then - LastWaypointIndexFlag = true - end - - local DCSTask - DCSTask = { - id = 'Follow', - params = { - groupId = FollowControllable:GetID(), - pos = Vec3, - lastWptIndexFlag = LastWaypointIndexFlag, - lastWptIndex = LastWaypointIndex - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR) Escort another airborne controllable. --- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders. --- The unit / controllable will also protect that controllable from threats of specified types. --- @param #CONTROLLABLE self --- @param Wrapper.Controllable#CONTROLLABLE EscortControllable The controllable to be escorted. --- @param Dcs.DCSTypes#Vec3 Vec3 Position of the unit / lead unit of the controllable relative lead unit of another controllable in frame reference oriented by course of lead unit of another controllable. If another controllable is on land the unit / controllable will orbit around. --- @param #number LastWaypointIndex Detach waypoint of another controllable. Once reached the unit / controllable Follow task is finished. --- @param #number EngagementDistanceMax Maximal distance from escorted controllable to threat. If the threat is already engaged by escort escort will disengage if the distance becomes greater than 1.5 * engagementDistMax. --- @param Dcs.DCSTypes#AttributeNameArray TargetTypes Array of AttributeName that is contains threat categories allowed to engage. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskEscort( FollowControllable, Vec3, LastWaypointIndex, EngagementDistance, TargetTypes ) - self:F2( { self.ControllableName, FollowControllable, Vec3, LastWaypointIndex, EngagementDistance, TargetTypes } ) - --- Escort = { --- id = 'Escort', --- params = { --- groupId = Group.ID, --- pos = Vec3, --- lastWptIndexFlag = boolean, --- lastWptIndex = number, --- engagementDistMax = Distance, --- targetTypes = array of AttributeName, --- } --- } - - local LastWaypointIndexFlag = false - if LastWaypointIndex then - LastWaypointIndexFlag = true - end - - local DCSTask - DCSTask = { id = 'Escort', - params = { - groupId = FollowControllable:GetID(), - pos = Vec3, - lastWptIndexFlag = LastWaypointIndexFlag, - lastWptIndex = LastWaypointIndex, - engagementDistMax = EngagementDistance, - targetTypes = TargetTypes, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - - --- GROUND TASKS - ---- (GROUND) Fire at a VEC2 point until ammunition is finished. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 The point to fire at. --- @param Dcs.DCSTypes#Distance Radius The radius of the zone to deploy the fire at. --- @param #number AmmoCount (optional) Quantity of ammunition to expand (omit to fire until ammunition is depleted). --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount ) - self:F2( { self.ControllableName, Vec2, Radius, AmmoCount } ) - - -- FireAtPoint = { - -- id = 'FireAtPoint', - -- params = { - -- point = Vec2, - -- radius = Distance, - -- expendQty = number, - -- expendQtyEnabled = boolean, - -- } - -- } - - local DCSTask - DCSTask = { id = 'FireAtPoint', - params = { - point = Vec2, - radius = Radius, - expendQty = 100, -- dummy value - expendQtyEnabled = false, - } - } - - if AmmoCount then - DCSTask.params.expendQty = AmmoCount - DCSTask.params.expendQtyEnabled = true - end - - self:T3( { DCSTask } ) - return DCSTask -end - ---- (GROUND) Hold ground controllable from moving. --- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskHold() - self:F2( { self.ControllableName } ) - --- Hold = { --- id = 'Hold', --- params = { --- } --- } - - local DCSTask - DCSTask = { id = 'Hold', - params = { - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - --- TASKS FOR AIRBORNE AND GROUND UNITS/CONTROLLABLES - ---- (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction. --- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC. --- If the task is assigned to the controllable lead unit will be a FAC. --- @param #CONTROLLABLE self --- @param Wrapper.Controllable#CONTROLLABLE AttackGroup Target CONTROLLABLE. --- @param #number WeaponType Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.Designation Designation (optional) Designation type. --- @param #boolean Datalink (optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink ) - self:F2( { self.ControllableName, AttackGroup, WeaponType, Designation, Datalink } ) - --- FAC_AttackGroup = { --- id = 'FAC_AttackGroup', --- params = { --- groupId = Group.ID, --- weaponType = number, --- designation = enum AI.Task.Designation, --- datalink = boolean --- } --- } - - local DCSTask - DCSTask = { id = 'FAC_AttackGroup', - params = { - groupId = AttackGroup:GetID(), - weaponType = WeaponType, - designation = Designation, - datalink = Datalink, - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - --- EN-ACT_ROUTE TASKS FOR AIRBORNE CONTROLLABLES - ---- (AIR) Engaging targets of defined types. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Distance Distance Maximal distance from the target to a route leg. If the target is on a greater distance it will be ignored. --- @param Dcs.DCSTypes#AttributeNameArray TargetTypes Array of target categories allowed to engage. --- @param #number Priority All enroute tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskEngageTargets( Distance, TargetTypes, Priority ) - self:F2( { self.ControllableName, Distance, TargetTypes, Priority } ) - --- EngageTargets ={ --- id = 'EngageTargets', --- params = { --- maxDist = Distance, --- targetTypes = array of AttributeName, --- priority = number --- } --- } - - local DCSTask - DCSTask = { id = 'EngageTargets', - params = { - maxDist = Distance, - targetTypes = TargetTypes, - priority = Priority - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - - ---- (AIR) Engaging a targets of defined types at circle-shaped zone. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the zone. --- @param Dcs.DCSTypes#Distance Radius Radius of the zone. --- @param Dcs.DCSTypes#AttributeNameArray TargetTypes Array of target categories allowed to engage. --- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskEngageTargets( Vec2, Radius, TargetTypes, Priority ) - self:F2( { self.ControllableName, Vec2, Radius, TargetTypes, Priority } ) - --- EngageTargetsInZone = { --- id = 'EngageTargetsInZone', --- params = { --- point = Vec2, --- zoneRadius = Distance, --- targetTypes = array of AttributeName, --- priority = number --- } --- } - - local DCSTask - DCSTask = { id = 'EngageTargetsInZone', - params = { - point = Vec2, - zoneRadius = Radius, - targetTypes = TargetTypes, - priority = Priority - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now; it just allows the unit/controllable to engage the target controllable as well as other assigned targets. --- @param #CONTROLLABLE self --- @param Wrapper.Controllable#CONTROLLABLE AttackGroup The Controllable to be attacked. --- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param Dcs.DCSTypes#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude. --- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit ) - self:F2( { self.ControllableName, AttackGroup, Priority, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit } ) - - -- EngageControllable = { - -- id = 'EngageControllable ', - -- params = { - -- groupId = Group.ID, - -- weaponType = number, - -- expend = enum AI.Task.WeaponExpend, - -- attackQty = number, - -- directionEnabled = boolean, - -- direction = Azimuth, - -- altitudeEnabled = boolean, - -- altitude = Distance, - -- attackQtyLimit = boolean, - -- priority = number, - -- } - -- } - - local DirectionEnabled = nil - if Direction then - DirectionEnabled = true - end - - local AltitudeEnabled = nil - if Altitude then - AltitudeEnabled = true - end - - local DCSTask - DCSTask = { id = 'EngageControllable', - params = { - groupId = AttackGroup:GetID(), - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - directionEnabled = DirectionEnabled, - direction = Direction, - altitudeEnabled = AltitudeEnabled, - altitude = Altitude, - attackQtyLimit = AttackQtyLimit, - priority = Priority, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR) Search and attack the Unit. --- @param #CONTROLLABLE self --- @param Wrapper.Unit#UNIT EngageUnit The UNIT. --- @param #number Priority (optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param Dcs.DCSTypes#Distance Altitude (optional) Desired altitude to perform the unit engagement. --- @param #boolean Visible (optional) Unit must be visible. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskEngageUnit( EngageUnit, Priority, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack ) - self:F2( { self.ControllableName, EngageUnit, Priority, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack } ) - - -- EngageUnit = { - -- id = 'EngageUnit', - -- params = { - -- unitId = Unit.ID, - -- weaponType = number, - -- expend = enum AI.Task.WeaponExpend - -- attackQty = number, - -- direction = Azimuth, - -- attackQtyLimit = boolean, - -- controllableAttack = boolean, - -- priority = number, - -- } - -- } - - local DCSTask - DCSTask = { id = 'EngageUnit', - params = { - unitId = EngageUnit:GetID(), - priority = Priority or 1, - groupAttack = GroupAttack or false, - visible = Visible or false, - expend = WeaponExpend or "Auto", - directionEnabled = Direction and true or false, - direction = Direction, - altitudeEnabled = Altitude and true or false, - altitude = Altitude, - attackQtyLimit = AttackQty and true or false, - attackQty = AttackQty, - controllableAttack = ControllableAttack, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - - - ---- (AIR) Aircraft will act as an AWACS for friendly units (will provide them with information about contacts). No parameters. --- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskAWACS( ) - self:F2( { self.ControllableName } ) - --- AWACS = { --- id = 'AWACS', --- params = { --- } --- } - - local DCSTask - DCSTask = { id = 'AWACS', - params = { - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR) Aircraft will act as a tanker for friendly units. No parameters. --- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskTanker( ) - self:F2( { self.ControllableName } ) - --- Tanker = { --- id = 'Tanker', --- params = { --- } --- } - - local DCSTask - DCSTask = { id = 'Tanker', - params = { - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - --- En-route tasks for ground units/controllables - ---- (GROUND) Ground unit (EW-radar) will act as an EWR for friendly units (will provide them with information about contacts). No parameters. --- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskEWR( ) - self:F2( { self.ControllableName } ) - --- EWR = { --- id = 'EWR', --- params = { --- } --- } - - local DCSTask - DCSTask = { id = 'EWR', - params = { - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - --- En-route tasks for airborne and ground units/controllables - ---- (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose the target (enemy ground controllable) as well as other assigned targets. --- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC. --- If the task is assigned to the controllable lead unit will be a FAC. --- @param #CONTROLLABLE self --- @param Wrapper.Controllable#CONTROLLABLE AttackGroup Target CONTROLLABLE. --- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @param #number WeaponType Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.Designation Designation (optional) Designation type. --- @param #boolean Datalink (optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskFAC_EngageGroup( AttackGroup, Priority, WeaponType, Designation, Datalink ) - self:F2( { self.ControllableName, AttackGroup, WeaponType, Priority, Designation, Datalink } ) - --- FAC_EngageControllable = { --- id = 'FAC_EngageControllable', --- params = { --- groupId = Group.ID, --- weaponType = number, --- designation = enum AI.Task.Designation, --- datalink = boolean, --- priority = number, --- } --- } - - local DCSTask - DCSTask = { id = 'FAC_EngageControllable', - params = { - groupId = AttackGroup:GetID(), - weaponType = WeaponType, - designation = Designation, - datalink = Datalink, - priority = Priority, - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose a targets (enemy ground controllable) around as well as other assigned targets. --- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC. --- If the task is assigned to the controllable lead unit will be a FAC. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Distance Radius The maximal distance from the FAC to a target. --- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskFAC( Radius, Priority ) - self:F2( { self.ControllableName, Radius, Priority } ) - --- FAC = { --- id = 'FAC', --- params = { --- radius = Distance, --- priority = number --- } --- } - - local DCSTask - DCSTask = { id = 'FAC', - params = { - radius = Radius, - priority = Priority - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - - - ---- (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Point The point where to wait. --- @param #number Duration The duration in seconds to wait. --- @param #CONTROLLABLE EmbarkingControllable The controllable to be embarked. --- @return Dcs.DCSTasking.Task#Task The DCS task structure -function CONTROLLABLE:TaskEmbarking( Point, Duration, EmbarkingControllable ) - self:F2( { self.ControllableName, Point, Duration, EmbarkingControllable.DCSControllable } ) - - local DCSTask - DCSTask = { id = 'Embarking', - params = { x = Point.x, - y = Point.y, - duration = Duration, - controllablesForEmbarking = { EmbarkingControllable.ControllableID }, - durationFlag = true, - distributionFlag = false, - distribution = {}, - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - ---- (GROUND) Embark to a Transport landed at a location. - ---- Move to a defined Vec2 Point, and embark to a controllable when arrived within a defined Radius. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Point The point where to wait. --- @param #number Radius The radius of the embarking zone around the Point. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskEmbarkToTransport( Point, Radius ) - self:F2( { self.ControllableName, Point, Radius } ) - - local DCSTask --Dcs.DCSTasking.Task#Task - DCSTask = { id = 'EmbarkToTransport', - params = { x = Point.x, - y = Point.y, - zoneRadius = Radius, - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - - ---- (AIR + GROUND) Return a mission task from a mission template. --- @param #CONTROLLABLE self --- @param #table TaskMission A table containing the mission task. --- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:TaskMission( TaskMission ) - self:F2( Points ) - - local DCSTask - DCSTask = { id = 'Mission', params = { TaskMission, }, } - - self:T3( { DCSTask } ) - return DCSTask -end - ---- Return a Misson task to follow a given route defined by Points. --- @param #CONTROLLABLE self --- @param #table Points A table of route points. --- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:TaskRoute( Points ) - self:F2( Points ) - - local DCSTask - DCSTask = { id = 'Mission', params = { route = { points = Points, }, }, } - - self:T3( { DCSTask } ) - return DCSTask -end - ---- (AIR + GROUND) Make the Controllable move to fly to a given point. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format. --- @param #number Speed The speed to travel. --- @return #CONTROLLABLE self -function CONTROLLABLE:RouteToVec2( Point, Speed ) - self:F2( { Point, Speed } ) - - local ControllablePoint = self:GetUnit( 1 ):GetVec2() - - local PointFrom = {} - PointFrom.x = ControllablePoint.x - PointFrom.y = ControllablePoint.y - PointFrom.type = "Turning Point" - PointFrom.action = "Turning Point" - PointFrom.speed = Speed - PointFrom.speed_locked = true - PointFrom.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - - - local PointTo = {} - PointTo.x = Point.x - PointTo.y = Point.y - PointTo.type = "Turning Point" - PointTo.action = "Fly Over Point" - PointTo.speed = Speed - PointTo.speed_locked = true - PointTo.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - - - local Points = { PointFrom, PointTo } - - self:T3( Points ) - - self:Route( Points ) - - return self -end - ---- (AIR + GROUND) Make the Controllable move to a given point. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format. --- @param #number Speed The speed to travel. --- @return #CONTROLLABLE self -function CONTROLLABLE:RouteToVec3( Point, Speed ) - self:F2( { Point, Speed } ) - - local ControllableVec3 = self:GetUnit( 1 ):GetVec3() - - local PointFrom = {} - PointFrom.x = ControllableVec3.x - PointFrom.y = ControllableVec3.z - PointFrom.alt = ControllableVec3.y - PointFrom.alt_type = "BARO" - PointFrom.type = "Turning Point" - PointFrom.action = "Turning Point" - PointFrom.speed = Speed - PointFrom.speed_locked = true - PointFrom.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - - - local PointTo = {} - PointTo.x = Point.x - PointTo.y = Point.z - PointTo.alt = Point.y - PointTo.alt_type = "BARO" - PointTo.type = "Turning Point" - PointTo.action = "Fly Over Point" - PointTo.speed = Speed - PointTo.speed_locked = true - PointTo.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - - - local Points = { PointFrom, PointTo } - - self:T3( Points ) - - self:Route( Points ) - - return self -end - - - ---- Make the controllable to follow a given route. --- @param #CONTROLLABLE self --- @param #table GoPoints A table of Route Points. --- @return #CONTROLLABLE self -function CONTROLLABLE:Route( GoPoints ) - self:F2( GoPoints ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local Points = routines.utils.deepCopy( GoPoints ) - local MissionTask = { id = 'Mission', params = { route = { points = Points, }, }, } - local Controller = self:_GetController() - --Controller.setTask( Controller, MissionTask ) - self.TaskScheduler:Schedule( Controller, Controller.setTask, { MissionTask }, 1 ) - return self - end - - return nil -end - - - ---- (AIR + GROUND) Route the controllable to a given zone. --- The controllable final destination point can be randomized. --- A speed can be given in km/h. --- A given formation can be given. --- @param #CONTROLLABLE self --- @param Core.Zone#ZONE Zone The zone where to route to. --- @param #boolean Randomize Defines whether to target point gets randomized within the Zone. --- @param #number Speed The speed. --- @param Base#FORMATION Formation The formation string. -function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation ) - self:F2( Zone ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - - local ControllablePoint = self:GetVec2() - - local PointFrom = {} - PointFrom.x = ControllablePoint.x - PointFrom.y = ControllablePoint.y - PointFrom.type = "Turning Point" - PointFrom.action = Formation or "Cone" - PointFrom.speed = 20 / 1.6 - - - local PointTo = {} - local ZonePoint - - if Randomize then - ZonePoint = Zone:GetRandomVec2() - else - ZonePoint = Zone:GetVec2() - end - - PointTo.x = ZonePoint.x - PointTo.y = ZonePoint.y - PointTo.type = "Turning Point" - - if Formation then - PointTo.action = Formation - else - PointTo.action = "Cone" - end - - if Speed then - PointTo.speed = Speed - else - PointTo.speed = 20 / 1.6 - end - - local Points = { PointFrom, PointTo } - - self:T3( Points ) - - self:Route( Points ) - - return self - end - - return nil -end - - --- Commands - ---- Do Script command --- @param #CONTROLLABLE self --- @param #string DoScript --- @return #DCSCommand -function CONTROLLABLE:CommandDoScript( DoScript ) - - local DCSDoScript = { - id = "Script", - params = { - command = DoScript, - }, - } - - self:T3( DCSDoScript ) - return DCSDoScript -end - - ---- Return the mission template of the controllable. --- @param #CONTROLLABLE self --- @return #table The MissionTemplate --- TODO: Rework the method how to retrieve a template ... -function CONTROLLABLE:GetTaskMission() - self:F2( self.ControllableName ) - - return routines.utils.deepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template ) -end - ---- Return the mission route of the controllable. --- @param #CONTROLLABLE self --- @return #table The mission route defined by points. -function CONTROLLABLE:GetTaskRoute() - self:F2( self.ControllableName ) - - return routines.utils.deepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template.route.points ) -end - - - ---- Return the route of a controllable by using the @{Database#DATABASE} class. --- @param #CONTROLLABLE self --- @param #number Begin The route point from where the copy will start. The base route point is 0. --- @param #number End The route point where the copy will end. The End point is the last point - the End point. The last point has base 0. --- @param #boolean Randomize Randomization of the route, when true. --- @param #number Radius When randomization is on, the randomization is within the radius. -function CONTROLLABLE:CopyRoute( Begin, End, Randomize, Radius ) - self:F2( { Begin, End } ) - - local Points = {} - - -- Could be a Spawned Controllable - local ControllableName = string.match( self:GetName(), ".*#" ) - if ControllableName then - ControllableName = ControllableName:sub( 1, -2 ) - else - ControllableName = self:GetName() - end - - self:T3( { ControllableName } ) - - local Template = _DATABASE.Templates.Controllables[ControllableName].Template - - if Template then - if not Begin then - Begin = 0 - end - if not End then - End = 0 - end - - for TPointID = Begin + 1, #Template.route.points - End do - if Template.route.points[TPointID] then - Points[#Points+1] = routines.utils.deepCopy( Template.route.points[TPointID] ) - if Randomize then - if not Radius then - Radius = 500 - end - Points[#Points].x = Points[#Points].x + math.random( Radius * -1, Radius ) - Points[#Points].y = Points[#Points].y + math.random( Radius * -1, Radius ) - end - end - end - return Points - else - error( "Template not found for Controllable : " .. ControllableName ) - end - - return nil -end - - ---- Return the detected targets of the controllable. --- The optional parametes specify the detection methods that can be applied. --- If no detection method is given, the detection will use all the available methods by default. --- @param Wrapper.Controllable#CONTROLLABLE self --- @param #boolean DetectVisual (optional) --- @param #boolean DetectOptical (optional) --- @param #boolean DetectRadar (optional) --- @param #boolean DetectIRST (optional) --- @param #boolean DetectRWR (optional) --- @param #boolean DetectDLINK (optional) --- @return #table DetectedTargets -function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK ) - self:F2( self.ControllableName ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local DetectionVisual = ( DetectVisual and DetectVisual == true ) and Controller.Detection.VISUAL or nil - local DetectionOptical = ( DetectOptical and DetectOptical == true ) and Controller.Detection.OPTICAL or nil - local DetectionRadar = ( DetectRadar and DetectRadar == true ) and Controller.Detection.RADAR or nil - local DetectionIRST = ( DetectIRST and DetectIRST == true ) and Controller.Detection.IRST or nil - local DetectionRWR = ( DetectRWR and DetectRWR == true ) and Controller.Detection.RWR or nil - local DetectionDLINK = ( DetectDLINK and DetectDLINK == true ) and Controller.Detection.DLINK or nil - - - return self:_GetController():getDetectedTargets( DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK ) - end - - return nil -end - -function CONTROLLABLE:IsTargetDetected( DCSObject ) - self:F2( self.ControllableName ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - - local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity - = self:_GetController().isTargetDetected( self:_GetController(), DCSObject, - Controller.Detection.VISUAL, - Controller.Detection.OPTIC, - Controller.Detection.RADAR, - Controller.Detection.IRST, - Controller.Detection.RWR, - Controller.Detection.DLINK - ) - return TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity - end - - return nil -end - --- Options - ---- Can the CONTROLLABLE hold their weapons? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROEHoldFirePossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() or self:IsGround() or self:IsShip() then - return true - end - - return false - end - - return nil -end - ---- Holding weapons. --- @param Wrapper.Controllable#CONTROLLABLE self --- @return Wrapper.Controllable#CONTROLLABLE self -function CONTROLLABLE:OptionROEHoldFire() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - elseif self:IsGround() then - Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.WEAPON_HOLD ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE attack returning on enemy fire? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROEReturnFirePossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() or self:IsGround() or self:IsShip() then - return true - end - - return false - end - - return nil -end - ---- Return fire. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROEReturnFire() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.RETURN_FIRE ) - elseif self:IsGround() then - Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.RETURN_FIRE ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.RETURN_FIRE ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE attack designated targets? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROEOpenFirePossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() or self:IsGround() or self:IsShip() then - return true - end - - return false - end - - return nil -end - ---- Openfire. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROEOpenFire() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - elseif self:IsGround() then - Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.OPEN_FIRE ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.OPEN_FIRE ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE attack targets of opportunity? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROEWeaponFreePossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() then - return true - end - - return false - end - - return nil -end - ---- Weapon free. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROEWeaponFree() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_FREE ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE ignore enemy fire? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROTNoReactionPossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() then - return true - end - - return false - end - - return nil -end - - ---- No evasion on enemy threats. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROTNoReaction() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.NO_REACTION ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE evade using passive defenses? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROTPassiveDefensePossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() then - return true - end - - return false - end - - return nil -end - ---- Evasion passive defense. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROTPassiveDefense() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.PASSIVE_DEFENCE ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE evade on enemy fire? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROTEvadeFirePossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() then - return true - end - - return false - end - - return nil -end - - ---- Evade on fire. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROTEvadeFire() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE evade on fire using vertical manoeuvres? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROTVerticalPossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() then - return true - end - - return false - end - - return nil -end - - ---- Evade on fire using vertical manoeuvres. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROTVertical() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) - end - - return self - end - - return nil -end - ---- Retrieve the controllable mission and allow to place function hooks within the mission waypoint plan. --- Use the method @{Controllable#CONTROLLABLE:WayPointFunction} to define the hook functions for specific waypoints. --- Use the method @{Controllable@CONTROLLABLE:WayPointExecute) to start the execution of the new mission plan. --- Note that when WayPointInitialize is called, the Mission of the controllable is RESTARTED! --- @param #CONTROLLABLE self --- @param #table WayPoints If WayPoints is given, then use the route. --- @return #CONTROLLABLE -function CONTROLLABLE:WayPointInitialize( WayPoints ) - self:F( { WayPoints } ) - - if WayPoints then - self.WayPoints = WayPoints - else - self.WayPoints = self:GetTaskRoute() - end - - return self -end - ---- Get the current WayPoints set with the WayPoint functions( Note that the WayPoints can be nil, although there ARE waypoints). --- @param #CONTROLLABLE self --- @return #table WayPoints If WayPoints is given, then return the WayPoints structure. -function CONTROLLABLE:GetWayPoints() - self:F( ) - - if self.WayPoints then - return self.WayPoints - end - - return nil -end - ---- Registers a waypoint function that will be executed when the controllable moves over the WayPoint. --- @param #CONTROLLABLE self --- @param #number WayPoint The waypoint number. Note that the start waypoint on the route is WayPoint 1! --- @param #number WayPointIndex When defining multiple WayPoint functions for one WayPoint, use WayPointIndex to set the sequence of actions. --- @param #function WayPointFunction The waypoint function to be called when the controllable moves over the waypoint. The waypoint function takes variable parameters. --- @return #CONTROLLABLE -function CONTROLLABLE:WayPointFunction( WayPoint, WayPointIndex, WayPointFunction, ... ) - self:F2( { WayPoint, WayPointIndex, WayPointFunction } ) - - table.insert( self.WayPoints[WayPoint].task.params.tasks, WayPointIndex ) - self.WayPoints[WayPoint].task.params.tasks[WayPointIndex] = self:TaskFunction( WayPoint, WayPointIndex, WayPointFunction, arg ) - return self -end - - -function CONTROLLABLE:TaskFunction( WayPoint, WayPointIndex, FunctionString, FunctionArguments ) - self:F2( { WayPoint, WayPointIndex, FunctionString, FunctionArguments } ) - - local DCSTask - - local DCSScript = {} - DCSScript[#DCSScript+1] = "local MissionControllable = GROUP:Find( ... ) " - - if FunctionArguments and #FunctionArguments > 0 then - DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable, " .. table.concat( FunctionArguments, "," ) .. ")" - else - DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable )" - end - - DCSTask = self:TaskWrappedAction( - self:CommandDoScript( - table.concat( DCSScript ) - ), WayPointIndex - ) - - self:T3( DCSTask ) - - return DCSTask - -end - ---- Executes the WayPoint plan. --- The function gets a WayPoint parameter, that you can use to restart the mission at a specific WayPoint. --- Note that when the WayPoint parameter is used, the new start mission waypoint of the controllable will be 1! --- @param #CONTROLLABLE self --- @param #number WayPoint The WayPoint from where to execute the mission. --- @param #number WaitTime The amount seconds to wait before initiating the mission. --- @return #CONTROLLABLE -function CONTROLLABLE:WayPointExecute( WayPoint, WaitTime ) - self:F( { WayPoint, WaitTime } ) - - if not WayPoint then - WayPoint = 1 - end - - -- When starting the mission from a certain point, the TaskPoints need to be deleted before the given WayPoint. - for TaskPointID = 1, WayPoint - 1 do - table.remove( self.WayPoints, 1 ) - end - - self:T3( self.WayPoints ) - - self:SetTask( self:TaskRoute( self.WayPoints ), WaitTime ) - - return self -end - --- Message APIs--- **Wrapper** -- GROUP is a wrapper class for the DCS Class Group. --- --- === --- --- The @{#GROUP} class is a wrapper class to handle the DCS Group objects: --- --- * Support all DCS Group APIs. --- * Enhance with Group specific APIs not in the DCS Group API set. --- * Handle local Group Controller. --- * Manage the "state" of the DCS Group. --- --- **IMPORTANT: ONE SHOULD NEVER SANATIZE these GROUP OBJECT REFERENCES! (make the GROUP object references nil).** --- --- See the detailed documentation on the GROUP class. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-26: GROUP:**RouteRTB( RTBAirbase, Speed )** added. --- --- 2017-03-07: GROUP:**HandleEvent( Event, EventFunction )** added. --- 2017-03-07: GROUP:**UnHandleEvent( Event )** added. --- --- 2017-01-24: GROUP:**SetAIOnOff( AIOnOff )** added. --- --- 2017-01-24: GROUP:**SetAIOn()** added. --- --- 2017-01-24: GROUP:**SetAIOff()** added. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * [**Entropy**](https://forums.eagle.ru/member.php?u=111471), **Afinegan**: Came up with the requirement for AIOnOff(). --- --- ### Authors: --- --- * **FlightControl**: Design & Programming --- --- @module Group --- @author FlightControl - ---- @type GROUP --- @extends Wrapper.Controllable#CONTROLLABLE --- @field #string GroupName The name of the group. - ---- --- # GROUP class, extends @{Controllable#CONTROLLABLE} --- --- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class). --- --- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference --- using the DCS Group or the DCS GroupName. --- --- Another thing to know is that GROUP objects do not "contain" the DCS Group object. --- The GROUP methods will reference the DCS Group object by name when it is needed during API execution. --- If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file. --- --- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance: --- --- * @{#GROUP.Find}(): Find a GROUP instance from the _DATABASE object using a DCS Group object. --- * @{#GROUP.FindByName}(): Find a GROUP instance from the _DATABASE object using a DCS Group name. --- --- ## GROUP task methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} task methods section for a description of the task methods. --- --- ### Obtain the mission from group templates --- --- Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another: --- --- * @{Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. --- --- ## GROUP Command methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} command methods section for a description of the command methods. --- --- ## GROUP option methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} option methods section for a description of the option methods. --- --- ## GROUP Zone validation methods --- --- The group can be validated whether it is completely, partly or not within a @{Zone}. --- Use the following Zone validation methods on the group: --- --- * @{#GROUP.IsCompletelyInZone}: Returns true if all units of the group are within a @{Zone}. --- * @{#GROUP.IsPartlyInZone}: Returns true if some units of the group are within a @{Zone}. --- * @{#GROUP.IsNotInZone}: Returns true if none of the group units of the group are within a @{Zone}. --- --- The zone can be of any @{Zone} class derived from @{Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on. --- --- ## GROUP AI methods --- --- A GROUP has AI methods to control the AI activation. --- --- * @{#GROUP.SetAIOnOff}(): Turns the GROUP AI On or Off. --- * @{#GROUP.SetAIOn}(): Turns the GROUP AI On. --- * @{#GROUP.SetAIOff}(): Turns the GROUP AI Off. --- --- @field #GROUP GROUP -GROUP = { - ClassName = "GROUP", -} - ---- Create a new GROUP from a DCSGroup --- @param #GROUP self --- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name --- @return #GROUP self -function GROUP:Register( GroupName ) - self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) - self:F2( GroupName ) - self.GroupName = GroupName - - self:SetEventPriority( 4 ) - return self -end - --- Reference methods. - ---- Find the GROUP wrapper class instance using the DCS Group. --- @param #GROUP self --- @param Dcs.DCSWrapper.Group#Group DCSGroup The DCS Group. --- @return #GROUP The GROUP. -function GROUP:Find( DCSGroup ) - - local GroupName = DCSGroup:getName() -- Wrapper.Group#GROUP - local GroupFound = _DATABASE:FindGroup( GroupName ) - return GroupFound -end - ---- Find the created GROUP using the DCS Group Name. --- @param #GROUP self --- @param #string GroupName The DCS Group Name. --- @return #GROUP The GROUP. -function GROUP:FindByName( GroupName ) - - local GroupFound = _DATABASE:FindGroup( GroupName ) - return GroupFound -end - --- DCS Group methods support. - ---- Returns the DCS Group. --- @param #GROUP self --- @return Dcs.DCSWrapper.Group#Group The DCS Group. -function GROUP:GetDCSObject() - local DCSGroup = Group.getByName( self.GroupName ) - - if DCSGroup then - return DCSGroup - end - - return nil -end - ---- Returns the @{DCSTypes#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Position The 3D position vectors of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionablePosition = DCSPositionable:getUnits()[1]:getPosition().p - self:T3( PositionablePosition ) - return PositionablePosition - end - - return nil -end - ---- Returns if the Group is alive. --- The Group must: --- --- * Exist at run-time. --- * Has at least one unit. --- --- When the first @{Unit} of the Group is active, it will return true. --- If the first @{Unit} of the Group is inactive, it will return false. --- --- @param #GROUP self --- @return #boolean true if the Group is alive and active. --- @return #boolean false if the Group is alive but inactive. --- @return #nil if the group does not exist anymore. -function GROUP:IsAlive() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() -- Dcs.DCSGroup#Group - - if DCSGroup then - if DCSGroup:isExist() then - local DCSUnit = DCSGroup:getUnit(1) -- Dcs.DCSUnit#Unit - if DCSUnit then - local GroupIsAlive = DCSUnit:isActive() - self:T3( GroupIsAlive ) - return GroupIsAlive - end - end - end - - return nil -end - ---- Destroys the DCS Group and all of its DCS Units. --- Note that this destroy method also raises a destroy event at run-time. --- So all event listeners will catch the destroy event of this DCS Group. --- @param #GROUP self -function GROUP:Destroy() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - for Index, UnitData in pairs( DCSGroup:getUnits() ) do - self:CreateEventCrash( timer.getTime(), UnitData ) - end - DCSGroup:destroy() - DCSGroup = nil - end - - return nil -end - ---- Returns category of the DCS Group. --- @param #GROUP self --- @return Dcs.DCSWrapper.Group#Group.Category The category ID -function GROUP:GetCategory() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - if DCSGroup then - local GroupCategory = DCSGroup:getCategory() - self:T3( GroupCategory ) - return GroupCategory - end - - return nil -end - ---- Returns the category name of the #GROUP. --- @param #GROUP self --- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship -function GROUP:GetCategoryName() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - if DCSGroup then - local CategoryNames = { - [Group.Category.AIRPLANE] = "Airplane", - [Group.Category.HELICOPTER] = "Helicopter", - [Group.Category.GROUND] = "Ground Unit", - [Group.Category.SHIP] = "Ship", - } - local GroupCategory = DCSGroup:getCategory() - self:T3( GroupCategory ) - - return CategoryNames[GroupCategory] - end - - return nil -end - - ---- Returns the coalition of the DCS Group. --- @param #GROUP self --- @return Dcs.DCSCoalitionWrapper.Object#coalition.side The coalition side of the DCS Group. -function GROUP:GetCoalition() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - if DCSGroup then - local GroupCoalition = DCSGroup:getCoalition() - self:T3( GroupCoalition ) - return GroupCoalition - end - - return nil -end - ---- Returns the country of the DCS Group. --- @param #GROUP self --- @return Dcs.DCScountry#country.id The country identifier. --- @return #nil The DCS Group is not existing or alive. -function GROUP:GetCountry() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - if DCSGroup then - local GroupCountry = DCSGroup:getUnit(1):getCountry() - self:T3( GroupCountry ) - return GroupCountry - end - - return nil -end - ---- Returns the UNIT wrapper class with number UnitNumber. --- If the underlying DCS Unit does not exist, the method will return nil. . --- @param #GROUP self --- @param #number UnitNumber The number of the UNIT wrapper class to be returned. --- @return Wrapper.Unit#UNIT The UNIT wrapper class. -function GROUP:GetUnit( UnitNumber ) - self:F2( { self.GroupName, UnitNumber } ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local UnitFound = UNIT:Find( DCSGroup:getUnit( UnitNumber ) ) - self:T2( UnitFound ) - return UnitFound - end - - return nil -end - ---- Returns the DCS Unit with number UnitNumber. --- If the underlying DCS Unit does not exist, the method will return nil. . --- @param #GROUP self --- @param #number UnitNumber The number of the DCS Unit to be returned. --- @return Dcs.DCSWrapper.Unit#Unit The DCS Unit. -function GROUP:GetDCSUnit( UnitNumber ) - self:F2( { self.GroupName, UnitNumber } ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local DCSUnitFound = DCSGroup:getUnit( UnitNumber ) - self:T3( DCSUnitFound ) - return DCSUnitFound - end - - return nil -end - ---- Returns current size of the DCS Group. --- If some of the DCS Units of the DCS Group are destroyed the size of the DCS Group is changed. --- @param #GROUP self --- @return #number The DCS Group size. -function GROUP:GetSize() - self:F2( { self.GroupName } ) - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupSize = DCSGroup:getSize() - self:T3( GroupSize ) - return GroupSize - end - - return nil -end - ---- ---- Returns the initial size of the DCS Group. --- If some of the DCS Units of the DCS Group are destroyed, the initial size of the DCS Group is unchanged. --- @param #GROUP self --- @return #number The DCS Group initial size. -function GROUP:GetInitialSize() - self:F2( { self.GroupName } ) - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupInitialSize = DCSGroup:getInitialSize() - self:T3( GroupInitialSize ) - return GroupInitialSize - end - - return nil -end - - ---- Returns the DCS Units of the DCS Group. --- @param #GROUP self --- @return #table The DCS Units. -function GROUP:GetDCSUnits() - self:F2( { self.GroupName } ) - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local DCSUnits = DCSGroup:getUnits() - self:T3( DCSUnits ) - return DCSUnits - end - - return nil -end - - ---- Activates a GROUP. --- @param #GROUP self -function GROUP:Activate() - self:F2( { self.GroupName } ) - trigger.action.activateGroup( self:GetDCSObject() ) - return self:GetDCSObject() -end - - ---- Gets the type name of the group. --- @param #GROUP self --- @return #string The type name of the group. -function GROUP:GetTypeName() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupTypeName = DCSGroup:getUnit(1):getTypeName() - self:T3( GroupTypeName ) - return( GroupTypeName ) - end - - return nil -end - ---- Gets the CallSign of the first DCS Unit of the DCS Group. --- @param #GROUP self --- @return #string The CallSign of the first DCS Unit of the DCS Group. -function GROUP:GetCallsign() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupCallSign = DCSGroup:getUnit(1):getCallsign() - self:T3( GroupCallSign ) - return GroupCallSign - end - - return nil -end - ---- Returns the current point (Vec2 vector) of the first DCS Unit in the DCS Group. --- @param #GROUP self --- @return Dcs.DCSTypes#Vec2 Current Vec2 point of the first DCS Unit of the DCS Group. -function GROUP:GetVec2() - self:F2( self.GroupName ) - - local UnitPoint = self:GetUnit(1) - UnitPoint:GetVec2() - local GroupPointVec2 = UnitPoint:GetVec2() - self:T3( GroupPointVec2 ) - return GroupPointVec2 -end - ---- Returns the current Vec3 vector of the first DCS Unit in the GROUP. --- @param #GROUP self --- @return Dcs.DCSTypes#Vec3 Current Vec3 of the first DCS Unit of the GROUP. -function GROUP:GetVec3() - self:F2( self.GroupName ) - - local GroupVec3 = self:GetUnit(1):GetVec3() - self:T3( GroupVec3 ) - return GroupVec3 -end - ---- Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission. --- @param #GROUP self --- @return Core.Point#POINT_VEC2 The 2D point vector of the first DCS Unit of the GROUP. --- @return #nil The first UNIT is not existing or alive. -function GROUP:GetPointVec2() - self:F2(self.GroupName) - - local FirstUnit = self:GetUnit(1) - - if FirstUnit then - local FirstUnitPointVec2 = FirstUnit:GetPointVec2() - self:T3(FirstUnitPointVec2) - return FirstUnitPointVec2 - end - - return nil -end - ---- Returns a random @{DCSTypes#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. --- @param #GROUP self --- @param #number Radius --- @return Dcs.DCSTypes#Vec3 The random 3D point vector around the first UNIT of the GROUP. --- @return #nil The GROUP is invalid or empty --- @usage --- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP -function GROUP:GetRandomVec3(Radius) - self:F2(self.GroupName) - - local FirstUnit = self:GetUnit(1) - - if FirstUnit then - local FirstUnitRandomPointVec3 = FirstUnit:GetRandomVec3(Radius) - self:T3(FirstUnitRandomPointVec3) - return FirstUnitRandomPointVec3 - end - - return nil -end - ---- Returns the mean heading of every UNIT in the GROUP in degrees --- @param #GROUP self --- @return #number mean heading of the GROUP --- @return #nil The first UNIT is not existing or alive. -function GROUP:GetHeading() - self:F2(self.GroupName) - - local GroupSize = self:GetSize() - local HeadingAccumulator = 0 - - if GroupSize then - for i = 1, GroupSize do - HeadingAccumulator = HeadingAccumulator + self:GetUnit(i):GetHeading() - end - return math.floor(HeadingAccumulator / GroupSize) - end - - return nil - -end - -do -- Is Zone methods - ---- Returns true if all units of the group are within a @{Zone}. --- @param #GROUP self --- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE} -function GROUP:IsCompletelyInZone( Zone ) - self:F2( { self.GroupName, Zone } ) - - for UnitID, UnitData in pairs( self:GetUnits() ) do - local Unit = UnitData -- Wrapper.Unit#UNIT - if Zone:IsVec3InZone( Unit:GetVec3() ) then - else - return false - end - end - - return true -end - ---- Returns true if some units of the group are within a @{Zone}. --- @param #GROUP self --- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE} -function GROUP:IsPartlyInZone( Zone ) - self:F2( { self.GroupName, Zone } ) - - for UnitID, UnitData in pairs( self:GetUnits() ) do - local Unit = UnitData -- Wrapper.Unit#UNIT - if Zone:IsVec3InZone( Unit:GetVec3() ) then - return true - end - end - - return false -end - ---- Returns true if none of the group units of the group are within a @{Zone}. --- @param #GROUP self --- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE} -function GROUP:IsNotInZone( Zone ) - self:F2( { self.GroupName, Zone } ) - - for UnitID, UnitData in pairs( self:GetUnits() ) do - local Unit = UnitData -- Wrapper.Unit#UNIT - if Zone:IsVec3InZone( Unit:GetVec3() ) then - return false - end - end - - return true -end - ---- Returns if the group is of an air category. --- If the group is a helicopter or a plane, then this method will return true, otherwise false. --- @param #GROUP self --- @return #boolean Air category evaluation result. -function GROUP:IsAir() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local IsAirResult = DCSGroup:getCategory() == Group.Category.AIRPLANE or DCSGroup:getCategory() == Group.Category.HELICOPTER - self:T3( IsAirResult ) - return IsAirResult - end - - return nil -end - ---- Returns if the DCS Group contains Helicopters. --- @param #GROUP self --- @return #boolean true if DCS Group contains Helicopters. -function GROUP:IsHelicopter() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupCategory = DCSGroup:getCategory() - self:T2( GroupCategory ) - return GroupCategory == Group.Category.HELICOPTER - end - - return nil -end - ---- Returns if the DCS Group contains AirPlanes. --- @param #GROUP self --- @return #boolean true if DCS Group contains AirPlanes. -function GROUP:IsAirPlane() - self:F2() - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupCategory = DCSGroup:getCategory() - self:T2( GroupCategory ) - return GroupCategory == Group.Category.AIRPLANE - end - - return nil -end - ---- Returns if the DCS Group contains Ground troops. --- @param #GROUP self --- @return #boolean true if DCS Group contains Ground troops. -function GROUP:IsGround() - self:F2() - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupCategory = DCSGroup:getCategory() - self:T2( GroupCategory ) - return GroupCategory == Group.Category.GROUND - end - - return nil -end - ---- Returns if the DCS Group contains Ships. --- @param #GROUP self --- @return #boolean true if DCS Group contains Ships. -function GROUP:IsShip() - self:F2() - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupCategory = DCSGroup:getCategory() - self:T2( GroupCategory ) - return GroupCategory == Group.Category.SHIP - end - - return nil -end - ---- Returns if all units of the group are on the ground or landed. --- If all units of this group are on the ground, this function will return true, otherwise false. --- @param #GROUP self --- @return #boolean All units on the ground result. -function GROUP:AllOnGround() - self:F2() - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local AllOnGroundResult = true - - for Index, UnitData in pairs( DCSGroup:getUnits() ) do - if UnitData:inAir() then - AllOnGroundResult = false - end - end - - self:T3( AllOnGroundResult ) - return AllOnGroundResult - end - - return nil -end - -end - -do -- AI methods - - --- Turns the AI On or Off for the GROUP. - -- @param #GROUP self - -- @param #boolean AIOnOff The value true turns the AI On, the value false turns the AI Off. - -- @return #GROUP The GROUP. - function GROUP:SetAIOnOff( AIOnOff ) - - local DCSGroup = self:GetDCSObject() -- Dcs.DCSGroup#Group - - if DCSGroup then - local DCSController = DCSGroup:getController() -- Dcs.DCSController#Controller - if DCSController then - DCSController:setOnOff( AIOnOff ) - return self - end - end - - return nil - end - - --- Turns the AI On for the GROUP. - -- @param #GROUP self - -- @return #GROUP The GROUP. - function GROUP:SetAIOn() - - return self:SetAIOnOff( true ) - end - - --- Turns the AI Off for the GROUP. - -- @param #GROUP self - -- @return #GROUP The GROUP. - function GROUP:SetAIOff() - - return self:SetAIOnOff( false ) - end - -end - - - ---- Returns the current maximum velocity of the group. --- Each unit within the group gets evaluated, and the maximum velocity (= the unit which is going the fastest) is returned. --- @param #GROUP self --- @return #number Maximum velocity found. -function GROUP:GetMaxVelocity() - self:F2() - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupVelocityMax = 0 - - for Index, UnitData in pairs( DCSGroup:getUnits() ) do - - local UnitVelocityVec3 = UnitData:getVelocity() - local UnitVelocity = math.abs( UnitVelocityVec3.x ) + math.abs( UnitVelocityVec3.y ) + math.abs( UnitVelocityVec3.z ) - - if UnitVelocity > GroupVelocityMax then - GroupVelocityMax = UnitVelocity - end - end - - return GroupVelocityMax - end - - return nil -end - ---- Returns the current minimum height of the group. --- Each unit within the group gets evaluated, and the minimum height (= the unit which is the lowest elevated) is returned. --- @param #GROUP self --- @return #number Minimum height found. -function GROUP:GetMinHeight() - self:F2() - -end - ---- Returns the current maximum height of the group. --- Each unit within the group gets evaluated, and the maximum height (= the unit which is the highest elevated) is returned. --- @param #GROUP self --- @return #number Maximum height found. -function GROUP:GetMaxHeight() - self:F2() - -end - --- SPAWNING - ---- Respawn the @{GROUP} using a (tweaked) template of the Group. --- The template must be retrieved with the @{Group#GROUP.GetTemplate}() function. --- The template contains all the definitions as declared within the mission file. --- To understand templates, do the following: --- --- * unpack your .miz file into a directory using 7-zip. --- * browse in the directory created to the file **mission**. --- * open the file and search for the country group definitions. --- --- Your group template will contain the fields as described within the mission file. --- --- This function will: --- --- * Get the current position and heading of the group. --- * When the group is alive, it will tweak the template x, y and heading coordinates of the group and the embedded units to the current units positions. --- * Then it will destroy the current alive group. --- * And it will respawn the group using your new template definition. --- @param Wrapper.Group#GROUP self --- @param #table Template The template of the Group retrieved with GROUP:GetTemplate() -function GROUP:Respawn( Template ) - - local Vec3 = self:GetVec3() - Template.x = Vec3.x - Template.y = Vec3.z - --Template.x = nil - --Template.y = nil - - self:E( #Template.units ) - for UnitID, UnitData in pairs( self:GetUnits() ) do - local GroupUnit = UnitData -- Wrapper.Unit#UNIT - self:E( GroupUnit:GetName() ) - if GroupUnit:IsAlive() then - local GroupUnitVec3 = GroupUnit:GetVec3() - local GroupUnitHeading = GroupUnit:GetHeading() - Template.units[UnitID].alt = GroupUnitVec3.y - Template.units[UnitID].x = GroupUnitVec3.x - Template.units[UnitID].y = GroupUnitVec3.z - Template.units[UnitID].heading = GroupUnitHeading - self:E( { UnitID, Template.units[UnitID], Template.units[UnitID] } ) - end - end - - self:Destroy() - _DATABASE:Spawn( Template ) -end - ---- Returns the group template from the @{DATABASE} (_DATABASE object). --- @param #GROUP self --- @return #table -function GROUP:GetTemplate() - local GroupName = self:GetName() - self:E( GroupName ) - return _DATABASE:GetGroupTemplate( GroupName ) -end - ---- Sets the controlled status in a Template. --- @param #GROUP self --- @param #boolean Controlled true is controlled, false is uncontrolled. --- @return #table -function GROUP:SetTemplateControlled( Template, Controlled ) - Template.uncontrolled = not Controlled - return Template -end - ---- Sets the CountryID of the group in a Template. --- @param #GROUP self --- @param Dcs.DCScountry#country.id CountryID The country ID. --- @return #table -function GROUP:SetTemplateCountry( Template, CountryID ) - Template.CountryID = CountryID - return Template -end - ---- Sets the CoalitionID of the group in a Template. --- @param #GROUP self --- @param Dcs.DCSCoalitionWrapper.Object#coalition.side CoalitionID The coalition ID. --- @return #table -function GROUP:SetTemplateCoalition( Template, CoalitionID ) - Template.CoalitionID = CoalitionID - return Template -end - - - - ---- Return the mission template of the group. --- @param #GROUP self --- @return #table The MissionTemplate -function GROUP:GetTaskMission() - self:F2( self.GroupName ) - - return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template ) -end - ---- Return the mission route of the group. --- @param #GROUP self --- @return #table The mission route defined by points. -function GROUP:GetTaskRoute() - self:F2( self.GroupName ) - - return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template.route.points ) -end - ---- Return the route of a group by using the @{Database#DATABASE} class. --- @param #GROUP self --- @param #number Begin The route point from where the copy will start. The base route point is 0. --- @param #number End The route point where the copy will end. The End point is the last point - the End point. The last point has base 0. --- @param #boolean Randomize Randomization of the route, when true. --- @param #number Radius When randomization is on, the randomization is within the radius. -function GROUP:CopyRoute( Begin, End, Randomize, Radius ) - self:F2( { Begin, End } ) - - local Points = {} - - -- Could be a Spawned Group - local GroupName = string.match( self:GetName(), ".*#" ) - if GroupName then - GroupName = GroupName:sub( 1, -2 ) - else - GroupName = self:GetName() - end - - self:T3( { GroupName } ) - - local Template = _DATABASE.Templates.Groups[GroupName].Template - - if Template then - if not Begin then - Begin = 0 - end - if not End then - End = 0 - end - - for TPointID = Begin + 1, #Template.route.points - End do - if Template.route.points[TPointID] then - Points[#Points+1] = routines.utils.deepCopy( Template.route.points[TPointID] ) - if Randomize then - if not Radius then - Radius = 500 - end - Points[#Points].x = Points[#Points].x + math.random( Radius * -1, Radius ) - Points[#Points].y = Points[#Points].y + math.random( Radius * -1, Radius ) - end - end - end - return Points - else - error( "Template not found for Group : " .. GroupName ) - end - - return nil -end - ---- Calculate the maxium A2G threat level of the Group. --- @param #GROUP self -function GROUP:CalculateThreatLevelA2G() - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( self:GetUnits() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end - - self:T3( MaxThreatLevelA2G ) - return MaxThreatLevelA2G -end - ---- Returns true if the first unit of the GROUP is in the air. --- @param Wrapper.Group#GROUP self --- @return #boolean true if in the first unit of the group is in the air. --- @return #nil The GROUP is not existing or not alive. -function GROUP:InAir() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local DCSUnit = DCSGroup:getUnit(1) - if DCSUnit then - local GroupInAir = DCSGroup:getUnit(1):inAir() - self:T3( GroupInAir ) - return GroupInAir - end - end - - return nil -end - -do -- Route methods - - --- (AIR) Return the Group to an @{Airbase#AIRBASE}. - -- The following things are to be taken into account: - -- - -- * The group is respawned to achieve the RTB, there may be side artefacts as a result of this. (Like weapons suddenly come back). - -- * A group consisting out of more than one unit, may rejoin formation when respawned. - -- * A speed can be given in km/h. If no speed is specified, the maximum speed of the first unit will be taken to return to base. - -- * When there is no @{Airbase} object specified, the group will return to the home base if the route of the group is pinned at take-off or at landing to a base. - -- * When there is no @{Airbase} object specified and the group route is not pinned to any airbase, it will return to the nearest airbase. - -- - -- @param #GROUP self - -- @param Wrapper.Airbase#AIRBASE RTBAirbase (optional) The @{Airbase} to return to. If blank, the controllable will return to the nearest friendly airbase. - -- @param #number Speed (optional) The Speed, if no Speed is given, the maximum Speed of the first unit is selected. - -- @return #GROUP - function GROUP:RouteRTB( RTBAirbase, Speed ) - self:F2( { RTBAirbase, Speed } ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - - if RTBAirbase then - - local GroupPoint = self:GetVec2() - local GroupVelocity = self:GetUnit(1):GetDesc().speedMax - - local PointFrom = {} - PointFrom.x = GroupPoint.x - PointFrom.y = GroupPoint.y - PointFrom.type = "Turning Point" - PointFrom.action = "Turning Point" - PointFrom.speed = GroupVelocity - - - local PointTo = {} - local AirbasePointVec2 = RTBAirbase:GetPointVec2() - local AirbaseAirPoint = AirbasePointVec2:RoutePointAir( - POINT_VEC3.RoutePointAltType.BARO, - "Land", - "Landing", - Speed or self:GetUnit(1):GetDesc().speedMax - ) - - AirbaseAirPoint["airdromeId"] = RTBAirbase:GetID() - AirbaseAirPoint["speed_locked"] = true, - - self:E(AirbaseAirPoint ) - - local Points = { PointFrom, AirbaseAirPoint } - - self:T3( Points ) - - local Template = self:GetTemplate() - Template.route.points = Points - self:Respawn( Template ) - - self:Route( Points ) - - self:Respawn(Template) - else - self:ClearTasks() - end - end - - return self - end - -end - -function GROUP:OnReSpawn( ReSpawnFunction ) - - self.ReSpawnFunction = ReSpawnFunction -end - -do -- Event Handling - - --- Subscribe to a DCS Event. - -- @param #GROUP self - -- @param Core.Event#EVENTS Event - -- @param #function EventFunction (optional) The function to be called when the event occurs for the GROUP. - -- @return #GROUP - function GROUP:HandleEvent( Event, EventFunction ) - - self:EventDispatcher():OnEventForGroup( self:GetName(), EventFunction, self, Event ) - - return self - end - - --- UnSubscribe to a DCS event. - -- @param #GROUP self - -- @param Core.Event#EVENTS Event - -- @return #GROUP - function GROUP:UnHandleEvent( Event ) - - self:EventDispatcher():RemoveForGroup( self:GetName(), self, Event ) - - return self - end - -end - -do -- Players - - --- Get player names - -- @param #GROUP self - -- @return #table The group has players, an array of player names is returned. - -- @return #nil The group has no players - function GROUP:GetPlayerNames() - - local PlayerNames = nil - - local Units = self:GetUnits() - for UnitID, UnitData in pairs( Units ) do - local Unit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = Unit:GetPlayerName() - if PlayerName and PlayerName ~= "" then - PlayerNames = PlayerNames or {} - table.insert( PlayerNames, PlayerName ) - end - end - - self:F( PlayerNames ) - return PlayerNames - end - -end--- **Wrapper** - UNIT is a wrapper class for the DCS Class Unit. --- --- === --- --- The @{#UNIT} class is a wrapper class to handle the DCS Unit objects: --- --- * Support all DCS Unit APIs. --- * Enhance with Unit specific APIs not in the DCS Unit API set. --- * Handle local Unit Controller. --- * Manage the "state" of the DCS Unit. --- --- @module Unit - ---- @type UNIT --- @extends Wrapper.Controllable#CONTROLLABLE - ---- --- # UNIT class, extends @{Controllable#CONTROLLABLE} --- --- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Unit objects are spawned (using the @{SPAWN} class). --- --- The UNIT class **does not contain a :New()** method, rather it provides **:Find()** methods to retrieve the object reference --- using the DCS Unit or the DCS UnitName. --- --- Another thing to know is that UNIT objects do not "contain" the DCS Unit object. --- The UNIT methods will reference the DCS Unit object by name when it is needed during API execution. --- If the DCS Unit object does not exist or is nil, the UNIT methods will return nil and log an exception in the DCS.log file. --- --- The UNIT class provides the following functions to retrieve quickly the relevant UNIT instance: --- --- * @{#UNIT.Find}(): Find a UNIT instance from the _DATABASE object using a DCS Unit object. --- * @{#UNIT.FindByName}(): Find a UNIT instance from the _DATABASE object using a DCS Unit name. --- --- IMPORTANT: ONE SHOULD NEVER SANATIZE these UNIT OBJECT REFERENCES! (make the UNIT object references nil). --- --- ## DCS UNIT APIs --- --- The DCS Unit APIs are used extensively within MOOSE. The UNIT class has for each DCS Unit API a corresponding method. --- To be able to distinguish easily in your code the difference between a UNIT API call and a DCS Unit API call, --- the first letter of the method is also capitalized. So, by example, the DCS Unit method @{DCSWrapper.Unit#Unit.getName}() --- is implemented in the UNIT class as @{#UNIT.GetName}(). --- --- ## Smoke, Flare Units --- --- The UNIT class provides methods to smoke or flare units easily. --- The @{#UNIT.SmokeBlue}(), @{#UNIT.SmokeGreen}(),@{#UNIT.SmokeOrange}(), @{#UNIT.SmokeRed}(), @{#UNIT.SmokeRed}() methods --- will smoke the unit in the corresponding color. Note that smoking a unit is done at the current position of the DCS Unit. --- When the DCS Unit moves for whatever reason, the smoking will still continue! --- The @{#UNIT.FlareGreen}(), @{#UNIT.FlareRed}(), @{#UNIT.FlareWhite}(), @{#UNIT.FlareYellow}() --- methods will fire off a flare in the air with the corresponding color. Note that a flare is a one-off shot and its effect is of very short duration. --- --- ## Location Position, Point --- --- The UNIT class provides methods to obtain the current point or position of the DCS Unit. --- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetVec3}() will obtain the current **location** of the DCS Unit in a Vec2 (2D) or a **point** in a Vec3 (3D) vector respectively. --- If you want to obtain the complete **3D position** including ori�ntation and direction vectors, consult the @{#UNIT.GetPositionVec3}() method respectively. --- --- ## Test if alive --- --- The @{#UNIT.IsAlive}(), @{#UNIT.IsActive}() methods determines if the DCS Unit is alive, meaning, it is existing and active. --- --- ## Test for proximity --- --- The UNIT class contains methods to test the location or proximity against zones or other objects. --- --- ### Zones --- --- To test whether the Unit is within a **zone**, use the @{#UNIT.IsInZone}() or the @{#UNIT.IsNotInZone}() methods. Any zone can be tested on, but the zone must be derived from @{Zone#ZONE_BASE}. --- --- ### Units --- --- Test if another DCS Unit is within a given radius of the current DCS Unit, use the @{#UNIT.OtherUnitInRadius}() method. --- --- @field #UNIT UNIT -UNIT = { - ClassName="UNIT", -} - - ---- Unit.SensorType --- @type Unit.SensorType --- @field OPTIC --- @field RADAR --- @field IRST --- @field RWR - - --- Registration. - ---- Create a new UNIT from DCSUnit. --- @param #UNIT self --- @param #string UnitName The name of the DCS unit. --- @return #UNIT -function UNIT:Register( UnitName ) - local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) ) - self.UnitName = UnitName - - self:SetEventPriority( 3 ) - return self -end - --- Reference methods. - ---- Finds a UNIT from the _DATABASE using a DCSUnit object. --- @param #UNIT self --- @param Dcs.DCSWrapper.Unit#Unit DCSUnit An existing DCS Unit object reference. --- @return #UNIT self -function UNIT:Find( DCSUnit ) - - local UnitName = DCSUnit:getName() - local UnitFound = _DATABASE:FindUnit( UnitName ) - return UnitFound -end - ---- Find a UNIT in the _DATABASE using the name of an existing DCS Unit. --- @param #UNIT self --- @param #string UnitName The Unit Name. --- @return #UNIT self -function UNIT:FindByName( UnitName ) - - local UnitFound = _DATABASE:FindUnit( UnitName ) - return UnitFound -end - ---- Return the name of the UNIT. --- @param #UNIT self --- @return #string The UNIT name. -function UNIT:Name() - - return self.UnitName -end - - ---- @param #UNIT self --- @return Dcs.DCSWrapper.Unit#Unit -function UNIT:GetDCSObject() - - local DCSUnit = Unit.getByName( self.UnitName ) - - if DCSUnit then - return DCSUnit - end - - return nil -end - ---- Respawn the @{Unit} using a (tweaked) template of the parent Group. --- --- This function will: --- --- * Get the current position and heading of the group. --- * When the unit is alive, it will tweak the template x, y and heading coordinates of the group and the embedded units to the current units positions. --- * Then it will respawn the re-modelled group. --- --- @param #UNIT self --- @param Dcs.DCSTypes#Vec3 SpawnVec3 The position where to Spawn the new Unit at. --- @param #number Heading The heading of the unit respawn. -function UNIT:ReSpawn( SpawnVec3, Heading ) - - local SpawnGroupTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplateFromUnitName( self:Name() ) ) - self:T( SpawnGroupTemplate ) - - local SpawnGroup = self:GetGroup() - - if SpawnGroup then - - local Vec3 = SpawnGroup:GetVec3() - SpawnGroupTemplate.x = SpawnVec3.x - SpawnGroupTemplate.y = SpawnVec3.z - - self:E( #SpawnGroupTemplate.units ) - for UnitID, UnitData in pairs( SpawnGroup:GetUnits() ) do - local GroupUnit = UnitData -- #UNIT - self:E( GroupUnit:GetName() ) - if GroupUnit:IsAlive() then - local GroupUnitVec3 = GroupUnit:GetVec3() - local GroupUnitHeading = GroupUnit:GetHeading() - SpawnGroupTemplate.units[UnitID].alt = GroupUnitVec3.y - SpawnGroupTemplate.units[UnitID].x = GroupUnitVec3.x - SpawnGroupTemplate.units[UnitID].y = GroupUnitVec3.z - SpawnGroupTemplate.units[UnitID].heading = GroupUnitHeading - self:E( { UnitID, SpawnGroupTemplate.units[UnitID], SpawnGroupTemplate.units[UnitID] } ) - end - end - end - - for UnitTemplateID, UnitTemplateData in pairs( SpawnGroupTemplate.units ) do - self:T( UnitTemplateData.name ) - if UnitTemplateData.name == self:Name() then - self:T("Adjusting") - SpawnGroupTemplate.units[UnitTemplateID].alt = SpawnVec3.y - SpawnGroupTemplate.units[UnitTemplateID].x = SpawnVec3.x - SpawnGroupTemplate.units[UnitTemplateID].y = SpawnVec3.z - SpawnGroupTemplate.units[UnitTemplateID].heading = Heading - self:E( { UnitTemplateID, SpawnGroupTemplate.units[UnitTemplateID], SpawnGroupTemplate.units[UnitTemplateID] } ) - else - self:E( SpawnGroupTemplate.units[UnitTemplateID].name ) - local GroupUnit = UNIT:FindByName( SpawnGroupTemplate.units[UnitTemplateID].name ) -- #UNIT - if GroupUnit and GroupUnit:IsAlive() then - local GroupUnitVec3 = GroupUnit:GetVec3() - local GroupUnitHeading = GroupUnit:GetHeading() - UnitTemplateData.alt = GroupUnitVec3.y - UnitTemplateData.x = GroupUnitVec3.x - UnitTemplateData.y = GroupUnitVec3.z - UnitTemplateData.heading = GroupUnitHeading - else - if SpawnGroupTemplate.units[UnitTemplateID].name ~= self:Name() then - self:T("nilling") - SpawnGroupTemplate.units[UnitTemplateID].delete = true - end - end - end - end - - -- Remove obscolete units from the group structure - local i = 1 - while i <= #SpawnGroupTemplate.units do - - local UnitTemplateData = SpawnGroupTemplate.units[i] - self:T( UnitTemplateData.name ) - - if UnitTemplateData.delete then - table.remove( SpawnGroupTemplate.units, i ) - else - i = i + 1 - end - end - - _DATABASE:Spawn( SpawnGroupTemplate ) -end - - - ---- Returns if the unit is activated. --- @param #UNIT self --- @return #boolean true if Unit is activated. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:IsActive() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - - local UnitIsActive = DCSUnit:isActive() - return UnitIsActive - end - - return nil -end - ---- Returns if the Unit is alive. --- If the Unit is not alive, nil is returned. --- If the Unit is alive and active, true is returned. --- If the Unit is alive but not active, false is returned. --- @param #UNIT self --- @return #boolean true if Unit is alive and active. --- @return #boolean false if Unit is alive but not active. --- @return #nil if the Unit is not existing or is not alive. -function UNIT:IsAlive() - self:F3( self.UnitName ) - - local DCSUnit = self:GetDCSObject() -- Dcs.DCSUnit#Unit - - if DCSUnit then - local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() - return UnitIsAlive - end - - return nil -end - - - ---- Returns the Unit's callsign - the localized string. --- @param #UNIT self --- @return #string The Callsign of the Unit. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetCallsign() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitCallSign = DCSUnit:getCallsign() - return UnitCallSign - end - - self:E( self.ClassName .. " " .. self.UnitName .. " not found!" ) - return nil -end - - ---- Returns name of the player that control the unit or nil if the unit is controlled by A.I. --- @param #UNIT self --- @return #string Player Name --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetPlayerName() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - - local PlayerName = DCSUnit:getPlayerName() - if PlayerName == nil then - PlayerName = "" - end - return PlayerName - end - - return nil -end - ---- Returns the unit's number in the group. --- The number is the same number the unit has in ME. --- It may not be changed during the mission. --- If any unit in the group is destroyed, the numbers of another units will not be changed. --- @param #UNIT self --- @return #number The Unit number. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetNumber() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitNumber = DCSUnit:getNumber() - return UnitNumber - end - - return nil -end - ---- Returns the unit's group if it exist and nil otherwise. --- @param Wrapper.Unit#UNIT self --- @return Wrapper.Group#GROUP The Group of the Unit. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetGroup() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitGroup = GROUP:Find( DCSUnit:getGroup() ) - return UnitGroup - end - - return nil -end - - --- Need to add here functions to check if radar is on and which object etc. - ---- Returns the prefix name of the DCS Unit. A prefix name is a part of the name before a '#'-sign. --- DCS Units spawned with the @{SPAWN} class contain a '#'-sign to indicate the end of the (base) DCS Unit name. --- The spawn sequence number and unit number are contained within the name after the '#' sign. --- @param #UNIT self --- @return #string The name of the DCS Unit. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetPrefix() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 ) - self:T3( UnitPrefix ) - return UnitPrefix - end - - return nil -end - ---- Returns the Unit's ammunition. --- @param #UNIT self --- @return Dcs.DCSWrapper.Unit#Unit.Ammo --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetAmmo() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitAmmo = DCSUnit:getAmmo() - return UnitAmmo - end - - return nil -end - ---- Returns the unit sensors. --- @param #UNIT self --- @return Dcs.DCSWrapper.Unit#Unit.Sensors --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetSensors() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitSensors = DCSUnit:getSensors() - return UnitSensors - end - - return nil -end - --- Need to add here a function per sensortype --- unit:hasSensors(Unit.SensorType.RADAR, Unit.RadarType.AS) - ---- Returns if the unit has sensors of a certain type. --- @param #UNIT self --- @return #boolean returns true if the unit has specified types of sensors. This function is more preferable than Unit.getSensors() if you don't want to get information about all the unit's sensors, and just want to check if the unit has specified types of sensors. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:HasSensors( ... ) - self:F2( arg ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local HasSensors = DCSUnit:hasSensors( unpack( arg ) ) - return HasSensors - end - - return nil -end - ---- Returns if the unit is SEADable. --- @param #UNIT self --- @return #boolean returns true if the unit is SEADable. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:HasSEAD() - self:F2() - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitSEADAttributes = DCSUnit:getDesc().attributes - - local HasSEAD = false - if UnitSEADAttributes["RADAR_BAND1_FOR_ARM"] and UnitSEADAttributes["RADAR_BAND1_FOR_ARM"] == true or - UnitSEADAttributes["RADAR_BAND2_FOR_ARM"] and UnitSEADAttributes["RADAR_BAND2_FOR_ARM"] == true then - HasSEAD = true - end - return HasSEAD - end - - return nil -end - ---- Returns two values: --- --- * First value indicates if at least one of the unit's radar(s) is on. --- * Second value is the object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target. --- @param #UNIT self --- @return #boolean Indicates if at least one of the unit's radar(s) is on. --- @return Dcs.DCSWrapper.Object#Object The object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetRadar() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitRadarOn, UnitRadarObject = DCSUnit:getRadar() - return UnitRadarOn, UnitRadarObject - end - - return nil, nil -end - ---- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. --- @param #UNIT self --- @return #number The relative amount of fuel (from 0.0 to 1.0). --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetFuel() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitFuel = DCSUnit:getFuel() - return UnitFuel - end - - return nil -end - ---- Returns the UNIT in a UNIT list of one element. --- @param #UNIT self --- @return #list The UNITs wrappers. -function UNIT:GetUnits() - self:F2( { self.UnitName } ) - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local DCSUnits = DCSUnit:getUnits() - local Units = {} - Units[1] = UNIT:Find( DCSUnit ) - self:T3( Units ) - return Units - end - - return nil -end - - ---- Returns the unit's health. Dead units has health <= 1.0. --- @param #UNIT self --- @return #number The Unit's health value. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetLife() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitLife = DCSUnit:getLife() - return UnitLife - end - - return nil -end - ---- Returns the Unit's initial health. --- @param #UNIT self --- @return #number The Unit's initial health value. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetLife0() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitLife0 = DCSUnit:getLife0() - return UnitLife0 - end - - return nil -end - ---- Returns the category name of the #UNIT. --- @param #UNIT self --- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship -function UNIT:GetCategoryName() - self:F3( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - if DCSUnit then - local CategoryNames = { - [Unit.Category.AIRPLANE] = "Airplane", - [Unit.Category.HELICOPTER] = "Helicopter", - [Unit.Category.GROUND_UNIT] = "Ground Unit", - [Unit.Category.SHIP] = "Ship", - [Unit.Category.STRUCTURE] = "Structure", - } - local UnitCategory = DCSUnit:getDesc().category - self:T3( UnitCategory ) - - return CategoryNames[UnitCategory] - end - - return nil -end - - ---- Returns the Unit's A2G threat level on a scale from 1 to 10 ... --- The following threat levels are foreseen: --- --- * Threat level 0: Unit is unarmed. --- * Threat level 1: Unit is infantry. --- * Threat level 2: Unit is an infantry vehicle. --- * Threat level 3: Unit is ground artillery. --- * Threat level 4: Unit is a tank. --- * Threat level 5: Unit is a modern tank or ifv with ATGM. --- * Threat level 6: Unit is a AAA. --- * Threat level 7: Unit is a SAM or manpad, IR guided. --- * Threat level 8: Unit is a Short Range SAM, radar guided. --- * Threat level 9: Unit is a Medium Range SAM, radar guided. --- * Threat level 10: Unit is a Long Range SAM, radar guided. --- @param #UNIT self -function UNIT:GetThreatLevel() - - local Attributes = self:GetDesc().attributes - self:T( Attributes ) - - local ThreatLevel = 0 - local ThreatText = "" - - if self:IsGround() then - - self:T( "Ground" ) - - local ThreatLevels = { - "Unarmed", - "Infantry", - "Old Tanks & APCs", - "Tanks & IFVs without ATGM", - "Tanks & IFV with ATGM", - "Modern Tanks", - "AAA", - "IR Guided SAMs", - "SR SAMs", - "MR SAMs", - "LR SAMs" - } - - - if Attributes["LR SAM"] then ThreatLevel = 10 - elseif Attributes["MR SAM"] then ThreatLevel = 9 - elseif Attributes["SR SAM"] and - not Attributes["IR Guided SAM"] then ThreatLevel = 8 - elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and - Attributes["IR Guided SAM"] then ThreatLevel = 7 - elseif Attributes["AAA"] then ThreatLevel = 6 - elseif Attributes["Modern Tanks"] then ThreatLevel = 5 - elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and - Attributes["ATGM"] then ThreatLevel = 4 - elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and - not Attributes["ATGM"] then ThreatLevel = 3 - elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2 - elseif Attributes["Infantry"] then ThreatLevel = 1 - end - - ThreatText = ThreatLevels[ThreatLevel+1] - end - - if self:IsAir() then - - self:T( "Air" ) - - local ThreatLevels = { - "Unarmed", - "Tanker", - "AWACS", - "Transport Helicpter", - "UAV", - "Bomber", - "Strategic Bomber", - "Attack Helicopter", - "Interceptor", - "Multirole Fighter", - "Fighter" - } - - - if Attributes["Fighters"] then ThreatLevel = 10 - elseif Attributes["Multirole fighters"] then ThreatLevel = 9 - elseif Attributes["Battleplanes"] then ThreatLevel = 8 - elseif Attributes["Attack helicopters"] then ThreatLevel = 7 - elseif Attributes["Strategic bombers"] then ThreatLevel = 6 - elseif Attributes["Bombers"] then ThreatLevel = 5 - elseif Attributes["UAVs"] then ThreatLevel = 4 - elseif Attributes["Transport helicopters"] then ThreatLevel = 3 - elseif Attributes["AWACS"] then ThreatLevel = 2 - elseif Attributes["Tankers"] then ThreatLevel = 1 - end - - ThreatText = ThreatLevels[ThreatLevel+1] - end - - if self:IsShip() then - - self:T( "Ship" ) - ---["Aircraft Carriers"] = {"Heavy armed ships",}, ---["Cruisers"] = {"Heavy armed ships",}, ---["Destroyers"] = {"Heavy armed ships",}, ---["Frigates"] = {"Heavy armed ships",}, ---["Corvettes"] = {"Heavy armed ships",}, ---["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",}, ---["Light armed ships"] = {"Armed ships","NonArmoredUnits"}, ---["Armed ships"] = {"Ships"}, ---["Unarmed ships"] = {"Ships","HeavyArmoredUnits",}, - - local ThreatLevels = { - "Unarmed ship", - "Light armed ships", - "Corvettes", - "", - "Frigates", - "", - "Cruiser", - "", - "Destroyer", - "", - "Aircraft Carrier" - } - - - if Attributes["Aircraft Carriers"] then ThreatLevel = 10 - elseif Attributes["Destroyers"] then ThreatLevel = 8 - elseif Attributes["Cruisers"] then ThreatLevel = 6 - elseif Attributes["Frigates"] then ThreatLevel = 4 - elseif Attributes["Corvettes"] then ThreatLevel = 2 - elseif Attributes["Light armed ships"] then ThreatLevel = 1 - end - - ThreatText = ThreatLevels[ThreatLevel+1] - end - - self:T2( ThreatLevel ) - return ThreatLevel, ThreatText - -end - - --- Is functions - ---- Returns true if the unit is within a @{Zone}. --- @param #UNIT self --- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the unit is within the @{Zone#ZONE_BASE} -function UNIT:IsInZone( Zone ) - self:F2( { self.UnitName, Zone } ) - - if self:IsAlive() then - local IsInZone = Zone:IsVec3InZone( self:GetVec3() ) - - self:T( { IsInZone } ) - return IsInZone - end - - return false -end - ---- Returns true if the unit is not within a @{Zone}. --- @param #UNIT self --- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the unit is not within the @{Zone#ZONE_BASE} -function UNIT:IsNotInZone( Zone ) - self:F2( { self.UnitName, Zone } ) - - if self:IsAlive() then - local IsInZone = not Zone:IsVec3InZone( self:GetVec3() ) - - self:T( { IsInZone } ) - return IsInZone - else - return false - end -end - - ---- Returns true if there is an **other** DCS Unit within a radius of the current 2D point of the DCS Unit. --- @param #UNIT self --- @param #UNIT AwaitUnit The other UNIT wrapper object. --- @param Radius The radius in meters with the DCS Unit in the centre. --- @return true If the other DCS Unit is within the radius of the 2D point of the DCS Unit. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:OtherUnitInRadius( AwaitUnit, Radius ) - self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitVec3 = self:GetVec3() - local AwaitUnitVec3 = AwaitUnit:GetVec3() - - if (((UnitVec3.x - AwaitUnitVec3.x)^2 + (UnitVec3.z - AwaitUnitVec3.z)^2)^0.5 <= Radius) then - self:T3( "true" ) - return true - else - self:T3( "false" ) - return false - end - end - - return nil -end - - - ---- Signal a flare at the position of the UNIT. --- @param #UNIT self --- @param Utilities.Utils#FLARECOLOR FlareColor -function UNIT:Flare( FlareColor ) - self:F2() - trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 ) -end - ---- Signal a white flare at the position of the UNIT. --- @param #UNIT self -function UNIT:FlareWhite() - self:F2() - trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 ) -end - ---- Signal a yellow flare at the position of the UNIT. --- @param #UNIT self -function UNIT:FlareYellow() - self:F2() - trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 ) -end - ---- Signal a green flare at the position of the UNIT. --- @param #UNIT self -function UNIT:FlareGreen() - self:F2() - trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 ) -end - ---- Signal a red flare at the position of the UNIT. --- @param #UNIT self -function UNIT:FlareRed() - self:F2() - local Vec3 = self:GetVec3() - if Vec3 then - trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 ) - end -end - ---- Smoke the UNIT. --- @param #UNIT self -function UNIT:Smoke( SmokeColor, Range ) - self:F2() - if Range then - trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor ) - else - trigger.action.smoke( self:GetVec3(), SmokeColor ) - end - -end - ---- Smoke the UNIT Green. --- @param #UNIT self -function UNIT:SmokeGreen() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green ) -end - ---- Smoke the UNIT Red. --- @param #UNIT self -function UNIT:SmokeRed() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red ) -end - ---- Smoke the UNIT White. --- @param #UNIT self -function UNIT:SmokeWhite() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White ) -end - ---- Smoke the UNIT Orange. --- @param #UNIT self -function UNIT:SmokeOrange() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange ) -end - ---- Smoke the UNIT Blue. --- @param #UNIT self -function UNIT:SmokeBlue() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue ) -end - --- Is methods - ---- Returns if the unit is of an air category. --- If the unit is a helicopter or a plane, then this method will return true, otherwise false. --- @param #UNIT self --- @return #boolean Air category evaluation result. -function UNIT:IsAir() - self:F2() - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitDescriptor = DCSUnit:getDesc() - self:T3( { UnitDescriptor.category, Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) - - local IsAirResult = ( UnitDescriptor.category == Unit.Category.AIRPLANE ) or ( UnitDescriptor.category == Unit.Category.HELICOPTER ) - - self:T3( IsAirResult ) - return IsAirResult - end - - return nil -end - ---- Returns if the unit is of an ground category. --- If the unit is a ground vehicle or infantry, this method will return true, otherwise false. --- @param #UNIT self --- @return #boolean Ground category evaluation result. -function UNIT:IsGround() - self:F2() - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitDescriptor = DCSUnit:getDesc() - self:T3( { UnitDescriptor.category, Unit.Category.GROUND_UNIT } ) - - local IsGroundResult = ( UnitDescriptor.category == Unit.Category.GROUND_UNIT ) - - self:T3( IsGroundResult ) - return IsGroundResult - end - - return nil -end - ---- Returns if the unit is a friendly unit. --- @param #UNIT self --- @return #boolean IsFriendly evaluation result. -function UNIT:IsFriendly( FriendlyCoalition ) - self:F2() - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitCoalition = DCSUnit:getCoalition() - self:T3( { UnitCoalition, FriendlyCoalition } ) - - local IsFriendlyResult = ( UnitCoalition == FriendlyCoalition ) - - self:E( IsFriendlyResult ) - return IsFriendlyResult - end - - return nil -end - ---- Returns if the unit is of a ship category. --- If the unit is a ship, this method will return true, otherwise false. --- @param #UNIT self --- @return #boolean Ship category evaluation result. -function UNIT:IsShip() - self:F2() - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitDescriptor = DCSUnit:getDesc() - self:T3( { UnitDescriptor.category, Unit.Category.SHIP } ) - - local IsShipResult = ( UnitDescriptor.category == Unit.Category.SHIP ) - - self:T3( IsShipResult ) - return IsShipResult - end - - return nil -end - ---- Returns true if the UNIT is in the air. --- @param Wrapper.Positionable#UNIT self --- @return #boolean true if in the air. --- @return #nil The UNIT is not existing or alive. -function UNIT:InAir() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitInAir = DCSUnit:inAir() - self:T3( UnitInAir ) - return UnitInAir - end - - return nil -end - -do -- Event Handling - - --- Subscribe to a DCS Event. - -- @param #UNIT self - -- @param Core.Event#EVENTS Event - -- @param #function EventFunction (optional) The function to be called when the event occurs for the unit. - -- @return #UNIT - function UNIT:HandleEvent( Event, EventFunction ) - - self:EventDispatcher():OnEventForUnit( self:GetName(), EventFunction, self, Event ) - - return self - end - - --- UnSubscribe to a DCS event. - -- @param #UNIT self - -- @param Core.Event#EVENTS Event - -- @return #UNIT - function UNIT:UnHandleEvent( Event ) - - self:EventDispatcher():RemoveForUnit( self:GetName(), self, Event ) - - return self - end - -end--- This module contains the CLIENT class. --- --- 1) @{Client#CLIENT} class, extends @{Unit#UNIT} --- =============================================== --- Clients are those **Units** defined within the Mission Editor that have the skillset defined as __Client__ or __Player__. --- Note that clients are NOT the same as Units, they are NOT necessarily alive. --- The @{Client#CLIENT} class is a wrapper class to handle the DCS Unit objects that have the skillset defined as __Client__ or __Player__: --- --- * Wraps the DCS Unit objects with skill level set to Player or Client. --- * Support all DCS Unit APIs. --- * Enhance with Unit specific APIs not in the DCS Group API set. --- * When player joins Unit, execute alive init logic. --- * Handles messages to players. --- * Manage the "state" of the DCS Unit. --- --- Clients are being used by the @{MISSION} class to follow players and register their successes. --- --- 1.1) CLIENT reference methods --- ----------------------------- --- For each DCS Unit having skill level Player or Client, a CLIENT wrapper object (instance) will be created within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts). --- --- The CLIENT class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference --- using the DCS Unit or the DCS UnitName. --- --- Another thing to know is that CLIENT objects do not "contain" the DCS Unit object. --- The CLIENT methods will reference the DCS Unit object by name when it is needed during API execution. --- If the DCS Unit object does not exist or is nil, the CLIENT methods will return nil and log an exception in the DCS.log file. --- --- The CLIENT class provides the following functions to retrieve quickly the relevant CLIENT instance: --- --- * @{#CLIENT.Find}(): Find a CLIENT instance from the _DATABASE object using a DCS Unit object. --- * @{#CLIENT.FindByName}(): Find a CLIENT instance from the _DATABASE object using a DCS Unit name. --- --- IMPORTANT: ONE SHOULD NEVER SANATIZE these CLIENT OBJECT REFERENCES! (make the CLIENT object references nil). --- --- @module Client - ---- The CLIENT class --- @type CLIENT --- @extends Wrapper.Unit#UNIT -CLIENT = { - ONBOARDSIDE = { - NONE = 0, - LEFT = 1, - RIGHT = 2, - BACK = 3, - FRONT = 4 - }, - ClassName = "CLIENT", - ClientName = nil, - ClientAlive = false, - ClientTransport = false, - ClientBriefingShown = false, - _Menus = {}, - _Tasks = {}, - Messages = { - } -} - - ---- Finds a CLIENT from the _DATABASE using the relevant DCS Unit. --- @param #CLIENT self --- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor. --- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client. --- @return #CLIENT --- @usage --- -- Create new Clients. --- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' ) --- Mission:AddGoal( DeploySA6TroopsGoal ) --- --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() ) -function CLIENT:Find( DCSUnit, Error ) - local ClientName = DCSUnit:getName() - local ClientFound = _DATABASE:FindClient( ClientName ) - - if ClientFound then - ClientFound:F( ClientName ) - return ClientFound - end - - if not Error then - error( "CLIENT not found for: " .. ClientName ) - end -end - - ---- Finds a CLIENT from the _DATABASE using the relevant Client Unit Name. --- As an optional parameter, a briefing text can be given also. --- @param #CLIENT self --- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor. --- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client. --- @param #boolean Error A flag that indicates whether an error should be raised if the CLIENT cannot be found. By default an error will be raised. --- @return #CLIENT --- @usage --- -- Create new Clients. --- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' ) --- Mission:AddGoal( DeploySA6TroopsGoal ) --- --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() ) -function CLIENT:FindByName( ClientName, ClientBriefing, Error ) - local ClientFound = _DATABASE:FindClient( ClientName ) - - if ClientFound then - ClientFound:F( { ClientName, ClientBriefing } ) - ClientFound:AddBriefing( ClientBriefing ) - ClientFound.MessageSwitch = true - - return ClientFound - end - - if not Error then - error( "CLIENT not found for: " .. ClientName ) - end -end - -function CLIENT:Register( ClientName ) - local self = BASE:Inherit( self, UNIT:Register( ClientName ) ) - - self:F( ClientName ) - self.ClientName = ClientName - self.MessageSwitch = true - self.ClientAlive2 = false - - --self.AliveCheckScheduler = routines.scheduleFunction( self._AliveCheckScheduler, { self }, timer.getTime() + 1, 5 ) - self.AliveCheckScheduler = SCHEDULER:New( self, self._AliveCheckScheduler, { "Client Alive " .. ClientName }, 1, 5 ) - - self:E( self ) - return self -end - - ---- Transport defines that the Client is a Transport. Transports show cargo. --- @param #CLIENT self --- @return #CLIENT -function CLIENT:Transport() - self:F() - - self.ClientTransport = true - return self -end - ---- AddBriefing adds a briefing to a CLIENT when a player joins a mission. --- @param #CLIENT self --- @param #string ClientBriefing is the text defining the Mission briefing. --- @return #CLIENT self -function CLIENT:AddBriefing( ClientBriefing ) - self:F( ClientBriefing ) - self.ClientBriefing = ClientBriefing - self.ClientBriefingShown = false - - return self -end - ---- Show the briefing of a CLIENT. --- @param #CLIENT self --- @return #CLIENT self -function CLIENT:ShowBriefing() - self:F( { self.ClientName, self.ClientBriefingShown } ) - - if not self.ClientBriefingShown then - self.ClientBriefingShown = true - local Briefing = "" - if self.ClientBriefing then - Briefing = Briefing .. self.ClientBriefing - end - Briefing = Briefing .. " Press [LEFT ALT]+[B] to view the complete mission briefing." - self:Message( Briefing, 60, "Briefing" ) - end - - return self -end - ---- Show the mission briefing of a MISSION to the CLIENT. --- @param #CLIENT self --- @param #string MissionBriefing --- @return #CLIENT self -function CLIENT:ShowMissionBriefing( MissionBriefing ) - self:F( { self.ClientName } ) - - if MissionBriefing then - self:Message( MissionBriefing, 60, "Mission Briefing" ) - end - - return self -end - - - ---- Resets a CLIENT. --- @param #CLIENT self --- @param #string ClientName Name of the Group as defined within the Mission Editor. The Group must have a Unit with the type Client. -function CLIENT:Reset( ClientName ) - self:F() - self._Menus = {} -end - --- Is Functions - ---- Checks if the CLIENT is a multi-seated UNIT. --- @param #CLIENT self --- @return #boolean true if multi-seated. -function CLIENT:IsMultiSeated() - self:F( self.ClientName ) - - local ClientMultiSeatedTypes = { - ["Mi-8MT"] = "Mi-8MT", - ["UH-1H"] = "UH-1H", - ["P-51B"] = "P-51B" - } - - if self:IsAlive() then - local ClientTypeName = self:GetClientGroupUnit():GetTypeName() - if ClientMultiSeatedTypes[ClientTypeName] then - return true - end - end - - return false -end - ---- Checks for a client alive event and calls a function on a continuous basis. --- @param #CLIENT self --- @param #function CallBackFunction Create a function that will be called when a player joins the slot. --- @return #CLIENT -function CLIENT:Alive( CallBackFunction, ... ) - self:F() - - self.ClientCallBack = CallBackFunction - self.ClientParameters = arg - - return self -end - ---- @param #CLIENT self -function CLIENT:_AliveCheckScheduler( SchedulerName ) - self:F3( { SchedulerName, self.ClientName, self.ClientAlive2, self.ClientBriefingShown, self.ClientCallBack } ) - - if self:IsAlive() then - if self.ClientAlive2 == false then - self:ShowBriefing() - if self.ClientCallBack then - self:T("Calling Callback function") - self.ClientCallBack( self, unpack( self.ClientParameters ) ) - end - self.ClientAlive2 = true - end - else - if self.ClientAlive2 == true then - self.ClientAlive2 = false - end - end - - return true -end - ---- Return the DCSGroup of a Client. --- This function is modified to deal with a couple of bugs in DCS 1.5.3 --- @param #CLIENT self --- @return Dcs.DCSWrapper.Group#Group -function CLIENT:GetDCSGroup() - self:F3() - --- local ClientData = Group.getByName( self.ClientName ) --- if ClientData and ClientData:isExist() then --- self:T( self.ClientName .. " : group found!" ) --- return ClientData --- else --- return nil --- end - - local ClientUnit = Unit.getByName( self.ClientName ) - - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - self:T3( { "CoalitionData:", CoalitionData } ) - for UnitId, UnitData in pairs( CoalitionData ) do - self:T3( { "UnitData:", UnitData } ) - if UnitData and UnitData:isExist() then - - --self:E(self.ClientName) - if ClientUnit then - local ClientGroup = ClientUnit:getGroup() - if ClientGroup then - self:T3( "ClientGroup = " .. self.ClientName ) - if ClientGroup:isExist() and UnitData:getGroup():isExist() then - if ClientGroup:getID() == UnitData:getGroup():getID() then - self:T3( "Normal logic" ) - self:T3( self.ClientName .. " : group found!" ) - self.ClientGroupID = ClientGroup:getID() - self.ClientGroupName = ClientGroup:getName() - return ClientGroup - end - else - -- Now we need to resolve the bugs in DCS 1.5 ... - -- Consult the database for the units of the Client Group. (ClientGroup:getUnits() returns nil) - self:T3( "Bug 1.5 logic" ) - local ClientGroupTemplate = _DATABASE.Templates.Units[self.ClientName].GroupTemplate - self.ClientGroupID = ClientGroupTemplate.groupId - self.ClientGroupName = _DATABASE.Templates.Units[self.ClientName].GroupName - self:T3( self.ClientName .. " : group found in bug 1.5 resolvement logic!" ) - return ClientGroup - end - -- else - -- error( "Client " .. self.ClientName .. " not found!" ) - end - else - --self:E( { "Client not found!", self.ClientName } ) - end - end - end - end - - -- For non player clients - if ClientUnit then - local ClientGroup = ClientUnit:getGroup() - if ClientGroup then - self:T3( "ClientGroup = " .. self.ClientName ) - if ClientGroup:isExist() then - self:T3( "Normal logic" ) - self:T3( self.ClientName .. " : group found!" ) - return ClientGroup - end - end - end - - self.ClientGroupID = nil - self.ClientGroupUnit = nil - - return nil -end - - --- TODO: Check Dcs.DCSTypes#Group.ID ---- Get the group ID of the client. --- @param #CLIENT self --- @return Dcs.DCSTypes#Group.ID -function CLIENT:GetClientGroupID() - - local ClientGroup = self:GetDCSGroup() - - --self:E( self.ClientGroupID ) -- Determined in GetDCSGroup() - return self.ClientGroupID -end - - ---- Get the name of the group of the client. --- @param #CLIENT self --- @return #string -function CLIENT:GetClientGroupName() - - local ClientGroup = self:GetDCSGroup() - - self:T( self.ClientGroupName ) -- Determined in GetDCSGroup() - return self.ClientGroupName -end - ---- Returns the UNIT of the CLIENT. --- @param #CLIENT self --- @return Wrapper.Unit#UNIT -function CLIENT:GetClientGroupUnit() - self:F2() - - local ClientDCSUnit = Unit.getByName( self.ClientName ) - - self:T( self.ClientDCSUnit ) - if ClientDCSUnit and ClientDCSUnit:isExist() then - local ClientUnit = _DATABASE:FindUnit( self.ClientName ) - self:T2( ClientUnit ) - return ClientUnit - end -end - ---- Returns the DCSUnit of the CLIENT. --- @param #CLIENT self --- @return Dcs.DCSTypes#Unit -function CLIENT:GetClientGroupDCSUnit() - self:F2() - - local ClientDCSUnit = Unit.getByName( self.ClientName ) - - if ClientDCSUnit and ClientDCSUnit:isExist() then - self:T2( ClientDCSUnit ) - return ClientDCSUnit - end -end - - ---- Evaluates if the CLIENT is a transport. --- @param #CLIENT self --- @return #boolean true is a transport. -function CLIENT:IsTransport() - self:F() - return self.ClientTransport -end - ---- Shows the @{AI_Cargo#CARGO} contained within the CLIENT to the player as a message. --- The @{AI_Cargo#CARGO} is shown using the @{Message#MESSAGE} distribution system. --- @param #CLIENT self -function CLIENT:ShowCargo() - self:F() - - local CargoMsg = "" - - for CargoName, Cargo in pairs( CARGOS ) do - if self == Cargo:IsLoadedInClient() then - CargoMsg = CargoMsg .. Cargo.CargoName .. " Type:" .. Cargo.CargoType .. " Weight: " .. Cargo.CargoWeight .. "\n" - end - end - - if CargoMsg == "" then - CargoMsg = "empty" - end - - self:Message( CargoMsg, 15, "Co-Pilot: Cargo Status", 30 ) - -end - --- TODO (1) I urgently need to revise this. ---- A local function called by the DCS World Menu system to switch off messages. -function CLIENT.SwitchMessages( PrmTable ) - PrmTable[1].MessageSwitch = PrmTable[2] -end - ---- The main message driver for the CLIENT. --- This function displays various messages to the Player logged into the CLIENT through the DCS World Messaging system. --- @param #CLIENT self --- @param #string Message is the text describing the message. --- @param #number MessageDuration is the duration in seconds that the Message should be displayed. --- @param #string MessageCategory is the category of the message (the title). --- @param #number MessageInterval is the interval in seconds between the display of the @{Message#MESSAGE} when the CLIENT is in the air. --- @param #string MessageID is the identifier of the message when displayed with intervals. -function CLIENT:Message( Message, MessageDuration, MessageCategory, MessageInterval, MessageID ) - self:F( { Message, MessageDuration, MessageCategory, MessageInterval } ) - - if self.MessageSwitch == true then - if MessageCategory == nil then - MessageCategory = "Messages" - end - if MessageID ~= nil then - if self.Messages[MessageID] == nil then - self.Messages[MessageID] = {} - self.Messages[MessageID].MessageId = MessageID - self.Messages[MessageID].MessageTime = timer.getTime() - self.Messages[MessageID].MessageDuration = MessageDuration - if MessageInterval == nil then - self.Messages[MessageID].MessageInterval = 600 - else - self.Messages[MessageID].MessageInterval = MessageInterval - end - MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self ) - else - if self:GetClientGroupDCSUnit() and not self:GetClientGroupDCSUnit():inAir() then - if timer.getTime() - self.Messages[MessageID].MessageTime >= self.Messages[MessageID].MessageDuration + 10 then - MESSAGE:New( Message, MessageDuration , MessageCategory):ToClient( self ) - self.Messages[MessageID].MessageTime = timer.getTime() - end - else - if timer.getTime() - self.Messages[MessageID].MessageTime >= self.Messages[MessageID].MessageDuration + self.Messages[MessageID].MessageInterval then - MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self ) - self.Messages[MessageID].MessageTime = timer.getTime() - end - end - end - else - MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self ) - end - end -end ---- This module contains the STATIC class. --- --- 1) @{Static#STATIC} class, extends @{Positionable#POSITIONABLE} --- =============================================================== --- Statics are **Static Units** defined within the Mission Editor. --- Note that Statics are almost the same as Units, but they don't have a controller. --- The @{Static#STATIC} class is a wrapper class to handle the DCS Static objects: --- --- * Wraps the DCS Static objects. --- * Support all DCS Static APIs. --- * Enhance with Static specific APIs not in the DCS API set. --- --- 1.1) STATIC reference methods --- ----------------------------- --- For each DCS Static will have a STATIC wrapper object (instance) within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts). --- --- The STATIC class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference --- using the Static Name. --- --- Another thing to know is that STATIC objects do not "contain" the DCS Static object. --- The STATIc methods will reference the DCS Static object by name when it is needed during API execution. --- If the DCS Static object does not exist or is nil, the STATIC methods will return nil and log an exception in the DCS.log file. --- --- The STATIc class provides the following functions to retrieve quickly the relevant STATIC instance: --- --- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name. --- --- IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil). --- --- @module Static --- @author FlightControl - - - - - - ---- The STATIC class --- @type STATIC --- @extends Wrapper.Positionable#POSITIONABLE -STATIC = { - ClassName = "STATIC", -} - - ---- Finds a STATIC from the _DATABASE using the relevant Static Name. --- As an optional parameter, a briefing text can be given also. --- @param #STATIC self --- @param #string StaticName Name of the DCS **Static** as defined within the Mission Editor. --- @param #boolean RaiseError Raise an error if not found. --- @return #STATIC -function STATIC:FindByName( StaticName, RaiseError ) - local StaticFound = _DATABASE:FindStatic( StaticName ) - - self.StaticName = StaticName - - if StaticFound then - StaticFound:F3( { StaticName } ) - - return StaticFound - end - - if RaiseError == nil or RaiseError == true then - error( "STATIC not found for: " .. StaticName ) - end - - return nil -end - -function STATIC:Register( StaticName ) - local self = BASE:Inherit( self, POSITIONABLE:New( StaticName ) ) - self.StaticName = StaticName - return self -end - - -function STATIC:GetDCSObject() - local DCSStatic = StaticObject.getByName( self.StaticName ) - - if DCSStatic then - return DCSStatic - end - - return nil -end - -function STATIC:GetThreatLevel() - - return 1, "Static" -end--- This module contains the AIRBASE classes. --- --- === --- --- 1) @{Airbase#AIRBASE} class, extends @{Positionable#POSITIONABLE} --- ================================================================= --- The @{AIRBASE} class is a wrapper class to handle the DCS Airbase objects: --- --- * Support all DCS Airbase APIs. --- * Enhance with Airbase specific APIs not in the DCS Airbase API set. --- --- --- 1.1) AIRBASE reference methods --- ------------------------------ --- For each DCS Airbase object alive within a running mission, a AIRBASE wrapper object (instance) will be created within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts). --- --- The AIRBASE class **does not contain a :New()** method, rather it provides **:Find()** methods to retrieve the object reference --- using the DCS Airbase or the DCS AirbaseName. --- --- Another thing to know is that AIRBASE objects do not "contain" the DCS Airbase object. --- The AIRBASE methods will reference the DCS Airbase object by name when it is needed during API execution. --- If the DCS Airbase object does not exist or is nil, the AIRBASE methods will return nil and log an exception in the DCS.log file. --- --- The AIRBASE class provides the following functions to retrieve quickly the relevant AIRBASE instance: --- --- * @{#AIRBASE.Find}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase object. --- * @{#AIRBASE.FindByName}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase name. --- --- IMPORTANT: ONE SHOULD NEVER SANATIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil). --- --- 1.2) DCS AIRBASE APIs --- --------------------- --- The DCS Airbase APIs are used extensively within MOOSE. The AIRBASE class has for each DCS Airbase API a corresponding method. --- To be able to distinguish easily in your code the difference between a AIRBASE API call and a DCS Airbase API call, --- the first letter of the method is also capitalized. So, by example, the DCS Airbase method @{DCSWrapper.Airbase#Airbase.getName}() --- is implemented in the AIRBASE class as @{#AIRBASE.GetName}(). --- --- More functions will be added --- ---------------------------- --- During the MOOSE development, more functions will be added. --- --- @module Airbase --- @author FlightControl - - - - - ---- The AIRBASE class --- @type AIRBASE --- @extends Wrapper.Positionable#POSITIONABLE -AIRBASE = { - ClassName="AIRBASE", - CategoryName = { - [Airbase.Category.AIRDROME] = "Airdrome", - [Airbase.Category.HELIPAD] = "Helipad", - [Airbase.Category.SHIP] = "Ship", - }, - } - --- Registration. - ---- Create a new AIRBASE from DCSAirbase. --- @param #AIRBASE self --- @param #string AirbaseName The name of the airbase. --- @return Wrapper.Airbase#AIRBASE -function AIRBASE:Register( AirbaseName ) - - local self = BASE:Inherit( self, POSITIONABLE:New( AirbaseName ) ) - self.AirbaseName = AirbaseName - return self -end - --- Reference methods. - ---- Finds a AIRBASE from the _DATABASE using a DCSAirbase object. --- @param #AIRBASE self --- @param Dcs.DCSWrapper.Airbase#Airbase DCSAirbase An existing DCS Airbase object reference. --- @return Wrapper.Airbase#AIRBASE self -function AIRBASE:Find( DCSAirbase ) - - local AirbaseName = DCSAirbase:getName() - local AirbaseFound = _DATABASE:FindAirbase( AirbaseName ) - return AirbaseFound -end - ---- Find a AIRBASE in the _DATABASE using the name of an existing DCS Airbase. --- @param #AIRBASE self --- @param #string AirbaseName The Airbase Name. --- @return Wrapper.Airbase#AIRBASE self -function AIRBASE:FindByName( AirbaseName ) - - local AirbaseFound = _DATABASE:FindAirbase( AirbaseName ) - return AirbaseFound -end - -function AIRBASE:GetDCSObject() - local DCSAirbase = Airbase.getByName( self.AirbaseName ) - - if DCSAirbase then - return DCSAirbase - end - - return nil -end - - - ---- This module contains the SCENERY class. --- --- 1) @{Scenery#SCENERY} class, extends @{Positionable#POSITIONABLE} --- =============================================================== --- Scenery objects are defined on the map. --- The @{Scenery#SCENERY} class is a wrapper class to handle the DCS Scenery objects: --- --- * Wraps the DCS Scenery objects. --- * Support all DCS Scenery APIs. --- * Enhance with Scenery specific APIs not in the DCS API set. --- --- @module Scenery --- @author FlightControl - - - ---- The SCENERY class --- @type SCENERY --- @extends Wrapper.Positionable#POSITIONABLE -SCENERY = { - ClassName = "SCENERY", -} - - -function SCENERY:Register( SceneryName, SceneryObject ) - local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) ) - self.SceneryName = SceneryName - self.SceneryObject = SceneryObject - return self -end - -function SCENERY:GetDCSObject() - return self.SceneryObject -end - -function SCENERY:GetThreatLevel() - - return 0, "Scenery" -end ---- Single-Player:**Yes** / Multi-Player:**Yes** / Core:**Yes** -- **Administer the scoring of player achievements, --- and create a CSV file logging the scoring events for use at team or squadron websites.** --- --- ![Banner Image](..\Presentations\SCORING\Dia1.JPG) --- --- === --- --- The @{#SCORING} class administers the scoring of player achievements, --- and creates a CSV file logging the scoring events and results for use at team or squadron websites. --- --- SCORING automatically calculates the threat level of the objects hit and destroyed by players, --- which can be @{Unit}, @{Static) and @{Scenery} objects. --- --- Positive score points are granted when enemy or neutral targets are destroyed. --- Negative score points or penalties are given when a friendly target is hit or destroyed. --- This brings a lot of dynamism in the scoring, where players need to take care to inflict damage on the right target. --- By default, penalties weight heavier in the scoring, to ensure that players don't commit fratricide. --- The total score of the player is calculated by **adding the scores minus the penalties**. --- --- ![Banner Image](..\Presentations\SCORING\Dia4.JPG) --- --- The score value is calculated based on the **threat level of the player** and the **threat level of the target**. --- A calculated score takes the threat level of the target divided by a balanced threat level of the player unit. --- As such, if the threat level of the target is high, and the player threat level is low, a higher score will be given than --- if the threat level of the player would be high too. --- --- ![Banner Image](..\Presentations\SCORING\Dia5.JPG) --- --- When multiple players hit the same target, and finally succeed in destroying the target, then each player who contributed to the target --- destruction, will receive a score. This is important for targets that require significant damage before it can be destroyed, like --- ships or heavy planes. --- --- ![Banner Image](..\Presentations\SCORING\Dia13.JPG) --- --- Optionally, the score values can be **scaled** by a **scale**. Specific scales can be set for positive cores or negative penalties. --- The default range of the scores granted is a value between 0 and 10. The default range of penalties given is a value between 0 and 30. --- --- ![Banner Image](..\Presentations\SCORING\Dia7.JPG) --- --- **Additional scores** can be granted to **specific objects**, when the player(s) destroy these objects. --- --- ![Banner Image](..\Presentations\SCORING\Dia9.JPG) --- --- Various @{Zone}s can be defined for which scores are also granted when objects in that @{Zone} are destroyed. --- This is **specifically useful** to designate **scenery targets on the map** that will generate points when destroyed. --- --- With a small change in MissionScripting.lua, the scoring results can also be logged in a **CSV file**. --- These CSV files can be used to: --- --- * Upload scoring to a database or a BI tool to publish the scoring results to the player community. --- * Upload scoring in an (online) Excel like tool, using pivot tables and pivot charts to show mission results. --- * Share scoring amoung players after the mission to discuss mission results. --- --- Scores can be **reported**. **Menu options** are automatically added to **each player group** when a player joins a client slot or a CA unit. --- Use the radio menu F10 to consult the scores while running the mission. --- Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission. --- --- # 1) @{Scoring#SCORING} class, extends @{Base#BASE} --- --- ## 1.1) Set the destroy score or penalty scale --- --- Score scales can be set for scores granted when enemies or friendlies are destroyed. --- Use the method @{#SCORING.SetScaleDestroyScore}() to set the scale of enemy destroys (positive destroys). --- Use the method @{#SCORING.SetScaleDestroyPenalty}() to set the scale of friendly destroys (negative destroys). --- --- local Scoring = SCORING:New( "Scoring File" ) --- Scoring:SetScaleDestroyScore( 10 ) --- Scoring:SetScaleDestroyPenalty( 40 ) --- --- The above sets the scale for valid scores to 10. So scores will be given in a scale from 0 to 10. --- The penalties will be given in a scale from 0 to 40. --- --- ## 1.2) Define special targets that will give extra scores. --- --- Special targets can be set that will give extra scores to the players when these are destroyed. --- Use the methods @{#SCORING.AddUnitScore}() and @{#SCORING.RemoveUnitScore}() to specify a special additional score for a specific @{Unit}s. --- Use the methods @{#SCORING.AddStaticScore}() and @{#SCORING.RemoveStaticScore}() to specify a special additional score for a specific @{Static}s. --- Use the method @{#SCORING.SetGroupGroup}() to specify a special additional score for a specific @{Group}s. --- --- local Scoring = SCORING:New( "Scoring File" ) --- Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 ) --- Scoring:AddStaticScore( STATIC:FindByName( "Static #1" ), 100 ) --- --- The above grants an additional score of 200 points for Unit #001 and an additional 100 points of Static #1 if these are destroyed. --- Note that later in the mission, one can remove these scores set, for example, when the a goal achievement time limit is over. --- For example, this can be done as follows: --- --- Scoring:RemoveUnitScore( UNIT:FindByName( "Unit #001" ) ) --- --- ## 1.3) Define destruction zones that will give extra scores. --- --- Define zones of destruction. Any object destroyed within the zone of the given category will give extra points. --- Use the method @{#SCORING.AddZoneScore}() to add a @{Zone} for additional scoring. --- Use the method @{#SCORING.RemoveZoneScore}() to remove a @{Zone} for additional scoring. --- There are interesting variations that can be achieved with this functionality. For example, if the @{Zone} is a @{Zone#ZONE_UNIT}, --- then the zone is a moving zone, and anything destroyed within that @{Zone} will generate points. --- The other implementation could be to designate a scenery target (a building) in the mission editor surrounded by a @{Zone}, --- just large enough around that building. --- --- ## 1.4) Add extra Goal scores upon an event or a condition. --- --- A mission has goals and achievements. The scoring system provides an API to set additional scores when a goal or achievement event happens. --- Use the method @{#SCORING.AddGoalScore}() to add a score for a Player at any time in your mission. --- --- ## 1.5) Configure fratricide level. --- --- When a player commits too much damage to friendlies, his penalty score will reach a certain level. --- Use the method @{#SCORING.SetFratricide}() to define the level when a player gets kicked. --- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. --- --- ## 1.6) Penalty score when a player changes the coalition. --- --- When a player changes the coalition, he can receive a penalty score. --- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition. --- By default, the penalty for changing coalition is the default penalty scale. --- --- ## 1.8) Define output CSV files. --- --- The CSV file is given the name of the string given in the @{#SCORING.New}{} constructor, followed by the .csv extension. --- The file is incrementally saved in the **\\Saved Games\\DCS\\Logs** folder, and has a time stamp indicating each mission run. --- See the following example: --- --- local ScoringFirstMission = SCORING:New( "FirstMission" ) --- local ScoringSecondMission = SCORING:New( "SecondMission" ) --- --- The above documents that 2 Scoring objects are created, ScoringFirstMission and ScoringSecondMission. --- --- ## 1.9) Configure messages. --- --- When players hit or destroy targets, messages are sent. --- Various methods exist to configure: --- --- * Which messages are sent upon the event. --- * Which audience receives the message. --- --- ### 1.9.1) Configure the messages sent upon the event. --- --- Use the following methods to configure when to send messages. By default, all messages are sent. --- --- * @{#SCORING.SetMessagesHit}(): Configure to send messages after a target has been hit. --- * @{#SCORING.SetMessagesDestroy}(): Configure to send messages after a target has been destroyed. --- * @{#SCORING.SetMessagesAddon}(): Configure to send messages for additional score, after a target has been destroyed. --- * @{#SCORING.SetMessagesZone}(): Configure to send messages for additional score, after a target has been destroyed within a given zone. --- --- ### 1.9.2) Configure the audience of the messages. --- --- Use the following methods to configure the audience of the messages. By default, the messages are sent to all players in the mission. --- --- * @{#SCORING.SetMessagesToAll}(): Configure to send messages to all players. --- * @{#SCORING.SetMessagesToCoalition}(): Configure to send messages to only those players within the same coalition as the player. --- --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-02-26: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **Wingthor (TAW)**: Testing & Advice. --- * **Dutch-Baron (TAW)**: Testing & Advice. --- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing and Advice. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module Scoring - - ---- The Scoring class --- @type SCORING --- @field Players A collection of the current players that have joined the game. --- @extends Core.Base#BASE -SCORING = { - ClassName = "SCORING", - ClassID = 0, - Players = {}, -} - -local _SCORINGCoalition = - { - [1] = "Red", - [2] = "Blue", - } - -local _SCORINGCategory = - { - [Unit.Category.AIRPLANE] = "Plane", - [Unit.Category.HELICOPTER] = "Helicopter", - [Unit.Category.GROUND_UNIT] = "Vehicle", - [Unit.Category.SHIP] = "Ship", - [Unit.Category.STRUCTURE] = "Structure", - } - ---- Creates a new SCORING object to administer the scoring achieved by players. --- @param #SCORING self --- @param #string GameName The name of the game. This name is also logged in the CSV score file. --- @return #SCORING self --- @usage --- -- Define a new scoring object for the mission Gori Valley. --- ScoringObject = SCORING:New( "Gori Valley" ) -function SCORING:New( GameName ) - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) -- #SCORING - - if GameName then - self.GameName = GameName - else - error( "A game name must be given to register the scoring results" ) - end - - - -- Additional Object scores - self.ScoringObjects = {} - - -- Additional Zone scores. - self.ScoringZones = {} - - -- Configure Messages - self:SetMessagesToAll() - self:SetMessagesHit( true ) - self:SetMessagesDestroy( true ) - self:SetMessagesScore( true ) - self:SetMessagesZone( true ) - - -- Scales - self:SetScaleDestroyScore( 10 ) - self:SetScaleDestroyPenalty( 30 ) - - -- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked). - self:SetFratricide( self.ScaleDestroyPenalty * 3 ) - - -- Default penalty when a player changes coalition. - self:SetCoalitionChangePenalty( self.ScaleDestroyPenalty ) - - -- Event handlers - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Hit, self._EventOnHit ) - self:HandleEvent( EVENTS.PlayerEnterUnit ) - self:HandleEvent( EVENTS.PlayerLeaveUnit ) - - -- Create the CSV file. - self:OpenCSV( GameName ) - - return self - -end - ---- Set the scale for scoring valid destroys (enemy destroys). --- A default calculated score is a value between 1 and 10. --- The scale magnifies the scores given to the players. --- @param #SCORING self --- @param #number Scale The scale of the score given. -function SCORING:SetScaleDestroyScore( Scale ) - - self.ScaleDestroyScore = Scale - - return self -end - ---- Set the scale for scoring penalty destroys (friendly destroys). --- A default calculated penalty is a value between 1 and 10. --- The scale magnifies the scores given to the players. --- @param #SCORING self --- @param #number Scale The scale of the score given. --- @return #SCORING -function SCORING:SetScaleDestroyPenalty( Scale ) - - self.ScaleDestroyPenalty = Scale - - return self -end - ---- Add a @{Unit} for additional scoring when the @{Unit} is destroyed. --- Note that if there was already a @{Unit} declared within the scoring with the same name, --- then the old @{Unit} will be replaced with the new @{Unit}. --- @param #SCORING self --- @param Wrapper.Unit#UNIT ScoreUnit The @{Unit} for which the Score needs to be given. --- @param #number Score The Score value. --- @return #SCORING -function SCORING:AddUnitScore( ScoreUnit, Score ) - - local UnitName = ScoreUnit:GetName() - - self.ScoringObjects[UnitName] = Score - - return self -end - ---- Removes a @{Unit} for additional scoring when the @{Unit} is destroyed. --- @param #SCORING self --- @param Wrapper.Unit#UNIT ScoreUnit The @{Unit} for which the Score needs to be given. --- @return #SCORING -function SCORING:RemoveUnitScore( ScoreUnit ) - - local UnitName = ScoreUnit:GetName() - - self.ScoringObjects[UnitName] = nil - - return self -end - ---- Add a @{Static} for additional scoring when the @{Static} is destroyed. --- Note that if there was already a @{Static} declared within the scoring with the same name, --- then the old @{Static} will be replaced with the new @{Static}. --- @param #SCORING self --- @param Wrapper.Static#UNIT ScoreStatic The @{Static} for which the Score needs to be given. --- @param #number Score The Score value. --- @return #SCORING -function SCORING:AddStaticScore( ScoreStatic, Score ) - - local StaticName = ScoreStatic:GetName() - - self.ScoringObjects[StaticName] = Score - - return self -end - ---- Removes a @{Static} for additional scoring when the @{Static} is destroyed. --- @param #SCORING self --- @param Wrapper.Static#UNIT ScoreStatic The @{Static} for which the Score needs to be given. --- @return #SCORING -function SCORING:RemoveStaticScore( ScoreStatic ) - - local StaticName = ScoreStatic:GetName() - - self.ScoringObjects[StaticName] = nil - - return self -end - - ---- Specify a special additional score for a @{Group}. --- @param #SCORING self --- @param Wrapper.Group#GROUP ScoreGroup The @{Group} for which each @{Unit} a Score is given. --- @param #number Score The Score value. --- @return #SCORING -function SCORING:AddScoreGroup( ScoreGroup, Score ) - - local ScoreUnits = ScoreGroup:GetUnits() - - for ScoreUnitID, ScoreUnit in pairs( ScoreUnits ) do - local UnitName = ScoreUnit:GetName() - self.ScoringObjects[UnitName] = Score - end - - return self -end - ---- Add a @{Zone} to define additional scoring when any object is destroyed in that zone. --- Note that if a @{Zone} with the same name is already within the scoring added, the @{Zone} (type) and Score will be replaced! --- This allows for a dynamic destruction zone evolution within your mission. --- @param #SCORING self --- @param Core.Zone#ZONE_BASE ScoreZone The @{Zone} which defines the destruction score perimeters. --- Note that a zone can be a polygon or a moving zone. --- @param #number Score The Score value. --- @return #SCORING -function SCORING:AddZoneScore( ScoreZone, Score ) - - local ZoneName = ScoreZone:GetName() - - self.ScoringZones[ZoneName] = {} - self.ScoringZones[ZoneName].ScoreZone = ScoreZone - self.ScoringZones[ZoneName].Score = Score - - return self -end - ---- Remove a @{Zone} for additional scoring. --- The scoring will search if any @{Zone} is added with the given name, and will remove that zone from the scoring. --- This allows for a dynamic destruction zone evolution within your mission. --- @param #SCORING self --- @param Core.Zone#ZONE_BASE ScoreZone The @{Zone} which defines the destruction score perimeters. --- Note that a zone can be a polygon or a moving zone. --- @return #SCORING -function SCORING:RemoveZoneScore( ScoreZone ) - - local ZoneName = ScoreZone:GetName() - - self.ScoringZones[ZoneName] = nil - - return self -end - - ---- Configure to send messages after a target has been hit. --- @param #SCORING self --- @param #boolean OnOff If true is given, the messages are sent. --- @return #SCORING -function SCORING:SetMessagesHit( OnOff ) - - self.MessagesHit = OnOff - return self -end - ---- If to send messages after a target has been hit. --- @param #SCORING self --- @return #boolean -function SCORING:IfMessagesHit() - - return self.MessagesHit -end - ---- Configure to send messages after a target has been destroyed. --- @param #SCORING self --- @param #boolean OnOff If true is given, the messages are sent. --- @return #SCORING -function SCORING:SetMessagesDestroy( OnOff ) - - self.MessagesDestroy = OnOff - return self -end - ---- If to send messages after a target has been destroyed. --- @param #SCORING self --- @return #boolean -function SCORING:IfMessagesDestroy() - - return self.MessagesDestroy -end - ---- Configure to send messages after a target has been destroyed and receives additional scores. --- @param #SCORING self --- @param #boolean OnOff If true is given, the messages are sent. --- @return #SCORING -function SCORING:SetMessagesScore( OnOff ) - - self.MessagesScore = OnOff - return self -end - ---- If to send messages after a target has been destroyed and receives additional scores. --- @param #SCORING self --- @return #boolean -function SCORING:IfMessagesScore() - - return self.MessagesScore -end - ---- Configure to send messages after a target has been hit in a zone, and additional score is received. --- @param #SCORING self --- @param #boolean OnOff If true is given, the messages are sent. --- @return #SCORING -function SCORING:SetMessagesZone( OnOff ) - - self.MessagesZone = OnOff - return self -end - ---- If to send messages after a target has been hit in a zone, and additional score is received. --- @param #SCORING self --- @return #boolean -function SCORING:IfMessagesZone() - - return self.MessagesZone -end - ---- Configure to send messages to all players. --- @param #SCORING self --- @return #SCORING -function SCORING:SetMessagesToAll() - - self.MessagesAudience = 1 - return self -end - ---- If to send messages to all players. --- @param #SCORING self --- @return #boolean -function SCORING:IfMessagesToAll() - - return self.MessagesAudience == 1 -end - ---- Configure to send messages to only those players within the same coalition as the player. --- @param #SCORING self --- @return #SCORING -function SCORING:SetMessagesToCoalition() - - self.MessagesAudience = 2 - return self -end - ---- If to send messages to only those players within the same coalition as the player. --- @param #SCORING self --- @return #boolean -function SCORING:IfMessagesToCoalition() - - return self.MessagesAudience == 2 -end - - ---- When a player commits too much damage to friendlies, his penalty score will reach a certain level. --- Use this method to define the level when a player gets kicked. --- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. --- @param #SCORING self --- @param #number Fratricide The amount of maximum penalty that may be inflicted by a friendly player before he gets kicked. --- @return #SCORING -function SCORING:SetFratricide( Fratricide ) - - self.Fratricide = Fratricide - return self -end - - ---- When a player changes the coalition, he can receive a penalty score. --- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition. --- By default, the penalty for changing coalition is the default penalty scale. --- @param #SCORING self --- @param #number CoalitionChangePenalty The amount of penalty that is given. --- @return #SCORING -function SCORING:SetCoalitionChangePenalty( CoalitionChangePenalty ) - - self.CoalitionChangePenalty = CoalitionChangePenalty - return self -end - - ---- Add a new player entering a Unit. --- @param #SCORING self --- @param Wrapper.Unit#UNIT UnitData -function SCORING:_AddPlayerFromUnit( UnitData ) - self:F( UnitData ) - - if UnitData:IsAlive() then - local UnitName = UnitData:GetName() - local PlayerName = UnitData:GetPlayerName() - local UnitDesc = UnitData:GetDesc() - local UnitCategory = UnitDesc.category - local UnitCoalition = UnitData:GetCoalition() - local UnitTypeName = UnitData:GetTypeName() - local UnitThreatLevel, UnitThreatType = UnitData:GetThreatLevel() - - self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) - - if self.Players[PlayerName] == nil then -- I believe this is the place where a Player gets a life in a mission when he enters a unit ... - self.Players[PlayerName] = {} - self.Players[PlayerName].Hit = {} - self.Players[PlayerName].Destroy = {} - self.Players[PlayerName].Goals = {} - self.Players[PlayerName].Mission = {} - - -- for CategoryID, CategoryName in pairs( SCORINGCategory ) do - -- self.Players[PlayerName].Hit[CategoryID] = {} - -- self.Players[PlayerName].Destroy[CategoryID] = {} - -- end - self.Players[PlayerName].HitPlayers = {} - self.Players[PlayerName].Score = 0 - self.Players[PlayerName].Penalty = 0 - self.Players[PlayerName].PenaltyCoalition = 0 - self.Players[PlayerName].PenaltyWarning = 0 - end - - if not self.Players[PlayerName].UnitCoalition then - self.Players[PlayerName].UnitCoalition = UnitCoalition - else - if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then - self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50 - self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] .. - "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.", - 2 - ):ToAll() - self:ScoreCSV( PlayerName, "", "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, - UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() ) - end - end - self.Players[PlayerName].UnitName = UnitName - self.Players[PlayerName].UnitCoalition = UnitCoalition - self.Players[PlayerName].UnitCategory = UnitCategory - self.Players[PlayerName].UnitType = UnitTypeName - self.Players[PlayerName].UNIT = UnitData - self.Players[PlayerName].ThreatLevel = UnitThreatLevel - self.Players[PlayerName].ThreatType = UnitThreatType - - if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then - if self.Players[PlayerName].PenaltyWarning < 1 then - MESSAGE:New( "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, - 30 - ):ToAll() - self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1 - end - end - - if self.Players[PlayerName].Penalty > self.Fratricide then - UnitData:Destroy() - MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", - 10 - ):ToAll() - end - - end -end - - ---- Add a goal score for a player. --- The method takes the PlayerUnit for which the Goal score needs to be set. --- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal. --- A free text can be given that is shown to the players. --- The Score can be both positive and negative. --- @param #SCORING self --- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the Player. Other Properties for the scoring are taken from this PlayerUnit, like coalition, type etc. --- @param #string GoalTag The string or identifier that is used in the CSV file to identify the goal (sort or group later in Excel). --- @param #string Text A free text that is shown to the players. --- @param #number Score The score can be both positive or negative ( Penalty ). -function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score ) - - local PlayerName = PlayerUnit:GetPlayerName() - - self:E( { PlayerUnit.UnitName, PlayerName, GoalTag, Text, Score } ) - - -- PlayerName can be nil, if the Unit with the player crashed or due to another reason. - if PlayerName then - local PlayerData = self.Players[PlayerName] - - PlayerData.Goals[GoalTag] = PlayerData.Goals[GoalTag] or { Score = 0 } - PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score - PlayerData.Score = PlayerData.Score + Score - - MESSAGE:New( Text, 30 ):ToAll() - - self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() ) - end -end - - ---- Registers Scores the players completing a Mission Task. --- @param #SCORING self --- @param Tasking.Mission#MISSION Mission --- @param Wrapper.Unit#UNIT PlayerUnit --- @param #string Text --- @param #number Score -function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score ) - - local PlayerName = PlayerUnit:GetPlayerName() - local MissionName = Mission:GetName() - - self:E( { Mission:GetName(), PlayerUnit.UnitName, PlayerName, Text, Score } ) - - -- PlayerName can be nil, if the Unit with the player crashed or due to another reason. - if PlayerName then - local PlayerData = self.Players[PlayerName] - - if not PlayerData.Mission[MissionName] then - PlayerData.Mission[MissionName] = {} - PlayerData.Mission[MissionName].ScoreTask = 0 - PlayerData.Mission[MissionName].ScoreMission = 0 - end - - self:T( PlayerName ) - self:T( PlayerData.Mission[MissionName] ) - - PlayerData.Score = self.Players[PlayerName].Score + Score - PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score - - MESSAGE:New( "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " .. - Score .. " task score!", - 30 ):ToAll() - - self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() ) - end -end - - ---- Registers Mission Scores for possible multiple players that contributed in the Mission. --- @param #SCORING self --- @param Tasking.Mission#MISSION Mission --- @param Wrapper.Unit#UNIT PlayerUnit --- @param #string Text --- @param #number Score -function SCORING:_AddMissionScore( Mission, Text, Score ) - - local MissionName = Mission:GetName() - - self:E( { Mission, Text, Score } ) - self:E( self.Players ) - - for PlayerName, PlayerData in pairs( self.Players ) do - - self:E( PlayerData ) - if PlayerData.Mission[MissionName] then - - PlayerData.Score = PlayerData.Score + Score - PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score - - MESSAGE:New( "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " .. - Score .. " mission score!", - 60 ):ToAll() - - self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score ) - end - end -end - - ---- Handles the OnPlayerEnterUnit event for the scoring. --- @param #SCORING self --- @param Core.Event#EVENTDATA Event -function SCORING:OnEventPlayerEnterUnit( Event ) - if Event.IniUnit then - self:_AddPlayerFromUnit( Event.IniUnit ) - local Menu = MENU_GROUP:New( Event.IniGroup, 'Scoring' ) - local ReportGroupSummary = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Summary report players in group', Menu, SCORING.ReportScoreGroupSummary, self, Event.IniGroup ) - local ReportGroupDetailed = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Detailed report players in group', Menu, SCORING.ReportScoreGroupDetailed, self, Event.IniGroup ) - local ReportToAllSummary = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Summary report all players', Menu, SCORING.ReportScoreAllSummary, self, Event.IniGroup ) - self:SetState( Event.IniUnit, "ScoringMenu", Menu ) - end -end - ---- Handles the OnPlayerLeaveUnit event for the scoring. --- @param #SCORING self --- @param Core.Event#EVENTDATA Event -function SCORING:OnEventPlayerLeaveUnit( Event ) - if Event.IniUnit then - local Menu = self:GetState( Event.IniUnit, "ScoringMenu" ) -- Core.Menu#MENU_GROUP - if Menu then - Menu:Remove() - end - end -end - - ---- Handles the OnHit event for the scoring. --- @param #SCORING self --- @param Core.Event#EVENTDATA Event -function SCORING:_EventOnHit( Event ) - self:F( { Event } ) - - local InitUnit = nil - local InitUNIT = nil - local InitUnitName = "" - local InitGroup = nil - local InitGroupName = "" - local InitPlayerName = nil - - local InitCoalition = nil - local InitCategory = nil - local InitType = nil - local InitUnitCoalition = nil - local InitUnitCategory = nil - local InitUnitType = nil - - local TargetUnit = nil - local TargetUNIT = nil - local TargetUnitName = "" - local TargetGroup = nil - local TargetGroupName = "" - local TargetPlayerName = nil - - local TargetCoalition = nil - local TargetCategory = nil - local TargetType = nil - local TargetUnitCoalition = nil - local TargetUnitCategory = nil - local TargetUnitType = nil - - if Event.IniDCSUnit then - - InitUnit = Event.IniDCSUnit - InitUNIT = Event.IniUnit - InitUnitName = Event.IniDCSUnitName - InitGroup = Event.IniDCSGroup - InitGroupName = Event.IniDCSGroupName - InitPlayerName = Event.IniPlayerName - - InitCoalition = Event.IniCoalition - --TODO: Workaround Client DCS Bug - --InitCategory = InitUnit:getCategory() - --InitCategory = InitUnit:getDesc().category - InitCategory = Event.IniCategory - InitType = Event.IniTypeName - - InitUnitCoalition = _SCORINGCoalition[InitCoalition] - InitUnitCategory = _SCORINGCategory[InitCategory] - InitUnitType = InitType - - self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType , InitUnitCoalition, InitUnitCategory, InitUnitType } ) - end - - - if Event.TgtDCSUnit then - - TargetUnit = Event.TgtDCSUnit - TargetUNIT = Event.TgtUnit - TargetUnitName = Event.TgtDCSUnitName - TargetGroup = Event.TgtDCSGroup - TargetGroupName = Event.TgtDCSGroupName - TargetPlayerName = Event.TgtPlayerName - - TargetCoalition = Event.TgtCoalition - --TODO: Workaround Client DCS Bug - --TargetCategory = TargetUnit:getCategory() - --TargetCategory = TargetUnit:getDesc().category - TargetCategory = Event.TgtCategory - TargetType = Event.TgtTypeName - - TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] - TargetUnitCategory = _SCORINGCategory[TargetCategory] - TargetUnitType = TargetType - - self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType, TargetUnitCoalition, TargetUnitCategory, TargetUnitType } ) - end - - if InitPlayerName ~= nil then -- It is a player that is hitting something - self:_AddPlayerFromUnit( InitUNIT ) - if self.Players[InitPlayerName] then -- This should normally not happen, but i'll test it anyway. - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - self:_AddPlayerFromUnit( TargetUNIT ) - end - - self:T( "Hitting Something" ) - - -- What is he hitting? - if TargetCategory then - - -- A target got hit, score it. - -- Player contains the score data from self.Players[InitPlayerName] - local Player = self.Players[InitPlayerName] - - -- Ensure there is a hit table per TargetCategory and TargetUnitName. - Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {} - Player.Hit[TargetCategory][TargetUnitName] = Player.Hit[TargetCategory][TargetUnitName] or {} - - -- PlayerHit contains the score counters and data per unit that was hit. - local PlayerHit = Player.Hit[TargetCategory][TargetUnitName] - - PlayerHit.Score = PlayerHit.Score or 0 - PlayerHit.Penalty = PlayerHit.Penalty or 0 - PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0 - PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 - PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 - PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT - PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() - - -- Only grant hit scores if there was more than one second between the last hit. - if timer.getTime() - PlayerHit.TimeStamp > 1 then - PlayerHit.TimeStamp = timer.getTime() - - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - - -- Ensure there is a Player to Player hit reference table. - Player.HitPlayers[TargetPlayerName] = true - end - - local Score = 0 - - if InitCoalition then -- A coalition object was hit. - if InitCoalition == TargetCoalition then - Player.Penalty = Player.Penalty + 10 - PlayerHit.Penalty = PlayerHit.Penalty + 10 - PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 - - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. - "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - else - MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit a friendly target " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. - "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - end - self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - Player.Score = Player.Score + 1 - PlayerHit.Score = PlayerHit.Score + 1 - PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. - "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - else - MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit an enemy target " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. - "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - end - self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - else -- A scenery object was hit. - MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit a scenery object.", - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 0, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) - end - end - end - end - elseif InitPlayerName == nil then -- It is an AI hitting a player??? - - end - - -- It is a weapon initiated by a player, that is hitting something - -- This seems to occur only with scenery and static objects. - if Event.WeaponPlayerName ~= nil then - self:_AddPlayerFromUnit( Event.WeaponUNIT ) - if self.Players[Event.WeaponPlayerName] then -- This should normally not happen, but i'll test it anyway. - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - self:_AddPlayerFromUnit( TargetUNIT ) - end - - self:T( "Hitting Scenery" ) - - -- What is he hitting? - if TargetCategory then - - -- A scenery or static got hit, score it. - -- Player contains the score data from self.Players[WeaponPlayerName] - local Player = self.Players[Event.WeaponPlayerName] - - -- Ensure there is a hit table per TargetCategory and TargetUnitName. - Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {} - Player.Hit[TargetCategory][TargetUnitName] = Player.Hit[TargetCategory][TargetUnitName] or {} - - -- PlayerHit contains the score counters and data per unit that was hit. - local PlayerHit = Player.Hit[TargetCategory][TargetUnitName] - - PlayerHit.Score = PlayerHit.Score or 0 - PlayerHit.Penalty = PlayerHit.Penalty or 0 - PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0 - PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 - PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 - PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT - PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() - - -- Only grant hit scores if there was more than one second between the last hit. - if timer.getTime() - PlayerHit.TimeStamp > 1 then - PlayerHit.TimeStamp = timer.getTime() - - local Score = 0 - - if InitCoalition then -- A coalition object was hit, probably a static. - if InitCoalition == TargetCoalition then - -- TODO: Penalty according scale - Player.Penalty = Player.Penalty + 10 - PlayerHit.Penalty = PlayerHit.Penalty + 10 - PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 - - MESSAGE - :New( "Player '" .. Event.WeaponPlayerName .. "' hit a friendly target " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. - "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - Player.Score = Player.Score + 1 - PlayerHit.Score = PlayerHit.Score + 1 - PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 - MESSAGE - :New( "Player '" .. Event.WeaponPlayerName .. "' hit an enemy target " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. - "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - else -- A scenery object was hit. - MESSAGE - :New( "Player '" .. Event.WeaponPlayerName .. "' hit a scenery object.", - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - self:ScoreCSV( Event.WeaponPlayerName, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType ) - end - end - end - end - end -end - ---- Track DEAD or CRASH events for the scoring. --- @param #SCORING self --- @param Core.Event#EVENTDATA Event -function SCORING:_EventOnDeadOrCrash( Event ) - self:F( { Event } ) - - local TargetUnit = nil - local TargetGroup = nil - local TargetUnitName = "" - local TargetGroupName = "" - local TargetPlayerName = "" - local TargetCoalition = nil - local TargetCategory = nil - local TargetType = nil - local TargetUnitCoalition = nil - local TargetUnitCategory = nil - local TargetUnitType = nil - - if Event.IniDCSUnit then - - TargetUnit = Event.IniUnit - TargetUnitName = Event.IniDCSUnitName - TargetGroup = Event.IniDCSGroup - TargetGroupName = Event.IniDCSGroupName - TargetPlayerName = Event.IniPlayerName - - TargetCoalition = Event.IniCoalition - --TargetCategory = TargetUnit:getCategory() - --TargetCategory = TargetUnit:getDesc().category -- Workaround - TargetCategory = Event.IniCategory - TargetType = Event.IniTypeName - - TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] - TargetUnitCategory = _SCORINGCategory[TargetCategory] - TargetUnitType = TargetType - - self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) - end - - -- Player contains the score and reference data for the player. - for PlayerName, Player in pairs( self.Players ) do - if Player then -- This should normally not happen, but i'll test it anyway. - self:T( "Something got destroyed" ) - - -- Some variables - local InitUnitName = Player.UnitName - local InitUnitType = Player.UnitType - local InitCoalition = Player.UnitCoalition - local InitCategory = Player.UnitCategory - local InitUnitCoalition = _SCORINGCoalition[InitCoalition] - local InitUnitCategory = _SCORINGCategory[InitCategory] - - self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) - - local Destroyed = false - - -- What is the player destroying? - if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 then -- Was there a hit for this unit for this player before registered??? - - local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel - local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType - - Player.Destroy[TargetCategory] = Player.Destroy[TargetCategory] or {} - Player.Destroy[TargetCategory][TargetType] = Player.Destroy[TargetCategory][TargetType] or {} - - -- PlayerDestroy contains the destroy score data per category and target type of the player. - local TargetDestroy = Player.Destroy[TargetCategory][TargetType] - TargetDestroy.Score = TargetDestroy.Score or 0 - TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy or 0 - TargetDestroy.Penalty = TargetDestroy.Penalty or 0 - TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy or 0 - - if TargetCoalition then - if InitCoalition == TargetCoalition then - local ThreatLevelTarget = TargetThreatLevel - local ThreatTypeTarget = TargetThreatType - local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1 - local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyPenalty / 10 ) - self:E( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) - - Player.Penalty = Player.Penalty + ThreatPenalty - TargetDestroy.Penalty = TargetDestroy.Penalty + ThreatPenalty - TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1 - - if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player - MESSAGE - :New( "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. - TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " .. - "Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, - 15 - ) - :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) - else - MESSAGE - :New( "Player '" .. PlayerName .. "' destroyed a friendly target " .. - TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " .. - "Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, - 15 - ) - :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) - end - - Destroyed = true - self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - - local ThreatLevelTarget = TargetThreatLevel - local ThreatTypeTarget = TargetThreatType - local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1 - local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) - - self:E( { ThreatLevel = ThreatScore, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) - - Player.Score = Player.Score + ThreatScore - TargetDestroy.Score = TargetDestroy.Score + ThreatScore - TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1 - if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player - MESSAGE - :New( "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. - TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " .. - "Score: " .. TargetDestroy.Score .. ". Score Total:" .. Player.Score - Player.Penalty, - 15 - ) - :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) - else - MESSAGE - :New( "Player '" .. PlayerName .. "' destroyed an enemy " .. - TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " .. - "Score: " .. TargetDestroy.Score .. ". Total:" .. Player.Score - Player.Penalty, - 15 - ) - :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) - end - Destroyed = true - self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - - local UnitName = TargetUnit:GetName() - local Score = self.ScoringObjects[UnitName] - if Score then - Player.Score = Player.Score + Score - TargetDestroy.Score = TargetDestroy.Score + Score - MESSAGE - :New( "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " .. - "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty, - 15 - ) - :ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() ) - self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - Destroyed = true - end - - -- Check if there are Zones where the destruction happened. - for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do - self:E( { ScoringZone = ScoreZoneData } ) - local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE - local Score = ScoreZoneData.Score - if ScoreZone:IsVec2InZone( TargetUnit:GetVec2() ) then - Player.Score = Player.Score + Score - TargetDestroy.Score = TargetDestroy.Score + Score - MESSAGE - :New( "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." .. - "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. - "Total: " .. Player.Score - Player.Penalty, - 15 ) - :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) - self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - Destroyed = true - end - end - - end - else - -- Check if there are Zones where the destruction happened. - for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do - self:E( { ScoringZone = ScoreZoneData } ) - local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE - local Score = ScoreZoneData.Score - if ScoreZone:IsVec2InZone( TargetUnit:GetVec2() ) then - Player.Score = Player.Score + Score - TargetDestroy.Score = TargetDestroy.Score + Score - MESSAGE - :New( "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." .. - "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. - "Total: " .. Player.Score - Player.Penalty, - 15 - ) - :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) - Destroyed = true - self:ScoreCSV( PlayerName, "", "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) - end - end - end - - -- Delete now the hit cache if the target was destroyed. - -- Otherwise points will be granted every time a target gets killed by the players that hit that target. - -- This is only relevant for player to player destroys. - if Destroyed then - Player.Hit[TargetCategory][TargetUnitName].TimeStamp = 0 - end - end - end - end -end - - ---- Produce detailed report of player hit scores. --- @param #SCORING self --- @param #string PlayerName The name of the player. --- @return #string The report. -function SCORING:ReportDetailedPlayerHits( PlayerName ) - - local ScoreMessage = "" - local PlayerScore = 0 - local PlayerPenalty = 0 - - local PlayerData = self.Players[PlayerName] - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) - - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local ScoreMessageHits = "" - for CategoryID, CategoryName in pairs( _SCORINGCategory ) do - self:T( CategoryName ) - if PlayerData.Hit[CategoryID] then - self:T( "Hit scores exist for player " .. PlayerName ) - local Score = 0 - local ScoreHit = 0 - local Penalty = 0 - local PenaltyHit = 0 - for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do - Score = Score + UnitData.Score - ScoreHit = ScoreHit + UnitData.ScoreHit - Penalty = Penalty + UnitData.Penalty - PenaltyHit = UnitData.PenaltyHit - end - local ScoreMessageHit = string.format( "%s:%d ", CategoryName, Score - Penalty ) - self:T( ScoreMessageHit ) - ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageHits ~= "" then - ScoreMessage = "Hits: " .. ScoreMessageHits - end - end - - return ScoreMessage, PlayerScore, PlayerPenalty -end - - ---- Produce detailed report of player destroy scores. --- @param #SCORING self --- @param #string PlayerName The name of the player. --- @return #string The report. -function SCORING:ReportDetailedPlayerDestroys( PlayerName ) - - local ScoreMessage = "" - local PlayerScore = 0 - local PlayerPenalty = 0 - - local PlayerData = self.Players[PlayerName] - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) - - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local ScoreMessageDestroys = "" - for CategoryID, CategoryName in pairs( _SCORINGCategory ) do - if PlayerData.Destroy[CategoryID] then - self:T( "Destroy scores exist for player " .. PlayerName ) - local Score = 0 - local ScoreDestroy = 0 - local Penalty = 0 - local PenaltyDestroy = 0 - - for UnitName, UnitData in pairs( PlayerData.Destroy[CategoryID] ) do - self:E( { UnitData = UnitData } ) - if UnitData ~= {} then - Score = Score + UnitData.Score - ScoreDestroy = ScoreDestroy + UnitData.ScoreDestroy - Penalty = Penalty + UnitData.Penalty - PenaltyDestroy = PenaltyDestroy + UnitData.PenaltyDestroy - end - end - - local ScoreMessageDestroy = string.format( " %s:%d ", CategoryName, Score - Penalty ) - self:T( ScoreMessageDestroy ) - ScoreMessageDestroys = ScoreMessageDestroys .. ScoreMessageDestroy - - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageDestroys = ScoreMessageDestroys .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageDestroys ~= "" then - ScoreMessage = "Destroys: " .. ScoreMessageDestroys - end - end - - return ScoreMessage, PlayerScore, PlayerPenalty -end - ---- Produce detailed report of player penalty scores because of changing the coalition. --- @param #SCORING self --- @param #string PlayerName The name of the player. --- @return #string The report. -function SCORING:ReportDetailedPlayerCoalitionChanges( PlayerName ) - - local ScoreMessage = "" - local PlayerScore = 0 - local PlayerPenalty = 0 - - local PlayerData = self.Players[PlayerName] - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) - - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local ScoreMessageCoalitionChangePenalties = "" - if PlayerData.PenaltyCoalition ~= 0 then - ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) - PlayerPenalty = PlayerPenalty + PlayerData.Penalty - end - if ScoreMessageCoalitionChangePenalties ~= "" then - ScoreMessage = ScoreMessage .. "Coalition Penalties: " .. ScoreMessageCoalitionChangePenalties - end - end - - return ScoreMessage, PlayerScore, PlayerPenalty -end - ---- Produce detailed report of player goal scores. --- @param #SCORING self --- @param #string PlayerName The name of the player. --- @return #string The report. -function SCORING:ReportDetailedPlayerGoals( PlayerName ) - - local ScoreMessage = "" - local PlayerScore = 0 - local PlayerPenalty = 0 - - local PlayerData = self.Players[PlayerName] - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) - - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local ScoreMessageGoal = "" - local ScoreGoal = 0 - local ScoreTask = 0 - for GoalName, GoalData in pairs( PlayerData.Goals ) do - ScoreGoal = ScoreGoal + GoalData.Score - ScoreMessageGoal = ScoreMessageGoal .. "'" .. GoalName .. "':" .. GoalData.Score .. "; " - end - PlayerScore = PlayerScore + ScoreGoal - - if ScoreMessageGoal ~= "" then - ScoreMessage = "Goals: " .. ScoreMessageGoal - end - end - - return ScoreMessage, PlayerScore, PlayerPenalty -end - ---- Produce detailed report of player penalty scores because of changing the coalition. --- @param #SCORING self --- @param #string PlayerName The name of the player. --- @return #string The report. -function SCORING:ReportDetailedPlayerMissions( PlayerName ) - - local ScoreMessage = "" - local PlayerScore = 0 - local PlayerPenalty = 0 - - local PlayerData = self.Players[PlayerName] - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) - - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local ScoreMessageMission = "" - local ScoreMission = 0 - local ScoreTask = 0 - for MissionName, MissionData in pairs( PlayerData.Mission ) do - ScoreMission = ScoreMission + MissionData.ScoreMission - ScoreTask = ScoreTask + MissionData.ScoreTask - ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " - end - PlayerScore = PlayerScore + ScoreMission + ScoreTask - - if ScoreMessageMission ~= "" then - ScoreMessage = "Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ")" - end - end - - return ScoreMessage, PlayerScore, PlayerPenalty -end - - ---- Report Group Score Summary --- @param #SCORING self --- @param Wrapper.Group#GROUP PlayerGroup The player group. -function SCORING:ReportScoreGroupSummary( PlayerGroup ) - - local PlayerMessage = "" - - self:T( "Report Score Group Summary" ) - - local PlayerUnits = PlayerGroup:GetUnits() - for UnitID, PlayerUnit in pairs( PlayerUnits ) do - local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT - local PlayerName = PlayerUnit:GetPlayerName() - - if PlayerName then - - local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) - ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits - self:E( { ReportHits, ScoreHits, PenaltyHits } ) - - local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) - ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys - self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) - - local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) - ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges - self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) - - local ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) - ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals - self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) - - local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) - ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions - self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) - - local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions - local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions - - PlayerMessage = - string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", - PlayerName, - PlayerScore - PlayerPenalty, - PlayerScore, - PlayerPenalty - ) - MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) - end - end - -end - ---- Report Group Score Detailed --- @param #SCORING self --- @param Wrapper.Group#GROUP PlayerGroup The player group. -function SCORING:ReportScoreGroupDetailed( PlayerGroup ) - - local PlayerMessage = "" - - self:T( "Report Score Group Detailed" ) - - local PlayerUnits = PlayerGroup:GetUnits() - for UnitID, PlayerUnit in pairs( PlayerUnits ) do - local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT - local PlayerName = PlayerUnit:GetPlayerName() - - if PlayerName then - - local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) - ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits - self:E( { ReportHits, ScoreHits, PenaltyHits } ) - - local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) - ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys - self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) - - local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) - ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges - self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) - - local ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) - ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals - self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) - - local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) - ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions - self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) - - local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions - local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions - - PlayerMessage = - string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s", - PlayerName, - PlayerScore - PlayerPenalty, - PlayerScore, - PlayerPenalty, - ReportHits, - ReportDestroys, - ReportCoalitionChanges, - ReportGoals, - ReportMissions - ) - MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) - end - end - -end - ---- Report all players score --- @param #SCORING self --- @param Wrapper.Group#GROUP PlayerGroup The player group. -function SCORING:ReportScoreAllSummary( PlayerGroup ) - - local PlayerMessage = "" - - self:T( "Report Score All Players" ) - - for PlayerName, PlayerData in pairs( self.Players ) do - - if PlayerName then - - local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) - ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits - self:E( { ReportHits, ScoreHits, PenaltyHits } ) - - local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) - ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys - self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) - - local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) - ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges - self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) - - local ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) - ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals - self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) - - local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) - ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions - self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) - - local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions - local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions - - PlayerMessage = - string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", - PlayerName, - PlayerScore - PlayerPenalty, - PlayerScore, - PlayerPenalty - ) - MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) - end - end - -end - - -function SCORING:SecondsToClock(sSeconds) - local nSeconds = sSeconds - if nSeconds == 0 then - --return nil; - return "00:00:00"; - else - nHours = string.format("%02.f", math.floor(nSeconds/3600)); - nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60))); - nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60)); - return nHours..":"..nMins..":"..nSecs - end -end - ---- Opens a score CSV file to log the scores. --- @param #SCORING self --- @param #string ScoringCSV --- @return #SCORING self --- @usage --- -- Open a new CSV file to log the scores of the game Gori Valley. Let the name of the CSV file begin with "Player Scores". --- ScoringObject = SCORING:New( "Gori Valley" ) --- ScoringObject:OpenCSV( "Player Scores" ) -function SCORING:OpenCSV( ScoringCSV ) - self:F( ScoringCSV ) - - if lfs and io and os then - if ScoringCSV then - self.ScoringCSV = ScoringCSV - local fdir = lfs.writedir() .. [[Logs\]] .. self.ScoringCSV .. " " .. os.date( "%Y-%m-%d %H-%M-%S" ) .. ".csv" - - self.CSVFile, self.err = io.open( fdir, "w+" ) - if not self.CSVFile then - error( "Error: Cannot open CSV file in " .. lfs.writedir() ) - end - - self.CSVFile:write( '"GameName","RunTime","Time","PlayerName","TargetPlayerName","ScoreType","PlayerUnitCoaltion","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n' ) - - self.RunTime = os.date("%y-%m-%d_%H-%M-%S") - else - error( "A string containing the CSV file name must be given." ) - end - else - self:E( "The MissionScripting.lua file has not been changed to allow lfs, io and os modules to be used..." ) - end - return self -end - - ---- Registers a score for a player. --- @param #SCORING self --- @param #string PlayerName The name of the player. --- @param #string TargetPlayerName The name of the target player. --- @param #string ScoreType The type of the score. --- @param #string ScoreTimes The amount of scores achieved. --- @param #string ScoreAmount The score given. --- @param #string PlayerUnitName The unit name of the player. --- @param #string PlayerUnitCoalition The coalition of the player unit. --- @param #string PlayerUnitCategory The category of the player unit. --- @param #string PlayerUnitType The type of the player unit. --- @param #string TargetUnitName The name of the target unit. --- @param #string TargetUnitCoalition The coalition of the target unit. --- @param #string TargetUnitCategory The category of the target unit. --- @param #string TargetUnitType The type of the target unit. --- @return #SCORING self -function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes, ScoreAmount, PlayerUnitName, PlayerUnitCoalition, PlayerUnitCategory, PlayerUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - --write statistic information to file - local ScoreTime = self:SecondsToClock( timer.getTime() ) - PlayerName = PlayerName:gsub( '"', '_' ) - - TargetPlayerName = TargetPlayerName or "" - TargetPlayerName = TargetPlayerName:gsub( '"', '_' ) - - if PlayerUnitName and PlayerUnitName ~= '' then - local PlayerUnit = Unit.getByName( PlayerUnitName ) - - if PlayerUnit then - if not PlayerUnitCategory then - --PlayerUnitCategory = SCORINGCategory[PlayerUnit:getCategory()] - PlayerUnitCategory = _SCORINGCategory[PlayerUnit:getDesc().category] - end - - if not PlayerUnitCoalition then - PlayerUnitCoalition = _SCORINGCoalition[PlayerUnit:getCoalition()] - end - - if not PlayerUnitType then - PlayerUnitType = PlayerUnit:getTypeName() - end - else - PlayerUnitName = '' - PlayerUnitCategory = '' - PlayerUnitCoalition = '' - PlayerUnitType = '' - end - else - PlayerUnitName = '' - PlayerUnitCategory = '' - PlayerUnitCoalition = '' - PlayerUnitType = '' - end - - TargetUnitCoalition = TargetUnitCoalition or "" - TargetUnitCategory = TargetUnitCategory or "" - TargetUnitType = TargetUnitType or "" - TargetUnitName = TargetUnitName or "" - - if lfs and io and os then - self.CSVFile:write( - '"' .. self.GameName .. '"' .. ',' .. - '"' .. self.RunTime .. '"' .. ',' .. - '' .. ScoreTime .. '' .. ',' .. - '"' .. PlayerName .. '"' .. ',' .. - '"' .. TargetPlayerName .. '"' .. ',' .. - '"' .. ScoreType .. '"' .. ',' .. - '"' .. PlayerUnitCoalition .. '"' .. ',' .. - '"' .. PlayerUnitCategory .. '"' .. ',' .. - '"' .. PlayerUnitType .. '"' .. ',' .. - '"' .. PlayerUnitName .. '"' .. ',' .. - '"' .. TargetUnitCoalition .. '"' .. ',' .. - '"' .. TargetUnitCategory .. '"' .. ',' .. - '"' .. TargetUnitType .. '"' .. ',' .. - '"' .. TargetUnitName .. '"' .. ',' .. - '' .. ScoreTimes .. '' .. ',' .. - '' .. ScoreAmount - ) - - self.CSVFile:write( "\n" ) - end -end - - -function SCORING:CloseCSV() - if lfs and io and os then - self.CSVFile:close() - end -end - ---- The CLEANUP class keeps an area clean of crashing or colliding airplanes. It also prevents airplanes from firing within this area. --- @module CleanUp --- @author Flightcontrol - - - - - - - ---- The CLEANUP class. --- @type CLEANUP --- @extends Core.Base#BASE -CLEANUP = { - ClassName = "CLEANUP", - ZoneNames = {}, - TimeInterval = 300, - CleanUpList = {}, -} - ---- Creates the main object which is handling the cleaning of the debris within the given Zone Names. --- @param #CLEANUP self --- @param #table ZoneNames Is a table of zone names where the debris should be cleaned. Also a single string can be passed with one zone name. --- @param #number TimeInterval The interval in seconds when the clean activity takes place. The default is 300 seconds, thus every 5 minutes. --- @return #CLEANUP --- @usage --- -- Clean these Zones. --- CleanUpAirports = CLEANUP:New( { 'CLEAN Tbilisi', 'CLEAN Kutaisi' }, 150 ) --- or --- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 ) --- CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 ) -function CLEANUP:New( ZoneNames, TimeInterval ) - - local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP - self:F( { ZoneNames, TimeInterval } ) - - if type( ZoneNames ) == 'table' then - self.ZoneNames = ZoneNames - else - self.ZoneNames = { ZoneNames } - end - if TimeInterval then - self.TimeInterval = TimeInterval - end - - self:HandleEvent( EVENTS.Birth ) - - self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval ) - - return self -end - - ---- Destroys a group from the simulator, but checks first if it is still existing! --- @param #CLEANUP self --- @param Dcs.DCSWrapper.Group#Group GroupObject The object to be destroyed. --- @param #string CleanUpGroupName The groupname... -function CLEANUP:_DestroyGroup( GroupObject, CleanUpGroupName ) - self:F( { GroupObject, CleanUpGroupName } ) - - if GroupObject then -- and GroupObject:isExist() then - trigger.action.deactivateGroup(GroupObject) - self:T( { "GroupObject Destroyed", GroupObject } ) - end -end - ---- Destroys a @{DCSWrapper.Unit#Unit} from the simulator, but checks first if it is still existing! --- @param #CLEANUP self --- @param Dcs.DCSWrapper.Unit#Unit CleanUpUnit The object to be destroyed. --- @param #string CleanUpUnitName The Unit name ... -function CLEANUP:_DestroyUnit( CleanUpUnit, CleanUpUnitName ) - self:F( { CleanUpUnit, CleanUpUnitName } ) - - if CleanUpUnit then - local CleanUpGroup = Unit.getGroup(CleanUpUnit) - -- TODO Client bug in 1.5.3 - if CleanUpGroup and CleanUpGroup:isExist() then - local CleanUpGroupUnits = CleanUpGroup:getUnits() - if #CleanUpGroupUnits == 1 then - local CleanUpGroupName = CleanUpGroup:getName() - --self:CreateEventCrash( timer.getTime(), CleanUpUnit ) - CleanUpGroup:destroy() - self:T( { "Destroyed Group:", CleanUpGroupName } ) - else - CleanUpUnit:destroy() - self:T( { "Destroyed Unit:", CleanUpUnitName } ) - end - self.CleanUpList[CleanUpUnitName] = nil -- Cleaning from the list - CleanUpUnit = nil - end - end -end - --- TODO check Dcs.DCSTypes#Weapon ---- Destroys a missile from the simulator, but checks first if it is still existing! --- @param #CLEANUP self --- @param Dcs.DCSTypes#Weapon MissileObject -function CLEANUP:_DestroyMissile( MissileObject ) - self:F( { MissileObject } ) - - if MissileObject and MissileObject:isExist() then - MissileObject:destroy() - self:T( "MissileObject Destroyed") - end -end - ---- @param #CLEANUP self --- @param Core.Event#EVENTDATA EventData -function CLEANUP:_OnEventBirth( EventData ) - self:F( { EventData } ) - - self.CleanUpList[EventData.IniDCSUnitName] = {} - self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit - self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup - self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName - self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName - - EventData.IniUnit:HandleEvent( EVENTS.EngineShutdown , self._EventAddForCleanUp ) - EventData.IniUnit:HandleEvent( EVENTS.EngineStartup, self._EventAddForCleanUp ) - EventData.IniUnit:HandleEvent( EVENTS.Hit, self._EventAddForCleanUp ) - EventData.IniUnit:HandleEvent( EVENTS.PilotDead, self._EventCrash ) - EventData.IniUnit:HandleEvent( EVENTS.Dead, self._EventCrash ) - EventData.IniUnit:HandleEvent( EVENTS.Crash, self._EventCrash ) - EventData.IniUnit:HandleEvent( EVENTS.Shot, self._EventShot ) - -end - ---- Detects if a crash event occurs. --- Crashed units go into a CleanUpList for removal. --- @param #CLEANUP self --- @param Dcs.DCSTypes#Event event -function CLEANUP:_EventCrash( Event ) - self:F( { Event } ) - - --TODO: This stuff is not working due to a DCS bug. Burning units cannot be destroyed. - -- self:T("before getGroup") - -- local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired - -- self:T("after getGroup") - -- _grp:destroy() - -- self:T("after deactivateGroup") - -- event.initiator:destroy() - - self.CleanUpList[Event.IniDCSUnitName] = {} - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName - -end - ---- Detects if a unit shoots a missile. --- If this occurs within one of the zones, then the weapon used must be destroyed. --- @param #CLEANUP self --- @param Dcs.DCSTypes#Event event -function CLEANUP:_EventShot( Event ) - self:F( { Event } ) - - -- Test if the missile was fired within one of the CLEANUP.ZoneNames. - local CurrentLandingZoneID = 0 - CurrentLandingZoneID = routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) - if ( CurrentLandingZoneID ) then - -- Okay, the missile was fired within the CLEANUP.ZoneNames, destroy the fired weapon. - --_SEADmissile:destroy() - SCHEDULER:New( self, CLEANUP._DestroyMissile, { Event.Weapon }, 0.1 ) - end -end - - ---- Detects if the Unit has an S_EVENT_HIT within the given ZoneNames. If this is the case, destroy the unit. --- @param #CLEANUP self --- @param Dcs.DCSTypes#Event event -function CLEANUP:_EventHitCleanUp( Event ) - self:F( { Event } ) - - if Event.IniDCSUnit then - if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then - self:T( { "Life: ", Event.IniDCSUnitName, ' = ', Event.IniDCSUnit:getLife(), "/", Event.IniDCSUnit:getLife0() } ) - if Event.IniDCSUnit:getLife() < Event.IniDCSUnit:getLife0() then - self:T( "CleanUp: Destroy: " .. Event.IniDCSUnitName ) - SCHEDULER:New( self, CLEANUP._DestroyUnit, { Event.IniDCSUnit }, 0.1 ) - end - end - end - - if Event.TgtDCSUnit then - if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then - self:T( { "Life: ", Event.TgtDCSUnitName, ' = ', Event.TgtDCSUnit:getLife(), "/", Event.TgtDCSUnit:getLife0() } ) - if Event.TgtDCSUnit:getLife() < Event.TgtDCSUnit:getLife0() then - self:T( "CleanUp: Destroy: " .. Event.TgtDCSUnitName ) - SCHEDULER:New( self, CLEANUP._DestroyUnit, { Event.TgtDCSUnit }, 0.1 ) - end - end - end -end - ---- Add the @{DCSWrapper.Unit#Unit} to the CleanUpList for CleanUp. -function CLEANUP:_AddForCleanUp( CleanUpUnit, CleanUpUnitName ) - self:F( { CleanUpUnit, CleanUpUnitName } ) - - self.CleanUpList[CleanUpUnitName] = {} - self.CleanUpList[CleanUpUnitName].CleanUpUnit = CleanUpUnit - self.CleanUpList[CleanUpUnitName].CleanUpUnitName = CleanUpUnitName - self.CleanUpList[CleanUpUnitName].CleanUpGroup = Unit.getGroup(CleanUpUnit) - self.CleanUpList[CleanUpUnitName].CleanUpGroupName = Unit.getGroup(CleanUpUnit):getName() - self.CleanUpList[CleanUpUnitName].CleanUpTime = timer.getTime() - self.CleanUpList[CleanUpUnitName].CleanUpMoved = false - - self:T( { "CleanUp: Add to CleanUpList: ", Unit.getGroup(CleanUpUnit):getName(), CleanUpUnitName } ) - -end - ---- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given ZoneNames. If this is the case, add the Group to the CLEANUP List. --- @param #CLEANUP self --- @param Dcs.DCSTypes#Event event -function CLEANUP:_EventAddForCleanUp( Event ) - - if Event.IniDCSUnit then - if self.CleanUpList[Event.IniDCSUnitName] == nil then - if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then - self:_AddForCleanUp( Event.IniDCSUnit, Event.IniDCSUnitName ) - end - end - end - - if Event.TgtDCSUnit then - if self.CleanUpList[Event.TgtDCSUnitName] == nil then - if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then - self:_AddForCleanUp( Event.TgtDCSUnit, Event.TgtDCSUnitName ) - end - end - end - -end - -local CleanUpSurfaceTypeText = { - "LAND", - "SHALLOW_WATER", - "WATER", - "ROAD", - "RUNWAY" - } - ---- At the defined time interval, CleanUp the Groups within the CleanUpList. --- @param #CLEANUP self -function CLEANUP:_CleanUpScheduler() - self:F( { "CleanUp Scheduler" } ) - - local CleanUpCount = 0 - for CleanUpUnitName, UnitData in pairs( self.CleanUpList ) do - CleanUpCount = CleanUpCount + 1 - - self:T( { CleanUpUnitName, UnitData } ) - local CleanUpUnit = Unit.getByName(UnitData.CleanUpUnitName) - local CleanUpGroupName = UnitData.CleanUpGroupName - local CleanUpUnitName = UnitData.CleanUpUnitName - if CleanUpUnit then - self:T( { "CleanUp Scheduler", "Checking:", CleanUpUnitName } ) - if _DATABASE:GetStatusGroup( CleanUpGroupName ) ~= "ReSpawn" then - local CleanUpUnitVec3 = CleanUpUnit:getPoint() - --self:T( CleanUpUnitVec3 ) - local CleanUpUnitVec2 = {} - CleanUpUnitVec2.x = CleanUpUnitVec3.x - CleanUpUnitVec2.y = CleanUpUnitVec3.z - --self:T( CleanUpUnitVec2 ) - local CleanUpSurfaceType = land.getSurfaceType(CleanUpUnitVec2) - --self:T( CleanUpSurfaceType ) - - if CleanUpUnit and CleanUpUnit:getLife() <= CleanUpUnit:getLife0() * 0.95 then - if CleanUpSurfaceType == land.SurfaceType.RUNWAY then - if CleanUpUnit:inAir() then - local CleanUpLandHeight = land.getHeight(CleanUpUnitVec2) - local CleanUpUnitHeight = CleanUpUnitVec3.y - CleanUpLandHeight - self:T( { "CleanUp Scheduler", "Height = " .. CleanUpUnitHeight } ) - if CleanUpUnitHeight < 30 then - self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because below safe height and damaged." } ) - self:_DestroyUnit(CleanUpUnit, CleanUpUnitName) - end - else - self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because on runway and damaged." } ) - self:_DestroyUnit(CleanUpUnit, CleanUpUnitName) - end - end - end - -- Clean Units which are waiting for a very long time in the CleanUpZone. - if CleanUpUnit then - local CleanUpUnitVelocity = CleanUpUnit:getVelocity() - local CleanUpUnitVelocityTotal = math.abs(CleanUpUnitVelocity.x) + math.abs(CleanUpUnitVelocity.y) + math.abs(CleanUpUnitVelocity.z) - if CleanUpUnitVelocityTotal < 1 then - if UnitData.CleanUpMoved then - if UnitData.CleanUpTime + 180 <= timer.getTime() then - self:T( { "CleanUp Scheduler", "Destroy due to not moving anymore " .. CleanUpUnitName } ) - self:_DestroyUnit(CleanUpUnit, CleanUpUnitName) - end - end - else - UnitData.CleanUpTime = timer.getTime() - UnitData.CleanUpMoved = true - end - end - - else - -- Do nothing ... - self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE - end - else - self:T( "CleanUp: Group " .. CleanUpUnitName .. " cannot be found in DCS RTE, removing ..." ) - self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE - end - end - self:T(CleanUpCount) - - return true -end - ---- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** -- --- **Spawn groups of units dynamically in your missions.** --- --- ![Banner Image](..\Presentations\SPAWN\SPAWN.JPG) --- --- === --- --- # 1) @{#SPAWN} class, extends @{Base#BASE} --- --- The @{#SPAWN} class allows to spawn dynamically new groups, based on pre-defined initialization settings, modifying the behaviour when groups are spawned. --- For each group to be spawned, within the mission editor, a group has to be created with the "late activation flag" set. We call this group the *"Spawn Template"* of the SPAWN object. --- A reference to this Spawn Template needs to be provided when constructing the SPAWN object, by indicating the name of the group within the mission editor in the constructor methods. --- --- Within the SPAWN object, there is an internal index that keeps track of which group from the internal group list was spawned. --- When new groups get spawned by using the SPAWN methods (see below), it will be validated whether the Limits (@{#SPAWN.Limit}) of the SPAWN object are not reached. --- When all is valid, a new group will be created by the spawning methods, and the internal index will be increased with 1. --- --- Regarding the name of new spawned groups, a _SpawnPrefix_ will be assigned for each new group created. --- If you want to have the Spawn Template name to be used as the _SpawnPrefix_ name, use the @{#SPAWN.New} constructor. --- However, when the @{#SPAWN.NewWithAlias} constructor was used, the Alias name will define the _SpawnPrefix_ name. --- Groups will follow the following naming structure when spawned at run-time: --- --- 1. Spawned groups will have the name _SpawnPrefix_#ggg, where ggg is a counter from 0 to 999. --- 2. Spawned units will have the name _SpawnPrefix_#ggg-uu, where uu is a counter from 0 to 99 for each new spawned unit belonging to the group. --- --- Some additional notes that need to be remembered: --- --- * Templates are actually groups defined within the mission editor, with the flag "Late Activation" set. As such, these groups are never used within the mission, but are used by the @{#SPAWN} module. --- * It is important to defined BEFORE you spawn new groups, a proper initialization of the SPAWN instance is done with the options you want to use. --- * When designing a mission, NEVER name groups using a "#" within the name of the group Spawn Template(s), or the SPAWN module logic won't work anymore. --- --- ## 1.1) SPAWN construction methods --- --- Create a new SPAWN object with the @{#SPAWN.New}() or the @{#SPAWN.NewWithAlias}() methods: --- --- * @{#SPAWN.New}(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition). --- * @{#SPAWN.NewWithAlias}(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition), and gives each spawned @{Group} an different name. --- --- It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned. --- The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons. --- So in principle, the group list will contain all parameters and configurations after initialization, and when groups get actually spawned, this spawning can be done quickly and efficient. --- --- ## 1.2) SPAWN initialization methods --- --- A spawn object will behave differently based on the usage of **initialization** methods, which all start with the **Init** prefix: --- --- * @{#SPAWN.InitKeepUnitNames}(): Keeps the unit names as defined within the mission editor, but note that anything after a # mark is ignored, and any spaces before and after the resulting name are removed. IMPORTANT! This method MUST be the first used after :New !!! --- * @{#SPAWN.InitLimit}(): Limits the amount of groups that can be alive at the same time and that can be dynamically spawned. --- * @{#SPAWN.InitRandomizeRoute}(): Randomize the routes of spawned groups, and for air groups also optionally the height. --- * @{#SPAWN.InitRandomizeTemplate}(): Randomize the group templates so that when a new group is spawned, a random group template is selected from one of the templates defined. --- * @{#SPAWN.InitUnControlled}(): Spawn plane groups uncontrolled. --- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a batallion in an array. --- * @{#SPAWN.InitRepeat}(): Re-spawn groups when they land at the home base. Similar methods are @{#SPAWN.InitRepeatOnLanding} and @{#SPAWN.InitRepeatOnEngineShutDown}. --- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens. --- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Unit}s in the @{Group} that is spawned within a **radius band**, given an Outer and Inner radius. --- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Zone}s that are declared using this function. Each zone can be given a probability factor. --- * @{#SPAWN.InitAIOn}(): Turns the AI On when spawning the new @{Group} object. --- * @{#SPAWN.InitAIOff}(): Turns the AI Off when spawning the new @{Group} object. --- * @{#SPAWN.InitAIOnOff}(): Turns the AI On or Off when spawning the new @{Group} object. --- --- ## 1.3) SPAWN spawning methods --- --- Groups can be spawned at different times and methods: --- --- * @{#SPAWN.Spawn}(): Spawn one new group based on the last spawned index. --- * @{#SPAWN.ReSpawn}(): Re-spawn a group based on a given index. --- * @{#SPAWN.SpawnScheduled}(): Spawn groups at scheduled but randomized intervals. You can use @{#SPAWN.SpawnScheduleStart}() and @{#SPAWN.SpawnScheduleStop}() to start and stop the schedule respectively. --- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air). --- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ). --- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}. --- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}. --- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}. --- --- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object. --- You can use the @{GROUP} object to do further actions with the DCSGroup. --- --- ## 1.4) Retrieve alive GROUPs spawned by the SPAWN object --- --- The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution. --- Every time a SPAWN object spawns a new GROUP object, a reference to the GROUP object is added to an internal table of GROUPS. --- SPAWN provides methods to iterate through that internal GROUP object reference table: --- --- * @{#SPAWN.GetFirstAliveGroup}(): Will find the first alive GROUP it has spawned, and return the alive GROUP object and the first Index where the first alive GROUP object has been found. --- * @{#SPAWN.GetNextAliveGroup}(): Will find the next alive GROUP object from a given Index, and return a reference to the alive GROUP object and the next Index where the alive GROUP has been found. --- * @{#SPAWN.GetLastAliveGroup}(): Will find the last alive GROUP object, and will return a reference to the last live GROUP object and the last Index where the last alive GROUP object has been found. --- --- You can use the methods @{#SPAWN.GetFirstAliveGroup}() and sequently @{#SPAWN.GetNextAliveGroup}() to iterate through the alive GROUPS within the SPAWN object, and to actions... See the respective methods for an example. --- The method @{#SPAWN.GetGroupFromIndex}() will return the GROUP object reference from the given Index, dead or alive... --- --- ## 1.5) SPAWN object cleaning --- --- Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive. --- In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't, --- and it may occur that no new groups are or can be spawned as limits are reached. --- To prevent this, a @{#SPAWN.InitCleanUp}() initialization method has been defined that will silently monitor the status of each spawned group. --- Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time. --- There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"... --- In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically. --- This models AI that has succesfully returned to their airbase, to restart their combat activities. --- Check the @{#SPAWN.InitCleanUp}() for further info. --- --- ## 1.6) Catch the @{Group} spawn event in a callback function! --- --- When using the SpawnScheduled method, new @{Group}s are created following the schedule timing parameters. --- When a new @{Group} is spawned, you maybe want to execute actions with that group spawned at the spawn event. --- To SPAWN class supports this functionality through the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method, which takes a function as a parameter that you can define locally. --- Whenever a new @{Group} is spawned, the given function is called, and the @{Group} that was just spawned, is given as a parameter. --- As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned @{Group} object. --- A coding example is provided at the description of the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-14: SPAWN:**InitKeepUnitNames()** added. --- 2017-03-14: SPAWN:**InitRandomizePosition( RandomizePosition, OuterRadious, InnerRadius )** added. --- --- 2017-02-04: SPAWN:InitUnControlled( **UnControlled** ) replaces SPAWN:InitUnControlled(). --- --- 2017-01-24: SPAWN:**InitAIOnOff( AIOnOff )** added. --- --- 2017-01-24: SPAWN:**InitAIOn()** added. --- --- 2017-01-24: SPAWN:**InitAIOff()** added. --- --- 2016-08-15: SPAWN:**InitCleanUp**( SpawnCleanUpInterval ) replaces SPAWN:_CleanUp_( SpawnCleanUpInterval ). --- --- 2016-08-15: SPAWN:**InitRandomizeZones( SpawnZones )** added. --- --- 2016-08-14: SPAWN:**OnSpawnGroup**( SpawnCallBackFunction, ... ) replaces SPAWN:_SpawnFunction_( SpawnCallBackFunction, ... ). --- --- 2016-08-14: SPAWN.SpawnInZone( Zone, __RandomizeGroup__, SpawnIndex ) replaces SpawnInZone( Zone, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ). --- --- 2016-08-14: SPAWN.SpawnFromVec3( Vec3, SpawnIndex ) replaces SpawnFromVec3( Vec3, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ): --- --- 2016-08-14: SPAWN.SpawnFromVec2( Vec2, SpawnIndex ) replaces SpawnFromVec2( Vec2, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ): --- --- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromUnit( SpawnUnit, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ): --- --- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromStatic( SpawnStatic, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ): --- --- 2016-08-14: SPAWN.**InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )** added: --- --- 2016-08-14: SPAWN.**Init**Limit( SpawnMaxUnitsAlive, SpawnMaxGroups ) replaces SPAWN._Limit_( SpawnMaxUnitsAlive, SpawnMaxGroups ): --- --- 2016-08-14: SPAWN.**Init**Array( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) replaces SPAWN._Array_( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ). --- --- 2016-08-14: SPAWN.**Init**RandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) replaces SPAWN._RandomizeRoute_( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ). --- --- 2016-08-14: SPAWN.**Init**RandomizeTemplate( SpawnTemplatePrefixTable ) replaces SPAWN._RandomizeTemplate_( SpawnTemplatePrefixTable ). --- --- 2016-08-14: SPAWN.**Init**UnControlled() replaces SPAWN._UnControlled_(). --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **Aaron**: Posed the idea for Group position randomization at SpawnInZone and make the Unit randomization separate from the Group randomization. --- * [**Entropy**](https://forums.eagle.ru/member.php?u=111471), **Afinegan**: Came up with the requirement for AIOnOff(). --- --- ### Authors: --- --- * **FlightControl**: Design & Programming --- --- @module Spawn - - - ---- SPAWN Class --- @type SPAWN --- @extends Core.Base#BASE --- @field ClassName --- @field #string SpawnTemplatePrefix --- @field #string SpawnAliasPrefix --- @field #number AliveUnits --- @field #number MaxAliveUnits --- @field #number SpawnIndex --- @field #number MaxAliveGroups --- @field #SPAWN.SpawnZoneTable SpawnZoneTable -SPAWN = { - ClassName = "SPAWN", - SpawnTemplatePrefix = nil, - SpawnAliasPrefix = nil, -} - - ---- @type SPAWN.SpawnZoneTable --- @list SpawnZone - - ---- Creates the main object to spawn a @{Group} defined in the DCS ME. --- @param #SPAWN self --- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix. --- @return #SPAWN --- @usage --- -- NATO helicopters engaging in the battle field. --- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ) --- @usage local Plane = SPAWN:New( "Plane" ) -- Creates a new local variable that can initiate new planes with the name "Plane#ddd" using the template "Plane" as defined within the ME. -function SPAWN:New( SpawnTemplatePrefix ) - local self = BASE:Inherit( self, BASE:New() ) -- #SPAWN - self:F( { SpawnTemplatePrefix } ) - - local TemplateGroup = Group.getByName( SpawnTemplatePrefix ) - if TemplateGroup then - self.SpawnTemplatePrefix = SpawnTemplatePrefix - self.SpawnIndex = 0 - self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. - self.AliveUnits = 0 -- Contains the counter how many units are currently alive - self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. - self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! - self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. - self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. - self.SpawnInitLimit = false -- By default, no InitLimit - self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. - self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. - self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. - self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. - self.AIOnOff = true -- The AI is on by default when spawning a group. - self.SpawnUnControlled = false - self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. - - self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. - else - error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) - end - - self:SetEventPriority( 5 ) - - return self -end - ---- Creates a new SPAWN instance to create new groups based on the defined template and using a new alias for each new group. --- @param #SPAWN self --- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. --- @param #string SpawnAliasPrefix is the name that will be given to the Group at runtime. --- @return #SPAWN --- @usage --- -- NATO helicopters engaging in the battle field. --- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' ) --- @usage local PlaneWithAlias = SPAWN:NewWithAlias( "Plane", "Bomber" ) -- Creates a new local variable that can instantiate new planes with the name "Bomber#ddd" using the template "Plane" as defined within the ME. -function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( { SpawnTemplatePrefix, SpawnAliasPrefix } ) - - local TemplateGroup = Group.getByName( SpawnTemplatePrefix ) - if TemplateGroup then - self.SpawnTemplatePrefix = SpawnTemplatePrefix - self.SpawnAliasPrefix = SpawnAliasPrefix - self.SpawnIndex = 0 - self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. - self.AliveUnits = 0 -- Contains the counter how many units are currently alive - self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. - self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! - self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. - self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. - self.SpawnInitLimit = false -- By default, no InitLimit - self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. - self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. - self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. - self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. - self.AIOnOff = true -- The AI is on by default when spawning a group. - self.SpawnUnControlled = false - self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. - - self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. - else - error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) - end - - self:SetEventPriority( 5 ) - - return self -end - - ---- Limits the Maximum amount of Units that can be alive at the same time, and the maximum amount of groups that can be spawned. --- Note that this method is exceptionally important to balance the performance of the mission. Depending on the machine etc, a mission can only process a maximum amount of units. --- If the time interval must be short, but there should not be more Units or Groups alive than a maximum amount of units, then this method should be used... --- When a @{#SPAWN.New} is executed and the limit of the amount of units alive is reached, then no new spawn will happen of the group, until some of these units of the spawn object will be destroyed. --- @param #SPAWN self --- @param #number SpawnMaxUnitsAlive The maximum amount of units that can be alive at runtime. --- @param #number SpawnMaxGroups The maximum amount of groups that can be spawned. When the limit is reached, then no more actual spawns will happen of the group. --- This parameter is useful to define a maximum amount of airplanes, ground troops, helicopters, ships etc within a supply area. --- This parameter accepts the value 0, which defines that there are no maximum group limits, but there are limits on the maximum of units that can be alive at the same time. --- @return #SPAWN self --- @usage --- -- NATO helicopters engaging in the battle field. --- -- This helicopter group consists of one Unit. So, this group will SPAWN maximum 2 groups simultaneously within the DCSRTE. --- -- There will be maximum 24 groups spawned during the whole mission lifetime. --- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitLimit( 2, 24 ) -function SPAWN:InitLimit( SpawnMaxUnitsAlive, SpawnMaxGroups ) - self:F( { self.SpawnTemplatePrefix, SpawnMaxUnitsAlive, SpawnMaxGroups } ) - - self.SpawnInitLimit = true - self.SpawnMaxUnitsAlive = SpawnMaxUnitsAlive -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. - self.SpawnMaxGroups = SpawnMaxGroups -- The maximum amount of groups that can be spawned. - - for SpawnGroupID = 1, self.SpawnMaxGroups do - self:_InitializeSpawnGroups( SpawnGroupID ) - end - - return self -end - ---- Keeps the unit names as defined within the mission editor, --- but note that anything after a # mark is ignored, --- and any spaces before and after the resulting name are removed. --- IMPORTANT! This method MUST be the first used after :New !!! --- @param #SPAWN self --- @return #SPAWN self -function SPAWN:InitKeepUnitNames() - self:F( ) - - self.SpawnInitKeepUnitNames = true - - return self -end - - ---- Randomizes the defined route of the SpawnTemplatePrefix group in the ME. This is very useful to define extra variation of the behaviour of groups. --- @param #SPAWN self --- @param #number SpawnStartPoint is the waypoint where the randomization begins. --- Note that the StartPoint = 0 equaling the point where the group is spawned. --- @param #number SpawnEndPoint is the waypoint where the randomization ends counting backwards. --- This parameter is useful to avoid randomization to end at a waypoint earlier than the last waypoint on the route. --- @param #number SpawnRadius is the radius in meters in which the randomization of the new waypoints, with the original waypoint of the original template located in the middle ... --- @param #number SpawnHeight (optional) Specifies the **additional** height in meters that can be added to the base height specified at each waypoint in the ME. --- @return #SPAWN --- @usage --- -- NATO helicopters engaging in the battle field. --- -- The KA-50 has waypoints Start point ( =0 or SP ), 1, 2, 3, 4, End point (= 5 or DP). --- -- Waypoints 2 and 3 will only be randomized. The others will remain on their original position with each new spawn of the helicopter. --- -- The randomization of waypoint 2 and 3 will take place within a radius of 2000 meters. --- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeRoute( 2, 2, 2000 ) -function SPAWN:InitRandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) - self:F( { self.SpawnTemplatePrefix, SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight } ) - - self.SpawnRandomizeRoute = true - self.SpawnRandomizeRouteStartPoint = SpawnStartPoint - self.SpawnRandomizeRouteEndPoint = SpawnEndPoint - self.SpawnRandomizeRouteRadius = SpawnRadius - self.SpawnRandomizeRouteHeight = SpawnHeight - - for GroupID = 1, self.SpawnMaxGroups do - self:_RandomizeRoute( GroupID ) - end - - return self -end - ---- Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens. --- @param #SPAWN self --- @param #boolean RandomizePosition If true, SPAWN will perform the randomization of the @{Group}s position between a given outer and inner radius. --- @param Dcs.DCSTypes#Distance OuterRadius (optional) The outer radius in meters where the new group will be spawned. --- @param Dcs.DCSTypes#Distance InnerRadius (optional) The inner radius in meters where the new group will NOT be spawned. --- @return #SPAWN -function SPAWN:InitRandomizePosition( RandomizePosition, OuterRadius, InnerRadius ) - self:F( { self.SpawnTemplatePrefix, RandomizePosition, OuterRadius, InnerRadius } ) - - self.SpawnRandomizePosition = RandomizePosition or false - self.SpawnRandomizePositionOuterRadius = OuterRadius or 0 - self.SpawnRandomizePositionInnerRadius = InnerRadius or 0 - - for GroupID = 1, self.SpawnMaxGroups do - self:_RandomizeRoute( GroupID ) - end - - return self -end - - ---- Randomizes the UNITs that are spawned within a radius band given an Outer and Inner radius. --- @param #SPAWN self --- @param #boolean RandomizeUnits If true, SPAWN will perform the randomization of the @{UNIT}s position within the group between a given outer and inner radius. --- @param Dcs.DCSTypes#Distance OuterRadius (optional) The outer radius in meters where the new group will be spawned. --- @param Dcs.DCSTypes#Distance InnerRadius (optional) The inner radius in meters where the new group will NOT be spawned. --- @return #SPAWN --- @usage --- -- NATO helicopters engaging in the battle field. --- -- The KA-50 has waypoints Start point ( =0 or SP ), 1, 2, 3, 4, End point (= 5 or DP). --- -- Waypoints 2 and 3 will only be randomized. The others will remain on their original position with each new spawn of the helicopter. --- -- The randomization of waypoint 2 and 3 will take place within a radius of 2000 meters. --- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeRoute( 2, 2, 2000 ) -function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius ) - self:F( { self.SpawnTemplatePrefix, RandomizeUnits, OuterRadius, InnerRadius } ) - - self.SpawnRandomizeUnits = RandomizeUnits or false - self.SpawnOuterRadius = OuterRadius or 0 - self.SpawnInnerRadius = InnerRadius or 0 - - for GroupID = 1, self.SpawnMaxGroups do - self:_RandomizeRoute( GroupID ) - end - - return self -end - ---- This method is rather complicated to understand. But I'll try to explain. --- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor, --- but they will all follow the same Template route and have the same prefix name. --- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group. --- @param #SPAWN self --- @param #string SpawnTemplatePrefixTable A table with the names of the groups defined within the mission editor, from which one will be choosen when a new group will be spawned. --- @return #SPAWN --- @usage --- -- NATO Tank Platoons invading Gori. --- -- Choose between 13 different 'US Tank Platoon' configurations for each new SPAWN the Group to be spawned for the --- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SpawnTemplatePrefixes. --- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and --- -- with a limit set of maximum 12 Units alive simulteneously and 150 Groups to be spawned during the whole mission. --- Spawn_US_Platoon = { 'US Tank Platoon 1', 'US Tank Platoon 2', 'US Tank Platoon 3', 'US Tank Platoon 4', 'US Tank Platoon 5', --- 'US Tank Platoon 6', 'US Tank Platoon 7', 'US Tank Platoon 8', 'US Tank Platoon 9', 'US Tank Platoon 10', --- 'US Tank Platoon 11', 'US Tank Platoon 12', 'US Tank Platoon 13' } --- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 ) --- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 ) --- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 ) -function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable ) - self:F( { self.SpawnTemplatePrefix, SpawnTemplatePrefixTable } ) - - self.SpawnTemplatePrefixTable = SpawnTemplatePrefixTable - self.SpawnRandomizeTemplate = true - - for SpawnGroupID = 1, self.SpawnMaxGroups do - self:_RandomizeTemplate( SpawnGroupID ) - end - - return self -end - ---TODO: Add example. ---- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types. --- @param #SPAWN self --- @param #table SpawnZoneTable A table with @{Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Zone}s objects. --- @return #SPAWN --- @usage --- -- NATO Tank Platoons invading Gori. --- -- Choose between 3 different zones for each new SPAWN the Group to be executed, regardless of the zone type. -function SPAWN:InitRandomizeZones( SpawnZoneTable ) - self:F( { self.SpawnTemplatePrefix, SpawnZoneTable } ) - - self.SpawnZoneTable = SpawnZoneTable - self.SpawnRandomizeZones = true - - for SpawnGroupID = 1, self.SpawnMaxGroups do - self:_RandomizeZones( SpawnGroupID ) - end - - return self -end - - - - - ---- For planes and helicopters, when these groups go home and land on their home airbases and farps, they normally would taxi to the parking spot, shut-down their engines and wait forever until the Group is removed by the runtime environment. --- This method is used to re-spawn automatically (so no extra call is needed anymore) the same group after it has landed. --- This will enable a spawned group to be re-spawned after it lands, until it is destroyed... --- Note: When the group is respawned, it will re-spawn from the original airbase where it took off. --- So ensure that the routes for groups that respawn, always return to the original airbase, or players may get confused ... --- @param #SPAWN self --- @return #SPAWN self --- @usage --- -- RU Su-34 - AI Ship Attack --- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically. --- SpawnRU_SU34 = SPAWN:New( 'TF1 RU Su-34 Krymsk@AI - Attack Ships' ):Schedule( 2, 3, 1800, 0.4 ):SpawnUncontrolled():InitRandomizeRoute( 1, 1, 3000 ):RepeatOnEngineShutDown() -function SPAWN:InitRepeat() - self:F( { self.SpawnTemplatePrefix, self.SpawnIndex } ) - - self.Repeat = true - self.RepeatOnEngineShutDown = false - self.RepeatOnLanding = true - - return self -end - ---- Respawn group after landing. --- @param #SPAWN self --- @return #SPAWN self -function SPAWN:InitRepeatOnLanding() - self:F( { self.SpawnTemplatePrefix } ) - - self:InitRepeat() - self.RepeatOnEngineShutDown = false - self.RepeatOnLanding = true - - return self -end - - ---- Respawn after landing when its engines have shut down. --- @param #SPAWN self --- @return #SPAWN self -function SPAWN:InitRepeatOnEngineShutDown() - self:F( { self.SpawnTemplatePrefix } ) - - self:InitRepeat() - self.RepeatOnEngineShutDown = true - self.RepeatOnLanding = false - - return self -end - - ---- CleanUp groups when they are still alive, but inactive. --- When groups are still alive and have become inactive due to damage and are unable to contribute anything, then this group will be removed at defined intervals in seconds. --- @param #SPAWN self --- @param #string SpawnCleanUpInterval The interval to check for inactive groups within seconds. --- @return #SPAWN self --- @usage Spawn_Helicopter:CleanUp( 20 ) -- CleanUp the spawning of the helicopters every 20 seconds when they become inactive. -function SPAWN:InitCleanUp( SpawnCleanUpInterval ) - self:F( { self.SpawnTemplatePrefix, SpawnCleanUpInterval } ) - - self.SpawnCleanUpInterval = SpawnCleanUpInterval - self.SpawnCleanUpTimeStamps = {} - - local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup() - self:T( { "CleanUp Scheduler:", SpawnGroup } ) - - --self.CleanUpFunction = routines.scheduleFunction( self._SpawnCleanUpScheduler, { self }, timer.getTime() + 1, SpawnCleanUpInterval ) - self.CleanUpScheduler = SCHEDULER:New( self, self._SpawnCleanUpScheduler, {}, 1, SpawnCleanUpInterval, 0.2 ) - return self -end - - - ---- Makes the groups visible before start (like a batallion). --- The method will take the position of the group as the first position in the array. --- @param #SPAWN self --- @param #number SpawnAngle The angle in degrees how the groups and each unit of the group will be positioned. --- @param #number SpawnWidth The amount of Groups that will be positioned on the X axis. --- @param #number SpawnDeltaX The space between each Group on the X-axis. --- @param #number SpawnDeltaY The space between each Group on the Y-axis. --- @return #SPAWN self --- @usage --- -- Define an array of Groups. --- Spawn_BE_Ground = SPAWN:New( 'BE Ground' ):InitLimit( 2, 24 ):InitArray( 90, "Diamond", 10, 100, 50 ) -function SPAWN:InitArray( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) - self:F( { self.SpawnTemplatePrefix, SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY } ) - - self.SpawnVisible = true -- When the first Spawn executes, all the Groups need to be made visible before start. - - local SpawnX = 0 - local SpawnY = 0 - local SpawnXIndex = 0 - local SpawnYIndex = 0 - - for SpawnGroupID = 1, self.SpawnMaxGroups do - self:T( { SpawnX, SpawnY, SpawnXIndex, SpawnYIndex } ) - - self.SpawnGroups[SpawnGroupID].Visible = true - self.SpawnGroups[SpawnGroupID].Spawned = false - - SpawnXIndex = SpawnXIndex + 1 - if SpawnWidth and SpawnWidth ~= 0 then - if SpawnXIndex >= SpawnWidth then - SpawnXIndex = 0 - SpawnYIndex = SpawnYIndex + 1 - end - end - - local SpawnRootX = self.SpawnGroups[SpawnGroupID].SpawnTemplate.x - local SpawnRootY = self.SpawnGroups[SpawnGroupID].SpawnTemplate.y - - self:_TranslateRotate( SpawnGroupID, SpawnRootX, SpawnRootY, SpawnX, SpawnY, SpawnAngle ) - - self.SpawnGroups[SpawnGroupID].SpawnTemplate.lateActivation = true - self.SpawnGroups[SpawnGroupID].SpawnTemplate.visible = true - - self.SpawnGroups[SpawnGroupID].Visible = true - - self:HandleEvent( EVENTS.Birth, self._OnBirth ) - self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) - if self.Repeat then - self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) - self:HandleEvent( EVENTS.Land, self._OnLand ) - end - if self.RepeatOnEngineShutDown then - self:HandleEvent( EVENTS.EngineShutdown, self._OnEngineShutDown ) - end - - self.SpawnGroups[SpawnGroupID].Group = _DATABASE:Spawn( self.SpawnGroups[SpawnGroupID].SpawnTemplate ) - - SpawnX = SpawnXIndex * SpawnDeltaX - SpawnY = SpawnYIndex * SpawnDeltaY - end - - return self -end - -do -- AI methods - --- Turns the AI On or Off for the @{Group} when spawning. - -- @param #SPAWN self - -- @param #boolean AIOnOff A value of true sets the AI On, a value of false sets the AI Off. - -- @return #SPAWN The SPAWN object - function SPAWN:InitAIOnOff( AIOnOff ) - - self.AIOnOff = AIOnOff - return self - end - - --- Turns the AI On for the @{Group} when spawning. - -- @param #SPAWN self - -- @return #SPAWN The SPAWN object - function SPAWN:InitAIOn() - - return self:InitAIOnOff( true ) - end - - --- Turns the AI Off for the @{Group} when spawning. - -- @param #SPAWN self - -- @return #SPAWN The SPAWN object - function SPAWN:InitAIOff() - - return self:InitAIOnOff( false ) - end - -end -- AI methods - ---- Will spawn a group based on the internal index. --- Note: Uses @{DATABASE} module defined in MOOSE. --- @param #SPAWN self --- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions. -function SPAWN:Spawn() - self:F( { self.SpawnTemplatePrefix, self.SpawnIndex, self.AliveUnits } ) - - return self:SpawnWithIndex( self.SpawnIndex + 1 ) -end - ---- Will re-spawn a group based on a given index. --- Note: Uses @{DATABASE} module defined in MOOSE. --- @param #SPAWN self --- @param #string SpawnIndex The index of the group to be spawned. --- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions. -function SPAWN:ReSpawn( SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, SpawnIndex } ) - - if not SpawnIndex then - SpawnIndex = 1 - end - --- TODO: This logic makes DCS crash and i don't know why (yet). - local SpawnGroup = self:GetGroupFromIndex( SpawnIndex ) - local WayPoints = SpawnGroup and SpawnGroup.WayPoints or nil - if SpawnGroup then - local SpawnDCSGroup = SpawnGroup:GetDCSObject() - if SpawnDCSGroup then - SpawnGroup:Destroy() - end - end - - local SpawnGroup = self:SpawnWithIndex( SpawnIndex ) - if SpawnGroup and WayPoints then - -- If there were WayPoints set, then Re-Execute those WayPoints! - SpawnGroup:WayPointInitialize( WayPoints ) - SpawnGroup:WayPointExecute( 1, 5 ) - end - - if SpawnGroup.ReSpawnFunction then - SpawnGroup:ReSpawnFunction() - end - - return SpawnGroup -end - ---- Will spawn a group with a specified index number. --- Uses @{DATABASE} global object defined in MOOSE. --- @param #SPAWN self --- @param #string SpawnIndex The index of the group to be spawned. --- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions. -function SPAWN:SpawnWithIndex( SpawnIndex ) - self:F2( { SpawnTemplatePrefix = self.SpawnTemplatePrefix, SpawnIndex = SpawnIndex, AliveUnits = self.AliveUnits, SpawnMaxGroups = self.SpawnMaxGroups } ) - - if self:_GetSpawnIndex( SpawnIndex ) then - - if self.SpawnGroups[self.SpawnIndex].Visible then - self.SpawnGroups[self.SpawnIndex].Group:Activate() - else - - local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate - self:T( SpawnTemplate.name ) - - if SpawnTemplate then - - local PointVec3 = POINT_VEC3:New( SpawnTemplate.route.points[1].x, SpawnTemplate.route.points[1].alt, SpawnTemplate.route.points[1].y ) - self:T( { "Current point of ", self.SpawnTemplatePrefix, PointVec3 } ) - - -- If RandomizePosition, then Randomize the formation in the zone band, keeping the template. - if self.SpawnRandomizePosition then - local RandomVec2 = PointVec3:GetRandomVec2InRadius( self.SpawnRandomizePositionOuterRadius, self.SpawnRandomizePositionInnerRadius ) - local CurrentX = SpawnTemplate.units[1].x - local CurrentY = SpawnTemplate.units[1].y - SpawnTemplate.x = RandomVec2.x - SpawnTemplate.y = RandomVec2.y - for UnitID = 1, #SpawnTemplate.units do - SpawnTemplate.units[UnitID].x = SpawnTemplate.units[UnitID].x + ( RandomVec2.x - CurrentX ) - SpawnTemplate.units[UnitID].y = SpawnTemplate.units[UnitID].y + ( RandomVec2.y - CurrentY ) - self:T( 'SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) - end - end - - -- If RandomizeUnits, then Randomize the formation at the start point. - if self.SpawnRandomizeUnits then - for UnitID = 1, #SpawnTemplate.units do - local RandomVec2 = PointVec3:GetRandomVec2InRadius( self.SpawnOuterRadius, self.SpawnInnerRadius ) - SpawnTemplate.units[UnitID].x = RandomVec2.x - SpawnTemplate.units[UnitID].y = RandomVec2.y - self:T( 'SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) - end - end - - if SpawnTemplate.CategoryID == Group.Category.HELICOPTER or SpawnTemplate.CategoryID == Group.Category.AIRPLANE then - if SpawnTemplate.route.points[1].type == "TakeOffParking" then - SpawnTemplate.uncontrolled = self.SpawnUnControlled - end - end - end - - self:HandleEvent( EVENTS.Birth, self._OnBirth ) - self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) - if self.Repeat then - self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) - self:HandleEvent( EVENTS.Land, self._OnLand ) - end - if self.RepeatOnEngineShutDown then - self:HandleEvent( EVENTS.EngineShutdown, self._OnEngineShutDown ) - end - - self.SpawnGroups[self.SpawnIndex].Group = _DATABASE:Spawn( SpawnTemplate ) - - local SpawnGroup = self.SpawnGroups[self.SpawnIndex].Group -- Wrapper.Group#GROUP - - --TODO: Need to check if this function doesn't need to be scheduled, as the group may not be immediately there! - if SpawnGroup then - - SpawnGroup:SetAIOnOff( self.AIOnOff ) - end - - self:T3( SpawnTemplate.name ) - - -- If there is a SpawnFunction hook defined, call it. - if self.SpawnFunctionHook then - self.SpawnFunctionHook( self.SpawnGroups[self.SpawnIndex].Group, unpack( self.SpawnFunctionArguments ) ) - end - -- TODO: Need to fix this by putting an "R" in the name of the group when the group repeats. - --if self.Repeat then - -- _DATABASE:SetStatusGroup( SpawnTemplate.name, "ReSpawn" ) - --end - end - - self.SpawnGroups[self.SpawnIndex].Spawned = true - return self.SpawnGroups[self.SpawnIndex].Group - else - --self:E( { self.SpawnTemplatePrefix, "No more Groups to Spawn:", SpawnIndex, self.SpawnMaxGroups } ) - end - - return nil -end - ---- Spawns new groups at varying time intervals. --- This is useful if you want to have continuity within your missions of certain (AI) groups to be present (alive) within your missions. --- @param #SPAWN self --- @param #number SpawnTime The time interval defined in seconds between each new spawn of new groups. --- @param #number SpawnTimeVariation The variation to be applied on the defined time interval between each new spawn. --- The variation is a number between 0 and 1, representing the %-tage of variation to be applied on the time interval. --- @return #SPAWN self --- @usage --- -- NATO helicopters engaging in the battle field. --- -- The time interval is set to SPAWN new helicopters between each 600 seconds, with a time variation of 50%. --- -- The time variation in this case will be between 450 seconds and 750 seconds. --- -- This is calculated as follows: --- -- Low limit: 600 * ( 1 - 0.5 / 2 ) = 450 --- -- High limit: 600 * ( 1 + 0.5 / 2 ) = 750 --- -- Between these two values, a random amount of seconds will be choosen for each new spawn of the helicopters. --- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):Schedule( 600, 0.5 ) -function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation ) - self:F( { SpawnTime, SpawnTimeVariation } ) - - if SpawnTime ~= nil and SpawnTimeVariation ~= nil then - self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, 1, SpawnTime, SpawnTimeVariation ) - end - - return self -end - ---- Will re-start the spawning scheduler. --- Note: This method is only required to be called when the schedule was stopped. -function SPAWN:SpawnScheduleStart() - self:F( { self.SpawnTemplatePrefix } ) - - self.SpawnScheduler:Start() -end - ---- Will stop the scheduled spawning scheduler. -function SPAWN:SpawnScheduleStop() - self:F( { self.SpawnTemplatePrefix } ) - - self.SpawnScheduler:Stop() -end - - ---- Allows to place a CallFunction hook when a new group spawns. --- The provided method will be called when a new group is spawned, including its given parameters. --- The first parameter of the SpawnFunction is the @{Group#GROUP} that was spawned. --- @param #SPAWN self --- @param #function SpawnCallBackFunction The function to be called when a group spawns. --- @param SpawnFunctionArguments A random amount of arguments to be provided to the function when the group spawns. --- @return #SPAWN --- @usage --- -- Declare SpawnObject and call a function when a new Group is spawned. --- local SpawnObject = SPAWN --- :New( "SpawnObject" ) --- :InitLimit( 2, 10 ) --- :OnSpawnGroup( --- function( SpawnGroup ) --- SpawnGroup:E( "I am spawned" ) --- end --- ) --- :SpawnScheduled( 300, 0.3 ) -function SPAWN:OnSpawnGroup( SpawnCallBackFunction, ... ) - self:F( "OnSpawnGroup" ) - - self.SpawnFunctionHook = SpawnCallBackFunction - self.SpawnFunctionArguments = {} - if arg then - self.SpawnFunctionArguments = arg - end - - return self -end - - ---- Will spawn a group from a Vec3 in 3D space. --- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes. --- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. --- You can use the returned group to further define the route to be followed. --- @param #SPAWN self --- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 coordinates where to spawn the group. --- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. --- @return Wrapper.Group#GROUP that was spawned. --- @return #nil Nothing was spawned. -function SPAWN:SpawnFromVec3( Vec3, SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, Vec3, SpawnIndex } ) - - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - self:T2(PointVec3) - - if SpawnIndex then - else - SpawnIndex = self.SpawnIndex + 1 - end - - if self:_GetSpawnIndex( SpawnIndex ) then - - local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate - - if SpawnTemplate then - - self:T( { "Current point of ", self.SpawnTemplatePrefix, Vec3 } ) - - -- Translate the position of the Group Template to the Vec3. - for UnitID = 1, #SpawnTemplate.units do - self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) - local UnitTemplate = SpawnTemplate.units[UnitID] - local SX = UnitTemplate.x - local SY = UnitTemplate.y - local BX = SpawnTemplate.route.points[1].x - local BY = SpawnTemplate.route.points[1].y - local TX = Vec3.x + ( SX - BX ) - local TY = Vec3.z + ( SY - BY ) - SpawnTemplate.units[UnitID].x = TX - SpawnTemplate.units[UnitID].y = TY - SpawnTemplate.units[UnitID].alt = Vec3.y - self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) - end - - SpawnTemplate.route.points[1].x = Vec3.x - SpawnTemplate.route.points[1].y = Vec3.z - SpawnTemplate.route.points[1].alt = Vec3.y - - SpawnTemplate.x = Vec3.x - SpawnTemplate.y = Vec3.z - - return self:SpawnWithIndex( self.SpawnIndex ) - end - end - - return nil -end - ---- Will spawn a group from a Vec2 in 3D space. --- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles. --- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. --- You can use the returned group to further define the route to be followed. --- @param #SPAWN self --- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 coordinates where to spawn the group. --- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. --- @return Wrapper.Group#GROUP that was spawned. --- @return #nil Nothing was spawned. -function SPAWN:SpawnFromVec2( Vec2, SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, Vec2, SpawnIndex } ) - - local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) - return self:SpawnFromVec3( PointVec2:GetVec3(), SpawnIndex ) -end - - ---- Will spawn a group from a hosting unit. This method is mostly advisable to be used if you want to simulate spawning from air units, like helicopters, which are dropping infantry into a defined Landing Zone. --- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. --- You can use the returned group to further define the route to be followed. --- @param #SPAWN self --- @param Wrapper.Unit#UNIT HostUnit The air or ground unit dropping or unloading the group. --- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. --- @return Wrapper.Group#GROUP that was spawned. --- @return #nil Nothing was spawned. -function SPAWN:SpawnFromUnit( HostUnit, SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, HostUnit, SpawnIndex } ) - - if HostUnit and HostUnit:IsAlive() then -- and HostUnit:getUnit(1):inAir() == false then - return self:SpawnFromVec3( HostUnit:GetVec3(), SpawnIndex ) - end - - return nil -end - ---- Will spawn a group from a hosting static. This method is mostly advisable to be used if you want to simulate spawning from buldings and structures (static buildings). --- You can use the returned group to further define the route to be followed. --- @param #SPAWN self --- @param Wrapper.Static#STATIC HostStatic The static dropping or unloading the group. --- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. --- @return Wrapper.Group#GROUP that was spawned. --- @return #nil Nothing was spawned. -function SPAWN:SpawnFromStatic( HostStatic, SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, HostStatic, SpawnIndex } ) - - if HostStatic and HostStatic:IsAlive() then - return self:SpawnFromVec3( HostStatic:GetVec3(), SpawnIndex ) - end - - return nil -end - ---- Will spawn a Group within a given @{Zone}. --- The @{Zone} can be of any type derived from @{Zone#ZONE_BASE}. --- Once the @{Group} is spawned within the zone, the @{Group} will continue on its route. --- The **first waypoint** (where the group is spawned) is replaced with the zone location coordinates. --- @param #SPAWN self --- @param Core.Zone#ZONE Zone The zone where the group is to be spawned. --- @param #boolean RandomizeGroup (optional) Randomization of the @{Group} position in the zone. --- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. --- @return Wrapper.Group#GROUP that was spawned. --- @return #nil when nothing was spawned. -function SPAWN:SpawnInZone( Zone, RandomizeGroup, SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, Zone, RandomizeGroup, SpawnIndex } ) - - if Zone then - if RandomizeGroup then - return self:SpawnFromVec2( Zone:GetRandomVec2(), SpawnIndex ) - else - return self:SpawnFromVec2( Zone:GetVec2(), SpawnIndex ) - end - end - - return nil -end - ---- (**AIR**) Will spawn a plane group in UnControlled or Controlled mode... --- This will be similar to the uncontrolled flag setting in the ME. --- You can use UnControlled mode to simulate planes startup and ready for take-off but aren't moving (yet). --- ReSpawn the plane in Controlled mode, and the plane will move... --- @param #SPAWN self --- @param #boolean UnControlled true if UnControlled, false if Controlled. --- @return #SPAWN self -function SPAWN:InitUnControlled( UnControlled ) - self:F2( { self.SpawnTemplatePrefix, UnControlled } ) - - self.SpawnUnControlled = UnControlled - - for SpawnGroupID = 1, self.SpawnMaxGroups do - self.SpawnGroups[SpawnGroupID].UnControlled = UnControlled - end - - return self -end - - - ---- Will return the SpawnGroupName either with with a specific count number or without any count. --- @param #SPAWN self --- @param #number SpawnIndex Is the number of the Group that is to be spawned. --- @return #string SpawnGroupName -function SPAWN:SpawnGroupName( SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, SpawnIndex } ) - - local SpawnPrefix = self.SpawnTemplatePrefix - if self.SpawnAliasPrefix then - SpawnPrefix = self.SpawnAliasPrefix - end - - if SpawnIndex then - local SpawnName = string.format( '%s#%03d', SpawnPrefix, SpawnIndex ) - self:T( SpawnName ) - return SpawnName - else - self:T( SpawnPrefix ) - return SpawnPrefix - end - -end - ---- Will find the first alive @{Group} it has spawned, and return the alive @{Group} object and the first Index where the first alive @{Group} object has been found. --- @param #SPAWN self --- @return Wrapper.Group#GROUP, #number The @{Group} object found, the new Index where the group was found. --- @return #nil, #nil When no group is found, #nil is returned. --- @usage --- -- Find the first alive @{Group} object of the SpawnPlanes SPAWN object @{Group} collection that it has spawned during the mission. --- local GroupPlane, Index = SpawnPlanes:GetFirstAliveGroup() --- while GroupPlane ~= nil do --- -- Do actions with the GroupPlane object. --- GroupPlane, Index = SpawnPlanes:GetNextAliveGroup( Index ) --- end -function SPAWN:GetFirstAliveGroup() - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) - - for SpawnIndex = 1, self.SpawnCount do - local SpawnGroup = self:GetGroupFromIndex( SpawnIndex ) - if SpawnGroup and SpawnGroup:IsAlive() then - return SpawnGroup, SpawnIndex - end - end - - return nil, nil -end - - ---- Will find the next alive @{Group} object from a given Index, and return a reference to the alive @{Group} object and the next Index where the alive @{Group} has been found. --- @param #SPAWN self --- @param #number SpawnIndexStart A Index holding the start position to search from. This method can also be used to find the first alive @{Group} object from the given Index. --- @return Wrapper.Group#GROUP, #number The next alive @{Group} object found, the next Index where the next alive @{Group} object was found. --- @return #nil, #nil When no alive @{Group} object is found from the start Index position, #nil is returned. --- @usage --- -- Find the first alive @{Group} object of the SpawnPlanes SPAWN object @{Group} collection that it has spawned during the mission. --- local GroupPlane, Index = SpawnPlanes:GetFirstAliveGroup() --- while GroupPlane ~= nil do --- -- Do actions with the GroupPlane object. --- GroupPlane, Index = SpawnPlanes:GetNextAliveGroup( Index ) --- end -function SPAWN:GetNextAliveGroup( SpawnIndexStart ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnIndexStart } ) - - SpawnIndexStart = SpawnIndexStart + 1 - for SpawnIndex = SpawnIndexStart, self.SpawnCount do - local SpawnGroup = self:GetGroupFromIndex( SpawnIndex ) - if SpawnGroup and SpawnGroup:IsAlive() then - return SpawnGroup, SpawnIndex - end - end - - return nil, nil -end - ---- Will find the last alive @{Group} object, and will return a reference to the last live @{Group} object and the last Index where the last alive @{Group} object has been found. --- @param #SPAWN self --- @return Wrapper.Group#GROUP, #number The last alive @{Group} object found, the last Index where the last alive @{Group} object was found. --- @return #nil, #nil When no alive @{Group} object is found, #nil is returned. --- @usage --- -- Find the last alive @{Group} object of the SpawnPlanes SPAWN object @{Group} collection that it has spawned during the mission. --- local GroupPlane, Index = SpawnPlanes:GetLastAliveGroup() --- if GroupPlane then -- GroupPlane can be nil!!! --- -- Do actions with the GroupPlane object. --- end -function SPAWN:GetLastAliveGroup() - self:F( { self.SpawnTemplatePrefixself.SpawnAliasPrefix } ) - - self.SpawnIndex = self:_GetLastIndex() - for SpawnIndex = self.SpawnIndex, 1, -1 do - local SpawnGroup = self:GetGroupFromIndex( SpawnIndex ) - if SpawnGroup and SpawnGroup:IsAlive() then - self.SpawnIndex = SpawnIndex - return SpawnGroup - end - end - - self.SpawnIndex = nil - return nil -end - - - ---- Get the group from an index. --- Returns the group from the SpawnGroups list. --- If no index is given, it will return the first group in the list. --- @param #SPAWN self --- @param #number SpawnIndex The index of the group to return. --- @return Wrapper.Group#GROUP self -function SPAWN:GetGroupFromIndex( SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnIndex } ) - - if not SpawnIndex then - SpawnIndex = 1 - end - - if self.SpawnGroups and self.SpawnGroups[SpawnIndex] then - local SpawnGroup = self.SpawnGroups[SpawnIndex].Group - return SpawnGroup - else - return nil - end -end - - ---- Return the prefix of a SpawnUnit. --- The method will search for a #-mark, and will return the text before the #-mark. --- It will return nil of no prefix was found. --- @param #SPAWN self --- @param Dcs.DCSWrapper.Unit#UNIT DCSUnit The @{DCSUnit} to be searched. --- @return #string The prefix --- @return #nil Nothing found -function SPAWN:_GetPrefixFromGroup( SpawnGroup ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) - - local GroupName = SpawnGroup:GetName() - if GroupName then - local SpawnPrefix = string.match( GroupName, ".*#" ) - if SpawnPrefix then - SpawnPrefix = SpawnPrefix:sub( 1, -2 ) - end - return SpawnPrefix - end - - return nil -end - - ---- Get the index from a given group. --- The function will search the name of the group for a #, and will return the number behind the #-mark. -function SPAWN:GetSpawnIndexFromGroup( SpawnGroup ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) - - local IndexString = string.match( SpawnGroup:GetName(), "#(%d*)$" ):sub( 2 ) - local Index = tonumber( IndexString ) - - self:T3( IndexString, Index ) - return Index - -end - ---- Return the last maximum index that can be used. -function SPAWN:_GetLastIndex() - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) - - return self.SpawnMaxGroups -end - ---- Initalize the SpawnGroups collection. --- @param #SPAWN self -function SPAWN:_InitializeSpawnGroups( SpawnIndex ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnIndex } ) - - if not self.SpawnGroups[SpawnIndex] then - self.SpawnGroups[SpawnIndex] = {} - self.SpawnGroups[SpawnIndex].Visible = false - self.SpawnGroups[SpawnIndex].Spawned = false - self.SpawnGroups[SpawnIndex].UnControlled = false - self.SpawnGroups[SpawnIndex].SpawnTime = 0 - - self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix = self.SpawnTemplatePrefix - self.SpawnGroups[SpawnIndex].SpawnTemplate = self:_Prepare( self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix, SpawnIndex ) - end - - self:_RandomizeTemplate( SpawnIndex ) - self:_RandomizeRoute( SpawnIndex ) - --self:_TranslateRotate( SpawnIndex ) - - return self.SpawnGroups[SpawnIndex] -end - - - ---- Gets the CategoryID of the Group with the given SpawnPrefix -function SPAWN:_GetGroupCategoryID( SpawnPrefix ) - local TemplateGroup = Group.getByName( SpawnPrefix ) - - if TemplateGroup then - return TemplateGroup:getCategory() - else - return nil - end -end - ---- Gets the CoalitionID of the Group with the given SpawnPrefix -function SPAWN:_GetGroupCoalitionID( SpawnPrefix ) - local TemplateGroup = Group.getByName( SpawnPrefix ) - - if TemplateGroup then - return TemplateGroup:getCoalition() - else - return nil - end -end - ---- Gets the CountryID of the Group with the given SpawnPrefix -function SPAWN:_GetGroupCountryID( SpawnPrefix ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnPrefix } ) - - local TemplateGroup = Group.getByName( SpawnPrefix ) - - if TemplateGroup then - local TemplateUnits = TemplateGroup:getUnits() - return TemplateUnits[1]:getCountry() - else - return nil - end -end - ---- Gets the Group Template from the ME environment definition. --- This method used the @{DATABASE} object, which contains ALL initial and new spawned object in MOOSE. --- @param #SPAWN self --- @param #string SpawnTemplatePrefix --- @return @SPAWN self -function SPAWN:_GetTemplate( SpawnTemplatePrefix ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnTemplatePrefix } ) - - local SpawnTemplate = nil - - SpawnTemplate = routines.utils.deepCopy( _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template ) - - if SpawnTemplate == nil then - error( 'No Template returned for SpawnTemplatePrefix = ' .. SpawnTemplatePrefix ) - end - - --SpawnTemplate.SpawnCoalitionID = self:_GetGroupCoalitionID( SpawnTemplatePrefix ) - --SpawnTemplate.SpawnCategoryID = self:_GetGroupCategoryID( SpawnTemplatePrefix ) - --SpawnTemplate.SpawnCountryID = self:_GetGroupCountryID( SpawnTemplatePrefix ) - - self:T3( { SpawnTemplate } ) - return SpawnTemplate -end - ---- Prepares the new Group Template. --- @param #SPAWN self --- @param #string SpawnTemplatePrefix --- @param #number SpawnIndex --- @return #SPAWN self -function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) - - local SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) - SpawnTemplate.name = self:SpawnGroupName( SpawnIndex ) - - SpawnTemplate.groupId = nil - --SpawnTemplate.lateActivation = false - SpawnTemplate.lateActivation = false - - if SpawnTemplate.CategoryID == Group.Category.GROUND then - self:T3( "For ground units, visible needs to be false..." ) - SpawnTemplate.visible = false - end - - if self.SpawnInitKeepUnitNames == false then - for UnitID = 1, #SpawnTemplate.units do - SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID ) - SpawnTemplate.units[UnitID].unitId = nil - end - else - for UnitID = 1, #SpawnTemplate.units do - local UnitPrefix, Rest = string.match( SpawnTemplate.units[UnitID].name, "^([^#]+)#?" ):gsub( "^%s*(.-)%s*$", "%1" ) - self:T( { UnitPrefix, Rest } ) - - SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID ) - SpawnTemplate.units[UnitID].unitId = nil - end - end - - self:T3( { "Template:", SpawnTemplate } ) - return SpawnTemplate - -end - ---- Private method randomizing the routes. --- @param #SPAWN self --- @param #number SpawnIndex The index of the group to be spawned. --- @return #SPAWN -function SPAWN:_RandomizeRoute( SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnRandomizeRoute, self.SpawnRandomizeRouteStartPoint, self.SpawnRandomizeRouteEndPoint, self.SpawnRandomizeRouteRadius } ) - - if self.SpawnRandomizeRoute then - local SpawnTemplate = self.SpawnGroups[SpawnIndex].SpawnTemplate - local RouteCount = #SpawnTemplate.route.points - - for t = self.SpawnRandomizeRouteStartPoint + 1, ( RouteCount - self.SpawnRandomizeRouteEndPoint ) do - - SpawnTemplate.route.points[t].x = SpawnTemplate.route.points[t].x + math.random( self.SpawnRandomizeRouteRadius * -1, self.SpawnRandomizeRouteRadius ) - SpawnTemplate.route.points[t].y = SpawnTemplate.route.points[t].y + math.random( self.SpawnRandomizeRouteRadius * -1, self.SpawnRandomizeRouteRadius ) - - -- Manage randomization of altitude for airborne units ... - if SpawnTemplate.CategoryID == Group.Category.AIRPLANE or SpawnTemplate.CategoryID == Group.Category.HELICOPTER then - if SpawnTemplate.route.points[t].alt and self.SpawnRandomizeRouteHeight then - SpawnTemplate.route.points[t].alt = SpawnTemplate.route.points[t].alt + math.random( 1, self.SpawnRandomizeRouteHeight ) - end - else - SpawnTemplate.route.points[t].alt = nil - end - - self:T( 'SpawnTemplate.route.points[' .. t .. '].x = ' .. SpawnTemplate.route.points[t].x .. ', SpawnTemplate.route.points[' .. t .. '].y = ' .. SpawnTemplate.route.points[t].y ) - end - end - - self:_RandomizeZones( SpawnIndex ) - - return self -end - ---- Private method that randomizes the template of the group. --- @param #SPAWN self --- @param #number SpawnIndex --- @return #SPAWN self -function SPAWN:_RandomizeTemplate( SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnRandomizeTemplate } ) - - if self.SpawnRandomizeTemplate then - self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix = self.SpawnTemplatePrefixTable[ math.random( 1, #self.SpawnTemplatePrefixTable ) ] - self.SpawnGroups[SpawnIndex].SpawnTemplate = self:_Prepare( self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix, SpawnIndex ) - self.SpawnGroups[SpawnIndex].SpawnTemplate.route = routines.utils.deepCopy( self.SpawnTemplate.route ) - self.SpawnGroups[SpawnIndex].SpawnTemplate.x = self.SpawnTemplate.x - self.SpawnGroups[SpawnIndex].SpawnTemplate.y = self.SpawnTemplate.y - self.SpawnGroups[SpawnIndex].SpawnTemplate.start_time = self.SpawnTemplate.start_time - local OldX = self.SpawnGroups[SpawnIndex].SpawnTemplate.units[1].x - local OldY = self.SpawnGroups[SpawnIndex].SpawnTemplate.units[1].y - for UnitID = 1, #self.SpawnGroups[SpawnIndex].SpawnTemplate.units do - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].heading = self.SpawnTemplate.units[1].heading - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x = self.SpawnTemplate.units[1].x + ( self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x - OldX ) - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y = self.SpawnTemplate.units[1].y + ( self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y - OldY ) - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].alt = self.SpawnTemplate.units[1].alt - end - end - - self:_RandomizeRoute( SpawnIndex ) - - return self -end - ---- Private method that randomizes the @{Zone}s where the Group will be spawned. --- @param #SPAWN self --- @param #number SpawnIndex --- @return #SPAWN self -function SPAWN:_RandomizeZones( SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnRandomizeZones } ) - - if self.SpawnRandomizeZones then - local SpawnZone = nil -- Core.Zone#ZONE_BASE - while not SpawnZone do - self:T( { SpawnZoneTableCount = #self.SpawnZoneTable, self.SpawnZoneTable } ) - local ZoneID = math.random( #self.SpawnZoneTable ) - self:T( ZoneID ) - SpawnZone = self.SpawnZoneTable[ ZoneID ]:GetZoneMaybe() - end - - self:T( "Preparing Spawn in Zone", SpawnZone:GetName() ) - - local SpawnVec2 = SpawnZone:GetRandomVec2() - - self:T( { SpawnVec2 = SpawnVec2 } ) - - local SpawnTemplate = self.SpawnGroups[SpawnIndex].SpawnTemplate - - self:T( { Route = SpawnTemplate.route } ) - - for UnitID = 1, #SpawnTemplate.units do - local UnitTemplate = SpawnTemplate.units[UnitID] - self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y ) - local SX = UnitTemplate.x - local SY = UnitTemplate.y - local BX = SpawnTemplate.route.points[1].x - local BY = SpawnTemplate.route.points[1].y - local TX = SpawnVec2.x + ( SX - BX ) - local TY = SpawnVec2.y + ( SY - BY ) - UnitTemplate.x = TX - UnitTemplate.y = TY - -- TODO: Manage altitude based on landheight... - --SpawnTemplate.units[UnitID].alt = SpawnVec2: - self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y ) - end - SpawnTemplate.x = SpawnVec2.x - SpawnTemplate.y = SpawnVec2.y - SpawnTemplate.route.points[1].x = SpawnVec2.x - SpawnTemplate.route.points[1].y = SpawnVec2.y - end - - return self - -end - -function SPAWN:_TranslateRotate( SpawnIndex, SpawnRootX, SpawnRootY, SpawnX, SpawnY, SpawnAngle ) - self:F( { self.SpawnTemplatePrefix, SpawnIndex, SpawnRootX, SpawnRootY, SpawnX, SpawnY, SpawnAngle } ) - - -- Translate - local TranslatedX = SpawnX - local TranslatedY = SpawnY - - -- Rotate - -- From Wikipedia: https://en.wikipedia.org/wiki/Rotation_matrix#Common_rotations - -- x' = x \cos \theta - y \sin \theta\ - -- y' = x \sin \theta + y \cos \theta\ - local RotatedX = - TranslatedX * math.cos( math.rad( SpawnAngle ) ) - + TranslatedY * math.sin( math.rad( SpawnAngle ) ) - local RotatedY = TranslatedX * math.sin( math.rad( SpawnAngle ) ) - + TranslatedY * math.cos( math.rad( SpawnAngle ) ) - - -- Assign - self.SpawnGroups[SpawnIndex].SpawnTemplate.x = SpawnRootX - RotatedX - self.SpawnGroups[SpawnIndex].SpawnTemplate.y = SpawnRootY + RotatedY - - - local SpawnUnitCount = table.getn( self.SpawnGroups[SpawnIndex].SpawnTemplate.units ) - for u = 1, SpawnUnitCount do - - -- Translate - local TranslatedX = SpawnX - local TranslatedY = SpawnY - 10 * ( u - 1 ) - - -- Rotate - local RotatedX = - TranslatedX * math.cos( math.rad( SpawnAngle ) ) - + TranslatedY * math.sin( math.rad( SpawnAngle ) ) - local RotatedY = TranslatedX * math.sin( math.rad( SpawnAngle ) ) - + TranslatedY * math.cos( math.rad( SpawnAngle ) ) - - -- Assign - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[u].x = SpawnRootX - RotatedX - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[u].y = SpawnRootY + RotatedY - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[u].heading = self.SpawnGroups[SpawnIndex].SpawnTemplate.units[u].heading + math.rad( SpawnAngle ) - end - - return self -end - ---- Get the next index of the groups to be spawned. This method is complicated, as it is used at several spaces. -function SPAWN:_GetSpawnIndex( SpawnIndex ) - self:F2( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnMaxGroups, self.SpawnMaxUnitsAlive, self.AliveUnits, #self.SpawnTemplate.units } ) - - if ( self.SpawnMaxGroups == 0 ) or ( SpawnIndex <= self.SpawnMaxGroups ) then - if ( self.SpawnMaxUnitsAlive == 0 ) or ( self.AliveUnits + #self.SpawnTemplate.units <= self.SpawnMaxUnitsAlive ) or self.UnControlled == true then - if SpawnIndex and SpawnIndex >= self.SpawnCount + 1 then - self.SpawnCount = self.SpawnCount + 1 - SpawnIndex = self.SpawnCount - end - self.SpawnIndex = SpawnIndex - if not self.SpawnGroups[self.SpawnIndex] then - self:_InitializeSpawnGroups( self.SpawnIndex ) - end - else - return nil - end - else - return nil - end - - return self.SpawnIndex -end - - --- TODO Need to delete this... _DATABASE does this now ... - ---- @param #SPAWN self --- @param Core.Event#EVENTDATA EventData -function SPAWN:_OnBirth( EventData ) - self:F( self.SpawnTemplatePrefix ) - - local SpawnGroup = EventData.IniGroup - - if SpawnGroup then - local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self.AliveUnits = self.AliveUnits + 1 - self:T( "Alive Units: " .. self.AliveUnits ) - end - end - -end - ---- Obscolete --- @todo Need to delete this... _DATABASE does this now ... - ---- @param #SPAWN self --- @param Core.Event#EVENTDATA EventData -function SPAWN:_OnDeadOrCrash( EventData ) - self:F( self.SpawnTemplatePrefix ) - - local SpawnGroup = EventData.IniGroup - - if SpawnGroup then - local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "Dead event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self.AliveUnits = self.AliveUnits - 1 - self:T( "Alive Units: " .. self.AliveUnits ) - end - end -end - ---- Will detect AIR Units taking off... When the event takes place, the spawned Group is registered as airborne... --- This is needed to ensure that Re-SPAWNing only is done for landed AIR Groups. --- @param #SPAWN self --- @param Core.Event#EVENTDATA EventData -function SPAWN:_OnTakeOff( EventData ) - self:F( self.SpawnTemplatePrefix ) - - local SpawnGroup = EventData.IniGroup - if SpawnGroup then - local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "TakeOff event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self:T( "self.Landed = false" ) - SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", false ) - end - end -end - ---- Will detect AIR Units landing... When the event takes place, the spawned Group is registered as landed. --- This is needed to ensure that Re-SPAWNing is only done for landed AIR Groups. --- @param #SPAWN self --- @param Core.Event#EVENTDATA EventData -function SPAWN:_OnLand( EventData ) - self:F( self.SpawnTemplatePrefix ) - - local SpawnGroup = EventData.IniGroup - if SpawnGroup then - local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "Land event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - -- TODO: Check if this is the last unit of the group that lands. - SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", true ) - if self.RepeatOnLanding then - local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) - self:T( { "Landed:", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) - self:ReSpawn( SpawnGroupIndex ) - end - end - end -end - ---- Will detect AIR Units shutting down their engines ... --- When the event takes place, and the method @{RepeatOnEngineShutDown} was called, the spawned Group will Re-SPAWN. --- But only when the Unit was registered to have landed. --- @param #SPAWN self --- @param Core.Event#EVENTDATA EventData -function SPAWN:_OnEngineShutDown( EventData ) - self:F( self.SpawnTemplatePrefix ) - - local SpawnGroup = EventData.IniGroup - if SpawnGroup then - local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "EngineShutdown event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - -- todo: test if on the runway - local Landed = SpawnGroup:GetState( SpawnGroup, "Spawn_Landed" ) - if Landed and self.RepeatOnEngineShutDown then - local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) - self:T( { "EngineShutDown: ", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) - self:ReSpawn( SpawnGroupIndex ) - end - end - end -end - ---- This function is called automatically by the Spawning scheduler. --- It is the internal worker method SPAWNing new Groups on the defined time intervals. -function SPAWN:_Scheduler() - self:F2( { "_Scheduler", self.SpawnTemplatePrefix, self.SpawnAliasPrefix, self.SpawnIndex, self.SpawnMaxGroups, self.SpawnMaxUnitsAlive } ) - - -- Validate if there are still groups left in the batch... - self:Spawn() - - return true -end - ---- Schedules the CleanUp of Groups --- @param #SPAWN self --- @return #boolean True = Continue Scheduler -function SPAWN:_SpawnCleanUpScheduler() - self:F( { "CleanUp Scheduler:", self.SpawnTemplatePrefix } ) - - local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup() - self:T( { "CleanUp Scheduler:", SpawnGroup, SpawnCursor } ) - - while SpawnGroup do - - local SpawnUnits = SpawnGroup:GetUnits() - - for UnitID, UnitData in pairs( SpawnUnits ) do - - local SpawnUnit = UnitData -- Wrapper.Unit#UNIT - local SpawnUnitName = SpawnUnit:GetName() - - - self.SpawnCleanUpTimeStamps[SpawnUnitName] = self.SpawnCleanUpTimeStamps[SpawnUnitName] or {} - local Stamp = self.SpawnCleanUpTimeStamps[SpawnUnitName] - self:T( { SpawnUnitName, Stamp } ) - - if Stamp.Vec2 then - if SpawnUnit:InAir() == false and SpawnUnit:GetVelocityKMH() < 1 then - local NewVec2 = SpawnUnit:GetVec2() - if Stamp.Vec2.x == NewVec2.x and Stamp.Vec2.y == NewVec2.y then - -- If the plane is not moving, and is on the ground, assign it with a timestamp... - if Stamp.Time + self.SpawnCleanUpInterval < timer.getTime() then - self:T( { "CleanUp Scheduler:", "ReSpawning:", SpawnGroup:GetName() } ) - self:ReSpawn( SpawnCursor ) - Stamp.Vec2 = nil - Stamp.Time = nil - end - else - Stamp.Time = timer.getTime() - Stamp.Vec2 = SpawnUnit:GetVec2() - end - else - Stamp.Vec2 = nil - Stamp.Time = nil - end - else - if SpawnUnit:InAir() == false then - Stamp.Vec2 = SpawnUnit:GetVec2() - if SpawnUnit:GetVelocityKMH() < 1 then - Stamp.Time = timer.getTime() - end - else - Stamp.Time = nil - Stamp.Vec2 = nil - end - end - end - - SpawnGroup, SpawnCursor = self:GetNextAliveGroup( SpawnCursor ) - - self:T( { "CleanUp Scheduler:", SpawnGroup, SpawnCursor } ) - - end - - return true -- Repeat - -end ---- Limit the simultaneous movement of Groups within a running Mission. --- This module is defined to improve the performance in missions, and to bring additional realism for GROUND vehicles. --- Performance: If in a DCSRTE there are a lot of moving GROUND units, then in a multi player mission, this WILL create lag if --- the main DCS execution core of your CPU is fully utilized. So, this class will limit the amount of simultaneous moving GROUND units --- on defined intervals (currently every minute). --- @module Movement - ---- the MOVEMENT class --- @type MOVEMENT --- @extends Core.Base#BASE -MOVEMENT = { - ClassName = "MOVEMENT", -} - ---- Creates the main object which is handling the GROUND forces movement. --- @param table{string,...}|string MovePrefixes is a table of the Prefixes (names) of the GROUND Groups that need to be controlled by the MOVEMENT Object. --- @param number MoveMaximum is a number that defines the maximum amount of GROUND Units to be moving during one minute. --- @return MOVEMENT --- @usage --- -- Limit the amount of simultaneous moving units on the ground to prevent lag. --- Movement_US_Platoons = MOVEMENT:New( { 'US Tank Platoon Left', 'US Tank Platoon Middle', 'US Tank Platoon Right', 'US CH-47D Troops' }, 15 ) - -function MOVEMENT:New( MovePrefixes, MoveMaximum ) - local self = BASE:Inherit( self, BASE:New() ) -- #MOVEMENT - self:F( { MovePrefixes, MoveMaximum } ) - - if type( MovePrefixes ) == 'table' then - self.MovePrefixes = MovePrefixes - else - self.MovePrefixes = { MovePrefixes } - end - self.MoveCount = 0 -- The internal counter of the amount of Moveing the has happened since MoveStart. - self.MoveMaximum = MoveMaximum -- Contains the Maximum amount of units that are allowed to move... - self.AliveUnits = 0 -- Contains the counter how many units are currently alive - self.MoveUnits = {} -- Reflects if the Moving for this MovePrefixes is going to be scheduled or not. - - self:HandleEvent( EVENTS.Birth ) - --- self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) --- --- self:EnableEvents() - - self:ScheduleStart() - - return self -end - ---- Call this function to start the MOVEMENT scheduling. -function MOVEMENT:ScheduleStart() - self:F() - --self.MoveFunction = routines.scheduleFunction( self._Scheduler, { self }, timer.getTime() + 1, 120 ) - self.MoveFunction = SCHEDULER:New( self, self._Scheduler, {}, 1, 120 ) -end - ---- Call this function to stop the MOVEMENT scheduling. --- @todo need to implement it ... Forgot. -function MOVEMENT:ScheduleStop() - self:F() - -end - ---- Captures the birth events when new Units were spawned. --- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. --- @param #MOVEMENT self --- @param Core.Event#EVENTDATA self -function MOVEMENT:OnEventBirth( EventData ) - self:F( { EventData } ) - - if timer.getTime0() < timer.getAbsTime() then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line - if EventData.IniDCSUnit then - self:T( "Birth object : " .. EventData.IniDCSUnitName ) - if EventData.IniDCSGroup and EventData.IniDCSGroup:isExist() then - for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do - if string.find( EventData.IniDCSUnitName, MovePrefix, 1, true ) then - self.AliveUnits = self.AliveUnits + 1 - self.MoveUnits[EventData.IniDCSUnitName] = EventData.IniDCSGroupName - self:T( self.AliveUnits ) - end - end - end - end - - EventData.IniUnit:HandleEvent( EVENTS.DEAD, self.OnDeadOrCrash ) - end - -end - ---- Captures the Dead or Crash events when Units crash or are destroyed. --- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. -function MOVEMENT:OnDeadOrCrash( Event ) - self:F( { Event } ) - - if Event.IniDCSUnit then - self:T( "Dead object : " .. Event.IniDCSUnitName ) - for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do - if string.find( Event.IniDCSUnitName, MovePrefix, 1, true ) then - self.AliveUnits = self.AliveUnits - 1 - self.MoveUnits[Event.IniDCSUnitName] = nil - self:T( self.AliveUnits ) - end - end - end -end - ---- This function is called automatically by the MOVEMENT scheduler. A new function is scheduled when MoveScheduled is true. -function MOVEMENT:_Scheduler() - self:F( { self.MovePrefixes, self.MoveMaximum, self.AliveUnits, self.MovementGroups } ) - - if self.AliveUnits > 0 then - local MoveProbability = ( self.MoveMaximum * 100 ) / self.AliveUnits - self:T( 'Move Probability = ' .. MoveProbability ) - - for MovementUnitName, MovementGroupName in pairs( self.MoveUnits ) do - local MovementGroup = Group.getByName( MovementGroupName ) - if MovementGroup and MovementGroup:isExist() then - local MoveOrStop = math.random( 1, 100 ) - self:T( 'MoveOrStop = ' .. MoveOrStop ) - if MoveOrStop <= MoveProbability then - self:T( 'Group continues moving = ' .. MovementGroupName ) - trigger.action.groupContinueMoving( MovementGroup ) - else - self:T( 'Group stops moving = ' .. MovementGroupName ) - trigger.action.groupStopMoving( MovementGroup ) - end - else - self.MoveUnits[MovementUnitName] = nil - end - end - end - return true -end ---- Provides defensive behaviour to a set of SAM sites within a running Mission. --- @module Sead --- @author to be searched on the forum --- @author (co) Flightcontrol (Modified and enriched with functionality) - ---- The SEAD class --- @type SEAD --- @extends Core.Base#BASE -SEAD = { - ClassName = "SEAD", - TargetSkill = { - Average = { Evade = 50, DelayOff = { 10, 25 }, DelayOn = { 10, 30 } } , - Good = { Evade = 30, DelayOff = { 8, 20 }, DelayOn = { 20, 40 } } , - High = { Evade = 15, DelayOff = { 5, 17 }, DelayOn = { 30, 50 } } , - Excellent = { Evade = 10, DelayOff = { 3, 10 }, DelayOn = { 30, 60 } } - }, - SEADGroupPrefixes = {} -} - ---- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. --- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions... --- Chances are big that the missile will miss. --- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCSRTE on which evasive actions need to be taken. --- @return SEAD --- @usage --- -- CCCP SEAD Defenses --- -- Defends the Russian SA installations from SEAD attacks. --- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } ) -function SEAD:New( SEADGroupPrefixes ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( SEADGroupPrefixes ) - if type( SEADGroupPrefixes ) == 'table' then - for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do - self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix - end - else - self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes - end - - self:HandleEvent( EVENTS.Shot ) - - return self -end - ---- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. --- @see SEAD --- @param #SEAD --- @param Core.Event#EVENTDATA EventData -function SEAD:OnEventShot( EventData ) - self:F( { EventData } ) - - local SEADUnit = EventData.IniDCSUnit - local SEADUnitName = EventData.IniDCSUnitName - local SEADWeapon = EventData.Weapon -- Identify the weapon fired - local SEADWeaponName = EventData.WeaponName -- return weapon type - -- Start of the 2nd loop - self:T( "Missile Launched = " .. SEADWeaponName ) - if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD - local _evade = math.random (1,100) -- random number for chance of evading action - local _targetMim = EventData.Weapon:getTarget() -- Identify target - local _targetMimname = Unit.getName(_targetMim) - local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) - local _targetMimgroupName = _targetMimgroup:getName() - local _targetMimcont= _targetMimgroup:getController() - local _targetskill = _DATABASE.Templates.Units[_targetMimname].Template.skill - self:T( self.SEADGroupPrefixes ) - self:T( _targetMimgroupName ) - local SEADGroupFound = false - for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do - if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then - SEADGroupFound = true - self:T( 'Group Found' ) - break - end - end - if SEADGroupFound == true then - if _targetskill == "Random" then -- when skill is random, choose a skill - local Skills = { "Average", "Good", "High", "Excellent" } - _targetskill = Skills[ math.random(1,4) ] - end - self:T( _targetskill ) - if self.TargetSkill[_targetskill] then - if (_evade > self.TargetSkill[_targetskill].Evade) then - self:T( string.format("Evading, target skill " ..string.format(_targetskill)) ) - local _targetMim = Weapon.getTarget(SEADWeapon) - local _targetMimname = Unit.getName(_targetMim) - local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) - local _targetMimcont= _targetMimgroup:getController() - routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly - local SuppressedGroups1 = {} -- unit suppressed radar off for a random time - local function SuppressionEnd1(id) - id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN) - SuppressedGroups1[id.groupName] = nil - end - local id = { - groupName = _targetMimgroup, - ctrl = _targetMimcont - } - local delay1 = math.random(self.TargetSkill[_targetskill].DelayOff[1], self.TargetSkill[_targetskill].DelayOff[2]) - if SuppressedGroups1[id.groupName] == nil then - SuppressedGroups1[id.groupName] = { - SuppressionEndTime1 = timer.getTime() + delay1, - SuppressionEndN1 = SuppressionEndCounter1 --Store instance of SuppressionEnd() scheduled function - } - Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN) - timer.scheduleFunction(SuppressionEnd1, id, SuppressedGroups1[id.groupName].SuppressionEndTime1) --Schedule the SuppressionEnd() function - --trigger.action.outText( string.format("Radar Off " ..string.format(delay1)), 20) - end - - local SuppressedGroups = {} - local function SuppressionEnd(id) - id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED) - SuppressedGroups[id.groupName] = nil - end - local id = { - groupName = _targetMimgroup, - ctrl = _targetMimcont - } - local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2]) - if SuppressedGroups[id.groupName] == nil then - SuppressedGroups[id.groupName] = { - SuppressionEndTime = timer.getTime() + delay, - SuppressionEndN = SuppressionEndCounter --Store instance of SuppressionEnd() scheduled function - } - timer.scheduleFunction(SuppressionEnd, id, SuppressedGroups[id.groupName].SuppressionEndTime) --Schedule the SuppressionEnd() function - --trigger.action.outText( string.format("Radar On " ..string.format(delay)), 20) - end - end - end - end - end -end ---- Taking the lead of AI escorting your flight. --- --- @{#ESCORT} class --- ================ --- The @{#ESCORT} class allows you to interact with escorting AI on your flight and take the lead. --- Each escorting group can be commanded with a whole set of radio commands (radio menu in your flight, and then F10). --- --- The radio commands will vary according the category of the group. The richest set of commands are with Helicopters and AirPlanes. --- Ships and Ground troops will have a more limited set, but they can provide support through the bombing of targets designated by the other escorts. --- --- RADIO MENUs that can be created: --- ================================ --- Find a summary below of the current available commands: --- --- Navigation ...: --- --------------- --- Escort group navigation functions: --- --- * **"Join-Up and Follow at x meters":** The escort group fill follow you at about x meters, and they will follow you. --- * **"Flare":** Provides menu commands to let the escort group shoot a flare in the air in a color. --- * **"Smoke":** Provides menu commands to let the escort group smoke the air in a color. Note that smoking is only available for ground and naval troops. --- --- Hold position ...: --- ------------------ --- Escort group navigation functions: --- --- * **"At current location":** Stops the escort group and they will hover 30 meters above the ground at the position they stopped. --- * **"At client location":** Stops the escort group and they will hover 30 meters above the ground at the position they stopped. --- --- Report targets ...: --- ------------------- --- Report targets will make the escort group to report any target that it identifies within a 8km range. Any detected target can be attacked using the 4. Attack nearby targets function. (see below). --- --- * **"Report now":** Will report the current detected targets. --- * **"Report targets on":** Will make the escort group to report detected targets and will fill the "Attack nearby targets" menu list. --- * **"Report targets off":** Will stop detecting targets. --- --- Scan targets ...: --- ----------------- --- Menu items to pop-up the escort group for target scanning. After scanning, the escort group will resume with the mission or defined task. --- --- * **"Scan targets 30 seconds":** Scan 30 seconds for targets. --- * **"Scan targets 60 seconds":** Scan 60 seconds for targets. --- --- Attack targets ...: --- ------------------- --- This menu item will list all detected targets within a 15km range. Depending on the level of detection (known/unknown) and visuality, the targets type will also be listed. --- --- Request assistance from ...: --- ---------------------------- --- This menu item will list all detected targets within a 15km range, as with the menu item **Attack Targets**. --- This menu item allows to request attack support from other escorts supporting the current client group. --- eg. the function allows a player to request support from the Ship escort to attack a target identified by the Plane escort with its Tomahawk missiles. --- eg. the function allows a player to request support from other Planes escorting to bomb the unit with illumination missiles or bombs, so that the main plane escort can attack the area. --- --- ROE ...: --- -------- --- Sets the Rules of Engagement (ROE) of the escort group when in flight. --- --- * **"Hold Fire":** The escort group will hold fire. --- * **"Return Fire":** The escort group will return fire. --- * **"Open Fire":** The escort group will open fire on designated targets. --- * **"Weapon Free":** The escort group will engage with any target. --- --- Evasion ...: --- ------------ --- Will define the evasion techniques that the escort group will perform during flight or combat. --- --- * **"Fight until death":** The escort group will have no reaction to threats. --- * **"Use flares, chaff and jammers":** The escort group will use passive defense using flares and jammers. No evasive manoeuvres are executed. --- * **"Evade enemy fire":** The rescort group will evade enemy fire before firing. --- * **"Go below radar and evade fire":** The escort group will perform evasive vertical manoeuvres. --- --- Resume Mission ...: --- ------------------- --- Escort groups can have their own mission. This menu item will allow the escort group to resume their Mission from a given waypoint. --- Note that this is really fantastic, as you now have the dynamic of taking control of the escort groups, and allowing them to resume their path or mission. --- --- ESCORT construction methods. --- ============================ --- Create a new SPAWN object with the @{#ESCORT.New} method: --- --- * @{#ESCORT.New}: Creates a new ESCORT object from a @{Group#GROUP} for a @{Client#CLIENT}, with an optional briefing text. --- --- ESCORT initialization methods. --- ============================== --- The following menus are created within the RADIO MENU (F10) of an active unit hosted by a player: --- --- * @{#ESCORT.MenuFollowAt}: Creates a menu to make the escort follow the client. --- * @{#ESCORT.MenuHoldAtEscortPosition}: Creates a menu to hold the escort at its current position. --- * @{#ESCORT.MenuHoldAtLeaderPosition}: Creates a menu to hold the escort at the client position. --- * @{#ESCORT.MenuScanForTargets}: Creates a menu so that the escort scans targets. --- * @{#ESCORT.MenuFlare}: Creates a menu to disperse flares. --- * @{#ESCORT.MenuSmoke}: Creates a menu to disparse smoke. --- * @{#ESCORT.MenuReportTargets}: Creates a menu so that the escort reports targets. --- * @{#ESCORT.MenuReportPosition}: Creates a menu so that the escort reports its current position from bullseye. --- * @{#ESCORT.MenuAssistedAttack: Creates a menu so that the escort supportes assisted attack from other escorts with the client. --- * @{#ESCORT.MenuROE: Creates a menu structure to set the rules of engagement of the escort. --- * @{#ESCORT.MenuEvasion: Creates a menu structure to set the evasion techniques when the escort is under threat. --- * @{#ESCORT.MenuResumeMission}: Creates a menu structure so that the escort can resume from a waypoint. --- --- --- @usage --- -- Declare a new EscortPlanes object as follows: --- --- -- First find the GROUP object and the CLIENT object. --- local EscortClient = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor. --- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client. --- --- -- Now use these 2 objects to construct the new EscortPlanes object. --- EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) --- --- --- --- @module Escort --- @author FlightControl - ---- ESCORT class --- @type ESCORT --- @extends Core.Base#BASE --- @field Wrapper.Client#CLIENT EscortClient --- @field Wrapper.Group#GROUP EscortGroup --- @field #string EscortName --- @field #ESCORT.MODE EscortMode The mode the escort is in. --- @field Core.Scheduler#SCHEDULER FollowScheduler The instance of the SCHEDULER class. --- @field #number FollowDistance The current follow distance. --- @field #boolean ReportTargets If true, nearby targets are reported. --- @Field Dcs.DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the EscortGroup. --- @field Dcs.DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the EscortGroup. --- @field Core.Menu#MENU_CLIENT EscortMenuResumeMission --- @field Functional.Detection#DETECTION_BASE Detection -ESCORT = { - ClassName = "ESCORT", - EscortName = nil, -- The Escort Name - EscortClient = nil, - EscortGroup = nil, - EscortMode = 1, - MODE = { - FOLLOW = 1, - MISSION = 2, - }, - Targets = {}, -- The identified targets - FollowScheduler = nil, - ReportTargets = true, - OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE, - OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION, - SmokeDirectionVector = false, - TaskPoints = {} -} - ---- ESCORT.Mode class --- @type ESCORT.MODE --- @field #number FOLLOW --- @field #number MISSION - ---- MENUPARAM type --- @type MENUPARAM --- @field #ESCORT ParamSelf --- @field #Distance ParamDistance --- @field #function ParamFunction --- @field #string ParamMessage - ---- ESCORT class constructor for an AI group --- @param #ESCORT self --- @param Wrapper.Client#CLIENT EscortClient The client escorted by the EscortGroup. --- @param Wrapper.Group#GROUP EscortGroup The group AI escorting the EscortClient. --- @param #string EscortName Name of the escort. --- @param #string EscortBriefing A text showing the ESCORT briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown. --- @return #ESCORT self --- @usage --- -- Declare a new EscortPlanes object as follows: --- --- -- First find the GROUP object and the CLIENT object. --- local EscortClient = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor. --- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client. --- --- -- Now use these 2 objects to construct the new EscortPlanes object. --- EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) -function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) - - local self = BASE:Inherit( self, BASE:New() ) -- #ESCORT - self:F( { EscortClient, EscortGroup, EscortName } ) - - self.EscortClient = EscortClient -- Wrapper.Client#CLIENT - self.EscortGroup = EscortGroup -- Wrapper.Group#GROUP - self.EscortName = EscortName - self.EscortBriefing = EscortBriefing - - self.EscortSetGroup = SET_GROUP:New() - self.EscortSetGroup:AddObject( self.EscortGroup ) - self.EscortSetGroup:Flush() - self.Detection = DETECTION_UNITS:New( self.EscortSetGroup, 15000 ) - - self.EscortGroup.Detection = self.Detection - - -- Set EscortGroup known at EscortClient. - if not self.EscortClient._EscortGroups then - self.EscortClient._EscortGroups = {} - end - - if not self.EscortClient._EscortGroups[EscortGroup:GetName()] then - self.EscortClient._EscortGroups[EscortGroup:GetName()] = {} - self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup - self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName - self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection = self.EscortGroup.Detection - end - - self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName ) - - self.EscortGroup:WayPointInitialize(1) - - self.EscortGroup:OptionROTVertical() - self.EscortGroup:OptionROEOpenFire() - - if not EscortBriefing then - EscortGroup:MessageToClient( EscortGroup:GetCategoryName() .. " '" .. EscortName .. "' (" .. EscortGroup:GetCallsign() .. ") reporting! " .. - "We're escorting your flight. " .. - "Use the Radio Menu and F10 and use the options under + " .. EscortName .. "\n", - 60, EscortClient - ) - else - EscortGroup:MessageToClient( EscortGroup:GetCategoryName() .. " '" .. EscortName .. "' (" .. EscortGroup:GetCallsign() .. ") " .. EscortBriefing, - 60, EscortClient - ) - end - - self.FollowDistance = 100 - self.CT1 = 0 - self.GT1 = 0 - - self.FollowScheduler, self.FollowSchedule = SCHEDULER:New( self, self._FollowScheduler, {}, 1, .5, .01 ) - self.FollowScheduler:Stop( self.FollowSchedule ) - - self.EscortMode = ESCORT.MODE.MISSION - - - return self -end - ---- Set a Detection method for the EscortClient to be reported upon. --- Detection methods are based on the derived classes from DETECTION_BASE. --- @param #ESCORT self --- @param Function.Detection#DETECTION_BASE Detection -function ESCORT:SetDetection( Detection ) - - self.Detection = Detection - self.EscortGroup.Detection = self.Detection - self.EscortClient._EscortGroups[self.EscortGroup:GetName()].Detection = self.EscortGroup.Detection - - Detection:__Start( 1 ) - -end - ---- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to. --- This allows to visualize where the escort is flying to. --- @param #ESCORT self --- @param #boolean SmokeDirection If true, then the direction vector will be smoked. -function ESCORT:TestSmokeDirectionVector( SmokeDirection ) - self.SmokeDirectionVector = ( SmokeDirection == true ) and true or false -end - - ---- Defines the default menus --- @param #ESCORT self --- @return #ESCORT -function ESCORT:Menus() - self:F() - - self:MenuFollowAt( 100 ) - self:MenuFollowAt( 200 ) - self:MenuFollowAt( 300 ) - self:MenuFollowAt( 400 ) - - self:MenuScanForTargets( 100, 60 ) - - self:MenuHoldAtEscortPosition( 30 ) - self:MenuHoldAtLeaderPosition( 30 ) - - self:MenuFlare() - self:MenuSmoke() - - self:MenuReportTargets( 60 ) - self:MenuAssistedAttack() - self:MenuROE() - self:MenuEvasion() - self:MenuResumeMission() - - - return self -end - - - ---- Defines a menu slot to let the escort Join and Follow you at a certain distance. --- This menu will appear under **Navigation**. --- @param #ESCORT self --- @param Dcs.DCSTypes#Distance Distance The distance in meters that the escort needs to follow the client. --- @return #ESCORT -function ESCORT:MenuFollowAt( Distance ) - self:F(Distance) - - if self.EscortGroup:IsAir() then - if not self.EscortMenuReportNavigation then - self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu ) - end - - if not self.EscortMenuJoinUpAndFollow then - self.EscortMenuJoinUpAndFollow = {} - end - - self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, self, Distance ) - - self.EscortMode = ESCORT.MODE.FOLLOW - end - - return self -end - ---- Defines a menu slot to let the escort hold at their current position and stay low with a specified height during a specified time in seconds. --- This menu will appear under **Hold position**. --- @param #ESCORT self --- @param Dcs.DCSTypes#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. --- @param Dcs.DCSTypes#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. --- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed. --- @return #ESCORT --- TODO: Implement Seconds parameter. Challenge is to first develop the "continue from last activity" function. -function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat ) - self:F( { Height, Seconds, MenuTextFormat } ) - - if self.EscortGroup:IsAir() then - - if not self.EscortMenuHold then - self.EscortMenuHold = MENU_CLIENT:New( self.EscortClient, "Hold position", self.EscortMenu ) - end - - if not Height then - Height = 30 - end - - if not Seconds then - Seconds = 0 - end - - local MenuText = "" - if not MenuTextFormat then - if Seconds == 0 then - MenuText = string.format( "Hold at %d meter", Height ) - else - MenuText = string.format( "Hold at %d meter for %d seconds", Height, Seconds ) - end - else - if Seconds == 0 then - MenuText = string.format( MenuTextFormat, Height ) - else - MenuText = string.format( MenuTextFormat, Height, Seconds ) - end - end - - if not self.EscortMenuHoldPosition then - self.EscortMenuHoldPosition = {} - end - - self.EscortMenuHoldPosition[#self.EscortMenuHoldPosition+1] = MENU_CLIENT_COMMAND - :New( - self.EscortClient, - MenuText, - self.EscortMenuHold, - ESCORT._HoldPosition, - self, - self.EscortGroup, - Height, - Seconds - ) - end - - return self -end - - ---- Defines a menu slot to let the escort hold at the client position and stay low with a specified height during a specified time in seconds. --- This menu will appear under **Navigation**. --- @param #ESCORT self --- @param Dcs.DCSTypes#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. --- @param Dcs.DCSTypes#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. --- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain one or two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed. --- @return #ESCORT --- TODO: Implement Seconds parameter. Challenge is to first develop the "continue from last activity" function. -function ESCORT:MenuHoldAtLeaderPosition( Height, Seconds, MenuTextFormat ) - self:F( { Height, Seconds, MenuTextFormat } ) - - if self.EscortGroup:IsAir() then - - if not self.EscortMenuHold then - self.EscortMenuHold = MENU_CLIENT:New( self.EscortClient, "Hold position", self.EscortMenu ) - end - - if not Height then - Height = 30 - end - - if not Seconds then - Seconds = 0 - end - - local MenuText = "" - if not MenuTextFormat then - if Seconds == 0 then - MenuText = string.format( "Rejoin and hold at %d meter", Height ) - else - MenuText = string.format( "Rejoin and hold at %d meter for %d seconds", Height, Seconds ) - end - else - if Seconds == 0 then - MenuText = string.format( MenuTextFormat, Height ) - else - MenuText = string.format( MenuTextFormat, Height, Seconds ) - end - end - - if not self.EscortMenuHoldAtLeaderPosition then - self.EscortMenuHoldAtLeaderPosition = {} - end - - self.EscortMenuHoldAtLeaderPosition[#self.EscortMenuHoldAtLeaderPosition+1] = MENU_CLIENT_COMMAND - :New( - self.EscortClient, - MenuText, - self.EscortMenuHold, - ESCORT._HoldPosition, - { ParamSelf = self, - ParamOrbitGroup = self.EscortClient, - ParamHeight = Height, - ParamSeconds = Seconds - } - ) - end - - return self -end - ---- Defines a menu slot to let the escort scan for targets at a certain height for a certain time in seconds. --- This menu will appear under **Scan targets**. --- @param #ESCORT self --- @param Dcs.DCSTypes#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. --- @param Dcs.DCSTypes#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. --- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain one or two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed. --- @return #ESCORT -function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat ) - self:F( { Height, Seconds, MenuTextFormat } ) - - if self.EscortGroup:IsAir() then - if not self.EscortMenuScan then - self.EscortMenuScan = MENU_CLIENT:New( self.EscortClient, "Scan for targets", self.EscortMenu ) - end - - if not Height then - Height = 100 - end - - if not Seconds then - Seconds = 30 - end - - local MenuText = "" - if not MenuTextFormat then - if Seconds == 0 then - MenuText = string.format( "At %d meter", Height ) - else - MenuText = string.format( "At %d meter for %d seconds", Height, Seconds ) - end - else - if Seconds == 0 then - MenuText = string.format( MenuTextFormat, Height ) - else - MenuText = string.format( MenuTextFormat, Height, Seconds ) - end - end - - if not self.EscortMenuScanForTargets then - self.EscortMenuScanForTargets = {} - end - - self.EscortMenuScanForTargets[#self.EscortMenuScanForTargets+1] = MENU_CLIENT_COMMAND - :New( - self.EscortClient, - MenuText, - self.EscortMenuScan, - ESCORT._ScanTargets, - self, - 30 - ) - end - - return self -end - - - ---- Defines a menu slot to let the escort disperse a flare in a certain color. --- This menu will appear under **Navigation**. --- The flare will be fired from the first unit in the group. --- @param #ESCORT self --- @param #string MenuTextFormat Optional parameter that shows the menu option text. If no text is given, the default text will be displayed. --- @return #ESCORT -function ESCORT:MenuFlare( MenuTextFormat ) - self:F() - - if not self.EscortMenuReportNavigation then - self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu ) - end - - local MenuText = "" - if not MenuTextFormat then - MenuText = "Flare" - else - MenuText = MenuTextFormat - end - - if not self.EscortMenuFlare then - self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, self ) - self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Green, "Released a green flare!" ) - self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Red, "Released a red flare!" ) - self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.White, "Released a white flare!" ) - self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Yellow, "Released a yellow flare!" ) - end - - return self -end - ---- Defines a menu slot to let the escort disperse a smoke in a certain color. --- This menu will appear under **Navigation**. --- Note that smoke menu options will only be displayed for ships and ground units. Not for air units. --- The smoke will be fired from the first unit in the group. --- @param #ESCORT self --- @param #string MenuTextFormat Optional parameter that shows the menu option text. If no text is given, the default text will be displayed. --- @return #ESCORT -function ESCORT:MenuSmoke( MenuTextFormat ) - self:F() - - if not self.EscortGroup:IsAir() then - if not self.EscortMenuReportNavigation then - self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu ) - end - - local MenuText = "" - if not MenuTextFormat then - MenuText = "Smoke" - else - MenuText = MenuTextFormat - end - - if not self.EscortMenuSmoke then - self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, self ) - self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Green, "Releasing green smoke!" ) - self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Red, "Releasing red smoke!" ) - self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.White, "Releasing white smoke!" ) - self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Orange, "Releasing orange smoke!" ) - self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Blue, "Releasing blue smoke!" ) - end - end - - return self -end - ---- Defines a menu slot to let the escort report their current detected targets with a specified time interval in seconds. --- This menu will appear under **Report targets**. --- Note that if a report targets menu is not specified, no targets will be detected by the escort, and the attack and assisted attack menus will not be displayed. --- @param #ESCORT self --- @param Dcs.DCSTypes#Time Seconds Optional parameter that lets the escort report their current detected targets after specified time interval in seconds. The default time is 30 seconds. --- @return #ESCORT -function ESCORT:MenuReportTargets( Seconds ) - self:F( { Seconds } ) - - if not self.EscortMenuReportNearbyTargets then - self.EscortMenuReportNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Report targets", self.EscortMenu ) - end - - if not Seconds then - Seconds = 30 - end - - -- Report Targets - self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, self ) - self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, true ) - self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, false ) - - -- Attack Targets - self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu ) - - - self.ReportTargetsScheduler = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, Seconds ) - - return self -end - ---- Defines a menu slot to let the escort attack its detected targets using assisted attack from another escort joined also with the client. --- This menu will appear under **Request assistance from**. --- Note that this method needs to be preceded with the method MenuReportTargets. --- @param #ESCORT self --- @return #ESCORT -function ESCORT:MenuAssistedAttack() - self:F() - - -- Request assistance from other escorts. - -- This is very useful to let f.e. an escorting ship attack a target detected by an escorting plane... - self.EscortMenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, "Request assistance from", self.EscortMenu ) - - return self -end - ---- Defines a menu to let the escort set its rules of engagement. --- All rules of engagement will appear under the menu **ROE**. --- @param #ESCORT self --- @return #ESCORT -function ESCORT:MenuROE( MenuTextFormat ) - self:F( MenuTextFormat ) - - if not self.EscortMenuROE then - -- Rules of Engagement - self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu ) - if self.EscortGroup:OptionROEHoldFirePossible() then - self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEHoldFire(), "Holding weapons!" ) - end - if self.EscortGroup:OptionROEReturnFirePossible() then - self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEReturnFire(), "Returning fire!" ) - end - if self.EscortGroup:OptionROEOpenFirePossible() then - self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEOpenFire(), "Opening fire on designated targets!!" ) - end - if self.EscortGroup:OptionROEWeaponFreePossible() then - self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEWeaponFree(), "Opening fire on targets of opportunity!" ) - end - end - - return self -end - - ---- Defines a menu to let the escort set its evasion when under threat. --- All rules of engagement will appear under the menu **Evasion**. --- @param #ESCORT self --- @return #ESCORT -function ESCORT:MenuEvasion( MenuTextFormat ) - self:F( MenuTextFormat ) - - if self.EscortGroup:IsAir() then - if not self.EscortMenuEvasion then - -- Reaction to Threats - self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu ) - if self.EscortGroup:OptionROTNoReactionPossible() then - self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTNoReaction(), "Fighting until death!" ) - end - if self.EscortGroup:OptionROTPassiveDefensePossible() then - self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTPassiveDefense(), "Defending using jammers, chaff and flares!" ) - end - if self.EscortGroup:OptionROTEvadeFirePossible() then - self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTEvadeFire(), "Evading on enemy fire!" ) - end - if self.EscortGroup:OptionROTVerticalPossible() then - self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTVertical(), "Evading on enemy fire with vertical manoeuvres!" ) - end - end - end - - return self -end - ---- Defines a menu to let the escort resume its mission from a waypoint on its route. --- All rules of engagement will appear under the menu **Resume mission from**. --- @param #ESCORT self --- @return #ESCORT -function ESCORT:MenuResumeMission() - self:F() - - if not self.EscortMenuResumeMission then - -- Mission Resume Menu Root - self.EscortMenuResumeMission = MENU_CLIENT:New( self.EscortClient, "Resume mission from", self.EscortMenu ) - end - - return self -end - - ---- @param #MENUPARAM MenuParam -function ESCORT:_HoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - local OrbitUnit = OrbitGroup:GetUnit(1) -- Wrapper.Unit#UNIT - - self.FollowScheduler:Stop( self.FollowSchedule ) - - local PointFrom = {} - local GroupVec3 = EscortGroup:GetUnit(1):GetVec3() - PointFrom = {} - PointFrom.x = GroupVec3.x - PointFrom.y = GroupVec3.z - PointFrom.speed = 250 - PointFrom.type = AI.Task.WaypointType.TURNING_POINT - PointFrom.alt = GroupVec3.y - PointFrom.alt_type = AI.Task.AltitudeType.BARO - - local OrbitPoint = OrbitUnit:GetVec2() - local PointTo = {} - PointTo.x = OrbitPoint.x - PointTo.y = OrbitPoint.y - PointTo.speed = 250 - PointTo.type = AI.Task.WaypointType.TURNING_POINT - PointTo.alt = OrbitHeight - PointTo.alt_type = AI.Task.AltitudeType.BARO - PointTo.task = EscortGroup:TaskOrbitCircleAtVec2( OrbitPoint, OrbitHeight, 0 ) - - local Points = { PointFrom, PointTo } - - EscortGroup:OptionROEHoldFire() - EscortGroup:OptionROTPassiveDefense() - - EscortGroup:SetTask( EscortGroup:TaskRoute( Points ) ) - EscortGroup:MessageToClient( "Orbiting at location.", 10, EscortClient ) - -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_JoinUpAndFollow( Distance ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - self.Distance = Distance - - self:JoinUpAndFollow( EscortGroup, EscortClient, self.Distance ) -end - ---- JoinsUp and Follows a CLIENT. --- @param Functional.Escort#ESCORT self --- @param Wrapper.Group#GROUP EscortGroup --- @param Wrapper.Client#CLIENT EscortClient --- @param Dcs.DCSTypes#Distance Distance -function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) - self:F( { EscortGroup, EscortClient, Distance } ) - - self.FollowScheduler:Stop( self.FollowSchedule ) - - EscortGroup:OptionROEHoldFire() - EscortGroup:OptionROTPassiveDefense() - - self.EscortMode = ESCORT.MODE.FOLLOW - - self.CT1 = 0 - self.GT1 = 0 - self.FollowScheduler:Start( self.FollowSchedule ) - - EscortGroup:MessageToClient( "Rejoining and Following at " .. Distance .. "!", 30, EscortClient ) -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_Flare( Color, Message ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - EscortGroup:GetUnit(1):Flare( Color ) - EscortGroup:MessageToClient( Message, 10, EscortClient ) -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_Smoke( Color, Message ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - EscortGroup:GetUnit(1):Smoke( Color ) - EscortGroup:MessageToClient( Message, 10, EscortClient ) -end - - ---- @param #MENUPARAM MenuParam -function ESCORT:_ReportNearbyTargetsNow() - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - self:_ReportTargetsScheduler() - -end - -function ESCORT:_SwitchReportNearbyTargets( ReportTargets ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - self.ReportTargets = ReportTargets - - if self.ReportTargets then - if not self.ReportTargetsScheduler then - self.ReportTargetsScheduler:Schedule( self, self._ReportTargetsScheduler, {}, 1, 30 ) - end - else - routines.removeFunction( self.ReportTargetsScheduler ) - self.ReportTargetsScheduler = nil - end -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_ScanTargets( ScanDuration ) - - local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP - local EscortClient = self.EscortClient - - self.FollowScheduler:Stop( self.FollowSchedule ) - - if EscortGroup:IsHelicopter() then - EscortGroup:PushTask( - EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 200, 20 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ), 1 ) - elseif EscortGroup:IsAirPlane() then - EscortGroup:PushTask( - EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 1000, 500 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ), 1 ) - end - - EscortGroup:MessageToClient( "Scanning targets for " .. ScanDuration .. " seconds.", ScanDuration, EscortClient ) - - if self.EscortMode == ESCORT.MODE.FOLLOW then - self.FollowScheduler:Start( self.FollowSchedule ) - end - -end - ---- @param Wrapper.Group#GROUP EscortGroup -function _Resume( EscortGroup ) - env.info( '_Resume' ) - - local Escort = EscortGroup:GetState( EscortGroup, "Escort" ) - env.info( "EscortMode = " .. Escort.EscortMode ) - if Escort.EscortMode == ESCORT.MODE.FOLLOW then - Escort:JoinUpAndFollow( EscortGroup, Escort.EscortClient, Escort.Distance ) - end - -end - ---- @param #ESCORT self --- @param #number DetectedItemID -function ESCORT:_AttackTarget( DetectedItemID ) - - local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP - self:E( EscortGroup ) - - local EscortClient = self.EscortClient - - self.FollowScheduler:Stop( self.FollowSchedule ) - - if EscortGroup:IsAir() then - EscortGroup:OptionROEOpenFire() - EscortGroup:OptionROTPassiveDefense() - EscortGroup:SetState( EscortGroup, "Escort", self ) - - local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) - - local Tasks = {} - - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit, Tasks ) - if DetectedUnit:IsAlive() then - Tasks[#Tasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit ) - end - end, Tasks - ) - - Tasks[#Tasks+1] = EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } ) - - EscortGroup:SetTask( - EscortGroup:TaskCombo( - Tasks - ), 1 - ) - - else - - local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) - - local Tasks = {} - - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit, Tasks ) - if DetectedUnit:IsAlive() then - Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) - end - end, Tasks - ) - - EscortGroup:SetTask( - EscortGroup:TaskCombo( - Tasks - ), 1 - ) - - end - - EscortGroup:MessageToClient( "Engaging Designated Unit!", 10, EscortClient ) - -end - ---- --- @param #number DetectedItemID -function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItemID ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - self.FollowScheduler:Stop( self.FollowSchedule ) - - if EscortGroupAttack:IsAir() then - EscortGroupAttack:OptionROEOpenFire() - EscortGroupAttack:OptionROTVertical() - - local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) - - local Tasks = {} - - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit, Tasks ) - if DetectedUnit:IsAlive() then - Tasks[#Tasks+1] = EscortGroupAttack:TaskAttackUnit( DetectedUnit ) - end - end, Tasks - ) - - Tasks[#Tasks+1] = EscortGroupAttack:TaskOrbitCircle( 500, 350 ) - - EscortGroupAttack:SetTask( - EscortGroupAttack:TaskCombo( - Tasks - ), 1 - ) - - else - local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) - - local Tasks = {} - - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit, Tasks ) - if DetectedUnit:IsAlive() then - Tasks[#Tasks+1] = EscortGroupAttack:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) - end - end, Tasks - ) - - EscortGroupAttack:SetTask( - EscortGroupAttack:TaskCombo( - Tasks - ), 1 - ) - - end - - EscortGroupAttack:MessageToClient( "Assisting with the destroying the enemy unit!", 10, EscortClient ) - -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_ROE( EscortROEFunction, EscortROEMessage ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - pcall( function() EscortROEFunction() end ) - EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient ) -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_ROT( EscortROTFunction, EscortROTMessage ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - pcall( function() EscortROTFunction() end ) - EscortGroup:MessageToClient( EscortROTMessage, 10, EscortClient ) -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_ResumeMission( WayPoint ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - self.FollowScheduler:Stop( self.FollowSchedule ) - - local WayPoints = EscortGroup:GetTaskRoute() - self:T( WayPoint, WayPoints ) - - for WayPointIgnore = 1, WayPoint do - table.remove( WayPoints, 1 ) - end - - SCHEDULER:New( EscortGroup, EscortGroup.SetTask, { EscortGroup:TaskRoute( WayPoints ) }, 1 ) - - EscortGroup:MessageToClient( "Resuming mission from waypoint " .. WayPoint .. ".", 10, EscortClient ) -end - ---- Registers the waypoints --- @param #ESCORT self --- @return #table -function ESCORT:RegisterRoute() - self:F() - - local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP - - local TaskPoints = EscortGroup:GetTaskRoute() - - self:T( TaskPoints ) - - return TaskPoints -end - ---- @param Functional.Escort#ESCORT self -function ESCORT:_FollowScheduler() - self:F( { self.FollowDistance } ) - - self:T( {self.EscortClient.UnitName, self.EscortGroup.GroupName } ) - if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then - - local ClientUnit = self.EscortClient:GetClientGroupUnit() - local GroupUnit = self.EscortGroup:GetUnit( 1 ) - local FollowDistance = self.FollowDistance - - self:T( {ClientUnit.UnitName, GroupUnit.UnitName } ) - - if self.CT1 == 0 and self.GT1 == 0 then - self.CV1 = ClientUnit:GetVec3() - self:T( { "self.CV1", self.CV1 } ) - self.CT1 = timer.getTime() - self.GV1 = GroupUnit:GetVec3() - self.GT1 = timer.getTime() - else - local CT1 = self.CT1 - local CT2 = timer.getTime() - local CV1 = self.CV1 - local CV2 = ClientUnit:GetVec3() - self.CT1 = CT2 - self.CV1 = CV2 - - local CD = ( ( CV2.x - CV1.x )^2 + ( CV2.y - CV1.y )^2 + ( CV2.z - CV1.z )^2 ) ^ 0.5 - local CT = CT2 - CT1 - - local CS = ( 3600 / CT ) * ( CD / 1000 ) - - self:T2( { "Client:", CS, CD, CT, CV2, CV1, CT2, CT1 } ) - - local GT1 = self.GT1 - local GT2 = timer.getTime() - local GV1 = self.GV1 - local GV2 = GroupUnit:GetVec3() - self.GT1 = GT2 - self.GV1 = GV2 - - local GD = ( ( GV2.x - GV1.x )^2 + ( GV2.y - GV1.y )^2 + ( GV2.z - GV1.z )^2 ) ^ 0.5 - local GT = GT2 - GT1 - - local GS = ( 3600 / GT ) * ( GD / 1000 ) - - self:T2( { "Group:", GS, GD, GT, GV2, GV1, GT2, GT1 } ) - - -- Calculate the group direction vector - local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z } - - -- Calculate GH2, GH2 with the same height as CV2. - local GH2 = { x = GV2.x, y = CV2.y, z = GV2.z } - - -- Calculate the angle of GV to the orthonormal plane - local alpha = math.atan2( GV.z, GV.x ) - - -- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2. - -- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2)) - local CVI = { x = CV2.x + FollowDistance * math.cos(alpha), - y = GH2.y, - z = CV2.z + FollowDistance * math.sin(alpha), - } - - -- Calculate the direction vector DV of the escort group. We use CVI as the base and CV2 as the direction. - local DV = { x = CV2.x - CVI.x, y = CV2.y - CVI.y, z = CV2.z - CVI.z } - - -- We now calculate the unary direction vector DVu, so that we can multiply DVu with the speed, which is expressed in meters / s. - -- We need to calculate this vector to predict the point the escort group needs to fly to according its speed. - -- The distance of the destination point should be far enough not to have the aircraft starting to swipe left to right... - local DVu = { x = DV.x / FollowDistance, y = DV.y / FollowDistance, z = DV.z / FollowDistance } - - -- Now we can calculate the group destination vector GDV. - local GDV = { x = DVu.x * CS * 8 + CVI.x, y = CVI.y, z = DVu.z * CS * 8 + CVI.z } - - if self.SmokeDirectionVector == true then - trigger.action.smoke( GDV, trigger.smokeColor.Red ) - end - - self:T2( { "CV2:", CV2 } ) - self:T2( { "CVI:", CVI } ) - self:T2( { "GDV:", GDV } ) - - -- Measure distance between client and group - local CatchUpDistance = ( ( GDV.x - GV2.x )^2 + ( GDV.y - GV2.y )^2 + ( GDV.z - GV2.z )^2 ) ^ 0.5 - - -- The calculation of the Speed would simulate that the group would take 30 seconds to overcome - -- the requested Distance). - local Time = 10 - local CatchUpSpeed = ( CatchUpDistance - ( CS * 8.4 ) ) / Time - - local Speed = CS + CatchUpSpeed - if Speed < 0 then - Speed = 0 - end - - self:T( { "Client Speed, Escort Speed, Speed, FollowDistance, Time:", CS, GS, Speed, FollowDistance, Time } ) - - -- Now route the escort to the desired point with the desired speed. - self.EscortGroup:RouteToVec3( GDV, Speed / 3.6 ) -- DCS models speed in Mps (Miles per second) - end - - return true - end - - return false -end - - ---- Report Targets Scheduler. --- @param #ESCORT self -function ESCORT:_ReportTargetsScheduler() - self:F( self.EscortGroup:GetName() ) - - if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then - - if true then - - local EscortGroupName = self.EscortGroup:GetName() - - self.EscortMenuAttackNearbyTargets:RemoveSubMenus() - - if self.EscortMenuTargetAssistance then - self.EscortMenuTargetAssistance:RemoveSubMenus() - end - - local DetectedItems = self.Detection:GetDetectedItems() - self:E( DetectedItems ) - - local DetectedTargets = false - - local DetectedMsgs = {} - - for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do - - local ClientEscortTargets = EscortGroupData.Detection - - for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do - self:E( { DetectedItemID, DetectedItem } ) - -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. - - local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID ) - - if ClientEscortGroupName == EscortGroupName then - - DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary - - MENU_CLIENT_COMMAND:New( self.EscortClient, - DetectedItemReportSummary, - self.EscortMenuAttackNearbyTargets, - ESCORT._AttackTarget, - self, - DetectedItemID - ) - else - if self.EscortMenuTargetAssistance then - - self:T( DetectedItemReportSummary ) - local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) - MENU_CLIENT_COMMAND:New( self.EscortClient, - DetectedItemReportSummary, - MenuTargetAssistance, - ESCORT._AssistTarget, - self, - EscortGroupData.EscortGroup, - DetectedItemID - ) - end - end - - DetectedTargets = true - - end - end - self:E( DetectedMsgs ) - if DetectedTargets then - self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient ) - else - self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient ) - end - - return true - else --- local EscortGroupName = self.EscortGroup:GetName() --- local EscortTargets = self.EscortGroup:GetDetectedTargets() --- --- local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets --- --- local EscortTargetMessages = "" --- for EscortTargetID, EscortTarget in pairs( EscortTargets ) do --- local EscortObject = EscortTarget.object --- self:T( EscortObject ) --- if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then --- --- local EscortTargetUnit = UNIT:Find( EscortObject ) --- local EscortTargetUnitName = EscortTargetUnit:GetName() --- --- --- --- -- local EscortTargetIsDetected, --- -- EscortTargetIsVisible, --- -- EscortTargetLastTime, --- -- EscortTargetKnowType, --- -- EscortTargetKnowDistance, --- -- EscortTargetLastPos, --- -- EscortTargetLastVelocity --- -- = self.EscortGroup:IsTargetDetected( EscortObject ) --- -- --- -- self:T( { EscortTargetIsDetected, --- -- EscortTargetIsVisible, --- -- EscortTargetLastTime, --- -- EscortTargetKnowType, --- -- EscortTargetKnowDistance, --- -- EscortTargetLastPos, --- -- EscortTargetLastVelocity } ) --- --- --- local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3() --- local EscortVec3 = self.EscortGroup:GetVec3() --- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + --- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + --- ( EscortTargetUnitVec3.z - EscortVec3.z )^2 --- ) ^ 0.5 / 1000 --- --- self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } ) --- --- if Distance <= 15 then --- --- if not ClientEscortTargets[EscortTargetUnitName] then --- ClientEscortTargets[EscortTargetUnitName] = {} --- end --- ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit --- ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible --- ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type --- ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance --- else --- if ClientEscortTargets[EscortTargetUnitName] then --- ClientEscortTargets[EscortTargetUnitName] = nil --- end --- end --- end --- end --- --- self:T( { "Sorting Targets Table:", ClientEscortTargets } ) --- table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end ) --- self:T( { "Sorted Targets Table:", ClientEscortTargets } ) --- --- -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. --- self.EscortMenuAttackNearbyTargets:RemoveSubMenus() --- --- if self.EscortMenuTargetAssistance then --- self.EscortMenuTargetAssistance:RemoveSubMenus() --- end --- --- --for MenuIndex = 1, #self.EscortMenuAttackTargets do --- -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) --- -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() --- --end --- --- --- if ClientEscortTargets then --- for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do --- --- for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do --- --- if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then --- --- local EscortTargetMessage = "" --- local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName() --- local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName() --- if ClientEscortTargetData.type then --- EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at " --- else --- EscortTargetMessage = EscortTargetMessage .. "Unknown target at " --- end --- --- local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3() --- local EscortVec3 = self.EscortGroup:GetVec3() --- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + --- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + --- ( EscortTargetUnitVec3.z - EscortVec3.z )^2 --- ) ^ 0.5 / 1000 --- --- self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } ) --- if ClientEscortTargetData.visible == false then --- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km" --- else --- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km" --- end --- --- if ClientEscortTargetData.visible then --- EscortTargetMessage = EscortTargetMessage .. ", visual" --- end --- --- if ClientEscortGroupName == EscortGroupName then --- --- MENU_CLIENT_COMMAND:New( self.EscortClient, --- EscortTargetMessage, --- self.EscortMenuAttackNearbyTargets, --- ESCORT._AttackTarget, --- { ParamSelf = self, --- ParamUnit = ClientEscortTargetData.AttackUnit --- } --- ) --- EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage --- else --- if self.EscortMenuTargetAssistance then --- local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) --- MENU_CLIENT_COMMAND:New( self.EscortClient, --- EscortTargetMessage, --- MenuTargetAssistance, --- ESCORT._AssistTarget, --- self, --- EscortGroupData.EscortGroup, --- ClientEscortTargetData.AttackUnit --- ) --- end --- end --- else --- ClientEscortTargetData = nil --- end --- end --- end --- --- if EscortTargetMessages ~= "" and self.ReportTargets == true then --- self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) --- else --- self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient ) --- end --- end --- --- if self.EscortMenuResumeMission then --- self.EscortMenuResumeMission:RemoveSubMenus() --- --- -- if self.EscortMenuResumeWayPoints then --- -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do --- -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } ) --- -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove() --- -- end --- -- end --- --- local TaskPoints = self:RegisterRoute() --- for WayPointID, WayPoint in pairs( TaskPoints ) do --- local EscortVec3 = self.EscortGroup:GetVec3() --- local Distance = ( ( WayPoint.x - EscortVec3.x )^2 + --- ( WayPoint.y - EscortVec3.z )^2 --- ) ^ 0.5 / 1000 --- MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } ) --- end --- end --- --- return true - end - end - - return false -end ---- This module contains the MISSILETRAINER class. --- --- === --- --- 1) @{MissileTrainer#MISSILETRAINER} class, extends @{Base#BASE} --- =============================================================== --- The @{#MISSILETRAINER} class uses the DCS world messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft, --- the class will destroy the missile within a certain range, to avoid damage to your aircraft. --- It suports the following functionality: --- --- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes. --- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range � --- * Provide alerts when a missile would have killed your aircraft. --- * Provide alerts when the missile self destructs. --- * Enable / Disable and Configure the Missile Trainer using the various menu options. --- --- When running a mission where MISSILETRAINER is used, the following radio menu structure ( 'Radio Menu' -> 'Other (F10)' -> 'MissileTrainer' ) options are available for the players: --- --- * **Messages**: Menu to configure all messages. --- * **Messages On**: Show all messages. --- * **Messages Off**: Disable all messages. --- * **Tracking**: Menu to configure missile tracking messages. --- * **To All**: Shows missile tracking messages to all players. --- * **To Target**: Shows missile tracking messages only to the player where the missile is targetted at. --- * **Tracking On**: Show missile tracking messages. --- * **Tracking Off**: Disable missile tracking messages. --- * **Frequency Increase**: Increases the missile tracking message frequency with one second. --- * **Frequency Decrease**: Decreases the missile tracking message frequency with one second. --- * **Alerts**: Menu to configure alert messages. --- * **To All**: Shows alert messages to all players. --- * **To Target**: Shows alert messages only to the player where the missile is (was) targetted at. --- * **Hits On**: Show missile hit alert messages. --- * **Hits Off**: Disable missile hit alert messages. --- * **Launches On**: Show missile launch messages. --- * **Launches Off**: Disable missile launch messages. --- * **Details**: Menu to configure message details. --- * **Range On**: Shows range information when a missile is fired to a target. --- * **Range Off**: Disable range information when a missile is fired to a target. --- * **Bearing On**: Shows bearing information when a missile is fired to a target. --- * **Bearing Off**: Disable bearing information when a missile is fired to a target. --- * **Distance**: Menu to configure the distance when a missile needs to be destroyed when near to a player, during tracking. This will improve/influence hit calculation accuracy, but has the risk of damaging the aircraft when the missile reaches the aircraft before the distance is measured. --- * **50 meter**: Destroys the missile when the distance to the aircraft is below or equal to 50 meter. --- * **100 meter**: Destroys the missile when the distance to the aircraft is below or equal to 100 meter. --- * **150 meter**: Destroys the missile when the distance to the aircraft is below or equal to 150 meter. --- * **200 meter**: Destroys the missile when the distance to the aircraft is below or equal to 200 meter. --- --- --- 1.1) MISSILETRAINER construction methods: --- ----------------------------------------- --- Create a new MISSILETRAINER object with the @{#MISSILETRAINER.New} method: --- --- * @{#MISSILETRAINER.New}: Creates a new MISSILETRAINER object taking the maximum distance to your aircraft to evaluate when a missile needs to be destroyed. --- --- MISSILETRAINER will collect each unit declared in the mission with a skill level "Client" and "Player", and will monitor the missiles shot at those. --- --- 1.2) MISSILETRAINER initialization methods: --- ------------------------------------------- --- A MISSILETRAINER object will behave differently based on the usage of initialization methods: --- --- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF. --- * @{#MISSILETRAINER.InitTrackingToAll}: Sets by default the missile tracking report for all players or only for those missiles targetted to you. --- * @{#MISSILETRAINER.InitTrackingOnOff}: Sets by default the display of missile tracking report to be ON or OFF. --- * @{#MISSILETRAINER.InitTrackingFrequency}: Increases, decreases the missile tracking message display frequency with the provided time interval in seconds. --- * @{#MISSILETRAINER.InitAlertsToAll}: Sets by default the display of alerts to be shown to all players or only to you. --- * @{#MISSILETRAINER.InitAlertsHitsOnOff}: Sets by default the display of hit alerts ON or OFF. --- * @{#MISSILETRAINER.InitAlertsLaunchesOnOff}: Sets by default the display of launch alerts ON or OFF. --- * @{#MISSILETRAINER.InitRangeOnOff}: Sets by default the display of range information of missiles ON of OFF. --- * @{#MISSILETRAINER.InitBearingOnOff}: Sets by default the display of bearing information of missiles ON of OFF. --- * @{#MISSILETRAINER.InitMenusOnOff}: Allows to configure the options through the radio menu. --- --- === --- --- CREDITS --- ======= --- **Stuka (Danny)** Who you can search on the Eagle Dynamics Forums. --- Working together with Danny has resulted in the MISSILETRAINER class. --- Danny has shared his ideas and together we made a design. --- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback! --- --- @module MissileTrainer --- @author FlightControl - - ---- The MISSILETRAINER class --- @type MISSILETRAINER --- @field Core.Set#SET_CLIENT DBClients --- @extends Core.Base#BASE -MISSILETRAINER = { - ClassName = "MISSILETRAINER", - TrackingMissiles = {}, -} - -function MISSILETRAINER._Alive( Client, self ) - - if self.Briefing then - Client:Message( self.Briefing, 15, "Trainer" ) - end - - if self.MenusOnOff == true then - Client:Message( "Use the 'Radio Menu' -> 'Other (F10)' -> 'Missile Trainer' menu options to change the Missile Trainer settings (for all players).", 15, "Trainer" ) - - Client.MainMenu = MENU_CLIENT:New( Client, "Missile Trainer", nil ) -- Menu#MENU_CLIENT - - Client.MenuMessages = MENU_CLIENT:New( Client, "Messages", Client.MainMenu ) - Client.MenuOn = MENU_CLIENT_COMMAND:New( Client, "Messages On", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = true } ) - Client.MenuOff = MENU_CLIENT_COMMAND:New( Client, "Messages Off", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = false } ) - - Client.MenuTracking = MENU_CLIENT:New( Client, "Tracking", Client.MainMenu ) - Client.MenuTrackingToAll = MENU_CLIENT_COMMAND:New( Client, "To All", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = true } ) - Client.MenuTrackingToTarget = MENU_CLIENT_COMMAND:New( Client, "To Target", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = false } ) - Client.MenuTrackOn = MENU_CLIENT_COMMAND:New( Client, "Tracking On", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingOnOff = true } ) - Client.MenuTrackOff = MENU_CLIENT_COMMAND:New( Client, "Tracking Off", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingOnOff = false } ) - Client.MenuTrackIncrease = MENU_CLIENT_COMMAND:New( Client, "Frequency Increase", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingFrequency = -1 } ) - Client.MenuTrackDecrease = MENU_CLIENT_COMMAND:New( Client, "Frequency Decrease", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingFrequency = 1 } ) - - Client.MenuAlerts = MENU_CLIENT:New( Client, "Alerts", Client.MainMenu ) - Client.MenuAlertsToAll = MENU_CLIENT_COMMAND:New( Client, "To All", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = true } ) - Client.MenuAlertsToTarget = MENU_CLIENT_COMMAND:New( Client, "To Target", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = false } ) - Client.MenuHitsOn = MENU_CLIENT_COMMAND:New( Client, "Hits On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHitsOnOff = true } ) - Client.MenuHitsOff = MENU_CLIENT_COMMAND:New( Client, "Hits Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHitsOnOff = false } ) - Client.MenuLaunchesOn = MENU_CLIENT_COMMAND:New( Client, "Launches On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunchesOnOff = true } ) - Client.MenuLaunchesOff = MENU_CLIENT_COMMAND:New( Client, "Launches Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunchesOnOff = false } ) - - Client.MenuDetails = MENU_CLIENT:New( Client, "Details", Client.MainMenu ) - Client.MenuDetailsDistanceOn = MENU_CLIENT_COMMAND:New( Client, "Range On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRangeOnOff = true } ) - Client.MenuDetailsDistanceOff = MENU_CLIENT_COMMAND:New( Client, "Range Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRangeOnOff = false } ) - Client.MenuDetailsBearingOn = MENU_CLIENT_COMMAND:New( Client, "Bearing On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearingOnOff = true } ) - Client.MenuDetailsBearingOff = MENU_CLIENT_COMMAND:New( Client, "Bearing Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearingOnOff = false } ) - - Client.MenuDistance = MENU_CLIENT:New( Client, "Set distance to plane", Client.MainMenu ) - Client.MenuDistance50 = MENU_CLIENT_COMMAND:New( Client, "50 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 50 / 1000 } ) - Client.MenuDistance100 = MENU_CLIENT_COMMAND:New( Client, "100 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 100 / 1000 } ) - Client.MenuDistance150 = MENU_CLIENT_COMMAND:New( Client, "150 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 150 / 1000 } ) - Client.MenuDistance200 = MENU_CLIENT_COMMAND:New( Client, "200 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 200 / 1000 } ) - else - if Client.MainMenu then - Client.MainMenu:Remove() - end - end - - local ClientID = Client:GetID() - self:T( ClientID ) - if not self.TrackingMissiles[ClientID] then - self.TrackingMissiles[ClientID] = {} - end - self.TrackingMissiles[ClientID].Client = Client - if not self.TrackingMissiles[ClientID].MissileData then - self.TrackingMissiles[ClientID].MissileData = {} - end -end - ---- Creates the main object which is handling missile tracking. --- When a missile is fired a SCHEDULER is set off that follows the missile. When near a certain a client player, the missile will be destroyed. --- @param #MISSILETRAINER self --- @param #number Distance The distance in meters when a tracked missile needs to be destroyed when close to a player. --- @param #string Briefing (Optional) Will show a text to the players when starting their mission. Can be used for briefing purposes. --- @return #MISSILETRAINER -function MISSILETRAINER:New( Distance, Briefing ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( Distance ) - - if Briefing then - self.Briefing = Briefing - end - - self.Schedulers = {} - self.SchedulerID = 0 - - self.MessageInterval = 2 - self.MessageLastTime = timer.getTime() - - self.Distance = Distance / 1000 - - self:HandleEvent( EVENTS.Shot ) - - self.DBClients = SET_CLIENT:New():FilterStart() - - --- for ClientID, Client in pairs( self.DBClients.Database ) do --- self:E( "ForEach:" .. Client.UnitName ) --- Client:Alive( self._Alive, self ) --- end --- - self.DBClients:ForEachClient( - function( Client ) - self:E( "ForEach:" .. Client.UnitName ) - Client:Alive( self._Alive, self ) - end - ) - - - --- self.DB:ForEachClient( --- --- @param Wrapper.Client#CLIENT Client --- function( Client ) --- --- ... actions ... --- --- end --- ) - - self.MessagesOnOff = true - - self.TrackingToAll = false - self.TrackingOnOff = true - self.TrackingFrequency = 3 - - self.AlertsToAll = true - self.AlertsHitsOnOff = true - self.AlertsLaunchesOnOff = true - - self.DetailsRangeOnOff = true - self.DetailsBearingOnOff = true - - self.MenusOnOff = true - - self.TrackingMissiles = {} - - self.TrackingScheduler = SCHEDULER:New( self, self._TrackMissiles, {}, 0.5, 0.05, 0 ) - - return self -end - --- Initialization methods. - - - ---- Sets by default the display of any message to be ON or OFF. --- @param #MISSILETRAINER self --- @param #boolean MessagesOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitMessagesOnOff( MessagesOnOff ) - self:F( MessagesOnOff ) - - self.MessagesOnOff = MessagesOnOff - if self.MessagesOnOff == true then - MESSAGE:New( "Messages ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Messages OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the missile tracking report for all players or only for those missiles targetted to you. --- @param #MISSILETRAINER self --- @param #boolean TrackingToAll true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitTrackingToAll( TrackingToAll ) - self:F( TrackingToAll ) - - self.TrackingToAll = TrackingToAll - if self.TrackingToAll == true then - MESSAGE:New( "Missile tracking to all players ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Missile tracking to all players OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the display of missile tracking report to be ON or OFF. --- @param #MISSILETRAINER self --- @param #boolean TrackingOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitTrackingOnOff( TrackingOnOff ) - self:F( TrackingOnOff ) - - self.TrackingOnOff = TrackingOnOff - if self.TrackingOnOff == true then - MESSAGE:New( "Missile tracking ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Missile tracking OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Increases, decreases the missile tracking message display frequency with the provided time interval in seconds. --- The default frequency is a 3 second interval, so the Tracking Frequency parameter specifies the increase or decrease from the default 3 seconds or the last frequency update. --- @param #MISSILETRAINER self --- @param #number TrackingFrequency Provide a negative or positive value in seconds to incraese or decrease the display frequency. --- @return #MISSILETRAINER self -function MISSILETRAINER:InitTrackingFrequency( TrackingFrequency ) - self:F( TrackingFrequency ) - - self.TrackingFrequency = self.TrackingFrequency + TrackingFrequency - if self.TrackingFrequency < 0.5 then - self.TrackingFrequency = 0.5 - end - if self.TrackingFrequency then - MESSAGE:New( "Missile tracking frequency is " .. self.TrackingFrequency .. " seconds.", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the display of alerts to be shown to all players or only to you. --- @param #MISSILETRAINER self --- @param #boolean AlertsToAll true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitAlertsToAll( AlertsToAll ) - self:F( AlertsToAll ) - - self.AlertsToAll = AlertsToAll - if self.AlertsToAll == true then - MESSAGE:New( "Alerts to all players ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Alerts to all players OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the display of hit alerts ON or OFF. --- @param #MISSILETRAINER self --- @param #boolean AlertsHitsOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitAlertsHitsOnOff( AlertsHitsOnOff ) - self:F( AlertsHitsOnOff ) - - self.AlertsHitsOnOff = AlertsHitsOnOff - if self.AlertsHitsOnOff == true then - MESSAGE:New( "Alerts Hits ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Alerts Hits OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the display of launch alerts ON or OFF. --- @param #MISSILETRAINER self --- @param #boolean AlertsLaunchesOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitAlertsLaunchesOnOff( AlertsLaunchesOnOff ) - self:F( AlertsLaunchesOnOff ) - - self.AlertsLaunchesOnOff = AlertsLaunchesOnOff - if self.AlertsLaunchesOnOff == true then - MESSAGE:New( "Alerts Launches ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Alerts Launches OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the display of range information of missiles ON of OFF. --- @param #MISSILETRAINER self --- @param #boolean DetailsRangeOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitRangeOnOff( DetailsRangeOnOff ) - self:F( DetailsRangeOnOff ) - - self.DetailsRangeOnOff = DetailsRangeOnOff - if self.DetailsRangeOnOff == true then - MESSAGE:New( "Range display ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Range display OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the display of bearing information of missiles ON of OFF. --- @param #MISSILETRAINER self --- @param #boolean DetailsBearingOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitBearingOnOff( DetailsBearingOnOff ) - self:F( DetailsBearingOnOff ) - - self.DetailsBearingOnOff = DetailsBearingOnOff - if self.DetailsBearingOnOff == true then - MESSAGE:New( "Bearing display OFF", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Bearing display OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Enables / Disables the menus. --- @param #MISSILETRAINER self --- @param #boolean MenusOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitMenusOnOff( MenusOnOff ) - self:F( MenusOnOff ) - - self.MenusOnOff = MenusOnOff - if self.MenusOnOff == true then - MESSAGE:New( "Menus are ENABLED (only when a player rejoins a slot)", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Menus are DISABLED", 15, "Menu" ):ToAll() - end - - return self -end - - --- Menu functions - -function MISSILETRAINER._MenuMessages( MenuParameters ) - - local self = MenuParameters.MenuSelf - - if MenuParameters.MessagesOnOff ~= nil then - self:InitMessagesOnOff( MenuParameters.MessagesOnOff ) - end - - if MenuParameters.TrackingToAll ~= nil then - self:InitTrackingToAll( MenuParameters.TrackingToAll ) - end - - if MenuParameters.TrackingOnOff ~= nil then - self:InitTrackingOnOff( MenuParameters.TrackingOnOff ) - end - - if MenuParameters.TrackingFrequency ~= nil then - self:InitTrackingFrequency( MenuParameters.TrackingFrequency ) - end - - if MenuParameters.AlertsToAll ~= nil then - self:InitAlertsToAll( MenuParameters.AlertsToAll ) - end - - if MenuParameters.AlertsHitsOnOff ~= nil then - self:InitAlertsHitsOnOff( MenuParameters.AlertsHitsOnOff ) - end - - if MenuParameters.AlertsLaunchesOnOff ~= nil then - self:InitAlertsLaunchesOnOff( MenuParameters.AlertsLaunchesOnOff ) - end - - if MenuParameters.DetailsRangeOnOff ~= nil then - self:InitRangeOnOff( MenuParameters.DetailsRangeOnOff ) - end - - if MenuParameters.DetailsBearingOnOff ~= nil then - self:InitBearingOnOff( MenuParameters.DetailsBearingOnOff ) - end - - if MenuParameters.Distance ~= nil then - self.Distance = MenuParameters.Distance - MESSAGE:New( "Hit detection distance set to " .. self.Distance .. " meters", 15, "Menu" ):ToAll() - end - -end - ---- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. --- @param #MISSILETRAINER self --- @param Core.Event#EVENTDATA EventData -function MISSILETRAINER:OnEventShot( EVentData ) - self:F( { EVentData } ) - - local TrainerSourceDCSUnit = EVentData.IniDCSUnit - local TrainerSourceDCSUnitName = EVentData.IniDCSUnitName - local TrainerWeapon = EVentData.Weapon -- Identify the weapon fired - local TrainerWeaponName = EVentData.WeaponName -- return weapon type - - self:T( "Missile Launched = " .. TrainerWeaponName ) - - local TrainerTargetDCSUnit = TrainerWeapon:getTarget() -- Identify target - if TrainerTargetDCSUnit then - local TrainerTargetDCSUnitName = Unit.getName( TrainerTargetDCSUnit ) - local TrainerTargetSkill = _DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill - - self:T(TrainerTargetDCSUnitName ) - - local Client = self.DBClients:FindClient( TrainerTargetDCSUnitName ) - if Client then - - local TrainerSourceUnit = UNIT:Find( TrainerSourceDCSUnit ) - local TrainerTargetUnit = UNIT:Find( TrainerTargetDCSUnit ) - - if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then - - local Message = MESSAGE:New( - string.format( "%s launched a %s", - TrainerSourceUnit:GetTypeName(), - TrainerWeaponName - ) .. self:_AddRange( Client, TrainerWeapon ) .. self:_AddBearing( Client, TrainerWeapon ), 5, "Launch Alert" ) - - if self.AlertsToAll then - Message:ToAll() - else - Message:ToClient( Client ) - end - end - - local ClientID = Client:GetID() - self:T( ClientID ) - local MissileData = {} - MissileData.TrainerSourceUnit = TrainerSourceUnit - MissileData.TrainerWeapon = TrainerWeapon - MissileData.TrainerTargetUnit = TrainerTargetUnit - MissileData.TrainerWeaponTypeName = TrainerWeapon:getTypeName() - MissileData.TrainerWeaponLaunched = true - table.insert( self.TrackingMissiles[ClientID].MissileData, MissileData ) - --self:T( self.TrackingMissiles ) - end - else - -- TODO: some weapons don't know the target unit... Need to develop a workaround for this. - if ( TrainerWeapon:getTypeName() == "9M311" ) then - SCHEDULER:New( TrainerWeapon, TrainerWeapon.destroy, {}, 1 ) - else - end - end -end - -function MISSILETRAINER:_AddRange( Client, TrainerWeapon ) - - local RangeText = "" - - if self.DetailsRangeOnOff then - - local PositionMissile = TrainerWeapon:getPoint() - local TargetVec3 = Client:GetVec3() - - local Range = ( ( PositionMissile.x - TargetVec3.x )^2 + - ( PositionMissile.y - TargetVec3.y )^2 + - ( PositionMissile.z - TargetVec3.z )^2 - ) ^ 0.5 / 1000 - - RangeText = string.format( ", at %4.2fkm", Range ) - end - - return RangeText -end - -function MISSILETRAINER:_AddBearing( Client, TrainerWeapon ) - - local BearingText = "" - - if self.DetailsBearingOnOff then - - local PositionMissile = TrainerWeapon:getPoint() - local TargetVec3 = Client:GetVec3() - - self:T2( { TargetVec3, PositionMissile }) - - local DirectionVector = { x = PositionMissile.x - TargetVec3.x, y = PositionMissile.y - TargetVec3.y, z = PositionMissile.z - TargetVec3.z } - local DirectionRadians = math.atan2( DirectionVector.z, DirectionVector.x ) - --DirectionRadians = DirectionRadians + routines.getNorthCorrection( PositionTarget ) - if DirectionRadians < 0 then - DirectionRadians = DirectionRadians + 2 * math.pi - end - local DirectionDegrees = DirectionRadians * 180 / math.pi - - BearingText = string.format( ", %d degrees", DirectionDegrees ) - end - - return BearingText -end - - -function MISSILETRAINER:_TrackMissiles() - self:F2() - - - local ShowMessages = false - if self.MessagesOnOff and self.MessageLastTime + self.TrackingFrequency <= timer.getTime() then - self.MessageLastTime = timer.getTime() - ShowMessages = true - end - - -- ALERTS PART - - -- Loop for all Player Clients to check the alerts and deletion of missiles. - for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do - - local Client = ClientData.Client - self:T2( { Client:GetName() } ) - - for MissileDataID, MissileData in pairs( ClientData.MissileData ) do - self:T3( MissileDataID ) - - local TrainerSourceUnit = MissileData.TrainerSourceUnit - local TrainerWeapon = MissileData.TrainerWeapon - local TrainerTargetUnit = MissileData.TrainerTargetUnit - local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName - local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched - - if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then - local PositionMissile = TrainerWeapon:getPosition().p - local TargetVec3 = Client:GetVec3() - - local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 + - ( PositionMissile.y - TargetVec3.y )^2 + - ( PositionMissile.z - TargetVec3.z )^2 - ) ^ 0.5 / 1000 - - if Distance <= self.Distance then - -- Hit alert - TrainerWeapon:destroy() - if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then - - self:T( "killed" ) - - local Message = MESSAGE:New( - string.format( "%s launched by %s killed %s", - TrainerWeapon:getTypeName(), - TrainerSourceUnit:GetTypeName(), - TrainerTargetUnit:GetPlayerName() - ), 15, "Hit Alert" ) - - if self.AlertsToAll == true then - Message:ToAll() - else - Message:ToClient( Client ) - end - - MissileData = nil - table.remove( ClientData.MissileData, MissileDataID ) - self:T(ClientData.MissileData) - end - end - else - if not ( TrainerWeapon and TrainerWeapon:isExist() ) then - if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then - -- Weapon does not exist anymore. Delete from Table - local Message = MESSAGE:New( - string.format( "%s launched by %s self destructed!", - TrainerWeaponTypeName, - TrainerSourceUnit:GetTypeName() - ), 5, "Tracking" ) - - if self.AlertsToAll == true then - Message:ToAll() - else - Message:ToClient( Client ) - end - end - MissileData = nil - table.remove( ClientData.MissileData, MissileDataID ) - self:T( ClientData.MissileData ) - end - end - end - end - - if ShowMessages == true and self.MessagesOnOff == true and self.TrackingOnOff == true then -- Only do this when tracking information needs to be displayed. - - -- TRACKING PART - - -- For the current client, the missile range and bearing details are displayed To the Player Client. - -- For the other clients, the missile range and bearing details are displayed To the other Player Clients. - -- To achieve this, a cross loop is done for each Player Client <-> Other Player Client missile information. - - -- Main Player Client loop - for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do - - local Client = ClientData.Client - self:T2( { Client:GetName() } ) - - - ClientData.MessageToClient = "" - ClientData.MessageToAll = "" - - -- Other Players Client loop - for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do - - for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do - self:T3( MissileDataID ) - - local TrainerSourceUnit = MissileData.TrainerSourceUnit - local TrainerWeapon = MissileData.TrainerWeapon - local TrainerTargetUnit = MissileData.TrainerTargetUnit - local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName - local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched - - if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then - - if ShowMessages == true then - local TrackingTo - TrackingTo = string.format( " -> %s", - TrainerWeaponTypeName - ) - - if ClientDataID == TrackingDataID then - if ClientData.MessageToClient == "" then - ClientData.MessageToClient = "Missiles to You:\n" - end - ClientData.MessageToClient = ClientData.MessageToClient .. TrackingTo .. self:_AddRange( ClientData.Client, TrainerWeapon ) .. self:_AddBearing( ClientData.Client, TrainerWeapon ) .. "\n" - else - if self.TrackingToAll == true then - if ClientData.MessageToAll == "" then - ClientData.MessageToAll = "Missiles to other Players:\n" - end - ClientData.MessageToAll = ClientData.MessageToAll .. TrackingTo .. self:_AddRange( ClientData.Client, TrainerWeapon ) .. self:_AddBearing( ClientData.Client, TrainerWeapon ) .. " ( " .. TrainerTargetUnit:GetPlayerName() .. " )\n" - end - end - end - end - end - end - - -- Once the Player Client and the Other Player Client tracking messages are prepared, show them. - if ClientData.MessageToClient ~= "" or ClientData.MessageToAll ~= "" then - local Message = MESSAGE:New( ClientData.MessageToClient .. ClientData.MessageToAll, 1, "Tracking" ):ToClient( Client ) - end - end - end - - return true -end ---- This module contains the AIRBASEPOLICE classes. --- --- === --- --- 1) @{AirbasePolice#AIRBASEPOLICE_BASE} class, extends @{Base#BASE} --- ================================================================== --- The @{AirbasePolice#AIRBASEPOLICE_BASE} class provides the main methods to monitor CLIENT behaviour at airbases. --- CLIENTS should not be allowed to: --- --- * Don't taxi faster than 40 km/h. --- * Don't take-off on taxiways. --- * Avoid to hit other planes on the airbase. --- * Obey ground control orders. --- --- 2) @{AirbasePolice#AIRBASEPOLICE_CAUCASUS} class, extends @{AirbasePolice#AIRBASEPOLICE_BASE} --- ============================================================================================= --- All the airbases on the caucasus map can be monitored using this class. --- If you want to monitor specific airbases, you need to use the @{#AIRBASEPOLICE_BASE.Monitor}() method, which takes a table or airbase names. --- The following names can be given: --- * AnapaVityazevo --- * Batumi --- * Beslan --- * Gelendzhik --- * Gudauta --- * Kobuleti --- * KrasnodarCenter --- * KrasnodarPashkovsky --- * Krymsk --- * Kutaisi --- * MaykopKhanskaya --- * MineralnyeVody --- * Mozdok --- * Nalchik --- * Novorossiysk --- * SenakiKolkhi --- * SochiAdler --- * Soganlug --- * SukhumiBabushara --- * TbilisiLochini --- * Vaziani --- --- 3) @{AirbasePolice#AIRBASEPOLICE_NEVADA} class, extends @{AirbasePolice#AIRBASEPOLICE_BASE} --- ============================================================================================= --- All the airbases on the NEVADA map can be monitored using this class. --- If you want to monitor specific airbases, you need to use the @{#AIRBASEPOLICE_BASE.Monitor}() method, which takes a table or airbase names. --- The following names can be given: --- * Nellis --- * McCarran --- * Creech --- * Groom Lake --- --- ### Contributions: Dutch Baron - Concept & Testing --- ### Author: FlightControl - Framework Design & Programming --- --- @module AirbasePolice - - - - - ---- @type AIRBASEPOLICE_BASE --- @field Core.Set#SET_CLIENT SetClient --- @extends Core.Base#BASE - -AIRBASEPOLICE_BASE = { - ClassName = "AIRBASEPOLICE_BASE", - SetClient = nil, - Airbases = nil, - AirbaseNames = nil, -} - - ---- Creates a new AIRBASEPOLICE_BASE object. --- @param #AIRBASEPOLICE_BASE self --- @param SetClient A SET_CLIENT object that will contain the CLIENT objects to be monitored if they follow the rules of the airbase. --- @param Airbases A table of Airbase Names. --- @return #AIRBASEPOLICE_BASE self -function AIRBASEPOLICE_BASE:New( SetClient, Airbases ) - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) - self:E( { self.ClassName, SetClient, Airbases } ) - - self.SetClient = SetClient - self.Airbases = Airbases - - for AirbaseID, Airbase in pairs( self.Airbases ) do - Airbase.ZoneBoundary = ZONE_POLYGON_BASE:New( "Boundary", Airbase.PointsBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - for PointsRunwayID, PointsRunway in pairs( Airbase.PointsRunways ) do - Airbase.ZoneRunways[PointsRunwayID] = ZONE_POLYGON_BASE:New( "Runway " .. PointsRunwayID, PointsRunway ):SmokeZone(SMOKECOLOR.Red):Flush() - end - end - --- -- Template --- local TemplateBoundary = GROUP:FindByName( "Template Boundary" ) --- self.Airbases.Template.ZoneBoundary = ZONE_POLYGON:New( "Template Boundary", TemplateBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local TemplateRunway1 = GROUP:FindByName( "Template Runway 1" ) --- self.Airbases.Template.ZoneRunways[1] = ZONE_POLYGON:New( "Template Runway 1", TemplateRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - - self.SetClient:ForEachClient( - --- @param Wrapper.Client#CLIENT Client - function( Client ) - Client:SetState( self, "Speeding", false ) - Client:SetState( self, "Warnings", 0) - Client:SetState( self, "Taxi", false ) - end - ) - - self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, {}, 0, 2, 0.05 ) - - return self -end - ---- @type AIRBASEPOLICE_BASE.AirbaseNames --- @list <#string> - ---- Monitor a table of airbase names. --- @param #AIRBASEPOLICE_BASE self --- @param #AIRBASEPOLICE_BASE.AirbaseNames AirbaseNames A list of AirbaseNames to monitor. If this parameters is nil, then all airbases will be monitored. --- @return #AIRBASEPOLICE_BASE self -function AIRBASEPOLICE_BASE:Monitor( AirbaseNames ) - - if AirbaseNames then - if type( AirbaseNames ) == "table" then - self.AirbaseNames = AirbaseNames - else - self.AirbaseNames = { AirbaseNames } - end - end -end - ---- @param #AIRBASEPOLICE_BASE self -function AIRBASEPOLICE_BASE:_AirbaseMonitor() - - for AirbaseID, Airbase in pairs( self.Airbases ) do - - if not self.AirbaseNames or self.AirbaseNames[AirbaseID] then - - self:E( AirbaseID ) - - self.SetClient:ForEachClientInZone( Airbase.ZoneBoundary, - - --- @param Wrapper.Client#CLIENT Client - function( Client ) - - self:E( Client.UnitName ) - if Client:IsAlive() then - local NotInRunwayZone = true - for ZoneRunwayID, ZoneRunway in pairs( Airbase.ZoneRunways ) do - NotInRunwayZone = ( Client:IsNotInZone( ZoneRunway ) == true ) and NotInRunwayZone or false - end - - if NotInRunwayZone then - local Taxi = self:GetState( self, "Taxi" ) - self:E( Taxi ) - if Taxi == false then - Client:Message( "Welcome at " .. AirbaseID .. ". The maximum taxiing speed is " .. Airbase.MaximumSpeed " km/h.", 20, "ATC" ) - self:SetState( self, "Taxi", true ) - end - - -- TODO: GetVelocityKMH function usage - local VelocityVec3 = Client:GetVelocity() - local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec - local Velocity = Velocity * 3.6 -- now it is in km/h. - -- MESSAGE:New( "Velocity = " .. Velocity, 1 ):ToAll() - local IsAboveRunway = Client:IsAboveRunway() - local IsOnGround = Client:InAir() == false - self:T( IsAboveRunway, IsOnGround ) - - if IsAboveRunway and IsOnGround then - - if Velocity > Airbase.MaximumSpeed then - local IsSpeeding = Client:GetState( self, "Speeding" ) - - if IsSpeeding == true then - local SpeedingWarnings = Client:GetState( self, "Warnings" ) - self:T( SpeedingWarnings ) - - if SpeedingWarnings <= 3 then - Client:Message( "You are speeding on the taxiway! Slow down or you will be removed from this airbase! Your current velocity is " .. string.format( "%2.0f km/h", Velocity ), 5, "Warning " .. SpeedingWarnings .. " / 3" ) - Client:SetState( self, "Warnings", SpeedingWarnings + 1 ) - else - MESSAGE:New( "Player " .. Client:GetPlayerName() .. " has been removed from the airbase, due to a speeding violation ...", 10, "Airbase Police" ):ToAll() - Client:Destroy() - trigger.action.setUserFlag( "AIRCRAFT_"..Client:GetID(), 100) - Client:SetState( self, "Speeding", false ) - Client:SetState( self, "Warnings", 0 ) - end - - else - Client:Message( "You are speeding on the taxiway, slow down now! Your current velocity is " .. string.format( "%2.0f km/h", Velocity ), 5, "Attention! " ) - Client:SetState( self, "Speeding", true ) - Client:SetState( self, "Warnings", 1 ) - end - - else - Client:SetState( self, "Speeding", false ) - Client:SetState( self, "Warnings", 0 ) - end - end - - else - Client:SetState( self, "Speeding", false ) - Client:SetState( self, "Warnings", 0 ) - local Taxi = self:GetState( self, "Taxi" ) - if Taxi == true then - Client:Message( "You have progressed to the runway ... Await take-off clearance ...", 20, "ATC" ) - self:SetState( self, "Taxi", false ) - end - end - end - end - ) - end - end - - return true -end - - ---- @type AIRBASEPOLICE_CAUCASUS --- @field Core.Set#SET_CLIENT SetClient --- @extends #AIRBASEPOLICE_BASE - -AIRBASEPOLICE_CAUCASUS = { - ClassName = "AIRBASEPOLICE_CAUCASUS", - Airbases = { - AnapaVityazevo = { - PointsBoundary = { - [1]={["y"]=242234.85714287,["x"]=-6616.5714285726,}, - [2]={["y"]=241060.57142858,["x"]=-5585.142857144,}, - [3]={["y"]=243806.2857143,["x"]=-3962.2857142868,}, - [4]={["y"]=245240.57142858,["x"]=-4816.5714285726,}, - [5]={["y"]=244783.42857144,["x"]=-5630.8571428583,}, - [6]={["y"]=243800.57142858,["x"]=-5065.142857144,}, - [7]={["y"]=242232.00000001,["x"]=-6622.2857142868,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=242140.57142858,["x"]=-6478.8571428583,}, - [2]={["y"]=242188.57142858,["x"]=-6522.0000000011,}, - [3]={["y"]=244124.2857143,["x"]=-4344.0000000011,}, - [4]={["y"]=244068.2857143,["x"]=-4296.5714285726,}, - [5]={["y"]=242140.57142858,["x"]=-6480.0000000011,} - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Batumi = { - PointsBoundary = { - [1]={["y"]=617567.14285714,["x"]=-355313.14285715,}, - [2]={["y"]=616181.42857142,["x"]=-354800.28571429,}, - [3]={["y"]=616007.14285714,["x"]=-355128.85714286,}, - [4]={["y"]=618230,["x"]=-356914.57142858,}, - [5]={["y"]=618727.14285714,["x"]=-356166,}, - [6]={["y"]=617572.85714285,["x"]=-355308.85714286,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=616442.28571429,["x"]=-355090.28571429,}, - [2]={["y"]=618450.57142857,["x"]=-356522,}, - [3]={["y"]=618407.71428571,["x"]=-356584.85714286,}, - [4]={["y"]=618361.99999999,["x"]=-356554.85714286,}, - [5]={["y"]=618324.85714285,["x"]=-356599.14285715,}, - [6]={["y"]=618250.57142856,["x"]=-356543.42857143,}, - [7]={["y"]=618257.7142857,["x"]=-356496.28571429,}, - [8]={["y"]=618237.7142857,["x"]=-356459.14285715,}, - [9]={["y"]=616555.71428571,["x"]=-355258.85714286,}, - [10]={["y"]=616486.28571428,["x"]=-355280.57142858,}, - [11]={["y"]=616410.57142856,["x"]=-355227.71428572,}, - [12]={["y"]=616441.99999999,["x"]=-355179.14285715,}, - [13]={["y"]=616401.99999999,["x"]=-355147.71428572,}, - [14]={["y"]=616441.42857142,["x"]=-355092.57142858,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Beslan = { - PointsBoundary = { - [1]={["y"]=842082.57142857,["x"]=-148445.14285715,}, - [2]={["y"]=845237.71428572,["x"]=-148639.71428572,}, - [3]={["y"]=845232,["x"]=-148765.42857143,}, - [4]={["y"]=844220.57142857,["x"]=-149168.28571429,}, - [5]={["y"]=843274.85714286,["x"]=-149125.42857143,}, - [6]={["y"]=842077.71428572,["x"]=-148554,}, - [7]={["y"]=842083.42857143,["x"]=-148445.42857143,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=842104.57142857,["x"]=-148460.57142857,}, - [2]={["y"]=845225.71428572,["x"]=-148656,}, - [3]={["y"]=845220.57142858,["x"]=-148750,}, - [4]={["y"]=842098.85714286,["x"]=-148556.28571429,}, - [5]={["y"]=842104,["x"]=-148460.28571429,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Gelendzhik = { - PointsBoundary = { - [1]={["y"]=297856.00000001,["x"]=-51151.428571429,}, - [2]={["y"]=299044.57142858,["x"]=-49720.000000001,}, - [3]={["y"]=298861.71428572,["x"]=-49580.000000001,}, - [4]={["y"]=298198.85714286,["x"]=-49842.857142858,}, - [5]={["y"]=297990.28571429,["x"]=-50151.428571429,}, - [6]={["y"]=297696.00000001,["x"]=-51054.285714286,}, - [7]={["y"]=297850.28571429,["x"]=-51160.000000001,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=297834.00000001,["x"]=-51107.428571429,}, - [2]={["y"]=297786.57142858,["x"]=-51068.857142858,}, - [3]={["y"]=298946.57142858,["x"]=-49686.000000001,}, - [4]={["y"]=298993.14285715,["x"]=-49725.714285715,}, - [5]={["y"]=297835.14285715,["x"]=-51107.714285715,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Gudauta = { - PointsBoundary = { - [1]={["y"]=517246.57142857,["x"]=-197850.28571429,}, - [2]={["y"]=516749.42857142,["x"]=-198070.28571429,}, - [3]={["y"]=515755.14285714,["x"]=-197598.85714286,}, - [4]={["y"]=515369.42857142,["x"]=-196538.85714286,}, - [5]={["y"]=515623.71428571,["x"]=-195618.85714286,}, - [6]={["y"]=515946.57142857,["x"]=-195510.28571429,}, - [7]={["y"]=517243.71428571,["x"]=-197858.85714286,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=517096.57142857,["x"]=-197804.57142857,}, - [2]={["y"]=515880.85714285,["x"]=-195590.28571429,}, - [3]={["y"]=515812.28571428,["x"]=-195628.85714286,}, - [4]={["y"]=517036.57142857,["x"]=-197834.57142857,}, - [5]={["y"]=517097.99999999,["x"]=-197807.42857143,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Kobuleti = { - PointsBoundary = { - [1]={["y"]=634427.71428571,["x"]=-318290.28571429,}, - [2]={["y"]=635033.42857143,["x"]=-317550.2857143,}, - [3]={["y"]=635864.85714286,["x"]=-317333.14285715,}, - [4]={["y"]=636967.71428571,["x"]=-317261.71428572,}, - [5]={["y"]=637144.85714286,["x"]=-317913.14285715,}, - [6]={["y"]=634630.57142857,["x"]=-318687.42857144,}, - [7]={["y"]=634424.85714286,["x"]=-318290.2857143,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=634509.71428571,["x"]=-318339.42857144,}, - [2]={["y"]=636767.42857143,["x"]=-317516.57142858,}, - [3]={["y"]=636790,["x"]=-317575.71428572,}, - [4]={["y"]=634531.42857143,["x"]=-318398.00000001,}, - [5]={["y"]=634510.28571429,["x"]=-318339.71428572,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - KrasnodarCenter = { - PointsBoundary = { - [1]={["y"]=366680.28571429,["x"]=11699.142857142,}, - [2]={["y"]=366654.28571429,["x"]=11225.142857142,}, - [3]={["y"]=367497.14285715,["x"]=11082.285714285,}, - [4]={["y"]=368025.71428572,["x"]=10396.57142857,}, - [5]={["y"]=369854.28571429,["x"]=11367.999999999,}, - [6]={["y"]=369840.00000001,["x"]=11910.857142856,}, - [7]={["y"]=366682.57142858,["x"]=11697.999999999,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=369205.42857144,["x"]=11789.142857142,}, - [2]={["y"]=369209.71428572,["x"]=11714.857142856,}, - [3]={["y"]=366699.71428572,["x"]=11581.714285713,}, - [4]={["y"]=366698.28571429,["x"]=11659.142857142,}, - [5]={["y"]=369208.85714286,["x"]=11788.57142857,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - KrasnodarPashkovsky = { - PointsBoundary = { - [1]={["y"]=386754,["x"]=6476.5714285703,}, - [2]={["y"]=389182.57142858,["x"]=8722.2857142846,}, - [3]={["y"]=388832.57142858,["x"]=9086.5714285703,}, - [4]={["y"]=386961.14285715,["x"]=7707.9999999989,}, - [5]={["y"]=385404,["x"]=9179.4285714274,}, - [6]={["y"]=383239.71428572,["x"]=7386.5714285703,}, - [7]={["y"]=383954,["x"]=6486.5714285703,}, - [8]={["y"]=385775.42857143,["x"]=8097.9999999989,}, - [9]={["y"]=386804,["x"]=7319.4285714274,}, - [10]={["y"]=386375.42857143,["x"]=6797.9999999989,}, - [11]={["y"]=386746.85714286,["x"]=6472.2857142846,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=385891.14285715,["x"]=8416.5714285703,}, - [2]={["y"]=385842.28571429,["x"]=8467.9999999989,}, - [3]={["y"]=384180.85714286,["x"]=6917.1428571417,}, - [4]={["y"]=384228.57142858,["x"]=6867.7142857132,}, - [5]={["y"]=385891.14285715,["x"]=8416.5714285703,}, - }, - [2] = { - [1]={["y"]=386714.85714286,["x"]=6674.857142856,}, - [2]={["y"]=386757.71428572,["x"]=6627.7142857132,}, - [3]={["y"]=389028.57142858,["x"]=8741.4285714275,}, - [4]={["y"]=388981.71428572,["x"]=8790.5714285703,}, - [5]={["y"]=386714.57142858,["x"]=6674.5714285703,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Krymsk = { - PointsBoundary = { - [1]={["y"]=293338.00000001,["x"]=-7575.4285714297,}, - [2]={["y"]=295199.42857144,["x"]=-5434.0000000011,}, - [3]={["y"]=295595.14285715,["x"]=-6239.7142857154,}, - [4]={["y"]=294152.2857143,["x"]=-8325.4285714297,}, - [5]={["y"]=293345.14285715,["x"]=-7596.8571428582,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=293522.00000001,["x"]=-7567.4285714297,}, - [2]={["y"]=293578.57142858,["x"]=-7616.0000000011,}, - [3]={["y"]=295246.00000001,["x"]=-5591.142857144,}, - [4]={["y"]=295187.71428573,["x"]=-5546.0000000011,}, - [5]={["y"]=293523.14285715,["x"]=-7568.2857142868,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Kutaisi = { - PointsBoundary = { - [1]={["y"]=682087.42857143,["x"]=-284512.85714286,}, - [2]={["y"]=685387.42857143,["x"]=-283662.85714286,}, - [3]={["y"]=685294.57142857,["x"]=-284977.14285715,}, - [4]={["y"]=682744.57142857,["x"]=-286505.71428572,}, - [5]={["y"]=682094.57142857,["x"]=-284527.14285715,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=682638,["x"]=-285202.28571429,}, - [2]={["y"]=685050.28571429,["x"]=-284507.42857144,}, - [3]={["y"]=685068.85714286,["x"]=-284578.85714286,}, - [4]={["y"]=682657.42857143,["x"]=-285264.28571429,}, - [5]={["y"]=682638.28571429,["x"]=-285202.85714286,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - MaykopKhanskaya = { - PointsBoundary = { - [1]={["y"]=456876.28571429,["x"]=-27665.42857143,}, - [2]={["y"]=457800,["x"]=-28392.857142858,}, - [3]={["y"]=459368.57142857,["x"]=-26378.571428573,}, - [4]={["y"]=459425.71428572,["x"]=-25242.857142858,}, - [5]={["y"]=458961.42857143,["x"]=-24964.285714287,}, - [6]={["y"]=456878.57142857,["x"]=-27667.714285715,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=457005.42857143,["x"]=-27668.000000001,}, - [2]={["y"]=459028.85714286,["x"]=-25168.857142858,}, - [3]={["y"]=459082.57142857,["x"]=-25216.857142858,}, - [4]={["y"]=457060,["x"]=-27714.285714287,}, - [5]={["y"]=457004.57142857,["x"]=-27669.714285715,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - MineralnyeVody = { - PointsBoundary = { - [1]={["y"]=703857.14285714,["x"]=-50226.000000002,}, - [2]={["y"]=707385.71428571,["x"]=-51911.714285716,}, - [3]={["y"]=707595.71428571,["x"]=-51434.857142859,}, - [4]={["y"]=707900,["x"]=-51568.857142859,}, - [5]={["y"]=707542.85714286,["x"]=-52326.000000002,}, - [6]={["y"]=706628.57142857,["x"]=-52568.857142859,}, - [7]={["y"]=705142.85714286,["x"]=-51790.285714288,}, - [8]={["y"]=703678.57142857,["x"]=-50611.714285716,}, - [9]={["y"]=703857.42857143,["x"]=-50226.857142859,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=703904,["x"]=-50352.571428573,}, - [2]={["y"]=707596.28571429,["x"]=-52094.571428573,}, - [3]={["y"]=707560.57142858,["x"]=-52161.714285716,}, - [4]={["y"]=703871.71428572,["x"]=-50420.571428573,}, - [5]={["y"]=703902,["x"]=-50352.000000002,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Mozdok = { - PointsBoundary = { - [1]={["y"]=832123.42857143,["x"]=-83608.571428573,}, - [2]={["y"]=835916.28571429,["x"]=-83144.285714288,}, - [3]={["y"]=835474.28571429,["x"]=-84170.571428573,}, - [4]={["y"]=832911.42857143,["x"]=-84470.571428573,}, - [5]={["y"]=832487.71428572,["x"]=-85565.714285716,}, - [6]={["y"]=831573.42857143,["x"]=-85351.42857143,}, - [7]={["y"]=832123.71428572,["x"]=-83610.285714288,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=832201.14285715,["x"]=-83699.428571431,}, - [2]={["y"]=832212.57142857,["x"]=-83780.571428574,}, - [3]={["y"]=835730.28571429,["x"]=-83335.714285717,}, - [4]={["y"]=835718.85714286,["x"]=-83246.571428574,}, - [5]={["y"]=832200.57142857,["x"]=-83700.000000002,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Nalchik = { - PointsBoundary = { - [1]={["y"]=759370,["x"]=-125502.85714286,}, - [2]={["y"]=761384.28571429,["x"]=-124177.14285714,}, - [3]={["y"]=761472.85714286,["x"]=-124325.71428572,}, - [4]={["y"]=761092.85714286,["x"]=-125048.57142857,}, - [5]={["y"]=760295.71428572,["x"]=-125685.71428572,}, - [6]={["y"]=759444.28571429,["x"]=-125734.28571429,}, - [7]={["y"]=759375.71428572,["x"]=-125511.42857143,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=759454.28571429,["x"]=-125551.42857143,}, - [2]={["y"]=759492.85714286,["x"]=-125610.85714286,}, - [3]={["y"]=761406.28571429,["x"]=-124304.28571429,}, - [4]={["y"]=761361.14285714,["x"]=-124239.71428572,}, - [5]={["y"]=759456,["x"]=-125552.57142857,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Novorossiysk = { - PointsBoundary = { - [1]={["y"]=278677.71428573,["x"]=-41656.571428572,}, - [2]={["y"]=278446.2857143,["x"]=-41453.714285715,}, - [3]={["y"]=278989.14285716,["x"]=-40188.000000001,}, - [4]={["y"]=279717.71428573,["x"]=-39968.000000001,}, - [5]={["y"]=280020.57142859,["x"]=-40208.000000001,}, - [6]={["y"]=278674.85714287,["x"]=-41660.857142858,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=278673.14285716,["x"]=-41615.142857144,}, - [2]={["y"]=278625.42857144,["x"]=-41570.571428572,}, - [3]={["y"]=279835.42857144,["x"]=-40226.000000001,}, - [4]={["y"]=279882.2857143,["x"]=-40270.000000001,}, - [5]={["y"]=278672.00000001,["x"]=-41614.857142858,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - SenakiKolkhi = { - PointsBoundary = { - [1]={["y"]=646036.57142857,["x"]=-281778.85714286,}, - [2]={["y"]=646045.14285714,["x"]=-281191.71428571,}, - [3]={["y"]=647032.28571429,["x"]=-280598.85714285,}, - [4]={["y"]=647669.42857143,["x"]=-281273.14285714,}, - [5]={["y"]=648323.71428571,["x"]=-281370.28571428,}, - [6]={["y"]=648520.85714286,["x"]=-281978.85714285,}, - [7]={["y"]=646039.42857143,["x"]=-281783.14285714,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=646060.85714285,["x"]=-281736,}, - [2]={["y"]=646056.57142857,["x"]=-281631.71428571,}, - [3]={["y"]=648442.28571428,["x"]=-281840.28571428,}, - [4]={["y"]=648432.28571428,["x"]=-281918.85714286,}, - [5]={["y"]=646063.71428571,["x"]=-281738.85714286,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - SochiAdler = { - PointsBoundary = { - [1]={["y"]=460642.28571428,["x"]=-164861.71428571,}, - [2]={["y"]=462820.85714285,["x"]=-163368.85714286,}, - [3]={["y"]=463649.42857142,["x"]=-163340.28571429,}, - [4]={["y"]=463835.14285714,["x"]=-164040.28571429,}, - [5]={["y"]=462535.14285714,["x"]=-165654.57142857,}, - [6]={["y"]=460678,["x"]=-165247.42857143,}, - [7]={["y"]=460635.14285714,["x"]=-164876,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=460831.42857143,["x"]=-165180,}, - [2]={["y"]=460878.57142857,["x"]=-165257.14285714,}, - [3]={["y"]=463663.71428571,["x"]=-163793.14285714,}, - [4]={["y"]=463612.28571428,["x"]=-163697.42857143,}, - [5]={["y"]=460831.42857143,["x"]=-165177.14285714,}, - }, - [2] = { - [1]={["y"]=460831.42857143,["x"]=-165180,}, - [2]={["y"]=460878.57142857,["x"]=-165257.14285714,}, - [3]={["y"]=463663.71428571,["x"]=-163793.14285714,}, - [4]={["y"]=463612.28571428,["x"]=-163697.42857143,}, - [5]={["y"]=460831.42857143,["x"]=-165177.14285714,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Soganlug = { - PointsBoundary = { - [1]={["y"]=894530.85714286,["x"]=-316928.28571428,}, - [2]={["y"]=896422.28571428,["x"]=-318622.57142857,}, - [3]={["y"]=896090.85714286,["x"]=-318934,}, - [4]={["y"]=894019.42857143,["x"]=-317119.71428571,}, - [5]={["y"]=894533.71428571,["x"]=-316925.42857143,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=894525.71428571,["x"]=-316964,}, - [2]={["y"]=896363.14285714,["x"]=-318634.28571428,}, - [3]={["y"]=896299.14285714,["x"]=-318702.85714286,}, - [4]={["y"]=894464,["x"]=-317031.71428571,}, - [5]={["y"]=894524.57142857,["x"]=-316963.71428571,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - SukhumiBabushara = { - PointsBoundary = { - [1]={["y"]=562541.14285714,["x"]=-219852.28571429,}, - [2]={["y"]=562691.14285714,["x"]=-219395.14285714,}, - [3]={["y"]=564326.85714286,["x"]=-219523.71428571,}, - [4]={["y"]=566262.57142857,["x"]=-221166.57142857,}, - [5]={["y"]=566069.71428571,["x"]=-221580.85714286,}, - [6]={["y"]=562534,["x"]=-219873.71428571,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=562684,["x"]=-219779.71428571,}, - [2]={["y"]=562717.71428571,["x"]=-219718,}, - [3]={["y"]=566046.85714286,["x"]=-221376.57142857,}, - [4]={["y"]=566012.28571428,["x"]=-221446.57142857,}, - [5]={["y"]=562684.57142857,["x"]=-219782.57142857,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - TbilisiLochini = { - PointsBoundary = { - [1]={["y"]=895172.85714286,["x"]=-314667.42857143,}, - [2]={["y"]=895337.42857143,["x"]=-314143.14285714,}, - [3]={["y"]=895990.28571429,["x"]=-314036,}, - [4]={["y"]=897730.28571429,["x"]=-315284.57142857,}, - [5]={["y"]=897901.71428571,["x"]=-316284.57142857,}, - [6]={["y"]=897684.57142857,["x"]=-316618.85714286,}, - [7]={["y"]=895173.14285714,["x"]=-314667.42857143,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=895261.14285715,["x"]=-314652.28571428,}, - [2]={["y"]=897654.57142857,["x"]=-316523.14285714,}, - [3]={["y"]=897711.71428571,["x"]=-316450.28571429,}, - [4]={["y"]=895327.42857143,["x"]=-314568.85714286,}, - [5]={["y"]=895261.71428572,["x"]=-314656,}, - }, - [2] = { - [1]={["y"]=895605.71428572,["x"]=-314724.57142857,}, - [2]={["y"]=897639.71428572,["x"]=-316148,}, - [3]={["y"]=897683.42857143,["x"]=-316087.14285714,}, - [4]={["y"]=895650,["x"]=-314660,}, - [5]={["y"]=895606,["x"]=-314724.85714286,} - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Vaziani = { - PointsBoundary = { - [1]={["y"]=902122,["x"]=-318163.71428572,}, - [2]={["y"]=902678.57142857,["x"]=-317594,}, - [3]={["y"]=903275.71428571,["x"]=-317405.42857143,}, - [4]={["y"]=903418.57142857,["x"]=-317891.14285714,}, - [5]={["y"]=904292.85714286,["x"]=-318748.28571429,}, - [6]={["y"]=904542,["x"]=-319740.85714286,}, - [7]={["y"]=904042,["x"]=-320166.57142857,}, - [8]={["y"]=902121.42857143,["x"]=-318164.85714286,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=902239.14285714,["x"]=-318190.85714286,}, - [2]={["y"]=904014.28571428,["x"]=-319994.57142857,}, - [3]={["y"]=904064.85714285,["x"]=-319945.14285715,}, - [4]={["y"]=902294.57142857,["x"]=-318146,}, - [5]={["y"]=902247.71428571,["x"]=-318190.85714286,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - }, -} - ---- Creates a new AIRBASEPOLICE_CAUCASUS object. --- @param #AIRBASEPOLICE_CAUCASUS self --- @param SetClient A SET_CLIENT object that will contain the CLIENT objects to be monitored if they follow the rules of the airbase. --- @return #AIRBASEPOLICE_CAUCASUS self -function AIRBASEPOLICE_CAUCASUS:New( SetClient ) - - -- Inherits from BASE - local self = BASE:Inherit( self, AIRBASEPOLICE_BASE:New( SetClient, self.Airbases ) ) - - -- -- AnapaVityazevo - -- local AnapaVityazevoBoundary = GROUP:FindByName( "AnapaVityazevo Boundary" ) - -- self.Airbases.AnapaVityazevo.ZoneBoundary = ZONE_POLYGON:New( "AnapaVityazevo Boundary", AnapaVityazevoBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local AnapaVityazevoRunway1 = GROUP:FindByName( "AnapaVityazevo Runway 1" ) - -- self.Airbases.AnapaVityazevo.ZoneRunways[1] = ZONE_POLYGON:New( "AnapaVityazevo Runway 1", AnapaVityazevoRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Batumi - -- local BatumiBoundary = GROUP:FindByName( "Batumi Boundary" ) - -- self.Airbases.Batumi.ZoneBoundary = ZONE_POLYGON:New( "Batumi Boundary", BatumiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local BatumiRunway1 = GROUP:FindByName( "Batumi Runway 1" ) - -- self.Airbases.Batumi.ZoneRunways[1] = ZONE_POLYGON:New( "Batumi Runway 1", BatumiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Beslan - -- local BeslanBoundary = GROUP:FindByName( "Beslan Boundary" ) - -- self.Airbases.Beslan.ZoneBoundary = ZONE_POLYGON:New( "Beslan Boundary", BeslanBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local BeslanRunway1 = GROUP:FindByName( "Beslan Runway 1" ) - -- self.Airbases.Beslan.ZoneRunways[1] = ZONE_POLYGON:New( "Beslan Runway 1", BeslanRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Gelendzhik - -- local GelendzhikBoundary = GROUP:FindByName( "Gelendzhik Boundary" ) - -- self.Airbases.Gelendzhik.ZoneBoundary = ZONE_POLYGON:New( "Gelendzhik Boundary", GelendzhikBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local GelendzhikRunway1 = GROUP:FindByName( "Gelendzhik Runway 1" ) - -- self.Airbases.Gelendzhik.ZoneRunways[1] = ZONE_POLYGON:New( "Gelendzhik Runway 1", GelendzhikRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Gudauta - -- local GudautaBoundary = GROUP:FindByName( "Gudauta Boundary" ) - -- self.Airbases.Gudauta.ZoneBoundary = ZONE_POLYGON:New( "Gudauta Boundary", GudautaBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local GudautaRunway1 = GROUP:FindByName( "Gudauta Runway 1" ) - -- self.Airbases.Gudauta.ZoneRunways[1] = ZONE_POLYGON:New( "Gudauta Runway 1", GudautaRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Kobuleti - -- local KobuletiBoundary = GROUP:FindByName( "Kobuleti Boundary" ) - -- self.Airbases.Kobuleti.ZoneBoundary = ZONE_POLYGON:New( "Kobuleti Boundary", KobuletiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KobuletiRunway1 = GROUP:FindByName( "Kobuleti Runway 1" ) - -- self.Airbases.Kobuleti.ZoneRunways[1] = ZONE_POLYGON:New( "Kobuleti Runway 1", KobuletiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- KrasnodarCenter - -- local KrasnodarCenterBoundary = GROUP:FindByName( "KrasnodarCenter Boundary" ) - -- self.Airbases.KrasnodarCenter.ZoneBoundary = ZONE_POLYGON:New( "KrasnodarCenter Boundary", KrasnodarCenterBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KrasnodarCenterRunway1 = GROUP:FindByName( "KrasnodarCenter Runway 1" ) - -- self.Airbases.KrasnodarCenter.ZoneRunways[1] = ZONE_POLYGON:New( "KrasnodarCenter Runway 1", KrasnodarCenterRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- KrasnodarPashkovsky - -- local KrasnodarPashkovskyBoundary = GROUP:FindByName( "KrasnodarPashkovsky Boundary" ) - -- self.Airbases.KrasnodarPashkovsky.ZoneBoundary = ZONE_POLYGON:New( "KrasnodarPashkovsky Boundary", KrasnodarPashkovskyBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KrasnodarPashkovskyRunway1 = GROUP:FindByName( "KrasnodarPashkovsky Runway 1" ) - -- self.Airbases.KrasnodarPashkovsky.ZoneRunways[1] = ZONE_POLYGON:New( "KrasnodarPashkovsky Runway 1", KrasnodarPashkovskyRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- local KrasnodarPashkovskyRunway2 = GROUP:FindByName( "KrasnodarPashkovsky Runway 2" ) - -- self.Airbases.KrasnodarPashkovsky.ZoneRunways[2] = ZONE_POLYGON:New( "KrasnodarPashkovsky Runway 2", KrasnodarPashkovskyRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Krymsk - -- local KrymskBoundary = GROUP:FindByName( "Krymsk Boundary" ) - -- self.Airbases.Krymsk.ZoneBoundary = ZONE_POLYGON:New( "Krymsk Boundary", KrymskBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KrymskRunway1 = GROUP:FindByName( "Krymsk Runway 1" ) - -- self.Airbases.Krymsk.ZoneRunways[1] = ZONE_POLYGON:New( "Krymsk Runway 1", KrymskRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Kutaisi - -- local KutaisiBoundary = GROUP:FindByName( "Kutaisi Boundary" ) - -- self.Airbases.Kutaisi.ZoneBoundary = ZONE_POLYGON:New( "Kutaisi Boundary", KutaisiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KutaisiRunway1 = GROUP:FindByName( "Kutaisi Runway 1" ) - -- self.Airbases.Kutaisi.ZoneRunways[1] = ZONE_POLYGON:New( "Kutaisi Runway 1", KutaisiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- MaykopKhanskaya - -- local MaykopKhanskayaBoundary = GROUP:FindByName( "MaykopKhanskaya Boundary" ) - -- self.Airbases.MaykopKhanskaya.ZoneBoundary = ZONE_POLYGON:New( "MaykopKhanskaya Boundary", MaykopKhanskayaBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local MaykopKhanskayaRunway1 = GROUP:FindByName( "MaykopKhanskaya Runway 1" ) - -- self.Airbases.MaykopKhanskaya.ZoneRunways[1] = ZONE_POLYGON:New( "MaykopKhanskaya Runway 1", MaykopKhanskayaRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- MineralnyeVody - -- local MineralnyeVodyBoundary = GROUP:FindByName( "MineralnyeVody Boundary" ) - -- self.Airbases.MineralnyeVody.ZoneBoundary = ZONE_POLYGON:New( "MineralnyeVody Boundary", MineralnyeVodyBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local MineralnyeVodyRunway1 = GROUP:FindByName( "MineralnyeVody Runway 1" ) - -- self.Airbases.MineralnyeVody.ZoneRunways[1] = ZONE_POLYGON:New( "MineralnyeVody Runway 1", MineralnyeVodyRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Mozdok - -- local MozdokBoundary = GROUP:FindByName( "Mozdok Boundary" ) - -- self.Airbases.Mozdok.ZoneBoundary = ZONE_POLYGON:New( "Mozdok Boundary", MozdokBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local MozdokRunway1 = GROUP:FindByName( "Mozdok Runway 1" ) - -- self.Airbases.Mozdok.ZoneRunways[1] = ZONE_POLYGON:New( "Mozdok Runway 1", MozdokRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Nalchik - -- local NalchikBoundary = GROUP:FindByName( "Nalchik Boundary" ) - -- self.Airbases.Nalchik.ZoneBoundary = ZONE_POLYGON:New( "Nalchik Boundary", NalchikBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local NalchikRunway1 = GROUP:FindByName( "Nalchik Runway 1" ) - -- self.Airbases.Nalchik.ZoneRunways[1] = ZONE_POLYGON:New( "Nalchik Runway 1", NalchikRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Novorossiysk - -- local NovorossiyskBoundary = GROUP:FindByName( "Novorossiysk Boundary" ) - -- self.Airbases.Novorossiysk.ZoneBoundary = ZONE_POLYGON:New( "Novorossiysk Boundary", NovorossiyskBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local NovorossiyskRunway1 = GROUP:FindByName( "Novorossiysk Runway 1" ) - -- self.Airbases.Novorossiysk.ZoneRunways[1] = ZONE_POLYGON:New( "Novorossiysk Runway 1", NovorossiyskRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- SenakiKolkhi - -- local SenakiKolkhiBoundary = GROUP:FindByName( "SenakiKolkhi Boundary" ) - -- self.Airbases.SenakiKolkhi.ZoneBoundary = ZONE_POLYGON:New( "SenakiKolkhi Boundary", SenakiKolkhiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local SenakiKolkhiRunway1 = GROUP:FindByName( "SenakiKolkhi Runway 1" ) - -- self.Airbases.SenakiKolkhi.ZoneRunways[1] = ZONE_POLYGON:New( "SenakiKolkhi Runway 1", SenakiKolkhiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- SochiAdler - -- local SochiAdlerBoundary = GROUP:FindByName( "SochiAdler Boundary" ) - -- self.Airbases.SochiAdler.ZoneBoundary = ZONE_POLYGON:New( "SochiAdler Boundary", SochiAdlerBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local SochiAdlerRunway1 = GROUP:FindByName( "SochiAdler Runway 1" ) - -- self.Airbases.SochiAdler.ZoneRunways[1] = ZONE_POLYGON:New( "SochiAdler Runway 1", SochiAdlerRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- local SochiAdlerRunway2 = GROUP:FindByName( "SochiAdler Runway 2" ) - -- self.Airbases.SochiAdler.ZoneRunways[2] = ZONE_POLYGON:New( "SochiAdler Runway 2", SochiAdlerRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Soganlug - -- local SoganlugBoundary = GROUP:FindByName( "Soganlug Boundary" ) - -- self.Airbases.Soganlug.ZoneBoundary = ZONE_POLYGON:New( "Soganlug Boundary", SoganlugBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local SoganlugRunway1 = GROUP:FindByName( "Soganlug Runway 1" ) - -- self.Airbases.Soganlug.ZoneRunways[1] = ZONE_POLYGON:New( "Soganlug Runway 1", SoganlugRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- SukhumiBabushara - -- local SukhumiBabusharaBoundary = GROUP:FindByName( "SukhumiBabushara Boundary" ) - -- self.Airbases.SukhumiBabushara.ZoneBoundary = ZONE_POLYGON:New( "SukhumiBabushara Boundary", SukhumiBabusharaBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local SukhumiBabusharaRunway1 = GROUP:FindByName( "SukhumiBabushara Runway 1" ) - -- self.Airbases.SukhumiBabushara.ZoneRunways[1] = ZONE_POLYGON:New( "SukhumiBabushara Runway 1", SukhumiBabusharaRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- TbilisiLochini - -- local TbilisiLochiniBoundary = GROUP:FindByName( "TbilisiLochini Boundary" ) - -- self.Airbases.TbilisiLochini.ZoneBoundary = ZONE_POLYGON:New( "TbilisiLochini Boundary", TbilisiLochiniBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local TbilisiLochiniRunway1 = GROUP:FindByName( "TbilisiLochini Runway 1" ) - -- self.Airbases.TbilisiLochini.ZoneRunways[1] = ZONE_POLYGON:New( "TbilisiLochini Runway 1", TbilisiLochiniRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- local TbilisiLochiniRunway2 = GROUP:FindByName( "TbilisiLochini Runway 2" ) - -- self.Airbases.TbilisiLochini.ZoneRunways[2] = ZONE_POLYGON:New( "TbilisiLochini Runway 2", TbilisiLochiniRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Vaziani - -- local VazianiBoundary = GROUP:FindByName( "Vaziani Boundary" ) - -- self.Airbases.Vaziani.ZoneBoundary = ZONE_POLYGON:New( "Vaziani Boundary", VazianiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local VazianiRunway1 = GROUP:FindByName( "Vaziani Runway 1" ) - -- self.Airbases.Vaziani.ZoneRunways[1] = ZONE_POLYGON:New( "Vaziani Runway 1", VazianiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - - - -- Template - -- local TemplateBoundary = GROUP:FindByName( "Template Boundary" ) - -- self.Airbases.Template.ZoneBoundary = ZONE_POLYGON:New( "Template Boundary", TemplateBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local TemplateRunway1 = GROUP:FindByName( "Template Runway 1" ) - -- self.Airbases.Template.ZoneRunways[1] = ZONE_POLYGON:New( "Template Runway 1", TemplateRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - - return self - -end - - - - ---- @type AIRBASEPOLICE_NEVADA --- @extends Functional.AirbasePolice#AIRBASEPOLICE_BASE -AIRBASEPOLICE_NEVADA = { - ClassName = "AIRBASEPOLICE_NEVADA", - Airbases = { - Nellis = { - PointsBoundary = { - [1]={["y"]=-17814.714285714,["x"]=-399823.14285714,}, - [2]={["y"]=-16875.857142857,["x"]=-398763.14285714,}, - [3]={["y"]=-16251.571428571,["x"]=-398988.85714286,}, - [4]={["y"]=-16163,["x"]=-398693.14285714,}, - [5]={["y"]=-16328.714285714,["x"]=-398034.57142857,}, - [6]={["y"]=-15943,["x"]=-397571.71428571,}, - [7]={["y"]=-15711.571428571,["x"]=-397551.71428571,}, - [8]={["y"]=-15748.714285714,["x"]=-396806,}, - [9]={["y"]=-16288.714285714,["x"]=-396517.42857143,}, - [10]={["y"]=-16751.571428571,["x"]=-396308.85714286,}, - [11]={["y"]=-17263,["x"]=-396234.57142857,}, - [12]={["y"]=-17577.285714286,["x"]=-396640.28571429,}, - [13]={["y"]=-17614.428571429,["x"]=-397400.28571429,}, - [14]={["y"]=-19405.857142857,["x"]=-399428.85714286,}, - [15]={["y"]=-19234.428571429,["x"]=-399683.14285714,}, - [16]={["y"]=-18708.714285714,["x"]=-399408.85714286,}, - [17]={["y"]=-18397.285714286,["x"]=-399657.42857143,}, - [18]={["y"]=-17814.428571429,["x"]=-399823.42857143,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=-18687,["x"]=-399380.28571429,}, - [2]={["y"]=-18620.714285714,["x"]=-399436.85714286,}, - [3]={["y"]=-16217.857142857,["x"]=-396596.85714286,}, - [4]={["y"]=-16300.142857143,["x"]=-396530,}, - [5]={["y"]=-18687,["x"]=-399380.85714286,}, - }, - [2] = { - [1]={["y"]=-18451.571428572,["x"]=-399580.57142857,}, - [2]={["y"]=-18392.142857143,["x"]=-399628.57142857,}, - [3]={["y"]=-16011,["x"]=-396806.85714286,}, - [4]={["y"]=-16074.714285714,["x"]=-396751.71428572,}, - [5]={["y"]=-18451.571428572,["x"]=-399580.85714285,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - McCarran = { - PointsBoundary = { - [1]={["y"]=-29455.285714286,["x"]=-416277.42857142,}, - [2]={["y"]=-28860.142857143,["x"]=-416492,}, - [3]={["y"]=-25044.428571429,["x"]=-416344.85714285,}, - [4]={["y"]=-24580.142857143,["x"]=-415959.14285714,}, - [5]={["y"]=-25073,["x"]=-415630.57142857,}, - [6]={["y"]=-25087.285714286,["x"]=-415130.57142857,}, - [7]={["y"]=-25830.142857143,["x"]=-414866.28571428,}, - [8]={["y"]=-26658.714285715,["x"]=-414880.57142857,}, - [9]={["y"]=-26973,["x"]=-415273.42857142,}, - [10]={["y"]=-27380.142857143,["x"]=-415187.71428571,}, - [11]={["y"]=-27715.857142857,["x"]=-414144.85714285,}, - [12]={["y"]=-27551.571428572,["x"]=-413473.42857142,}, - [13]={["y"]=-28630.142857143,["x"]=-413201.99999999,}, - [14]={["y"]=-29494.428571429,["x"]=-415437.71428571,}, - [15]={["y"]=-29455.571428572,["x"]=-416277.71428571,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=-29408.428571429,["x"]=-416016.28571428,}, - [2]={["y"]=-29408.142857144,["x"]=-416105.42857142,}, - [3]={["y"]=-24680.714285715,["x"]=-416003.14285713,}, - [4]={["y"]=-24681.857142858,["x"]=-415926.57142856,}, - [5]={["y"]=-29408.42857143,["x"]=-416016.57142856,}, - }, - [2] = { - [1]={["y"]=-28575.571428572,["x"]=-416303.14285713,}, - [2]={["y"]=-28575.571428572,["x"]=-416382.57142856,}, - [3]={["y"]=-25111.000000001,["x"]=-416309.7142857,}, - [4]={["y"]=-25111.000000001,["x"]=-416249.14285713,}, - [5]={["y"]=-28575.571428572,["x"]=-416303.7142857,}, - }, - [3] = { - [1]={["y"]=-29331.000000001,["x"]=-416275.42857141,}, - [2]={["y"]=-29259.000000001,["x"]=-416306.85714284,}, - [3]={["y"]=-28005.571428572,["x"]=-413449.7142857,}, - [4]={["y"]=-28068.714285715,["x"]=-413422.85714284,}, - [5]={["y"]=-29331.000000001,["x"]=-416275.7142857,}, - }, - [4] = { - [1]={["y"]=-29073.285714286,["x"]=-416386.57142856,}, - [2]={["y"]=-28997.285714286,["x"]=-416417.42857141,}, - [3]={["y"]=-27697.571428572,["x"]=-413464.57142856,}, - [4]={["y"]=-27767.857142858,["x"]=-413434.28571427,}, - [5]={["y"]=-29073.000000001,["x"]=-416386.85714284,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Creech = { - PointsBoundary = { - [1]={["y"]=-74522.714285715,["x"]=-360887.99999998,}, - [2]={["y"]=-74197,["x"]=-360556.57142855,}, - [3]={["y"]=-74402.714285715,["x"]=-359639.42857141,}, - [4]={["y"]=-74637,["x"]=-359279.42857141,}, - [5]={["y"]=-75759.857142857,["x"]=-359005.14285712,}, - [6]={["y"]=-75834.142857143,["x"]=-359045.14285712,}, - [7]={["y"]=-75902.714285714,["x"]=-359782.28571427,}, - [8]={["y"]=-76099.857142857,["x"]=-360399.42857141,}, - [9]={["y"]=-77314.142857143,["x"]=-360219.42857141,}, - [10]={["y"]=-77728.428571429,["x"]=-360445.14285713,}, - [11]={["y"]=-77585.571428571,["x"]=-360585.14285713,}, - [12]={["y"]=-76471.285714286,["x"]=-360819.42857141,}, - [13]={["y"]=-76325.571428571,["x"]=-360942.28571427,}, - [14]={["y"]=-74671.857142857,["x"]=-360927.7142857,}, - [15]={["y"]=-74522.714285714,["x"]=-360888.85714284,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=-74237.571428571,["x"]=-360591.7142857,}, - [2]={["y"]=-74234.428571429,["x"]=-360493.71428571,}, - [3]={["y"]=-77605.285714286,["x"]=-360399.14285713,}, - [4]={["y"]=-77608.714285715,["x"]=-360498.85714285,}, - [5]={["y"]=-74237.857142857,["x"]=-360591.7142857,}, - }, - [2] = { - [1]={["y"]=-75807.571428572,["x"]=-359073.42857142,}, - [2]={["y"]=-74770.142857144,["x"]=-360581.71428571,}, - [3]={["y"]=-74641.285714287,["x"]=-360585.42857142,}, - [4]={["y"]=-75734.142857144,["x"]=-359023.14285714,}, - [5]={["y"]=-75807.285714287,["x"]=-359073.42857142,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - GroomLake = { - PointsBoundary = { - [1]={["y"]=-88916.714285714,["x"]=-289102.28571425,}, - [2]={["y"]=-87023.571428572,["x"]=-290388.57142857,}, - [3]={["y"]=-85916.428571429,["x"]=-290674.28571428,}, - [4]={["y"]=-87645.000000001,["x"]=-286567.14285714,}, - [5]={["y"]=-88380.714285715,["x"]=-286388.57142857,}, - [6]={["y"]=-89670.714285715,["x"]=-283524.28571428,}, - [7]={["y"]=-89797.857142858,["x"]=-283567.14285714,}, - [8]={["y"]=-88635.000000001,["x"]=-286749.99999999,}, - [9]={["y"]=-89177.857142858,["x"]=-287207.14285714,}, - [10]={["y"]=-89092.142857144,["x"]=-288892.85714285,}, - [11]={["y"]=-88917.000000001,["x"]=-289102.85714285,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=-86039.000000001,["x"]=-290606.28571428,}, - [2]={["y"]=-85965.285714287,["x"]=-290573.99999999,}, - [3]={["y"]=-87692.714285715,["x"]=-286634.85714285,}, - [4]={["y"]=-87756.714285715,["x"]=-286663.99999999,}, - [5]={["y"]=-86038.714285715,["x"]=-290606.85714285,}, - }, - [2] = { - [1]={["y"]=-86808.428571429,["x"]=-290375.7142857,}, - [2]={["y"]=-86732.714285715,["x"]=-290344.28571427,}, - [3]={["y"]=-89672.714285714,["x"]=-283546.57142855,}, - [4]={["y"]=-89772.142857143,["x"]=-283587.71428569,}, - [5]={["y"]=-86808.142857143,["x"]=-290375.7142857,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - }, -} - ---- Creates a new AIRBASEPOLICE_NEVADA object. --- @param #AIRBASEPOLICE_NEVADA self --- @param SetClient A SET_CLIENT object that will contain the CLIENT objects to be monitored if they follow the rules of the airbase. --- @return #AIRBASEPOLICE_NEVADA self -function AIRBASEPOLICE_NEVADA:New( SetClient ) - - -- Inherits from BASE - local self = BASE:Inherit( self, AIRBASEPOLICE_BASE:New( SetClient, self.Airbases ) ) - --- -- Nellis --- local NellisBoundary = GROUP:FindByName( "Nellis Boundary" ) --- self.Airbases.Nellis.ZoneBoundary = ZONE_POLYGON:New( "Nellis Boundary", NellisBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local NellisRunway1 = GROUP:FindByName( "Nellis Runway 1" ) --- self.Airbases.Nellis.ZoneRunways[1] = ZONE_POLYGON:New( "Nellis Runway 1", NellisRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local NellisRunway2 = GROUP:FindByName( "Nellis Runway 2" ) --- self.Airbases.Nellis.ZoneRunways[2] = ZONE_POLYGON:New( "Nellis Runway 2", NellisRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- -- McCarran --- local McCarranBoundary = GROUP:FindByName( "McCarran Boundary" ) --- self.Airbases.McCarran.ZoneBoundary = ZONE_POLYGON:New( "McCarran Boundary", McCarranBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local McCarranRunway1 = GROUP:FindByName( "McCarran Runway 1" ) --- self.Airbases.McCarran.ZoneRunways[1] = ZONE_POLYGON:New( "McCarran Runway 1", McCarranRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local McCarranRunway2 = GROUP:FindByName( "McCarran Runway 2" ) --- self.Airbases.McCarran.ZoneRunways[2] = ZONE_POLYGON:New( "McCarran Runway 2", McCarranRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local McCarranRunway3 = GROUP:FindByName( "McCarran Runway 3" ) --- self.Airbases.McCarran.ZoneRunways[3] = ZONE_POLYGON:New( "McCarran Runway 3", McCarranRunway3 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local McCarranRunway4 = GROUP:FindByName( "McCarran Runway 4" ) --- self.Airbases.McCarran.ZoneRunways[4] = ZONE_POLYGON:New( "McCarran Runway 4", McCarranRunway4 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- -- Creech --- local CreechBoundary = GROUP:FindByName( "Creech Boundary" ) --- self.Airbases.Creech.ZoneBoundary = ZONE_POLYGON:New( "Creech Boundary", CreechBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local CreechRunway1 = GROUP:FindByName( "Creech Runway 1" ) --- self.Airbases.Creech.ZoneRunways[1] = ZONE_POLYGON:New( "Creech Runway 1", CreechRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local CreechRunway2 = GROUP:FindByName( "Creech Runway 2" ) --- self.Airbases.Creech.ZoneRunways[2] = ZONE_POLYGON:New( "Creech Runway 2", CreechRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- -- Groom Lake --- local GroomLakeBoundary = GROUP:FindByName( "GroomLake Boundary" ) --- self.Airbases.GroomLake.ZoneBoundary = ZONE_POLYGON:New( "GroomLake Boundary", GroomLakeBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local GroomLakeRunway1 = GROUP:FindByName( "GroomLake Runway 1" ) --- self.Airbases.GroomLake.ZoneRunways[1] = ZONE_POLYGON:New( "GroomLake Runway 1", GroomLakeRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local GroomLakeRunway2 = GROUP:FindByName( "GroomLake Runway 2" ) --- self.Airbases.GroomLake.ZoneRunways[2] = ZONE_POLYGON:New( "GroomLake Runway 2", GroomLakeRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - -end - - - - - - --- **Functional** - DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods. --- --- ![Banner Image](..\Presentations\DETECTION\Dia1.JPG) --- --- === --- --- DETECTION classes facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units). --- DETECTION uses the in-built detection capabilities of DCS World, but adds new functionalities. --- --- Please watch this [youtube video](https://youtu.be/C7p81dUwP-E) that explains the detection concepts. --- --- --- ### Contributions: --- --- * Mechanist : Early concept of DETECTION_AREAS. --- --- ### Authors: --- --- * FlightControl : Analysis, Design, Programming, Testing --- --- @module Detection - - -do -- DETECTION_BASE - - --- # 1) DETECTION_BASE class, extends @{Fsm#FSM} - -- - -- The DETECTION_BASE class defines the core functions to administer detected objects. - -- The DETECTION_BASE class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). - -- - -- ## 1.1) DETECTION_BASE constructor - -- - -- Construct a new DETECTION_BASE instance using the @{#DETECTION_BASE.New}() method. - -- - -- ## 1.2) DETECTION_BASE initialization - -- - -- By default, detection will return detected objects with all the detection sensors available. - -- However, you can ask how the objects were found with specific detection methods. - -- If you use one of the below methods, the detection will work with the detection method specified. - -- You can specify to apply multiple detection methods. - -- - -- Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK: - -- - -- * @{#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. - -- * @{#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. - -- * @{#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. - -- * @{#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. - -- * @{#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. - -- * @{#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. - -- - -- ## 1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list - -- - -- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later - -- of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains - -- a SET_UNIT object that contains the detected units that belong to that group. - -- - -- Derived classes will apply different methods to group the detected units. - -- Examples are per area, per quadrant, per distance, per type. - -- See further the derived DETECTION classes on which grouping methods are currently supported. - -- - -- Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class: - -- - -- * The method @{Detection#DETECTION_BASE.GetDetectedItems}() retrieves the DetectedItems[] list. - -- * A DetectedItem from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedItem}( DetectedItemIndex ). - -- Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information - -- about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem. - -- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ). - -- This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ). - -- - -- ## 1.4) Apply additional Filters to fine-tune the detected objects - -- - -- By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. - -- That being said, the DCS World detection algorithm can sometimes be unrealistic. - -- Especially for a visual detection, DCS World is able to report within 1 second a detailed detection of a group of 20 units (including types of the units) that are 10 kilometers away, using only visual capabilities. - -- Additionally, trees and other obstacles are not accounted during the DCS World detection. - -- - -- Therefore, an additional (optional) filtering has been built into the DETECTION_BASE class, that can be set for visual detected units. - -- For electronic detection, this filtering is not applied, only for visually detected targets. - -- - -- The following additional filtering can be applied for visual filtering: - -- - -- * A probability factor per kilometer distance. - -- * A probability factor based on the alpha angle between the detected object and the unit detecting. - -- A detection from a higher altitude allows for better detection than when on the ground. - -- * Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. - -- The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone. - -- - -- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. - -- Only when you experience unrealistic behaviour in your missions, these filters could be applied. - -- - -- ### 1.4.1 ) Distance visual detection probability - -- - -- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. - -- Also, the speed of accurate detection plays a role. - -- - -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. - -- - -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: - -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. - -- - -- Note that based on this probability factor, not only the detection but also the **type** of the unit will be applied! - -- - -- Use the method @{Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance. - -- - -- ### 1.4.2 ) Alpha Angle visual detection probability - -- - -- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. - -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. - -- - -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. - -- - -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: - -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% - -- - -- Use the method @{Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°. - -- - -- ### 1.4.3 ) Cloudy Zones detection probability - -- - -- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. - -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission - -- zones that reflect cloudy areas where detected units may not be so easily visually detected. - -- - -- Use the method @{Detection#DETECTION_BASE.SetZoneProbability}() to set for a defined number of zones, the probability factors. - -- - -- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take - -- from the DETECTION_BASE to calculate the presence of the detected unit within each zone. - -- Expecially for ZONE_POLYGON, try to limit the amount of nodes of the polygon! - -- - -- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for - -- AI not to detect so easily targets within a forrest or village rich area. - -- - -- ## 1.5 ) Accept / Reject detected units - -- - -- DETECTION_BASE can accept or reject successful detections based on the location of the detected object, - -- if it is located in range or located inside or outside of specific zones. - -- - -- ### 1.5.1 ) Detection acceptance of within range limit - -- - -- A range can be set that will limit a successful detection for a unit. - -- Use the method @{Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted. - -- - -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. - -- - -- -- Build a detect object. - -- local Detection = DETECTION_BASE:New( SetGroup ) - -- - -- -- This will accept detected units if the range is below 5000 meters. - -- Detection:SetAcceptRange( 5000 ) - -- - -- -- Start the Detection. - -- Detection:Start() - -- - -- - -- ### 1.5.2 ) Detection acceptance if within zone(s). - -- - -- Specific ZONE_BASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONE_BASE object(s). - -- Use the method @{Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones. - -- - -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. - -- - -- -- Search fo the zones where units are to be accepted. - -- local ZoneAccept1 = ZONE:New( "AcceptZone1" ) - -- local ZoneAccept2 = ZONE:New( "AcceptZone2" ) - -- - -- -- Build a detect object. - -- local Detection = DETECTION_BASE:New( SetGroup ) - -- - -- -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2. - -- Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) - -- - -- -- Start the Detection. - -- Detection:Start() - -- - -- ### 1.5.3 ) Detection rejectance if within zone(s). - -- - -- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s). - -- Use the method @{Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. - -- An example of how to use the method is shown below. - -- - -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. - -- - -- -- Search fo the zones where units are to be rejected. - -- local ZoneReject1 = ZONE:New( "RejectZone1" ) - -- local ZoneReject2 = ZONE:New( "RejectZone2" ) - -- - -- -- Build a detect object. - -- local Detection = DETECTION_BASE:New( SetGroup ) - -- - -- -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2. - -- Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) - -- - -- -- Start the Detection. - -- Detection:Start() - -- - -- ## 1.6) DETECTION_BASE is a Finite State Machine - -- - -- Various Events and State Transitions can be tailored using DETECTION_BASE. - -- - -- ### 1.6.1) DETECTION_BASE States - -- - -- * **Detecting**: The detection is running. - -- * **Stopped**: The detection is stopped. - -- - -- ### 1.6.2) DETECTION_BASE Events - -- - -- * **Start**: Start the detection process. - -- * **Detect**: Detect new units. - -- * **Detected**: New units have been detected. - -- * **Stop**: Stop the detection process. - -- - -- @type DETECTION_BASE - -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. - -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. - -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. - -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. - -- @field #number DetectionRun - -- @extends Core.Fsm#FSM - DETECTION_BASE = { - ClassName = "DETECTION_BASE", - DetectionSetGroup = nil, - DetectionRange = nil, - DetectedObjects = {}, - DetectionRun = 0, - DetectedObjectsIdentified = {}, - DetectedItems = {}, - } - - --- @type DETECTION_BASE.DetectedObjects - -- @list <#DETECTION_BASE.DetectedObject> - - --- @type DETECTION_BASE.DetectedObject - -- @field #string Name - -- @field #boolean Visible - -- @field #string Type - -- @field #number Distance - -- @field #boolean Identified - - --- @type DETECTION_BASE.DetectedItems - -- @list <#DETECTION_BASE.DetectedItem> - - --- @type DETECTION_BASE.DetectedItem - -- @field Core.Set#SET_UNIT Set - -- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. - -- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. - -- @field #boolean Changed Documents if the detected area has changes. - -- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). - -- @field #number ItemID -- The identifier of the detected area. - -- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. - -- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - - - --- DETECTION constructor. - -- @param #DETECTION_BASE self - -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. - -- @return #DETECTION_BASE self - function DETECTION_BASE:New( DetectionSetGroup ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_BASE - - self.DetectedItemCount = 0 - self.DetectedItemMax = 0 - self.DetectedItems = {} - - self.DetectionSetGroup = DetectionSetGroup - - self.DetectionInterval = 30 - - self:InitDetectVisual( true ) - self:InitDetectOptical( false ) - self:InitDetectRadar( false ) - self:InitDetectRWR( false ) - self:InitDetectIRST( false ) - self:InitDetectDLINK( false ) - - -- Create FSM transitions. - - self:SetStartState( "Stopped" ) - self.CountryID = DetectionSetGroup:GetFirst():GetCountry() - - self:AddTransition( "Stopped", "Start", "Detecting") - - --- OnLeave Transition Handler for State Stopped. - -- @function [parent=#DETECTION_BASE] OnLeaveStopped - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Stopped. - -- @function [parent=#DETECTION_BASE] OnEnterStopped - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Start. - -- @function [parent=#DETECTION_BASE] OnBeforeStart - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Start. - -- @function [parent=#DETECTION_BASE] OnAfterStart - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Start. - -- @function [parent=#DETECTION_BASE] Start - -- @param #DETECTION_BASE self - - --- Asynchronous Event Trigger for Event Start. - -- @function [parent=#DETECTION_BASE] __Start - -- @param #DETECTION_BASE self - -- @param #number Delay The delay in seconds. - - --- OnLeave Transition Handler for State Detecting. - -- @function [parent=#DETECTION_BASE] OnLeaveDetecting - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Detecting. - -- @function [parent=#DETECTION_BASE] OnEnterDetecting - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - self:AddTransition( "Detecting", "Detect", "Detecting" ) - self:AddTransition( "Detecting", "DetectionGroup", "Detecting" ) - - --- OnBefore Transition Handler for Event Detect. - -- @function [parent=#DETECTION_BASE] OnBeforeDetect - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Detect. - -- @function [parent=#DETECTION_BASE] OnAfterDetect - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Detect. - -- @function [parent=#DETECTION_BASE] Detect - -- @param #DETECTION_BASE self - - --- Asynchronous Event Trigger for Event Detect. - -- @function [parent=#DETECTION_BASE] __Detect - -- @param #DETECTION_BASE self - -- @param #number Delay The delay in seconds. - - - self:AddTransition( "Detecting", "Detected", "Detecting" ) - - --- OnBefore Transition Handler for Event Detected. - -- @function [parent=#DETECTION_BASE] OnBeforeDetected - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Detected. - -- @function [parent=#DETECTION_BASE] OnAfterDetected - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Detected. - -- @function [parent=#DETECTION_BASE] Detected - -- @param #DETECTION_BASE self - - --- Asynchronous Event Trigger for Event Detected. - -- @function [parent=#DETECTION_BASE] __Detected - -- @param #DETECTION_BASE self - -- @param #number Delay The delay in seconds. - - - self:AddTransition( "*", "Stop", "Stopped" ) - - --- OnBefore Transition Handler for Event Stop. - -- @function [parent=#DETECTION_BASE] OnBeforeStop - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Stop. - -- @function [parent=#DETECTION_BASE] OnAfterStop - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Stop. - -- @function [parent=#DETECTION_BASE] Stop - -- @param #DETECTION_BASE self - - --- Asynchronous Event Trigger for Event Stop. - -- @function [parent=#DETECTION_BASE] __Stop - -- @param #DETECTION_BASE self - -- @param #number Delay The delay in seconds. - - --- OnLeave Transition Handler for State Stopped. - -- @function [parent=#DETECTION_BASE] OnLeaveStopped - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Stopped. - -- @function [parent=#DETECTION_BASE] OnEnterStopped - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - return self - end - - do -- State Transition Handling - - --- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - function DETECTION_BASE:onafterStart(From,Event,To) - self:__Detect(0.1) - end - - --- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - function DETECTION_BASE:onafterDetect(From,Event,To) - self:E( {From,Event,To}) - - local DetectDelay = 0.1 - self.DetectionCount = 0 - self.DetectionRun = 0 - self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table - - self.DetectionSetGroup:Flush() - - for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - self:E( {DetectionGroupData}) - self:__DetectionGroup( DetectDelay, DetectionGroupData ) -- Process each detection asynchronously. - self.DetectionCount = self.DetectionCount + 1 - DetectDelay = DetectDelay + 0.1 - end - end - - --- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. - function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup ) - self:E( {From,Event,To}) - - self.DetectionRun = self.DetectionRun + 1 - - local HasDetectedObjects = false - - if DetectionGroup:IsAlive() then - - self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) - - local DetectionGroupName = DetectionGroup:GetName() - - local DetectedUnits = {} - - local DetectedTargets = DetectionGroup:GetDetectedTargets( - self.DetectVisual, - self.DetectOptical, - self.DetectRadar, - self.DetectIRST, - self.DetectRWR, - self.DetectDLINK - ) - - self:T( DetectedTargets ) - - for DetectionObjectID, Detection in pairs( DetectedTargets ) do - local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object - self:T2( DetectedObject ) - - if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then - - local DetectionAccepted = true - - local DetectedObjectName = DetectedObject:getName() - - local DetectedObjectVec3 = DetectedObject:getPoint() - local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } - local DetectionGroupVec3 = DetectionGroup:GetVec3() - local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z } - - local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 + - ( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 + - ( DetectedObjectVec3.z - DetectionGroupVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T( { "Detected Target", DetectionGroupName, DetectedObjectName, Distance } ) - - -- Calculate Acceptance - - if self.AcceptRange and Distance > self.AcceptRange then - DetectionAccepted = false - end - - if self.AcceptZones then - for AcceptZoneID, AcceptZone in pairs( self.AcceptZones ) do - local AcceptZone = AcceptZone -- Core.Zone#ZONE_BASE - if AcceptZone:IsPointVec2InZone( DetectedObjectVec2 ) == false then - DetectionAccepted = false - end - end - end - - if self.RejectZones then - for RejectZoneID, RejectZone in pairs( self.RejectZones ) do - local RejectZone = RejectZone -- Core.Zone#ZONE_BASE - if RejectZone:IsPointVec2InZone( DetectedObjectVec2 ) == true then - DetectionAccepted = false - end - end - end - - -- Calculate additional probabilities - - if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.DistanceProbability then - local DistanceFactor = Distance / 4 - local DistanceProbabilityReversed = ( 1 - self.DistanceProbability ) * DistanceFactor - local DistanceProbability = 1 - DistanceProbabilityReversed - DistanceProbability = DistanceProbability * 30 / 300 - local Probability = math.random() -- Selects a number between 0 and 1 - self:T( { Probability, DistanceProbability } ) - if Probability > DistanceProbability then - DetectionAccepted = false - end - end - - if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.AlphaAngleProbability then - local NormalVec2 = { x = DetectedObjectVec2.x - DetectionGroupVec2.x, y = DetectedObjectVec2.y - DetectionGroupVec2.y } - local AlphaAngle = math.atan2( NormalVec2.y, NormalVec2.x ) - local Sinus = math.sin( AlphaAngle ) - local AlphaAngleProbabilityReversed = ( 1 - self.AlphaAngleProbability ) * ( 1 - Sinus ) - local AlphaAngleProbability = 1 - AlphaAngleProbabilityReversed - - AlphaAngleProbability = AlphaAngleProbability * 30 / 300 - - local Probability = math.random() -- Selects a number between 0 and 1 - self:T( { Probability, AlphaAngleProbability } ) - if Probability > AlphaAngleProbability then - DetectionAccepted = false - end - - end - - if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.ZoneProbability then - - for ZoneDataID, ZoneData in pairs( self.ZoneProbability ) do - self:E({ZoneData}) - local ZoneObject = ZoneData[1] -- Core.Zone#ZONE_BASE - local ZoneProbability = ZoneData[2] -- #number - ZoneProbability = ZoneProbability * 30 / 300 - - if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then - local Probability = math.random() -- Selects a number between 0 and 1 - self:T( { Probability, ZoneProbability } ) - if Probability > ZoneProbability then - DetectionAccepted = false - break - end - end - end - end - - if DetectionAccepted then - - HasDetectedObjects = true - - if not self.DetectedObjects[DetectedObjectName] then - self.DetectedObjects[DetectedObjectName] = {} - end - self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName - self.DetectedObjects[DetectedObjectName].Visible = Detection.visible - self.DetectedObjects[DetectedObjectName].Type = Detection.type - self.DetectedObjects[DetectedObjectName].Distance = Distance - - local DetectedUnit = UNIT:FindByName( DetectedObjectName ) - - DetectedUnits[DetectedObjectName] = DetectedUnit - else - -- if beyond the DetectionRange then nullify... - if self.DetectedObjects[DetectedObjectName] then - self.DetectedObjects[DetectedObjectName] = nil - end - end - end - - self:T2( self.DetectedObjects ) - end - - if HasDetectedObjects then - self:__Detected( 0.1, DetectedUnits ) - end - - end - - if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then - self:__Detect( self.DetectionInterval ) - - self:T( "--> Create Detection Sets" ) - self:CreateDetectionSets() - end - - end - - - end - - do -- Initialization methods - - --- Detect Visual. - -- @param #DETECTION_BASE self - -- @param #boolean DetectVisual - -- @return #DETECTION_BASE self - function DETECTION_BASE:InitDetectVisual( DetectVisual ) - - self.DetectVisual = DetectVisual - end - - --- Detect Optical. - -- @param #DETECTION_BASE self - -- @param #boolean DetectOptical - -- @return #DETECTION_BASE self - function DETECTION_BASE:InitDetectOptical( DetectOptical ) - self:F2() - - self.DetectOptical = DetectOptical - end - - --- Detect Radar. - -- @param #DETECTION_BASE self - -- @param #boolean DetectRadar - -- @return #DETECTION_BASE self - function DETECTION_BASE:InitDetectRadar( DetectRadar ) - self:F2() - - self.DetectRadar = DetectRadar - end - - --- Detect IRST. - -- @param #DETECTION_BASE self - -- @param #boolean DetectIRST - -- @return #DETECTION_BASE self - function DETECTION_BASE:InitDetectIRST( DetectIRST ) - self:F2() - - self.DetectIRST = DetectIRST - end - - --- Detect RWR. - -- @param #DETECTION_BASE self - -- @param #boolean DetectRWR - -- @return #DETECTION_BASE self - function DETECTION_BASE:InitDetectRWR( DetectRWR ) - self:F2() - - self.DetectRWR = DetectRWR - end - - --- Detect DLINK. - -- @param #DETECTION_BASE self - -- @param #boolean DetectDLINK - -- @return #DETECTION_BASE self - function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) - self:F2() - - self.DetectDLINK = DetectDLINK - end - - end - - do - - --- Set the detection interval time in seconds. - -- @param #DETECTION_BASE self - -- @param #number DetectionInterval Interval in seconds. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetDetectionInterval( DetectionInterval ) - self:F2() - - self.DetectionInterval = DetectionInterval - - return self - end - - end - - do -- Accept / Reject detected units - - --- Accept detections if within a range in meters. - -- @param #DETECTION_BASE self - -- @param #number AcceptRange Accept a detection if the unit is within the AcceptRange in meters. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetAcceptRange( AcceptRange ) - self:F2() - - self.AcceptRange = AcceptRange - - return self - end - - --- Accept detections if within the specified zone(s). - -- @param #DETECTION_BASE self - -- @param AcceptZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetAcceptZones( AcceptZones ) - self:F2() - - if type( AcceptZones ) == "table" then - self.AcceptZones = AcceptZones - else - self.AcceptZones = { AcceptZones } - end - - return self - end - - --- Reject detections if within the specified zone(s). - -- @param #DETECTION_BASE self - -- @param RejectZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetRejectZones( RejectZones ) - self:F2() - - if type( RejectZones ) == "table" then - self.RejectZones = RejectZones - else - self.RejectZones = { RejectZones } - end - - return self - end - - end - - do -- Probability methods - - --- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. - -- Also, the speed of accurate detection plays a role. - -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. - -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: - -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. - -- @param #DETECTION_BASE self - -- @param DistanceProbability The probability factor. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetDistanceProbability( DistanceProbability ) - self:F2() - - self.DistanceProbability = DistanceProbability - - return self - end - - - --- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. - -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. - -- - -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. - -- - -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: - -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% - -- @param #DETECTION_BASE self - -- @param AlphaAngleProbability The probability factor. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetAlphaAngleProbability( AlphaAngleProbability ) - self:F2() - - self.AlphaAngleProbability = AlphaAngleProbability - - return self - end - - --- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. - -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission - -- zones that reflect cloudy areas where detected units may not be so easily visually detected. - -- @param #DETECTION_BASE self - -- @param ZoneArray Aray of a The ZONE_BASE object and a ZoneProbability pair.. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetZoneProbability( ZoneArray ) - self:F2() - - self.ZoneProbability = ZoneArray - - return self - end - - - end - - do -- Change processing - - --- Accepts changes from the detected item. - -- @param #DETECTION_BASE self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @return #DETECTION_BASE self - function DETECTION_BASE:AcceptChanges( DetectedItem ) - - DetectedItem.Changed = false - DetectedItem.Changes = {} - - return self - end - - --- Add a change to the detected zone. - -- @param #DETECTION_BASE self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @param #string ChangeCode - -- @return #DETECTION_BASE self - function DETECTION_BASE:AddChangeItem( DetectedItem, ChangeCode, ItemUnitType ) - - DetectedItem.Changed = true - local ItemID = DetectedItem.ItemID - - DetectedItem.Changes = DetectedItem.Changes or {} - DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} - DetectedItem.Changes[ChangeCode].ItemID = ItemID - DetectedItem.Changes[ChangeCode].ItemUnitType = ItemUnitType - - self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ItemUnitType } ) - - return self - end - - - --- Add a change to the detected zone. - -- @param #DETECTION_BASE self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @param #string ChangeCode - -- @param #string ChangeUnitType - -- @return #DETECTION_BASE self - function DETECTION_BASE:AddChangeUnit( DetectedItem, ChangeCode, ChangeUnitType ) - - DetectedItem.Changed = true - local ItemID = DetectedItem.ItemID - - DetectedItem.Changes = DetectedItem.Changes or {} - DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} - DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] or 0 - DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] + 1 - DetectedItem.Changes[ChangeCode].ItemID = ItemID - - self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ChangeUnitType } ) - - return self - end - - - end - - do -- Threat - - --- Returns if there are friendlies nearby the FAC units ... - -- @param #DETECTION_BASE self - -- @return #boolean trhe if there are friendlies nearby - function DETECTION_BASE:IsFriendliesNearBy( DetectedItem ) - - self:T3( DetectedItem.FriendliesNearBy ) - return DetectedItem.FriendliesNearBy or false - end - - --- Background worker function to determine if there are friendlies nearby ... - -- @param #DETECTION_BASE self - function DETECTION_BASE:ReportFriendliesNearBy( ReportGroupData ) - self:F2() - - local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem - local DetectedSet = ReportGroupData.DetectedItem.Set - local DetectedUnit = DetectedSet:GetFirst() - - DetectedItem.FriendliesNearBy = false - - if DetectedUnit then - - - local SphereSearch = { - id = world.VolumeType.SPHERE, - params = { - point = DetectedUnit:GetVec3(), - radius = 6000, - } - - } - - --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit - -- @param Wrapper.Group#GROUP ReportGroup - -- @param Set#SET_GROUP ReportSetGroup - local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) - - local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem - local DetectedSet = ReportGroupData.DetectedItem.Set - local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT - local ReportSetGroup = ReportGroupData.ReportSetGroup - - local EnemyCoalition = DetectedUnit:GetCoalition() - - local FoundUnitCoalition = FoundDCSUnit:getCoalition() - local FoundUnitName = FoundDCSUnit:getName() - local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() - local EnemyUnitName = DetectedUnit:GetName() - local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil - - self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) - - if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then - DetectedItem.FriendliesNearBy = true - return false - end - - return true - end - - world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) - end - end - - end - - --- Determines if a detected object has already been identified during detection processing. - -- @param #DETECTION_BASE self - -- @param #DETECTION_BASE.DetectedObject DetectedObject - -- @return #boolean true if already identified. - function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) - self:F3( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true - self:T3( DetectedObjectIdentified ) - return DetectedObjectIdentified - end - - --- Identifies a detected object during detection processing. - -- @param #DETECTION_BASE self - -- @param #DETECTION_BASE.DetectedObject DetectedObject - function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) - self:F( { "Identified:", DetectedObject.Name } ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = true - end - - --- UnIdentify a detected object during detection processing. - -- @param #DETECTION_BASE self - -- @param #DETECTION_BASE.DetectedObject DetectedObject - function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = false - end - - --- UnIdentify all detected objects during detection processing. - -- @param #DETECTION_BASE self - function DETECTION_BASE:UnIdentifyAllDetectedObjects() - - self.DetectedObjectsIdentified = {} -- Table will be garbage collected. - end - - --- Gets a detected object with a given name. - -- @param #DETECTION_BASE self - -- @param #string ObjectName - -- @return #DETECTION_BASE.DetectedObject - function DETECTION_BASE:GetDetectedObject( ObjectName ) - self:F( ObjectName ) - - if ObjectName then - local DetectedObject = self.DetectedObjects[ObjectName] - - -- Only return detected objects that are alive! - local DetectedUnit = UNIT:FindByName( ObjectName ) - if DetectedUnit and DetectedUnit:IsAlive() then - if self:IsDetectedObjectIdentified( DetectedObject ) == false then - return DetectedObject - end - end - end - - return nil - end - - - --- Adds a new DetectedItem to the DetectedItems list. - -- The DetectedItem is a table and contains a SET_UNIT in the field Set. - -- @param #DETECTION_BASE self - -- @param #string DetectedItemIndex The index of the DetectedItem. - -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. - -- @return #DETECTION_BASE.DetectedItem - function DETECTION_BASE:AddDetectedItem( DetectedItemIndex, Set ) - - local DetectedItem = {} - self.DetectedItemCount = self.DetectedItemCount + 1 - self.DetectedItemMax = self.DetectedItemMax + 1 - - if DetectedItemIndex then - self.DetectedItems[DetectedItemIndex] = DetectedItem - else - self.DetectedItems[self.DetectedItemCount] = DetectedItem - end - - DetectedItem.Set = Set or SET_UNIT:New() - DetectedItem.ItemID = self.DetectedItemMax - DetectedItem.Removed = false - - return DetectedItem - end - - --- Adds a new DetectedItem to the DetectedItems list. - -- The DetectedItem is a table and contains a SET_UNIT in the field Set. - -- @param #DETECTION_BASE self - -- @param #string DetectedItemIndex The index of the DetectedItem. - -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. - -- @param Core.Zone#ZONE_UNIT Zone (optional) The Zone to be added where the Units are located. - -- @return #DETECTION_BASE.DetectedItem - function DETECTION_BASE:AddDetectedItemZone( DetectedItemIndex, Set, Zone ) - - local DetectedItem = self:AddDetectedItem( DetectedItemIndex, Set ) - - DetectedItem.Zone = Zone - - return DetectedItem - end - - --- Removes an existing DetectedItem from the DetectedItems list. - -- The DetectedItem is a table and contains a SET_UNIT in the field Set. - -- @param #DETECTION_BASE self - -- @param #number DetectedItemIndex The index or position in the DetectedItems list where the item needs to be removed. - function DETECTION_BASE:RemoveDetectedItem( DetectedItemIndex ) - - self.DetectedItemCount = self.DetectedItemCount - 1 - self.DetectedItems[DetectedItemIndex] = nil - end - - - --- Get the detected @{Set#SET_BASE}s. - -- @param #DETECTION_BASE self - -- @return #DETECTION_BASE.DetectedItems - function DETECTION_BASE:GetDetectedItems() - - return self.DetectedItems - end - - --- Get the amount of SETs with detected objects. - -- @param #DETECTION_BASE self - -- @return #number Count - function DETECTION_BASE:GetDetectedItemsCount() - - local DetectedCount = self.DetectedItemCount - return DetectedCount - end - - --- Get a detected item using a given numeric index. - -- @param #DETECTION_BASE self - -- @param #number Index - -- @return #DETECTION_BASE.DetectedItem - function DETECTION_BASE:GetDetectedItem( Index ) - - local DetectedItem = self.DetectedItems[Index] - if DetectedItem then - return DetectedItem - end - - return nil - end - - --- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. - -- @param #DETECTION_BASE self - -- @param #number Index - -- @return Core.Set#SET_UNIT DetectedSet - function DETECTION_BASE:GetDetectedSet( Index ) - - local DetectedItem = self:GetDetectedItem( Index ) - local DetectedSetUnit = DetectedItem.Set - if DetectedSetUnit then - return DetectedSetUnit - end - - return nil - end - - do -- Zones - - --- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. - -- @param #DETECTION_BASE self - -- @param #number Index - -- @return Core.Zone#ZONE_UNIT DetectedZone - function DETECTION_BASE:GetDetectedZone( Index ) - - local DetectedZone = self.DetectedItems[Index].Zone - if DetectedZone then - return DetectedZone - end - - return nil - end - - end - - - --- Report summary of a detected item using a given numeric index. - -- @param #DETECTION_BASE self - -- @param Index - -- @return #string - function DETECTION_BASE:DetectedItemReportSummary( Index ) - self:F( Index ) - return nil - end - - --- Report detailed of a detectedion result. - -- @param #DETECTION_BASE self - -- @return #string - function DETECTION_BASE:DetectedReportDetailed() - self:F() - return nil - end - - --- Get the detection Groups. - -- @param #DETECTION_BASE self - -- @return Wrapper.Group#GROUP - function DETECTION_BASE:GetDetectionSetGroup() - - local DetectionSetGroup = self.DetectionSetGroup - return DetectionSetGroup - end - - --- Make a DetectionSet table. This function will be overridden in the derived clsses. - -- @param #DETECTION_BASE self - -- @return #DETECTION_BASE self - function DETECTION_BASE:CreateDetectionSets() - self:F2() - - self:E( "Error, in DETECTION_BASE class..." ) - - end - - - --- Schedule the DETECTION construction. - -- @param #DETECTION_BASE self - -- @param #number DelayTime The delay in seconds to wait the reporting. - -- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. - -- @return #DETECTION_BASE self - function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) - self:F2() - - self.ScheduleDelayTime = DelayTime - self.ScheduleRepeatInterval = RepeatInterval - - self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) - return self - end - -end - -do -- DETECTION_UNITS - - --- # 2) DETECTION_UNITS class, extends @{Detection#DETECTION_BASE} - -- - -- The DETECTION_UNITS class will detect units within the battle zone. - -- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. - -- Beware that when the amount of units detected is large, the DetectedItems list will be large also. - -- - -- @type DETECTION_UNITS - -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are detected. - -- @extends #DETECTION_BASE - DETECTION_UNITS = { - ClassName = "DETECTION_UNITS", - DetectionRange = nil, - } - - --- DETECTION_UNITS constructor. - -- @param Functional.Detection#DETECTION_UNITS self - -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. - -- @return Functional.Detection#DETECTION_UNITS self - function DETECTION_UNITS:New( DetectionSetGroup ) - - -- Inherits from DETECTION_BASE - local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_UNITS - - self._SmokeDetectedUnits = false - self._FlareDetectedUnits = false - self._SmokeDetectedZones = false - self._FlareDetectedZones = false - self._BoundDetectedZones = false - - return self - end - - --- Make text documenting the changes of the detected zone. - -- @param #DETECTION_UNITS self - -- @param #DETECTION_UNITS.DetectedItem DetectedItem - -- @return #string The Changes text - function DETECTION_UNITS:GetChangeText( DetectedItem ) - self:F( DetectedItem ) - - local MT = {} - - for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do - - if ChangeCode == "AU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." - end - - if ChangeCode == "RU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." - end - - end - - return table.concat( MT, "\n" ) - - end - - - --- Create the DetectedItems list from the DetectedObjects table. - -- For each DetectedItem, a one field array is created containing the Unit detected. - -- @param #DETECTION_UNITS self - -- @return #DETECTION_UNITS self - function DETECTION_UNITS:CreateDetectionSets() - self:F2( #self.DetectedObjects ) - - -- Loop the current detected items, and check if each object still exists and is detected. - - for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do - - local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT - local DetectedTypeName = DetectedItem.Type - - for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - - local DetectedObject = nil - self:E( DetectedUnit ) - if DetectedUnit:IsAlive() then - --self:E(DetectedUnit:GetName()) - DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) - end - if DetectedObject then - - -- Yes, the DetectedUnit is still detected or exists. Flag as identified. - self:IdentifyDetectedObject( DetectedObject ) - else - -- There was no DetectedObject, remove DetectedUnit from the Set. - self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) - DetectedItemSet:Remove( DetectedUnitName ) - end - end - end - - - -- Now we need to loop through the unidentified detected units and add these... These are all new items. - for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - - local DetectedObject = self:GetDetectedObject( DetectedUnitName ) - if DetectedObject then - self:T( { "Detected Unit #", DetectedUnitName } ) - - local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT - - if DetectedUnit then - local DetectedTypeName = DetectedUnit:GetTypeName() - local DetectedItem = self:GetDetectedItem( DetectedUnitName ) - if not DetectedItem then - self:T( "Added new DetectedItem" ) - DetectedItem = self:AddDetectedItem( DetectedUnitName ) - DetectedItem.Type = DetectedUnit:GetTypeName() - DetectedItem.Name = DetectedObjectData.Name - DetectedItem.Visible = DetectedObjectData.Visible - DetectedItem.Distance = DetectedObjectData.Distance - end - - DetectedItem.Set:AddUnit( DetectedUnit ) - self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) - end - end - end - - for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do - - local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem - local DetectedSet = DetectedItem.Set - - self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - --self:NearestFAC( DetectedItem ) - end - - end - - --- Report summary of a DetectedItem using a given numeric index. - -- @param #DETECTION_UNITS self - -- @param Index - -- @return #string - function DETECTION_UNITS:DetectedItemReportSummary( Index ) - self:F( Index ) - - local DetectedItem = self:GetDetectedItem( Index ) - local DetectedSet = self:GetDetectedSet( Index ) - - self:T( DetectedSet ) - if DetectedSet then - local ReportSummary = "" - local UnitDistanceText = "" - local UnitCategoryText = "" - - local DetectedItemUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT - - if DetectedItemUnit and DetectedItemUnit:IsAlive() then - self:T(DetectedItemUnit) - - local UnitCategoryName = DetectedItemUnit:GetCategoryName() or "" - local UnitCategoryType = DetectedItemUnit:GetTypeName() or "" - - if DetectedItem.Type and UnitCategoryName and UnitCategoryType then - UnitCategoryText = UnitCategoryName .. " (" .. UnitCategoryType .. ") at " - else - UnitCategoryText = "Unknown target at " - end - - if DetectedItem.Visible == false then - UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " estimated km" - else - UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " km, visual contact" - end - - local DetectedItemPointVec3 = DetectedItemUnit:GetPointVec3() - local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) - - local ThreatLevelA2G = DetectedItemUnit:GetThreatLevel( DetectedItem ) - - ReportSummary = string.format( - "%s - Threat [%s] (%2d) - %s%s", - DetectedItemPointLL, - string.rep( "â– ", ThreatLevelA2G ), - ThreatLevelA2G, - UnitCategoryText, - UnitDistanceText - ) - end - - self:T( ReportSummary ) - - return ReportSummary - end - end - - --- Report detailed of a detection result. - -- @param #DETECTION_UNITS self - -- @return #string - function DETECTION_UNITS:DetectedReportDetailed() - self:F() - - local Report = REPORT:New( "Detected units:" ) - for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do - local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem - local ReportSummary = self:DetectedItemReportSummary( DetectedItemID ) - Report:Add( ReportSummary ) - end - - local ReportText = Report:Text() - - return ReportText - end - -end - -do -- DETECTION_TYPES - - --- # 3) DETECTION_TYPES class, extends @{Detection#DETECTION_BASE} - -- - -- The DETECTION_TYPES class will detect units within the battle zone. - -- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. - -- Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. - -- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also. - -- - -- @type DETECTION_TYPES - -- @extends #DETECTION_BASE - DETECTION_TYPES = { - ClassName = "DETECTION_TYPES", - DetectionRange = nil, - } - - --- DETECTION_TYPES constructor. - -- @param Functional.Detection#DETECTION_TYPES self - -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Recce role. - -- @return Functional.Detection#DETECTION_TYPES self - function DETECTION_TYPES:New( DetectionSetGroup ) - - -- Inherits from DETECTION_BASE - local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_TYPES - - self._SmokeDetectedUnits = false - self._FlareDetectedUnits = false - self._SmokeDetectedZones = false - self._FlareDetectedZones = false - self._BoundDetectedZones = false - - return self - end - - --- Make text documenting the changes of the detected zone. - -- @param #DETECTION_TYPES self - -- @param #DETECTION_TYPES.DetectedItem DetectedItem - -- @return #string The Changes text - function DETECTION_TYPES:GetChangeText( DetectedItem ) - self:F( DetectedItem ) - - local MT = {} - - for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do - - if ChangeCode == "AU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." - end - - if ChangeCode == "RU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." - end - - end - - return table.concat( MT, "\n" ) - - end - - - --- Create the DetectedItems list from the DetectedObjects table. - -- For each DetectedItem, a one field array is created containing the Unit detected. - -- @param #DETECTION_TYPES self - -- @return #DETECTION_TYPES self - function DETECTION_TYPES:CreateDetectionSets() - self:F2( #self.DetectedObjects ) - - -- Loop the current detected items, and check if each object still exists and is detected. - - for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do - - local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT - local DetectedTypeName = DetectedItem.Type - - for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - - local DetectedObject = nil - if DetectedUnit:IsAlive() then - --self:E(DetectedUnit:GetName()) - DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) - end - if DetectedObject then - - -- Yes, the DetectedUnit is still detected or exists. Flag as identified. - self:IdentifyDetectedObject( DetectedObject ) - else - -- There was no DetectedObject, remove DetectedUnit from the Set. - self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) - DetectedItemSet:Remove( DetectedUnitName ) - end - end - end - - - -- Now we need to loop through the unidentified detected units and add these... These are all new items. - for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - - local DetectedObject = self:GetDetectedObject( DetectedUnitName ) - if DetectedObject then - self:T( { "Detected Unit #", DetectedUnitName } ) - - local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT - - if DetectedUnit then - local DetectedTypeName = DetectedUnit:GetTypeName() - local DetectedItem = self:GetDetectedItem( DetectedTypeName ) - if not DetectedItem then - DetectedItem = self:AddDetectedItem( DetectedTypeName ) - DetectedItem.Type = DetectedUnit:GetTypeName() - end - - DetectedItem.Set:AddUnit( DetectedUnit ) - self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) - end - end - end - - for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do - - local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem - local DetectedSet = DetectedItem.Set - - self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - --self:NearestFAC( DetectedItem ) - end - - end - - --- Report summary of a DetectedItem using a given numeric index. - -- @param #DETECTION_TYPES self - -- @param Index - -- @return #string - function DETECTION_TYPES:DetectedItemReportSummary( DetectedTypeName ) - self:F( DetectedTypeName ) - - local DetectedItem = self:GetDetectedItem( DetectedTypeName ) - local DetectedSet = self:GetDetectedSet( DetectedTypeName ) - - self:T( DetectedItem ) - if DetectedItem then - - local ThreatLevelA2G = DetectedSet:CalculateThreatLevelA2G() - local DetectedItemsCount = DetectedSet:Count() - local DetectedItemType = DetectedItem.Type - - local ReportSummary = string.format( - "Threat [%s] (%2d) - %2d of %s", - string.rep( "â– ", ThreatLevelA2G ), - ThreatLevelA2G, - DetectedItemsCount, - DetectedItemType - ) - self:T( ReportSummary ) - - return ReportSummary - end - end - - --- Report detailed of a detection result. - -- @param #DETECTION_TYPES self - -- @return #string - function DETECTION_TYPES:DetectedReportDetailed() - self:F() - - local Report = REPORT:New( "Detected types:" ) - for DetectedItemTypeName, DetectedItem in pairs( self.DetectedItems ) do - local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem - local ReportSummary = self:DetectedItemReportSummary( DetectedItemTypeName ) - Report:Add( ReportSummary ) - end - - local ReportText = Report:Text() - - return ReportText - end - -end - - -do -- DETECTION_AREAS - - --- # 4) DETECTION_AREAS class, extends @{Detection#DETECTION_BASE} - -- - -- The DETECTION_AREAS class will detect units within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s), - -- and will build a list (table) of @{Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. - -- The class is group the detected units within zones given a DetectedZoneRange parameter. - -- A set with multiple detected zones will be created as there are groups of units detected. - -- - -- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones - -- - -- The methods to manage the DetectedItems[].Set(s) are implemented in @{Detection#DECTECTION_BASE} and - -- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Detection#DETECTION_AREAS}. - -- - -- Retrieve the DetectedItems[].Set with the method @{Detection#DETECTION_BASE.GetDetectedSet}(). A @{Set#SET_UNIT} object will be returned. - -- - -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). - -- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). - -- If you want to obtain a specific zone from the DetectedZones, use the method @{Detection#DETECTION_BASE.GetDetectionZone}() with a given index. - -- - -- ## 4.4) Flare or Smoke detected units - -- - -- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. - -- - -- ## 4.5) Flare or Smoke or Bound detected zones - -- - -- Use the methods: - -- - -- * @{Detection#DETECTION_AREAS.FlareDetectedZones}() to flare in a color - -- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to smoke in a color - -- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to bound with a tire with a white flag - -- - -- the detected zones when a new detection has taken place. - -- - -- @type DETECTION_AREAS - -- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. - -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. - -- @extends #DETECTION_BASE - DETECTION_AREAS = { - ClassName = "DETECTION_AREAS", - DetectionZoneRange = nil, - } - - - --- DETECTION_AREAS constructor. - -- @param #DETECTION_AREAS self - -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. - -- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. - -- @return #DETECTION_AREAS - function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange ) - - -- Inherits from DETECTION_BASE - local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) - - self.DetectionZoneRange = DetectionZoneRange - - self._SmokeDetectedUnits = false - self._FlareDetectedUnits = false - self._SmokeDetectedZones = false - self._FlareDetectedZones = false - self._BoundDetectedZones = false - - return self - end - - --- Report summary of a detected item using a given numeric index. - -- @param #DETECTION_AREAS self - -- @param Index - -- @return #string - function DETECTION_AREAS:DetectedItemReportSummary( Index ) - self:F( Index ) - - local DetectedItem = self:GetDetectedItem( Index ) - if DetectedItem then - local DetectedSet = self:GetDetectedSet( Index ) - local ReportSummaryItem - - local DetectedZone = self:GetDetectedZone( Index ) - local DetectedItemPointVec3 = DetectedZone:GetPointVec3() - local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) - - local ThreatLevelA2G = self:GetTreatLevelA2G( DetectedItem ) - local DetectedItemsCount = DetectedSet:Count() - local DetectedItemsTypes = DetectedSet:GetTypeNames() - - local ReportSummary = string.format( - "%s - Threat [%s] (%2d) - %2d of %s", - DetectedItemPointLL, - string.rep( "â– ", ThreatLevelA2G ), - ThreatLevelA2G, - DetectedItemsCount, - DetectedItemsTypes - ) - - return ReportSummary - end - - return nil - end - - - --- Returns if there are friendlies nearby the FAC units ... - -- @param #DETECTION_AREAS self - -- @return #boolean trhe if there are friendlies nearby - function DETECTION_AREAS:IsFriendliesNearBy( DetectedItem ) - - self:T3( DetectedItem.FriendliesNearBy ) - return DetectedItem.FriendliesNearBy or false - end - - --- Calculate the maxium A2G threat level of the DetectedItem. - -- @param #DETECTION_AREAS self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedItem ) - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( DetectedItem.Set:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end - - self:T3( MaxThreatLevelA2G ) - DetectedItem.MaxThreatLevelA2G = MaxThreatLevelA2G - - end - - --- Find the nearest FAC of the DetectedItem. - -- @param #DETECTION_AREAS self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @return Wrapper.Unit#UNIT The nearest FAC unit - function DETECTION_AREAS:NearestFAC( DetectedItem ) - - local NearestFAC = nil - local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) - - for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do - local FACUnit = FACUnitData -- Wrapper.Unit#UNIT - if FACUnit:IsActive() then - local Vec3 = FACUnit:GetVec3() - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) - if Distance < MinDistance then - MinDistance = Distance - NearestFAC = FACUnit - end - end - end - end - - DetectedItem.NearestFAC = NearestFAC - - end - - --- Returns the A2G threat level of the units in the DetectedItem - -- @param #DETECTION_AREAS self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @return #number a scale from 0 to 10. - function DETECTION_AREAS:GetTreatLevelA2G( DetectedItem ) - - self:T3( DetectedItem.MaxThreatLevelA2G ) - return DetectedItem.MaxThreatLevelA2G - end - - - - --- Smoke the detected units - -- @param #DETECTION_AREAS self - -- @return #DETECTION_AREAS self - function DETECTION_AREAS:SmokeDetectedUnits() - self:F2() - - self._SmokeDetectedUnits = true - return self - end - - --- Flare the detected units - -- @param #DETECTION_AREAS self - -- @return #DETECTION_AREAS self - function DETECTION_AREAS:FlareDetectedUnits() - self:F2() - - self._FlareDetectedUnits = true - return self - end - - --- Smoke the detected zones - -- @param #DETECTION_AREAS self - -- @return #DETECTION_AREAS self - function DETECTION_AREAS:SmokeDetectedZones() - self:F2() - - self._SmokeDetectedZones = true - return self - end - - --- Flare the detected zones - -- @param #DETECTION_AREAS self - -- @return #DETECTION_AREAS self - function DETECTION_AREAS:FlareDetectedZones() - self:F2() - - self._FlareDetectedZones = true - return self - end - - --- Bound the detected zones - -- @param #DETECTION_AREAS self - -- @return #DETECTION_AREAS self - function DETECTION_AREAS:BoundDetectedZones() - self:F2() - - self._BoundDetectedZones = true - return self - end - - --- Make text documenting the changes of the detected zone. - -- @param #DETECTION_AREAS self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @return #string The Changes text - function DETECTION_AREAS:GetChangeText( DetectedItem ) - self:F( DetectedItem ) - - local MT = {} - - for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do - - if ChangeCode == "AA" then - MT[#MT+1] = "Detected new area " .. ChangeData.ItemID .. ". The center target is a " .. ChangeData.ItemUnitType .. "." - end - - if ChangeCode == "RAU" then - MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". Removed the center target." - end - - if ChangeCode == "AAU" then - MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". The new center target is a " .. ChangeData.ItemUnitType .. "." - end - - if ChangeCode == "RA" then - MT[#MT+1] = "Removed old area " .. ChangeData.ItemID .. ". No more targets in this area." - end - - if ChangeCode == "AU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Detected for area " .. ChangeData.ItemID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - if ChangeCode == "RU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Removed for area " .. ChangeData.ItemID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - end - - return table.concat( MT, "\n" ) - - end - - - --- Make a DetectionSet table. This function will be overridden in the derived clsses. - -- @param #DETECTION_AREAS self - -- @return #DETECTION_AREAS self - function DETECTION_AREAS:CreateDetectionSets() - self:F2() - - - self:T( "Checking Detected Items for new Detected Units ..." ) - -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. - -- Regroup when needed, split groups when needed. - for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do - - local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem - if DetectedItem then - - self:T( { "Detected Item ID:", DetectedItemID } ) - - - local DetectedSet = DetectedItem.Set - - local AreaExists = false -- This flag will determine of the detected area is still existing. - - -- First test if the center unit is detected in the detection area. - self:T3( { "Zone Center Unit:", DetectedItem.Zone.ZoneUNIT.UnitName } ) - local DetectedZoneObject = self:GetDetectedObject( DetectedItem.Zone.ZoneUNIT.UnitName ) - self:T3( { "Detected Zone Object:", DetectedItem.Zone:GetName(), DetectedZoneObject } ) - - if DetectedZoneObject then - - --self:IdentifyDetectedObject( DetectedZoneObject ) - AreaExists = true - - - - else - -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. - -- First remove the center unit from the set. - DetectedSet:RemoveUnitsByName( DetectedItem.Zone.ZoneUNIT.UnitName ) - - self:AddChangeItem( DetectedItem, 'RAU', "Dummy" ) - - -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) - - -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. - -- If the DetectedUnit was already identified, DetectedObject will be nil. - if DetectedObject then - self:IdentifyDetectedObject( DetectedObject ) - AreaExists = true - - DetectedItem.Zone:BoundZone( 12, self.CountryID, true) - - -- Assign the Unit as the new center unit of the detected area. - DetectedItem.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) - - self:AddChangeItem( DetectedItem, "AAU", DetectedItem.Zone.ZoneUNIT:GetTypeName() ) - - -- We don't need to add the DetectedObject to the area set, because it is already there ... - break - end - end - end - - -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. - -- Note that the position of the area may have moved due to the center unit repositioning. - -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. - if AreaExists then - - -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... - -- Those units within the zone are flagged as Identified. - -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = nil - if DetectedUnit:IsAlive() then - --self:E(DetectedUnit:GetName()) - DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) - end - if DetectedObject then - - -- Check if the DetectedUnit is within the DetectedItem.Zone - if DetectedUnit:IsInZone( DetectedItem.Zone ) then - - -- Yes, the DetectedUnit is within the DetectedItem.Zone, no changes, DetectedUnit can be kept within the Set. - self:IdentifyDetectedObject( DetectedObject ) - - else - -- No, the DetectedUnit is not within the DetectedItem.Zone, remove DetectedUnit from the Set. - DetectedSet:Remove( DetectedUnitName ) - self:AddChangeUnit( DetectedItem, "RU", DetectedUnit:GetTypeName() ) - end - - else - -- There was no DetectedObject, remove DetectedUnit from the Set. - self:AddChangeUnit( DetectedItem, "RU", "destroyed target" ) - DetectedSet:Remove( DetectedUnitName ) - - -- The DetectedObject has been identified, because it does not exist ... - -- self:IdentifyDetectedObject( DetectedObject ) - end - end - else - DetectedItem.Zone:BoundZone( 12, self.CountryID, true) - self:RemoveDetectedItem( DetectedItemID ) - self:AddChangeItem( DetectedItem, "RA" ) - end - end - end - - -- We iterated through the existing detection areas and: - -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. - -- - We recentered the detection area to new center units where it was needed. - -- - -- Now we need to loop through the unidentified detected units and see where they belong: - -- - They can be added to a new detection area and become the new center unit. - -- - They can be added to a new detection area. - for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - - local DetectedObject = self:GetDetectedObject( DetectedUnitName ) - - if DetectedObject then - - -- We found an unidentified unit outside of any existing detection area. - local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT - - local AddedToDetectionArea = false - - for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do - - local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem - if DetectedItem then - self:T( "Detection Area #" .. DetectedItem.ItemID ) - local DetectedSet = DetectedItem.Set - if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedItem.Zone ) then - self:IdentifyDetectedObject( DetectedObject ) - DetectedSet:AddUnit( DetectedUnit ) - AddedToDetectionArea = true - self:AddChangeUnit( DetectedItem, "AU", DetectedUnit:GetTypeName() ) - end - end - end - - if AddedToDetectionArea == false then - - -- New detection area - local DetectedItem = self:AddDetectedItemZone( nil, - SET_UNIT:New(), - ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) - ) - --self:E( DetectedItem.Zone.ZoneUNIT.UnitName ) - DetectedItem.Set:AddUnit( DetectedUnit ) - self:AddChangeItem( DetectedItem, "AA", DetectedUnit:GetTypeName() ) - end - end - end - - -- Now all the tests should have been build, now make some smoke and flares... - -- We also report here the friendlies within the detected areas. - - for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do - - local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem - local DetectedSet = DetectedItem.Set - local DetectedZone = DetectedItem.Zone - - self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - self:CalculateThreatLevelA2G( DetectedItem ) -- Calculate A2G threat level - self:NearestFAC( DetectedItem ) - - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedZone.ZoneUNIT:SmokeRed() - end - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit ) - if DetectedUnit:IsAlive() then - self:T( "Detected Set #" .. DetectedItem.ItemID .. ":" .. DetectedUnit:GetName() ) - if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then - DetectedUnit:FlareGreen() - end - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedUnit:SmokeGreen() - end - end - end - ) - if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then - DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) - end - if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then - DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) - end - - if DETECTION_AREAS._BoundDetectedZones or self._BoundDetectedZones then - DetectedZone:BoundZone( 12, self.CountryID ) - end - end - - end - -end ---- Single-Player:**No** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** -- **AI Balancing will replace in multi player missions --- non-occupied human slots with AI groups, in order to provide an engaging simulation environment, --- even when there are hardly any players in the mission.** --- --- ![Banner Image](..\Presentations\AI_Balancer\Dia1.JPG) --- --- === --- --- # 1) @{AI_Balancer#AI_BALANCER} class, extends @{Fsm#FSM_SET} --- --- The @{AI_Balancer#AI_BALANCER} class monitors and manages as many replacement AI groups as there are --- CLIENTS in a SET_CLIENT collection, which are not occupied by human players. --- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions. --- --- The parent class @{Fsm#FSM_SET} manages the functionality to control the Finite State Machine (FSM). --- The mission designer can tailor the behaviour of the AI_BALANCER, by defining event and state transition methods. --- An explanation about state and event transition methods can be found in the @{FSM} module documentation. --- --- The mission designer can tailor the AI_BALANCER behaviour, by implementing a state or event handling method for the following: --- --- * **@{#AI_BALANCER.OnAfterSpawned}**( AISet, From, Event, To, AIGroup ): Define to add extra logic when an AI is spawned. --- --- ## 1.1) AI_BALANCER construction --- --- Create a new AI_BALANCER object with the @{#AI_BALANCER.New}() method: --- --- ## 1.2) AI_BALANCER is a FSM --- --- ![Process](..\Presentations\AI_Balancer\Dia13.JPG) --- --- ### 1.2.1) AI_BALANCER States --- --- * **Monitoring** ( Set ): Monitoring the Set if all AI is spawned for the Clients. --- * **Spawning** ( Set, ClientName ): There is a new AI group spawned with ClientName as the name of reference. --- * **Spawned** ( Set, AIGroup ): A new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes. --- * **Destroying** ( Set, AIGroup ): The AI is being destroyed. --- * **Returning** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. Handle this state to customize the return behaviour of the AI, if any. --- --- ### 1.2.2) AI_BALANCER Events --- --- * **Monitor** ( Set ): Every 10 seconds, the Monitor event is triggered to monitor the Set. --- * **Spawn** ( Set, ClientName ): Triggers when there is a new AI group to be spawned with ClientName as the name of reference. --- * **Spawned** ( Set, AIGroup ): Triggers when a new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes. --- * **Destroy** ( Set, AIGroup ): The AI is being destroyed. --- * **Return** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. --- --- ## 1.3) AI_BALANCER spawn interval for replacement AI --- --- Use the method @{#AI_BALANCER.InitSpawnInterval}() to set the earliest and latest interval in seconds that is waited until a new replacement AI is spawned. --- --- ## 1.4) AI_BALANCER returns AI to Airbases --- --- By default, When a human player joins a slot that is AI_BALANCED, the AI group will be destroyed by default. --- However, there are 2 additional options that you can use to customize the destroy behaviour. --- When a human player joins a slot, you can configure to let the AI return to: --- --- * @{#AI_BALANCER.ReturnToHomeAirbase}: Returns the AI to the **home** @{Airbase#AIRBASE}. --- * @{#AI_BALANCER.ReturnToNearestAirbases}: Returns the AI to the **nearest friendly** @{Airbase#AIRBASE}. --- --- Note that when AI returns to an airbase, the AI_BALANCER will trigger the **Return** event and the AI will return, --- otherwise the AI_BALANCER will trigger a **Destroy** event, and the AI will be destroyed. --- --- === --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-17: There is still a problem with AI being destroyed, but not respawned. Need to check further upon that. --- --- 2017-01-08: AI_BALANCER:**InitSpawnInterval( Earliest, Latest )** added. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) --- * **SNAFU**: Had a couple of mails with the guys to validate, if the same concept in the GCI/CAP script could be reworked within MOOSE. None of the script code has been used however within the new AI_BALANCER moose class. --- --- ### Authors: --- --- * FlightControl: Framework Design & Programming and Documentation. --- --- @module AI_Balancer - ---- AI_BALANCER class --- @type AI_BALANCER --- @field Core.Set#SET_CLIENT SetClient --- @field Functional.Spawn#SPAWN SpawnAI --- @field Wrapper.Group#GROUP Test --- @extends Core.Fsm#FSM_SET -AI_BALANCER = { - ClassName = "AI_BALANCER", - PatrolZones = {}, - AIGroups = {}, - Earliest = 5, -- Earliest a new AI can be spawned is in 5 seconds. - Latest = 60, -- Latest a new AI can be spawned is in 60 seconds. -} - - - ---- Creates a new AI_BALANCER object --- @param #AI_BALANCER self --- @param Core.Set#SET_CLIENT SetClient A SET\_CLIENT object that will contain the CLIENT objects to be monitored if they are alive or not (joined by a player). --- @param Functional.Spawn#SPAWN SpawnAI The default Spawn object to spawn new AI Groups when needed. --- @return #AI_BALANCER -function AI_BALANCER:New( SetClient, SpawnAI ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM_SET:New( SET_GROUP:New() ) ) -- AI.AI_Balancer#AI_BALANCER - - -- TODO: Define the OnAfterSpawned event - self:SetStartState( "None" ) - self:AddTransition( "*", "Monitor", "Monitoring" ) - self:AddTransition( "*", "Spawn", "Spawning" ) - self:AddTransition( "Spawning", "Spawned", "Spawned" ) - self:AddTransition( "*", "Destroy", "Destroying" ) - self:AddTransition( "*", "Return", "Returning" ) - - self.SetClient = SetClient - self.SetClient:FilterOnce() - self.SpawnAI = SpawnAI - - self.SpawnQueue = {} - - self.ToNearestAirbase = false - self.ToHomeAirbase = false - - self:__Monitor( 1 ) - - return self -end - ---- Sets the earliest to the latest interval in seconds how long AI_BALANCER will wait to spawn a new AI. --- Provide 2 identical seconds if the interval should be a fixed amount of seconds. --- @param #AI_BALANCER self --- @param #number Earliest The earliest a new AI can be spawned in seconds. --- @param #number Latest The latest a new AI can be spawned in seconds. --- @return self -function AI_BALANCER:InitSpawnInterval( Earliest, Latest ) - - self.Earliest = Earliest - self.Latest = Latest - - return self -end - ---- Returns the AI to the nearest friendly @{Airbase#AIRBASE}. --- @param #AI_BALANCER self --- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}. --- @param Core.Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Set#SET_AIRBASE}s to evaluate where to return to. -function AI_BALANCER:ReturnToNearestAirbases( ReturnTresholdRange, ReturnAirbaseSet ) - - self.ToNearestAirbase = true - self.ReturnTresholdRange = ReturnTresholdRange - self.ReturnAirbaseSet = ReturnAirbaseSet -end - ---- Returns the AI to the home @{Airbase#AIRBASE}. --- @param #AI_BALANCER self --- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}. -function AI_BALANCER:ReturnToHomeAirbase( ReturnTresholdRange ) - - self.ToHomeAirbase = true - self.ReturnTresholdRange = ReturnTresholdRange -end - ---- @param #AI_BALANCER self --- @param Core.Set#SET_GROUP SetGroup --- @param #string ClientName --- @param Wrapper.Group#GROUP AIGroup -function AI_BALANCER:onenterSpawning( SetGroup, From, Event, To, ClientName ) - - -- OK, Spawn a new group from the default SpawnAI object provided. - local AIGroup = self.SpawnAI:Spawn() -- Wrapper.Group#GROUP - if AIGroup then - AIGroup:E( "Spawning new AIGroup" ) - --TODO: need to rework UnitName thing ... - - SetGroup:Add( ClientName, AIGroup ) - self.SpawnQueue[ClientName] = nil - - -- Fire the Spawned event. The first parameter is the AIGroup just Spawned. - -- Mission designers can catch this event to bind further actions to the AIGroup. - self:Spawned( AIGroup ) - end -end - ---- @param #AI_BALANCER self --- @param Core.Set#SET_GROUP SetGroup --- @param Wrapper.Group#GROUP AIGroup -function AI_BALANCER:onenterDestroying( SetGroup, From, Event, To, ClientName, AIGroup ) - - AIGroup:Destroy() - SetGroup:Flush() - SetGroup:Remove( ClientName ) - SetGroup:Flush() -end - ---- @param #AI_BALANCER self --- @param Core.Set#SET_GROUP SetGroup --- @param Wrapper.Group#GROUP AIGroup -function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup ) - - local AIGroupTemplate = AIGroup:GetTemplate() - if self.ToHomeAirbase == true then - local WayPointCount = #AIGroupTemplate.route.points - local SwitchWayPointCommand = AIGroup:CommandSwitchWayPoint( 1, WayPointCount, 1 ) - AIGroup:SetCommand( SwitchWayPointCommand ) - AIGroup:MessageToRed( "Returning to home base ...", 30 ) - else - -- Okay, we need to send this Group back to the nearest base of the Coalition of the AI. - --TODO: i need to rework the POINT_VEC2 thing. - local PointVec2 = POINT_VEC2:New( AIGroup:GetVec2().x, AIGroup:GetVec2().y ) - local ClosestAirbase = self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2( PointVec2 ) - self:T( ClosestAirbase.AirbaseName ) - AIGroup:MessageToRed( "Returning to " .. ClosestAirbase:GetName().. " ...", 30 ) - local RTBRoute = AIGroup:RouteReturnToAirbase( ClosestAirbase ) - AIGroupTemplate.route = RTBRoute - AIGroup:Respawn( AIGroupTemplate ) - end - -end - - ---- @param #AI_BALANCER self -function AI_BALANCER:onenterMonitoring( SetGroup ) - - self:T2( { self.SetClient:Count() } ) - --self.SetClient:Flush() - - self.SetClient:ForEachClient( - --- @param Wrapper.Client#CLIENT Client - function( Client ) - self:T3(Client.ClientName) - - local AIGroup = self.Set:Get( Client.UnitName ) -- Wrapper.Group#GROUP - if Client:IsAlive() then - - if AIGroup and AIGroup:IsAlive() == true then - - if self.ToNearestAirbase == false and self.ToHomeAirbase == false then - self:Destroy( Client.UnitName, AIGroup ) - else - -- We test if there is no other CLIENT within the self.ReturnTresholdRange of the first unit of the AI group. - -- If there is a CLIENT, the AI stays engaged and will not return. - -- If there is no CLIENT within the self.ReturnTresholdRange, then the unit will return to the Airbase return method selected. - - local PlayerInRange = { Value = false } - local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnTresholdRange ) - - self:T2( RangeZone ) - - _DATABASE:ForEachPlayer( - --- @param Wrapper.Unit#UNIT RangeTestUnit - function( RangeTestUnit, RangeZone, AIGroup, PlayerInRange ) - self:T2( { PlayerInRange, RangeTestUnit.UnitName, RangeZone.ZoneName } ) - if RangeTestUnit:IsInZone( RangeZone ) == true then - self:T2( "in zone" ) - if RangeTestUnit:GetCoalition() ~= AIGroup:GetCoalition() then - self:T2( "in range" ) - PlayerInRange.Value = true - end - end - end, - - --- @param Core.Zone#ZONE_RADIUS RangeZone - -- @param Wrapper.Group#GROUP AIGroup - function( RangeZone, AIGroup, PlayerInRange ) - if PlayerInRange.Value == false then - self:Return( AIGroup ) - end - end - , RangeZone, AIGroup, PlayerInRange - ) - - end - self.Set:Remove( Client.UnitName ) - end - else - if not AIGroup or not AIGroup:IsAlive() == true then - self:T( "Client " .. Client.UnitName .. " not alive." ) - if not self.SpawnQueue[Client.UnitName] then - -- Spawn a new AI taking into account the spawn interval Earliest, Latest - self:__Spawn( math.random( self.Earliest, self.Latest ), Client.UnitName ) - self.SpawnQueue[Client.UnitName] = true - self:E( "New AI Spawned for Client " .. Client.UnitName ) - end - end - end - return true - end - ) - - self:__Monitor( 10 ) -end - - - ---- **AI** -- **Air Patrolling or Staging.** --- --- ![Banner Image](..\Presentations\AI_PATROL\Dia1.JPG) --- --- === --- --- AI PATROL classes makes AI Controllables execute an Patrol. --- --- There are the following types of PATROL classes defined: --- --- * @{#AI_PATROL_ZONE}: Perform a PATROL in a zone. --- --- ==== --- --- # **OPEN ISSUES** --- --- 2017-01-17: When Spawned AI is located at an airbase, it will be routed first back to the airbase after take-off. --- --- 2016-01-17: --- -- Fixed problem with AI returning to base too early and unexpected. --- -- ReSpawning of AI will reset the AI_PATROL and derived classes. --- -- Checked the correct workings of SCHEDULER, and it DOES work correctly. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-17: Rename of class: **AI\_PATROL\_ZONE** is the new name for the old _AI\_PATROLZONE_. --- --- 2017-01-15: Complete revision. AI_PATROL_ZONE is the base class for other AI_PATROL like classes. --- --- 2016-09-01: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. --- --- ### Authors: --- --- * **FlightControl**: Design & Programming. --- --- @module AI_Patrol - ---- AI_PATROL_ZONE class --- @type AI_PATROL_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @field Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @field Functional.Spawn#SPAWN CoordTest --- @extends Core.Fsm#FSM_CONTROLLABLE - ---- # 1) @{#AI_PATROL_ZONE} class, extends @{Fsm#FSM_CONTROLLABLE} --- --- The @{#AI_PATROL_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}. --- --- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) --- --- The AI_PATROL_ZONE is assigned a @{Group} and this must be done before the AI_PATROL_ZONE process can be started using the **Start** event. --- --- ![Process](..\Presentations\AI_PATROL\Dia4.JPG) --- --- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. --- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. --- --- ![Process](..\Presentations\AI_PATROL\Dia5.JPG) --- --- This cycle will continue. --- --- ![Process](..\Presentations\AI_PATROL\Dia6.JPG) --- --- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. --- --- ![Process](..\Presentations\AI_PATROL\Dia9.JPG) --- ----- Note that the enemy is not engaged! To model enemy engagement, either tailor the **Detected** event, or --- use derived AI_ classes to model AI offensive or defensive behaviour. --- --- ![Process](..\Presentations\AI_PATROL\Dia10.JPG) --- --- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. --- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. --- --- ![Process](..\Presentations\AI_PATROL\Dia11.JPG) --- --- ## 1.1) AI_PATROL_ZONE constructor --- --- * @{#AI_PATROL_ZONE.New}(): Creates a new AI_PATROL_ZONE object. --- --- ## 1.2) AI_PATROL_ZONE is a FSM --- --- ![Process](..\Presentations\AI_PATROL\Dia2.JPG) --- --- ### 1.2.1) AI_PATROL_ZONE States --- --- * **None** ( Group ): The process is not started yet. --- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. --- * **Returning** ( Group ): The AI is returning to Base. --- * **Stopped** ( Group ): The process is stopped. --- * **Crashed** ( Group ): The AI has crashed or is dead. --- --- ### 1.2.2) AI_PATROL_ZONE Events --- --- * **Start** ( Group ): Start the process. --- * **Stop** ( Group ): Stop the process. --- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone. --- * **RTB** ( Group ): Route the AI to the home base. --- * **Detect** ( Group ): The AI is detecting targets. --- * **Detected** ( Group ): The AI has detected new targets. --- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. --- --- ## 1.3) Set or Get the AI controllable --- --- * @{#AI_PATROL_ZONE.SetControllable}(): Set the AIControllable. --- * @{#AI_PATROL_ZONE.GetControllable}(): Get the AIControllable. --- --- ## 1.4) Set the Speed and Altitude boundaries of the AI controllable --- --- * @{#AI_PATROL_ZONE.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol. --- * @{#AI_PATROL_ZONE.SetAltitude}(): Set altitude boundaries of the AI, for the next patrol. --- --- ## 1.5) Manage the detection process of the AI controllable --- --- The detection process of the AI controllable can be manipulated. --- Detection requires an amount of CPU power, which has an impact on your mission performance. --- Only put detection on when absolutely necessary, and the frequency of the detection can also be set. --- --- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets. --- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased. --- --- The detection frequency can be set with @{#AI_PATROL_ZONE.SetDetectionInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. --- Use the method @{#AI_PATROL_ZONE.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI. --- --- The detection can be filtered to potential targets in a specific zone. --- Use the method @{#AI_PATROL_ZONE.SetDetectionZone}() to set the zone where targets need to be detected. --- Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected --- according the weather conditions. --- --- ## 1.6) Manage the "out of fuel" in the AI_PATROL_ZONE --- --- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. --- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. --- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, --- while a new AI is targetted to the AI_PATROL_ZONE. --- Once the time is finished, the old AI will return to the base. --- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this proces in place. --- --- ## 1.7) Manage "damage" behaviour of the AI in the AI_PATROL_ZONE --- --- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on. --- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). --- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this proces in place. --- --- === --- --- @field #AI_PATROL_ZONE AI_PATROL_ZONE --- -AI_PATROL_ZONE = { - ClassName = "AI_PATROL_ZONE", -} - ---- Creates a new AI_PATROL_ZONE object --- @param #AI_PATROL_ZONE self --- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO --- @return #AI_PATROL_ZONE self --- @usage --- -- Define a new AI_PATROL_ZONE Object. This PatrolArea will patrol an AIControllable within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h. --- PatrolZone = ZONE:New( 'PatrolZone' ) --- PatrolSpawn = SPAWN:New( 'Patrol Group' ) --- PatrolArea = AI_PATROL_ZONE:New( PatrolZone, 3000, 6000, 600, 900 ) -function AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_PATROL_ZONE - - - self.PatrolZone = PatrolZone - self.PatrolFloorAltitude = PatrolFloorAltitude - self.PatrolCeilingAltitude = PatrolCeilingAltitude - self.PatrolMinSpeed = PatrolMinSpeed - self.PatrolMaxSpeed = PatrolMaxSpeed - - -- defafult PatrolAltType to "RADIO" if not specified - self.PatrolAltType = PatrolAltType or "RADIO" - - self:SetDetectionInterval( 30 ) - - self.CheckStatus = true - - self:ManageFuel( .2, 60 ) - self:ManageDamage( 1 ) - - - self.DetectedUnits = {} -- This table contains the targets detected during patrol. - - self:SetStartState( "None" ) - - self:AddTransition( "*", "Stop", "Stopped" ) - ---- OnLeave Transition Handler for State Stopped. --- @function [parent=#AI_PATROL_ZONE] OnLeaveStopped --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnEnter Transition Handler for State Stopped. --- @function [parent=#AI_PATROL_ZONE] OnEnterStopped --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- OnBefore Transition Handler for Event Stop. --- @function [parent=#AI_PATROL_ZONE] OnBeforeStop --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event Stop. --- @function [parent=#AI_PATROL_ZONE] OnAfterStop --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event Stop. --- @function [parent=#AI_PATROL_ZONE] Stop --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event Stop. --- @function [parent=#AI_PATROL_ZONE] __Stop --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - - self:AddTransition( "None", "Start", "Patrolling" ) - ---- OnBefore Transition Handler for Event Start. --- @function [parent=#AI_PATROL_ZONE] OnBeforeStart --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event Start. --- @function [parent=#AI_PATROL_ZONE] OnAfterStart --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event Start. --- @function [parent=#AI_PATROL_ZONE] Start --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event Start. --- @function [parent=#AI_PATROL_ZONE] __Start --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - ---- OnLeave Transition Handler for State Patrolling. --- @function [parent=#AI_PATROL_ZONE] OnLeavePatrolling --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnEnter Transition Handler for State Patrolling. --- @function [parent=#AI_PATROL_ZONE] OnEnterPatrolling --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - - self:AddTransition( "Patrolling", "Route", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE. - ---- OnBefore Transition Handler for Event Route. --- @function [parent=#AI_PATROL_ZONE] OnBeforeRoute --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event Route. --- @function [parent=#AI_PATROL_ZONE] OnAfterRoute --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event Route. --- @function [parent=#AI_PATROL_ZONE] Route --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event Route. --- @function [parent=#AI_PATROL_ZONE] __Route --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE. - ---- OnBefore Transition Handler for Event Status. --- @function [parent=#AI_PATROL_ZONE] OnBeforeStatus --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event Status. --- @function [parent=#AI_PATROL_ZONE] OnAfterStatus --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event Status. --- @function [parent=#AI_PATROL_ZONE] Status --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event Status. --- @function [parent=#AI_PATROL_ZONE] __Status --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Detect", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE. - ---- OnBefore Transition Handler for Event Detect. --- @function [parent=#AI_PATROL_ZONE] OnBeforeDetect --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event Detect. --- @function [parent=#AI_PATROL_ZONE] OnAfterDetect --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event Detect. --- @function [parent=#AI_PATROL_ZONE] Detect --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event Detect. --- @function [parent=#AI_PATROL_ZONE] __Detect --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Detected", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE. - ---- OnBefore Transition Handler for Event Detected. --- @function [parent=#AI_PATROL_ZONE] OnBeforeDetected --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event Detected. --- @function [parent=#AI_PATROL_ZONE] OnAfterDetected --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event Detected. --- @function [parent=#AI_PATROL_ZONE] Detected --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event Detected. --- @function [parent=#AI_PATROL_ZONE] __Detected --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "RTB", "Returning" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE. - ---- OnBefore Transition Handler for Event RTB. --- @function [parent=#AI_PATROL_ZONE] OnBeforeRTB --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event RTB. --- @function [parent=#AI_PATROL_ZONE] OnAfterRTB --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event RTB. --- @function [parent=#AI_PATROL_ZONE] RTB --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event RTB. --- @function [parent=#AI_PATROL_ZONE] __RTB --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - ---- OnLeave Transition Handler for State Returning. --- @function [parent=#AI_PATROL_ZONE] OnLeaveReturning --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnEnter Transition Handler for State Returning. --- @function [parent=#AI_PATROL_ZONE] OnEnterReturning --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - - self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE. - - self:AddTransition( "*", "Eject", "*" ) - self:AddTransition( "*", "Crash", "Crashed" ) - self:AddTransition( "*", "PilotDead", "*" ) - - return self -end - - - - ---- Sets (modifies) the minimum and maximum speed of the patrol. --- @param #AI_PATROL_ZONE self --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed ) - self:F2( { PatrolMinSpeed, PatrolMaxSpeed } ) - - self.PatrolMinSpeed = PatrolMinSpeed - self.PatrolMaxSpeed = PatrolMaxSpeed -end - - - ---- Sets the floor and ceiling altitude of the patrol. --- @param #AI_PATROL_ZONE self --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude ) - self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } ) - - self.PatrolFloorAltitude = PatrolFloorAltitude - self.PatrolCeilingAltitude = PatrolCeilingAltitude -end - --- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets. --- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased. - ---- Set the detection on. The AI will detect for targets. --- @param #AI_PATROL_ZONE self --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionOn() - self:F2() - - self.DetectOn = true -end - ---- Set the detection off. The AI will NOT detect for targets. --- However, the list of already detected targets will be kept and can be enquired! --- @param #AI_PATROL_ZONE self --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionOff() - self:F2() - - self.DetectOn = false -end - ---- Set the status checking off. --- @param #AI_PATROL_ZONE self --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetStatusOff() - self:F2() - - self.CheckStatus = false -end - ---- Activate the detection. The AI will detect for targets if the Detection is switched On. --- @param #AI_PATROL_ZONE self --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionActivated() - self:F2() - - self:ClearDetectedUnits() - self.DetectActivated = true - self:__Detect( -self.DetectInterval ) -end - ---- Deactivate the detection. The AI will NOT detect for targets. --- @param #AI_PATROL_ZONE self --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionDeactivated() - self:F2() - - self:ClearDetectedUnits() - self.DetectActivated = false -end - ---- Set the interval in seconds between each detection executed by the AI. --- The list of already detected targets will be kept and updated. --- Newly detected targets will be added, but already detected targets that were --- not detected in this cycle, will NOT be removed! --- The default interval is 30 seconds. --- @param #AI_PATROL_ZONE self --- @param #number Seconds The interval in seconds. --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionInterval( Seconds ) - self:F2() - - if Seconds then - self.DetectInterval = Seconds - else - self.DetectInterval = 30 - end -end - ---- Set the detection zone where the AI is detecting targets. --- @param #AI_PATROL_ZONE self --- @param Core.Zone#ZONE DetectionZone The zone where to detect targets. --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionZone( DetectionZone ) - self:F2() - - if DetectionZone then - self.DetectZone = DetectionZone - else - self.DetectZone = nil - end -end - ---- Gets a list of @{Unit#UNIT}s that were detected by the AI. --- No filtering is applied, so, ANY detected UNIT can be in this list. --- It is up to the mission designer to use the @{Unit} class and methods to filter the targets. --- @param #AI_PATROL_ZONE self --- @return #table The list of @{Unit#UNIT}s -function AI_PATROL_ZONE:GetDetectedUnits() - self:F2() - - return self.DetectedUnits -end - ---- Clears the list of @{Unit#UNIT}s that were detected by the AI. --- @param #AI_PATROL_ZONE self -function AI_PATROL_ZONE:ClearDetectedUnits() - self:F2() - self.DetectedUnits = {} -end - ---- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. --- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. --- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE. --- Once the time is finished, the old AI will return to the base. --- @param #AI_PATROL_ZONE self --- @param #number PatrolFuelTresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel. --- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base. --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:ManageFuel( PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime ) - - self.PatrolManageFuel = true - self.PatrolFuelTresholdPercentage = PatrolFuelTresholdPercentage - self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime - - return self -end - ---- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base. --- However, damage cannot be foreseen early on. --- Therefore, when the damage treshold is reached, --- the AI will return immediately to the home base (RTB). --- Note that for groups, the average damage of the complete group will be calculated. --- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25. --- @param #AI_PATROL_ZONE self --- @param #number PatrolDamageTreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged. --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:ManageDamage( PatrolDamageTreshold ) - - self.PatrolManageDamage = true - self.PatrolDamageTreshold = PatrolDamageTreshold - - return self -end - ---- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. --- @param #AI_PATROL_ZONE self --- @return #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_PATROL_ZONE:onafterStart( Controllable, From, Event, To ) - self:F2() - - self:__Route( 1 ) -- Route to the patrol point. The asynchronous trigger is important, because a spawned group and units takes at least one second to come live. - self:__Status( 60 ) -- Check status status every 30 seconds. - self:SetDetectionActivated() - - self:HandleEvent( EVENTS.PilotDead, self.OnPilotDead ) - self:HandleEvent( EVENTS.Crash, self.OnCrash ) - self:HandleEvent( EVENTS.Ejection, self.OnEjection ) - - Controllable:OptionROEHoldFire() - Controllable:OptionROTVertical() - - self.Controllable:OnReSpawn( - function( PatrolGroup ) - self:E( "ReSpawn" ) - self:__Reset( 1 ) - self:__Route( 5 ) - end - ) - - self:SetDetectionOn() - -end - - ---- @param #AI_PATROL_ZONE self ---- @param Wrapper.Controllable#CONTROLLABLE Controllable -function AI_PATROL_ZONE:onbeforeDetect( Controllable, From, Event, To ) - - return self.DetectOn and self.DetectActivated -end - ---- @param #AI_PATROL_ZONE self ---- @param Wrapper.Controllable#CONTROLLABLE Controllable -function AI_PATROL_ZONE:onafterDetect( Controllable, From, Event, To ) - - local Detected = false - - local DetectedTargets = Controllable:GetDetectedTargets() - for TargetID, Target in pairs( DetectedTargets or {} ) do - local TargetObject = Target.object - - if TargetObject and TargetObject:isExist() and TargetObject.id_ < 50000000 then - - local TargetUnit = UNIT:Find( TargetObject ) - local TargetUnitName = TargetUnit:GetName() - - if self.DetectionZone then - if TargetUnit:IsInZone( self.DetectionZone ) then - self:T( {"Detected ", TargetUnit } ) - if self.DetectedUnits[TargetUnit] == nil then - self.DetectedUnits[TargetUnit] = true - end - Detected = true - end - else - if self.DetectedUnits[TargetUnit] == nil then - self.DetectedUnits[TargetUnit] = true - end - Detected = true - end - end - end - - self:__Detect( -self.DetectInterval ) - - if Detected == true then - self:__Detected( 1.5 ) - end - -end - ---- @param Wrapper.Controllable#CONTROLLABLE AIControllable --- This statis method is called from the route path within the last task at the last waaypoint of the Controllable. --- Note that this method is required, as triggers the next route when patrolling for the Controllable. -function AI_PATROL_ZONE:_NewPatrolRoute( AIControllable ) - - local PatrolZone = AIControllable:GetState( AIControllable, "PatrolZone" ) -- PatrolCore.Zone#AI_PATROL_ZONE - PatrolZone:__Route( 1 ) -end - - ---- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To ) - - self:F2() - - -- When RTB, don't allow anymore the routing. - if From == "RTB" then - return - end - - - if self.Controllable:IsAlive() then - -- Determine if the AIControllable is within the PatrolZone. - -- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point. - - local PatrolRoute = {} - - -- Calculate the current route point of the controllable as the start point of the route. - -- However, when the controllable is not in the air, - -- the controllable current waypoint is probably the airbase... - -- Thus, if we would take the current waypoint as the startpoint, upon take-off, the controllable flies - -- immediately back to the airbase, and this is not correct. - -- Therefore, when on a runway, get as the current route point a random point within the PatrolZone. - -- This will make the plane fly immediately to the patrol zone. - - if self.Controllable:InAir() == false then - self:E( "Not in the air, finding route path within PatrolZone" ) - local CurrentVec2 = self.Controllable:GetVec2() - --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). - local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() - local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) - local ToPatrolZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TakeOffParking, - POINT_VEC3.RoutePointAction.FromParkingArea, - ToPatrolZoneSpeed, - true - ) - PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint - else - self:E( "In the air, finding route path within PatrolZone" ) - local CurrentVec2 = self.Controllable:GetVec2() - --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). - local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() - local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) - local ToPatrolZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - ToPatrolZoneSpeed, - true - ) - PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint - end - - - --- Define a random point in the @{Zone}. The AI will fly to that point within the zone. - - --- Find a random 2D point in PatrolZone. - local ToTargetVec2 = self.PatrolZone:GetRandomVec2() - self:T2( ToTargetVec2 ) - - --- Define Speed and Altitude. - local ToTargetAltitude = math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ) - local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) - self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } ) - - --- Obtain a 3D @{Point} from the 2D point + altitude. - local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y ) - - --- Create a route point of type air. - local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - ToTargetSpeed, - true - ) - - --self.CoordTest:SpawnFromVec3( ToTargetPointVec3:GetVec3() ) - - --ToTargetPointVec3:SmokeRed() - - PatrolRoute[#PatrolRoute+1] = ToTargetRoutePoint - - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - self.Controllable:WayPointInitialize( PatrolRoute ) - - --- Do a trick, link the NewPatrolRoute function of the PATROLGROUP object to the AIControllable in a temporary variable ... - self.Controllable:SetState( self.Controllable, "PatrolZone", self ) - self.Controllable:WayPointFunction( #PatrolRoute, 1, "AI_PATROL_ZONE:_NewPatrolRoute" ) - - --- NOW ROUTE THE GROUP! - self.Controllable:WayPointExecute( 1, 2 ) - end - -end - ---- @param #AI_PATROL_ZONE self -function AI_PATROL_ZONE:onbeforeStatus() - - return self.CheckStatus -end - ---- @param #AI_PATROL_ZONE self -function AI_PATROL_ZONE:onafterStatus() - self:F2() - - if self.Controllable and self.Controllable:IsAlive() then - - local RTB = false - - local Fuel = self.Controllable:GetUnit(1):GetFuel() - if Fuel < self.PatrolFuelTresholdPercentage then - self:E( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" ) - local OldAIControllable = self.Controllable - local AIControllableTemplate = self.Controllable:GetTemplate() - - local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed ) - local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) ) - OldAIControllable:SetTask( TimedOrbitTask, 10 ) - - RTB = true - else - end - - -- TODO: Check GROUP damage function. - local Damage = self.Controllable:GetLife() - if Damage <= self.PatrolDamageTreshold then - self:E( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" ) - RTB = true - end - - if RTB == true then - self:RTB() - else - self:__Status( 60 ) -- Execute the Patrol event after 30 seconds. - end - end -end - ---- @param #AI_PATROL_ZONE self -function AI_PATROL_ZONE:onafterRTB() - self:F2() - - if self.Controllable and self.Controllable:IsAlive() then - - self:SetDetectionOff() - self.CheckStatus = false - - local PatrolRoute = {} - - --- Calculate the current route point. - local CurrentVec2 = self.Controllable:GetVec2() - - --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). - local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() - local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) - local ToPatrolZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - ToPatrolZoneSpeed, - true - ) - - PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint - - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - self.Controllable:WayPointInitialize( PatrolRoute ) - - --- NOW ROUTE THE GROUP! - self.Controllable:WayPointExecute( 1, 1 ) - - end - -end - ---- @param #AI_PATROL_ZONE self -function AI_PATROL_ZONE:onafterDead() - self:SetDetectionOff() - self:SetStatusOff() -end - ---- @param #AI_PATROL_ZONE self --- @param Core.Event#EVENTDATA EventData -function AI_PATROL_ZONE:OnCrash( EventData ) - - if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then - self:E( self.Controllable:GetUnits() ) - if #self.Controllable:GetUnits() == 1 then - self:__Crash( 1, EventData ) - end - end -end - ---- @param #AI_PATROL_ZONE self --- @param Core.Event#EVENTDATA EventData -function AI_PATROL_ZONE:OnEjection( EventData ) - - if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then - self:__Eject( 1, EventData ) - end -end - ---- @param #AI_PATROL_ZONE self --- @param Core.Event#EVENTDATA EventData -function AI_PATROL_ZONE:OnPilotDead( EventData ) - - if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then - self:__PilotDead( 1, EventData ) - end -end ---- **AI** -- **Provide Close Air Support to friendly ground troops.** --- --- ![Banner Image](..\Presentations\AI_CAS\Dia1.JPG) --- --- === --- --- AI CAS classes makes AI Controllables execute a Close Air Support. --- --- There are the following types of CAS classes defined: --- --- * @{#AI_CAS_ZONE}: Perform a CAS in a zone. --- --- === --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-15: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. --- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module AI_Cas - - ---- AI_CAS_ZONE class --- @type AI_CAS_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. --- @extends AI.AI_Patrol#AI_PATROL_ZONE - ---- # 1) @{#AI_CAS_ZONE} class, extends @{AI_Patrol#AI_PATROL_ZONE} --- --- @{#AI_CAS_ZONE} derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. --- --- The @{#AI_CAS_ZONE} class implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Controllable} or @{Group}. --- The AI_CAS_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. --- --- ![HoldAndEngage](..\Presentations\AI_CAS\Dia3.JPG) --- --- The AI_CAS_ZONE is assigned a @{Group} and this must be done before the AI_CAS_ZONE process can be started through the **Start** event. --- --- ![Start Event](..\Presentations\AI_CAS\Dia4.JPG) --- --- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone, --- using a random speed within the given altitude and speed limits. --- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. --- This cycle will continue until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. --- --- ![Route Event](..\Presentations\AI_CAS\Dia5.JPG) --- --- When the AI is commanded to provide Close Air Support (through the event **Engage**), the AI will fly towards the Engage Zone. --- Any target that is detected in the Engage Zone will be reported and will be destroyed by the AI. --- --- ![Engage Event](..\Presentations\AI_CAS\Dia6.JPG) --- --- The AI will detect the targets and will only destroy the targets within the Engage Zone. --- --- ![Engage Event](..\Presentations\AI_CAS\Dia7.JPG) --- --- Every target that is destroyed, is reported< by the AI. --- --- ![Engage Event](..\Presentations\AI_CAS\Dia8.JPG) --- --- Note that the AI does not know when the Engage Zone is cleared, and therefore will keep circling in the zone. --- --- ![Engage Event](..\Presentations\AI_CAS\Dia9.JPG) --- --- Until it is notified through the event **Accomplish**, which is to be triggered by an observing party: --- --- * a FAC --- * a timed event --- * a menu option selected by a human --- * a condition --- * others ... --- --- ![Engage Event](..\Presentations\AI_CAS\Dia10.JPG) --- --- When the AI has accomplished the CAS, it will fly back to the Patrol Zone. --- --- ![Engage Event](..\Presentations\AI_CAS\Dia11.JPG) --- --- It will keep patrolling there, until it is notified to RTB or move to another CAS Zone. --- It can be notified to go RTB through the **RTB** event. --- --- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. --- --- ![Engage Event](..\Presentations\AI_CAS\Dia12.JPG) --- --- # 1.1) AI_CAS_ZONE constructor --- --- * @{#AI_CAS_ZONE.New}(): Creates a new AI_CAS_ZONE object. --- --- ## 1.2) AI_CAS_ZONE is a FSM --- --- ![Process](..\Presentations\AI_CAS\Dia2.JPG) --- --- ### 1.2.1) AI_CAS_ZONE States --- --- * **None** ( Group ): The process is not started yet. --- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. --- * **Engaging** ( Group ): The AI is engaging the targets in the Engage Zone, executing CAS. --- * **Returning** ( Group ): The AI is returning to Base.. --- --- ### 1.2.2) AI_CAS_ZONE Events --- --- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. --- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. --- * **@{#AI_CAS_ZONE.Engage}**: Engage the AI to provide CAS in the Engage Zone, destroying any target it finds. --- * **@{#AI_CAS_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone. --- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. --- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Unit}. --- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Unit}s assigned in the CAS task. --- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. --- --- === --- --- @field #AI_CAS_ZONE AI_CAS_ZONE --- -AI_CAS_ZONE = { - ClassName = "AI_CAS_ZONE", -} - - - ---- Creates a new AI_CAS_ZONE object --- @param #AI_CAS_ZONE self --- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @param Core.Zone#ZONE_BASE EngageZone The zone where the engage will happen. --- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO --- @return #AI_CAS_ZONE self -function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType ) - - -- Inherits from BASE - local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_CAS_ZONE - - self.EngageZone = EngageZone - self.Accomplished = false - - self:SetDetectionZone( self.EngageZone ) - - self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE. - - --- OnBefore Transition Handler for Event Engage. - -- @function [parent=#AI_CAS_ZONE] OnBeforeEngage - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Engage. - -- @function [parent=#AI_CAS_ZONE] OnAfterEngage - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Engage. - -- @function [parent=#AI_CAS_ZONE] Engage - -- @param #AI_CAS_ZONE self - -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. - -- If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. - -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. - - --- Asynchronous Event Trigger for Event Engage. - -- @function [parent=#AI_CAS_ZONE] __Engage - -- @param #AI_CAS_ZONE self - -- @param #number Delay The delay in seconds. - -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. - -- If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. - -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. - ---- OnLeave Transition Handler for State Engaging. --- @function [parent=#AI_CAS_ZONE] OnLeaveEngaging --- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnEnter Transition Handler for State Engaging. --- @function [parent=#AI_CAS_ZONE] OnEnterEngaging --- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - - self:AddTransition( "Engaging", "Target", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE. - - self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE. - - --- OnBefore Transition Handler for Event Fired. - -- @function [parent=#AI_CAS_ZONE] OnBeforeFired - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Fired. - -- @function [parent=#AI_CAS_ZONE] OnAfterFired - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Fired. - -- @function [parent=#AI_CAS_ZONE] Fired - -- @param #AI_CAS_ZONE self - - --- Asynchronous Event Trigger for Event Fired. - -- @function [parent=#AI_CAS_ZONE] __Fired - -- @param #AI_CAS_ZONE self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE. - - --- OnBefore Transition Handler for Event Destroy. - -- @function [parent=#AI_CAS_ZONE] OnBeforeDestroy - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Destroy. - -- @function [parent=#AI_CAS_ZONE] OnAfterDestroy - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Destroy. - -- @function [parent=#AI_CAS_ZONE] Destroy - -- @param #AI_CAS_ZONE self - - --- Asynchronous Event Trigger for Event Destroy. - -- @function [parent=#AI_CAS_ZONE] __Destroy - -- @param #AI_CAS_ZONE self - -- @param #number Delay The delay in seconds. - - - self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE. - - --- OnBefore Transition Handler for Event Abort. - -- @function [parent=#AI_CAS_ZONE] OnBeforeAbort - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Abort. - -- @function [parent=#AI_CAS_ZONE] OnAfterAbort - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Abort. - -- @function [parent=#AI_CAS_ZONE] Abort - -- @param #AI_CAS_ZONE self - - --- Asynchronous Event Trigger for Event Abort. - -- @function [parent=#AI_CAS_ZONE] __Abort - -- @param #AI_CAS_ZONE self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE. - - --- OnBefore Transition Handler for Event Accomplish. - -- @function [parent=#AI_CAS_ZONE] OnBeforeAccomplish - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Accomplish. - -- @function [parent=#AI_CAS_ZONE] OnAfterAccomplish - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Accomplish. - -- @function [parent=#AI_CAS_ZONE] Accomplish - -- @param #AI_CAS_ZONE self - - --- Asynchronous Event Trigger for Event Accomplish. - -- @function [parent=#AI_CAS_ZONE] __Accomplish - -- @param #AI_CAS_ZONE self - -- @param #number Delay The delay in seconds. - - return self -end - - ---- Set the Engage Zone where the AI is performing CAS. Note that if the EngageZone is changed, the AI needs to re-detect targets. --- @param #AI_CAS_ZONE self --- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAS. --- @return #AI_CAS_ZONE self -function AI_CAS_ZONE:SetEngageZone( EngageZone ) - self:F2() - - if EngageZone then - self.EngageZone = EngageZone - else - self.EngageZone = nil - end -end - - - ---- onafter State Transition for Event Start. --- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAS_ZONE:onafterStart( Controllable, From, Event, To ) - - -- Call the parent Start event handler - self:GetParent(self).onafterStart( self, Controllable, From, Event, To ) - self:HandleEvent( EVENTS.Dead ) - - self:SetDetectionDeactivated() -- When not engaging, set the detection off. -end - ---- @param Wrapper.Controllable#CONTROLLABLE AIControllable -function _NewEngageRoute( AIControllable ) - - AIControllable:T( "NewEngageRoute" ) - local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cas#AI_CAS_ZONE - EngageZone:__Engage( 1, EngageZone.EngageSpeed, EngageZone.EngageAltitude, EngageZone.EngageWeaponExpend, EngageZone.EngageAttackQty, EngageZone.EngageDirection ) -end - - ---- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAS_ZONE:onbeforeEngage( Controllable, From, Event, To ) - - if self.Accomplished == true then - return false - end -end - ---- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To ) - self:E("onafterTarget") - - if Controllable:IsAlive() then - - local AttackTasks = {} - - for DetectedUnit, Detected in pairs( self.DetectedUnits ) do - local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT - if DetectedUnit:IsAlive() then - if DetectedUnit:IsInZone( self.EngageZone ) then - if Detected == true then - self:E( {"Target: ", DetectedUnit } ) - self.DetectedUnits[DetectedUnit] = false - local AttackTask = Controllable:TaskAttackUnit( DetectedUnit, false, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil ) - self.Controllable:PushTask( AttackTask, 1 ) - end - end - else - self.DetectedUnits[DetectedUnit] = nil - end - end - - self:__Target( -10 ) - - end -end - - ---- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAS_ZONE:onafterAbort( Controllable, From, Event, To ) - Controllable:ClearTasks() - self:__Route( 1 ) -end - ---- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. --- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, - EngageSpeed, - EngageAltitude, - EngageWeaponExpend, - EngageAttackQty, - EngageDirection ) - self:F("onafterEngage") - - self.EngageSpeed = EngageSpeed or 400 - self.EngageAltitude = EngageAltitude or 2000 - self.EngageWeaponExpend = EngageWeaponExpend - self.EngageAttackQty = EngageAttackQty - self.EngageDirection = EngageDirection - - if Controllable:IsAlive() then - - local EngageRoute = {} - - --- Calculate the current route point. - local CurrentVec2 = self.Controllable:GetVec2() - - --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). - local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() - local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) - local ToEngageZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - self.EngageSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = CurrentRoutePoint - - local AttackTasks = {} - - for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do - local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT - self:T( DetectedUnit ) - if DetectedUnit:IsAlive() then - if DetectedUnit:IsInZone( self.EngageZone ) then - self:E( {"Engaging ", DetectedUnit } ) - AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit, - true, - EngageWeaponExpend, - EngageAttackQty, - EngageDirection - ) - end - else - self.DetectedUnits[DetectedUnit] = nil - end - end - - EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) - - --- Define a random point in the @{Zone}. The AI will fly to that point within the zone. - - --- Find a random 2D point in EngageZone. - local ToTargetVec2 = self.EngageZone:GetRandomVec2() - self:T2( ToTargetVec2 ) - - --- Obtain a 3D @{Point} from the 2D point + altitude. - local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y ) - - --- Create a route point of type air. - local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - self.EngageSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = ToTargetRoutePoint - - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - Controllable:WayPointInitialize( EngageRoute ) - - --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... - Controllable:SetState( Controllable, "EngageZone", self ) - - Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" ) - - --- NOW ROUTE THE GROUP! - Controllable:WayPointExecute( 1 ) - - Controllable:OptionROEOpenFire() - Controllable:OptionROTVertical() - - self:SetDetectionInterval( 2 ) - self:SetDetectionActivated() - self:__Target( -2 ) -- Start Targetting - end -end - - ---- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAS_ZONE:onafterAccomplish( Controllable, From, Event, To ) - self.Accomplished = true - self:SetDetectionDeactivated() -end - - ---- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @param Core.Event#EVENTDATA EventData -function AI_CAS_ZONE:onafterDestroy( Controllable, From, Event, To, EventData ) - - if EventData.IniUnit then - self.DetectedUnits[EventData.IniUnit] = nil - end -end - - ---- @param #AI_CAS_ZONE self --- @param Core.Event#EVENTDATA EventData -function AI_CAS_ZONE:OnEventDead( EventData ) - self:F( { "EventDead", EventData } ) - - if EventData.IniDCSUnit then - if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then - self:__Destroy( 1, EventData ) - end - end -end - - ---- **AI** - **Execute Combat Air Patrol (CAP).** --- --- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG) --- --- === --- --- AI CAP classes makes AI Controllables execute a Combat Air Patrol. --- --- There are the following types of CAP classes defined: --- --- * @{#AI_CAP_ZONE}: Perform a CAP in a zone. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-15: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. --- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. --- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. --- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module AI_Cap - - ---- @type AI_CAP_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. --- @extends AI.AI_Patrol#AI_PATROL_ZONE - - ---- # 1) @{#AI_CAP_ZONE} class, extends @{AI_CAP#AI_PATROL_ZONE} --- --- The @{#AI_CAP_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group} --- and automatically engage any airborne enemies that are within a certain range or within a certain zone. --- --- ![Process](..\Presentations\AI_CAP\Dia3.JPG) --- --- The AI_CAP_ZONE is assigned a @{Group} and this must be done before the AI_CAP_ZONE process can be started using the **Start** event. --- --- ![Process](..\Presentations\AI_CAP\Dia4.JPG) --- --- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. --- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. --- --- ![Process](..\Presentations\AI_CAP\Dia5.JPG) --- --- This cycle will continue. --- --- ![Process](..\Presentations\AI_CAP\Dia6.JPG) --- --- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. --- --- ![Process](..\Presentations\AI_CAP\Dia9.JPG) --- --- When enemies are detected, the AI will automatically engage the enemy. --- --- ![Process](..\Presentations\AI_CAP\Dia10.JPG) --- --- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. --- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. --- --- ![Process](..\Presentations\AI_CAP\Dia13.JPG) --- --- ## 1.1) AI_CAP_ZONE constructor --- --- * @{#AI_CAP_ZONE.New}(): Creates a new AI_CAP_ZONE object. --- --- ## 1.2) AI_CAP_ZONE is a FSM --- --- ![Process](..\Presentations\AI_CAP\Dia2.JPG) --- --- ### 1.2.1) AI_CAP_ZONE States --- --- * **None** ( Group ): The process is not started yet. --- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. --- * **Engaging** ( Group ): The AI is engaging the bogeys. --- * **Returning** ( Group ): The AI is returning to Base.. --- --- ### 1.2.2) AI_CAP_ZONE Events --- --- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. --- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. --- * **@{#AI_CAP_ZONE.Engage}**: Let the AI engage the bogeys. --- * **@{#AI_CAP_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone. --- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. --- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Unit}. --- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task. --- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. --- --- ## 1.3) Set the Range of Engagement --- --- ![Range](..\Presentations\AI_CAP\Dia11.JPG) --- --- An optional range can be set in meters, --- that will define when the AI will engage with the detected airborne enemy targets. --- The range can be beyond or smaller than the range of the Patrol Zone. --- The range is applied at the position of the AI. --- Use the method @{AI_CAP#AI_CAP_ZONE.SetEngageRange}() to define that range. --- --- ## 1.4) Set the Zone of Engagement --- --- ![Zone](..\Presentations\AI_CAP\Dia12.JPG) --- --- An optional @{Zone} can be set, --- that will define when the AI will engage with the detected airborne enemy targets. --- Use the method @{AI_Cap#AI_CAP_ZONE.SetEngageZone}() to define that Zone. --- --- === --- --- @field #AI_CAP_ZONE AI_CAP_ZONE --- -AI_CAP_ZONE = { - ClassName = "AI_CAP_ZONE", -} - - - ---- Creates a new AI_CAP_ZONE object --- @param #AI_CAP_ZONE self --- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO --- @return #AI_CAP_ZONE self -function AI_CAP_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) - - -- Inherits from BASE - local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_CAP_ZONE - - self.Accomplished = false - self.Engaging = false - - self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. - - --- OnBefore Transition Handler for Event Engage. - -- @function [parent=#AI_CAP_ZONE] OnBeforeEngage - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Engage. - -- @function [parent=#AI_CAP_ZONE] OnAfterEngage - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Engage. - -- @function [parent=#AI_CAP_ZONE] Engage - -- @param #AI_CAP_ZONE self - - --- Asynchronous Event Trigger for Event Engage. - -- @function [parent=#AI_CAP_ZONE] __Engage - -- @param #AI_CAP_ZONE self - -- @param #number Delay The delay in seconds. - ---- OnLeave Transition Handler for State Engaging. --- @function [parent=#AI_CAP_ZONE] OnLeaveEngaging --- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnEnter Transition Handler for State Engaging. --- @function [parent=#AI_CAP_ZONE] OnEnterEngaging --- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - - self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. - - --- OnBefore Transition Handler for Event Fired. - -- @function [parent=#AI_CAP_ZONE] OnBeforeFired - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Fired. - -- @function [parent=#AI_CAP_ZONE] OnAfterFired - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Fired. - -- @function [parent=#AI_CAP_ZONE] Fired - -- @param #AI_CAP_ZONE self - - --- Asynchronous Event Trigger for Event Fired. - -- @function [parent=#AI_CAP_ZONE] __Fired - -- @param #AI_CAP_ZONE self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. - - --- OnBefore Transition Handler for Event Destroy. - -- @function [parent=#AI_CAP_ZONE] OnBeforeDestroy - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Destroy. - -- @function [parent=#AI_CAP_ZONE] OnAfterDestroy - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Destroy. - -- @function [parent=#AI_CAP_ZONE] Destroy - -- @param #AI_CAP_ZONE self - - --- Asynchronous Event Trigger for Event Destroy. - -- @function [parent=#AI_CAP_ZONE] __Destroy - -- @param #AI_CAP_ZONE self - -- @param #number Delay The delay in seconds. - - - self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. - - --- OnBefore Transition Handler for Event Abort. - -- @function [parent=#AI_CAP_ZONE] OnBeforeAbort - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Abort. - -- @function [parent=#AI_CAP_ZONE] OnAfterAbort - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Abort. - -- @function [parent=#AI_CAP_ZONE] Abort - -- @param #AI_CAP_ZONE self - - --- Asynchronous Event Trigger for Event Abort. - -- @function [parent=#AI_CAP_ZONE] __Abort - -- @param #AI_CAP_ZONE self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. - - --- OnBefore Transition Handler for Event Accomplish. - -- @function [parent=#AI_CAP_ZONE] OnBeforeAccomplish - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Accomplish. - -- @function [parent=#AI_CAP_ZONE] OnAfterAccomplish - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Accomplish. - -- @function [parent=#AI_CAP_ZONE] Accomplish - -- @param #AI_CAP_ZONE self - - --- Asynchronous Event Trigger for Event Accomplish. - -- @function [parent=#AI_CAP_ZONE] __Accomplish - -- @param #AI_CAP_ZONE self - -- @param #number Delay The delay in seconds. - - return self -end - - ---- Set the Engage Zone which defines where the AI will engage bogies. --- @param #AI_CAP_ZONE self --- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP. --- @return #AI_CAP_ZONE self -function AI_CAP_ZONE:SetEngageZone( EngageZone ) - self:F2() - - if EngageZone then - self.EngageZone = EngageZone - else - self.EngageZone = nil - end -end - ---- Set the Engage Range when the AI will engage with airborne enemies. --- @param #AI_CAP_ZONE self --- @param #number EngageRange The Engage Range. --- @return #AI_CAP_ZONE self -function AI_CAP_ZONE:SetEngageRange( EngageRange ) - self:F2() - - if EngageRange then - self.EngageRange = EngageRange - else - self.EngageRange = nil - end -end - ---- onafter State Transition for Event Start. --- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAP_ZONE:onafterStart( Controllable, From, Event, To ) - - -- Call the parent Start event handler - self:GetParent(self).onafterStart( self, Controllable, From, Event, To ) - self:HandleEvent( EVENTS.Dead ) - -end - --- todo: need to fix this global function - ---- @param Wrapper.Controllable#CONTROLLABLE AIControllable -function _NewEngageCapRoute( AIControllable ) - - AIControllable:T( "NewEngageRoute" ) - local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_CAP_ZONE - EngageZone:__Engage( 1 ) -end - ---- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAP_ZONE:onbeforeEngage( Controllable, From, Event, To ) - - if self.Accomplished == true then - return false - end -end - ---- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To ) - - if From ~= "Engaging" then - - local Engage = false - - for DetectedUnit, Detected in pairs( self.DetectedUnits ) do - - local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT - self:T( DetectedUnit ) - if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then - Engage = true - break - end - end - - if Engage == true then - self:E( 'Detected -> Engaging' ) - self:__Engage( 1 ) - end - end -end - - ---- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAP_ZONE:onafterAbort( Controllable, From, Event, To ) - Controllable:ClearTasks() - self:__Route( 1 ) -end - - - - ---- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To ) - - if Controllable:IsAlive() then - - local EngageRoute = {} - - --- Calculate the current route point. - local CurrentVec2 = self.Controllable:GetVec2() - - --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). - local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() - local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) - local ToEngageZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - ToEngageZoneSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = CurrentRoutePoint - - - --- Find a random 2D point in PatrolZone. - local ToTargetVec2 = self.PatrolZone:GetRandomVec2() - self:T2( ToTargetVec2 ) - - --- Define Speed and Altitude. - local ToTargetAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude ) - local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) - self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } ) - - --- Obtain a 3D @{Point} from the 2D point + altitude. - local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y ) - - --- Create a route point of type air. - local ToPatrolRoutePoint = ToTargetPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - ToTargetSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint - - Controllable:OptionROEOpenFire() - Controllable:OptionROTPassiveDefense() - - local AttackTasks = {} - - for DetectedUnit, Detected in pairs( self.DetectedUnits ) do - local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT - self:T( { DetectedUnit, DetectedUnit:IsAlive(), DetectedUnit:IsAir() } ) - if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then - if self.EngageZone then - if DetectedUnit:IsInZone( self.EngageZone ) then - self:E( {"Within Zone and Engaging ", DetectedUnit } ) - AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) - end - else - if self.EngageRange then - if DetectedUnit:GetPointVec3():Get2DDistance(Controllable:GetPointVec3() ) <= self.EngageRange then - self:E( {"Within Range and Engaging", DetectedUnit } ) - AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) - end - else - AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) - end - end - else - self.DetectedUnits[DetectedUnit] = nil - end - end - - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - self.Controllable:WayPointInitialize( EngageRoute ) - - - if #AttackTasks == 0 then - self:E("No targets found -> Going back to Patrolling") - self:__Abort( 1 ) - self:__Route( 1 ) - self:SetDetectionActivated() - else - EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) - - --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... - self.Controllable:SetState( self.Controllable, "EngageZone", self ) - - self.Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageCapRoute" ) - - self:SetDetectionDeactivated() - end - - --- NOW ROUTE THE GROUP! - self.Controllable:WayPointExecute( 1, 2 ) - - end -end - ---- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAP_ZONE:onafterAccomplish( Controllable, From, Event, To ) - self.Accomplished = true - self:SetDetectionOff() -end - ---- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @param Core.Event#EVENTDATA EventData -function AI_CAP_ZONE:onafterDestroy( Controllable, From, Event, To, EventData ) - - if EventData.IniUnit then - self.DetectedUnits[EventData.IniUnit] = nil - end -end - ---- @param #AI_CAP_ZONE self --- @param Core.Event#EVENTDATA EventData -function AI_CAP_ZONE:OnEventDead( EventData ) - self:F( { "EventDead", EventData } ) - - if EventData.IniDCSUnit then - if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then - self:__Destroy( 1, EventData ) - end - end -end ----Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Ground** -- --- **Management of logical cargo objects, that can be transported from and to transportation carriers.** --- --- ![Banner Image](..\Presentations\AI_CARGO\CARGO.JPG) --- --- === --- --- Cargo can be of various forms, always are composed out of ONE object ( one unit or one static or one slingload crate ): --- --- * AI_CARGO_UNIT, represented by a @{Unit} in a @{Group}: Cargo can be represented by a Unit in a Group. Destruction of the Unit will mean that the cargo is lost. --- * CARGO_STATIC, represented by a @{Static}: Cargo can be represented by a Static. Destruction of the Static will mean that the cargo is lost. --- * AI_CARGO_PACKAGE, contained in a @{Unit} of a @{Group}: Cargo can be contained within a Unit of a Group. The cargo can be **delivered** by the @{Unit}. If the Unit is destroyed, the cargo will be destroyed also. --- * AI_CARGO_PACKAGE, Contained in a @{Static}: Cargo can be contained within a Static. The cargo can be **collected** from the @Static. If the @{Static} is destroyed, the cargo will be destroyed. --- * CARGO_SLINGLOAD, represented by a @{Cargo} that is transportable: Cargo can be represented by a Cargo object that is transportable. Destruction of the Cargo will mean that the cargo is lost. --- --- * AI_CARGO_GROUPED, represented by a Group of CARGO_UNITs. --- --- # 1) @{#AI_CARGO} class, extends @{Fsm#FSM_PROCESS} --- --- The @{#AI_CARGO} class defines the core functions that defines a cargo object within MOOSE. --- A cargo is a logical object defined that is available for transport, and has a life status within a simulation. --- --- The AI_CARGO is a state machine: it manages the different events and states of the cargo. --- All derived classes from AI_CARGO follow the same state machine, expose the same cargo event functions, and provide the same cargo states. --- --- ## 1.2.1) AI_CARGO Events: --- --- * @{#AI_CARGO.Board}( ToCarrier ): Boards the cargo to a carrier. --- * @{#AI_CARGO.Load}( ToCarrier ): Loads the cargo into a carrier, regardless of its position. --- * @{#AI_CARGO.UnBoard}( ToPointVec2 ): UnBoard the cargo from a carrier. This will trigger a movement of the cargo to the option ToPointVec2. --- * @{#AI_CARGO.UnLoad}( ToPointVec2 ): UnLoads the cargo from a carrier. --- * @{#AI_CARGO.Dead}( Controllable ): The cargo is dead. The cargo process will be ended. --- --- ## 1.2.2) AI_CARGO States: --- --- * **UnLoaded**: The cargo is unloaded from a carrier. --- * **Boarding**: The cargo is currently boarding (= running) into a carrier. --- * **Loaded**: The cargo is loaded into a carrier. --- * **UnBoarding**: The cargo is currently unboarding (=running) from a carrier. --- * **Dead**: The cargo is dead ... --- * **End**: The process has come to an end. --- --- ## 1.2.3) AI_CARGO state transition methods: --- --- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. --- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Leaving** the state. --- The state transition method needs to start with the name **OnLeave + the name of the state**. --- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, --- but then you'll need to specify your own logic using the AIControllable! --- --- * **Entering** the state. --- The state transition method needs to start with the name **OnEnter + the name of the state**. --- These state transition methods need to provide a return value, which is specified at the function description. --- --- # 2) #AI_CARGO_UNIT class --- --- The AI_CARGO_UNIT class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. --- Use the event functions as described above to Load, UnLoad, Board, UnBoard the AI_CARGO_UNIT objects to and from carriers. --- --- # 5) #AI_CARGO_GROUPED class --- --- The AI_CARGO_GROUPED class defines a cargo that is represented by a group of UNIT objects within the simulator, and can be transported by a carrier. --- Use the event functions as described above to Load, UnLoad, Board, UnBoard the AI_CARGO_UNIT objects to and from carriers. --- --- This module is still under construction, but is described above works already, and will keep working ... --- --- @module Cargo - --- Events - --- Board - ---- Boards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo to the Carrier. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#AI_CARGO] Board --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - ---- Boards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo to the Carrier. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#AI_CARGO] __Board --- @param #AI_CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - - --- UnBoard - ---- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. --- The cargo must be in the **Loaded** state. --- @function [parent=#AI_CARGO] UnBoard --- @param #AI_CARGO self --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. - ---- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. --- The cargo must be in the **Loaded** state. --- @function [parent=#AI_CARGO] __UnBoard --- @param #AI_CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. - - --- Load - ---- Loads the cargo to a Carrier. The event will load the cargo into the Carrier regardless of its position. There will be no movement simulated of the cargo loading. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#AI_CARGO] Load --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - ---- Loads the cargo to a Carrier. The event will load the cargo into the Carrier regardless of its position. There will be no movement simulated of the cargo loading. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#AI_CARGO] __Load --- @param #AI_CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - - --- UnLoad - ---- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. --- The cargo must be in the **Loaded** state. --- @function [parent=#AI_CARGO] UnLoad --- @param #AI_CARGO self --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. - ---- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. --- The cargo must be in the **Loaded** state. --- @function [parent=#AI_CARGO] __UnLoad --- @param #AI_CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. - --- State Transition Functions - --- UnLoaded - ---- @function [parent=#AI_CARGO] OnLeaveUnLoaded --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#AI_CARGO] OnEnterUnLoaded --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - --- Loaded - ---- @function [parent=#AI_CARGO] OnLeaveLoaded --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#AI_CARGO] OnEnterLoaded --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - --- Boarding - ---- @function [parent=#AI_CARGO] OnLeaveBoarding --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#AI_CARGO] OnEnterBoarding --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - --- UnBoarding - ---- @function [parent=#AI_CARGO] OnLeaveUnBoarding --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#AI_CARGO] OnEnterUnBoarding --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - - --- TODO: Find all Carrier objects and make the type of the Carriers Wrapper.Unit#UNIT in the documentation. - -CARGOS = {} - -do -- AI_CARGO - - --- @type AI_CARGO - -- @extends Core.Fsm#FSM_PROCESS - -- @field #string Type A string defining the type of the cargo. eg. Engineers, Equipment, Screwdrivers. - -- @field #string Name A string defining the name of the cargo. The name is the unique identifier of the cargo. - -- @field #number Weight A number defining the weight of the cargo. The weight is expressed in kg. - -- @field #number ReportRadius (optional) A number defining the radius in meters when the cargo is signalling or reporting to a Carrier. - -- @field #number NearRadius (optional) A number defining the radius in meters when the cargo is near to a Carrier, so that it can be loaded. - -- @field Wrapper.Controllable#CONTROLLABLE CargoObject The alive DCS object representing the cargo. This value can be nil, meaning, that the cargo is not represented anywhere... - -- @field Wrapper.Controllable#CONTROLLABLE CargoCarrier The alive DCS object carrying the cargo. This value can be nil, meaning, that the cargo is not contained anywhere... - -- @field #boolean Slingloadable This flag defines if the cargo can be slingloaded. - -- @field #boolean Moveable This flag defines if the cargo is moveable. - -- @field #boolean Representable This flag defines if the cargo can be represented by a DCS Unit. - -- @field #boolean Containable This flag defines if the cargo can be contained within a DCS Unit. - AI_CARGO = { - ClassName = "AI_CARGO", - Type = nil, - Name = nil, - Weight = nil, - CargoObject = nil, - CargoCarrier = nil, - Representable = false, - Slingloadable = false, - Moveable = false, - Containable = false, - } - ---- @type AI_CARGO.CargoObjects --- @map < #string, Wrapper.Positionable#POSITIONABLE > The alive POSITIONABLE objects representing the the cargo. - - ---- AI_CARGO Constructor. This class is an abstract class and should not be instantiated. --- @param #AI_CARGO self --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO -function AI_CARGO:New( Type, Name, Weight, ReportRadius, NearRadius ) - - local self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM_CONTROLLABLE - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - self:SetStartState( "UnLoaded" ) - self:AddTransition( "UnLoaded", "Board", "Boarding" ) - self:AddTransition( "Boarding", "Boarding", "Boarding" ) - self:AddTransition( "Boarding", "Load", "Loaded" ) - self:AddTransition( "UnLoaded", "Load", "Loaded" ) - self:AddTransition( "Loaded", "UnBoard", "UnBoarding" ) - self:AddTransition( "UnBoarding", "UnBoarding", "UnBoarding" ) - self:AddTransition( "UnBoarding", "UnLoad", "UnLoaded" ) - self:AddTransition( "Loaded", "UnLoad", "UnLoaded" ) - - - self.Type = Type - self.Name = Name - self.Weight = Weight - self.ReportRadius = ReportRadius - self.NearRadius = NearRadius - self.CargoObject = nil - self.CargoCarrier = nil - self.Representable = false - self.Slingloadable = false - self.Moveable = false - self.Containable = false - - - self.CargoScheduler = SCHEDULER:New() - - CARGOS[self.Name] = self - - return self -end - - ---- Template method to spawn a new representation of the AI_CARGO in the simulator. --- @param #AI_CARGO self --- @return #AI_CARGO -function AI_CARGO:Spawn( PointVec2 ) - self:F() - -end - - ---- Check if CargoCarrier is near the Cargo to be Loaded. --- @param #AI_CARGO self --- @param Core.Point#POINT_VEC2 PointVec2 --- @return #boolean -function AI_CARGO:IsNear( PointVec2 ) - self:F( { PointVec2 } ) - - local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - self:T( Distance ) - - if Distance <= self.NearRadius then - return true - else - return false - end -end - -end - -do -- AI_CARGO_REPRESENTABLE - - --- @type AI_CARGO_REPRESENTABLE - -- @extends #AI_CARGO - AI_CARGO_REPRESENTABLE = { - ClassName = "AI_CARGO_REPRESENTABLE" - } - ---- AI_CARGO_REPRESENTABLE Constructor. --- @param #AI_CARGO_REPRESENTABLE self --- @param Wrapper.Controllable#Controllable CargoObject --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_REPRESENTABLE -function AI_CARGO_REPRESENTABLE:New( CargoObject, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO:New( Type, Name, Weight, ReportRadius, NearRadius ) ) -- #AI_CARGO - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - return self -end - ---- Route a cargo unit to a PointVec2. --- @param #AI_CARGO_REPRESENTABLE self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #number Speed --- @return #AI_CARGO_REPRESENTABLE -function AI_CARGO_REPRESENTABLE:RouteTo( ToPointVec2, Speed ) - self:F2( ToPointVec2 ) - - local Points = {} - - local PointStartVec2 = self.CargoObject:GetPointVec2() - - Points[#Points+1] = PointStartVec2:RoutePointGround( Speed ) - Points[#Points+1] = ToPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 2 ) - return self -end - -end -- AI_CARGO - -do -- AI_CARGO_UNIT - - --- @type AI_CARGO_UNIT - -- @extends #AI_CARGO_REPRESENTABLE - AI_CARGO_UNIT = { - ClassName = "AI_CARGO_UNIT" - } - ---- AI_CARGO_UNIT Constructor. --- @param #AI_CARGO_UNIT self --- @param Wrapper.Unit#UNIT CargoUnit --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_UNIT -function AI_CARGO_UNIT:New( CargoUnit, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, Weight, ReportRadius, NearRadius ) ) -- #AI_CARGO_UNIT - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - self:T( CargoUnit ) - self.CargoObject = CargoUnit - - self:T( self.ClassName ) - - return self -end - ---- Enter UnBoarding State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Point#POINT_VEC2 ToPointVec2 -function AI_CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2 ) - self:F() - - local Angle = 180 - local Speed = 10 - local DeployDistance = 5 - local RouteDistance = 60 - - if From == "Loaded" then - - local CargoCarrierPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, CargoDeployHeading ) - local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading ) - - -- if there is no ToPointVec2 given, then use the CargoRoutePointVec2 - ToPointVec2 = ToPointVec2 or CargoRoutePointVec2 - - local FromPointVec2 = CargoCarrierPointVec2 - - -- Respawn the group... - if self.CargoObject then - self.CargoObject:ReSpawn( CargoDeployPointVec2:GetVec3(), CargoDeployHeading ) - self.CargoCarrier = nil - - local Points = {} - Points[#Points+1] = FromPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = ToPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 1 ) - - self:__UnBoarding( 1, ToPointVec2 ) - end - end - -end - ---- Leave UnBoarding State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Point#POINT_VEC2 ToPointVec2 -function AI_CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "UnBoarding" then - if self:IsNear( ToPointVec2 ) then - return true - else - self:__UnBoarding( 1, ToPointVec2 ) - end - return false - end - -end - ---- UnBoard Event. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Point#POINT_VEC2 ToPointVec2 -function AI_CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - self.CargoInAir = self.CargoObject:InAir() - - self:T( self.CargoInAir ) - - -- Only unboard the cargo when the carrier is not in the air. - -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). - if not self.CargoInAir then - - end - - self:__UnLoad( 1, ToPointVec2 ) - -end - - - ---- Enter UnLoaded State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Point#POINT_VEC2 -function AI_CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "Loaded" then - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = StartPointVec2:Translate( Distance, CargoDeployHeading ) - - ToPointVec2 = ToPointVec2 or POINT_VEC2:New( CargoDeployPointVec2:GetX(), CargoDeployPointVec2:GetY() ) - - -- Respawn the group... - if self.CargoObject then - self.CargoObject:ReSpawn( ToPointVec2:GetVec3(), 0 ) - self.CargoCarrier = nil - end - - end - - if self.OnUnLoadedCallBack then - self.OnUnLoadedCallBack( self, unpack( self.OnUnLoadedParameters ) ) - self.OnUnLoadedCallBack = nil - end - -end - - - ---- Enter Boarding State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - local Speed = 10 - local Angle = 180 - local Distance = 5 - - if From == "UnLoaded" then - local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) - - local Points = {} - - local PointStartVec2 = self.CargoObject:GetPointVec2() - - Points[#Points+1] = PointStartVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 2 ) - end - -end - ---- Leave Boarding State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_UNIT:onleaveBoarding( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - if self:IsNear( CargoCarrier:GetPointVec2() ) then - self:__Load( 1, CargoCarrier ) - return true - else - self:__Boarding( 1, CargoCarrier ) - end - return false -end - ---- Loaded State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier ) - self:F() - - self.CargoCarrier = CargoCarrier - - -- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects). - if self.CargoObject then - self:T("Destroying") - self.CargoObject:Destroy() - end -end - - ---- Board Event. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier ) - self:F() - - self.CargoInAir = self.CargoObject:InAir() - - self:T( self.CargoInAir ) - - -- Only move the group to the carrier when the cargo is not in the air - -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). - if not self.CargoInAir then - self:Load( CargoCarrier ) - end - -end - -end - -do -- AI_CARGO_PACKAGE - - --- @type AI_CARGO_PACKAGE - -- @extends #AI_CARGO_REPRESENTABLE - AI_CARGO_PACKAGE = { - ClassName = "AI_CARGO_PACKAGE" - } - ---- AI_CARGO_PACKAGE Constructor. --- @param #AI_CARGO_PACKAGE self --- @param Wrapper.Unit#UNIT CargoCarrier The UNIT carrying the package. --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_PACKAGE -function AI_CARGO_PACKAGE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO_REPRESENTABLE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) ) -- #AI_CARGO_PACKAGE - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - self:T( CargoCarrier ) - self.CargoCarrier = CargoCarrier - - return self -end - ---- Board Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #number Speed --- @param #number BoardDistance --- @param #number Angle -function AI_CARGO_PACKAGE:onafterOnBoard( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - self:F() - - self.CargoInAir = self.CargoCarrier:InAir() - - self:T( self.CargoInAir ) - - -- Only move the CargoCarrier to the New CargoCarrier when the New CargoCarrier is not in the air. - if not self.CargoInAir then - - local Points = {} - - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - self:T( { CargoCarrierHeading, CargoDeployHeading } ) - local CargoDeployPointVec2 = CargoCarrier:GetPointVec2():Translate( BoardDistance, CargoDeployHeading ) - - Points[#Points+1] = StartPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoCarrier:TaskRoute( Points ) - self.CargoCarrier:SetTask( TaskRoute, 1 ) - end - - self:Boarded( CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - -end - ---- Check if CargoCarrier is near the Cargo to be Loaded. --- @param #AI_CARGO_PACKAGE self --- @param Wrapper.Unit#UNIT CargoCarrier --- @return #boolean -function AI_CARGO_PACKAGE:IsNear( CargoCarrier ) - self:F() - - local CargoCarrierPoint = CargoCarrier:GetPointVec2() - - local Distance = CargoCarrierPoint:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) - self:T( Distance ) - - if Distance <= self.NearRadius then - return true - else - return false - end -end - ---- Boarded Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_PACKAGE:onafterOnBoarded( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - self:F() - - if self:IsNear( CargoCarrier ) then - self:__Load( 1, CargoCarrier, Speed, LoadDistance, Angle ) - else - self:__Boarded( 1, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - end -end - ---- UnBoard Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param #number Speed --- @param #number UnLoadDistance --- @param #number UnBoardDistance --- @param #number Radius --- @param #number Angle -function AI_CARGO_PACKAGE:onafterUnBoard( From, Event, To, CargoCarrier, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle ) - self:F() - - self.CargoInAir = self.CargoCarrier:InAir() - - self:T( self.CargoInAir ) - - -- Only unboard the cargo when the carrier is not in the air. - -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). - if not self.CargoInAir then - - self:_Next( self.FsmP.UnLoad, UnLoadDistance, Angle ) - - local Points = {} - - local StartPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - self:T( { CargoCarrierHeading, CargoDeployHeading } ) - local CargoDeployPointVec2 = StartPointVec2:Translate( UnBoardDistance, CargoDeployHeading ) - - Points[#Points+1] = StartPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = CargoCarrier:TaskRoute( Points ) - CargoCarrier:SetTask( TaskRoute, 1 ) - end - - self:__UnBoarded( 1 , CargoCarrier, Speed ) - -end - ---- UnBoarded Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_PACKAGE:onafterUnBoarded( From, Event, To, CargoCarrier, Speed ) - self:F() - - if self:IsNear( CargoCarrier ) then - self:__UnLoad( 1, CargoCarrier, Speed ) - else - self:__UnBoarded( 1, CargoCarrier, Speed ) - end -end - ---- Load Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #number Speed --- @param #number LoadDistance --- @param #number Angle -function AI_CARGO_PACKAGE:onafterLoad( From, Event, To, CargoCarrier, Speed, LoadDistance, Angle ) - self:F() - - self.CargoCarrier = CargoCarrier - - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = StartPointVec2:Translate( LoadDistance, CargoDeployHeading ) - - local Points = {} - Points[#Points+1] = StartPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoCarrier:TaskRoute( Points ) - self.CargoCarrier:SetTask( TaskRoute, 1 ) - -end - ---- UnLoad Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param #number Distance --- @param #number Angle -function AI_CARGO_PACKAGE:onafterUnLoad( From, Event, To, CargoCarrier, Speed, Distance, Angle ) - self:F() - - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = StartPointVec2:Translate( Distance, CargoDeployHeading ) - - self.CargoCarrier = CargoCarrier - - local Points = {} - Points[#Points+1] = StartPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoCarrier:TaskRoute( Points ) - self.CargoCarrier:SetTask( TaskRoute, 1 ) - -end - - -end - -do -- AI_CARGO_GROUP - - --- @type AI_CARGO_GROUP - -- @extends AI.AI_Cargo#AI_CARGO - -- @field Set#SET_BASE CargoSet A set of cargo objects. - -- @field #string Name A string defining the name of the cargo group. The name is the unique identifier of the cargo. - AI_CARGO_GROUP = { - ClassName = "AI_CARGO_GROUP", - } - ---- AI_CARGO_GROUP constructor. --- @param #AI_CARGO_GROUP self --- @param Core.Set#Set_BASE CargoSet --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_GROUP -function AI_CARGO_GROUP:New( CargoSet, Type, Name, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO:New( Type, Name, 0, ReportRadius, NearRadius ) ) -- #AI_CARGO_GROUP - self:F( { Type, Name, ReportRadius, NearRadius } ) - - self.CargoSet = CargoSet - - - return self -end - -end -- AI_CARGO_GROUP - -do -- AI_CARGO_GROUPED - - --- @type AI_CARGO_GROUPED - -- @extends AI.AI_Cargo#AI_CARGO_GROUP - AI_CARGO_GROUPED = { - ClassName = "AI_CARGO_GROUPED", - } - ---- AI_CARGO_GROUPED constructor. --- @param #AI_CARGO_GROUPED self --- @param Core.Set#Set_BASE CargoSet --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_GROUPED -function AI_CARGO_GROUPED:New( CargoSet, Type, Name, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO_GROUP:New( CargoSet, Type, Name, ReportRadius, NearRadius ) ) -- #AI_CARGO_GROUPED - self:F( { Type, Name, ReportRadius, NearRadius } ) - - return self -end - ---- Enter Boarding State. --- @param #AI_CARGO_GROUPED self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onenterBoarding( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - if From == "UnLoaded" then - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - Cargo:__Board( 1, CargoCarrier ) - end - ) - - self:__Boarding( 1, CargoCarrier ) - end - -end - ---- Enter Loaded State. --- @param #AI_CARGO_GROUPED self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onenterLoaded( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - if From == "UnLoaded" then - -- For each Cargo object within the AI_CARGO_GROUPED, load each cargo to the CargoCarrier. - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - Cargo:Load( CargoCarrier ) - end - end -end - ---- Leave Boarding State. --- @param #AI_CARGO_GROUPED self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onleaveBoarding( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - local Boarded = true - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( Cargo.current ) - if not Cargo:is( "Loaded" ) then - Boarded = false - end - end - - if not Boarded then - self:__Boarding( 1, CargoCarrier ) - else - self:__Load( 1, CargoCarrier ) - end - return Boarded -end - ---- Enter UnBoarding State. --- @param #AI_CARGO_GROUPED self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onenterUnBoarding( From, Event, To, ToPointVec2 ) - self:F() - - local Timer = 1 - - if From == "Loaded" then - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - Cargo:__UnBoard( Timer, ToPointVec2 ) - Timer = Timer + 10 - end - ) - - self:__UnBoarding( 1, ToPointVec2 ) - end - -end - ---- Leave UnBoarding State. --- @param #AI_CARGO_GROUPED self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onleaveUnBoarding( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "UnBoarding" then - local UnBoarded = true - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( Cargo.current ) - if not Cargo:is( "UnLoaded" ) then - UnBoarded = false - end - end - - if UnBoarded then - return true - else - self:__UnBoarding( 1, ToPointVec2 ) - end - - return false - end - -end - ---- UnBoard Event. --- @param #AI_CARGO_GROUPED self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onafterUnBoarding( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - self:__UnLoad( 1, ToPointVec2 ) -end - - - ---- Enter UnLoaded State. --- @param #AI_CARGO_GROUPED self --- @param Core.Point#POINT_VEC2 --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onenterUnLoaded( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - if From == "Loaded" then - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - Cargo:UnLoad( ToPointVec2 ) - end - ) - - end - -end - -end -- AI_CARGO_GROUPED - - - ---- (SP) (MP) (FSM) Accept or reject process for player (task) assignments. --- --- === --- --- # @{#ACT_ASSIGN} FSM template class, extends @{Fsm#FSM_PROCESS} --- --- ## ACT_ASSIGN state machine: --- --- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. --- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. --- Each derived class follows exactly the same process, using the same events and following the same state transitions, --- but will have **different implementation behaviour** upon each event or state transition. --- --- ### ACT_ASSIGN **Events**: --- --- These are the events defined in this class: --- --- * **Start**: Start the tasking acceptance process. --- * **Assign**: Assign the task. --- * **Reject**: Reject the task.. --- --- ### ACT_ASSIGN **Event methods**: --- --- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. --- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine: --- --- * **Immediate**: The event method has exactly the name of the event. --- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. --- --- ### ACT_ASSIGN **States**: --- --- * **UnAssigned**: The player has not accepted the task. --- * **Assigned (*)**: The player has accepted the task. --- * **Rejected (*)**: The player has not accepted the task. --- * **Waiting**: The process is awaiting player feedback. --- * **Failed (*)**: The process has failed. --- --- (*) End states of the process. --- --- ### ACT_ASSIGN state transition methods: --- --- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. --- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Before** the state transition. --- The state transition method needs to start with the name **OnBefore + the name of the state**. --- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, --- but then you'll need to specify your own logic using the AIControllable! --- --- * **After** the state transition. --- The state transition method needs to start with the name **OnAfter + the name of the state**. --- These state transition methods need to provide a return value, which is specified at the function description. --- --- === --- --- # 1) @{#ACT_ASSIGN_ACCEPT} class, extends @{Fsm.Assign#ACT_ASSIGN} --- --- The ACT_ASSIGN_ACCEPT class accepts by default a task for a player. No player intervention is allowed to reject the task. --- --- ## 1.1) ACT_ASSIGN_ACCEPT constructor: --- --- * @{#ACT_ASSIGN_ACCEPT.New}(): Creates a new ACT_ASSIGN_ACCEPT object. --- --- === --- --- # 2) @{#ACT_ASSIGN_MENU_ACCEPT} class, extends @{Fsm.Assign#ACT_ASSIGN} --- --- The ACT_ASSIGN_MENU_ACCEPT class accepts a task when the player accepts the task through an added menu option. --- This assignment type is useful to conditionally allow the player to choose whether or not he would accept the task. --- The assignment type also allows to reject the task. --- --- ## 2.1) ACT_ASSIGN_MENU_ACCEPT constructor: --- ----------------------------------------- --- --- * @{#ACT_ASSIGN_MENU_ACCEPT.New}(): Creates a new ACT_ASSIGN_MENU_ACCEPT object. --- --- === --- --- @module Assign - - -do -- ACT_ASSIGN - - --- ACT_ASSIGN class - -- @type ACT_ASSIGN - -- @field Tasking.Task#TASK Task - -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone - -- @extends Core.Fsm#FSM_PROCESS - ACT_ASSIGN = { - ClassName = "ACT_ASSIGN", - } - - - --- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted. - -- @param #ACT_ASSIGN self - -- @return #ACT_ASSIGN The task acceptance process. - function ACT_ASSIGN:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ASSIGN" ) ) -- Core.Fsm#FSM_PROCESS - - self:AddTransition( "UnAssigned", "Start", "Waiting" ) - self:AddTransition( "Waiting", "Assign", "Assigned" ) - self:AddTransition( "Waiting", "Reject", "Rejected" ) - self:AddTransition( "*", "Fail", "Failed" ) - - self:AddEndState( "Assigned" ) - self:AddEndState( "Rejected" ) - self:AddEndState( "Failed" ) - - self:SetStartState( "UnAssigned" ) - - return self - end - -end -- ACT_ASSIGN - - - -do -- ACT_ASSIGN_ACCEPT - - --- ACT_ASSIGN_ACCEPT class - -- @type ACT_ASSIGN_ACCEPT - -- @field Tasking.Task#TASK Task - -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone - -- @extends #ACT_ASSIGN - ACT_ASSIGN_ACCEPT = { - ClassName = "ACT_ASSIGN_ACCEPT", - } - - - --- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted. - -- @param #ACT_ASSIGN_ACCEPT self - -- @param #string TaskBriefing - function ACT_ASSIGN_ACCEPT:New( TaskBriefing ) - - local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_ACCEPT - - self.TaskBriefing = TaskBriefing - - return self - end - - function ACT_ASSIGN_ACCEPT:Init( FsmAssign ) - - self.TaskBriefing = FsmAssign.TaskBriefing - end - - --- StateMachine callback function - -- @param #ACT_ASSIGN_ACCEPT self - -- @param Wrapper.Unit#UNIT ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, From, Event, To ) - self:E( { ProcessUnit, From, Event, To } ) - - self:__Assign( 1 ) - end - - --- StateMachine callback function - -- @param #ACT_ASSIGN_ACCEPT self - -- @param Wrapper.Unit#UNIT ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, From, Event, To ) - env.info( "in here" ) - self:E( { ProcessUnit, From, Event, To } ) - - local ProcessGroup = ProcessUnit:GetGroup() - - self:Message( "You are assigned to the task " .. self.Task:GetName() ) - - self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) - end - -end -- ACT_ASSIGN_ACCEPT - - -do -- ACT_ASSIGN_MENU_ACCEPT - - --- ACT_ASSIGN_MENU_ACCEPT class - -- @type ACT_ASSIGN_MENU_ACCEPT - -- @field Tasking.Task#TASK Task - -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone - -- @extends #ACT_ASSIGN - ACT_ASSIGN_MENU_ACCEPT = { - ClassName = "ACT_ASSIGN_MENU_ACCEPT", - } - - --- Init. - -- @param #ACT_ASSIGN_MENU_ACCEPT self - -- @param #string TaskName - -- @param #string TaskBriefing - -- @return #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:New( TaskName, TaskBriefing ) - - -- Inherits from BASE - local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_MENU_ACCEPT - - self.TaskName = TaskName - self.TaskBriefing = TaskBriefing - - return self - end - - function ACT_ASSIGN_MENU_ACCEPT:Init( FsmAssign ) - - self.TaskName = FsmAssign.TaskName - self.TaskBriefing = FsmAssign.TaskBriefing - end - - - --- Creates a new task assignment state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. - -- @param #ACT_ASSIGN_MENU_ACCEPT self - -- @param #string TaskName - -- @param #string TaskBriefing - -- @return #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:Init( TaskName, TaskBriefing ) - - self.TaskBriefing = TaskBriefing - self.TaskName = TaskName - - return self - end - - --- StateMachine callback function - -- @param #ACT_ASSIGN_MENU_ACCEPT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, From, Event, To ) - self:E( { ProcessUnit, From, Event, To } ) - - self:Message( "Access the radio menu to accept the task. You have 30 seconds or the assignment will be cancelled." ) - - local ProcessGroup = ProcessUnit:GetGroup() - - self.Menu = MENU_GROUP:New( ProcessGroup, "Task " .. self.TaskName .. " acceptance" ) - self.MenuAcceptTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Accept task " .. self.TaskName, self.Menu, self.MenuAssign, self ) - self.MenuRejectTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Reject task " .. self.TaskName, self.Menu, self.MenuReject, self ) - end - - --- Menu function. - -- @param #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:MenuAssign() - self:E( ) - - self:__Assign( 1 ) - end - - --- Menu function. - -- @param #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:MenuReject() - self:E( ) - - self:__Reject( 1 ) - end - - --- StateMachine callback function - -- @param #ACT_ASSIGN_MENU_ACCEPT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, From, Event, To ) - self:E( { ProcessUnit.UnitNameFrom, Event, To } ) - - self.Menu:Remove() - end - - --- StateMachine callback function - -- @param #ACT_ASSIGN_MENU_ACCEPT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, From, Event, To ) - self:E( { ProcessUnit.UnitName, From, Event, To } ) - - self.Menu:Remove() - --TODO: need to resolve this problem ... it has to do with the events ... - --self.Task:UnAssignFromUnit( ProcessUnit )needs to become a callback funtion call upon the event - ProcessUnit:Destroy() - end - -end -- ACT_ASSIGN_MENU_ACCEPT ---- (SP) (MP) (FSM) Route AI or players through waypoints or to zones. --- --- === --- --- # @{#ACT_ROUTE} FSM class, extends @{Fsm#FSM_PROCESS} --- --- ## ACT_ROUTE state machine: --- --- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. --- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. --- Each derived class follows exactly the same process, using the same events and following the same state transitions, --- but will have **different implementation behaviour** upon each event or state transition. --- --- ### ACT_ROUTE **Events**: --- --- These are the events defined in this class: --- --- * **Start**: The process is started. The process will go into the Report state. --- * **Report**: The process is reporting to the player the route to be followed. --- * **Route**: The process is routing the controllable. --- * **Pause**: The process is pausing the route of the controllable. --- * **Arrive**: The controllable has arrived at a route point. --- * **More**: There are more route points that need to be followed. The process will go back into the Report state. --- * **NoMore**: There are no more route points that need to be followed. The process will go into the Success state. --- --- ### ACT_ROUTE **Event methods**: --- --- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. --- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine: --- --- * **Immediate**: The event method has exactly the name of the event. --- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. --- --- ### ACT_ROUTE **States**: --- --- * **None**: The controllable did not receive route commands. --- * **Arrived (*)**: The controllable has arrived at a route point. --- * **Aborted (*)**: The controllable has aborted the route path. --- * **Routing**: The controllable is understay to the route point. --- * **Pausing**: The process is pausing the routing. AI air will go into hover, AI ground will stop moving. Players can fly around. --- * **Success (*)**: All route points were reached. --- * **Failed (*)**: The process has failed. --- --- (*) End states of the process. --- --- ### ACT_ROUTE state transition methods: --- --- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. --- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Before** the state transition. --- The state transition method needs to start with the name **OnBefore + the name of the state**. --- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, --- but then you'll need to specify your own logic using the AIControllable! --- --- * **After** the state transition. --- The state transition method needs to start with the name **OnAfter + the name of the state**. --- These state transition methods need to provide a return value, which is specified at the function description. --- --- === --- --- # 1) @{#ACT_ROUTE_ZONE} class, extends @{Fsm.Route#ACT_ROUTE} --- --- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Controllable} player @{Unit} to a @{Zone}. --- The player receives on perioding times messages with the coordinates of the route to follow. --- Upon arrival at the zone, a confirmation of arrival is sent, and the process will be ended. --- --- # 1.1) ACT_ROUTE_ZONE constructor: --- --- * @{#ACT_ROUTE_ZONE.New}(): Creates a new ACT_ROUTE_ZONE object. --- --- === --- --- @module Route - - -do -- ACT_ROUTE - - --- ACT_ROUTE class - -- @type ACT_ROUTE - -- @field Tasking.Task#TASK TASK - -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE Zone - -- @extends Core.Fsm#FSM_PROCESS - ACT_ROUTE = { - ClassName = "ACT_ROUTE", - } - - - --- Creates a new routing state machine. The process will route a CLIENT to a ZONE until the CLIENT is within that ZONE. - -- @param #ACT_ROUTE self - -- @return #ACT_ROUTE self - function ACT_ROUTE:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ROUTE" ) ) -- Core.Fsm#FSM_PROCESS - - self:AddTransition( "None", "Start", "Routing" ) - self:AddTransition( "*", "Report", "Reporting" ) - self:AddTransition( "*", "Route", "Routing" ) - self:AddTransition( "Routing", "Pause", "Pausing" ) - self:AddTransition( "*", "Abort", "Aborted" ) - self:AddTransition( "Routing", "Arrive", "Arrived" ) - self:AddTransition( "Arrived", "Success", "Success" ) - self:AddTransition( "*", "Fail", "Failed" ) - self:AddTransition( "", "", "" ) - self:AddTransition( "", "", "" ) - - self:AddEndState( "Arrived" ) - self:AddEndState( "Failed" ) - - self:SetStartState( "None" ) - - return self - end - - --- Task Events - - --- StateMachine callback function - -- @param #ACT_ROUTE self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ROUTE:onafterStart( ProcessUnit, From, Event, To ) - - - self:__Route( 1 ) - end - - --- Check if the controllable has arrived. - -- @param #ACT_ROUTE self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @return #boolean - function ACT_ROUTE:onfuncHasArrived( ProcessUnit ) - return false - end - - --- StateMachine callback function - -- @param #ACT_ROUTE self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ROUTE:onbeforeRoute( ProcessUnit, From, Event, To ) - self:F( { "BeforeRoute 1", self.DisplayCount, self.DisplayInterval } ) - - if ProcessUnit:IsAlive() then - self:F( "BeforeRoute 2" ) - local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic - if self.DisplayCount >= self.DisplayInterval then - self:T( { HasArrived = HasArrived } ) - if not HasArrived then - self:Report() - end - self.DisplayCount = 1 - else - self.DisplayCount = self.DisplayCount + 1 - end - - self:T( { DisplayCount = self.DisplayCount } ) - - if HasArrived then - self:__Arrive( 1 ) - else - self:__Route( 1 ) - end - - return HasArrived -- if false, then the event will not be executed... - end - - return false - - end - -end -- ACT_ROUTE - - -do -- ACT_ROUTE_POINT - - --- ACT_ROUTE_POINT class - -- @type ACT_ROUTE_POINT - -- @field Tasking.Task#TASK TASK - -- @extends #ACT_ROUTE - ACT_ROUTE_POINT = { - ClassName = "ACT_ROUTE_POINT", - } - - - --- Creates a new routing state machine. - -- The task will route a controllable to a PointVec2 until the controllable is within the Range. - -- @param #ACT_ROUTE_POINT self - -- @param Core.Point#POINT_VEC2 The PointVec2 to Target. - -- @param #number Range The Distance to Target. - -- @param Core.Zone#ZONE_BASE Zone - function ACT_ROUTE_POINT:New( PointVec2, Range ) - local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_POINT - - self.PointVec2 = PointVec2 - self.Range = Range or 0 - - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - - return self - end - - function ACT_ROUTE_POINT:Init( FsmRoute ) - - self.PointVec2 = FsmRoute.PointVec2 - self.Range = FsmRoute.Range or 0 - - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - end - - --- Set PointVec2 - -- @param #ACT_ROUTE_POINT self - -- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to. - function ACT_ROUTE_POINT:SetPointVec2( PointVec2 ) - self:F2( { PointVec2 } ) - self.PointVec2 = PointVec2 - end - - --- Get PointVec2 - -- @param #ACT_ROUTE_POINT self - -- @return Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to. - function ACT_ROUTE_POINT:GetPointVec2() - self:F2( { self.PointVec2 } ) - return self.PointVec2 - end - - --- Set Range around PointVec2 - -- @param #ACT_ROUTE_POINT self - -- @param #number Range The Range to consider the arrival. Default is 10000 meters. - function ACT_ROUTE_POINT:SetRange( Range ) - self:F2( { self.Range } ) - self.Range = Range or 10000 - end - - --- Get Range around PointVec2 - -- @param #ACT_ROUTE_POINT self - -- @return #number The Range to consider the arrival. Default is 10000 meters. - function ACT_ROUTE_POINT:GetRange() - return self.Range - end - - --- Method override to check if the controllable has arrived. - -- @param #ACT_ROUTE_POINT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @return #boolean - function ACT_ROUTE_POINT:onfuncHasArrived( ProcessUnit ) - - if ProcessUnit:IsAlive() then - local Distance = self.PointVec2:Get2DDistance( ProcessUnit:GetPointVec2() ) - - if Distance <= self.Range then - local RouteText = "You have arrived." - self:Message( RouteText ) - return true - end - end - - return false - end - - --- Task Events - - --- StateMachine callback function - -- @param #ACT_ROUTE_POINT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ROUTE_POINT:onenterReporting( ProcessUnit, From, Event, To ) - - local TaskUnitPointVec2 = ProcessUnit:GetPointVec2() - local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( self.PointVec2 ) .. " km." - self:Message( RouteText ) - end - -end -- ACT_ROUTE_POINT - - -do -- ACT_ROUTE_ZONE - - --- ACT_ROUTE_ZONE class - -- @type ACT_ROUTE_ZONE - -- @field Tasking.Task#TASK TASK - -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE Zone - -- @extends #ACT_ROUTE - ACT_ROUTE_ZONE = { - ClassName = "ACT_ROUTE_ZONE", - } - - - --- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE. - -- @param #ACT_ROUTE_ZONE self - -- @param Core.Zone#ZONE_BASE Zone - function ACT_ROUTE_ZONE:New( Zone ) - local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE - - self.Zone = Zone - - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - - return self - end - - function ACT_ROUTE_ZONE:Init( FsmRoute ) - - self.Zone = FsmRoute.Zone - - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - end - - --- Set Zone - -- @param #ACT_ROUTE_ZONE self - -- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to. - function ACT_ROUTE_ZONE:SetZone( Zone ) - self.Zone = Zone - end - - --- Get Zone - -- @param #ACT_ROUTE_ZONE self - -- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to. - function ACT_ROUTE_ZONE:GetZone() - return self.Zone - end - - --- Method override to check if the controllable has arrived. - -- @param #ACT_ROUTE self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @return #boolean - function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit ) - - if ProcessUnit:IsInZone( self.Zone ) then - local RouteText = "You have arrived within the zone." - self:Message( RouteText ) - end - - return ProcessUnit:IsInZone( self.Zone ) - end - - --- Task Events - - --- StateMachine callback function - -- @param #ACT_ROUTE_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ROUTE_ZONE:onenterReporting( ProcessUnit, From, Event, To ) - - local ZoneVec2 = self.Zone:GetVec2() - local ZonePointVec2 = POINT_VEC2:New( ZoneVec2.x, ZoneVec2.y ) - local TaskUnitVec2 = ProcessUnit:GetVec2() - local TaskUnitPointVec2 = POINT_VEC2:New( TaskUnitVec2.x, TaskUnitVec2.y ) - local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( ZonePointVec2 ) .. " km." - self:Message( RouteText ) - end - -end -- ACT_ROUTE_ZONE ---- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Unit}s. --- --- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG) --- --- === --- --- @module Account - - -do -- ACT_ACCOUNT - - --- # @{#ACT_ACCOUNT} FSM class, extends @{Fsm#FSM_PROCESS} - -- - -- ## ACT_ACCOUNT state machine: - -- - -- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. - -- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. - -- Each derived class follows exactly the same process, using the same events and following the same state transitions, - -- but will have **different implementation behaviour** upon each event or state transition. - -- - -- ### ACT_ACCOUNT States - -- - -- * **Asigned**: The player is assigned. - -- * **Waiting**: Waiting for an event. - -- * **Report**: Reporting. - -- * **Account**: Account for an event. - -- * **Accounted**: All events have been accounted for, end of the process. - -- * **Failed**: Failed the process. - -- - -- ### ACT_ACCOUNT Events - -- - -- * **Start**: Start the process. - -- * **Wait**: Wait for an event. - -- * **Report**: Report the status of the accounting. - -- * **Event**: An event happened, process the event. - -- * **More**: More targets. - -- * **NoMore (*)**: No more targets. - -- * **Fail (*)**: The action process has failed. - -- - -- (*) End states of the process. - -- - -- ### ACT_ACCOUNT state transition methods: - -- - -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. - -- There are 2 moments when state transition methods will be called by the state machine: - -- - -- * **Before** the state transition. - -- The state transition method needs to start with the name **OnBefore + the name of the state**. - -- If the state transition method returns false, then the processing of the state transition will not be done! - -- If you want to change the behaviour of the AIControllable at this event, return false, - -- but then you'll need to specify your own logic using the AIControllable! - -- - -- * **After** the state transition. - -- The state transition method needs to start with the name **OnAfter + the name of the state**. - -- These state transition methods need to provide a return value, which is specified at the function description. - -- - -- @type ACT_ACCOUNT - -- @field Set#SET_UNIT TargetSetUnit - -- @extends Core.Fsm#FSM_PROCESS - ACT_ACCOUNT = { - ClassName = "ACT_ACCOUNT", - TargetSetUnit = nil, - } - - --- Creates a new DESTROY process. - -- @param #ACT_ACCOUNT self - -- @return #ACT_ACCOUNT - function ACT_ACCOUNT:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM_PROCESS:New() ) -- Core.Fsm#FSM_PROCESS - - self:AddTransition( "Assigned", "Start", "Waiting") - self:AddTransition( "*", "Wait", "Waiting") - self:AddTransition( "*", "Report", "Report") - self:AddTransition( "*", "Event", "Account") - self:AddTransition( "Account", "More", "Wait") - self:AddTransition( "Account", "NoMore", "Accounted") - self:AddTransition( "*", "Fail", "Failed") - - self:AddEndState( "Accounted" ) - self:AddEndState( "Failed" ) - - self:SetStartState( "Assigned" ) - - return self - end - - --- Process Events - - --- StateMachine callback function - -- @param #ACT_ACCOUNT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ACCOUNT:onafterStart( ProcessUnit, From, Event, To ) - - self:HandleEvent( EVENTS.Dead, self.onfuncEventDead ) - - self:__Wait( 1 ) - end - - - --- StateMachine callback function - -- @param #ACT_ACCOUNT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ACCOUNT:onenterWaiting( ProcessUnit, From, Event, To ) - - if self.DisplayCount >= self.DisplayInterval then - self:Report() - self.DisplayCount = 1 - else - self.DisplayCount = self.DisplayCount + 1 - end - - return true -- Process always the event. - end - - --- StateMachine callback function - -- @param #ACT_ACCOUNT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ACCOUNT:onafterEvent( ProcessUnit, From, Event, To, Event ) - - self:__NoMore( 1 ) - end - -end -- ACT_ACCOUNT - -do -- ACT_ACCOUNT_DEADS - - --- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Fsm.Account#ACT_ACCOUNT} - -- - -- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units. - -- The process is given a @{Set} of units that will be tracked upon successful destruction. - -- The process will end after each target has been successfully destroyed. - -- Each successful dead will trigger an Account state transition that can be scored, modified or administered. - -- - -- - -- ## ACT_ACCOUNT_DEADS constructor: - -- - -- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object. - -- - -- @type ACT_ACCOUNT_DEADS - -- @field Set#SET_UNIT TargetSetUnit - -- @extends #ACT_ACCOUNT - ACT_ACCOUNT_DEADS = { - ClassName = "ACT_ACCOUNT_DEADS", - TargetSetUnit = nil, - } - - - --- Creates a new DESTROY process. - -- @param #ACT_ACCOUNT_DEADS self - -- @param Set#SET_UNIT TargetSetUnit - -- @param #string TaskName - function ACT_ACCOUNT_DEADS:New( TargetSetUnit, TaskName ) - -- Inherits from BASE - local self = BASE:Inherit( self, ACT_ACCOUNT:New() ) -- #ACT_ACCOUNT_DEADS - - self.TargetSetUnit = TargetSetUnit - self.TaskName = TaskName - - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - self.DisplayCategory = "HQ" -- Targets is the default display category - - return self - end - - function ACT_ACCOUNT_DEADS:Init( FsmAccount ) - - self.TargetSetUnit = FsmAccount.TargetSetUnit - self.TaskName = FsmAccount.TaskName - end - - --- Process Events - - --- StateMachine callback function - -- @param #ACT_ACCOUNT_DEADS self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To ) - self:E( { ProcessUnit, From, Event, To } ) - - self:Message( "Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed." ) - end - - - --- StateMachine callback function - -- @param #ACT_ACCOUNT_DEADS self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, Task, From, Event, To, EventData ) - self:T( { ProcessUnit, EventData, From, Event, To } ) - - self:T({self.Controllable}) - - self.TargetSetUnit:Flush() - - self:T( { "Before sending Message", EventData.IniUnitName, self.TargetSetUnit:FindUnit( EventData.IniUnitName ) } ) - if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then - self:T( "Sending Message" ) - local TaskGroup = ProcessUnit:GetGroup() - self.TargetSetUnit:Remove( EventData.IniUnitName ) - self:Message( "You hit a target. Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." ) - end - self:T( { "After sending Message" } ) - end - - --- StateMachine callback function - -- @param #ACT_ACCOUNT_DEADS self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To ) - - if self.TargetSetUnit:Count() > 0 then - self:__More( 1 ) - else - self:__NoMore( 1 ) - end - end - - --- DCS Events - - --- @param #ACT_ACCOUNT_DEADS self - -- @param Event#EVENTDATA EventData - function ACT_ACCOUNT_DEADS:onfuncEventDead( EventData ) - self:T( { "EventDead", EventData } ) - - if EventData.IniDCSUnit then - self:Event( EventData ) - end - end - -end -- ACT_ACCOUNT DEADS ---- (SP) (MP) (FSM) Route AI or players through waypoints or to zones. --- --- === --- --- # @{#ACT_ASSIST} FSM class, extends @{Fsm#FSM_PROCESS} --- --- ## ACT_ASSIST state machine: --- --- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. --- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. --- Each derived class follows exactly the same process, using the same events and following the same state transitions, --- but will have **different implementation behaviour** upon each event or state transition. --- --- ### ACT_ASSIST **Events**: --- --- These are the events defined in this class: --- --- * **Start**: The process is started. --- * **Next**: The process is smoking the targets in the given zone. --- --- ### ACT_ASSIST **Event methods**: --- --- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. --- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine: --- --- * **Immediate**: The event method has exactly the name of the event. --- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. --- --- ### ACT_ASSIST **States**: --- --- * **None**: The controllable did not receive route commands. --- * **AwaitSmoke (*)**: The process is awaiting to smoke the targets in the zone. --- * **Smoking (*)**: The process is smoking the targets in the zone. --- * **Failed (*)**: The process has failed. --- --- (*) End states of the process. --- --- ### ACT_ASSIST state transition methods: --- --- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. --- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Before** the state transition. --- The state transition method needs to start with the name **OnBefore + the name of the state**. --- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, --- but then you'll need to specify your own logic using the AIControllable! --- --- * **After** the state transition. --- The state transition method needs to start with the name **OnAfter + the name of the state**. --- These state transition methods need to provide a return value, which is specified at the function description. --- --- === --- --- # 1) @{#ACT_ASSIST_SMOKE_TARGETS_ZONE} class, extends @{Fsm.Route#ACT_ASSIST} --- --- The ACT_ASSIST_SMOKE_TARGETS_ZONE class implements the core functions to smoke targets in a @{Zone}. --- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour. --- At random intervals, a new target is smoked. --- --- # 1.1) ACT_ASSIST_SMOKE_TARGETS_ZONE constructor: --- --- * @{#ACT_ASSIST_SMOKE_TARGETS_ZONE.New}(): Creates a new ACT_ASSIST_SMOKE_TARGETS_ZONE object. --- --- === --- --- @module Smoke - -do -- ACT_ASSIST - - --- ACT_ASSIST class - -- @type ACT_ASSIST - -- @extends Core.Fsm#FSM_PROCESS - ACT_ASSIST = { - ClassName = "ACT_ASSIST", - } - - --- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. - -- @param #ACT_ASSIST self - -- @return #ACT_ASSIST - function ACT_ASSIST:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ASSIST" ) ) -- Core.Fsm#FSM_PROCESS - - self:AddTransition( "None", "Start", "AwaitSmoke" ) - self:AddTransition( "AwaitSmoke", "Next", "Smoking" ) - self:AddTransition( "Smoking", "Next", "AwaitSmoke" ) - self:AddTransition( "*", "Stop", "Success" ) - self:AddTransition( "*", "Fail", "Failed" ) - - self:AddEndState( "Failed" ) - self:AddEndState( "Success" ) - - self:SetStartState( "None" ) - - return self - end - - --- Task Events - - --- StateMachine callback function - -- @param #ACT_ASSIST self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To ) - - local ProcessGroup = ProcessUnit:GetGroup() - local MissionMenu = self:GetMission():GetMenu( ProcessGroup ) - - local function MenuSmoke( MenuParam ) - self:E( MenuParam ) - local self = MenuParam.self - local SmokeColor = MenuParam.SmokeColor - self.SmokeColor = SmokeColor - self:__Next( 1 ) - end - - self.Menu = MENU_GROUP:New( ProcessGroup, "Target acquisition", MissionMenu ) - self.MenuSmokeBlue = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop blue smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Blue } ) - self.MenuSmokeGreen = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop green smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Green } ) - self.MenuSmokeOrange = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop Orange smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Orange } ) - self.MenuSmokeRed = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop Red smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Red } ) - self.MenuSmokeWhite = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop White smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.White } ) - end - - --- StateMachine callback function - -- @param #ACT_ASSIST self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To ) - - self.Menu:Remove() -- When stopped, remove the menus - end - -end - -do -- ACT_ASSIST_SMOKE_TARGETS_ZONE - - --- ACT_ASSIST_SMOKE_TARGETS_ZONE class - -- @type ACT_ASSIST_SMOKE_TARGETS_ZONE - -- @field Set#SET_UNIT TargetSetUnit - -- @field Core.Zone#ZONE_BASE TargetZone - -- @extends #ACT_ASSIST - ACT_ASSIST_SMOKE_TARGETS_ZONE = { - ClassName = "ACT_ASSIST_SMOKE_TARGETS_ZONE", - } - --- function ACT_ASSIST_SMOKE_TARGETS_ZONE:_Destructor() --- self:E("_Destructor") --- --- self.Menu:Remove() --- self:EventRemoveAll() --- end - - --- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. - -- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self - -- @param Set#SET_UNIT TargetSetUnit - -- @param Core.Zone#ZONE_BASE TargetZone - function ACT_ASSIST_SMOKE_TARGETS_ZONE:New( TargetSetUnit, TargetZone ) - local self = BASE:Inherit( self, ACT_ASSIST:New() ) -- #ACT_ASSIST - - self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - - return self - end - - function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( FsmSmoke ) - - self.TargetSetUnit = FsmSmoke.TargetSetUnit - self.TargetZone = FsmSmoke.TargetZone - end - - --- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. - -- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self - -- @param Set#SET_UNIT TargetSetUnit - -- @param Core.Zone#ZONE_BASE TargetZone - -- @return #ACT_ASSIST_SMOKE_TARGETS_ZONE self - function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( TargetSetUnit, TargetZone ) - - self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - - return self - end - - --- StateMachine callback function - -- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIST_SMOKE_TARGETS_ZONE:onenterSmoking( ProcessUnit, From, Event, To ) - - self.TargetSetUnit:ForEachUnit( - --- @param Wrapper.Unit#UNIT SmokeUnit - function( SmokeUnit ) - if math.random( 1, ( 100 * self.TargetSetUnit:Count() ) / 4 ) <= 100 then - SCHEDULER:New( self, - function() - if SmokeUnit:IsAlive() then - SmokeUnit:Smoke( self.SmokeColor, 150 ) - end - end, {}, math.random( 10, 60 ) - ) - end - end - ) - - end - -end--- A COMMANDCENTER is the owner of multiple missions within MOOSE. --- A COMMANDCENTER governs multiple missions, the tasking and the reporting. --- @module CommandCenter - - - ---- The REPORT class --- @type REPORT --- @extends Core.Base#BASE -REPORT = { - ClassName = "REPORT", -} - ---- Create a new REPORT. --- @param #REPORT self --- @param #string Title --- @return #REPORT -function REPORT:New( Title ) - - local self = BASE:Inherit( self, BASE:New() ) - - self.Report = {} - if Title then - self.Report[#self.Report+1] = Title - end - - return self -end - ---- Add a new line to a REPORT. --- @param #REPORT self --- @param #string Text --- @return #REPORT -function REPORT:Add( Text ) - self.Report[#self.Report+1] = Text - return self.Report[#self.Report] -end - ---- Produces the text of the report, taking into account an optional delimeter, which is \n by default. --- @param #REPORT self --- @param #string Delimiter (optional) A delimiter text. --- @return #string The report text. -function REPORT:Text( Delimiter ) - Delimiter = Delimiter or "\n" - local ReportText = table.concat( self.Report, Delimiter ) or "" - return ReportText -end - ---- The COMMANDCENTER class --- @type COMMANDCENTER --- @field Wrapper.Group#GROUP HQ --- @field Dcs.DCSCoalitionWrapper.Object#coalition CommandCenterCoalition --- @list Missions --- @extends Core.Base#BASE -COMMANDCENTER = { - ClassName = "COMMANDCENTER", - CommandCenterName = "", - CommandCenterCoalition = nil, - CommandCenterPositionable = nil, - Name = "", -} ---- The constructor takes an IDENTIFIABLE as the HQ command center. --- @param #COMMANDCENTER self --- @param Wrapper.Positionable#POSITIONABLE CommandCenterPositionable --- @param #string CommandCenterName --- @return #COMMANDCENTER -function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) - - local self = BASE:Inherit( self, BASE:New() ) - - self.CommandCenterPositionable = CommandCenterPositionable - self.CommandCenterName = CommandCenterName or CommandCenterPositionable:GetName() - self.CommandCenterCoalition = CommandCenterPositionable:GetCoalition() - - self.Missions = {} - - self:HandleEvent( EVENTS.Birth, - --- @param #COMMANDCENTER self - -- @param Core.Event#EVENTDATA EventData - function( self, EventData ) - if EventData.IniObjectCategory == 1 then - local EventGroup = GROUP:Find( EventData.IniDCSGroup ) - if EventGroup and self:HasGroup( EventGroup ) then - local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu ) - local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup ) - local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) - self:ReportSummary( EventGroup ) - end - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! - Mission:JoinUnit( PlayerUnit, PlayerGroup ) - Mission:ReportDetails() - end - end - - end - ) - - -- When a player enters a client or a unit, the CommandCenter will check for each Mission and each Task in the Mission if the player has things to do. - -- For these elements, it will= - -- - Set the correct menu. - -- - Assign the PlayerUnit to the Task if required. - -- - Send a message to the other players in the group that this player has joined. - self:HandleEvent( EVENTS.PlayerEnterUnit, - --- @param #COMMANDCENTER self - -- @param Core.Event#EVENTDATA EventData - function( self, EventData ) - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! - Mission:JoinUnit( PlayerUnit, PlayerGroup ) - Mission:ReportDetails() - end - end - ) - - -- Handle when a player leaves a slot and goes back to spectators ... - -- The PlayerUnit will be UnAssigned from the Task. - -- When there is no Unit left running the Task, the Task goes into Abort... - self:HandleEvent( EVENTS.PlayerLeaveUnit, - --- @param #TASK self - -- @param Core.Event#EVENTDATA EventData - function( self, EventData ) - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:AbortUnit( PlayerUnit ) - end - end - ) - - -- Handle when a player leaves a slot and goes back to spectators ... - -- The PlayerUnit will be UnAssigned from the Task. - -- When there is no Unit left running the Task, the Task goes into Abort... - self:HandleEvent( EVENTS.Crash, - --- @param #TASK self - -- @param Core.Event#EVENTDATA EventData - function( self, EventData ) - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - Mission:CrashUnit( PlayerUnit ) - end - end - ) - - return self -end - ---- Gets the name of the HQ command center. --- @param #COMMANDCENTER self --- @return #string -function COMMANDCENTER:GetName() - - return self.CommandCenterName -end - ---- Gets the POSITIONABLE of the HQ command center. --- @param #COMMANDCENTER self --- @return Wrapper.Positionable#POSITIONABLE -function COMMANDCENTER:GetPositionable() - return self.CommandCenterPositionable -end - ---- Get the Missions governed by the HQ command center. --- @param #COMMANDCENTER self --- @return #list -function COMMANDCENTER:GetMissions() - - return self.Missions -end - ---- Add a MISSION to be governed by the HQ command center. --- @param #COMMANDCENTER self --- @param Tasking.Mission#MISSION Mission --- @return Tasking.Mission#MISSION -function COMMANDCENTER:AddMission( Mission ) - - self.Missions[Mission] = Mission - - return Mission -end - ---- Removes a MISSION to be governed by the HQ command center. --- The given Mission is not nilified. --- @param #COMMANDCENTER self --- @param Tasking.Mission#MISSION Mission --- @return Tasking.Mission#MISSION -function COMMANDCENTER:RemoveMission( Mission ) - - self.Missions[Mission] = nil - - return Mission -end - ---- Sets the menu structure of the Missions governed by the HQ command center. --- @param #COMMANDCENTER self -function COMMANDCENTER:SetMenu() - self:F() - - self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "Command Center (" .. self:GetName() .. ")" ) - - local MenuTime = timer.getTime() - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:SetMenu( MenuTime ) - end - - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:RemoveMenu( MenuTime ) - end - -end - ---- Gets the commandcenter menu structure governed by the HQ command center. --- @param #COMMANDCENTER self --- @return Core.Menu#MENU_COALITION -function COMMANDCENTER:GetMenu() - self:F() - return self.CommandCenterMenu -end - ---- Checks of the COMMANDCENTER has a GROUP. --- @param #COMMANDCENTER self --- @param Wrapper.Group#GROUP --- @return #boolean -function COMMANDCENTER:HasGroup( MissionGroup ) - - local Has = false - - for MissionID, Mission in pairs( self.Missions ) do - local Mission = Mission -- Tasking.Mission#MISSION - if Mission:HasGroup( MissionGroup ) then - Has = true - break - end - end - - return Has -end - ---- Send a CC message to the coalition of the CC. --- @param #COMMANDCENTER self -function COMMANDCENTER:MessageToAll( Message ) - - self:GetPositionable():MessageToAll( Message, 20, self:GetName() ) - -end - ---- Send a CC message to a GROUP. --- @param #COMMANDCENTER self --- @param #string Message --- @param Wrapper.Group#GROUP TaskGroup --- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. -function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name ) - - local Prefix = "@ Group" - Prefix = Prefix .. ( Name and " (" .. Name .. "): " or '' ) - Message = Prefix .. Message - self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() ) - -end - ---- Send a CC message to the coalition of the CC. --- @param #COMMANDCENTER self -function COMMANDCENTER:MessageToCoalition( Message ) - - local CCCoalition = self:GetPositionable():GetCoalition() - --TODO: Fix coalition bug! - self:GetPositionable():MessageToCoalition( Message, 20, CCCoalition, self:GetName() ) - -end - - ---- Report the status of all MISSIONs to a GROUP. --- Each Mission is listed, with an indication how many Tasks are still to be completed. --- @param #COMMANDCENTER self -function COMMANDCENTER:ReportSummary( ReportGroup ) - self:E( ReportGroup ) - - local Report = REPORT:New() - - for MissionID, Mission in pairs( self.Missions ) do - local Mission = Mission -- Tasking.Mission#MISSION - Report:Add( " - " .. Mission:ReportOverview() ) - end - - self:GetPositionable():MessageToGroup( Report:Text(), 30, ReportGroup ) - -end - ---- Report the status of a Task to a Group. --- Report the details of a Mission, listing the Mission, and all the Task details. --- @param #COMMANDCENTER self -function COMMANDCENTER:ReportDetails( ReportGroup, Task ) - self:E( ReportGroup ) - - local Report = REPORT:New() - - for MissionID, Mission in pairs( self.Missions ) do - local Mission = Mission -- Tasking.Mission#MISSION - Report:Add( " - " .. Mission:ReportDetails() ) - end - - self:GetPositionable():MessageToGroup( Report:Text(), 30, ReportGroup ) -end - ---- A MISSION is the main owner of a Mission orchestration within MOOSE . The Mission framework orchestrates @{CLIENT}s, @{TASK}s, @{STAGE}s etc. --- A @{CLIENT} needs to be registered within the @{MISSION} through the function @{AddClient}. A @{TASK} needs to be registered within the @{MISSION} through the function @{AddTask}. --- @module Mission - ---- The MISSION class --- @type MISSION --- @field #MISSION.Clients _Clients --- @field Core.Menu#MENU_COALITION MissionMenu --- @field #string MissionBriefing --- @extends Core.Fsm#FSM -MISSION = { - ClassName = "MISSION", - Name = "", - MissionStatus = "PENDING", -} - ---- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. --- @param #MISSION self --- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter --- @param #string MissionName is the name of the mission. This name will be used to reference the status of each mission by the players. --- @param #string MissionPriority is a string indicating the "priority" of the Mission. f.e. "Primary", "Secondary" or "First", "Second". It is free format and up to the Mission designer to choose. There are no rules behind this field. --- @param #string MissionBriefing is a string indicating the mission briefing to be shown when a player joins a @{CLIENT}. --- @param Dcs.DCSCoalitionWrapper.Object#coalition MissionCoalition is a string indicating the coalition or party to which this mission belongs to. It is free format and can be chosen freely by the mission designer. Note that this field is not to be confused with the coalition concept of the ME. Examples of a Mission Coalition could be "NATO", "CCCP", "Intruders", "Terrorists"... --- @return #MISSION self -function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition ) - - local self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM - - self:SetStartState( "Idle" ) - - self:AddTransition( "Idle", "Start", "Ongoing" ) - - --- OnLeave Transition Handler for State Idle. - -- @function [parent=#MISSION] OnLeaveIdle - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Idle. - -- @function [parent=#MISSION] OnEnterIdle - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnLeave Transition Handler for State Ongoing. - -- @function [parent=#MISSION] OnLeaveOngoing - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Ongoing. - -- @function [parent=#MISSION] OnEnterOngoing - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Start. - -- @function [parent=#MISSION] OnBeforeStart - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Start. - -- @function [parent=#MISSION] OnAfterStart - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Start. - -- @function [parent=#MISSION] Start - -- @param #MISSION self - - --- Asynchronous Event Trigger for Event Start. - -- @function [parent=#MISSION] __Start - -- @param #MISSION self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "Ongoing", "Stop", "Idle" ) - - --- OnLeave Transition Handler for State Idle. - -- @function [parent=#MISSION] OnLeaveIdle - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Idle. - -- @function [parent=#MISSION] OnEnterIdle - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Stop. - -- @function [parent=#MISSION] OnBeforeStop - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Stop. - -- @function [parent=#MISSION] OnAfterStop - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Stop. - -- @function [parent=#MISSION] Stop - -- @param #MISSION self - - --- Asynchronous Event Trigger for Event Stop. - -- @function [parent=#MISSION] __Stop - -- @param #MISSION self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "Ongoing", "Complete", "Completed" ) - - --- OnLeave Transition Handler for State Completed. - -- @function [parent=#MISSION] OnLeaveCompleted - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Completed. - -- @function [parent=#MISSION] OnEnterCompleted - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Complete. - -- @function [parent=#MISSION] OnBeforeComplete - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Complete. - -- @function [parent=#MISSION] OnAfterComplete - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Complete. - -- @function [parent=#MISSION] Complete - -- @param #MISSION self - - --- Asynchronous Event Trigger for Event Complete. - -- @function [parent=#MISSION] __Complete - -- @param #MISSION self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Fail", "Failed" ) - - --- OnLeave Transition Handler for State Failed. - -- @function [parent=#MISSION] OnLeaveFailed - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Failed. - -- @function [parent=#MISSION] OnEnterFailed - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Fail. - -- @function [parent=#MISSION] OnBeforeFail - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Fail. - -- @function [parent=#MISSION] OnAfterFail - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Fail. - -- @function [parent=#MISSION] Fail - -- @param #MISSION self - - --- Asynchronous Event Trigger for Event Fail. - -- @function [parent=#MISSION] __Fail - -- @param #MISSION self - -- @param #number Delay The delay in seconds. - - self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) - - self.CommandCenter = CommandCenter - CommandCenter:AddMission( self ) - - self.Name = MissionName - self.MissionPriority = MissionPriority - self.MissionBriefing = MissionBriefing - self.MissionCoalition = MissionCoalition - - self.Tasks = {} - - -- Private implementations - - - - return self -end - --- FSM function for a MISSION --- @param #MISSION self --- @param #string From --- @param #string Event --- @param #string To -function MISSION:onbeforeComplete( From, Event, To ) - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if not Task:IsStateSuccess() and not Task:IsStateFailed() and not Task:IsStateAborted() and not Task:IsStateCancelled() then - return false -- Mission cannot be completed. Other Tasks are still active. - end - end - return true -- Allow Mission completion. -end - --- FSM function for a MISSION --- @param #MISSION self --- @param #string From --- @param #string Event --- @param #string To -function MISSION:onenterCompleted( From, Event, To ) - - self:GetCommandCenter():MessageToCoalition( "Mission " .. self:GetName() .. " has been completed! Good job guys!" ) -end - ---- Gets the mission name. --- @param #MISSION self --- @return #MISSION self -function MISSION:GetName() - return self.Name -end - ---- Add a Unit to join the Mission. --- For each Task within the Mission, the Unit is joined with the Task. --- If the Unit was not part of a Task in the Mission, false is returned. --- If the Unit is part of a Task in the Mission, true is returned. --- @param #MISSION self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player joining the Mission. --- @param Wrapper.Group#GROUP PlayerGroup The GROUP of the player joining the Mission. --- @return #boolean true if Unit is part of a Task in the Mission. -function MISSION:JoinUnit( PlayerUnit, PlayerGroup ) - self:F( { PlayerUnit = PlayerUnit, PlayerGroup = PlayerGroup } ) - - local PlayerUnitAdded = false - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if Task:JoinUnit( PlayerUnit, PlayerGroup ) then - PlayerUnitAdded = true - end - end - - return PlayerUnitAdded -end - ---- Aborts a PlayerUnit from the Mission. --- For each Task within the Mission, the PlayerUnit is removed from Task where it is assigned. --- If the Unit was not part of a Task in the Mission, false is returned. --- If the Unit is part of a Task in the Mission, true is returned. --- @param #MISSION self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player joining the Mission. --- @return #boolean true if Unit is part of a Task in the Mission. -function MISSION:AbortUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - local PlayerUnitRemoved = false - - for TaskID, Task in pairs( self:GetTasks() ) do - if Task:AbortUnit( PlayerUnit ) then - PlayerUnitRemoved = true - end - end - - return PlayerUnitRemoved -end - ---- Handles a crash of a PlayerUnit from the Mission. --- For each Task within the Mission, the PlayerUnit is removed from Task where it is assigned. --- If the Unit was not part of a Task in the Mission, false is returned. --- If the Unit is part of a Task in the Mission, true is returned. --- @param #MISSION self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player crashing. --- @return #boolean true if Unit is part of a Task in the Mission. -function MISSION:CrashUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - local PlayerUnitRemoved = false - - for TaskID, Task in pairs( self:GetTasks() ) do - if Task:CrashUnit( PlayerUnit ) then - PlayerUnitRemoved = true - end - end - - return PlayerUnitRemoved -end - ---- Add a scoring to the mission. --- @param #MISSION self --- @return #MISSION self -function MISSION:AddScoring( Scoring ) - self.Scoring = Scoring - return self -end - ---- Get the scoring object of a mission. --- @param #MISSION self --- @return #SCORING Scoring -function MISSION:GetScoring() - return self.Scoring -end - ---- Get the groups for which TASKS are given in the mission --- @param #MISSION self --- @return Core.Set#SET_GROUP -function MISSION:GetGroups() - - local SetGroup = SET_GROUP:New() - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - local GroupSet = Task:GetGroups() - GroupSet:ForEachGroup( - function( TaskGroup ) - SetGroup:Add( TaskGroup, TaskGroup ) - end - ) - end - - return SetGroup - -end - - ---- Sets the Planned Task menu. --- @param #MISSION self --- @param #number MenuTime -function MISSION:SetMenu( MenuTime ) - self:F() - - for _, TaskData in pairs( self:GetTasks() ) do - local Task = TaskData -- Tasking.Task#TASK - Task:SetMenu( MenuTime ) - end -end - ---- Removes the Planned Task menu. --- @param #MISSION self --- @param #number MenuTime -function MISSION:RemoveMenu( MenuTime ) - self:F() - - for _, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Task:RemoveMenu( MenuTime ) - end -end - - ---- Gets the COMMANDCENTER. --- @param #MISSION self --- @return Tasking.CommandCenter#COMMANDCENTER -function MISSION:GetCommandCenter() - return self.CommandCenter -end - - ---- Removes a Task menu. --- @param #MISSION self --- @param Tasking.Task#TASK Task --- @return #MISSION self -function MISSION:RemoveTaskMenu( Task ) - - Task:RemoveMenu() -end - - ---- Gets the mission menu for the coalition. --- @param #MISSION self --- @param Wrapper.Group#GROUP TaskGroup --- @return Core.Menu#MENU_COALITION self -function MISSION:GetMenu( TaskGroup ) - - local CommandCenter = self:GetCommandCenter() - local CommandCenterMenu = CommandCenter:GetMenu() - - local MissionName = self:GetName() - local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) - - return MissionMenu -end - - ---- Get the TASK identified by the TaskNumber from the Mission. This function is useful in GoalFunctions. --- @param #string TaskName The Name of the @{Task} within the @{Mission}. --- @return Tasking.Task#TASK The Task --- @return #nil Returns nil if no task was found. -function MISSION:GetTask( TaskName ) - self:F( { TaskName } ) - - return self.Tasks[TaskName] -end - - ---- Register a @{Task} to be completed within the @{Mission}. --- Note that there can be multiple @{Task}s registered to be completed. --- Each Task can be set a certain Goals. The Mission will not be completed until all Goals are reached. --- @param #MISSION self --- @param Tasking.Task#TASK Task is the @{Task} object. --- @return Tasking.Task#TASK The task added. -function MISSION:AddTask( Task ) - - local TaskName = Task:GetTaskName() - self:F( TaskName ) - - self.Tasks[TaskName] = self.Tasks[TaskName] or { n = 0 } - - self.Tasks[TaskName] = Task - - self:GetCommandCenter():SetMenu() - - return Task -end - ---- Removes a @{Task} to be completed within the @{Mission}. --- Note that there can be multiple @{Task}s registered to be completed. --- Each Task can be set a certain Goals. The Mission will not be completed until all Goals are reached. --- @param #MISSION self --- @param Tasking.Task#TASK Task is the @{Task} object. --- @return #nil The cleaned Task reference. -function MISSION:RemoveTask( Task ) - - local TaskName = Task:GetTaskName() - - self:F( TaskName ) - self.Tasks[TaskName] = self.Tasks[TaskName] or { n = 0 } - - -- Ensure everything gets garbarge collected. - self.Tasks[TaskName] = nil - Task = nil - - collectgarbage() - - self:GetCommandCenter():SetMenu() - - return nil -end - ---- Return the next @{Task} ID to be completed within the @{Mission}. --- @param #MISSION self --- @param Tasking.Task#TASK Task is the @{Task} object. --- @return Tasking.Task#TASK The task added. -function MISSION:GetNextTaskID( Task ) - - local TaskName = Task:GetTaskName() - self:F( TaskName ) - self.Tasks[TaskName] = self.Tasks[TaskName] or { n = 0 } - - self.Tasks[TaskName].n = self.Tasks[TaskName].n + 1 - - return self.Tasks[TaskName].n -end - ---- Is the @{Mission} **Completed**. --- @param #MISSION self --- @return #boolean -function MISSION:IsCompleted() - return self:Is( "Completed" ) -end - ---- Is the @{Mission} **Idle**. --- @param #MISSION self --- @return #boolean -function MISSION:IsIdle() - return self:Is( "Idle" ) -end - ---- Is the @{Mission} **Ongoing**. --- @param #MISSION self --- @return #boolean -function MISSION:IsOngoing() - return self:Is( "Ongoing" ) -end - ---- Is the @{Mission} **Failed**. --- @param #MISSION self --- @return #boolean -function MISSION:IsFailed() - return self:Is( "Failed" ) -end - ---- Is the @{Mission} **Hold**. --- @param #MISSION self --- @return #boolean -function MISSION:IsHold() - return self:Is( "Hold" ) -end - ---- Validates if the Mission has a Group --- @param #MISSION --- @return #boolean true if the Mission has a Group. -function MISSION:HasGroup( TaskGroup ) - local Has = false - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if Task:HasGroup( TaskGroup ) then - Has = true - break - end - end - - return Has -end - ---- Create a summary report of the Mission (one line). --- @param #MISSION self --- @return #string -function MISSION:ReportSummary() - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetName() - - -- Determine the status of the mission. - local Status = self:GetState() - - -- Determine how many tasks are remaining. - local TasksRemaining = 0 - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if Task:IsStateSuccess() or Task:IsStateFailed() then - else - TasksRemaining = TasksRemaining + 1 - end - end - - Report:Add( "Mission " .. Name .. " - " .. Status .. " - " .. TasksRemaining .. " tasks remaining." ) - - return Report:Text() -end - ---- Create a overview report of the Mission (multiple lines). --- @param #MISSION self --- @return #string -function MISSION:ReportOverview() - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetName() - - -- Determine the status of the mission. - local Status = self:GetState() - - Report:Add( "Mission " .. Name .. " - State '" .. Status .. "'" ) - - -- Determine how many tasks are remaining. - local TasksRemaining = 0 - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Report:Add( "- " .. Task:ReportSummary() ) - end - - return Report:Text() -end - ---- Create a detailed report of the Mission, listing all the details of the Task. --- @param #MISSION self --- @return #string -function MISSION:ReportDetails() - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetName() - - -- Determine the status of the mission. - local Status = self:GetState() - - Report:Add( "Mission " .. Name .. " - State '" .. Status .. "'" ) - - -- Determine how many tasks are remaining. - local TasksRemaining = 0 - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Report:Add( Task:ReportDetails() ) - end - - return Report:Text() -end - ---- Get all the TASKs from the Mission. This function is useful in GoalFunctions. --- @return {TASK,...} Structure of TASKS with the @{TASK} number as the key. --- @usage --- -- Get Tasks from the Mission. --- Tasks = Mission:GetTasks() --- env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" ) -function MISSION:GetTasks() - self:F() - - return self.Tasks -end - - ---- This module contains the TASK class. --- --- 1) @{#TASK} class, extends @{Base#BASE} --- ============================================ --- 1.1) The @{#TASK} class implements the methods for task orchestration within MOOSE. --- ---------------------------------------------------------------------------------------- --- The class provides a couple of methods to: --- --- * @{#TASK.AssignToGroup}():Assign a task to a group (of players). --- * @{#TASK.AddProcess}():Add a @{Process} to a task. --- * @{#TASK.RemoveProcesses}():Remove a running @{Process} from a running task. --- * @{#TASK.SetStateMachine}():Set a @{Fsm} to a task. --- * @{#TASK.RemoveStateMachine}():Remove @{Fsm} from a task. --- * @{#TASK.HasStateMachine}():Enquire if the task has a @{Fsm} --- * @{#TASK.AssignToUnit}(): Assign a task to a unit. (Needs to be implemented in the derived classes from @{#TASK}. --- * @{#TASK.UnAssignFromUnit}(): Unassign the task from a unit. --- * @{#TASK.SetTimeOut}(): Set timer in seconds before task gets cancelled if not assigned. --- --- 1.2) Set and enquire task status (beyond the task state machine processing). --- ---------------------------------------------------------------------------- --- A task needs to implement as a minimum the following task states: --- --- * **Success**: Expresses the successful execution and finalization of the task. --- * **Failed**: Expresses the failure of a task. --- * **Planned**: Expresses that the task is created, but not yet in execution and is not assigned yet. --- * **Assigned**: Expresses that the task is assigned to a Group of players, and that the task is in execution mode. --- --- A task may also implement the following task states: --- --- * **Rejected**: Expresses that the task is rejected by a player, who was requested to accept the task. --- * **Cancelled**: Expresses that the task is cancelled by HQ or through a logical situation where a cancellation of the task is required. --- --- A task can implement more statusses than the ones outlined above. Please consult the documentation of the specific tasks to understand the different status modelled. --- --- The status of tasks can be set by the methods **State** followed by the task status. An example is `StateAssigned()`. --- The status of tasks can be enquired by the methods **IsState** followed by the task status name. An example is `if IsStateAssigned() then`. --- --- 1.3) Add scoring when reaching a certain task status: --- ----------------------------------------------------- --- Upon reaching a certain task status in a task, additional scoring can be given. If the Mission has a scoring system attached, the scores will be added to the mission scoring. --- Use the method @{#TASK.AddScore}() to add scores when a status is reached. --- --- 1.4) Task briefing: --- ------------------- --- A task briefing can be given that is shown to the player when he is assigned to the task. --- --- === --- --- ### Authors: FlightControl - Design and Programming --- --- @module Task - ---- The TASK class --- @type TASK --- @field Core.Scheduler#SCHEDULER TaskScheduler --- @field Tasking.Mission#MISSION Mission --- @field Core.Set#SET_GROUP SetGroup The Set of Groups assigned to the Task --- @field Core.Fsm#FSM_PROCESS FsmTemplate --- @field Tasking.Mission#MISSION Mission --- @field Tasking.CommandCenter#COMMANDCENTER CommandCenter --- @extends Core.Fsm#FSM_TASK -TASK = { - ClassName = "TASK", - TaskScheduler = nil, - ProcessClasses = {}, -- The container of the Process classes that will be used to create and assign new processes for the task to ProcessUnits. - Processes = {}, -- The container of actual process objects instantiated and assigned to ProcessUnits. - Players = nil, - Scores = {}, - Menu = {}, - SetGroup = nil, - FsmTemplate = nil, - Mission = nil, - CommandCenter = nil, - TimeOut = 0, -} - ---- FSM PlayerAborted event handler prototype for TASK. --- @function [parent=#TASK] OnAfterPlayerAborted --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The Unit of the Player when he went back to spectators or left the mission. --- @param #string PlayerName The name of the Player. - ---- FSM PlayerCrashed event handler prototype for TASK. --- @function [parent=#TASK] OnAfterPlayerCrashed --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The Unit of the Player when he crashed in the mission. --- @param #string PlayerName The name of the Player. - ---- FSM PlayerDead event handler prototype for TASK. --- @function [parent=#TASK] OnAfterPlayerDead --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The Unit of the Player when he died in the mission. --- @param #string PlayerName The name of the Player. - ---- FSM Fail synchronous event function for TASK. --- Use this event to Fail the Task. --- @function [parent=#TASK] Fail --- @param #TASK self - ---- FSM Fail asynchronous event function for TASK. --- Use this event to Fail the Task. --- @function [parent=#TASK] __Fail --- @param #TASK self - ---- FSM Abort synchronous event function for TASK. --- Use this event to Abort the Task. --- @function [parent=#TASK] Abort --- @param #TASK self - ---- FSM Abort asynchronous event function for TASK. --- Use this event to Abort the Task. --- @function [parent=#TASK] __Abort --- @param #TASK self - ---- FSM Success synchronous event function for TASK. --- Use this event to make the Task a Success. --- @function [parent=#TASK] Success --- @param #TASK self - ---- FSM Success asynchronous event function for TASK. --- Use this event to make the Task a Success. --- @function [parent=#TASK] __Success --- @param #TASK self - ---- FSM Cancel synchronous event function for TASK. --- Use this event to Cancel the Task. --- @function [parent=#TASK] Cancel --- @param #TASK self - ---- FSM Cancel asynchronous event function for TASK. --- Use this event to Cancel the Task. --- @function [parent=#TASK] __Cancel --- @param #TASK self - ---- FSM Replan synchronous event function for TASK. --- Use this event to Replan the Task. --- @function [parent=#TASK] Replan --- @param #TASK self - ---- FSM Replan asynchronous event function for TASK. --- Use this event to Replan the Task. --- @function [parent=#TASK] __Replan --- @param #TASK self - - ---- Instantiates a new TASK. Should never be used. Interface Class. --- @param #TASK self --- @param Tasking.Mission#MISSION Mission The mission wherein the Task is registered. --- @param Core.Set#SET_GROUP SetGroupAssign The set of groups for which the Task can be assigned. --- @param #string TaskName The name of the Task --- @param #string TaskType The type of the Task --- @return #TASK self -function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) - - local self = BASE:Inherit( self, FSM_TASK:New() ) -- Core.Fsm#FSM_TASK - - self:SetStartState( "Planned" ) - self:AddTransition( "Planned", "Assign", "Assigned" ) - self:AddTransition( "Assigned", "AssignUnit", "Assigned" ) - self:AddTransition( "Assigned", "Success", "Success" ) - self:AddTransition( "Assigned", "Fail", "Failed" ) - self:AddTransition( "Assigned", "Abort", "Aborted" ) - self:AddTransition( "Assigned", "Cancel", "Cancelled" ) - self:AddTransition( "*", "PlayerCrashed", "*" ) - self:AddTransition( "*", "PlayerAborted", "*" ) - self:AddTransition( "*", "PlayerDead", "*" ) - self:AddTransition( { "Failed", "Aborted", "Cancelled" }, "Replan", "Planned" ) - self:AddTransition( "*", "TimeOut", "Cancelled" ) - - self:E( "New TASK " .. TaskName ) - - self.Processes = {} - self.Fsm = {} - - self.Mission = Mission - self.CommandCenter = Mission:GetCommandCenter() - - self.SetGroup = SetGroupAssign - - self:SetType( TaskType ) - self:SetName( TaskName ) - self:SetID( Mission:GetNextTaskID( self ) ) -- The Mission orchestrates the task sequences .. - - self.TaskBriefing = "You are invited for the task: " .. self.TaskName .. "." - - self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() - - - return self -end - ---- Get the Task FSM Process Template --- @param #TASK self --- @return Core.Fsm#FSM_PROCESS -function TASK:GetUnitProcess( TaskUnit ) - - if TaskUnit then - return self:GetStateMachine( TaskUnit ) - else - return self.FsmTemplate - end -end - ---- Sets the Task FSM Process Template --- @param #TASK self --- @param Core.Fsm#FSM_PROCESS -function TASK:SetUnitProcess( FsmTemplate ) - - self.FsmTemplate = FsmTemplate -end - ---- Add a PlayerUnit to join the Task. --- For each Group within the Task, the Unit is check if it can join the Task. --- If the Unit was not part of the Task, false is returned. --- If the Unit is part of the Task, true is returned. --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player joining the Mission. --- @param Wrapper.Group#GROUP PlayerGroup The GROUP of the player joining the Mission. --- @return #boolean true if Unit is part of the Task. -function TASK:JoinUnit( PlayerUnit, PlayerGroup ) - self:F( { PlayerUnit = PlayerUnit, PlayerGroup = PlayerGroup } ) - - local PlayerUnitAdded = false - - local PlayerGroups = self:GetGroups() - - -- Is the PlayerGroup part of the PlayerGroups? - if PlayerGroups:IsIncludeObject( PlayerGroup ) then - - -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is added to the Task. - -- If the PlayerGroup is not assigned to the Task, the menu needs to be set. In that case, the PlayerUnit will become the GroupPlayer leader. - if self:IsStatePlanned() or self:IsStateReplanned() then - self:SetMenuForGroup( PlayerGroup ) - --self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) - end - if self:IsStateAssigned() then - local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) - self:E( { IsAssignedToGroup = IsAssignedToGroup } ) - if IsAssignedToGroup then - self:AssignToUnit( PlayerUnit ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " joined Task " .. self:GetName() ) - end - end - end - - return PlayerUnitAdded -end - ---- Abort a PlayerUnit from a Task. --- If the Unit was not part of the Task, false is returned. --- If the Unit is part of the Task, true is returned. --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. --- @return #boolean true if Unit is part of the Task. -function TASK:AbortUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - local PlayerUnitAborted = false - - local PlayerGroups = self:GetGroups() - local PlayerGroup = PlayerUnit:GetGroup() - - -- Is the PlayerGroup part of the PlayerGroups? - if PlayerGroups:IsIncludeObject( PlayerGroup ) then - - -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. - -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. - if self:IsStateAssigned() then - local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) - self:E( { IsAssignedToGroup = IsAssignedToGroup } ) - if IsAssignedToGroup then - self:UnAssignFromUnit( PlayerUnit ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " aborted Task " .. self:GetName() ) - self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) - if #PlayerGroup:GetUnits() == 1 then - self:UnAssignFromGroup( PlayerGroup ) - PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) - self:RemoveMenuForGroup( PlayerGroup ) - end - self:Abort() - end - end - end - - return PlayerUnitAborted -end - ---- A PlayerUnit crashed in a Task. Abort the Player. --- If the Unit was not part of the Task, false is returned. --- If the Unit is part of the Task, true is returned. --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. --- @return #boolean true if Unit is part of the Task. -function TASK:CrashUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - local PlayerUnitCrashed = false - - local PlayerGroups = self:GetGroups() - local PlayerGroup = PlayerUnit:GetGroup() - - -- Is the PlayerGroup part of the PlayerGroups? - if PlayerGroups:IsIncludeObject( PlayerGroup ) then - - -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. - -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. - if self:IsStateAssigned() then - local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) - self:E( { IsAssignedToGroup = IsAssignedToGroup } ) - if IsAssignedToGroup then - self:UnAssignFromUnit( PlayerUnit ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " crashed in Task " .. self:GetName() ) - self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) - if #PlayerGroup:GetUnits() == 1 then - PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) - self:RemoveMenuForGroup( PlayerGroup ) - end - self:PlayerCrashed( PlayerUnit ) - end - end - end - - return PlayerUnitCrashed -end - - - ---- Gets the Mission to where the TASK belongs. --- @param #TASK self --- @return Tasking.Mission#MISSION -function TASK:GetMission() - - return self.Mission -end - - ---- Gets the SET_GROUP assigned to the TASK. --- @param #TASK self --- @return Core.Set#SET_GROUP -function TASK:GetGroups() - return self.SetGroup -end - - - ---- Assign the @{Task} to a @{Group}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @return #TASK -function TASK:AssignToGroup( TaskGroup ) - self:F2( TaskGroup:GetName() ) - - local TaskGroupName = TaskGroup:GetName() - - TaskGroup:SetState( TaskGroup, "Assigned", self ) - - local Mission = self:GetMission() - local MissionMenu = Mission:GetMenu( TaskGroup ) - MissionMenu:RemoveSubMenus() - - --self:RemoveMenuForGroup( TaskGroup ) - self:SetAssignedMenuForGroup( TaskGroup ) - - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - self:E(PlayerName) - if PlayerName ~= nil or PlayerName ~= "" then - self:AssignToUnit( TaskUnit ) - end - end - - return self -end - ---- --- @param #TASK self --- @param Wrapper.Group#GROUP FindGroup --- @return #boolean -function TASK:HasGroup( FindGroup ) - - return self:GetGroups():IsIncludeObject( FindGroup ) - -end - ---- Assign the @{Task} to an alive @{Unit}. --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return #TASK self -function TASK:AssignToUnit( TaskUnit ) - self:F( TaskUnit:GetName() ) - - local FsmTemplate = self:GetUnitProcess() - - -- Assign a new FsmUnit to TaskUnit. - local FsmUnit = self:SetStateMachine( TaskUnit, FsmTemplate:Copy( TaskUnit, self ) ) -- Core.Fsm#FSM_PROCESS - self:E({"Address FsmUnit", tostring( FsmUnit ) } ) - - FsmUnit:SetStartState( "Planned" ) - - FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. - - return self -end - ---- UnAssign the @{Task} from an alive @{Unit}. --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return #TASK self -function TASK:UnAssignFromUnit( TaskUnit ) - self:F( TaskUnit:GetName() ) - - self:RemoveStateMachine( TaskUnit ) - - return self -end - ---- Sets the TimeOut for the @{Task}. If @{Task} stayed planned for longer than TimeOut, it gets into Cancelled status. --- @param #TASK self --- @param #integer Timer in seconds --- @return #TASK self -function TASK:SetTimeOut ( Timer ) - self:F( Timer ) - self.TimeOut = Timer - self:__TimeOut( self.TimeOut ) - return self -end - ---- Send a message of the @{Task} to the assigned @{Group}s. --- @param #TASK self -function TASK:MessageToGroups( Message ) - self:F( { Message = Message } ) - - local Mission = self:GetMission() - local CC = Mission:GetCommandCenter() - - for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - local TaskGroup = TaskGroup -- Wrapper.Group#GROUP - CC:MessageToGroup( Message, TaskGroup, TaskGroup:GetName() ) - end -end - - ---- Send the briefng message of the @{Task} to the assigned @{Group}s. --- @param #TASK self -function TASK:SendBriefingToAssignedGroups() - self:F2() - - for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - - if self:IsAssignedToGroup( TaskGroup ) then - TaskGroup:Message( self.TaskBriefing, 60 ) - end - end -end - - ---- UnAssign the @{Task} from the @{Group}s. --- @param #TASK self -function TASK:UnAssignFromGroups() - self:F2() - - for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - self:UnAssignFromGroup( TaskGroup ) - end -end - ---- UnAssign the @{Task} from a @{Group}. --- @param #TASK self -function TASK:UnAssignFromGroup( TaskGroup ) - self:F2( { TaskGroup } ) - - TaskGroup:SetState( TaskGroup, "Assigned", nil ) - - self:RemoveAssignedMenuForGroup( TaskGroup ) - - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - if PlayerName ~= nil or PlayerName ~= "" then - self:UnAssignFromUnit( TaskUnit ) - end - end -end - - - ---- Returns if the @{Task} is assigned to the Group. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @return #boolean -function TASK:IsAssignedToGroup( TaskGroup ) - - local TaskGroupName = TaskGroup:GetName() - - if self:IsStateAssigned() then - if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then - self:T( { "Task is assigned to:", TaskGroup:GetName() } ) - return true - end - end - - self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) - return false -end - ---- Returns if the @{Task} has still alive and assigned Units. --- @param #TASK self --- @return #boolean -function TASK:HasAliveUnits() - self:F() - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if self:IsStateAssigned() then - if self:IsAssignedToGroup( TaskGroup ) then - for TaskUnitID, TaskUnit in pairs( TaskGroup:GetUnits() ) do - if TaskUnit:IsAlive() then - self:T( { HasAliveUnits = true } ) - return true - end - end - end - end - end - - self:T( { HasAliveUnits = false } ) - return false -end - ---- Set the menu options of the @{Task} to all the groups in the SetGroup. --- @param #TASK self --- @param #number MenuTime --- @return #TASK -function TASK:SetMenu( MenuTime ) - self:F() - - self.SetGroup:Flush() - for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do - local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP - if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then - if self:IsStatePlanned() or self:IsStateReplanned() then - self:SetMenuForGroup( TaskGroup, MenuTime ) - end - end - end -end - - - ---- Set the Menu for a Group --- @param #TASK self --- @param #number MenuTime --- @return #TASK -function TASK:SetMenuForGroup( TaskGroup, MenuTime ) - - if not TaskGroup:GetState( TaskGroup, "Assigned" ) then - self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName(), MenuTime ) - else - if not self:IsAssignedToGroup( TaskGroup ) then - self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) - end - end -end - - ---- Set the planned menu option of the @{Task}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @param #string MenuText The menu text. --- @param #number MenuTime --- @return #TASK self -function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime ) - self:E( TaskGroup:GetName() ) - - local Mission = self:GetMission() - local MissionName = Mission:GetName() - local CommandCenter = Mission:GetCommandCenter() - local CommandCenterMenu = CommandCenter:GetMenu() - - local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) - - - local MissionMenu = Mission:GetMenu( TaskGroup ) - - local TaskType = self:GetType() - local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ):SetTime( MenuTime ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ):SetTime( MenuTime ):SetRemoveParent( true ) - - return self -end - ---- Set the assigned menu options of the @{Task}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @param #number MenuTime --- @return #TASK self -function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) - self:E( TaskGroup:GetName() ) - - local Mission = self:GetMission() - local MissionMenu = Mission:GetMenu( TaskGroup ) - - self:E( { MissionMenu = MissionMenu } ) - - local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ) - - return self -end - ---- Remove the menu options of the @{Task} to all the groups in the SetGroup. --- @param #TASK self --- @param #number MenuTime --- @return #TASK -function TASK:RemoveMenu( MenuTime ) - self:F() - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - local TaskGroup = TaskGroup -- Wrapper.Group#GROUP - if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then - if not self:IsAssignedToGroup( TaskGroup ) then - self:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) - end - end - end -end - - ---- Remove the menu option of the @{Task} for a @{Group}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @param #number MenuTime --- @return #TASK self -function TASK:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) - self:F() - - local Mission = self:GetMission() - local MissionName = Mission:GetName() - - local MissionMenu = Mission:GetMenu( TaskGroup ) - - if MissionMenu then - local TaskType = self:GetType() - local TypeMenu = MissionMenu:GetMenu( TaskType ) - - if TypeMenu then - local TaskMenu = TypeMenu:GetMenu( self:GetTaskName() ) - if TaskMenu then - TaskMenu:Remove( MenuTime ) - end - end - end - -end - ---- Remove the assigned menu option of the @{Task} for a @{Group}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @param #number MenuTime --- @return #TASK self -function TASK:RemoveAssignedMenuForGroup( TaskGroup ) - self:F() - - local Mission = self:GetMission() - local MissionName = Mission:GetName() - - local MissionMenu = Mission:GetMenu( TaskGroup ) - - if MissionMenu then - MissionMenu:RemoveSubMenus() - end - -end - -function TASK.MenuAssignToGroup( MenuParam ) - - local self = MenuParam.self - local TaskGroup = MenuParam.TaskGroup - - self:E( "Assigned menu selected") - - self:AssignToGroup( TaskGroup ) -end - ---- Report the task status. --- @param #TASK self -function TASK:MenuTaskStatus( TaskGroup ) - - local ReportText = self:ReportDetails() - - self:T( ReportText ) - self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup ) - -end - ---- Report the task status. --- @param #TASK self -function TASK:MenuTaskAbort( TaskGroup ) - - self:Abort() -end - - - ---- Returns the @{Task} name. --- @param #TASK self --- @return #string TaskName -function TASK:GetTaskName() - return self.TaskName -end - - - - ---- Get the default or currently assigned @{Process} template with key ProcessName. --- @param #TASK self --- @param #string ProcessName --- @return Core.Fsm#FSM_PROCESS -function TASK:GetProcessTemplate( ProcessName ) - - local ProcessTemplate = self.ProcessClasses[ProcessName] - - return ProcessTemplate -end - - - --- TODO: Obscolete? ---- Fail processes from @{Task} with key @{Unit} --- @param #TASK self --- @param #string TaskUnitName --- @return #TASK self -function TASK:FailProcesses( TaskUnitName ) - - for ProcessID, ProcessData in pairs( self.Processes[TaskUnitName] ) do - local Process = ProcessData - Process.Fsm:Fail() - end -end - ---- Add a FiniteStateMachine to @{Task} with key Task@{Unit} --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @param Core.Fsm#FSM_PROCESS Fsm --- @return #TASK self -function TASK:SetStateMachine( TaskUnit, Fsm ) - self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil, Fsm:GetClassNameAndID() } ) - - self.Fsm[TaskUnit] = Fsm - - return Fsm -end - ---- Gets the FiniteStateMachine of @{Task} with key Task@{Unit} --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return Core.Fsm#FSM_PROCESS -function TASK:GetStateMachine( TaskUnit ) - self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) - - return self.Fsm[TaskUnit] -end - ---- Remove FiniteStateMachines from @{Task} with key Task@{Unit} --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return #TASK self -function TASK:RemoveStateMachine( TaskUnit ) - self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) - - self:E( self.Fsm ) - for TaskUnitT, Fsm in pairs( self.Fsm ) do - self:E( TaskUnitT ) - end - - self.Fsm[TaskUnit] = nil - - collectgarbage() - self:E( "Garbage Collected, Processes should be finalized now ...") -end - ---- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return #TASK self -function TASK:HasStateMachine( TaskUnit ) - self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) - - return ( self.Fsm[TaskUnit] ~= nil ) -end - - ---- Gets the Scoring of the task --- @param #TASK self --- @return Functional.Scoring#SCORING Scoring -function TASK:GetScoring() - return self.Mission:GetScoring() -end - - ---- Gets the Task Index, which is a combination of the Task type, the Task name. --- @param #TASK self --- @return #string The Task ID -function TASK:GetTaskIndex() - - local TaskType = self:GetType() - local TaskName = self:GetName() - - return TaskType .. "." .. TaskName -end - ---- Sets the Name of the Task --- @param #TASK self --- @param #string TaskName -function TASK:SetName( TaskName ) - self.TaskName = TaskName -end - ---- Gets the Name of the Task --- @param #TASK self --- @return #string The Task Name -function TASK:GetName() - return self.TaskName -end - ---- Sets the Type of the Task --- @param #TASK self --- @param #string TaskType -function TASK:SetType( TaskType ) - self.TaskType = TaskType -end - ---- Gets the Type of the Task --- @param #TASK self --- @return #string TaskType -function TASK:GetType() - return self.TaskType -end - ---- Sets the ID of the Task --- @param #TASK self --- @param #string TaskID -function TASK:SetID( TaskID ) - self.TaskID = TaskID -end - ---- Gets the ID of the Task --- @param #TASK self --- @return #string TaskID -function TASK:GetID() - return self.TaskID -end - - ---- Sets a @{Task} to status **Success**. --- @param #TASK self -function TASK:StateSuccess() - self:SetState( self, "State", "Success" ) - return self -end - ---- Is the @{Task} status **Success**. --- @param #TASK self -function TASK:IsStateSuccess() - return self:Is( "Success" ) -end - ---- Sets a @{Task} to status **Failed**. --- @param #TASK self -function TASK:StateFailed() - self:SetState( self, "State", "Failed" ) - return self -end - ---- Is the @{Task} status **Failed**. --- @param #TASK self -function TASK:IsStateFailed() - return self:Is( "Failed" ) -end - ---- Sets a @{Task} to status **Planned**. --- @param #TASK self -function TASK:StatePlanned() - self:SetState( self, "State", "Planned" ) - return self -end - ---- Is the @{Task} status **Planned**. --- @param #TASK self -function TASK:IsStatePlanned() - return self:Is( "Planned" ) -end - ---- Sets a @{Task} to status **Aborted**. --- @param #TASK self -function TASK:StateAborted() - self:SetState( self, "State", "Aborted" ) - return self -end - ---- Is the @{Task} status **Aborted**. --- @param #TASK self -function TASK:IsStateAborted() - return self:Is( "Aborted" ) -end - ---- Sets a @{Task} to status **Cancelled**. --- @param #TASK self -function TASK:StateCancelled() - self:SetState( self, "State", "Cancelled" ) - return self -end - ---- Is the @{Task} status **Cancelled**. --- @param #TASK self -function TASK:IsStateCancelled() - return self:Is( "Cancelled" ) -end - ---- Sets a @{Task} to status **Assigned**. --- @param #TASK self -function TASK:StateAssigned() - self:SetState( self, "State", "Assigned" ) - return self -end - ---- Is the @{Task} status **Assigned**. --- @param #TASK self -function TASK:IsStateAssigned() - return self:Is( "Assigned" ) -end - ---- Sets a @{Task} to status **Hold**. --- @param #TASK self -function TASK:StateHold() - self:SetState( self, "State", "Hold" ) - return self -end - ---- Is the @{Task} status **Hold**. --- @param #TASK self -function TASK:IsStateHold() - return self:Is( "Hold" ) -end - ---- Sets a @{Task} to status **Replanned**. --- @param #TASK self -function TASK:StateReplanned() - self:SetState( self, "State", "Replanned" ) - return self -end - ---- Is the @{Task} status **Replanned**. --- @param #TASK self -function TASK:IsStateReplanned() - return self:Is( "Replanned" ) -end - ---- Gets the @{Task} status. --- @param #TASK self -function TASK:GetStateString() - return self:GetState( self, "State" ) -end - ---- Sets a @{Task} briefing. --- @param #TASK self --- @param #string TaskBriefing --- @return #TASK self -function TASK:SetBriefing( TaskBriefing ) - self.TaskBriefing = TaskBriefing - return self -end - - - - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName ) - - self:E( { "Task Assigned", self.Dispatcher } ) - - self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned to your group." ) - - if self.Dispatcher then - self:E( "Firing Assign event " ) - self.Dispatcher:Assign( self, PlayerUnit, PlayerName ) - end - - self:GetMission():__Start( 1 ) -end - - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onenterSuccess( From, Event, To ) - - self:E( "Task Success" ) - - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is successful! Good job!" ) - self:UnAssignFromGroups() - - self:GetMission():__Complete( 1 ) - -end - - ---- FSM function for a TASK --- @param #TASK self --- @param #string From --- @param #string Event --- @param #string To -function TASK:onenterAborted( From, Event, To ) - - self:E( "Task Aborted" ) - - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has been aborted! Task may be replanned." ) - - self:UnAssignFromGroups() - - self:__Replan( 5 ) -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string From --- @param #string Event --- @param #string To -function TASK:onafterReplan( From, Event, To ) - - self:E( "Task Replanned" ) - - self:GetMission():GetCommandCenter():MessageToCoalition( "Replanning Task " .. self:GetName() .. "." ) - - self:SetMenu() - -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string From --- @param #string Event --- @param #string To -function TASK:onenterFailed( From, Event, To ) - - self:E( "Task Failed" ) - - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has failed!" ) - - self:UnAssignFromGroups() -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onstatechange( From, Event, To ) - - if self:IsTrace() then - MESSAGE:New( "@ Task " .. self.TaskName .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() - end - - if self.Scores[To] then - local Scoring = self:GetScoring() - if Scoring then - self:E( { self.Scores[To].ScoreText, self.Scores[To].Score } ) - Scoring:_AddMissionScore( self.Mission, self.Scores[To].ScoreText, self.Scores[To].Score ) - end - end - -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onenterPlanned( From, Event, To) - if not self.TimeOut == 0 then - self.__TimeOut( self.TimeOut ) - end -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onbeforeTimeOut( From, Event, To ) - if From == "Planned" then - self:RemoveMenu() - return true - end - return false -end - -do -- Dispatcher - - --- Set dispatcher of a task - -- @param #TASK self - -- @param Tasking.DetectionManager#DETECTION_MANAGER Dispatcher - -- @return #TASK - function TASK:SetDispatcher( Dispatcher ) - self.Dispatcher = Dispatcher - end - -end - -do -- Reporting - ---- Create a summary report of the Task. --- List the Task Name and Status --- @param #TASK self --- @return #string -function TASK:ReportSummary() - - local Report = REPORT:New() - - -- List the name of the Task. - local Name = self:GetName() - - -- Determine the status of the Task. - local State = self:GetState() - - Report:Add( "Task " .. Name .. " - State '" .. State ) - - return Report:Text() -end - - ---- Create a detailed report of the Task. --- List the Task Status, and the Players assigned to the Task. --- @param #TASK self --- @return #string -function TASK:ReportDetails() - - local Report = REPORT:New() - - -- List the name of the Task. - local Name = self:GetName() - - -- Determine the status of the Task. - local State = self:GetState() - - -- Loop each Unit active in the Task, and find Player Names. - local PlayerNames = {} - local PlayerReport = REPORT:New( " - Players:" ) - for PlayerGroupID, PlayerGroupData in pairs( self:GetGroups():GetSet() ) do - local PlayerGroup = PlayerGroupData -- Wrapper.Group#GROUP - PlayerNames = PlayerGroup:GetPlayerNames() - if PlayerNames then - PlayerReport:Add( " -- Group " .. PlayerGroup:GetCallsign() .. ": " .. table.concat( PlayerNames, ", " ) ) - end - end - - -- Loop each Process in the Task, and find Reporting Details. - Report:Add( string.format( " - Task %s\n -- State '%s'\n%s", Name, State, PlayerReport:Text() ) ) - return Report:Text() -end - - -end -- Reporting ---- This module contains the DETECTION_MANAGER class and derived classes. --- --- === --- --- 1) @{DetectionManager#DETECTION_MANAGER} class, extends @{Fsm#FSM} --- ==================================================================== --- The @{DetectionManager#DETECTION_MANAGER} class defines the core functions to report detected objects to groups. --- Reportings can be done in several manners, and it is up to the derived classes if DETECTION_MANAGER to model the reporting behaviour. --- --- 1.1) DETECTION_MANAGER constructor: --- ----------------------------------- --- * @{DetectionManager#DETECTION_MANAGER.New}(): Create a new DETECTION_MANAGER instance. --- --- 1.2) DETECTION_MANAGER reporting: --- --------------------------------- --- Derived DETECTION_MANAGER classes will reports detected units using the method @{DetectionManager#DETECTION_MANAGER.ReportDetected}(). This method implements polymorphic behaviour. --- --- The time interval in seconds of the reporting can be changed using the methods @{DetectionManager#DETECTION_MANAGER.SetReportInterval}(). --- To control how long a reporting message is displayed, use @{DetectionManager#DETECTION_MANAGER.SetReportDisplayTime}(). --- Derived classes need to implement the method @{DetectionManager#DETECTION_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report. --- --- Reporting can be started and stopped using the methods @{DetectionManager#DETECTION_MANAGER.StartReporting}() and @{DetectionManager#DETECTION_MANAGER.StopReporting}() respectively. --- If an ad-hoc report is requested, use the method @{DetectionManager#DETECTION_MANAGER#ReportNow}(). --- --- The default reporting interval is every 60 seconds. The reporting messages are displayed 15 seconds. --- --- === --- --- 2) @{DetectionManager#DETECTION_REPORTING} class, extends @{DetectionManager#DETECTION_MANAGER} --- ========================================================================================= --- The @{DetectionManager#DETECTION_REPORTING} class implements detected units reporting. Reporting can be controlled using the reporting methods available in the @{DetectionManager#DETECTION_MANAGER} class. --- --- 2.1) DETECTION_REPORTING constructor: --- ------------------------------- --- The @{DetectionManager#DETECTION_REPORTING.New}() method creates a new DETECTION_REPORTING instance. --- --- --- === --- --- ### Contributions: Mechanist, Prof_Hilactic, FlightControl - Concept & Testing --- ### Author: FlightControl - Framework Design & Programming --- --- @module DetectionManager - -do -- DETECTION MANAGER - - --- DETECTION_MANAGER class. - -- @type DETECTION_MANAGER - -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. - -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. - -- @extends Core.Fsm#FSM - DETECTION_MANAGER = { - ClassName = "DETECTION_MANAGER", - SetGroup = nil, - Detection = nil, - } - - --- FAC constructor. - -- @param #DETECTION_MANAGER self - -- @param Set#SET_GROUP SetGroup - -- @param Functional.Detection#DETECTION_BASE Detection - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:New( SetGroup, Detection ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_MANAGER - - self.SetGroup = SetGroup - self.Detection = Detection - - self:SetStartState( "Stopped" ) - self:AddTransition( "Stopped", "Start", "Started" ) - self:AddTransition( "Started", "Stop", "Stopped" ) - self:AddTransition( "Started", "Report", "Started" ) - - self:SetReportInterval( 30 ) - self:SetReportDisplayTime( 25 ) - - Detection:__Start( 1 ) - - return self - end - - function DETECTION_MANAGER:onafterStart( From, Event, To ) - self:Report() - end - - function DETECTION_MANAGER:onafterReport( From, Event, To ) - - self:E( "onafterReport" ) - - self:__Report( -self._ReportInterval ) - - self:ProcessDetected( self.Detection ) - end - - --- Set the reporting time interval. - -- @param #DETECTION_MANAGER self - -- @param #number ReportInterval The interval in seconds when a report needs to be done. - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:SetReportInterval( ReportInterval ) - self:F2() - - self._ReportInterval = ReportInterval - end - - - --- Set the reporting message display time. - -- @param #DETECTION_MANAGER self - -- @param #number ReportDisplayTime The display time in seconds when a report needs to be done. - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:SetReportDisplayTime( ReportDisplayTime ) - self:F2() - - self._ReportDisplayTime = ReportDisplayTime - end - - --- Get the reporting message display time. - -- @param #DETECTION_MANAGER self - -- @return #number ReportDisplayTime The display time in seconds when a report needs to be done. - function DETECTION_MANAGER:GetReportDisplayTime() - self:F2() - - return self._ReportDisplayTime - end - - --- Reports the detected items to the @{Set#SET_GROUP}. - -- @param #DETECTION_MANAGER self - -- @param Functional.Detection#DETECTION_BASE Detection - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:ProcessDetected( Detection ) - self:E() - - end - -end - - -do -- DETECTION_REPORTING - - --- DETECTION_REPORTING class. - -- @type DETECTION_REPORTING - -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. - -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. - -- @extends #DETECTION_MANAGER - DETECTION_REPORTING = { - ClassName = "DETECTION_REPORTING", - } - - - --- DETECTION_REPORTING constructor. - -- @param #DETECTION_REPORTING self - -- @param Set#SET_GROUP SetGroup - -- @param Functional.Detection#DETECTION_AREAS Detection - -- @return #DETECTION_REPORTING self - function DETECTION_REPORTING:New( SetGroup, Detection ) - - -- Inherits from DETECTION_MANAGER - local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #DETECTION_REPORTING - - self:Schedule( 1, 30 ) - return self - end - - --- Creates a string of the detected items in a @{Detection}. - -- @param #DETECTION_MANAGER self - -- @param Set#SET_UNIT DetectedSet The detected Set created by the @{Detection#DETECTION_BASE} object. - -- @return #DETECTION_MANAGER self - function DETECTION_REPORTING:GetDetectedItemsText( DetectedSet ) - self:F2() - - local MT = {} -- Message Text - local UnitTypes = {} - - for DetectedUnitID, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - if DetectedUnit:IsAlive() then - local UnitType = DetectedUnit:GetTypeName() - - if not UnitTypes[UnitType] then - UnitTypes[UnitType] = 1 - else - UnitTypes[UnitType] = UnitTypes[UnitType] + 1 - end - end - end - - for UnitTypeID, UnitType in pairs( UnitTypes ) do - MT[#MT+1] = UnitType .. " of " .. UnitTypeID - end - - return table.concat( MT, ", " ) - end - - - - --- Reports the detected items to the @{Set#SET_GROUP}. - -- @param #DETECTION_REPORTING self - -- @param Wrapper.Group#GROUP Group The @{Group} object to where the report needs to go. - -- @param Functional.Detection#DETECTION_AREAS Detection The detection created by the @{Detection#DETECTION_BASE} object. - -- @return #boolean Return true if you want the reporting to continue... false will cancel the reporting loop. - function DETECTION_REPORTING:ProcessDetected( Group, Detection ) - self:F2( Group ) - - self:E( Group ) - local DetectedMsg = {} - for DetectedAreaID, DetectedAreaData in pairs( Detection:GetDetectedAreas() ) do - local DetectedArea = DetectedAreaData -- Functional.Detection#DETECTION_AREAS.DetectedArea - DetectedMsg[#DetectedMsg+1] = " - Group #" .. DetectedAreaID .. ": " .. self:GetDetectedItemsText( DetectedArea.Set ) - end - local FACGroup = Detection:GetDetectionGroups() - FACGroup:MessageToGroup( "Reporting detected target groups:\n" .. table.concat( DetectedMsg, "\n" ), self:GetReportDisplayTime(), Group ) - - return true - end - -end - ---- **Tasking** - The TASK_A2G_DISPATCHER creates and manages player TASK_A2G tasks based on detected targets. --- --- === --- --- # 1) @{#TASK_A2G_DISPATCHER} class, extends @{#DETECTION_MANAGER} --- --- The @{#TASK_A2G_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups). --- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. --- Find a summary below describing for which situation a task type is created: --- --- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter. --- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter. --- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars. --- --- Other task types will follow... --- --- 3.1) TASK_A2G_DISPATCHER constructor: --- -------------------------------------- --- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK_A2G_DISPATCHER instance. --- --- === --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-09: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module Task_A2G_Dispatcher - -do -- TASK_A2G_DISPATCHER - - --- TASK_A2G_DISPATCHER class. - -- @type TASK_A2G_DISPATCHER - -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. - -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. - -- @field Tasking.Mission#MISSION Mission - -- @extends Tasking.DetectionManager#DETECTION_MANAGER - TASK_A2G_DISPATCHER = { - ClassName = "TASK_A2G_DISPATCHER", - Mission = nil, - Detection = nil, - } - - - --- TASK_A2G_DISPATCHER constructor. - -- @param #TASK_A2G_DISPATCHER self - -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. - -- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. - -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. - -- @return #TASK_A2G_DISPATCHER self - function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection ) - - -- Inherits from DETECTION_MANAGER - local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER - - self.Detection = Detection - self.Mission = Mission - - self:AddTransition( "Started", "Assign", "Started" ) - - --- OnAfter Transition Handler for Event Assign. - -- @function [parent=#TASK_A2G_DISPATCHER] OnAfterAssign - -- @param #TASK_A2G_DISPATCHER self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Tasking.Task_A2G#TASK_A2G Task - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param #string PlayerName - - self:__Start( 5 ) - - return self - end - - - --- Creates a SEAD task when there are targets for it. - -- @param #TASK_A2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem - -- @return Set#SET_UNIT TargetSetUnit: The target set of units. - -- @return #nil If there are no targets to be set. - function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem ) - self:F( { DetectedItem.ItemID } ) - - local DetectedSet = DetectedItem.Set - local DetectedZone = DetectedItem.Zone - - -- Determine if the set has radar targets. If it does, construct a SEAD task. - local RadarCount = DetectedSet:HasSEAD() - - if RadarCount > 0 then - - -- Here we're doing something advanced... We're copying the DetectedSet, but making a new Set only with SEADable Radar units in it. - local TargetSetUnit = SET_UNIT:New() - TargetSetUnit:SetDatabase( DetectedSet ) - TargetSetUnit:FilterHasSEAD() - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Creates a CAS task when there are targets for it. - -- @param #TASK_A2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem - -- @return Tasking.Task#TASK - function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem ) - self:F( { DetectedItem.ItemID } ) - - local DetectedSet = DetectedItem.Set - local DetectedZone = DetectedItem.Zone - - - -- Determine if the set has radar targets. If it does, construct a SEAD task. - local GroundUnitCount = DetectedSet:HasGroundUnits() - local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) - - if GroundUnitCount > 0 and FriendliesNearBy == true then - - -- Copy the Set - local TargetSetUnit = SET_UNIT:New() - TargetSetUnit:SetDatabase( DetectedSet ) - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Creates a BAI task when there are targets for it. - -- @param #TASK_A2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem - -- @return Tasking.Task#TASK - function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition ) - self:F( { DetectedItem.ItemID } ) - - local DetectedSet = DetectedItem.Set - local DetectedZone = DetectedItem.Zone - - - -- Determine if the set has radar targets. If it does, construct a SEAD task. - local GroundUnitCount = DetectedSet:HasGroundUnits() - local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) - - if GroundUnitCount > 0 and FriendliesNearBy == false then - - -- Copy the Set - local TargetSetUnit = SET_UNIT:New() - TargetSetUnit:SetDatabase( DetectedSet ) - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Evaluates the removal of the Task from the Mission. - -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". - -- @param #TASK_A2G_DISPATCHER self - -- @param Tasking.Mission#MISSION Mission - -- @param Tasking.Task#TASK Task - -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem - -- @return Tasking.Task#TASK - function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItem ) - - if Task then - if Task:IsStatePlanned() and DetectedItem.Changed == true then - self:E( "Removing Tasking: " .. Task:GetTaskName() ) - Task = Mission:RemoveTask( Task ) - end - end - - return Task - end - - - --- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}. - -- @param #TASK_A2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. - -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. - function TASK_A2G_DISPATCHER:ProcessDetected( Detection ) - self:E() - - local AreaMsg = {} - local TaskMsg = {} - local ChangeMsg = {} - - local Mission = self.Mission - local ReportSEAD = REPORT:New( "- SEAD Tasks:") - local ReportCAS = REPORT:New( "- CAS Tasks:") - local ReportBAI = REPORT:New( "- BAI Tasks:") - local ReportChanges = REPORT:New( " - Changes:" ) - - --- First we need to the detected targets. - for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do - - local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem - local DetectedSet = DetectedItem.Set -- Functional.Detection#DETECTION_BASE.DetectedSet - local DetectedZone = DetectedItem.Zone - self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) - DetectedSet:Flush() - - local ItemID = DetectedItem.ItemID - - -- Evaluate SEAD Tasking - local SEADTask = Mission:GetTask( string.format( "SEAD.%03d", ItemID ) ) - SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedItem ) - if not SEADTask then - local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - local Task = TASK_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", ItemID ), TargetSetUnit ) - Task:SetTargetZone( DetectedZone ) - Task:SetDispatcher( self ) - SEADTask = Mission:AddTask( Task ) - end - end - if SEADTask and SEADTask:IsStatePlanned() then - ReportSEAD:Add( string.format( " - %s.%02d - %s", "SEAD", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) - end - - -- Evaluate CAS Tasking - local CASTask = Mission:GetTask( string.format( "CAS.%03d", ItemID ) ) - CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedItem ) - if not CASTask then - local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - local Task = TASK_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", ItemID ), TargetSetUnit ) - Task:SetTargetZone( DetectedZone ) - Task:SetDispatcher( self ) - CASTask = Mission:AddTask( Task ) - end - end - if CASTask and CASTask:IsStatePlanned() then - ReportCAS:Add( string.format( " - %s.%02d - %s", "CAS", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) - end - - -- Evaluate BAI Tasking - local BAITask = Mission:GetTask( string.format( "BAI.%03d", ItemID ) ) - BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedItem ) - if not BAITask then - local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - local Task = TASK_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", ItemID ), TargetSetUnit ) - Task:SetTargetZone( DetectedZone ) - Task:SetDispatcher( self ) - BAITask = Mission:AddTask( Task ) - end - end - if BAITask and BAITask:IsStatePlanned() then - ReportBAI:Add( string.format( " - %s.%02d - %s", "BAI", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) - end - - - -- Loop through the changes ... - local ChangeText = Detection:GetChangeText( DetectedItem ) - ReportChanges:Add( ChangeText ) - - - -- OK, so the tasking has been done, now delete the changes reported for the area. - Detection:AcceptChanges( DetectedItem ) - - end - - -- TODO set menus using the HQ coordinator - Mission:GetCommandCenter():SetMenu() - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if not TaskGroup:GetState( TaskGroup, "Assigned" ) then - Mission:GetCommandCenter():MessageToGroup( - string.format( "HQ Reporting - Planned tasks for mission '%s':\n%s\n", - self.Mission:GetName(), - string.format( "%s\n\n%s\n\n%s\n\n%s", ReportSEAD:Text(), ReportCAS:Text(), ReportBAI:Text(), ReportChanges:Text() - ) - ), TaskGroup - ) - end - end - - return true - end - -end--- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements. --- --- ![Banner Image](..\Presentations\TASK_A2G\Dia1.JPG) --- --- --- # 1) @{Task_A2G#TASK_A2G} class, extends @{Task#TASK} --- --- The @{#TASK_A2G} class defines Air To Ground tasks for a @{Set} of Target Units, --- based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_A2G is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: --- --- * **None**: Start of the process --- * **Planned**: The A2G task is planned. --- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}. --- * **Success**: The A2G task is successfully completed. --- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. --- --- # 1.1) Set the scoring of achievements in an A2G attack. --- --- Scoring or penalties can be given in the following circumstances: --- --- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed. --- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed. --- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed. --- --- # 2) @{Task_A2G#TASK_SEAD} class, extends @{Task_A2G#TASK_A2G} --- --- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units. --- --- === --- --- # 3) @{Task_A2G#TASK_CAS} class, extends @{Task_A2G#TASK_A2G} --- --- The @{#TASK_CAS} class defines a CAS task for a @{Set} of Target Units. --- --- === --- --- # 4) @{Task_A2G#TASK_BAI} class, extends @{Task_A2G#TASK_A2G} --- --- The @{#TASK_BAI} class defines a BAI task for a @{Set} of Target Units. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-09: Revised version. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[WingThor]**: Concept, Advice & Testing. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module Task_A2G - -do -- TASK_A2G - - --- The TASK_A2G class - -- @type TASK_A2G - -- @field Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - TASK_A2G = { - ClassName = "TASK_A2G", - } - - --- Instantiates a new TASK_A2G. - -- @param #TASK_A2G self - -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. - -- @return #TASK_A2G self - function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- Tasking.Task#TASK_A2G - self:F() - - self.TargetSetUnit = TargetSetUnit - self.TaskType = TaskType - - Mission:AddTask( self ) - - local Fsm = self:GetUnitProcess() - - - Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } ) - - Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) - Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) - Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) - - Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" ) - - Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" ) - Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" ) - - Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, self.TaskType ), { Accounted = "Success" } ) - Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" ) - Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} ) - Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} ) - Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" ) - - Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) - Fsm:AddTransition( "Accounted", "Success", "Success" ) - Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) - Fsm:AddTransition( "Failed", "Fail", "Failed" ) - - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_A2G#TASK_A2G Task - function Fsm:onafterRouteToRendezVous( TaskUnit, Task ) - self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - -- Determine the first Unit from the self.RendezVousSetUnit - - if Task:GetRendezVousZone( TaskUnit ) then - self:__RouteToRendezVousZone( 0.1 ) - else - if Task:GetRendezVousPointVec2( TaskUnit ) then - self:__RouteToRendezVousPoint( 0.1 ) - else - self:__ArriveAtRendezVous( 0.1 ) - end - end - end - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task#TASK_A2G Task - function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task ) - self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - -- Determine the first Unit from the self.TargetSetUnit - - self:__Engage( 0.1 ) - end - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task#TASK_A2G Task - function Fsm:onafterEngage( TaskUnit, Task ) - self:E( { self } ) - self:__Account( 0.1 ) - self:__RouteToTarget(0.1 ) - self:__RouteToTargets( -10 ) - end - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_A2G#TASK_A2G Task - function Fsm:onafterRouteToTarget( TaskUnit, Task ) - self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - -- Determine the first Unit from the self.TargetSetUnit - - if Task:GetTargetZone( TaskUnit ) then - self:__RouteToTargetZone( 0.1 ) - else - local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT - if TargetUnit then - local PointVec2 = TargetUnit:GetPointVec2() - self:T( { TargetPointVec2 = PointVec2, PointVec2:GetX(), PointVec2:GetAlt(), PointVec2:GetZ() } ) - Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit ) - end - self:__RouteToTargetPoint( 0.1 ) - end - end - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_A2G#TASK_A2G Task - function Fsm:onafterRouteToTargets( TaskUnit, Task ) - self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT - if TargetUnit then - Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit ) - end - self:__RouteToTargets( -10 ) - end - - return self - - end - - --- @param #TASK_A2G self - function TASK_A2G:GetPlannedMenuText() - return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" - end - - --- @param #TASK_A2G self - -- @param Core.Point#POINT_VEC2 RendezVousPointVec2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map. - -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. - -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_A2G:SetRendezVousPointVec2( RendezVousPointVec2, RendezVousRange, TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - ActRouteRendezVous:SetPointVec2( RendezVousPointVec2 ) - ActRouteRendezVous:SetRange( RendezVousRange ) - end - - --- @param #TASK_A2G self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Point#POINT_VEC2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map. - -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. - function TASK_A2G:GetRendezVousPointVec2( TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - return ActRouteRendezVous:GetPointVec2(), ActRouteRendezVous:GetRange() - end - - - - --- @param #TASK_A2G self - -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. - -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE - ActRouteRendezVous:SetZone( RendezVousZone ) - end - - --- @param #TASK_A2G self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map. - function TASK_A2G:GetRendezVousZone( TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE - return ActRouteRendezVous:GetZone() - end - - --- @param #TASK_A2G self - -- @param Core.Point#POINT_VEC2 TargetPointVec2 The PointVec2 object where the Target is located on the map. - -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_A2G:SetTargetPointVec2( TargetPointVec2, TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - ActRouteTarget:SetPointVec2( TargetPointVec2 ) - end - - - --- @param #TASK_A2G self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Point#POINT_VEC2 The PointVec2 object where the Target is located on the map. - function TASK_A2G:GetTargetPointVec2( TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - return ActRouteTarget:GetPointVec2() - end - - - --- @param #TASK_A2G self - -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. - -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_A2G:SetTargetZone( TargetZone, TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE - ActRouteTarget:SetZone( TargetZone ) - end - - - --- @param #TASK_A2G self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. - function TASK_A2G:GetTargetZone( TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE - return ActRouteTarget:GetZone() - end - - --- Set a score when a target in scope of the A2G attack, has been destroyed . - -- @param #TASK_A2G self - -- @param #string Text The text to display to the player, when the target has been destroyed. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G - function TASK_A2G:SetScoreOnDestroy( Text, Score, TaskUnit ) - self:F( { Text, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScoreProcess( "Engaging", "Account", "Account", Text, Score ) - - return self - end - - --- Set a score when all the targets in scope of the A2G attack, have been destroyed. - -- @param #TASK_A2G self - -- @param #string Text The text to display to the player, when all targets hav been destroyed. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G - function TASK_A2G:SetScoreOnSuccess( Text, Score, TaskUnit ) - self:F( { Text, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Success", Text, Score ) - - return self - end - - --- Set a penalty when the A2G attack has failed. - -- @param #TASK_A2G self - -- @param #string Text The text to display to the player, when the A2G attack has failed. - -- @param #number Penalty The penalty in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G - function TASK_A2G:SetPenaltyOnFailed( Text, Penalty, TaskUnit ) - self:F( { Text, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Failed", Text, Penalty ) - - return self - end - - -end - - -do -- TASK_SEAD - - --- The TASK_SEAD class - -- @type TASK_SEAD - -- @field Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - TASK_SEAD = { - ClassName = "TASK_SEAD", - } - - --- Instantiates a new TASK_SEAD. - -- @param #TASK_SEAD self - -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. - -- @return #TASK_SEAD self - function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit ) - local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD" ) ) -- #TASK_SEAD - self:F() - - return self - end - -end - -do -- TASK_BAI - - --- The TASK_BAI class - -- @type TASK_BAI - -- @field Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - TASK_BAI = { - ClassName = "TASK_BAI", - } - - --- Instantiates a new TASK_BAI. - -- @param #TASK_BAI self - -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. - -- @return #TASK_BAI self - function TASK_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit ) - local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI" ) ) -- #TASK_BAI - self:F() - - return self - end - -end - -do -- TASK_CAS - - --- The TASK_CAS class - -- @type TASK_CAS - -- @field Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - TASK_CAS = { - ClassName = "TASK_CAS", - } - - --- Instantiates a new TASK_CAS. - -- @param #TASK_CAS self - -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. - -- @return #TASK_CAS self - function TASK_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit ) - local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS" ) ) -- #TASK_CAS - self:F() - - return self - end - -end ---- The main include file for the MOOSE system. --- Test of permissions - ---- Core Routines -Include.File( "Utilities/Routines" ) -Include.File( "Utilities/Utils" ) - ---- Core Classes -Include.File( "Core/Base" ) -Include.File( "Core/Scheduler" ) -Include.File( "Core/ScheduleDispatcher") -Include.File( "Core/Event" ) -Include.File( "Core/Menu" ) -Include.File( "Core/Zone" ) -Include.File( "Core/Database" ) -Include.File( "Core/Set" ) -Include.File( "Core/Point" ) -Include.File( "Core/Message" ) -Include.File( "Core/Fsm" ) -Include.File( "Core/Radio" ) - ---- Wrapper Classes -Include.File( "Wrapper/Object" ) -Include.File( "Wrapper/Identifiable" ) -Include.File( "Wrapper/Positionable" ) -Include.File( "Wrapper/Controllable" ) -Include.File( "Wrapper/Group" ) -Include.File( "Wrapper/Unit" ) -Include.File( "Wrapper/Client" ) -Include.File( "Wrapper/Static" ) -Include.File( "Wrapper/Airbase" ) -Include.File( "Wrapper/Scenery" ) - ---- Functional Classes -Include.File( "Functional/Scoring" ) -Include.File( "Functional/CleanUp" ) -Include.File( "Functional/Spawn" ) -Include.File( "Functional/Movement" ) -Include.File( "Functional/Sead" ) -Include.File( "Functional/Escort" ) -Include.File( "Functional/MissileTrainer" ) -Include.File( "Functional/AirbasePolice" ) -Include.File( "Functional/Detection" ) - ---- AI Classes -Include.File( "AI/AI_Balancer" ) -Include.File( "AI/AI_Patrol" ) -Include.File( "AI/AI_Cap" ) -Include.File( "AI/AI_Cas" ) -Include.File( "AI/AI_Cargo" ) - ---- Actions -Include.File( "Actions/Act_Assign" ) -Include.File( "Actions/Act_Route" ) -Include.File( "Actions/Act_Account" ) -Include.File( "Actions/Act_Assist" ) - ---- Task Handling Classes -Include.File( "Tasking/CommandCenter" ) -Include.File( "Tasking/Mission" ) -Include.File( "Tasking/Task" ) -Include.File( "Tasking/DetectionManager" ) -Include.File( "Tasking/Task_A2G_Dispatcher") -Include.File( "Tasking/Task_A2G" ) - - --- The order of the declarations is important here. Don't touch it. - ---- Declare the event dispatcher based on the EVENT class -_EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT - ---- Declare the timer dispatcher based on the SCHEDULEDISPATCHER class -_SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.Timer#SCHEDULEDISPATCHER - ---- Declare the main database object, which is used internally by the MOOSE classes. -_DATABASE = DATABASE:New() -- Database#DATABASE +Include.ProgramPath = "Scripts/Moose/" +env.info( "Include.ProgramPath = " .. Include.ProgramPath) +Include.Files = {} +Include.File( "Moose" ) -BASE:TraceOnOff( false ) +BASE:TraceOnOff( true ) env.info( '*** MOOSE INCLUDE END *** ' ) diff --git a/Moose Mission Setup/Moose.files b/Moose Mission Setup/Moose.files new file mode 100644 index 000000000..1f686de8b --- /dev/null +++ b/Moose Mission Setup/Moose.files @@ -0,0 +1,71 @@ +Utilities/Routines.lua +Utilities/Utils.lua + +Core/Base.lua +Core/Report.lua +Core/Scheduler.lua +Core/ScheduleDispatcher.lua +Core/Event.lua +Core/Settings.lua +Core/Menu.lua +Core/Zone.lua +Core/Database.lua +Core/Set.lua +Core/Point.lua +Core/Message.lua +Core/Fsm.lua +Core/Radio.lua +Core/SpawnStatic.lua +Core/Cargo.lua +Core/Spot.lua + +Wrapper/Object.lua +Wrapper/Identifiable.lua +Wrapper/Positionable.lua +Wrapper/Controllable.lua +Wrapper/Group.lua +Wrapper/Unit.lua +Wrapper/Client.lua +Wrapper/Static.lua +Wrapper/Airbase.lua +Wrapper/Scenery.lua + +Functional/Scoring.lua +Functional/CleanUp.lua +Functional/Spawn.lua +Functional/Movement.lua +Functional/Sead.lua +Functional/Escort.lua +Functional/MissileTrainer.lua +Functional/AirbasePolice.lua +Functional/Detection.lua +Functional/Designate.lua + +AI/AI_Balancer.lua +AI/AI_A2A.lua +AI/AI_A2A_Patrol.lua +AI/AI_A2A_Cap.lua +AI/AI_A2A_Gci.lua +AI/AI_A2A_Dispatcher.lua +AI/AI_Patrol.lua +AI/AI_Cap.lua +AI/AI_Cas.lua +AI/AI_Bai.lua +AI/AI_Formation.lua + +Actions/Act_Assign.lua +Actions/Act_Route.lua +Actions/Act_Account.lua +Actions/Act_Assist.lua + +Tasking/CommandCenter.lua +Tasking/Mission.lua +Tasking/Task.lua +Tasking/DetectionManager.lua +Tasking/Task_A2G_Dispatcher.lua +Tasking/Task_A2G.lua +Tasking/Task_A2A_Dispatcher.lua +Tasking/Task_A2A.lua +Tasking/Task_Cargo.lua + +Moose.lua diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index 0135a9cc2..1cd7ba934 100644 --- a/Moose Mission Setup/Moose.lua +++ b/Moose Mission Setup/Moose.lua @@ -1,35940 +1,89 @@ -env.info( '*** MOOSE STATIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170328_1316' ) +env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' ) +env.info( 'Moose Generation Timestamp: 20170811_0800' ) + local base = _G -Include = {} -Include.Files = {} -Include.File = function( IncludeFile ) -end +__Moose = {} ---- Various routines --- @module routines --- @author Flightcontrol - -env.setErrorMessageBoxEnabled(false) - ---- Extract of MIST functions. --- @author Grimes - -routines = {} - - --- don't change these -routines.majorVersion = 3 -routines.minorVersion = 3 -routines.build = 22 - ------------------------------------------------------------------------------------------------------------------ - ----------------------------------------------------------------------------------------------- --- Utils- conversion, Lua utils, etc. -routines.utils = {} - ---from http://lua-users.org/wiki/CopyTable -routines.utils.deepCopy = function(object) - local lookup_table = {} - local function _copy(object) - if type(object) ~= "table" then - return object - elseif lookup_table[object] then - return lookup_table[object] - end - local new_table = {} - lookup_table[object] = new_table - for index, value in pairs(object) do - new_table[_copy(index)] = _copy(value) - end - return setmetatable(new_table, getmetatable(object)) - end - local objectreturn = _copy(object) - return objectreturn -end - - --- porting in Slmod's serialize_slmod2 -routines.utils.oneLineSerialize = function(tbl) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function - - lookup_table = {} - - local function _Serialize( tbl ) - - if type(tbl) == 'table' then --function only works for tables! - - if lookup_table[tbl] then - return lookup_table[object] - end - - local tbl_str = {} - - lookup_table[tbl] = tbl_str - - tbl_str[#tbl_str + 1] = '{' - - for ind,val in pairs(tbl) do -- serialize its fields - local ind_str = {} - if type(ind) == "number" then - ind_str[#ind_str + 1] = '[' - ind_str[#ind_str + 1] = tostring(ind) - ind_str[#ind_str + 1] = ']=' - else --must be a string - ind_str[#ind_str + 1] = '[' - ind_str[#ind_str + 1] = routines.utils.basicSerialize(ind) - ind_str[#ind_str + 1] = ']=' - end - - local val_str = {} - if ((type(val) == 'number') or (type(val) == 'boolean')) then - val_str[#val_str + 1] = tostring(val) - val_str[#val_str + 1] = ',' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'string' then - val_str[#val_str + 1] = routines.utils.basicSerialize(val) - val_str[#val_str + 1] = ',' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'nil' then -- won't ever happen, right? - val_str[#val_str + 1] = 'nil,' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'table' then - if ind == "__index" then - -- tbl_str[#tbl_str + 1] = "__index" - -- tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - else - - val_str[#val_str + 1] = _Serialize(val) - val_str[#val_str + 1] = ',' --I think this is right, I just added it - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - end - elseif type(val) == 'function' then - -- tbl_str[#tbl_str + 1] = "function " .. tostring(ind) - -- tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - else --- env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) --- env.info( debug.traceback() ) - end - - end - tbl_str[#tbl_str + 1] = '}' - return table.concat(tbl_str) +__Moose.Include = function( IncludeFile ) + if not __Moose.Includes[ IncludeFile ] then + __Moose.Includes[IncludeFile] = IncludeFile + local f = assert( base.loadfile( __Moose.ProgramPath .. IncludeFile ) ) + if f == nil then + error ("Moose: Could not load Moose file " .. IncludeFile ) else - return tostring(tbl) - end - end - - local objectreturn = _Serialize(tbl) - return objectreturn -end - ---porting in Slmod's "safestring" basic serialize -routines.utils.basicSerialize = function(s) - if s == nil then - return "\"\"" - else - if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then - return tostring(s) - elseif type(s) == 'string' then - s = string.format('%q', s) - return s - end - end -end - - -routines.utils.toDegree = function(angle) - return angle*180/math.pi -end - -routines.utils.toRadian = function(angle) - return angle*math.pi/180 -end - -routines.utils.metersToNM = function(meters) - return meters/1852 -end - -routines.utils.metersToFeet = function(meters) - return meters/0.3048 -end - -routines.utils.NMToMeters = function(NM) - return NM*1852 -end - -routines.utils.feetToMeters = function(feet) - return feet*0.3048 -end - -routines.utils.mpsToKnots = function(mps) - return mps*3600/1852 -end - -routines.utils.mpsToKmph = function(mps) - return mps*3.6 -end - -routines.utils.knotsToMps = function(knots) - return knots*1852/3600 -end - -routines.utils.kmphToMps = function(kmph) - return kmph/3.6 -end - -function routines.utils.makeVec2(Vec3) - if Vec3.z then - return {x = Vec3.x, y = Vec3.z} - else - return {x = Vec3.x, y = Vec3.y} -- it was actually already vec2. - end -end - -function routines.utils.makeVec3(Vec2, y) - if not Vec2.z then - if not y then - y = 0 - end - return {x = Vec2.x, y = y, z = Vec2.y} - else - return {x = Vec2.x, y = Vec2.y, z = Vec2.z} -- it was already Vec3, actually. - end -end - -function routines.utils.makeVec3GL(Vec2, offset) - local adj = offset or 0 - - if not Vec2.z then - return {x = Vec2.x, y = (land.getHeight(Vec2) + adj), z = Vec2.y} - else - return {x = Vec2.x, y = (land.getHeight({x = Vec2.x, y = Vec2.z}) + adj), z = Vec2.z} - end -end - -routines.utils.zoneToVec3 = function(zone) - local new = {} - if type(zone) == 'table' and zone.point then - new.x = zone.point.x - new.y = zone.point.y - new.z = zone.point.z - return new - elseif type(zone) == 'string' then - zone = trigger.misc.getZone(zone) - if zone then - new.x = zone.point.x - new.y = zone.point.y - new.z = zone.point.z - return new - end - end -end - --- gets heading-error corrected direction from point along vector vec. -function routines.utils.getDir(vec, point) - local dir = math.atan2(vec.z, vec.x) - dir = dir + routines.getNorthCorrection(point) - if dir < 0 then - dir = dir + 2*math.pi -- put dir in range of 0 to 2*pi - end - return dir -end - --- gets distance in meters between two points (2 dimensional) -function routines.utils.get2DDist(point1, point2) - point1 = routines.utils.makeVec3(point1) - point2 = routines.utils.makeVec3(point2) - return routines.vec.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z}) -end - --- gets distance in meters between two points (3 dimensional) -function routines.utils.get3DDist(point1, point2) - return routines.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) -end - - - - - ---3D Vector manipulation -routines.vec = {} - -routines.vec.add = function(vec1, vec2) - return {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z} -end - -routines.vec.sub = function(vec1, vec2) - return {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z} -end - -routines.vec.scalarMult = function(vec, mult) - return {x = vec.x*mult, y = vec.y*mult, z = vec.z*mult} -end - -routines.vec.scalar_mult = routines.vec.scalarMult - -routines.vec.dp = function(vec1, vec2) - return vec1.x*vec2.x + vec1.y*vec2.y + vec1.z*vec2.z -end - -routines.vec.cp = function(vec1, vec2) - return { x = vec1.y*vec2.z - vec1.z*vec2.y, y = vec1.z*vec2.x - vec1.x*vec2.z, z = vec1.x*vec2.y - vec1.y*vec2.x} -end - -routines.vec.mag = function(vec) - return (vec.x^2 + vec.y^2 + vec.z^2)^0.5 -end - -routines.vec.getUnitVec = function(vec) - local mag = routines.vec.mag(vec) - return { x = vec.x/mag, y = vec.y/mag, z = vec.z/mag } -end - -routines.vec.rotateVec2 = function(vec2, theta) - return { x = vec2.x*math.cos(theta) - vec2.y*math.sin(theta), y = vec2.x*math.sin(theta) + vec2.y*math.cos(theta)} -end ---------------------------------------------------------------------------------------------------------------------------- - - - - --- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5. -routines.tostringMGRS = function(MGRS, acc) - if acc == 0 then - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph - else - return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', routines.utils.round(MGRS.Easting/(10^(5-acc)), 0)) - .. ' ' .. string.format('%0' .. acc .. 'd', routines.utils.round(MGRS.Northing/(10^(5-acc)), 0)) - end -end - ---[[acc: -in DM: decimal point of minutes. -In DMS: decimal point of seconds. -position after the decimal of the least significant digit: -So: -42.32 - acc of 2. -]] -routines.tostringLL = function(lat, lon, acc, DMS) - - local latHemi, lonHemi - if lat > 0 then - latHemi = 'N' - else - latHemi = 'S' - end - - if lon > 0 then - lonHemi = 'E' - else - lonHemi = 'W' - end - - lat = math.abs(lat) - lon = math.abs(lon) - - local latDeg = math.floor(lat) - local latMin = (lat - latDeg)*60 - - local lonDeg = math.floor(lon) - local lonMin = (lon - lonDeg)*60 - - if DMS then -- degrees, minutes, and seconds. - local oldLatMin = latMin - latMin = math.floor(latMin) - local latSec = routines.utils.round((oldLatMin - latMin)*60, acc) - - local oldLonMin = lonMin - lonMin = math.floor(lonMin) - local lonSec = routines.utils.round((oldLonMin - lonMin)*60, acc) - - if latSec == 60 then - latSec = 0 - latMin = latMin + 1 - end - - if lonSec == 60 then - lonSec = 0 - lonMin = lonMin + 1 - end - - local secFrmtStr -- create the formatting string for the seconds place - if acc <= 0 then -- no decimal place. - secFrmtStr = '%02d' - else - local width = 3 + acc -- 01.310 - that's a width of 6, for example. - secFrmtStr = '%0' .. width .. '.' .. acc .. 'f' - end - - return string.format('%02d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' ' - .. string.format('%02d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi - - else -- degrees, decimal minutes. - latMin = routines.utils.round(latMin, acc) - lonMin = routines.utils.round(lonMin, acc) - - if latMin == 60 then - latMin = 0 - latDeg = latDeg + 1 - end - - if lonMin == 60 then - lonMin = 0 - lonDeg = lonDeg + 1 - end - - local minFrmtStr -- create the formatting string for the minutes place - if acc <= 0 then -- no decimal place. - minFrmtStr = '%02d' - else - local width = 3 + acc -- 01.310 - that's a width of 6, for example. - minFrmtStr = '%0' .. width .. '.' .. acc .. 'f' - end - - return string.format('%02d', latDeg) .. ' ' .. string.format(minFrmtStr, latMin) .. '\'' .. latHemi .. ' ' - .. string.format('%02d', lonDeg) .. ' ' .. string.format(minFrmtStr, lonMin) .. '\'' .. lonHemi - - end -end - ---[[ required: az - radian - required: dist - meters - optional: alt - meters (set to false or nil if you don't want to use it). - optional: metric - set true to get dist and alt in km and m. - precision will always be nearest degree and NM or km.]] -routines.tostringBR = function(az, dist, alt, metric) - az = routines.utils.round(routines.utils.toDegree(az), 0) - - if metric then - dist = routines.utils.round(dist/1000, 2) - else - dist = routines.utils.round(routines.utils.metersToNM(dist), 2) - end - - local s = string.format('%03d', az) .. ' for ' .. dist - - if alt then - if metric then - s = s .. ' at ' .. routines.utils.round(alt, 0) - else - s = s .. ' at ' .. routines.utils.round(routines.utils.metersToFeet(alt), 0) - end - end - return s -end - -routines.getNorthCorrection = function(point) --gets the correction needed for true north - if not point.z then --Vec2; convert to Vec3 - point.z = point.y - point.y = 0 - end - local lat, lon = coord.LOtoLL(point) - local north_posit = coord.LLtoLO(lat + 1, lon) - return math.atan2(north_posit.z - point.z, north_posit.x - point.x) -end - - -do - local idNum = 0 - - --Simplified event handler - routines.addEventHandler = function(f) --id is optional! - local handler = {} - idNum = idNum + 1 - handler.id = idNum - handler.f = f - handler.onEvent = function(self, event) - self.f(event) - end - world.addEventHandler(handler) - end - - routines.removeEventHandler = function(id) - for key, handler in pairs(world.eventHandlers) do - if handler.id and handler.id == id then - world.eventHandlers[key] = nil - return true - end - end - return false - end -end - --- need to return a Vec3 or Vec2? -function routines.getRandPointInCircle(point, radius, innerRadius) - local theta = 2*math.pi*math.random() - local rad = math.random() + math.random() - if rad > 1 then - rad = 2 - rad - end - - local radMult - if innerRadius and innerRadius <= radius then - radMult = (radius - innerRadius)*rad + innerRadius - else - radMult = radius*rad - end - - if not point.z then --might as well work with vec2/3 - point.z = point.y - end - - local rndCoord - if radius > 0 then - rndCoord = {x = math.cos(theta)*radMult + point.x, y = math.sin(theta)*radMult + point.z} - else - rndCoord = {x = point.x, y = point.z} - end - return rndCoord -end - -routines.goRoute = function(group, path) - local misTask = { - id = 'Mission', - params = { - route = { - points = routines.utils.deepCopy(path), - }, - }, - } - if type(group) == 'string' then - group = Group.getByName(group) - end - local groupCon = group:getController() - if groupCon then - groupCon:setTask(misTask) - return true - end - - Controller.setTask(groupCon, misTask) - return false -end - - --- Useful atomic functions from mist, ported. - -routines.ground = {} -routines.fixedWing = {} -routines.heli = {} - -routines.ground.buildWP = function(point, overRideForm, overRideSpeed) - - local wp = {} - wp.x = point.x - - if point.z then - wp.y = point.z - else - wp.y = point.y - end - local form, speed - - if point.speed and not overRideSpeed then - wp.speed = point.speed - elseif type(overRideSpeed) == 'number' then - wp.speed = overRideSpeed - else - wp.speed = routines.utils.kmphToMps(20) - end - - if point.form and not overRideForm then - form = point.form - else - form = overRideForm - end - - if not form then - wp.action = 'Cone' - else - form = string.lower(form) - if form == 'off_road' or form == 'off road' then - wp.action = 'Off Road' - elseif form == 'on_road' or form == 'on road' then - wp.action = 'On Road' - elseif form == 'rank' or form == 'line_abrest' or form == 'line abrest' or form == 'lineabrest'then - wp.action = 'Rank' - elseif form == 'cone' then - wp.action = 'Cone' - elseif form == 'diamond' then - wp.action = 'Diamond' - elseif form == 'vee' then - wp.action = 'Vee' - elseif form == 'echelon_left' or form == 'echelon left' or form == 'echelonl' then - wp.action = 'EchelonL' - elseif form == 'echelon_right' or form == 'echelon right' or form == 'echelonr' then - wp.action = 'EchelonR' - else - wp.action = 'Cone' -- if nothing matched - end - end - - wp.type = 'Turning Point' - - return wp - -end - -routines.fixedWing.buildWP = function(point, WPtype, speed, alt, altType) - - local wp = {} - wp.x = point.x - - if point.z then - wp.y = point.z - else - wp.y = point.y - end - - if alt and type(alt) == 'number' then - wp.alt = alt - else - wp.alt = 2000 - end - - if altType then - altType = string.lower(altType) - if altType == 'radio' or 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or 'asl' then - wp.alt_type = 'BARO' - end - else - wp.alt_type = 'RADIO' - end - - if point.speed then - speed = point.speed - end - - if point.type then - WPtype = point.type - end - - if not speed then - wp.speed = routines.utils.kmphToMps(500) - else - wp.speed = speed - end - - if not WPtype then - wp.action = 'Turning Point' - else - WPtype = string.lower(WPtype) - if WPtype == 'flyover' or WPtype == 'fly over' or WPtype == 'fly_over' then - wp.action = 'Fly Over Point' - elseif WPtype == 'turningpoint' or WPtype == 'turning point' or WPtype == 'turning_point' then - wp.action = 'Turning Point' - else - wp.action = 'Turning Point' - end - end - - wp.type = 'Turning Point' - return wp -end - -routines.heli.buildWP = function(point, WPtype, speed, alt, altType) - - local wp = {} - wp.x = point.x - - if point.z then - wp.y = point.z - else - wp.y = point.y - end - - if alt and type(alt) == 'number' then - wp.alt = alt - else - wp.alt = 500 - end - - if altType then - altType = string.lower(altType) - if altType == 'radio' or 'agl' then - wp.alt_type = 'RADIO' - elseif altType == 'baro' or 'asl' then - wp.alt_type = 'BARO' - end - else - wp.alt_type = 'RADIO' - end - - if point.speed then - speed = point.speed - end - - if point.type then - WPtype = point.type - end - - if not speed then - wp.speed = routines.utils.kmphToMps(200) - else - wp.speed = speed - end - - if not WPtype then - wp.action = 'Turning Point' - else - WPtype = string.lower(WPtype) - if WPtype == 'flyover' or WPtype == 'fly over' or WPtype == 'fly_over' then - wp.action = 'Fly Over Point' - elseif WPtype == 'turningpoint' or WPtype == 'turning point' or WPtype == 'turning_point' then - wp.action = 'Turning Point' - else - wp.action = 'Turning Point' - end - end - - wp.type = 'Turning Point' - return wp -end - -routines.groupToRandomPoint = function(vars) - local group = vars.group --Required - local point = vars.point --required - local radius = vars.radius or 0 - local innerRadius = vars.innerRadius - local form = vars.form or 'Cone' - local heading = vars.heading or math.random()*2*math.pi - local headingDegrees = vars.headingDegrees - local speed = vars.speed or routines.utils.kmphToMps(20) - - - local useRoads - if not vars.disableRoads then - useRoads = true - else - useRoads = false - end - - local path = {} - - if headingDegrees then - heading = headingDegrees*math.pi/180 - end - - if heading >= 2*math.pi then - heading = heading - 2*math.pi - end - - local rndCoord = routines.getRandPointInCircle(point, radius, innerRadius) - - local offset = {} - local posStart = routines.getLeadPos(group) - - offset.x = routines.utils.round(math.sin(heading - (math.pi/2)) * 50 + rndCoord.x, 3) - offset.z = routines.utils.round(math.cos(heading + (math.pi/2)) * 50 + rndCoord.y, 3) - path[#path + 1] = routines.ground.buildWP(posStart, form, speed) - - - if useRoads == true and ((point.x - posStart.x)^2 + (point.z - posStart.z)^2)^0.5 > radius * 1.3 then - path[#path + 1] = routines.ground.buildWP({['x'] = posStart.x + 11, ['z'] = posStart.z + 11}, 'off_road', speed) - path[#path + 1] = routines.ground.buildWP(posStart, 'on_road', speed) - path[#path + 1] = routines.ground.buildWP(offset, 'on_road', speed) - else - path[#path + 1] = routines.ground.buildWP({['x'] = posStart.x + 25, ['z'] = posStart.z + 25}, form, speed) - end - - path[#path + 1] = routines.ground.buildWP(offset, form, speed) - path[#path + 1] = routines.ground.buildWP(rndCoord, form, speed) - - routines.goRoute(group, path) - - return -end - -routines.groupRandomDistSelf = function(gpData, dist, form, heading, speed) - local pos = routines.getLeadPos(gpData) - local fakeZone = {} - fakeZone.radius = dist or math.random(300, 1000) - fakeZone.point = {x = pos.x, y, pos.y, z = pos.z} - routines.groupToRandomZone(gpData, fakeZone, form, heading, speed) - - return -end - -routines.groupToRandomZone = function(gpData, zone, form, heading, speed) - if type(gpData) == 'string' then - gpData = Group.getByName(gpData) - end - - if type(zone) == 'string' then - zone = trigger.misc.getZone(zone) - elseif type(zone) == 'table' and not zone.radius then - zone = trigger.misc.getZone(zone[math.random(1, #zone)]) - end - - if speed then - speed = routines.utils.kmphToMps(speed) - end - - local vars = {} - vars.group = gpData - vars.radius = zone.radius - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.point = routines.utils.zoneToVec3(zone) - - routines.groupToRandomPoint(vars) - - return -end - -routines.isTerrainValid = function(coord, terrainTypes) -- vec2/3 and enum or table of acceptable terrain types - if coord.z then - coord.y = coord.z - end - local typeConverted = {} - - if type(terrainTypes) == 'string' then -- if its a string it does this check - for constId, constData in pairs(land.SurfaceType) do - if string.lower(constId) == string.lower(terrainTypes) or string.lower(constData) == string.lower(terrainTypes) then - table.insert(typeConverted, constId) - end - end - elseif type(terrainTypes) == 'table' then -- if its a table it does this check - for typeId, typeData in pairs(terrainTypes) do - for constId, constData in pairs(land.SurfaceType) do - if string.lower(constId) == string.lower(typeData) or string.lower(constData) == string.lower(typeId) then - table.insert(typeConverted, constId) - end - end - end - end - for validIndex, validData in pairs(typeConverted) do - if land.getSurfaceType(coord) == land.SurfaceType[validData] then - return true - end - end - return false -end - -routines.groupToPoint = function(gpData, point, form, heading, speed, useRoads) - if type(point) == 'string' then - point = trigger.misc.getZone(point) - end - if speed then - speed = routines.utils.kmphToMps(speed) - end - - local vars = {} - vars.group = gpData - vars.form = form - vars.headingDegrees = heading - vars.speed = speed - vars.disableRoads = useRoads - vars.point = routines.utils.zoneToVec3(point) - routines.groupToRandomPoint(vars) - - return -end - - -routines.getLeadPos = function(group) - if type(group) == 'string' then -- group name - group = Group.getByName(group) - end - - local units = group:getUnits() - - local leader = units[1] - if not leader then -- SHOULD be good, but if there is a bug, this code future-proofs it then. - local lowestInd = math.huge - for ind, unit in pairs(units) do - if ind < lowestInd then - lowestInd = ind - leader = unit - end - end - end - if leader and Unit.isExist(leader) then -- maybe a little too paranoid now... - return leader:getPosition().p - end -end - ---[[ vars for routines.getMGRSString: -vars.units - table of unit names (NOT unitNameTable- maybe this should change). -vars.acc - integer between 0 and 5, inclusive -]] -routines.getMGRSString = function(vars) - local units = vars.units - local acc = vars.acc or 5 - local avgPos = routines.getAvgPos(units) - if avgPos then - return routines.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(avgPos)), acc) - end -end - ---[[ vars for routines.getLLString -vars.units - table of unit names (NOT unitNameTable- maybe this should change). -vars.acc - integer, number of numbers after decimal place -vars.DMS - if true, output in degrees, minutes, seconds. Otherwise, output in degrees, minutes. - - -]] -routines.getLLString = function(vars) - local units = vars.units - local acc = vars.acc or 3 - local DMS = vars.DMS - local avgPos = routines.getAvgPos(units) - if avgPos then - local lat, lon = coord.LOtoLL(avgPos) - return routines.tostringLL(lat, lon, acc, DMS) - end -end - ---[[ -vars.zone - table of a zone name. -vars.ref - vec3 ref point, maybe overload for vec2 as well? -vars.alt - boolean, if used, includes altitude in string -vars.metric - boolean, gives distance in km instead of NM. -]] -routines.getBRStringZone = function(vars) - local zone = trigger.misc.getZone( vars.zone ) - local ref = routines.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. - local alt = vars.alt - local metric = vars.metric - if zone then - local vec = {x = zone.point.x - ref.x, y = zone.point.y - ref.y, z = zone.point.z - ref.z} - local dir = routines.utils.getDir(vec, ref) - local dist = routines.utils.get2DDist(zone.point, ref) - if alt then - alt = zone.y - end - return routines.tostringBR(dir, dist, alt, metric) - else - env.info( 'routines.getBRStringZone: error: zone is nil' ) - end -end - ---[[ -vars.units- table of unit names (NOT unitNameTable- maybe this should change). -vars.ref - vec3 ref point, maybe overload for vec2 as well? -vars.alt - boolean, if used, includes altitude in string -vars.metric - boolean, gives distance in km instead of NM. -]] -routines.getBRString = function(vars) - local units = vars.units - local ref = routines.utils.makeVec3(vars.ref, 0) -- turn it into Vec3 if it is not already. - local alt = vars.alt - local metric = vars.metric - local avgPos = routines.getAvgPos(units) - if avgPos then - local vec = {x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z} - local dir = routines.utils.getDir(vec, ref) - local dist = routines.utils.get2DDist(avgPos, ref) - if alt then - alt = avgPos.y - end - return routines.tostringBR(dir, dist, alt, metric) - end -end - - --- Returns the Vec3 coordinates of the average position of the concentration of units most in the heading direction. ---[[ vars for routines.getLeadingPos: -vars.units - table of unit names -vars.heading - direction -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees -]] -routines.getLeadingPos = function(vars) - local units = vars.units - local heading = vars.heading - local radius = vars.radius - if vars.headingDegrees then - heading = routines.utils.toRadian(vars.headingDegrees) - end - - local unitPosTbl = {} - for i = 1, #units do - local unit = Unit.getByName(units[i]) - if unit and unit:isExist() then - unitPosTbl[#unitPosTbl + 1] = unit:getPosition().p - end - end - if #unitPosTbl > 0 then -- one more more units found. - -- first, find the unit most in the heading direction - local maxPos = -math.huge - - local maxPosInd -- maxPos - the furthest in direction defined by heading; maxPosInd = - for i = 1, #unitPosTbl do - local rotatedVec2 = routines.vec.rotateVec2(routines.utils.makeVec2(unitPosTbl[i]), heading) - if (not maxPos) or maxPos < rotatedVec2.x then - maxPos = rotatedVec2.x - maxPosInd = i - end - end - - --now, get all the units around this unit... - local avgPos - if radius then - local maxUnitPos = unitPosTbl[maxPosInd] - local avgx, avgy, avgz, totNum = 0, 0, 0, 0 - for i = 1, #unitPosTbl do - if routines.utils.get2DDist(maxUnitPos, unitPosTbl[i]) <= radius then - avgx = avgx + unitPosTbl[i].x - avgy = avgy + unitPosTbl[i].y - avgz = avgz + unitPosTbl[i].z - totNum = totNum + 1 - end - end - avgPos = { x = avgx/totNum, y = avgy/totNum, z = avgz/totNum} - else - avgPos = unitPosTbl[maxPosInd] - end - - return avgPos - end -end - - ---[[ vars for routines.getLeadingMGRSString: -vars.units - table of unit names -vars.heading - direction -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees -vars.acc - number, 0 to 5. -]] -routines.getLeadingMGRSString = function(vars) - local pos = routines.getLeadingPos(vars) - if pos then - local acc = vars.acc or 5 - return routines.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(pos)), acc) - end -end - ---[[ vars for routines.getLeadingLLString: -vars.units - table of unit names -vars.heading - direction, number -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees -vars.acc - number of digits after decimal point (can be negative) -vars.DMS - boolean, true if you want DMS. -]] -routines.getLeadingLLString = function(vars) - local pos = routines.getLeadingPos(vars) - if pos then - local acc = vars.acc or 3 - local DMS = vars.DMS - local lat, lon = coord.LOtoLL(pos) - return routines.tostringLL(lat, lon, acc, DMS) - end -end - - - ---[[ vars for routines.getLeadingBRString: -vars.units - table of unit names -vars.heading - direction, number -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees -vars.metric - boolean, if true, use km instead of NM. -vars.alt - boolean, if true, include altitude. -vars.ref - vec3/vec2 reference point. -]] -routines.getLeadingBRString = function(vars) - local pos = routines.getLeadingPos(vars) - if pos then - local ref = vars.ref - local alt = vars.alt - local metric = vars.metric - - local vec = {x = pos.x - ref.x, y = pos.y - ref.y, z = pos.z - ref.z} - local dir = routines.utils.getDir(vec, ref) - local dist = routines.utils.get2DDist(pos, ref) - if alt then - alt = pos.y - end - return routines.tostringBR(dir, dist, alt, metric) - end -end - ---[[ vars for routines.message.add - vars.text = 'Hello World' - vars.displayTime = 20 - vars.msgFor = {coa = {'red'}, countries = {'Ukraine', 'Georgia'}, unitTypes = {'A-10C'}} - -]] - ---[[ vars for routines.msgMGRS -vars.units - table of unit names (NOT unitNameTable- maybe this should change). -vars.acc - integer between 0 and 5, inclusive -vars.text - text in the message -vars.displayTime - self explanatory -vars.msgFor - scope -]] -routines.msgMGRS = function(vars) - local units = vars.units - local acc = vars.acc - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getMGRSString{units = units, acc = acc} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } -end - ---[[ vars for routines.msgLL -vars.units - table of unit names (NOT unitNameTable- maybe this should change) (Yes). -vars.acc - integer, number of numbers after decimal place -vars.DMS - if true, output in degrees, minutes, seconds. Otherwise, output in degrees, minutes. -vars.text - text in the message -vars.displayTime - self explanatory -vars.msgFor - scope -]] -routines.msgLL = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local acc = vars.acc - local DMS = vars.DMS - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getLLString{units = units, acc = acc, DMS = DMS} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - -end - - ---[[ -vars.units- table of unit names (NOT unitNameTable- maybe this should change). -vars.ref - vec3 ref point, maybe overload for vec2 as well? -vars.alt - boolean, if used, includes altitude in string -vars.metric - boolean, gives distance in km instead of NM. -vars.text - text of the message -vars.displayTime -vars.msgFor - scope -]] -routines.msgBR = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local ref = vars.ref -- vec2/vec3 will be handled in routines.getBRString - local alt = vars.alt - local metric = vars.metric - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getBRString{units = units, ref = ref, alt = alt, metric = metric} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - -end - - --------------------------------------------------------------------------------------------- --- basically, just sub-types of routines.msgBR... saves folks the work of getting the ref point. ---[[ -vars.units- table of unit names (NOT unitNameTable- maybe this should change). -vars.ref - string red, blue -vars.alt - boolean, if used, includes altitude in string -vars.metric - boolean, gives distance in km instead of NM. -vars.text - text of the message -vars.displayTime -vars.msgFor - scope -]] -routines.msgBullseye = function(vars) - if string.lower(vars.ref) == 'red' then - vars.ref = routines.DBs.missionData.bullseye.red - routines.msgBR(vars) - elseif string.lower(vars.ref) == 'blue' then - vars.ref = routines.DBs.missionData.bullseye.blue - routines.msgBR(vars) - end -end - ---[[ -vars.units- table of unit names (NOT unitNameTable- maybe this should change). -vars.ref - unit name of reference point -vars.alt - boolean, if used, includes altitude in string -vars.metric - boolean, gives distance in km instead of NM. -vars.text - text of the message -vars.displayTime -vars.msgFor - scope -]] - -routines.msgBRA = function(vars) - if Unit.getByName(vars.ref) then - vars.ref = Unit.getByName(vars.ref):getPosition().p - if not vars.alt then - vars.alt = true - end - routines.msgBR(vars) - end -end --------------------------------------------------------------------------------------------- - ---[[ vars for routines.msgLeadingMGRS: -vars.units - table of unit names -vars.heading - direction -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees (optional) -vars.acc - number, 0 to 5. -vars.text - text of the message -vars.displayTime -vars.msgFor - scope -]] -routines.msgLeadingMGRS = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local heading = vars.heading - local radius = vars.radius - local headingDegrees = vars.headingDegrees - local acc = vars.acc - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getLeadingMGRSString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - - -end ---[[ vars for routines.msgLeadingLL: -vars.units - table of unit names -vars.heading - direction, number -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees (optional) -vars.acc - number of digits after decimal point (can be negative) -vars.DMS - boolean, true if you want DMS. (optional) -vars.text - text of the message -vars.displayTime -vars.msgFor - scope -]] -routines.msgLeadingLL = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local heading = vars.heading - local radius = vars.radius - local headingDegrees = vars.headingDegrees - local acc = vars.acc - local DMS = vars.DMS - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getLeadingLLString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, acc = acc, DMS = DMS} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } - -end - ---[[ -vars.units - table of unit names -vars.heading - direction, number -vars.radius - number -vars.headingDegrees - boolean, switches heading to degrees (optional) -vars.metric - boolean, if true, use km instead of NM. (optional) -vars.alt - boolean, if true, include altitude. (optional) -vars.ref - vec3/vec2 reference point. -vars.text - text of the message -vars.displayTime -vars.msgFor - scope -]] -routines.msgLeadingBR = function(vars) - local units = vars.units -- technically, I don't really need to do this, but it helps readability. - local heading = vars.heading - local radius = vars.radius - local headingDegrees = vars.headingDegrees - local metric = vars.metric - local alt = vars.alt - local ref = vars.ref -- vec2/vec3 will be handled in routines.getBRString - local text = vars.text - local displayTime = vars.displayTime - local msgFor = vars.msgFor - - local s = routines.getLeadingBRString{units = units, heading = heading, radius = radius, headingDegrees = headingDegrees, metric = metric, alt = alt, ref = ref} - local newText - if string.find(text, '%%s') then -- look for %s - newText = string.format(text, s) -- insert the coordinates into the message - else -- else, just append to the end. - newText = text .. s - end - - routines.message.add{ - text = newText, - displayTime = displayTime, - msgFor = msgFor - } -end - - -function spairs(t, order) - -- collect the keys - local keys = {} - for k in pairs(t) do keys[#keys+1] = k end - - -- if order function given, sort by it by passing the table and keys a, b, - -- otherwise just sort the keys - if order then - table.sort(keys, function(a,b) return order(t, a, b) end) - else - table.sort(keys) - end - - -- return the iterator function - local i = 0 - return function() - i = i + 1 - if keys[i] then - return keys[i], t[keys[i]] - end - end -end - - -function routines.IsPartOfGroupInZones( CargoGroup, LandingZones ) ---trace.f() - - local CurrentZoneID = nil - - if CargoGroup then - local CargoUnits = CargoGroup:getUnits() - for CargoUnitID, CargoUnit in pairs( CargoUnits ) do - if CargoUnit and CargoUnit:getLife() >= 1.0 then - CurrentZoneID = routines.IsUnitInZones( CargoUnit, LandingZones ) - if CurrentZoneID then - break - end - end - end - end - ---trace.r( "", "", { CurrentZoneID } ) - return CurrentZoneID -end - - - -function routines.IsUnitInZones( TransportUnit, LandingZones ) ---trace.f("", "routines.IsUnitInZones" ) - - local TransportZoneResult = nil - local TransportZonePos = nil - local TransportZone = nil - - -- fill-up some local variables to support further calculations to determine location of units within the zone. - if TransportUnit then - local TransportUnitPos = TransportUnit:getPosition().p - if type( LandingZones ) == "table" then - for LandingZoneID, LandingZoneName in pairs( LandingZones ) do - TransportZone = trigger.misc.getZone( LandingZoneName ) - if TransportZone then - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then - TransportZoneResult = LandingZoneID - break - end - end - end - else - TransportZone = trigger.misc.getZone( LandingZones ) - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then - TransportZoneResult = 1 - end - end - if TransportZoneResult then - --trace.i( "routines", "TransportZone:" .. TransportZoneResult ) - else - --trace.i( "routines", "TransportZone:nil logic" ) - end - return TransportZoneResult - else - --trace.i( "routines", "TransportZone:nil hard" ) - return nil - end -end - -function routines.IsUnitNearZonesRadius( TransportUnit, LandingZones, ZoneRadius ) ---trace.f("", "routines.IsUnitInZones" ) - - local TransportZoneResult = nil - local TransportZonePos = nil - local TransportZone = nil - - -- fill-up some local variables to support further calculations to determine location of units within the zone. - if TransportUnit then - local TransportUnitPos = TransportUnit:getPosition().p - if type( LandingZones ) == "table" then - for LandingZoneID, LandingZoneName in pairs( LandingZones ) do - TransportZone = trigger.misc.getZone( LandingZoneName ) - if TransportZone then - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= ZoneRadius ) then - TransportZoneResult = LandingZoneID - break - end - end - end - else - TransportZone = trigger.misc.getZone( LandingZones ) - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportUnitPos.x - TransportZonePos.x)^2 + (TransportUnitPos.z - TransportZonePos.z)^2)^0.5 <= ZoneRadius ) then - TransportZoneResult = 1 - end - end - if TransportZoneResult then - --trace.i( "routines", "TransportZone:" .. TransportZoneResult ) - else - --trace.i( "routines", "TransportZone:nil logic" ) - end - return TransportZoneResult - else - --trace.i( "routines", "TransportZone:nil hard" ) - return nil - end -end - - -function routines.IsStaticInZones( TransportStatic, LandingZones ) ---trace.f() - - local TransportZoneResult = nil - local TransportZonePos = nil - local TransportZone = nil - - -- fill-up some local variables to support further calculations to determine location of units within the zone. - local TransportStaticPos = TransportStatic:getPosition().p - if type( LandingZones ) == "table" then - for LandingZoneID, LandingZoneName in pairs( LandingZones ) do - TransportZone = trigger.misc.getZone( LandingZoneName ) - if TransportZone then - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportStaticPos.x - TransportZonePos.x)^2 + (TransportStaticPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then - TransportZoneResult = LandingZoneID - break - end - end - end - else - TransportZone = trigger.misc.getZone( LandingZones ) - TransportZonePos = {radius = TransportZone.radius, x = TransportZone.point.x, y = TransportZone.point.y, z = TransportZone.point.z} - if ((( TransportStaticPos.x - TransportZonePos.x)^2 + (TransportStaticPos.z - TransportZonePos.z)^2)^0.5 <= TransportZonePos.radius) then - TransportZoneResult = 1 - end - end - ---trace.r( "", "", { TransportZoneResult } ) - return TransportZoneResult -end - - -function routines.IsUnitInRadius( CargoUnit, ReferencePosition, Radius ) ---trace.f() - - local Valid = true - - -- fill-up some local variables to support further calculations to determine location of units within the zone. - local CargoPos = CargoUnit:getPosition().p - local ReferenceP = ReferencePosition.p - - if (((CargoPos.x - ReferenceP.x)^2 + (CargoPos.z - ReferenceP.z)^2)^0.5 <= Radius) then - else - Valid = false - end - - return Valid -end - -function routines.IsPartOfGroupInRadius( CargoGroup, ReferencePosition, Radius ) ---trace.f() - - local Valid = true - - Valid = routines.ValidateGroup( CargoGroup, "CargoGroup", Valid ) - - -- fill-up some local variables to support further calculations to determine location of units within the zone - local CargoUnits = CargoGroup:getUnits() - for CargoUnitId, CargoUnit in pairs( CargoUnits ) do - local CargoUnitPos = CargoUnit:getPosition().p --- env.info( 'routines.IsPartOfGroupInRadius: CargoUnitPos.x = ' .. CargoUnitPos.x .. ' CargoUnitPos.z = ' .. CargoUnitPos.z ) - local ReferenceP = ReferencePosition.p --- env.info( 'routines.IsPartOfGroupInRadius: ReferenceGroupPos.x = ' .. ReferenceGroupPos.x .. ' ReferenceGroupPos.z = ' .. ReferenceGroupPos.z ) - - if ((( CargoUnitPos.x - ReferenceP.x)^2 + (CargoUnitPos.z - ReferenceP.z)^2)^0.5 <= Radius) then - else - Valid = false - break - end - end - - return Valid -end - - -function routines.ValidateString( Variable, VariableName, Valid ) ---trace.f() - - if type( Variable ) == "string" then - if Variable == "" then - error( "routines.ValidateString: error: " .. VariableName .. " must be filled out!" ) - Valid = false - end - else - error( "routines.ValidateString: error: " .. VariableName .. " is not a string." ) - Valid = false - end - ---trace.r( "", "", { Valid } ) - return Valid -end - -function routines.ValidateNumber( Variable, VariableName, Valid ) ---trace.f() - - if type( Variable ) == "number" then - else - error( "routines.ValidateNumber: error: " .. VariableName .. " is not a number." ) - Valid = false - end - ---trace.r( "", "", { Valid } ) - return Valid - -end - -function routines.ValidateGroup( Variable, VariableName, Valid ) ---trace.f() - - if Variable == nil then - error( "routines.ValidateGroup: error: " .. VariableName .. " is a nil value!" ) - Valid = false - end - ---trace.r( "", "", { Valid } ) - return Valid -end - -function routines.ValidateZone( LandingZones, VariableName, Valid ) ---trace.f() - - if LandingZones == nil then - error( "routines.ValidateGroup: error: " .. VariableName .. " is a nil value!" ) - Valid = false - end - - if type( LandingZones ) == "table" then - for LandingZoneID, LandingZoneName in pairs( LandingZones ) do - if trigger.misc.getZone( LandingZoneName ) == nil then - error( "routines.ValidateGroup: error: Zone " .. LandingZoneName .. " does not exist!" ) - Valid = false - break - end - end - else - if trigger.misc.getZone( LandingZones ) == nil then - error( "routines.ValidateGroup: error: Zone " .. LandingZones .. " does not exist!" ) - Valid = false - end - end - ---trace.r( "", "", { Valid } ) - return Valid -end - -function routines.ValidateEnumeration( Variable, VariableName, Enum, Valid ) ---trace.f() - - local ValidVariable = false - - for EnumId, EnumData in pairs( Enum ) do - if Variable == EnumData then - ValidVariable = true - break - end - end - - if ValidVariable then - else - error( 'TransportValidateEnum: " .. VariableName .. " is not a valid type.' .. Variable ) - Valid = false - end - ---trace.r( "", "", { Valid } ) - return Valid -end - -function routines.getGroupRoute(groupIdent, task) -- same as getGroupPoints but returns speed and formation type along with vec2 of point} - -- refactor to search by groupId and allow groupId and groupName as inputs - local gpId = groupIdent - if type(groupIdent) == 'string' and not tonumber(groupIdent) then - gpId = _DATABASE.Templates.Groups[groupIdent].groupId - end - - for coa_name, coa_data in pairs(env.mission.coalition) do - if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - for obj_type_name, obj_type_data in pairs(cntry_data) do - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - for group_num, group_data in pairs(obj_type_data.group) do - if group_data and group_data.groupId == gpId then -- this is the group we are looking for - if group_data.route and group_data.route.points and #group_data.route.points > 0 then - local points = {} - - for point_num, point in pairs(group_data.route.points) do - local routeData = {} - if not point.point then - routeData.x = point.x - routeData.y = point.y - else - routeData.point = point.point --it's possible that the ME could move to the point = Vec2 notation. - end - routeData.form = point.action - routeData.speed = point.speed - routeData.alt = point.alt - routeData.alt_type = point.alt_type - routeData.airdromeId = point.airdromeId - routeData.helipadId = point.helipadId - routeData.type = point.type - routeData.action = point.action - if task then - routeData.task = point.task - end - points[point_num] = routeData - end - - return points - end - return - end --if group_data and group_data.name and group_data.name == 'groupname' - end --for group_num, group_data in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do - end --for cntry_id, cntry_data in pairs(coa_data.country) do - end --if coa_data.country then --there is a country table - end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then - end --for coa_name, coa_data in pairs(mission.coalition) do -end - -routines.ground.patrolRoute = function(vars) - - - local tempRoute = {} - local useRoute = {} - local gpData = vars.gpData - if type(gpData) == 'string' then - gpData = Group.getByName(gpData) - end - - local useGroupRoute - if not vars.useGroupRoute then - useGroupRoute = vars.gpData - else - useGroupRoute = vars.useGroupRoute - end - local routeProvided = false - if not vars.route then - if useGroupRoute then - tempRoute = routines.getGroupRoute(useGroupRoute) - end - else - useRoute = vars.route - local posStart = routines.getLeadPos(gpData) - useRoute[1] = routines.ground.buildWP(posStart, useRoute[1].action, useRoute[1].speed) - routeProvided = true - end - - - local overRideSpeed = vars.speed or 'default' - local pType = vars.pType - local offRoadForm = vars.offRoadForm or 'default' - local onRoadForm = vars.onRoadForm or 'default' - - if routeProvided == false and #tempRoute > 0 then - local posStart = routines.getLeadPos(gpData) - - - useRoute[#useRoute + 1] = routines.ground.buildWP(posStart, offRoadForm, overRideSpeed) - for i = 1, #tempRoute do - local tempForm = tempRoute[i].action - local tempSpeed = tempRoute[i].speed - - if offRoadForm == 'default' then - tempForm = tempRoute[i].action - end - if onRoadForm == 'default' then - onRoadForm = 'On Road' - end - if (string.lower(tempRoute[i].action) == 'on road' or string.lower(tempRoute[i].action) == 'onroad' or string.lower(tempRoute[i].action) == 'on_road') then - tempForm = onRoadForm - else - tempForm = offRoadForm - end - - if type(overRideSpeed) == 'number' then - tempSpeed = overRideSpeed - end - - - useRoute[#useRoute + 1] = routines.ground.buildWP(tempRoute[i], tempForm, tempSpeed) - end - - if pType and string.lower(pType) == 'doubleback' then - local curRoute = routines.utils.deepCopy(useRoute) - for i = #curRoute, 2, -1 do - useRoute[#useRoute + 1] = routines.ground.buildWP(curRoute[i], curRoute[i].action, curRoute[i].speed) - end - end - - useRoute[1].action = useRoute[#useRoute].action -- make it so the first WP matches the last WP - end - - local cTask3 = {} - local newPatrol = {} - newPatrol.route = useRoute - newPatrol.gpData = gpData:getName() - cTask3[#cTask3 + 1] = 'routines.ground.patrolRoute(' - cTask3[#cTask3 + 1] = routines.utils.oneLineSerialize(newPatrol) - cTask3[#cTask3 + 1] = ')' - cTask3 = table.concat(cTask3) - local tempTask = { - id = 'WrappedAction', - params = { - action = { - id = 'Script', - params = { - command = cTask3, - - }, - }, - }, - } - - - useRoute[#useRoute].task = tempTask - routines.goRoute(gpData, useRoute) - - return -end - -routines.ground.patrol = function(gpData, pType, form, speed) - local vars = {} - - if type(gpData) == 'table' and gpData:getName() then - gpData = gpData:getName() - end - - vars.useGroupRoute = gpData - vars.gpData = gpData - vars.pType = pType - vars.offRoadForm = form - vars.speed = speed - - routines.ground.patrolRoute(vars) - - return -end - -function routines.GetUnitHeight( CheckUnit ) ---trace.f( "routines" ) - - local UnitPoint = CheckUnit:getPoint() - local UnitPosition = { x = UnitPoint.x, y = UnitPoint.z } - local UnitHeight = UnitPoint.y - - local LandHeight = land.getHeight( UnitPosition ) - - --env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) - - --trace.f( "routines", "Unit Height = " .. UnitHeight - LandHeight ) - - return UnitHeight - LandHeight - -end - - - -Su34Status = { status = {} } -boardMsgRed = { statusMsg = "" } -boardMsgAll = { timeMsg = "" } -SpawnSettings = {} -Su34MenuPath = {} -Su34Menus = 0 - - -function Su34AttackCarlVinson(groupName) ---trace.menu("", "Su34AttackCarlVinson") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34.getController(groupSu34) - local groupCarlVinson = Group.getByName("US Carl Vinson #001") - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - if groupCarlVinson ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupCarlVinson:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true}}) - end - Su34Status.status[groupName] = 1 - MessageToRed( string.format('%s: ',groupName) .. 'Attacking carrier Carl Vinson. ', 10, 'RedStatus' .. groupName ) -end - -function Su34AttackWest(groupName) ---trace.f("","Su34AttackWest") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34.getController(groupSu34) - local groupShipWest1 = Group.getByName("US Ship West #001") - local groupShipWest2 = Group.getByName("US Ship West #002") - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - if groupShipWest1 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipWest1:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true}}) - end - if groupShipWest2 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipWest2:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = true}}) - end - Su34Status.status[groupName] = 2 - MessageToRed( string.format('%s: ',groupName) .. 'Attacking invading ships in the west. ', 10, 'RedStatus' .. groupName ) -end - -function Su34AttackNorth(groupName) ---trace.menu("","Su34AttackNorth") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34.getController(groupSu34) - local groupShipNorth1 = Group.getByName("US Ship North #001") - local groupShipNorth2 = Group.getByName("US Ship North #002") - local groupShipNorth3 = Group.getByName("US Ship North #003") - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - if groupShipNorth1 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipNorth1:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false}}) - end - if groupShipNorth2 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipNorth2:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false}}) - end - if groupShipNorth3 ~= nil then - controllerSu34.pushTask(controllerSu34,{id = 'AttackGroup', params = { groupId = groupShipNorth3:getID(), expend = AI.Task.WeaponExpend.ALL, attackQtyLimit = false}}) - end - Su34Status.status[groupName] = 3 - MessageToRed( string.format('%s: ',groupName) .. 'Attacking invading ships in the north. ', 10, 'RedStatus' .. groupName ) -end - -function Su34Orbit(groupName) ---trace.menu("","Su34Orbit") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34:getController() - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - controllerSu34:pushTask( {id = 'ControlledTask', params = { task = { id = 'Orbit', params = { pattern = AI.Task.OrbitPattern.RACE_TRACK } }, stopCondition = { duration = 600 } } } ) - Su34Status.status[groupName] = 4 - MessageToRed( string.format('%s: ',groupName) .. 'In orbit and awaiting further instructions. ', 10, 'RedStatus' .. groupName ) -end - -function Su34TakeOff(groupName) ---trace.menu("","Su34TakeOff") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34:getController() - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) - Su34Status.status[groupName] = 8 - MessageToRed( string.format('%s: ',groupName) .. 'Take-Off. ', 10, 'RedStatus' .. groupName ) -end - -function Su34Hold(groupName) ---trace.menu("","Su34Hold") - local groupSu34 = Group.getByName( groupName ) - local controllerSu34 = groupSu34:getController() - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - controllerSu34.setOption( controllerSu34, AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) - Su34Status.status[groupName] = 5 - MessageToRed( string.format('%s: ',groupName) .. 'Holding Weapons. ', 10, 'RedStatus' .. groupName ) -end - -function Su34RTB(groupName) ---trace.menu("","Su34RTB") - Su34Status.status[groupName] = 6 - MessageToRed( string.format('%s: ',groupName) .. 'Return to Krasnodar. ', 10, 'RedStatus' .. groupName ) -end - -function Su34Destroyed(groupName) ---trace.menu("","Su34Destroyed") - Su34Status.status[groupName] = 7 - MessageToRed( string.format('%s: ',groupName) .. 'Destroyed. ', 30, 'RedStatus' .. groupName ) -end - -function GroupAlive( groupName ) ---trace.menu("","GroupAlive") - local groupTest = Group.getByName( groupName ) - - local groupExists = false - - if groupTest then - groupExists = groupTest:isExist() - end - - --trace.r( "", "", { groupExists } ) - return groupExists -end - -function Su34IsDead() ---trace.f() - -end - -function Su34OverviewStatus() ---trace.menu("","Su34OverviewStatus") - local msg = "" - local currentStatus = 0 - local Exists = false - - for groupName, currentStatus in pairs(Su34Status.status) do - - env.info(('Su34 Overview Status: GroupName = ' .. groupName )) - Alive = GroupAlive( groupName ) - - if Alive then - if currentStatus == 1 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Attacking carrier Carl Vinson. " - elseif currentStatus == 2 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Attacking supporting ships in the west. " - elseif currentStatus == 3 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Attacking invading ships in the north. " - elseif currentStatus == 4 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "In orbit and awaiting further instructions. " - elseif currentStatus == 5 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Holding Weapons. " - elseif currentStatus == 6 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Return to Krasnodar. " - elseif currentStatus == 7 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Destroyed. " - elseif currentStatus == 8 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Take-Off. " - end - else - if currentStatus == 7 then - msg = msg .. string.format("%s: ",groupName) - msg = msg .. "Destroyed. " - else - Su34Destroyed(groupName) - end - end - end - - boardMsgRed.statusMsg = msg -end - - -function UpdateBoardMsg() ---trace.f() - Su34OverviewStatus() - MessageToRed( boardMsgRed.statusMsg, 15, 'RedStatus' ) -end - -function MusicReset( flg ) ---trace.f() - trigger.action.setUserFlag(95,flg) -end - -function PlaneActivate(groupNameFormat, flg) ---trace.f() - local groupName = groupNameFormat .. string.format("#%03d", trigger.misc.getUserFlag(flg)) - --trigger.action.outText(groupName,10) - trigger.action.activateGroup(Group.getByName(groupName)) -end - -function Su34Menu(groupName) ---trace.f() - - --env.info(( 'Su34Menu(' .. groupName .. ')' )) - local groupSu34 = Group.getByName( groupName ) - - if Su34Status.status[groupName] == 1 or - Su34Status.status[groupName] == 2 or - Su34Status.status[groupName] == 3 or - Su34Status.status[groupName] == 4 or - Su34Status.status[groupName] == 5 then - if Su34MenuPath[groupName] == nil then - if planeMenuPath == nil then - planeMenuPath = missionCommands.addSubMenuForCoalition( - coalition.side.RED, - "SU-34 anti-ship flights", - nil - ) - end - Su34MenuPath[groupName] = missionCommands.addSubMenuForCoalition( - coalition.side.RED, - "Flight " .. groupName, - planeMenuPath - ) - - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Attack carrier Carl Vinson", - Su34MenuPath[groupName], - Su34AttackCarlVinson, - groupName - ) - - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Attack ships in the west", - Su34MenuPath[groupName], - Su34AttackWest, - groupName - ) - - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Attack ships in the north", - Su34MenuPath[groupName], - Su34AttackNorth, - groupName - ) - - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Hold position and await instructions", - Su34MenuPath[groupName], - Su34Orbit, - groupName - ) - - missionCommands.addCommandForCoalition( - coalition.side.RED, - "Report status", - Su34MenuPath[groupName], - Su34OverviewStatus - ) - end - else - if Su34MenuPath[groupName] then - missionCommands.removeItemForCoalition(coalition.side.RED, Su34MenuPath[groupName]) - end - end -end - ---- Obsolete function, but kept to rework in framework. - -function ChooseInfantry ( TeleportPrefixTable, TeleportMax ) ---trace.f("Spawn") - --env.info(( 'ChooseInfantry: ' )) - - TeleportPrefixTableCount = #TeleportPrefixTable - TeleportPrefixTableIndex = math.random( 1, TeleportPrefixTableCount ) - - --env.info(( 'ChooseInfantry: TeleportPrefixTableIndex = ' .. TeleportPrefixTableIndex .. ' TeleportPrefixTableCount = ' .. TeleportPrefixTableCount .. ' TeleportMax = ' .. TeleportMax )) - - local TeleportFound = false - local TeleportLoop = true - local Index = TeleportPrefixTableIndex - local TeleportPrefix = '' - - while TeleportLoop do - TeleportPrefix = TeleportPrefixTable[Index] - if SpawnSettings[TeleportPrefix] then - if SpawnSettings[TeleportPrefix]['SpawnCount'] - 1 < TeleportMax then - SpawnSettings[TeleportPrefix]['SpawnCount'] = SpawnSettings[TeleportPrefix]['SpawnCount'] + 1 - TeleportFound = true - else - TeleportFound = false - end - else - SpawnSettings[TeleportPrefix] = {} - SpawnSettings[TeleportPrefix]['SpawnCount'] = 0 - TeleportFound = true - end - if TeleportFound then - TeleportLoop = false - else - if Index < TeleportPrefixTableCount then - Index = Index + 1 - else - TeleportLoop = false - end - end - --env.info(( 'ChooseInfantry: Loop 1 - TeleportPrefix = ' .. TeleportPrefix .. ' Index = ' .. Index )) - end - - if TeleportFound == false then - TeleportLoop = true - Index = 1 - while TeleportLoop do - TeleportPrefix = TeleportPrefixTable[Index] - if SpawnSettings[TeleportPrefix] then - if SpawnSettings[TeleportPrefix]['SpawnCount'] - 1 < TeleportMax then - SpawnSettings[TeleportPrefix]['SpawnCount'] = SpawnSettings[TeleportPrefix]['SpawnCount'] + 1 - TeleportFound = true - else - TeleportFound = false - end - else - SpawnSettings[TeleportPrefix] = {} - SpawnSettings[TeleportPrefix]['SpawnCount'] = 0 - TeleportFound = true - end - if TeleportFound then - TeleportLoop = false - else - if Index < TeleportPrefixTableIndex then - Index = Index + 1 - else - TeleportLoop = false - end - end - --env.info(( 'ChooseInfantry: Loop 2 - TeleportPrefix = ' .. TeleportPrefix .. ' Index = ' .. Index )) - end - end - - local TeleportGroupName = '' - if TeleportFound == true then - TeleportGroupName = TeleportPrefix .. string.format("#%03d", SpawnSettings[TeleportPrefix]['SpawnCount'] ) - else - TeleportGroupName = '' - end - - --env.info(('ChooseInfantry: TeleportGroupName = ' .. TeleportGroupName )) - --env.info(('ChooseInfantry: return')) - - return TeleportGroupName -end - -SpawnedInfantry = 0 - -function LandCarrier ( CarrierGroup, LandingZonePrefix ) ---trace.f() - --env.info(( 'LandCarrier: ' )) - --env.info(( 'LandCarrier: CarrierGroup = ' .. CarrierGroup:getName() )) - --env.info(( 'LandCarrier: LandingZone = ' .. LandingZonePrefix )) - - local controllerGroup = CarrierGroup:getController() - - local LandingZone = trigger.misc.getZone(LandingZonePrefix) - local LandingZonePos = {} - LandingZonePos.x = LandingZone.point.x + math.random(LandingZone.radius * -1, LandingZone.radius) - LandingZonePos.y = LandingZone.point.z + math.random(LandingZone.radius * -1, LandingZone.radius) - - controllerGroup:pushTask( { id = 'Land', params = { point = LandingZonePos, durationFlag = true, duration = 10 } } ) - - --env.info(( 'LandCarrier: end' )) -end - -EscortCount = 0 -function EscortCarrier ( CarrierGroup, EscortPrefix, EscortLastWayPoint, EscortEngagementDistanceMax, EscortTargetTypes ) ---trace.f() - --env.info(( 'EscortCarrier: ' )) - --env.info(( 'EscortCarrier: CarrierGroup = ' .. CarrierGroup:getName() )) - --env.info(( 'EscortCarrier: EscortPrefix = ' .. EscortPrefix )) - - local CarrierName = CarrierGroup:getName() - - local EscortMission = {} - local CarrierMission = {} - - local EscortMission = SpawnMissionGroup( EscortPrefix ) - local CarrierMission = SpawnMissionGroup( CarrierGroup:getName() ) - - if EscortMission ~= nil and CarrierMission ~= nil then - - EscortCount = EscortCount + 1 - EscortMissionName = string.format( EscortPrefix .. '#Escort %s', CarrierName ) - EscortMission.name = EscortMissionName - EscortMission.groupId = nil - EscortMission.lateActivation = false - EscortMission.taskSelected = false - - local EscortUnits = #EscortMission.units - for u = 1, EscortUnits do - EscortMission.units[u].name = string.format( EscortPrefix .. '#Escort %s %02d', CarrierName, u ) - EscortMission.units[u].unitId = nil - end - - - EscortMission.route.points[1].task = { id = "ComboTask", - params = - { - tasks = - { - [1] = - { - enabled = true, - auto = false, - id = "Escort", - number = 1, - params = - { - lastWptIndexFlagChangedManually = false, - groupId = CarrierGroup:getID(), - lastWptIndex = nil, - lastWptIndexFlag = false, - engagementDistMax = EscortEngagementDistanceMax, - targetTypes = EscortTargetTypes, - pos = - { - y = 20, - x = 20, - z = 0, - } -- end of ["pos"] - } -- end of ["params"] - } -- end of [1] - } -- end of ["tasks"] - } -- end of ["params"] - } -- end of ["task"] - - SpawnGroupAdd( EscortPrefix, EscortMission ) - - end -end - -function SendMessageToCarrier( CarrierGroup, CarrierMessage ) ---trace.f() - - if CarrierGroup ~= nil then - MessageToGroup( CarrierGroup, CarrierMessage, 30, 'Carrier/' .. CarrierGroup:getName() ) - end - -end - -function MessageToGroup( MsgGroup, MsgText, MsgTime, MsgName ) ---trace.f() - - if type(MsgGroup) == 'string' then - --env.info( 'MessageToGroup: Converted MsgGroup string "' .. MsgGroup .. '" into a Group structure.' ) - MsgGroup = Group.getByName( MsgGroup ) - end - - if MsgGroup ~= nil then - local MsgTable = {} - MsgTable.text = MsgText - MsgTable.displayTime = MsgTime - MsgTable.msgFor = { units = { MsgGroup:getUnits()[1]:getName() } } - MsgTable.name = MsgName - --routines.message.add( MsgTable ) - --env.info(('MessageToGroup: Message sent to ' .. MsgGroup:getUnits()[1]:getName() .. ' -> ' .. MsgText )) - end -end - -function MessageToUnit( UnitName, MsgText, MsgTime, MsgName ) ---trace.f() - - if UnitName ~= nil then - local MsgTable = {} - MsgTable.text = MsgText - MsgTable.displayTime = MsgTime - MsgTable.msgFor = { units = { UnitName } } - MsgTable.name = MsgName - --routines.message.add( MsgTable ) - end -end - -function MessageToAll( MsgText, MsgTime, MsgName ) ---trace.f() - - MESSAGE:New( MsgText, MsgTime, "Message" ):ToCoalition( coalition.side.RED ):ToCoalition( coalition.side.BLUE ) -end - -function MessageToRed( MsgText, MsgTime, MsgName ) ---trace.f() - - MESSAGE:New( MsgText, MsgTime, "To Red Coalition" ):ToCoalition( coalition.side.RED ) -end - -function MessageToBlue( MsgText, MsgTime, MsgName ) ---trace.f() - - MESSAGE:New( MsgText, MsgTime, "To Blue Coalition" ):ToCoalition( coalition.side.RED ) -end - -function getCarrierHeight( CarrierGroup ) ---trace.f() - - if CarrierGroup ~= nil then - if table.getn(CarrierGroup:getUnits()) == 1 then - local CarrierUnit = CarrierGroup:getUnits()[1] - local CurrentPoint = CarrierUnit:getPoint() - - local CurrentPosition = { x = CurrentPoint.x, y = CurrentPoint.z } - local CarrierHeight = CurrentPoint.y - - local LandHeight = land.getHeight( CurrentPosition ) - - --env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) - - return CarrierHeight - LandHeight - else - return 999999 - end - else - return 999999 - end - -end - -function GetUnitHeight( CheckUnit ) ---trace.f() - - local UnitPoint = CheckUnit:getPoint() - local UnitPosition = { x = CurrentPoint.x, y = CurrentPoint.z } - local UnitHeight = CurrentPoint.y - - local LandHeight = land.getHeight( CurrentPosition ) - - --env.info(( 'CarrierHeight: LandHeight = ' .. LandHeight .. ' CarrierHeight = ' .. CarrierHeight )) - - return UnitHeight - LandHeight - -end - - -_MusicTable = {} -_MusicTable.Files = {} -_MusicTable.Queue = {} -_MusicTable.FileCnt = 0 - - -function MusicRegister( SndRef, SndFile, SndTime ) ---trace.f() - - env.info(( 'MusicRegister: SndRef = ' .. SndRef )) - env.info(( 'MusicRegister: SndFile = ' .. SndFile )) - env.info(( 'MusicRegister: SndTime = ' .. SndTime )) - - - _MusicTable.FileCnt = _MusicTable.FileCnt + 1 - - _MusicTable.Files[_MusicTable.FileCnt] = {} - _MusicTable.Files[_MusicTable.FileCnt].Ref = SndRef - _MusicTable.Files[_MusicTable.FileCnt].File = SndFile - _MusicTable.Files[_MusicTable.FileCnt].Time = SndTime - - if not _MusicTable.Function then - _MusicTable.Function = routines.scheduleFunction( MusicScheduler, { }, timer.getTime() + 10, 10) - end - -end - -function MusicToPlayer( SndRef, PlayerName, SndContinue ) ---trace.f() - - --env.info(( 'MusicToPlayer: SndRef = ' .. SndRef )) - - local PlayerUnits = AlivePlayerUnits() - for PlayerUnitIdx, PlayerUnit in pairs(PlayerUnits) do - local PlayerUnitName = PlayerUnit:getPlayerName() - --env.info(( 'MusicToPlayer: PlayerUnitName = ' .. PlayerUnitName )) - if PlayerName == PlayerUnitName then - PlayerGroup = PlayerUnit:getGroup() - if PlayerGroup then - --env.info(( 'MusicToPlayer: PlayerGroup = ' .. PlayerGroup:getName() )) - MusicToGroup( SndRef, PlayerGroup, SndContinue ) - end - break - end - end - - --env.info(( 'MusicToPlayer: end' )) - -end - -function MusicToGroup( SndRef, SndGroup, SndContinue ) ---trace.f() - - --env.info(( 'MusicToGroup: SndRef = ' .. SndRef )) - - if SndGroup ~= nil then - if _MusicTable and _MusicTable.FileCnt > 0 then - if SndGroup:isExist() then - if MusicCanStart(SndGroup:getUnit(1):getPlayerName()) then - --env.info(( 'MusicToGroup: OK for Sound.' )) - local SndIdx = 0 - if SndRef == '' then - --env.info(( 'MusicToGroup: SndRef as empty. Queueing at random.' )) - SndIdx = math.random( 1, _MusicTable.FileCnt ) - else - for SndIdx = 1, _MusicTable.FileCnt do - if _MusicTable.Files[SndIdx].Ref == SndRef then - break - end - end - end - --env.info(( 'MusicToGroup: SndIdx = ' .. SndIdx )) - --env.info(( 'MusicToGroup: Queueing Music ' .. _MusicTable.Files[SndIdx].File .. ' for Group ' .. SndGroup:getID() )) - trigger.action.outSoundForGroup( SndGroup:getID(), _MusicTable.Files[SndIdx].File ) - MessageToGroup( SndGroup, 'Playing ' .. _MusicTable.Files[SndIdx].File, 15, 'Music-' .. SndGroup:getUnit(1):getPlayerName() ) - - local SndQueueRef = SndGroup:getUnit(1):getPlayerName() - if _MusicTable.Queue[SndQueueRef] == nil then - _MusicTable.Queue[SndQueueRef] = {} - end - _MusicTable.Queue[SndQueueRef].Start = timer.getTime() - _MusicTable.Queue[SndQueueRef].PlayerName = SndGroup:getUnit(1):getPlayerName() - _MusicTable.Queue[SndQueueRef].Group = SndGroup - _MusicTable.Queue[SndQueueRef].ID = SndGroup:getID() - _MusicTable.Queue[SndQueueRef].Ref = SndIdx - _MusicTable.Queue[SndQueueRef].Continue = SndContinue - _MusicTable.Queue[SndQueueRef].Type = Group - end - end - end - end -end - -function MusicCanStart(PlayerName) ---trace.f() - - --env.info(( 'MusicCanStart:' )) - - local MusicOut = false - - if _MusicTable['Queue'] ~= nil and _MusicTable.FileCnt > 0 then - --env.info(( 'MusicCanStart: PlayerName = ' .. PlayerName )) - local PlayerFound = false - local MusicStart = 0 - local MusicTime = 0 - for SndQueueIdx, SndQueue in pairs( _MusicTable.Queue ) do - if SndQueue.PlayerName == PlayerName then - PlayerFound = true - MusicStart = SndQueue.Start - MusicTime = _MusicTable.Files[SndQueue.Ref].Time - break - end - end - if PlayerFound then - --env.info(( 'MusicCanStart: MusicStart = ' .. MusicStart )) - --env.info(( 'MusicCanStart: MusicTime = ' .. MusicTime )) - --env.info(( 'MusicCanStart: timer.getTime() = ' .. timer.getTime() )) - - if MusicStart + MusicTime <= timer.getTime() then - MusicOut = true - end - else - MusicOut = true - end - end - - if MusicOut then - --env.info(( 'MusicCanStart: true' )) - else - --env.info(( 'MusicCanStart: false' )) - end - - return MusicOut -end - -function MusicScheduler() ---trace.scheduled("", "MusicScheduler") - - --env.info(( 'MusicScheduler:' )) - if _MusicTable['Queue'] ~= nil and _MusicTable.FileCnt > 0 then - --env.info(( 'MusicScheduler: Walking Sound Queue.')) - for SndQueueIdx, SndQueue in pairs( _MusicTable.Queue ) do - if SndQueue.Continue then - if MusicCanStart(SndQueue.PlayerName) then - --env.info(('MusicScheduler: MusicToGroup')) - MusicToPlayer( '', SndQueue.PlayerName, true ) - end - end - end - end - -end - - -env.info(( 'Init: Scripts Loaded v1.1' )) - ---- This module contains derived utilities taken from the MIST framework, --- which are excellent tools to be reused in an OO environment!. --- --- ### Authors: --- --- * Grimes : Design & Programming of the MIST framework. --- --- ### Contributions: --- --- * FlightControl : Rework to OO framework --- --- @module Utils - - ---- @type SMOKECOLOR --- @field Green --- @field Red --- @field White --- @field Orange --- @field Blue - -SMOKECOLOR = trigger.smokeColor -- #SMOKECOLOR - ---- @type FLARECOLOR --- @field Green --- @field Red --- @field White --- @field Yellow - -FLARECOLOR = trigger.flareColor -- #FLARECOLOR - ---- Utilities static class. --- @type UTILS -UTILS = {} - - ---from http://lua-users.org/wiki/CopyTable -UTILS.DeepCopy = function(object) - local lookup_table = {} - local function _copy(object) - if type(object) ~= "table" then - return object - elseif lookup_table[object] then - return lookup_table[object] - end - local new_table = {} - lookup_table[object] = new_table - for index, value in pairs(object) do - new_table[_copy(index)] = _copy(value) - end - return setmetatable(new_table, getmetatable(object)) - end - local objectreturn = _copy(object) - return objectreturn -end - - --- porting in Slmod's serialize_slmod2 -UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function - - lookup_table = {} - - local function _Serialize( tbl ) - - if type(tbl) == 'table' then --function only works for tables! - - if lookup_table[tbl] then - return lookup_table[object] - end - - local tbl_str = {} - - lookup_table[tbl] = tbl_str - - tbl_str[#tbl_str + 1] = '{' - - for ind,val in pairs(tbl) do -- serialize its fields - local ind_str = {} - if type(ind) == "number" then - ind_str[#ind_str + 1] = '[' - ind_str[#ind_str + 1] = tostring(ind) - ind_str[#ind_str + 1] = ']=' - else --must be a string - ind_str[#ind_str + 1] = '[' - ind_str[#ind_str + 1] = routines.utils.basicSerialize(ind) - ind_str[#ind_str + 1] = ']=' - end - - local val_str = {} - if ((type(val) == 'number') or (type(val) == 'boolean')) then - val_str[#val_str + 1] = tostring(val) - val_str[#val_str + 1] = ',' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'string' then - val_str[#val_str + 1] = routines.utils.basicSerialize(val) - val_str[#val_str + 1] = ',' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'nil' then -- won't ever happen, right? - val_str[#val_str + 1] = 'nil,' - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - elseif type(val) == 'table' then - if ind == "__index" then - -- tbl_str[#tbl_str + 1] = "__index" - -- tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - else - - val_str[#val_str + 1] = _Serialize(val) - val_str[#val_str + 1] = ',' --I think this is right, I just added it - tbl_str[#tbl_str + 1] = table.concat(ind_str) - tbl_str[#tbl_str + 1] = table.concat(val_str) - end - elseif type(val) == 'function' then - tbl_str[#tbl_str + 1] = "f() " .. tostring(ind) - tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it - else - env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) - env.info( debug.traceback() ) - end - - end - tbl_str[#tbl_str + 1] = '}' - return table.concat(tbl_str) - else - return tostring(tbl) - end - end - - local objectreturn = _Serialize(tbl) - return objectreturn -end - ---porting in Slmod's "safestring" basic serialize -UTILS.BasicSerialize = function(s) - if s == nil then - return "\"\"" - else - if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then - return tostring(s) - elseif type(s) == 'string' then - s = string.format('%q', s) - return s - end - end -end - - -UTILS.ToDegree = function(angle) - return angle*180/math.pi -end - -UTILS.ToRadian = function(angle) - return angle*math.pi/180 -end - -UTILS.MetersToNM = function(meters) - return meters/1852 -end - -UTILS.MetersToFeet = function(meters) - return meters/0.3048 -end - -UTILS.NMToMeters = function(NM) - return NM*1852 -end - -UTILS.FeetToMeters = function(feet) - return feet*0.3048 -end - -UTILS.MpsToKnots = function(mps) - return mps*3600/1852 -end - -UTILS.MpsToKmph = function(mps) - return mps*3.6 -end - -UTILS.KnotsToMps = function(knots) - return knots*1852/3600 -end - -UTILS.KmphToMps = function(kmph) - return kmph/3.6 -end - ---[[acc: -in DM: decimal point of minutes. -In DMS: decimal point of seconds. -position after the decimal of the least significant digit: -So: -42.32 - acc of 2. -]] -UTILS.tostringLL = function( lat, lon, acc, DMS) - - local latHemi, lonHemi - if lat > 0 then - latHemi = 'N' - else - latHemi = 'S' - end - - if lon > 0 then - lonHemi = 'E' - else - lonHemi = 'W' - end - - lat = math.abs(lat) - lon = math.abs(lon) - - local latDeg = math.floor(lat) - local latMin = (lat - latDeg)*60 - - local lonDeg = math.floor(lon) - local lonMin = (lon - lonDeg)*60 - - if DMS then -- degrees, minutes, and seconds. - local oldLatMin = latMin - latMin = math.floor(latMin) - local latSec = UTILS.Round((oldLatMin - latMin)*60, acc) - - local oldLonMin = lonMin - lonMin = math.floor(lonMin) - local lonSec = UTILS.Round((oldLonMin - lonMin)*60, acc) - - if latSec == 60 then - latSec = 0 - latMin = latMin + 1 - end - - if lonSec == 60 then - lonSec = 0 - lonMin = lonMin + 1 - end - - local secFrmtStr -- create the formatting string for the seconds place - if acc <= 0 then -- no decimal place. - secFrmtStr = '%02d' - else - local width = 3 + acc -- 01.310 - that's a width of 6, for example. - secFrmtStr = '%0' .. width .. '.' .. acc .. 'f' - end - - return string.format('%02d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' ' - .. string.format('%02d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi - - else -- degrees, decimal minutes. - latMin = UTILS.Round(latMin, acc) - lonMin = UTILS.Round(lonMin, acc) - - if latMin == 60 then - latMin = 0 - latDeg = latDeg + 1 - end - - if lonMin == 60 then - lonMin = 0 - lonDeg = lonDeg + 1 - end - - local minFrmtStr -- create the formatting string for the minutes place - if acc <= 0 then -- no decimal place. - minFrmtStr = '%02d' - else - local width = 3 + acc -- 01.310 - that's a width of 6, for example. - minFrmtStr = '%0' .. width .. '.' .. acc .. 'f' - end - - return string.format('%02d', latDeg) .. ' ' .. string.format(minFrmtStr, latMin) .. '\'' .. latHemi .. ' ' - .. string.format('%02d', lonDeg) .. ' ' .. string.format(minFrmtStr, lonMin) .. '\'' .. lonHemi - - end -end - - ---- From http://lua-users.org/wiki/SimpleRound --- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place -function UTILS.Round( num, idp ) - local mult = 10 ^ ( idp or 0 ) - return math.floor( num * mult + 0.5 ) / mult -end - --- porting in Slmod's dostring -function UTILS.DoString( s ) - local f, err = loadstring( s ) - if f then - return true, f() - else - return false, err - end -end ---- **Core** - BASE forms **the basis of the MOOSE framework**. Each class within the MOOSE framework derives from BASE. --- --- ![Banner Image](..\Presentations\BASE\Dia1.JPG) --- --- === --- --- The @{#BASE} class is the core root class from where every other class in moose is derived. --- --- === --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) --- YYYY-MM-DD: CLASS:**NewFunction( Params )** added --- --- Hereby the change log: --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * None. --- --- ### Authors: --- --- * **FlightControl**: Design & Programming --- --- @module Base - - - -local _TraceOnOff = true -local _TraceLevel = 1 -local _TraceAll = false -local _TraceClass = {} -local _TraceClassMethod = {} - -local _ClassID = 0 - ---- @type BASE --- @field ClassName The name of the class. --- @field ClassID The ID number of the class. --- @field ClassNameAndID The name of the class concatenated with the ID number of the class. - ---- # 1) #BASE class --- --- All classes within the MOOSE framework are derived from the BASE class. --- --- BASE provides facilities for : --- --- * The construction and inheritance of MOOSE classes. --- * The class naming and numbering system. --- * The class hierarchy search system. --- * The tracing of information or objects during mission execution for debuggin purposes. --- * The subscription to DCS events for event handling in MOOSE objects. --- --- Note: The BASE class is an abstract class and is not meant to be used directly. --- --- ## 1.1) BASE constructor --- --- Any class derived from BASE, will use the @{Base#BASE.New} constructor embedded in the @{Base#BASE.Inherit} method. --- See an example at the @{Base#BASE.New} method how this is done. --- --- ## 1.2) Trace information for debugging --- --- The BASE class contains trace methods to trace progress within a mission execution of a certain object. --- These trace methods are inherited by each MOOSE class interiting BASE, soeach object created from derived class from BASE can use the tracing methods to trace its execution. --- --- Any type of information can be passed to these tracing methods. See the following examples: --- --- self:E( "Hello" ) --- --- Result in the word "Hello" in the dcs.log. --- --- local Array = { 1, nil, "h", { "a","b" }, "x" } --- self:E( Array ) --- --- Results with the text [1]=1,[3]="h",[4]={[1]="a",[2]="b"},[5]="x"} in the dcs.log. --- --- local Object1 = "Object1" --- local Object2 = 3 --- local Object3 = { Object 1, Object 2 } --- self:E( { Object1, Object2, Object3 } ) --- --- Results with the text [1]={[1]="Object",[2]=3,[3]={[1]="Object",[2]=3}} in the dcs.log. --- --- local SpawnObject = SPAWN:New( "Plane" ) --- local GroupObject = GROUP:FindByName( "Group" ) --- self:E( { Spawn = SpawnObject, Group = GroupObject } ) --- --- Results with the text [1]={Spawn={....),Group={...}} in the dcs.log. --- --- Below a more detailed explanation of the different method types for tracing. --- --- ### 1.2.1) Tracing methods categories --- --- There are basically 3 types of tracing methods available: --- --- * @{#BASE.F}: Used to trace the entrance of a function and its given parameters. An F is indicated at column 44 in the DCS.log file. --- * @{#BASE.T}: Used to trace further logic within a function giving optional variables or parameters. A T is indicated at column 44 in the DCS.log file. --- * @{#BASE.E}: Used to always trace information giving optional variables or parameters. An E is indicated at column 44 in the DCS.log file. --- --- ### 1.2.2) Tracing levels --- --- There are 3 tracing levels within MOOSE. --- These tracing levels were defined to avoid bulks of tracing to be generated by lots of objects. --- --- As such, the F and T methods have additional variants to trace level 2 and 3 respectively: --- --- * @{#BASE.F2}: Trace the beginning of a function and its given parameters with tracing level 2. --- * @{#BASE.F3}: Trace the beginning of a function and its given parameters with tracing level 3. --- * @{#BASE.T2}: Trace further logic within a function giving optional variables or parameters with tracing level 2. --- * @{#BASE.T3}: Trace further logic within a function giving optional variables or parameters with tracing level 3. --- --- ### 1.2.3) Trace activation. --- --- Tracing can be activated in several ways: --- --- * Switch tracing on or off through the @{#BASE.TraceOnOff}() method. --- * Activate all tracing through the @{#BASE.TraceAll}() method. --- * Activate only the tracing of a certain class (name) through the @{#BASE.TraceClass}() method. --- * Activate only the tracing of a certain method of a certain class through the @{#BASE.TraceClassMethod}() method. --- * Activate only the tracing of a certain level through the @{#BASE.TraceLevel}() method. --- --- ### 1.2.4) Check if tracing is on. --- --- The method @{#BASE.IsTrace}() will validate if tracing is activated or not. --- --- ## 1.3 DCS simulator Event Handling --- --- The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator, --- and handled through lua scripting. MOOSE provides an encapsulation to handle these events more efficiently. --- --- ### 1.3.1 Subscribe / Unsubscribe to DCS Events --- --- At first, the mission designer will need to **Subscribe** to a specific DCS event for the class. --- So, when the DCS event occurs, the class will be notified of that event. --- There are two methods which you use to subscribe to or unsubscribe from an event. --- --- * @{#BASE.HandleEvent}(): Subscribe to a DCS Event. --- * @{#BASE.UnHandleEvent}(): Unsubscribe from a DCS Event. --- --- ### 1.3.2 Event Handling of DCS Events --- --- Once the class is subscribed to the event, an **Event Handling** method on the object or class needs to be written that will be called --- when the DCS event occurs. The Event Handling method receives an @{Event#EVENTDATA} structure, which contains a lot of information --- about the event that occurred. --- --- Find below an example of the prototype how to write an event handling function for two units: --- --- local Tank1 = UNIT:FindByName( "Tank A" ) --- local Tank2 = UNIT:FindByName( "Tank B" ) --- --- -- Here we subscribe to the Dead events. So, if one of these tanks dies, the Tank1 or Tank2 objects will be notified. --- Tank1:HandleEvent( EVENTS.Dead ) --- Tank2:HandleEvent( EVENTS.Dead ) --- --- --- This function is an Event Handling function that will be called when Tank1 is Dead. --- -- @param Wrapper.Unit#UNIT self --- -- @param Core.Event#EVENTDATA EventData --- function Tank1:OnEventDead( EventData ) --- --- self:SmokeGreen() --- end --- --- --- This function is an Event Handling function that will be called when Tank2 is Dead. --- -- @param Wrapper.Unit#UNIT self --- -- @param Core.Event#EVENTDATA EventData --- function Tank2:OnEventDead( EventData ) --- --- self:SmokeBlue() --- end --- --- --- --- See the @{Event} module for more information about event handling. --- --- ## 1.4) Class identification methods --- --- BASE provides methods to get more information of each object: --- --- * @{#BASE.GetClassID}(): Gets the ID (number) of the object. Each object created is assigned a number, that is incremented by one. --- * @{#BASE.GetClassName}(): Gets the name of the object, which is the name of the class the object was instantiated from. --- * @{#BASE.GetClassNameAndID}(): Gets the name and ID of the object. --- --- ## 1.5) All objects derived from BASE can have "States" --- --- A mechanism is in place in MOOSE, that allows to let the objects administer **states**. --- States are essentially properties of objects, which are identified by a **Key** and a **Value**. --- --- The method @{#BASE.SetState}() can be used to set a Value with a reference Key to the object. --- To **read or retrieve** a state Value based on a Key, use the @{#BASE.GetState} method. --- --- These two methods provide a very handy way to keep state at long lasting processes. --- Values can be stored within the objects, and later retrieved or changed when needed. --- There is one other important thing to note, the @{#BASE.SetState}() and @{#BASE.GetState} methods --- receive as the **first parameter the object for which the state needs to be set**. --- Thus, if the state is to be set for the same object as the object for which the method is used, then provide the same --- object name to the method. --- --- ## 1.10) Inheritance --- --- The following methods are available to implement inheritance --- --- * @{#BASE.Inherit}: Inherits from a class. --- * @{#BASE.GetParent}: Returns the parent object from the object it is handling, or nil if there is no parent object. --- --- === --- --- @field #BASE BASE --- -BASE = { - ClassName = "BASE", - ClassID = 0, - _Private = {}, - Events = {}, - States = {} -} - ---- The Formation Class --- @type FORMATION --- @field Cone A cone formation. -FORMATION = { - Cone = "Cone" -} - - - ---- BASE constructor. --- --- This is an example how to use the BASE:New() constructor in a new class definition when inheriting from BASE. --- --- function EVENT:New() --- local self = BASE:Inherit( self, BASE:New() ) -- #EVENT --- return self --- end --- --- @param #BASE self --- @return #BASE -function BASE:New() - local self = routines.utils.deepCopy( self ) -- Create a new self instance - local MetaTable = {} - setmetatable( self, MetaTable ) - self.__index = self - _ClassID = _ClassID + 1 - self.ClassID = _ClassID - - - return self -end - -function BASE:_Destructor() - --self:E("_Destructor") - - --self:EventRemoveAll() -end - - --- THIS IS WHY WE NEED LUA 5.2 ... -function BASE:_SetDestructor() - - -- TODO: Okay, this is really technical... - -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak... - -- Therefore, I am parking this logic until I've properly discussed all this with the community. - - local proxy = newproxy(true) - local proxyMeta = getmetatable(proxy) - - proxyMeta.__gc = function () - env.info("In __gc for " .. self:GetClassNameAndID() ) - if self._Destructor then - self:_Destructor() - end - end - - -- keep the userdata from newproxy reachable until the object - -- table is about to be garbage-collected - then the __gc hook - -- will be invoked and the destructor called - rawset( self, '__proxy', proxy ) - -end - ---- This is the worker method to inherit from a parent class. --- @param #BASE self --- @param Child is the Child class that inherits. --- @param #BASE Parent is the Parent class that the Child inherits from. --- @return #BASE Child -function BASE:Inherit( Child, Parent ) - local Child = routines.utils.deepCopy( Child ) - --local Parent = routines.utils.deepCopy( Parent ) - --local Parent = Parent - if Child ~= nil then - setmetatable( Child, Parent ) - Child.__index = Child - - --Child:_SetDestructor() - end - --self:T( 'Inherited from ' .. Parent.ClassName ) - return Child -end - ---- This is the worker method to retrieve the Parent class. --- Note that the Parent class must be passed to call the parent class method. --- --- self:GetParent(self):ParentMethod() --- --- --- @param #BASE self --- @param #BASE Child is the Child class from which the Parent class needs to be retrieved. --- @return #BASE -function BASE:GetParent( Child ) - local Parent = getmetatable( Child ) --- env.info('Inherited class of ' .. Child.ClassName .. ' is ' .. Parent.ClassName ) - return Parent -end - ---- Get the ClassName + ClassID of the class instance. --- The ClassName + ClassID is formatted as '%s#%09d'. --- @param #BASE self --- @return #string The ClassName + ClassID of the class instance. -function BASE:GetClassNameAndID() - return string.format( '%s#%09d', self.ClassName, self.ClassID ) -end - ---- Get the ClassName of the class instance. --- @param #BASE self --- @return #string The ClassName of the class instance. -function BASE:GetClassName() - return self.ClassName -end - ---- Get the ClassID of the class instance. --- @param #BASE self --- @return #string The ClassID of the class instance. -function BASE:GetClassID() - return self.ClassID -end - -do -- Event Handling - - --- Returns the event dispatcher - -- @param #BASE self - -- @return Core.Event#EVENT - function BASE:EventDispatcher() - - return _EVENTDISPATCHER - end - - - --- Get the Class @{Event} processing Priority. - -- The Event processing Priority is a number from 1 to 10, - -- reflecting the order of the classes subscribed to the Event to be processed. - -- @param #BASE self - -- @return #number The @{Event} processing Priority. - function BASE:GetEventPriority() - return self._Private.EventPriority or 5 - end - - --- Set the Class @{Event} processing Priority. - -- The Event processing Priority is a number from 1 to 10, - -- reflecting the order of the classes subscribed to the Event to be processed. - -- @param #BASE self - -- @param #number EventPriority The @{Event} processing Priority. - -- @return self - function BASE:SetEventPriority( EventPriority ) - self._Private.EventPriority = EventPriority - end - - --- Remove all subscribed events - -- @param #BASE self - -- @return #BASE - function BASE:EventRemoveAll() - - self:EventDispatcher():RemoveAll( self ) - - return self - end - - --- Subscribe to a DCS Event. - -- @param #BASE self - -- @param Core.Event#EVENTS Event - -- @param #function EventFunction (optional) The function to be called when the event occurs for the unit. - -- @return #BASE - function BASE:HandleEvent( Event, EventFunction ) - - self:EventDispatcher():OnEventGeneric( EventFunction, self, Event ) - - return self - end - - --- UnSubscribe to a DCS event. - -- @param #BASE self - -- @param Core.Event#EVENTS Event - -- @return #BASE - function BASE:UnHandleEvent( Event ) - - self:EventDispatcher():Remove( self, Event ) - - return self - end - - -- Event handling function prototypes - - --- Occurs whenever any unit in a mission fires a weapon. But not any machine gun or autocannon based weapon, those are handled by EVENT.ShootingStart. - -- @function [parent=#BASE] OnEventShot - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs whenever an object is hit by a weapon. - -- initiator : The unit object the fired the weapon - -- weapon: Weapon object that hit the target - -- target: The Object that was hit. - -- @function [parent=#BASE] OnEventHit - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when an aircraft takes off from an airbase, farp, or ship. - -- initiator : The unit that tookoff - -- place: Object from where the AI took-off from. Can be an Airbase Object, FARP, or Ships - -- @function [parent=#BASE] OnEventTakeoff - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when an aircraft lands at an airbase, farp or ship - -- initiator : The unit that has landed - -- place: Object that the unit landed on. Can be an Airbase Object, FARP, or Ships - -- @function [parent=#BASE] OnEventLand - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any aircraft crashes into the ground and is completely destroyed. - -- initiator : The unit that has crashed - -- @function [parent=#BASE] OnEventCrash - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when a pilot ejects from an aircraft - -- initiator : The unit that has ejected - -- @function [parent=#BASE] OnEventEjection - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when an aircraft connects with a tanker and begins taking on fuel. - -- initiator : The unit that is receiving fuel. - -- @function [parent=#BASE] OnEventRefueling - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when an object is completely destroyed. - -- initiator : The unit that is was destroyed. - -- @function [parent=#BASE] OnEvent - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when the pilot of an aircraft is killed. Can occur either if the player is alive and crashes or if a weapon kills the pilot without completely destroying the plane. - -- initiator : The unit that the pilot has died in. - -- @function [parent=#BASE] OnEventPilotDead - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when a ground unit captures either an airbase or a farp. - -- initiator : The unit that captured the base - -- place: The airbase that was captured, can be a FARP or Airbase. When calling place:getCoalition() the faction will already be the new owning faction. - -- @function [parent=#BASE] OnEventBaseCaptured - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when a mission starts - -- @function [parent=#BASE] OnEventMissionStart - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when a mission ends - -- @function [parent=#BASE] OnEventMissionEnd - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when an aircraft is finished taking fuel. - -- initiator : The unit that was receiving fuel. - -- @function [parent=#BASE] OnEventRefuelingStop - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any object is spawned into the mission. - -- initiator : The unit that was spawned - -- @function [parent=#BASE] OnEventBirth - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any system fails on a human controlled aircraft. - -- initiator : The unit that had the failure - -- @function [parent=#BASE] OnEventHumanFailure - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any aircraft starts its engines. - -- initiator : The unit that is starting its engines. - -- @function [parent=#BASE] OnEventEngineStartup - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any aircraft shuts down its engines. - -- initiator : The unit that is stopping its engines. - -- @function [parent=#BASE] OnEventEngineShutdown - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any player assumes direct control of a unit. - -- initiator : The unit that is being taken control of. - -- @function [parent=#BASE] OnEventPlayerEnterUnit - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any player relieves control of a unit to the AI. - -- initiator : The unit that the player left. - -- @function [parent=#BASE] OnEventPlayerLeaveUnit - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any unit begins firing a weapon that has a high rate of fire. Most common with aircraft cannons (GAU-8), autocannons, and machine guns. - -- initiator : The unit that is doing the shooing. - -- target: The unit that is being targeted. - -- @function [parent=#BASE] OnEventShootingStart - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - - --- Occurs when any unit stops firing its weapon. Event will always correspond with a shooting start event. - -- initiator : The unit that was doing the shooing. - -- @function [parent=#BASE] OnEventShootingEnd - -- @param #BASE self - -- @param Core.Event#EVENTDATA EventData The EventData structure. - -end - - ---- Creation of a Birth Event. --- @param #BASE self --- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. --- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event. --- @param #string IniUnitName The initiating unit name. --- @param place --- @param subplace -function BASE:CreateEventBirth( EventTime, Initiator, IniUnitName, place, subplace ) - self:F( { EventTime, Initiator, IniUnitName, place, subplace } ) - - local Event = { - id = world.event.S_EVENT_BIRTH, - time = EventTime, - initiator = Initiator, - IniUnitName = IniUnitName, - place = place, - subplace = subplace - } - - world.onEvent( Event ) -end - ---- Creation of a Crash Event. --- @param #BASE self --- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. --- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event. -function BASE:CreateEventCrash( EventTime, Initiator ) - self:F( { EventTime, Initiator } ) - - local Event = { - id = world.event.S_EVENT_CRASH, - time = EventTime, - initiator = Initiator, - } - - world.onEvent( Event ) -end - --- TODO: Complete Dcs.DCSTypes#Event structure. ---- The main event handling function... This function captures all events generated for the class. --- @param #BASE self --- @param Dcs.DCSTypes#Event event -function BASE:onEvent(event) - --self:F( { BaseEventCodes[event.id], event } ) - - if self then - for EventID, EventObject in pairs( self.Events ) do - if EventObject.EventEnabled then - --env.info( 'onEvent Table EventObject.Self = ' .. tostring(EventObject.Self) ) - --env.info( 'onEvent event.id = ' .. tostring(event.id) ) - --env.info( 'onEvent EventObject.Event = ' .. tostring(EventObject.Event) ) - if event.id == EventObject.Event then - if self == EventObject.Self then - if event.initiator and event.initiator:isExist() then - event.IniUnitName = event.initiator:getName() - end - if event.target and event.target:isExist() then - event.TgtUnitName = event.target:getName() - end - --self:T( { BaseEventCodes[event.id], event } ) - --EventObject.EventFunction( self, event ) - end - end - end - end - end -end - ---- Set a state or property of the Object given a Key and a Value. --- Note that if the Object is destroyed, nillified or garbage collected, then the Values and Keys will also be gone. --- @param #BASE self --- @param Object The object that will hold the Value set by the Key. --- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type! --- @param Value The value to is stored in the object. --- @return The Value set. --- @return #nil The Key was not found and thus the Value could not be retrieved. -function BASE:SetState( Object, Key, Value ) - - local ClassNameAndID = Object:GetClassNameAndID() - - self.States[ClassNameAndID] = self.States[ClassNameAndID] or {} - self.States[ClassNameAndID][Key] = Value - self:T2( { ClassNameAndID, Key, Value } ) - - return self.States[ClassNameAndID][Key] -end - - ---- Get a Value given a Key from the Object. --- Note that if the Object is destroyed, nillified or garbage collected, then the Values and Keys will also be gone. --- @param #BASE self --- @param Object The object that holds the Value set by the Key. --- @param Key The key that is used to retrieve the value. Note that the key can be a #string, but it can also be any other type! --- @param Value The value to is stored in the Object. --- @return The Value retrieved. -function BASE:GetState( Object, Key ) - - local ClassNameAndID = Object:GetClassNameAndID() - - if self.States[ClassNameAndID] then - local Value = self.States[ClassNameAndID][Key] or false - self:T2( { ClassNameAndID, Key, Value } ) - return Value - end - - return nil -end - -function BASE:ClearState( Object, StateName ) - - local ClassNameAndID = Object:GetClassNameAndID() - if self.States[ClassNameAndID] then - self.States[ClassNameAndID][StateName] = nil - end -end - --- Trace section - --- Log a trace (only shown when trace is on) --- TODO: Make trace function using variable parameters. - ---- Set trace on or off --- Note that when trace is off, no debug statement is performed, increasing performance! --- When Moose is loaded statically, (as one file), tracing is switched off by default. --- So tracing must be switched on manually in your mission if you are using Moose statically. --- When moose is loading dynamically (for moose class development), tracing is switched on by default. --- @param #BASE self --- @param #boolean TraceOnOff Switch the tracing on or off. --- @usage --- -- Switch the tracing On --- BASE:TraceOnOff( true ) --- --- -- Switch the tracing Off --- BASE:TraceOnOff( false ) -function BASE:TraceOnOff( TraceOnOff ) - _TraceOnOff = TraceOnOff -end - - ---- Enquires if tracing is on (for the class). --- @param #BASE self --- @return #boolean -function BASE:IsTrace() - - if debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then - return true - else - return false - end -end - ---- Set trace level --- @param #BASE self --- @param #number Level -function BASE:TraceLevel( Level ) - _TraceLevel = Level - self:E( "Tracing level " .. Level ) -end - ---- Trace all methods in MOOSE --- @param #BASE self --- @param #boolean TraceAll true = trace all methods in MOOSE. -function BASE:TraceAll( TraceAll ) - - _TraceAll = TraceAll - - if _TraceAll then - self:E( "Tracing all methods in MOOSE " ) - else - self:E( "Switched off tracing all methods in MOOSE" ) - end -end - ---- Set tracing for a class --- @param #BASE self --- @param #string Class -function BASE:TraceClass( Class ) - _TraceClass[Class] = true - _TraceClassMethod[Class] = {} - self:E( "Tracing class " .. Class ) -end - ---- Set tracing for a specific method of class --- @param #BASE self --- @param #string Class --- @param #string Method -function BASE:TraceClassMethod( Class, Method ) - if not _TraceClassMethod[Class] then - _TraceClassMethod[Class] = {} - _TraceClassMethod[Class].Method = {} - end - _TraceClassMethod[Class].Method[Method] = true - self:E( "Tracing method " .. Method .. " of class " .. Class ) -end - ---- Trace a function call. This function is private. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam ) - - if debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then - - local DebugInfoCurrent = DebugInfoCurrentParam and DebugInfoCurrentParam or debug.getinfo( 2, "nl" ) - local DebugInfoFrom = DebugInfoFromParam and DebugInfoFromParam or debug.getinfo( 3, "l" ) - - local Function = "function" - if DebugInfoCurrent.name then - Function = DebugInfoCurrent.name - end - - if _TraceAll == true or _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName].Method[Function] then - local LineCurrent = 0 - if DebugInfoCurrent.currentline then - LineCurrent = DebugInfoCurrent.currentline - end - local LineFrom = 0 - if DebugInfoFrom then - LineFrom = DebugInfoFrom.currentline - end - env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) ) - end - end -end - ---- Trace a function call. Must be at the beginning of the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:F( Arguments ) - - if debug and _TraceOnOff then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - if _TraceLevel >= 1 then - self:_F( Arguments, DebugInfoCurrent, DebugInfoFrom ) - end - end -end - - ---- Trace a function call level 2. Must be at the beginning of the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:F2( Arguments ) - - if debug and _TraceOnOff then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - if _TraceLevel >= 2 then - self:_F( Arguments, DebugInfoCurrent, DebugInfoFrom ) - end - end -end - ---- Trace a function call level 3. Must be at the beginning of the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:F3( Arguments ) - - if debug and _TraceOnOff then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - if _TraceLevel >= 3 then - self:_F( Arguments, DebugInfoCurrent, DebugInfoFrom ) - end - end -end - ---- Trace a function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam ) - - if debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then - - local DebugInfoCurrent = DebugInfoCurrentParam and DebugInfoCurrentParam or debug.getinfo( 2, "nl" ) - local DebugInfoFrom = DebugInfoFromParam and DebugInfoFromParam or debug.getinfo( 3, "l" ) - - local Function = "function" - if DebugInfoCurrent.name then - Function = DebugInfoCurrent.name - end - - if _TraceAll == true or _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName].Method[Function] then - local LineCurrent = 0 - if DebugInfoCurrent.currentline then - LineCurrent = DebugInfoCurrent.currentline - end - local LineFrom = 0 - if DebugInfoFrom then - LineFrom = DebugInfoFrom.currentline - end - env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s" , LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) ) - end - end -end - ---- Trace a function logic level 1. Can be anywhere within the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:T( Arguments ) - - if debug and _TraceOnOff then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - if _TraceLevel >= 1 then - self:_T( Arguments, DebugInfoCurrent, DebugInfoFrom ) - end - end -end - - ---- Trace a function logic level 2. Can be anywhere within the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:T2( Arguments ) - - if debug and _TraceOnOff then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - if _TraceLevel >= 2 then - self:_T( Arguments, DebugInfoCurrent, DebugInfoFrom ) - end - end -end - ---- Trace a function logic level 3. Can be anywhere within the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:T3( Arguments ) - - if debug and _TraceOnOff then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - if _TraceLevel >= 3 then - self:_T( Arguments, DebugInfoCurrent, DebugInfoFrom ) - end - end -end - ---- Log an exception which will be traced always. Can be anywhere within the function logic. --- @param #BASE self --- @param Arguments A #table or any field. -function BASE:E( Arguments ) - - if debug then - local DebugInfoCurrent = debug.getinfo( 2, "nl" ) - local DebugInfoFrom = debug.getinfo( 3, "l" ) - - local Function = "function" - if DebugInfoCurrent.name then - Function = DebugInfoCurrent.name - end - - local LineCurrent = DebugInfoCurrent.currentline - local LineFrom = -1 - if DebugInfoFrom then - LineFrom = DebugInfoFrom.currentline - end - - env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) ) - end - -end - - - ---- **Core** - SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**. --- --- ![Banner Image](..\Presentations\SCHEDULER\Dia1.JPG) --- --- === --- --- # 1) @{Scheduler#SCHEDULER} class, extends @{Base#BASE} --- --- The @{Scheduler#SCHEDULER} class creates schedule. --- --- ## 1.1) SCHEDULER constructor --- --- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters: --- --- * @{Scheduler#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection. --- * @{Scheduler#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection. --- * @{Scheduler#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters. --- * @{Scheduler#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters. --- --- ## 1.2) SCHEDULER timer stopping and (re-)starting. --- --- The SCHEDULER can be stopped and restarted with the following methods: --- --- * @{Scheduler#SCHEDULER.Start}(): (Re-)Start the schedules within the SCHEDULER object. If a CallID is provided to :Start(), only the schedule referenced by CallID will be (re-)started. --- * @{Scheduler#SCHEDULER.Stop}(): Stop the schedules within the SCHEDULER object. If a CallID is provided to :Stop(), then only the schedule referenced by CallID will be stopped. --- --- ## 1.3) Create a new schedule --- --- With @{Scheduler#SCHEDULER.Schedule}() a new time event can be scheduled. This function is used by the :New() constructor when a new schedule is planned. --- --- === --- --- ### Contributions: --- --- * FlightControl : Concept & Testing --- --- ### Authors: --- --- * FlightControl : Design & Programming --- --- ### Test Missions: --- --- * SCH - Scheduler --- --- === --- --- @module Scheduler - - ---- The SCHEDULER class --- @type SCHEDULER --- @field #number ScheduleID the ID of the scheduler. --- @extends Core.Base#BASE -SCHEDULER = { - ClassName = "SCHEDULER", - Schedules = {}, -} - ---- SCHEDULER constructor. --- @param #SCHEDULER self --- @param #table SchedulerObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference. --- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments. --- @param #table SchedulerArguments Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }. --- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called. --- @param #number Repeat Specifies the interval in seconds when the scheduler will call the event function. --- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat. --- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped. --- @return #SCHEDULER self. --- @return #number The ScheduleID of the planned schedule. -function SCHEDULER:New( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop ) - local self = BASE:Inherit( self, BASE:New() ) - self:F2( { Start, Repeat, RandomizeFactor, Stop } ) - - local ScheduleID = nil - - self.MasterObject = SchedulerObject - - if SchedulerFunction then - ScheduleID = self:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop ) - end - - return self, ScheduleID -end - ---function SCHEDULER:_Destructor() --- --self:E("_Destructor") --- --- _SCHEDULEDISPATCHER:RemoveSchedule( self.CallID ) ---end - ---- Schedule a new time event. Note that the schedule will only take place if the scheduler is *started*. Even for a single schedule event, the scheduler needs to be started also. --- @param #SCHEDULER self --- @param #table SchedulerObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference. --- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments. --- @param #table SchedulerArguments Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }. --- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called. --- @param #number Repeat Specifies the interval in seconds when the scheduler will call the event function. --- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat. --- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped. --- @return #number The ScheduleID of the planned schedule. -function SCHEDULER:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop ) - self:F2( { Start, Repeat, RandomizeFactor, Stop } ) - self:T3( { SchedulerArguments } ) - - local ObjectName = "-" - if SchedulerObject and SchedulerObject.ClassName and SchedulerObject.ClassID then - ObjectName = SchedulerObject.ClassName .. SchedulerObject.ClassID - end - self:F3( { "Schedule :", ObjectName, tostring( SchedulerObject ), Start, Repeat, RandomizeFactor, Stop } ) - self.SchedulerObject = SchedulerObject - - local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( - self, - SchedulerFunction, - SchedulerArguments, - Start, - Repeat, - RandomizeFactor, - Stop - ) - - self.Schedules[#self.Schedules+1] = ScheduleID - - return ScheduleID -end - ---- (Re-)Starts the schedules or a specific schedule if a valid ScheduleID is provided. --- @param #SCHEDULER self --- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule. -function SCHEDULER:Start( ScheduleID ) - self:F3( { ScheduleID } ) - - _SCHEDULEDISPATCHER:Start( self, ScheduleID ) -end - ---- Stops the schedules or a specific schedule if a valid ScheduleID is provided. --- @param #SCHEDULER self --- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule. -function SCHEDULER:Stop( ScheduleID ) - self:F3( { ScheduleID } ) - - _SCHEDULEDISPATCHER:Stop( self, ScheduleID ) -end - ---- Removes a specific schedule if a valid ScheduleID is provided. --- @param #SCHEDULER self --- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule. -function SCHEDULER:Remove( ScheduleID ) - self:F3( { ScheduleID } ) - - _SCHEDULEDISPATCHER:Remove( self, ScheduleID ) -end - ---- Clears all pending schedules. --- @param #SCHEDULER self -function SCHEDULER:Clear() - self:F3( ) - - _SCHEDULEDISPATCHER:Clear( self ) -end - - - - - - - - - - - - - - ---- This module defines the SCHEDULEDISPATCHER class, which is used by a central object called _SCHEDULEDISPATCHER. --- --- === --- --- Takes care of the creation and dispatching of scheduled functions for SCHEDULER objects. --- --- This class is tricky and needs some thorought explanation. --- SCHEDULE classes are used to schedule functions for objects, or as persistent objects. --- The SCHEDULEDISPATCHER class ensures that: --- --- - Scheduled functions are planned according the SCHEDULER object parameters. --- - Scheduled functions are repeated when requested, according the SCHEDULER object parameters. --- - Scheduled functions are automatically removed when the schedule is finished, according the SCHEDULER object parameters. --- --- The SCHEDULEDISPATCHER class will manage SCHEDULER object in memory during garbage collection: --- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER --- object is _persistent_ within memory. --- - When a SCHEDULER object *is* attached to another object, then the SCHEDULER object is _not persistent_ within memory after a garbage collection! --- The none persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collectged, when the parent object is also desroyed or nillified and garbage collected. --- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object, --- these will not be executed anymore when the SCHEDULER object has been destroyed. --- --- The SCHEDULEDISPATCHER allows multiple scheduled functions to be planned and executed for one SCHEDULER object. --- The SCHEDULER object therefore keeps a table of "CallID's", which are returned after each planning of a new scheduled function by the SCHEDULEDISPATCHER. --- The SCHEDULER object plans new scheduled functions through the @{Scheduler#SCHEDULER.Schedule}() method. --- The Schedule() method returns the CallID that is the reference ID for each planned schedule. --- --- === --- --- === --- --- ### Contributions: - --- ### Authors: FlightControl : Design & Programming --- --- @module ScheduleDispatcher - ---- The SCHEDULEDISPATCHER structure --- @type SCHEDULEDISPATCHER -SCHEDULEDISPATCHER = { - ClassName = "SCHEDULEDISPATCHER", - CallID = 0, -} - -function SCHEDULEDISPATCHER:New() - local self = BASE:Inherit( self, BASE:New() ) - self:F3() - return self -end - ---- Add a Schedule to the ScheduleDispatcher. --- The development of this method was really tidy. --- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is nillified. --- Nothing of this code should be modified without testing it thoroughly. --- @param #SCHEDULEDISPATCHER self --- @param Core.Scheduler#SCHEDULER Scheduler -function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop ) - self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop } ) - - self.CallID = self.CallID + 1 - - -- Initialize the ObjectSchedulers array, which is a weakly coupled table. - -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.PersistentSchedulers = self.PersistentSchedulers or {} - - -- Initialize the ObjectSchedulers array, which is a weakly coupled table. - -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) -- or {} - - if Scheduler.MasterObject then - self.ObjectSchedulers[self.CallID] = Scheduler - self:F3( { CallID = self.CallID, ObjectScheduler = tostring(self.ObjectSchedulers[self.CallID]), MasterObject = tostring(Scheduler.MasterObject) } ) - else - self.PersistentSchedulers[self.CallID] = Scheduler - self:F3( { CallID = self.CallID, PersistentScheduler = self.PersistentSchedulers[self.CallID] } ) - end - - self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } ) - self.Schedule[Scheduler] = self.Schedule[Scheduler] or {} - self.Schedule[Scheduler][self.CallID] = {} - self.Schedule[Scheduler][self.CallID].Function = ScheduleFunction - self.Schedule[Scheduler][self.CallID].Arguments = ScheduleArguments - self.Schedule[Scheduler][self.CallID].StartTime = timer.getTime() + ( Start or 0 ) - self.Schedule[Scheduler][self.CallID].Start = Start + .1 - self.Schedule[Scheduler][self.CallID].Repeat = Repeat - self.Schedule[Scheduler][self.CallID].Randomize = Randomize - self.Schedule[Scheduler][self.CallID].Stop = Stop - - self:T3( self.Schedule[Scheduler][self.CallID] ) - - self.Schedule[Scheduler][self.CallID].CallHandler = function( CallID ) - self:F2( CallID ) - - local ErrorHandler = function( errmsg ) - env.info( "Error in timer function: " .. errmsg ) - if debug ~= nil then - env.info( debug.traceback() ) - end - return errmsg - end - - local Scheduler = self.ObjectSchedulers[CallID] - if not Scheduler then - Scheduler = self.PersistentSchedulers[CallID] - end - - self:T3( { Scheduler = Scheduler } ) - - if Scheduler then - - local Schedule = self.Schedule[Scheduler][CallID] - - self:T3( { Schedule = Schedule } ) - - local ScheduleObject = Scheduler.SchedulerObject - --local ScheduleObjectName = Scheduler.SchedulerObject:GetNameAndClassID() - local ScheduleFunction = Schedule.Function - local ScheduleArguments = Schedule.Arguments - local Start = Schedule.Start - local Repeat = Schedule.Repeat or 0 - local Randomize = Schedule.Randomize or 0 - local Stop = Schedule.Stop or 0 - local ScheduleID = Schedule.ScheduleID - - local Status, Result - if ScheduleObject then - local function Timer() - return ScheduleFunction( ScheduleObject, unpack( ScheduleArguments ) ) - end - Status, Result = xpcall( Timer, ErrorHandler ) - else - local function Timer() - return ScheduleFunction( unpack( ScheduleArguments ) ) - end - Status, Result = xpcall( Timer, ErrorHandler ) - end - - local CurrentTime = timer.getTime() - local StartTime = CurrentTime + Start - - if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then - if Repeat ~= 0 and ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) then - local ScheduleTime = - CurrentTime + - Repeat + - math.random( - - ( Randomize * Repeat / 2 ), - ( Randomize * Repeat / 2 ) - ) + - 0.01 - self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } ) - return ScheduleTime -- returns the next time the function needs to be called. - else - self:Stop( Scheduler, CallID ) - end - else - self:Stop( Scheduler, CallID ) - end - else - self:E( "Scheduled obscolete call for CallID: " .. CallID ) - end - - return nil - end - - self:Start( Scheduler, self.CallID ) - - return self.CallID -end - -function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID ) - self:F2( { Remove = CallID, Scheduler = Scheduler } ) - - if CallID then - self:Stop( Scheduler, CallID ) - self.Schedule[Scheduler][CallID] = nil - end -end - -function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) - self:F2( { Start = CallID, Scheduler = Scheduler } ) - - if CallID then - local Schedule = self.Schedule[Scheduler] - -- Only start when there is no ScheduleID defined! - -- This prevents to "Start" the scheduler twice with the same CallID... - if not Schedule[CallID].ScheduleID then - Schedule[CallID].ScheduleID = timer.scheduleFunction( - Schedule[CallID].CallHandler, - CallID, - timer.getTime() + Schedule[CallID].Start - ) - end - else - for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do - self:Start( Scheduler, CallID ) -- Recursive - end - end -end - -function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) - self:F2( { Stop = CallID, Scheduler = Scheduler } ) - - if CallID then - local Schedule = self.Schedule[Scheduler] - -- Only stop when there is a ScheduleID defined for the CallID. - -- So, when the scheduler was stopped before, do nothing. - if Schedule[CallID].ScheduleID then - timer.removeFunction( Schedule[CallID].ScheduleID ) - Schedule[CallID].ScheduleID = nil - end - else - for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do - self:Stop( Scheduler, CallID ) -- Recursive - end - end -end - -function SCHEDULEDISPATCHER:Clear( Scheduler ) - self:F2( { Scheduler = Scheduler } ) - - for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do - self:Stop( Scheduler, CallID ) -- Recursive - end -end - - - ---- **Core** - EVENT models DCS **event dispatching** using a **publish-subscribe** model. --- --- ![Banner Image](..\Presentations\EVENT\Dia1.JPG) --- --- === --- --- # 1) Event Handling Overview --- --- ![Objects](..\Presentations\EVENT\Dia2.JPG) --- --- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc. --- This module provides a mechanism to dispatch those events occuring within your running mission, to the different objects orchestrating your mission. --- --- ![Objects](..\Presentations\EVENT\Dia3.JPG) --- --- Objects can subscribe to different events. The Event dispatcher will publish the received DCS events to the subscribed MOOSE objects, in a specified order. --- In this way, the subscribed MOOSE objects are kept in sync with your evolving running mission. --- --- ## 1.1) Event Dispatching --- --- ![Objects](..\Presentations\EVENT\Dia4.JPG) --- --- The _EVENTDISPATCHER object is automatically created within MOOSE, --- and handles the dispatching of DCS Events occurring --- in the simulator to the subscribed objects --- in the correct processing order. --- --- ![Objects](..\Presentations\EVENT\Dia5.JPG) --- --- There are 5 levels of kind of objects that the _EVENTDISPATCHER services: --- --- * _DATABASE object: The core of the MOOSE objects. Any object that is created, deleted or updated, is done in this database. --- * SET_ derived classes: Subsets of the _DATABASE object. These subsets are updated by the _EVENTDISPATCHER as the second priority. --- * UNIT objects: UNIT objects can subscribe to DCS events. Each DCS event will be directly published to teh subscribed UNIT object. --- * GROUP objects: GROUP objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed GROUP object. --- * Any other object: Various other objects can subscribe to DCS events. Each DCS event triggered will be published to each subscribed object. --- --- ![Objects](..\Presentations\EVENT\Dia6.JPG) --- --- For most DCS events, the above order of updating will be followed. --- --- ![Objects](..\Presentations\EVENT\Dia7.JPG) --- --- But for some DCS events, the publishing order is reversed. This is due to the fact that objects need to be **erased** instead of added. --- --- ## 1.2) Event Handling --- --- ![Objects](..\Presentations\EVENT\Dia8.JPG) --- --- The actual event subscribing and handling is not facilitated through the _EVENTDISPATCHER, but it is done through the @{BASE} class, @{UNIT} class and @{GROUP} class. --- The _EVENTDISPATCHER is a component that is quietly working in the background of MOOSE. --- --- ![Objects](..\Presentations\EVENT\Dia9.JPG) --- --- The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator, --- and handled through lua scripting. MOOSE provides an encapsulation to handle these events more efficiently. --- --- ### 1.2.1 Subscribe / Unsubscribe to DCS Events --- --- At first, the mission designer will need to **Subscribe** to a specific DCS event for the class. --- So, when the DCS event occurs, the class will be notified of that event. --- There are two functions which you use to subscribe to or unsubscribe from an event. --- --- * @{Base#BASE.HandleEvent}(): Subscribe to a DCS Event. --- * @{Base#BASE.UnHandleEvent}(): Unsubscribe from a DCS Event. --- --- Note that for a UNIT, the event will be handled **for that UNIT only**! --- Note that for a GROUP, the event will be handled **for all the UNITs in that GROUP only**! --- --- For all objects of other classes, the subscribed events will be handled for **all UNITs within the Mission**! --- So if a UNIT within the mission has the subscribed event for that object, --- then the object event handler will receive the event for that UNIT! --- --- ### 1.3.2 Event Handling of DCS Events --- --- Once the class is subscribed to the event, an **Event Handling** method on the object or class needs to be written that will be called --- when the DCS event occurs. The Event Handling method receives an @{Event#EVENTDATA} structure, which contains a lot of information --- about the event that occurred. --- --- Find below an example of the prototype how to write an event handling function for two units: --- --- local Tank1 = UNIT:FindByName( "Tank A" ) --- local Tank2 = UNIT:FindByName( "Tank B" ) --- --- -- Here we subscribe to the Dead events. So, if one of these tanks dies, the Tank1 or Tank2 objects will be notified. --- Tank1:HandleEvent( EVENTS.Dead ) --- Tank2:HandleEvent( EVENTS.Dead ) --- --- --- This function is an Event Handling function that will be called when Tank1 is Dead. --- -- @param Wrapper.Unit#UNIT self --- -- @param Core.Event#EVENTDATA EventData --- function Tank1:OnEventDead( EventData ) --- --- self:SmokeGreen() --- end --- --- --- This function is an Event Handling function that will be called when Tank2 is Dead. --- -- @param Wrapper.Unit#UNIT self --- -- @param Core.Event#EVENTDATA EventData --- function Tank2:OnEventDead( EventData ) --- --- self:SmokeBlue() --- end --- --- ### 1.3.3 Event Handling methods that are automatically called upon subscribed DCS events --- --- ![Objects](..\Presentations\EVENT\Dia10.JPG) --- --- The following list outlines which EVENTS item in the structure corresponds to which Event Handling method. --- Always ensure that your event handling methods align with the events being subscribed to, or nothing will be executed. --- --- # 2) EVENTS type --- --- The EVENTS structure contains names for all the different DCS events that objects can subscribe to using the --- @{Base#BASE.HandleEvent}() method. --- --- # 3) EVENTDATA type --- --- The @{Event#EVENTDATA} structure contains all the fields that are populated with event information before --- an Event Handler method is being called by the event dispatcher. --- The Event Handler received the EVENTDATA object as a parameter, and can be used to investigate further the different events. --- There are basically 4 main categories of information stored in the EVENTDATA structure: --- --- * Initiator Unit data: Several fields documenting the initiator unit related to the event. --- * Target Unit data: Several fields documenting the target unit related to the event. --- * Weapon data: Certain events populate weapon information. --- * Place data: Certain events populate place information. --- --- --- This function is an Event Handling function that will be called when Tank1 is Dead. --- -- EventData is an EVENTDATA structure. --- -- We use the EventData.IniUnit to smoke the tank Green. --- -- @param Wrapper.Unit#UNIT self --- -- @param Core.Event#EVENTDATA EventData --- function Tank1:OnEventDead( EventData ) --- --- EventData.IniUnit:SmokeGreen() --- end --- --- --- Find below an overview which events populate which information categories: --- --- ![Objects](..\Presentations\EVENT\Dia14.JPG) --- --- **IMPORTANT NOTE:** Some events can involve not just UNIT objects, but also STATIC objects!!! --- In that case the initiator or target unit fields will refer to a STATIC object! --- In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated. --- The fields **IniObjectCategory** and **TgtObjectCategory** contain the indicator which **kind of object is involved** in the event. --- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory. --- Example code snippet: --- --- if Event.IniObjectCategory == Object.Category.UNIT then --- ... --- end --- if Event.IniObjectCategory == Object.Category.STATIC then --- ... --- end --- --- When a static object is involved in the event, the Group and Player fields won't be populated. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) --- YYYY-MM-DD: CLASS:**NewFunction( Params )** added --- --- Hereby the change log: --- --- * 2017-03-07: Added the correct event dispatching in case the event is subscribed by a GROUP. --- --- * 2017-02-07: Did a complete revision of the Event Handing API and underlying mechanisms. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- ### Authors: --- --- * [**FlightControl**](https://forums.eagle.ru/member.php?u=89536): Design & Programming & documentation. --- --- @module Event - - ---- The EVENT structure --- @type EVENT --- @field #EVENT.Events Events --- @extends Core.Base#BASE -EVENT = { - ClassName = "EVENT", - ClassID = 0, -} - ---- The different types of events supported by MOOSE. --- Use this structure to subscribe to events using the @{Base#BASE.HandleEvent}() method. --- @type EVENTS -EVENTS = { - Shot = world.event.S_EVENT_SHOT, - Hit = world.event.S_EVENT_HIT, - Takeoff = world.event.S_EVENT_TAKEOFF, - Land = world.event.S_EVENT_LAND, - Crash = world.event.S_EVENT_CRASH, - Ejection = world.event.S_EVENT_EJECTION, - Refueling = world.event.S_EVENT_REFUELING, - Dead = world.event.S_EVENT_DEAD, - PilotDead = world.event.S_EVENT_PILOT_DEAD, - BaseCaptured = world.event.S_EVENT_BASE_CAPTURED, - MissionStart = world.event.S_EVENT_MISSION_START, - MissionEnd = world.event.S_EVENT_MISSION_END, - TookControl = world.event.S_EVENT_TOOK_CONTROL, - RefuelingStop = world.event.S_EVENT_REFUELING_STOP, - Birth = world.event.S_EVENT_BIRTH, - HumanFailure = world.event.S_EVENT_HUMAN_FAILURE, - EngineStartup = world.event.S_EVENT_ENGINE_STARTUP, - EngineShutdown = world.event.S_EVENT_ENGINE_SHUTDOWN, - PlayerEnterUnit = world.event.S_EVENT_PLAYER_ENTER_UNIT, - PlayerLeaveUnit = world.event.S_EVENT_PLAYER_LEAVE_UNIT, - PlayerComment = world.event.S_EVENT_PLAYER_COMMENT, - ShootingStart = world.event.S_EVENT_SHOOTING_START, - ShootingEnd = world.event.S_EVENT_SHOOTING_END, -} - ---- The Event structure --- Note that at the beginning of each field description, there is an indication which field will be populated depending on the object type involved in the Event: --- --- * A (Object.Category.)UNIT : A UNIT object type is involved in the Event. --- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event.µ --- --- @type EVENTDATA --- @field #number id The identifier of the event. --- --- @field Dcs.DCSUnit#Unit initiator (UNIT/STATIC/SCENERY) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. --- @field Dcs.DCSObject#Object.Category IniObjectCategory (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). --- @field Dcs.DCSUnit#Unit IniDCSUnit (UNIT/STATIC) The initiating @{DCSUnit#Unit} or @{DCSStaticObject#StaticObject}. --- @field #string IniDCSUnitName (UNIT/STATIC) The initiating Unit name. --- @field Wrapper.Unit#UNIT IniUnit (UNIT/STATIC) The initiating MOOSE wrapper @{Unit#UNIT} of the initiator Unit object. --- @field #string IniUnitName (UNIT/STATIC) The initiating UNIT name (same as IniDCSUnitName). --- @field Dcs.DCSGroup#Group IniDCSGroup (UNIT) The initiating {DCSGroup#Group}. --- @field #string IniDCSGroupName (UNIT) The initiating Group name. --- @field Wrapper.Group#GROUP IniGroup (UNIT) The initiating MOOSE wrapper @{Group#GROUP} of the initiator Group object. --- @field #string IniGroupName UNIT) The initiating GROUP name (same as IniDCSGroupName). --- @field #string IniPlayerName (UNIT) The name of the initiating player in case the Unit is a client or player slot. --- @field Dcs.DCScoalition#coalition.side IniCoalition (UNIT) The coalition of the initiator. --- @field Dcs.DCSUnit#Unit.Category IniCategory (UNIT) The category of the initiator. --- @field #string IniTypeName (UNIT) The type name of the initiator. --- --- @field Dcs.DCSUnit#Unit target (UNIT/STATIC) The target @{Dcs.DCSUnit#Unit} or @{DCSStaticObject#StaticObject}. --- @field Dcs.DCSObject#Object.Category TgtObjectCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). --- @field Dcs.DCSUnit#Unit TgtDCSUnit (UNIT/STATIC) The target @{DCSUnit#Unit} or @{DCSStaticObject#StaticObject}. --- @field #string TgtDCSUnitName (UNIT/STATIC) The target Unit name. --- @field Wrapper.Unit#UNIT TgtUnit (UNIT/STATIC) The target MOOSE wrapper @{Unit#UNIT} of the target Unit object. --- @field #string TgtUnitName (UNIT/STATIC) The target UNIT name (same as TgtDCSUnitName). --- @field Dcs.DCSGroup#Group TgtDCSGroup (UNIT) The target {DCSGroup#Group}. --- @field #string TgtDCSGroupName (UNIT) The target Group name. --- @field Wrapper.Group#GROUP TgtGroup (UNIT) The target MOOSE wrapper @{Group#GROUP} of the target Group object. --- @field #string TgtGroupName (UNIT) The target GROUP name (same as TgtDCSGroupName). --- @field #string TgtPlayerName (UNIT) The name of the target player in case the Unit is a client or player slot. --- @field Dcs.DCScoalition#coalition.side TgtCoalition (UNIT) The coalition of the target. --- @field Dcs.DCSUnit#Unit.Category TgtCategory (UNIT) The category of the target. --- @field #string TgtTypeName (UNIT) The type name of the target. --- --- @field weapon The weapon used during the event. --- @field Weapon --- @field WeaponName --- @field WeaponTgtDCSUnit - - -local _EVENTMETA = { - [world.event.S_EVENT_SHOT] = { - Order = 1, - Event = "OnEventShot", - Text = "S_EVENT_SHOT" - }, - [world.event.S_EVENT_HIT] = { - Order = 1, - Event = "OnEventHit", - Text = "S_EVENT_HIT" - }, - [world.event.S_EVENT_TAKEOFF] = { - Order = 1, - Event = "OnEventTakeoff", - Text = "S_EVENT_TAKEOFF" - }, - [world.event.S_EVENT_LAND] = { - Order = 1, - Event = "OnEventLand", - Text = "S_EVENT_LAND" - }, - [world.event.S_EVENT_CRASH] = { - Order = -1, - Event = "OnEventCrash", - Text = "S_EVENT_CRASH" - }, - [world.event.S_EVENT_EJECTION] = { - Order = 1, - Event = "OnEventEjection", - Text = "S_EVENT_EJECTION" - }, - [world.event.S_EVENT_REFUELING] = { - Order = 1, - Event = "OnEventRefueling", - Text = "S_EVENT_REFUELING" - }, - [world.event.S_EVENT_DEAD] = { - Order = -1, - Event = "OnEventDead", - Text = "S_EVENT_DEAD" - }, - [world.event.S_EVENT_PILOT_DEAD] = { - Order = 1, - Event = "OnEventPilotDead", - Text = "S_EVENT_PILOT_DEAD" - }, - [world.event.S_EVENT_BASE_CAPTURED] = { - Order = 1, - Event = "OnEventBaseCaptured", - Text = "S_EVENT_BASE_CAPTURED" - }, - [world.event.S_EVENT_MISSION_START] = { - Order = 1, - Event = "OnEventMissionStart", - Text = "S_EVENT_MISSION_START" - }, - [world.event.S_EVENT_MISSION_END] = { - Order = 1, - Event = "OnEventMissionEnd", - Text = "S_EVENT_MISSION_END" - }, - [world.event.S_EVENT_TOOK_CONTROL] = { - Order = 1, - Event = "OnEventTookControl", - Text = "S_EVENT_TOOK_CONTROL" - }, - [world.event.S_EVENT_REFUELING_STOP] = { - Order = 1, - Event = "OnEventRefuelingStop", - Text = "S_EVENT_REFUELING_STOP" - }, - [world.event.S_EVENT_BIRTH] = { - Order = 1, - Event = "OnEventBirth", - Text = "S_EVENT_BIRTH" - }, - [world.event.S_EVENT_HUMAN_FAILURE] = { - Order = 1, - Event = "OnEventHumanFailure", - Text = "S_EVENT_HUMAN_FAILURE" - }, - [world.event.S_EVENT_ENGINE_STARTUP] = { - Order = 1, - Event = "OnEventEngineStartup", - Text = "S_EVENT_ENGINE_STARTUP" - }, - [world.event.S_EVENT_ENGINE_SHUTDOWN] = { - Order = 1, - Event = "OnEventEngineShutdown", - Text = "S_EVENT_ENGINE_SHUTDOWN" - }, - [world.event.S_EVENT_PLAYER_ENTER_UNIT] = { - Order = 1, - Event = "OnEventPlayerEnterUnit", - Text = "S_EVENT_PLAYER_ENTER_UNIT" - }, - [world.event.S_EVENT_PLAYER_LEAVE_UNIT] = { - Order = -1, - Event = "OnEventPlayerLeaveUnit", - Text = "S_EVENT_PLAYER_LEAVE_UNIT" - }, - [world.event.S_EVENT_PLAYER_COMMENT] = { - Order = 1, - Event = "OnEventPlayerComment", - Text = "S_EVENT_PLAYER_COMMENT" - }, - [world.event.S_EVENT_SHOOTING_START] = { - Order = 1, - Event = "OnEventShootingStart", - Text = "S_EVENT_SHOOTING_START" - }, - [world.event.S_EVENT_SHOOTING_END] = { - Order = 1, - Event = "OnEventShootingEnd", - Text = "S_EVENT_SHOOTING_END" - }, -} - - ---- The Events structure --- @type EVENT.Events --- @field #number IniUnit - -function EVENT:New() - local self = BASE:Inherit( self, BASE:New() ) - self:F2() - self.EventHandler = world.addEventHandler( self ) - return self -end - -function EVENT:EventText( EventID ) - - local EventText = _EVENTMETA[EventID].Text - - return EventText -end - - ---- Initializes the Events structure for the event --- @param #EVENT self --- @param Dcs.DCSWorld#world.event EventID --- @param Core.Base#BASE EventClass --- @return #EVENT.Events -function EVENT:Init( EventID, EventClass ) - self:F3( { _EVENTMETA[EventID].Text, EventClass } ) - - if not self.Events[EventID] then - -- Create a WEAK table to ensure that the garbage collector is cleaning the event links when the object usage is cleaned. - self.Events[EventID] = setmetatable( {}, { __mode = "k" } ) - end - - -- Each event has a subtable of EventClasses, ordered by EventPriority. - local EventPriority = EventClass:GetEventPriority() - if not self.Events[EventID][EventPriority] then - self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } ) - end - - if not self.Events[EventID][EventPriority][EventClass] then - self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "v" } ) - end - return self.Events[EventID][EventPriority][EventClass] -end - ---- Removes an Events entry --- @param #EVENT self --- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param Dcs.DCSWorld#world.event EventID --- @return #EVENT.Events -function EVENT:Remove( EventClass, EventID ) - self:F3( { EventClass, _EVENTMETA[EventID].Text } ) - - local EventClass = EventClass - local EventPriority = EventClass:GetEventPriority() - self.Events[EventID][EventPriority][EventClass] = nil -end - ---- Removes an Events entry for a UNIT. --- @param #EVENT self --- @param #string UnitName The name of the UNIT. --- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param Dcs.DCSWorld#world.event EventID --- @return #EVENT.Events -function EVENT:RemoveForUnit( UnitName, EventClass, EventID ) - self:F3( { EventClass, _EVENTMETA[EventID].Text } ) - - local EventClass = EventClass - local EventPriority = EventClass:GetEventPriority() - local Event = self.Events[EventID][EventPriority][EventClass] - Event.EventUnit[UnitName] = nil -end - ---- Removes an Events entry for a GROUP. --- @param #EVENT self --- @param #string GroupName The name of the GROUP. --- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param Dcs.DCSWorld#world.event EventID --- @return #EVENT.Events -function EVENT:RemoveForGroup( GroupName, EventClass, EventID ) - self:F3( { EventClass, _EVENTMETA[EventID].Text } ) - - local EventClass = EventClass - local EventPriority = EventClass:GetEventPriority() - local Event = self.Events[EventID][EventPriority][EventClass] - Event.EventGroup[GroupName] = nil -end - ---- Clears all event subscriptions for a @{Base#BASE} derived object. --- @param #EVENT self --- @param Core.Base#BASE EventObject -function EVENT:RemoveAll( EventObject ) - self:F3( { EventObject:GetClassNameAndID() } ) - - local EventClass = EventObject:GetClassNameAndID() - local EventPriority = EventClass:GetEventPriority() - for EventID, EventData in pairs( self.Events ) do - self.Events[EventID][EventPriority][EventClass] = nil - end -end - - - ---- Create an OnDead event handler for a group --- @param #EVENT self --- @param #table EventTemplate --- @param #function EventFunction The function to be called when the event occurs for the unit. --- @param EventClass The instance of the class for which the event is. --- @param #function OnEventFunction --- @return #EVENT -function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID ) - self:F2( EventTemplate.name ) - - for EventUnitID, EventUnit in pairs( EventTemplate.units ) do - self:OnEventForUnit( EventUnit.name, EventFunction, EventClass, EventID ) - end - return self -end - ---- Set a new listener for an S_EVENT_X event independent from a unit or a weapon. --- @param #EVENT self --- @param #function EventFunction The function to be called when the event occurs for the unit. --- @param Core.Base#BASE EventClass The self instance of the class for which the event is captured. When the event happens, the event process will be called in this class provided. --- @param EventID --- @return #EVENT -function EVENT:OnEventGeneric( EventFunction, EventClass, EventID ) - self:F2( { EventID } ) - - local EventData = self:Init( EventID, EventClass ) - EventData.EventFunction = EventFunction - EventData.EventClass = EventClass - - return self -end - - ---- Set a new listener for an S_EVENT_X event for a UNIT. --- @param #EVENT self --- @param #string UnitName The name of the UNIT. --- @param #function EventFunction The function to be called when the event occurs for the GROUP. --- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param EventID --- @return #EVENT -function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID ) - self:F2( UnitName ) - - local EventData = self:Init( EventID, EventClass ) - if not EventData.EventUnit then - EventData.EventUnit = {} - end - EventData.EventUnit[UnitName] = {} - EventData.EventUnit[UnitName].EventFunction = EventFunction - EventData.EventUnit[UnitName].EventClass = EventClass - return self -end - ---- Set a new listener for an S_EVENT_X event for a GROUP. --- @param #EVENT self --- @param #string GroupName The name of the GROUP. --- @param #function EventFunction The function to be called when the event occurs for the GROUP. --- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param EventID --- @return #EVENT -function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID ) - self:F2( GroupName ) - - local Event = self:Init( EventID, EventClass ) - if not Event.EventGroup then - Event.EventGroup = {} - end - Event.EventGroup[GroupName] = {} - Event.EventGroup[GroupName].EventFunction = EventFunction - Event.EventGroup[GroupName].EventClass = EventClass - return self -end - -do -- OnBirth - - --- Create an OnBirth event handler for a group - -- @param #EVENT self - -- @param Wrapper.Group#GROUP EventGroup - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass ) - self:F2( EventTemplate.name ) - - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Birth ) - - return self - end - -end - -do -- OnCrash - - --- Create an OnCrash event handler for a group - -- @param #EVENT self - -- @param Wrapper.Group#GROUP EventGroup - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventClass ) - self:F2( EventTemplate.name ) - - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Crash ) - - return self - end - -end - -do -- OnDead - - --- Create an OnDead event handler for a group - -- @param #EVENT self - -- @param Wrapper.Group#GROUP EventGroup - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass ) - self:F2( EventTemplate.name ) - - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Dead ) - - return self - end - -end - - -do -- OnLand - --- Create an OnLand event handler for a group - -- @param #EVENT self - -- @param #table EventTemplate - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass ) - self:F2( EventTemplate.name ) - - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Land ) - - return self - end - -end - -do -- OnTakeOff - --- Create an OnTakeOff event handler for a group - -- @param #EVENT self - -- @param #table EventTemplate - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass ) - self:F2( EventTemplate.name ) - - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Takeoff ) - - return self - end - -end - -do -- OnEngineShutDown - - --- Create an OnDead event handler for a group - -- @param #EVENT self - -- @param #table EventTemplate - -- @param #function EventFunction The function to be called when the event occurs for the unit. - -- @param EventClass The self instance of the class for which the event is. - -- @return #EVENT - function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventClass ) - self:F2( EventTemplate.name ) - - self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.EngineShutdown ) - - return self - end - -end - - ---- @param #EVENT self --- @param #EVENTDATA Event -function EVENT:onEvent( Event ) - - local ErrorHandler = function( errmsg ) - - env.info( "Error in SCHEDULER function:" .. errmsg ) - if debug ~= nil then - env.info( debug.traceback() ) - end - - return errmsg - end - - self:E( _EVENTMETA[Event.id].Text, Event ) - - if self and self.Events and self.Events[Event.id] then - - - if Event.initiator then - - Event.IniObjectCategory = Event.initiator:getCategory() - - if Event.IniObjectCategory == Object.Category.UNIT then - Event.IniDCSUnit = Event.initiator - Event.IniDCSUnitName = Event.IniDCSUnit:getName() - Event.IniUnitName = Event.IniDCSUnitName - Event.IniDCSGroup = Event.IniDCSUnit:getGroup() - Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName ) - if not Event.IniUnit then - -- Unit can be a CLIENT. Most likely this will be the case ... - Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true ) - end - Event.IniDCSGroupName = "" - if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then - Event.IniDCSGroupName = Event.IniDCSGroup:getName() - Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName ) - if Event.IniGroup then - Event.IniGroupName = Event.IniDCSGroupName - end - end - Event.IniPlayerName = Event.IniDCSUnit:getPlayerName() - Event.IniCoalition = Event.IniDCSUnit:getCoalition() - Event.IniTypeName = Event.IniDCSUnit:getTypeName() - Event.IniCategory = Event.IniDCSUnit:getDesc().category - end - - if Event.IniObjectCategory == Object.Category.STATIC then - Event.IniDCSUnit = Event.initiator - Event.IniDCSUnitName = Event.IniDCSUnit:getName() - Event.IniUnitName = Event.IniDCSUnitName - Event.IniUnit = STATIC:FindByName( Event.IniDCSUnitName, false ) - Event.IniCoalition = Event.IniDCSUnit:getCoalition() - Event.IniCategory = Event.IniDCSUnit:getDesc().category - Event.IniTypeName = Event.IniDCSUnit:getTypeName() - end - - if Event.IniObjectCategory == Object.Category.SCENERY then - Event.IniDCSUnit = Event.initiator - Event.IniDCSUnitName = Event.IniDCSUnit:getName() - Event.IniUnitName = Event.IniDCSUnitName - Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator ) - Event.IniCategory = Event.IniDCSUnit:getDesc().category - Event.IniTypeName = Event.IniDCSUnit:getTypeName() - end - end - - if Event.target then - - Event.TgtObjectCategory = Event.target:getCategory() - - if Event.TgtObjectCategory == Object.Category.UNIT then - Event.TgtDCSUnit = Event.target - Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup() - Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() - Event.TgtUnitName = Event.TgtDCSUnitName - Event.TgtUnit = UNIT:FindByName( Event.TgtDCSUnitName ) - Event.TgtDCSGroupName = "" - if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then - Event.TgtDCSGroupName = Event.TgtDCSGroup:getName() - Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) - if Event.TgtGroup then - Event.TgtGroupName = Event.TgtDCSGroupName - end - end - Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName() - Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() - Event.TgtCategory = Event.TgtDCSUnit:getDesc().category - Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() - end - - if Event.TgtObjectCategory == Object.Category.STATIC then - Event.TgtDCSUnit = Event.target - Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() - Event.TgtUnitName = Event.TgtDCSUnitName - Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName ) - Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() - Event.TgtCategory = Event.TgtDCSUnit:getDesc().category - Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() - end - - if Event.TgtObjectCategory == Object.Category.SCENERY then - Event.TgtDCSUnit = Event.target - Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() - Event.TgtUnitName = Event.TgtDCSUnitName - Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target ) - Event.TgtCategory = Event.TgtDCSUnit:getDesc().category - Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() - end - end - - if Event.weapon then - Event.Weapon = Event.weapon - Event.WeaponName = Event.Weapon:getTypeName() - Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit! - Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName() - Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition() - Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category - Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() - --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() - end - - local PriorityOrder = _EVENTMETA[Event.id].Order - local PriorityBegin = PriorityOrder == -1 and 5 or 1 - local PriorityEnd = PriorityOrder == -1 and 1 or 5 - - if Event.IniObjectCategory ~= 3 then - self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) - end - - for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do - - if self.Events[Event.id][EventPriority] then - - -- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called. - for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do - - Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName ) - Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) - - -- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT. - if ( Event.IniDCSUnitName and EventData.EventUnit and EventData.EventUnit[Event.IniDCSUnitName] ) or - ( Event.TgtDCSUnitName and EventData.EventUnit and EventData.EventUnit[Event.TgtDCSUnitName] ) then - - if EventData.EventUnit[Event.IniDCSUnitName] then - - -- First test if a EventFunction is Set, otherwise search for the default function - if EventData.EventUnit[Event.IniDCSUnitName].EventFunction then - - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event ) - end, ErrorHandler ) - - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event ) - end, ErrorHandler ) - end - end - end - - if EventData.EventUnit[Event.TgtDCSUnitName] then - - -- First test if a EventFunction is Set, otherwise search for the default function - if EventData.EventUnit[Event.TgtDCSUnitName].EventFunction then - - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventData.EventUnit[Event.TgtDCSUnitName].EventFunction( EventClass, Event ) - end, ErrorHandler ) - - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event ) - end, ErrorHandler ) - end - end - end - - else - - -- If the EventData is for a GROUP, the call directly the EventClass EventFunction for the UNIT in that GROUP. - if ( Event.IniDCSUnitName and Event.IniDCSGroupName and Event.IniGroupName and EventData.EventGroup and EventData.EventGroup[Event.IniGroupName] ) or - ( Event.TgtDCSUnitName and Event.TgtDCSGroupName and Event.TgtGroupName and EventData.EventGroup and EventData.EventGroup[Event.TgtGroupName] ) then - - if EventData.EventGroup[Event.IniGroupName] then - -- First test if a EventFunction is Set, otherwise search for the default function - if EventData.EventGroup[Event.IniGroupName].EventFunction then - - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event ) - end, ErrorHandler ) - - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event ) - end, ErrorHandler ) - end - end - end - - if EventData.EventGroup[Event.TgtGroupName] then - if EventData.EventGroup[Event.TgtGroupName].EventFunction then - - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event ) - end, ErrorHandler ) - - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) - end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event ) - end, ErrorHandler ) - end - end - end - - else - - -- If the EventData is not bound to a specific unit, then call the EventClass EventFunction. - -- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon. - if (Event.IniDCSUnit or Event.WeaponUNIT) and not EventData.EventUnit then - - if EventClass == EventData.EventClass then - - -- First test if a EventFunction is Set, otherwise search for the default function - if EventData.EventFunction then - - -- There is an EventFunction defined, so call the EventFunction. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - end - local Result, Value = xpcall( - function() - return EventData.EventFunction( EventClass, Event ) - end, ErrorHandler ) - else - - -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. - local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ] - if EventFunction and type( EventFunction ) == "function" then - - -- Now call the default event function. - if Event.IniObjectCategory ~= 3 then - self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - end - - local Result, Value = xpcall( - function() - local Result, Value = EventFunction( EventClass, Event ) - return Result, Value - end, ErrorHandler ) - end - end - end - end - end - end - end - end - end - else - self:E( { _EVENTMETA[Event.id].Text, Event } ) - end - - Event = nil -end - ---- The EVENTHANDLER structure --- @type EVENTHANDLER --- @extends Core.Base#BASE -EVENTHANDLER = { - ClassName = "EVENTHANDLER", - ClassID = 0, -} - ---- The EVENTHANDLER constructor --- @param #EVENTHANDLER self --- @return #EVENTHANDLER -function EVENTHANDLER:New() - self = BASE:Inherit( self, BASE:New() ) -- #EVENTHANDLER - return self -end ---- **Core** -- MENU_ classes model the definition of **hierarchical menu structures** and **commands for players** within a mission. --- --- === --- --- DCS Menus can be managed using the MENU classes. --- The advantage of using MENU classes is that it hides the complexity of dealing with menu management in more advanced scanerios where you need to --- set menus and later remove them, and later set them again. You'll find while using use normal DCS scripting functions, that setting and removing --- menus is not a easy feat if you have complex menu hierarchies defined. --- Using the MOOSE menu classes, the removal and refreshing of menus are nicely being handled within these classes, and becomes much more easy. --- On top, MOOSE implements **variable parameter** passing for command menus. --- --- There are basically two different MENU class types that you need to use: --- --- ### To manage **main menus**, the classes begin with **MENU_**: --- --- * @{Menu#MENU_MISSION}: Manages main menus for whole mission file. --- * @{Menu#MENU_COALITION}: Manages main menus for whole coalition. --- * @{Menu#MENU_GROUP}: Manages main menus for GROUPs. --- * @{Menu#MENU_CLIENT}: Manages main menus for CLIENTs. This manages menus for units with the skill level "Client". --- --- ### To manage **command menus**, which are menus that allow the player to issue **functions**, the classes begin with **MENU_COMMAND_**: --- --- * @{Menu#MENU_MISSION_COMMAND}: Manages command menus for whole mission file. --- * @{Menu#MENU_COALITION_COMMAND}: Manages command menus for whole coalition. --- * @{Menu#MENU_GROUP_COMMAND}: Manages command menus for GROUPs. --- * @{Menu#MENU_CLIENT_COMMAND}: Manages command menus for CLIENTs. This manages menus for units with the skill level "Client". --- --- === --- --- The above menus classes **are derived** from 2 main **abstract** classes defined within the MOOSE framework (so don't use these): --- --- 1) MENU_ BASE abstract base classes (don't use them) --- ==================================================== --- The underlying base menu classes are **NOT** to be used within your missions. --- These are simply abstract base classes defining a couple of fields that are used by the --- derived MENU_ classes to manage menus. --- --- 1.1) @{#MENU_BASE} class, extends @{Base#BASE} --- -------------------------------------------------- --- The @{#MENU_BASE} class defines the main MENU class where other MENU classes are derived from. --- --- 1.2) @{#MENU_COMMAND_BASE} class, extends @{Base#BASE} --- ---------------------------------------------------------- --- The @{#MENU_COMMAND_BASE} class defines the main MENU class where other MENU COMMAND_ classes are derived from, in order to set commands. --- --- === --- --- **The next menus define the MENU classes that you can use within your missions.** --- --- 2) MENU MISSION classes --- ====================== --- The underlying classes manage the menus for a complete mission file. --- --- 2.1) @{#MENU_MISSION} class, extends @{Menu#MENU_BASE} --- --------------------------------------------------------- --- The @{Menu#MENU_MISSION} class manages the main menus for a complete mission. --- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}. --- --- 2.2) @{#MENU_MISSION_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE} --- ------------------------------------------------------------------------- --- The @{Menu#MENU_MISSION_COMMAND} class manages the command menus for a complete mission, which allow players to execute functions during mission execution. --- You can add menus with the @{#MENU_MISSION_COMMAND.New} method, which constructs a MENU_MISSION_COMMAND object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION_COMMAND.Remove}. --- --- === --- --- 3) MENU COALITION classes --- ========================= --- The underlying classes manage the menus for whole coalitions. --- --- 3.1) @{#MENU_COALITION} class, extends @{Menu#MENU_BASE} --- ------------------------------------------------------------ --- The @{Menu#MENU_COALITION} class manages the main menus for coalitions. --- You can add menus with the @{#MENU_COALITION.New} method, which constructs a MENU_COALITION object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION.Remove}. --- --- 3.2) @{Menu#MENU_COALITION_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE} --- ---------------------------------------------------------------------------- --- The @{Menu#MENU_COALITION_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. --- You can add menus with the @{#MENU_COALITION_COMMAND.New} method, which constructs a MENU_COALITION_COMMAND object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION_COMMAND.Remove}. --- --- === --- --- 4) MENU GROUP classes --- ===================== --- The underlying classes manage the menus for groups. Note that groups can be inactive, alive or can be destroyed. --- --- 4.1) @{Menu#MENU_GROUP} class, extends @{Menu#MENU_BASE} --- -------------------------------------------------------- --- The @{Menu#MENU_GROUP} class manages the main menus for coalitions. --- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}. --- --- 4.2) @{Menu#MENU_GROUP_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE} --- ------------------------------------------------------------------------ --- The @{Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. --- You can add menus with the @{#MENU_GROUP_COMMAND.New} method, which constructs a MENU_GROUP_COMMAND object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP_COMMAND.Remove}. --- --- === --- --- 5) MENU CLIENT classes --- ====================== --- The underlying classes manage the menus for units with skill level client or player. --- --- 5.1) @{Menu#MENU_CLIENT} class, extends @{Menu#MENU_BASE} --- --------------------------------------------------------- --- The @{Menu#MENU_CLIENT} class manages the main menus for coalitions. --- You can add menus with the @{#MENU_CLIENT.New} method, which constructs a MENU_CLIENT object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT.Remove}. --- --- 5.2) @{Menu#MENU_CLIENT_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE} --- ------------------------------------------------------------------------- --- The @{Menu#MENU_CLIENT_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. --- You can add menus with the @{#MENU_CLIENT_COMMAND.New} method, which constructs a MENU_CLIENT_COMMAND object and returns you the object reference. --- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT_COMMAND.Remove}. --- --- === --- --- ### Contributions: - --- ### Authors: FlightControl : Design & Programming --- --- @module Menu - - -do -- MENU_BASE - - --- The MENU_BASE class - -- @type MENU_BASE - -- @extends Base#BASE - MENU_BASE = { - ClassName = "MENU_BASE", - MenuPath = nil, - MenuText = "", - MenuParentPath = nil - } - - --- Consructor - -- @param #MENU_BASE - -- @return #MENU_BASE - function MENU_BASE:New( MenuText, ParentMenu ) - - local MenuParentPath = {} - if ParentMenu ~= nil then - MenuParentPath = ParentMenu.MenuPath - end - - local self = BASE:Inherit( self, BASE:New() ) - - self.MenuPath = nil - self.MenuText = MenuText - self.MenuParentPath = MenuParentPath - self.Menus = {} - self.MenuCount = 0 - self.MenuRemoveParent = false - self.MenuTime = timer.getTime() - - return self - end - - --- Gets a @{Menu} from a parent @{Menu} - -- @param #MENU_BASE self - -- @param #string MenuText The text of the child menu. - -- @return #MENU_BASE - function MENU_BASE:GetMenu( MenuText ) - self:F( { self.Menus, MenuText } ) - return self.Menus[MenuText] - end - - --- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}. - -- @param #MENU_BASE self - -- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}. - -- @return #MENU_BASE - function MENU_BASE:SetRemoveParent( RemoveParent ) - self:F( { RemoveParent } ) - self.MenuRemoveParent = RemoveParent - return self - end - - - --- Sets a time stamp for later prevention of menu removal. - -- @param #MENU_BASE self - -- @param MenuTime - -- @return #MENU_BASE - function MENU_BASE:SetTime( MenuTime ) - self.MenuTime = MenuTime - return self - end - -end - -do -- MENU_COMMAND_BASE - - --- The MENU_COMMAND_BASE class - -- @type MENU_COMMAND_BASE - -- @field #function MenuCallHandler - -- @extends Core.Menu#MENU_BASE - MENU_COMMAND_BASE = { - ClassName = "MENU_COMMAND_BASE", - CommandMenuFunction = nil, - CommandMenuArgument = nil, - MenuCallHandler = nil, - } - - --- Constructor - -- @param #MENU_COMMAND_BASE - -- @return #MENU_COMMAND_BASE - function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments ) - - local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) - - self.CommandMenuFunction = CommandMenuFunction - self.MenuCallHandler = function( CommandMenuArguments ) - self.CommandMenuFunction( unpack( CommandMenuArguments ) ) - end - - return self - end - -end - - -do -- MENU_MISSION - - --- The MENU_MISSION class - -- @type MENU_MISSION - -- @extends Core.Menu#MENU_BASE - MENU_MISSION = { - ClassName = "MENU_MISSION" - } - - --- MENU_MISSION constructor. Creates a new MENU_MISSION object and creates the menu for a complete mission file. - -- @param #MENU_MISSION self - -- @param #string MenuText The text for the menu. - -- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other). - -- @return #MENU_MISSION - function MENU_MISSION:New( MenuText, ParentMenu ) - - local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) - - self:F( { MenuText, ParentMenu } ) - - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self.Menus = {} - - self:T( { MenuText } ) - - self.MenuPath = missionCommands.addSubMenu( MenuText, self.MenuParentPath ) - - self:T( { self.MenuPath } ) - - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self - end - - return self - end - - --- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept! - -- @param #MENU_MISSION self - -- @return #MENU_MISSION - function MENU_MISSION:RemoveSubMenus() - self:F( self.MenuPath ) - - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() - end - - end - - --- Removes the main menu and the sub menus recursively of this MENU_MISSION. - -- @param #MENU_MISSION self - -- @return #nil - function MENU_MISSION:Remove() - self:F( self.MenuPath ) - - self:RemoveSubMenus() - missionCommands.removeItem( self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil - end - - return nil - end - -end - -do -- MENU_MISSION_COMMAND - - --- The MENU_MISSION_COMMAND class - -- @type MENU_MISSION_COMMAND - -- @extends Core.Menu#MENU_COMMAND_BASE - MENU_MISSION_COMMAND = { - ClassName = "MENU_MISSION_COMMAND" - } - - --- MENU_MISSION constructor. Creates a new radio command item for a complete mission file, which can invoke a function with parameters. - -- @param #MENU_MISSION_COMMAND self - -- @param #string MenuText The text for the menu. - -- @param Menu#MENU_MISSION ParentMenu The parent menu. - -- @param CommandMenuFunction A function that is called when the menu key is pressed. - -- @param CommandMenuArgument An argument for the function. There can only be ONE argument given. So multiple arguments must be wrapped into a table. See the below example how to do this. - -- @return #MENU_MISSION_COMMAND self - function MENU_MISSION_COMMAND:New( MenuText, ParentMenu, CommandMenuFunction, ... ) - - local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) - - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self:T( { MenuText, CommandMenuFunction, arg } ) - - - self.MenuPath = missionCommands.addCommand( MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) - - ParentMenu.Menus[self.MenuPath] = self - - return self - end - - --- Removes a radio command item for a coalition - -- @param #MENU_MISSION_COMMAND self - -- @return #nil - function MENU_MISSION_COMMAND:Remove() - self:F( self.MenuPath ) - - missionCommands.removeItem( self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil - end - return nil - end - -end - - - -do -- MENU_COALITION - - --- The MENU_COALITION class - -- @type MENU_COALITION - -- @extends Core.Menu#MENU_BASE - -- @usage - -- -- This demo creates a menu structure for the planes within the red coalition. - -- -- To test, join the planes, then look at the other radio menus (Option F10). - -- -- Then switch planes and check if the menu is still there. - -- - -- local Plane1 = CLIENT:FindByName( "Plane 1" ) - -- local Plane2 = CLIENT:FindByName( "Plane 2" ) - -- - -- - -- -- This would create a menu for the red coalition under the main DCS "Others" menu. - -- local MenuCoalitionRed = MENU_COALITION:New( coalition.side.RED, "Manage Menus" ) - -- - -- - -- local function ShowStatus( StatusText, Coalition ) - -- - -- MESSAGE:New( Coalition, 15 ):ToRed() - -- Plane1:Message( StatusText, 15 ) - -- Plane2:Message( StatusText, 15 ) - -- end - -- - -- local MenuStatus -- Menu#MENU_COALITION - -- local MenuStatusShow -- Menu#MENU_COALITION_COMMAND - -- - -- local function RemoveStatusMenu() - -- MenuStatus:Remove() - -- end - -- - -- local function AddStatusMenu() - -- - -- -- This would create a menu for the red coalition under the MenuCoalitionRed menu object. - -- MenuStatus = MENU_COALITION:New( coalition.side.RED, "Status for Planes" ) - -- MenuStatusShow = MENU_COALITION_COMMAND:New( coalition.side.RED, "Show Status", MenuStatus, ShowStatus, "Status of planes is ok!", "Message to Red Coalition" ) - -- end - -- - -- local MenuAdd = MENU_COALITION_COMMAND:New( coalition.side.RED, "Add Status Menu", MenuCoalitionRed, AddStatusMenu ) - -- local MenuRemove = MENU_COALITION_COMMAND:New( coalition.side.RED, "Remove Status Menu", MenuCoalitionRed, RemoveStatusMenu ) - MENU_COALITION = { - ClassName = "MENU_COALITION" - } - - --- MENU_COALITION constructor. Creates a new MENU_COALITION object and creates the menu for a complete coalition. - -- @param #MENU_COALITION self - -- @param Dcs.DCSCoalition#coalition.side Coalition The coalition owning the menu. - -- @param #string MenuText The text for the menu. - -- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other). - -- @return #MENU_COALITION self - function MENU_COALITION:New( Coalition, MenuText, ParentMenu ) - - local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) - - self:F( { Coalition, MenuText, ParentMenu } ) - - self.Coalition = Coalition - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self.Menus = {} - - self:T( { MenuText } ) - - self.MenuPath = missionCommands.addSubMenuForCoalition( Coalition, MenuText, self.MenuParentPath ) - - self:T( { self.MenuPath } ) - - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self - end - - return self - end - - --- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept! - -- @param #MENU_COALITION self - -- @return #MENU_COALITION - function MENU_COALITION:RemoveSubMenus() - self:F( self.MenuPath ) - - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() - end - - end - - --- Removes the main menu and the sub menus recursively of this MENU_COALITION. - -- @param #MENU_COALITION self - -- @return #nil - function MENU_COALITION:Remove() - self:F( self.MenuPath ) - - self:RemoveSubMenus() - missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil - end - - return nil - end - -end - -do -- MENU_COALITION_COMMAND - - --- The MENU_COALITION_COMMAND class - -- @type MENU_COALITION_COMMAND - -- @extends Core.Menu#MENU_COMMAND_BASE - MENU_COALITION_COMMAND = { - ClassName = "MENU_COALITION_COMMAND" - } - - --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. - -- @param #MENU_COALITION_COMMAND self - -- @param Dcs.DCSCoalition#coalition.side Coalition The coalition owning the menu. - -- @param #string MenuText The text for the menu. - -- @param Menu#MENU_COALITION ParentMenu The parent menu. - -- @param CommandMenuFunction A function that is called when the menu key is pressed. - -- @param CommandMenuArgument An argument for the function. There can only be ONE argument given. So multiple arguments must be wrapped into a table. See the below example how to do this. - -- @return #MENU_COALITION_COMMAND - function MENU_COALITION_COMMAND:New( Coalition, MenuText, ParentMenu, CommandMenuFunction, ... ) - - local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) - - self.MenuCoalition = Coalition - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self:T( { MenuText, CommandMenuFunction, arg } ) - - - self.MenuPath = missionCommands.addCommandForCoalition( self.MenuCoalition, MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) - - ParentMenu.Menus[self.MenuPath] = self - - return self - end - - --- Removes a radio command item for a coalition - -- @param #MENU_COALITION_COMMAND self - -- @return #nil - function MENU_COALITION_COMMAND:Remove() - self:F( self.MenuPath ) - - missionCommands.removeItemForCoalition( self.MenuCoalition, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuPath] = nil - end - return nil - end - -end - -do -- MENU_CLIENT - - -- This local variable is used to cache the menus registered under clients. - -- Menus don't dissapear when clients are destroyed and restarted. - -- So every menu for a client created must be tracked so that program logic accidentally does not create - -- the same menus twice during initialization logic. - -- These menu classes are handling this logic with this variable. - local _MENUCLIENTS = {} - - --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. - -- @type MENU_CLIENT - -- @extends Core.Menu#MENU_BASE - -- @usage - -- -- This demo creates a menu structure for the two clients of planes. - -- -- Each client will receive a different menu structure. - -- -- To test, join the planes, then look at the other radio menus (Option F10). - -- -- Then switch planes and check if the menu is still there. - -- -- And play with the Add and Remove menu options. - -- - -- -- Note that in multi player, this will only work after the DCS clients bug is solved. - -- - -- local function ShowStatus( PlaneClient, StatusText, Coalition ) - -- - -- MESSAGE:New( Coalition, 15 ):ToRed() - -- PlaneClient:Message( StatusText, 15 ) - -- end - -- - -- local MenuStatus = {} - -- - -- local function RemoveStatusMenu( MenuClient ) - -- local MenuClientName = MenuClient:GetName() - -- MenuStatus[MenuClientName]:Remove() - -- end - -- - -- --- @param Wrapper.Client#CLIENT MenuClient - -- local function AddStatusMenu( MenuClient ) - -- local MenuClientName = MenuClient:GetName() - -- -- This would create a menu for the red coalition under the MenuCoalitionRed menu object. - -- MenuStatus[MenuClientName] = MENU_CLIENT:New( MenuClient, "Status for Planes" ) - -- MENU_CLIENT_COMMAND:New( MenuClient, "Show Status", MenuStatus[MenuClientName], ShowStatus, MenuClient, "Status of planes is ok!", "Message to Red Coalition" ) - -- end - -- - -- SCHEDULER:New( nil, - -- function() - -- local PlaneClient = CLIENT:FindByName( "Plane 1" ) - -- if PlaneClient and PlaneClient:IsAlive() then - -- local MenuManage = MENU_CLIENT:New( PlaneClient, "Manage Menus" ) - -- MENU_CLIENT_COMMAND:New( PlaneClient, "Add Status Menu Plane 1", MenuManage, AddStatusMenu, PlaneClient ) - -- MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 1", MenuManage, RemoveStatusMenu, PlaneClient ) - -- end - -- end, {}, 10, 10 ) - -- - -- SCHEDULER:New( nil, - -- function() - -- local PlaneClient = CLIENT:FindByName( "Plane 2" ) - -- if PlaneClient and PlaneClient:IsAlive() then - -- local MenuManage = MENU_CLIENT:New( PlaneClient, "Manage Menus" ) - -- MENU_CLIENT_COMMAND:New( PlaneClient, "Add Status Menu Plane 2", MenuManage, AddStatusMenu, PlaneClient ) - -- MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneClient ) - -- end - -- end, {}, 10, 10 ) - MENU_CLIENT = { - ClassName = "MENU_CLIENT" - } - - --- MENU_CLIENT constructor. Creates a new radio menu item for a client. - -- @param #MENU_CLIENT self - -- @param Wrapper.Client#CLIENT Client The Client owning the menu. - -- @param #string MenuText The text for the menu. - -- @param #table ParentMenu The parent menu. - -- @return #MENU_CLIENT self - function MENU_CLIENT:New( Client, MenuText, ParentMenu ) - - -- Arrange meta tables - local MenuParentPath = {} - if ParentMenu ~= nil then - MenuParentPath = ParentMenu.MenuPath - end - - local self = BASE:Inherit( self, MENU_BASE:New( MenuText, MenuParentPath ) ) - self:F( { Client, MenuText, ParentMenu } ) - - self.MenuClient = Client - self.MenuClientGroupID = Client:GetClientGroupID() - self.MenuParentPath = MenuParentPath - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self.Menus = {} - - if not _MENUCLIENTS[self.MenuClientGroupID] then - _MENUCLIENTS[self.MenuClientGroupID] = {} - end - - local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] - - self:T( { Client:GetClientGroupName(), MenuPath[table.concat(MenuParentPath)], MenuParentPath, MenuText } ) - - local MenuPathID = table.concat(MenuParentPath) .. "/" .. MenuText - if MenuPath[MenuPathID] then - missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] ) - end - - self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath ) - MenuPath[MenuPathID] = self.MenuPath - - self:T( { Client:GetClientGroupName(), self.MenuPath } ) - - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self - end - return self - end - - --- Removes the sub menus recursively of this @{#MENU_CLIENT}. - -- @param #MENU_CLIENT self - -- @return #MENU_CLIENT self - function MENU_CLIENT:RemoveSubMenus() - self:F( self.MenuPath ) - - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() - end - - end - - --- Removes the sub menus recursively of this MENU_CLIENT. - -- @param #MENU_CLIENT self - -- @return #nil - function MENU_CLIENT:Remove() - self:F( self.MenuPath ) - - self:RemoveSubMenus() - - if not _MENUCLIENTS[self.MenuClientGroupID] then - _MENUCLIENTS[self.MenuClientGroupID] = {} - end - - local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] - - if MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] then - MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] = nil - end - - missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), self.MenuPath ) - self.ParentMenu.Menus[self.MenuPath] = nil - return nil - end - - - --- The MENU_CLIENT_COMMAND class - -- @type MENU_CLIENT_COMMAND - -- @extends Core.Menu#MENU_COMMAND - MENU_CLIENT_COMMAND = { - ClassName = "MENU_CLIENT_COMMAND" - } - - --- MENU_CLIENT_COMMAND constructor. Creates a new radio command item for a client, which can invoke a function with parameters. - -- @param #MENU_CLIENT_COMMAND self - -- @param Wrapper.Client#CLIENT Client The Client owning the menu. - -- @param #string MenuText The text for the menu. - -- @param #MENU_BASE ParentMenu The parent menu. - -- @param CommandMenuFunction A function that is called when the menu key is pressed. - -- @return Menu#MENU_CLIENT_COMMAND self - function MENU_CLIENT_COMMAND:New( Client, MenuText, ParentMenu, CommandMenuFunction, ... ) - - -- Arrange meta tables - - local MenuParentPath = {} - if ParentMenu ~= nil then - MenuParentPath = ParentMenu.MenuPath - end - - local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, MenuParentPath, CommandMenuFunction, arg ) ) -- Menu#MENU_CLIENT_COMMAND - - self.MenuClient = Client - self.MenuClientGroupID = Client:GetClientGroupID() - self.MenuParentPath = MenuParentPath - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - if not _MENUCLIENTS[self.MenuClientGroupID] then - _MENUCLIENTS[self.MenuClientGroupID] = {} - end - - local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] - - self:T( { Client:GetClientGroupName(), MenuPath[table.concat(MenuParentPath)], MenuParentPath, MenuText, CommandMenuFunction, arg } ) - - local MenuPathID = table.concat(MenuParentPath) .. "/" .. MenuText - if MenuPath[MenuPathID] then - missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] ) - end - - self.MenuPath = missionCommands.addCommandForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath, self.MenuCallHandler, arg ) - MenuPath[MenuPathID] = self.MenuPath - - if ParentMenu and ParentMenu.Menus then - ParentMenu.Menus[self.MenuPath] = self - end - - return self - end - - --- Removes a menu structure for a client. - -- @param #MENU_CLIENT_COMMAND self - -- @return #nil - function MENU_CLIENT_COMMAND:Remove() - self:F( self.MenuPath ) - - if not _MENUCLIENTS[self.MenuClientGroupID] then - _MENUCLIENTS[self.MenuClientGroupID] = {} - end - - local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] - - if MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] then - MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] = nil - end - - missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), self.MenuPath ) - self.ParentMenu.Menus[self.MenuPath] = nil - return nil - end -end - ---- MENU_GROUP - -do - -- This local variable is used to cache the menus registered under groups. - -- Menus don't dissapear when groups for players are destroyed and restarted. - -- So every menu for a client created must be tracked so that program logic accidentally does not create. - -- the same menus twice during initialization logic. - -- These menu classes are handling this logic with this variable. - local _MENUGROUPS = {} - - --- The MENU_GROUP class - -- @type MENU_GROUP - -- @extends Core.Menu#MENU_BASE - -- @usage - -- -- This demo creates a menu structure for the two groups of planes. - -- -- Each group will receive a different menu structure. - -- -- To test, join the planes, then look at the other radio menus (Option F10). - -- -- Then switch planes and check if the menu is still there. - -- -- And play with the Add and Remove menu options. - -- - -- -- Note that in multi player, this will only work after the DCS groups bug is solved. - -- - -- local function ShowStatus( PlaneGroup, StatusText, Coalition ) - -- - -- MESSAGE:New( Coalition, 15 ):ToRed() - -- PlaneGroup:Message( StatusText, 15 ) - -- end - -- - -- local MenuStatus = {} - -- - -- local function RemoveStatusMenu( MenuGroup ) - -- local MenuGroupName = MenuGroup:GetName() - -- MenuStatus[MenuGroupName]:Remove() - -- end - -- - -- --- @param Wrapper.Group#GROUP MenuGroup - -- local function AddStatusMenu( MenuGroup ) - -- local MenuGroupName = MenuGroup:GetName() - -- -- This would create a menu for the red coalition under the MenuCoalitionRed menu object. - -- MenuStatus[MenuGroupName] = MENU_GROUP:New( MenuGroup, "Status for Planes" ) - -- MENU_GROUP_COMMAND:New( MenuGroup, "Show Status", MenuStatus[MenuGroupName], ShowStatus, MenuGroup, "Status of planes is ok!", "Message to Red Coalition" ) - -- end - -- - -- SCHEDULER:New( nil, - -- function() - -- local PlaneGroup = GROUP:FindByName( "Plane 1" ) - -- if PlaneGroup and PlaneGroup:IsAlive() then - -- local MenuManage = MENU_GROUP:New( PlaneGroup, "Manage Menus" ) - -- MENU_GROUP_COMMAND:New( PlaneGroup, "Add Status Menu Plane 1", MenuManage, AddStatusMenu, PlaneGroup ) - -- MENU_GROUP_COMMAND:New( PlaneGroup, "Remove Status Menu Plane 1", MenuManage, RemoveStatusMenu, PlaneGroup ) - -- end - -- end, {}, 10, 10 ) - -- - -- SCHEDULER:New( nil, - -- function() - -- local PlaneGroup = GROUP:FindByName( "Plane 2" ) - -- if PlaneGroup and PlaneGroup:IsAlive() then - -- local MenuManage = MENU_GROUP:New( PlaneGroup, "Manage Menus" ) - -- MENU_GROUP_COMMAND:New( PlaneGroup, "Add Status Menu Plane 2", MenuManage, AddStatusMenu, PlaneGroup ) - -- MENU_GROUP_COMMAND:New( PlaneGroup, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneGroup ) - -- end - -- end, {}, 10, 10 ) - -- - MENU_GROUP = { - ClassName = "MENU_GROUP" - } - - --- MENU_GROUP constructor. Creates a new radio menu item for a group. - -- @param #MENU_GROUP self - -- @param Wrapper.Group#GROUP MenuGroup The Group owning the menu. - -- @param #string MenuText The text for the menu. - -- @param #table ParentMenu The parent menu. - -- @return #MENU_GROUP self - function MENU_GROUP:New( MenuGroup, MenuText, ParentMenu ) - - -- Determine if the menu was not already created and already visible at the group. - -- If it is visible, then return the cached self, otherwise, create self and cache it. - - MenuGroup._Menus = MenuGroup._Menus or {} - local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText - if MenuGroup._Menus[Path] then - self = MenuGroup._Menus[Path] - else - self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) - MenuGroup._Menus[Path] = self - - self.MenuGroup = MenuGroup - self.Path = Path - self.MenuGroupID = MenuGroup:GetID() - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self:T( { "Adding Menu ", MenuText, self.MenuParentPath } ) - self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuGroupID, MenuText, self.MenuParentPath ) - - if self.ParentMenu and self.ParentMenu.Menus then - self.ParentMenu.Menus[MenuText] = self - self:F( { self.ParentMenu.Menus, MenuText } ) - self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 - end - end - - --self:F( { MenuGroup:GetName(), MenuText, ParentMenu.MenuPath } ) - - return self - end - - --- Removes the sub menus recursively of this MENU_GROUP. - -- @param #MENU_GROUP self - -- @param MenuTime - -- @return #MENU_GROUP self - function MENU_GROUP:RemoveSubMenus( MenuTime ) - self:F2( { self.MenuPath, MenuTime, self.MenuTime } ) - - self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } ) - for MenuText, Menu in pairs( self.Menus ) do - Menu:Remove( MenuTime ) - end - - end - - - --- Removes the main menu and sub menus recursively of this MENU_GROUP. - -- @param #MENU_GROUP self - -- @param MenuTime - -- @return #nil - function MENU_GROUP:Remove( MenuTime ) - self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - - self:RemoveSubMenus( MenuTime ) - - if not MenuTime or self.MenuTime ~= MenuTime then - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] - - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - if self.ParentMenu then - self.ParentMenu.Menus[self.MenuText] = nil - self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 - if self.ParentMenu.MenuCount == 0 then - if self.MenuRemoveParent == true then - self:T( "Removing Parent Menu " ) - self.ParentMenu:Remove() - end - end - end - self:T( { "Removing Group Menu:", self.MenuGroup:GetName(), self.MenuGroup._Menus[self.Path].Path } ) - self.MenuGroup._Menus[self.Path] = nil - self = nil - end - end - - return nil - end - - - --- The MENU_GROUP_COMMAND class - -- @type MENU_GROUP_COMMAND - -- @extends Core.Menu#MENU_BASE - MENU_GROUP_COMMAND = { - ClassName = "MENU_GROUP_COMMAND" - } - - --- Creates a new radio command item for a group - -- @param #MENU_GROUP_COMMAND self - -- @param Wrapper.Group#GROUP MenuGroup The Group owning the menu. - -- @param MenuText The text for the menu. - -- @param ParentMenu The parent menu. - -- @param CommandMenuFunction A function that is called when the menu key is pressed. - -- @param CommandMenuArgument An argument for the function. - -- @return #MENU_GROUP_COMMAND - function MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, CommandMenuFunction, ... ) - - MenuGroup._Menus = MenuGroup._Menus or {} - local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText - if MenuGroup._Menus[Path] then - self = MenuGroup._Menus[Path] - self:T( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } ) - else - self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) - MenuGroup._Menus[Path] = self - - self.Path = Path - self.MenuGroup = MenuGroup - self.MenuGroupID = MenuGroup:GetID() - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - self:T( { "Adding Group Command Menu:", MenuGroup:GetName(), MenuText, self.MenuParentPath } ) - self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler, arg ) - - if self.ParentMenu and self.ParentMenu.Menus then - self.ParentMenu.Menus[MenuText] = self - self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 - self:F( { ParentMenu.Menus, MenuText } ) - end - end - - return self - end - - --- Removes a menu structure for a group. - -- @param #MENU_GROUP_COMMAND self - -- @param MenuTime - -- @return #nil - function MENU_GROUP_COMMAND:Remove( MenuTime ) - self:F( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } ) - - if not MenuTime or self.MenuTime ~= MenuTime then - if self.MenuGroup._Menus[self.Path] then - self = self.MenuGroup._Menus[self.Path] - - missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) - self:T( { "Removing Group Command Menu:", self.MenuGroup:GetName(), self.MenuText, self.Path, self.MenuGroup._Menus[self.Path].Path } ) - - self.ParentMenu.Menus[self.MenuText] = nil - self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 - if self.ParentMenu.MenuCount == 0 then - if self.MenuRemoveParent == true then - self:T( "Removing Parent Menu " ) - self.ParentMenu:Remove() - end - end - - self.MenuGroup._Menus[self.Path] = nil - self = nil - end - end - - return nil - end - -end - ---- **Core** - ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**. --- --- ![Banner Image](..\Presentations\ZONE\Dia1.JPG) --- --- === --- --- There are essentially two core functions that zones accomodate: --- --- * Test if an object is within the zone boundaries. --- * Provide the zone behaviour. Some zones are static, while others are moveable. --- --- The object classes are using the zone classes to test the zone boundaries, which can take various forms: --- --- * Test if completely within the zone. --- * Test if partly within the zone (for @{Group#GROUP} objects). --- * Test if not in the zone. --- * Distance to the nearest intersecting point of the zone. --- * Distance to the center of the zone. --- * ... --- --- Each of these ZONE classes have a zone name, and specific parameters defining the zone type: --- --- * @{#ZONE_BASE}: The ZONE_BASE class defining the base for all other zone classes. --- * @{#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius. --- * @{#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor. --- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Unit#UNIT} with a radius. --- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. --- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- --- === --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-02-28: ZONE\_BASE:**IsVec2InZone()** replaces ZONE\_BASE:_IsPointVec2InZone()_. --- 2017-02-28: ZONE\_BASE:**IsVec3InZone()** replaces ZONE\_BASE:_IsPointVec3InZone()_. --- 2017-02-28: ZONE\_RADIUS:**IsVec2InZone()** replaces ZONE\_RADIUS:_IsPointVec2InZone()_. --- 2017-02-28: ZONE\_RADIUS:**IsVec3InZone()** replaces ZONE\_RADIUS:_IsPointVec3InZone()_. --- 2017-02-28: ZONE\_POLYGON:**IsVec2InZone()** replaces ZONE\_POLYGON:_IsPointVec2InZone()_. --- 2017-02-28: ZONE\_POLYGON:**IsVec3InZone()** replaces ZONE\_POLYGON:_IsPointVec3InZone()_. --- --- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec2()** added. --- --- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec3()** added. --- --- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec3( inner, outer )** added. --- --- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec2( inner, outer )** added. --- --- 2016-08-15: ZONE\_BASE:**GetName()** added. --- --- 2016-08-15: ZONE\_BASE:**SetZoneProbability( ZoneProbability )** added. --- --- 2016-08-15: ZONE\_BASE:**GetZoneProbability()** added. --- --- 2016-08-15: ZONE\_BASE:**GetZoneMaybe()** added. --- --- === --- --- @module Zone - - ---- The ZONE_BASE class --- @type ZONE_BASE --- @field #string ZoneName Name of the zone. --- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. --- @extends Core.Base#BASE - - ---- # 1) ZONE_BASE class, extends @{Base#BASE} --- --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- --- ## 1.1) Each zone has a name: --- --- * @{#ZONE_BASE.GetName}(): Returns the name of the zone. --- --- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: --- --- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone. --- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone. --- --- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones: --- --- * @{#ZONE_BASE.SetRandomizeProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% ) --- * @{#ZONE_BASE.GetRandomizeProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% ) --- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate. --- --- ## 1.4) A zone manages Vectors: --- --- * @{#ZONE_BASE.GetVec2}(): Returns the @{DCSTypes#Vec2} coordinate of the zone. --- * @{#ZONE_BASE.GetRandomVec2}(): Define a random @{DCSTypes#Vec2} within the zone. --- --- ## 1.5) A zone has a bounding square: --- --- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone. --- --- ## 1.6) A zone can be marked: --- --- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color. --- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color. --- --- === --- @field #ZONE_BASE ZONE_BASE -ZONE_BASE = { - ClassName = "ZONE_BASE", - ZoneName = "", - ZoneProbability = 1, - } - - ---- The ZONE_BASE.BoundingSquare --- @type ZONE_BASE.BoundingSquare --- @field Dcs.DCSTypes#Distance x1 The lower x coordinate (left down) --- @field Dcs.DCSTypes#Distance y1 The lower y coordinate (left down) --- @field Dcs.DCSTypes#Distance x2 The higher x coordinate (right up) --- @field Dcs.DCSTypes#Distance y2 The higher y coordinate (right up) - - ---- ZONE_BASE constructor --- @param #ZONE_BASE self --- @param #string ZoneName Name of the zone. --- @return #ZONE_BASE self -function ZONE_BASE:New( ZoneName ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( ZoneName ) - - self.ZoneName = ZoneName - - return self -end - ---- Returns the name of the zone. --- @param #ZONE_BASE self --- @return #string The name of the zone. -function ZONE_BASE:GetName() - self:F2() - - return self.ZoneName -end ---- Returns if a location is within the zone. --- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. --- @return #boolean true if the location is within the zone. -function ZONE_BASE:IsVec2InZone( Vec2 ) - self:F2( Vec2 ) - - return false -end - ---- Returns if a point is within the zone. --- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. --- @return #boolean true if the point is within the zone. -function ZONE_BASE:IsVec3InZone( Vec3 ) - self:F2( Vec3 ) - - local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) - - return InZone -end - ---- Returns the @{DCSTypes#Vec2} coordinate of the zone. --- @param #ZONE_BASE self --- @return #nil. -function ZONE_BASE:GetVec2() - self:F2( self.ZoneName ) - - return nil -end - ---- Returns a @{Point#POINT_VEC2} of the zone. --- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Core.Point#POINT_VEC2 The PointVec2 of the zone. -function ZONE_BASE:GetPointVec2() - self:F2( self.ZoneName ) - - local Vec2 = self:GetVec2() - - local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) - - self:T2( { PointVec2 } ) - - return PointVec2 -end - - ---- Returns the @{DCSTypes#Vec3} of the zone. --- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Dcs.DCSTypes#Vec3 The Vec3 of the zone. -function ZONE_BASE:GetVec3( Height ) - self:F2( self.ZoneName ) - - Height = Height or 0 - - local Vec2 = self:GetVec2() - - local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } - - self:T2( { Vec3 } ) - - return Vec3 -end - ---- Returns a @{Point#POINT_VEC3} of the zone. --- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Core.Point#POINT_VEC3 The PointVec3 of the zone. -function ZONE_BASE:GetPointVec3( Height ) - self:F2( self.ZoneName ) - - local Vec3 = self:GetVec3( Height ) - - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - - self:T2( { PointVec3 } ) - - return PointVec3 -end - - ---- Define a random @{DCSTypes#Vec2} within the zone. --- @param #ZONE_BASE self --- @return Dcs.DCSTypes#Vec2 The Vec2 coordinates. -function ZONE_BASE:GetRandomVec2() - return nil -end - ---- Define a random @{Point#POINT_VEC2} within the zone. --- @param #ZONE_BASE self --- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. -function ZONE_BASE:GetRandomPointVec2() - return nil -end - ---- Define a random @{Point#POINT_VEC3} within the zone. --- @param #ZONE_BASE self --- @return Core.Point#POINT_VEC3 The PointVec3 coordinates. -function ZONE_BASE:GetRandomPointVec3() - return nil -end - ---- Get the bounding square the zone. --- @param #ZONE_BASE self --- @return #nil The bounding square. -function ZONE_BASE:GetBoundingSquare() - --return { x1 = 0, y1 = 0, x2 = 0, y2 = 0 } - return nil -end - ---- Bound the zone boundaries with a tires. --- @param #ZONE_BASE self -function ZONE_BASE:BoundZone() - self:F2() - -end - ---- Smokes the zone boundaries in a color. --- @param #ZONE_BASE self --- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. -function ZONE_BASE:SmokeZone( SmokeColor ) - self:F2( SmokeColor ) - -end - ---- Set the randomization probability of a zone to be selected. --- @param #ZONE_BASE self --- @param ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. -function ZONE_BASE:SetZoneProbability( ZoneProbability ) - self:F2( ZoneProbability ) - - self.ZoneProbability = ZoneProbability or 1 - return self -end - ---- Get the randomization probability of a zone to be selected. --- @param #ZONE_BASE self --- @return #number A value between 0 and 1. 0 = 0% and 1 = 100% probability. -function ZONE_BASE:GetZoneProbability() - self:F2() - - return self.ZoneProbability -end - ---- Get the zone taking into account the randomization probability of a zone to be selected. --- @param #ZONE_BASE self --- @return #ZONE_BASE The zone is selected taking into account the randomization probability factor. --- @return #nil The zone is not selected taking into account the randomization probability factor. -function ZONE_BASE:GetZoneMaybe() - self:F2() - - local Randomization = math.random() - if Randomization <= self.ZoneProbability then - return self - else - return nil - end -end - - ---- The ZONE_RADIUS class, defined by a zone name, a location and a radius. --- @type ZONE_RADIUS --- @field Dcs.DCSTypes#Vec2 Vec2 The current location of the zone. --- @field Dcs.DCSTypes#Distance Radius The radius of the zone. --- @extends Core.Zone#ZONE_BASE - ---- # 2) @{Zone#ZONE_RADIUS} class, extends @{Zone#ZONE_BASE} --- --- The ZONE_RADIUS class defined by a zone name, a location and a radius. --- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties. --- --- ## 2.1) @{Zone#ZONE_RADIUS} constructor --- --- * @{#ZONE_RADIUS.New}(): Constructor. --- --- ## 2.2) Manage the radius of the zone --- --- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone. --- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone. --- --- ## 2.3) Manage the location of the zone --- --- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone. --- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone. --- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter. --- --- ## 2.4) Zone point randomization --- --- Various functions exist to find random points within the zone. --- --- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone. --- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone. --- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight. --- --- === --- --- @field #ZONE_RADIUS ZONE_RADIUS --- -ZONE_RADIUS = { - ClassName="ZONE_RADIUS", - } - ---- Constructor of @{#ZONE_RADIUS}, taking the zone name, the zone location and a radius. --- @param #ZONE_RADIUS self --- @param #string ZoneName Name of the zone. --- @param Dcs.DCSTypes#Vec2 Vec2 The location of the zone. --- @param Dcs.DCSTypes#Distance Radius The radius of the zone. --- @return #ZONE_RADIUS self -function ZONE_RADIUS:New( ZoneName, Vec2, Radius ) - local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS - self:F( { ZoneName, Vec2, Radius } ) - - self.Radius = Radius - self.Vec2 = Vec2 - - return self -end - ---- Bounds the zone with tires. --- @param #ZONE_RADIUS self --- @param #number Points (optional) The amount of points in the circle. --- @param #boolean UnBound If true the tyres will be destroyed. --- @return #ZONE_RADIUS self -function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) - - local Point = {} - local Vec2 = self:GetVec2() - - Points = Points and Points or 360 - - local Angle - local RadialBase = math.pi*2 - - -- - for Angle = 0, 360, (360 / Points ) do - local Radial = Angle * RadialBase / 360 - Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() - Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() - - local CountryName = _DATABASE.COUNTRY_NAME[CountryID] - - local Tire = { - ["country"] = CountryName, - ["category"] = "Fortifications", - ["canCargo"] = false, - ["shape_name"] = "H-tyre_B_WF", - ["type"] = "Black_Tyre_WF", - --["unitId"] = Angle + 10000, - ["y"] = Point.y, - ["x"] = Point.x, - ["name"] = string.format( "%s-Tire #%0d", self:GetName(), Angle ), - ["heading"] = 0, - } -- end of ["group"] - - local Group = coalition.addStaticObject( CountryID, Tire ) - if UnBound and UnBound == true then - Group:destroy() - end - end - - return self -end - - ---- Smokes the zone boundaries in a color. --- @param #ZONE_RADIUS self --- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. --- @param #number Points (optional) The amount of points in the circle. --- @return #ZONE_RADIUS self -function ZONE_RADIUS:SmokeZone( SmokeColor, Points ) - self:F2( SmokeColor ) - - local Point = {} - local Vec2 = self:GetVec2() - - Points = Points and Points or 360 - - local Angle - local RadialBase = math.pi*2 - - for Angle = 0, 360, 360 / Points do - local Radial = Angle * RadialBase / 360 - Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() - Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() - POINT_VEC2:New( Point.x, Point.y ):Smoke( SmokeColor ) - end - - return self -end - - ---- Flares the zone boundaries in a color. --- @param #ZONE_RADIUS self --- @param Utilities.Utils#FLARECOLOR FlareColor The flare color. --- @param #number Points (optional) The amount of points in the circle. --- @param Dcs.DCSTypes#Azimuth Azimuth (optional) Azimuth The azimuth of the flare. --- @return #ZONE_RADIUS self -function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth ) - self:F2( { FlareColor, Azimuth } ) - - local Point = {} - local Vec2 = self:GetVec2() - - Points = Points and Points or 360 - - local Angle - local RadialBase = math.pi*2 - - for Angle = 0, 360, 360 / Points do - local Radial = Angle * RadialBase / 360 - Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() - Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() - POINT_VEC2:New( Point.x, Point.y ):Flare( FlareColor, Azimuth ) - end - - return self -end - ---- Returns the radius of the zone. --- @param #ZONE_RADIUS self --- @return Dcs.DCSTypes#Distance The radius of the zone. -function ZONE_RADIUS:GetRadius() - self:F2( self.ZoneName ) - - self:T2( { self.Radius } ) - - return self.Radius -end - ---- Sets the radius of the zone. --- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Distance Radius The radius of the zone. --- @return Dcs.DCSTypes#Distance The radius of the zone. -function ZONE_RADIUS:SetRadius( Radius ) - self:F2( self.ZoneName ) - - self.Radius = Radius - self:T2( { self.Radius } ) - - return self.Radius -end - ---- Returns the @{DCSTypes#Vec2} of the zone. --- @param #ZONE_RADIUS self --- @return Dcs.DCSTypes#Vec2 The location of the zone. -function ZONE_RADIUS:GetVec2() - self:F2( self.ZoneName ) - - self:T2( { self.Vec2 } ) - - return self.Vec2 -end - ---- Sets the @{DCSTypes#Vec2} of the zone. --- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Vec2 Vec2 The new location of the zone. --- @return Dcs.DCSTypes#Vec2 The new location of the zone. -function ZONE_RADIUS:SetVec2( Vec2 ) - self:F2( self.ZoneName ) - - self.Vec2 = Vec2 - - self:T2( { self.Vec2 } ) - - return self.Vec2 -end - ---- Returns the @{DCSTypes#Vec3} of the ZONE_RADIUS. --- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Dcs.DCSTypes#Vec3 The point of the zone. -function ZONE_RADIUS:GetVec3( Height ) - self:F2( { self.ZoneName, Height } ) - - Height = Height or 0 - local Vec2 = self:GetVec2() - - local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } - - self:T2( { Vec3 } ) - - return Vec3 -end - - ---- Returns if a location is within the zone. --- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. --- @return #boolean true if the location is within the zone. -function ZONE_RADIUS:IsVec2InZone( Vec2 ) - self:F2( Vec2 ) - - local ZoneVec2 = self:GetVec2() - - if ZoneVec2 then - if (( Vec2.x - ZoneVec2.x )^2 + ( Vec2.y - ZoneVec2.y ) ^2 ) ^ 0.5 <= self:GetRadius() then - return true - end - end - - return false -end - ---- Returns if a point is within the zone. --- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. --- @return #boolean true if the point is within the zone. -function ZONE_RADIUS:IsVec3InZone( Vec3 ) - self:F2( Vec3 ) - - local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) - - return InZone -end - ---- Returns a random Vec2 location within the zone. --- @param #ZONE_RADIUS self --- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. --- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. --- @return Dcs.DCSTypes#Vec2 The random location within the zone. -function ZONE_RADIUS:GetRandomVec2( inner, outer ) - self:F( self.ZoneName, inner, outer ) - - local Point = {} - local Vec2 = self:GetVec2() - local _inner = inner or 0 - local _outer = outer or self:GetRadius() - - local angle = math.random() * math.pi * 2; - Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer); - Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer); - - self:T( { Point } ) - - return Point -end - ---- Returns a @{Point#POINT_VEC2} object reflecting a random 2D location within the zone. --- @param #ZONE_RADIUS self --- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. --- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. --- @return Core.Point#POINT_VEC2 The @{Point#POINT_VEC2} object reflecting the random 3D location within the zone. -function ZONE_RADIUS:GetRandomPointVec2( inner, outer ) - self:F( self.ZoneName, inner, outer ) - - local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() ) - - self:T3( { PointVec2 } ) - - return PointVec2 -end - ---- Returns a @{Point#POINT_VEC3} object reflecting a random 3D location within the zone. --- @param #ZONE_RADIUS self --- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. --- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. --- @return Core.Point#POINT_VEC3 The @{Point#POINT_VEC3} object reflecting the random 3D location within the zone. -function ZONE_RADIUS:GetRandomPointVec3( inner, outer ) - self:F( self.ZoneName, inner, outer ) - - local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2() ) - - self:T3( { PointVec3 } ) - - return PointVec3 -end - - - --- @type ZONE --- @extends Core.Zone#ZONE_RADIUS - - ---- # 3) ZONE class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE class, defined by the zone name as defined within the Mission Editor. --- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- @field #ZONE ZONE --- -ZONE = { - ClassName="ZONE", - } - - ---- Constructor of ZONE, taking the zone name. --- @param #ZONE self --- @param #string ZoneName The name of the zone as defined within the mission editor. --- @return #ZONE -function ZONE:New( ZoneName ) - - local Zone = trigger.misc.getZone( ZoneName ) - - if not Zone then - error( "Zone " .. ZoneName .. " does not exist." ) - return nil - end - - local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, { x = Zone.point.x, y = Zone.point.z }, Zone.radius ) ) - self:F( ZoneName ) - - self.Zone = Zone - - return self -end - - ---- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. --- @type ZONE_UNIT --- @field Wrapper.Unit#UNIT ZoneUNIT --- @extends Core.Zone#ZONE_RADIUS - ---- # 4) #ZONE_UNIT class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. --- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- @field #ZONE_UNIT ZONE_UNIT --- -ZONE_UNIT = { - ClassName="ZONE_UNIT", - } - ---- Constructor to create a ZONE_UNIT instance, taking the zone name, a zone unit and a radius. --- @param #ZONE_UNIT self --- @param #string ZoneName Name of the zone. --- @param Wrapper.Unit#UNIT ZoneUNIT The unit as the center of the zone. --- @param Dcs.DCSTypes#Distance Radius The radius of the zone. --- @return #ZONE_UNIT self -function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius ) - local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneUNIT:GetVec2(), Radius ) ) - self:F( { ZoneName, ZoneUNIT:GetVec2(), Radius } ) - - self.ZoneUNIT = ZoneUNIT - self.LastVec2 = ZoneUNIT:GetVec2() - - return self -end - - ---- Returns the current location of the @{Unit#UNIT}. --- @param #ZONE_UNIT self --- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Unit#UNIT}location. -function ZONE_UNIT:GetVec2() - self:F( self.ZoneName ) - - local ZoneVec2 = self.ZoneUNIT:GetVec2() - if ZoneVec2 then - self.LastVec2 = ZoneVec2 - return ZoneVec2 - else - return self.LastVec2 - end - - self:T( { ZoneVec2 } ) - - return nil -end - ---- Returns a random location within the zone. --- @param #ZONE_UNIT self --- @return Dcs.DCSTypes#Vec2 The random location within the zone. -function ZONE_UNIT:GetRandomVec2() - self:F( self.ZoneName ) - - local RandomVec2 = {} - local Vec2 = self.ZoneUNIT:GetVec2() - - if not Vec2 then - Vec2 = self.LastVec2 - end - - local angle = math.random() * math.pi*2; - RandomVec2.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius(); - RandomVec2.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius(); - - self:T( { RandomVec2 } ) - - return RandomVec2 -end - ---- Returns the @{DCSTypes#Vec3} of the ZONE_UNIT. --- @param #ZONE_UNIT self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Dcs.DCSTypes#Vec3 The point of the zone. -function ZONE_UNIT:GetVec3( Height ) - self:F2( self.ZoneName ) - - Height = Height or 0 - - local Vec2 = self:GetVec2() - - local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } - - self:T2( { Vec3 } ) - - return Vec3 -end - ---- @type ZONE_GROUP --- @field Wrapper.Group#GROUP ZoneGROUP --- @extends Core.Zone#ZONE_RADIUS - - ---- # 5) #ZONE_GROUP class, extends @{Zone#ZONE_RADIUS} --- --- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- @field #ZONE_GROUP ZONE_GROUP --- -ZONE_GROUP = { - ClassName="ZONE_GROUP", - } - ---- Constructor to create a ZONE_GROUP instance, taking the zone name, a zone @{Group#GROUP} and a radius. --- @param #ZONE_GROUP self --- @param #string ZoneName Name of the zone. --- @param Wrapper.Group#GROUP ZoneGROUP The @{Group} as the center of the zone. --- @param Dcs.DCSTypes#Distance Radius The radius of the zone. --- @return #ZONE_GROUP self -function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius ) - local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneGROUP:GetVec2(), Radius ) ) - self:F( { ZoneName, ZoneGROUP:GetVec2(), Radius } ) - - self.ZoneGROUP = ZoneGROUP - - return self -end - - ---- Returns the current location of the @{Group}. --- @param #ZONE_GROUP self --- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Group} location. -function ZONE_GROUP:GetVec2() - self:F( self.ZoneName ) - - local ZoneVec2 = self.ZoneGROUP:GetVec2() - - self:T( { ZoneVec2 } ) - - return ZoneVec2 -end - ---- Returns a random location within the zone of the @{Group}. --- @param #ZONE_GROUP self --- @return Dcs.DCSTypes#Vec2 The random location of the zone based on the @{Group} location. -function ZONE_GROUP:GetRandomVec2() - self:F( self.ZoneName ) - - local Point = {} - local Vec2 = self.ZoneGROUP:GetVec2() - - local angle = math.random() * math.pi*2; - Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius(); - Point.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius(); - - self:T( { Point } ) - - return Point -end - - - ---- @type ZONE_POLYGON_BASE --- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCSTypes#Vec2}. --- @extends Core.Zone#ZONE_BASE - - ---- # 6) ZONE_POLYGON_BASE class, extends @{Zone#ZONE_BASE} --- --- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- --- ## 6.1) Zone point randomization --- --- Various functions exist to find random points within the zone. --- --- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone. --- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone. --- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. --- --- === --- --- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE --- -ZONE_POLYGON_BASE = { - ClassName="ZONE_POLYGON_BASE", - } - ---- A points array. --- @type ZONE_POLYGON_BASE.ListVec2 --- @list - ---- Constructor to create a ZONE_POLYGON_BASE instance, taking the zone name and an array of @{DCSTypes#Vec2}, forming a polygon. --- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected. --- @param #ZONE_POLYGON_BASE self --- @param #string ZoneName Name of the zone. --- @param #ZONE_POLYGON_BASE.ListVec2 PointsArray An array of @{DCSTypes#Vec2}, forming a polygon.. --- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:New( ZoneName, PointsArray ) - local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) - self:F( { ZoneName, PointsArray } ) - - local i = 0 - - self.Polygon = {} - - for i = 1, #PointsArray do - self.Polygon[i] = {} - self.Polygon[i].x = PointsArray[i].x - self.Polygon[i].y = PointsArray[i].y - end - - return self -end - ---- Flush polygon coordinates as a table in DCS.log. --- @param #ZONE_POLYGON_BASE self --- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:Flush() - self:F2() - - self:E( { Polygon = self.ZoneName, Coordinates = self.Polygon } ) - - return self -end - ---- Smokes the zone boundaries in a color. --- @param #ZONE_POLYGON_BASE self --- @param #boolean UnBound If true, the tyres will be destroyed. --- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:BoundZone( UnBound ) - - local i - local j - local Segments = 10 - - i = 1 - j = #self.Polygon - - while i <= #self.Polygon do - self:T( { i, j, self.Polygon[i], self.Polygon[j] } ) - - local DeltaX = self.Polygon[j].x - self.Polygon[i].x - local DeltaY = self.Polygon[j].y - self.Polygon[i].y - - for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line. - local PointX = self.Polygon[i].x + ( Segment * DeltaX / Segments ) - local PointY = self.Polygon[i].y + ( Segment * DeltaY / Segments ) - local Tire = { - ["country"] = "USA", - ["category"] = "Fortifications", - ["canCargo"] = false, - ["shape_name"] = "H-tyre_B_WF", - ["type"] = "Black_Tyre_WF", - ["y"] = PointY, - ["x"] = PointX, - ["name"] = string.format( "%s-Tire #%0d", self:GetName(), ((i - 1) * Segments) + Segment ), - ["heading"] = 0, - } -- end of ["group"] - - local Group = coalition.addStaticObject( country.id.USA, Tire ) - if UnBound and UnBound == true then - Group:destroy() - end - - end - j = i - i = i + 1 - end - - return self -end - - - ---- Smokes the zone boundaries in a color. --- @param #ZONE_POLYGON_BASE self --- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. --- @return #ZONE_POLYGON_BASE self -function ZONE_POLYGON_BASE:SmokeZone( SmokeColor ) - self:F2( SmokeColor ) - - local i - local j - local Segments = 10 - - i = 1 - j = #self.Polygon - - while i <= #self.Polygon do - self:T( { i, j, self.Polygon[i], self.Polygon[j] } ) - - local DeltaX = self.Polygon[j].x - self.Polygon[i].x - local DeltaY = self.Polygon[j].y - self.Polygon[i].y - - for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line. - local PointX = self.Polygon[i].x + ( Segment * DeltaX / Segments ) - local PointY = self.Polygon[i].y + ( Segment * DeltaY / Segments ) - POINT_VEC2:New( PointX, PointY ):Smoke( SmokeColor ) - end - j = i - i = i + 1 - end - - return self -end - - - - ---- Returns if a location is within the zone. --- Source learned and taken from: https://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html --- @param #ZONE_POLYGON_BASE self --- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. --- @return #boolean true if the location is within the zone. -function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) - self:F2( Vec2 ) - - local Next - local Prev - local InPolygon = false - - Next = 1 - Prev = #self.Polygon - - while Next <= #self.Polygon do - self:T( { Next, Prev, self.Polygon[Next], self.Polygon[Prev] } ) - if ( ( ( self.Polygon[Next].y > Vec2.y ) ~= ( self.Polygon[Prev].y > Vec2.y ) ) and - ( Vec2.x < ( self.Polygon[Prev].x - self.Polygon[Next].x ) * ( Vec2.y - self.Polygon[Next].y ) / ( self.Polygon[Prev].y - self.Polygon[Next].y ) + self.Polygon[Next].x ) - ) then - InPolygon = not InPolygon - end - self:T2( { InPolygon = InPolygon } ) - Prev = Next - Next = Next + 1 - end - - self:T( { InPolygon = InPolygon } ) - return InPolygon -end - ---- Define a random @{DCSTypes#Vec2} within the zone. --- @param #ZONE_POLYGON_BASE self --- @return Dcs.DCSTypes#Vec2 The Vec2 coordinate. -function ZONE_POLYGON_BASE:GetRandomVec2() - self:F2() - - --- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way... - local Vec2Found = false - local Vec2 - local BS = self:GetBoundingSquare() - - self:T2( BS ) - - while Vec2Found == false do - Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) } - self:T2( Vec2 ) - if self:IsVec2InZone( Vec2 ) then - Vec2Found = true - end - end - - self:T2( Vec2 ) - - return Vec2 -end - ---- Return a @{Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. --- @param #ZONE_POLYGON_BASE self --- @return @{Point#POINT_VEC2} -function ZONE_POLYGON_BASE:GetRandomPointVec2() - self:F2() - - local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() ) - - self:T2( PointVec2 ) - - return PointVec2 -end - ---- Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. --- @param #ZONE_POLYGON_BASE self --- @return @{Point#POINT_VEC3} -function ZONE_POLYGON_BASE:GetRandomPointVec3() - self:F2() - - local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2() ) - - self:T2( PointVec3 ) - - return PointVec3 -end - - ---- Get the bounding square the zone. --- @param #ZONE_POLYGON_BASE self --- @return #ZONE_POLYGON_BASE.BoundingSquare The bounding square. -function ZONE_POLYGON_BASE:GetBoundingSquare() - - local x1 = self.Polygon[1].x - local y1 = self.Polygon[1].y - local x2 = self.Polygon[1].x - local y2 = self.Polygon[1].y - - for i = 2, #self.Polygon do - self:T2( { self.Polygon[i], x1, y1, x2, y2 } ) - x1 = ( x1 > self.Polygon[i].x ) and self.Polygon[i].x or x1 - x2 = ( x2 < self.Polygon[i].x ) and self.Polygon[i].x or x2 - y1 = ( y1 > self.Polygon[i].y ) and self.Polygon[i].y or y1 - y2 = ( y2 < self.Polygon[i].y ) and self.Polygon[i].y or y2 - - end - - return { x1 = x1, y1 = y1, x2 = x2, y2 = y2 } -end - - ---- @type ZONE_POLYGON --- @extends Core.Zone#ZONE_POLYGON_BASE - - ---- # 7) ZONE_POLYGON class, extends @{Zone#ZONE_POLYGON_BASE} --- --- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- --- === --- --- @field #ZONE_POLYGON ZONE_POLYGON --- -ZONE_POLYGON = { - ClassName="ZONE_POLYGON", - } - ---- Constructor to create a ZONE_POLYGON instance, taking the zone name and the name of the @{Group#GROUP} defined within the Mission Editor. --- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON. --- @param #ZONE_POLYGON self --- @param #string ZoneName Name of the zone. --- @param Wrapper.Group#GROUP ZoneGroup The GROUP waypoints as defined within the Mission Editor define the polygon shape. --- @return #ZONE_POLYGON self -function ZONE_POLYGON:New( ZoneName, ZoneGroup ) - - local GroupPoints = ZoneGroup:GetTaskRoute() - - local self = BASE:Inherit( self, ZONE_POLYGON_BASE:New( ZoneName, GroupPoints ) ) - self:F( { ZoneName, ZoneGroup, self.Polygon } ) - - return self -end - ---- This module contains the DATABASE class, managing the database of mission objects. --- --- ==== --- --- 1) @{#DATABASE} class, extends @{Base#BASE} --- =================================================== --- Mission designers can use the DATABASE class to refer to: --- --- * UNITS --- * GROUPS --- * CLIENTS --- * AIRPORTS --- * PLAYERSJOINED --- * PLAYERS --- --- On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor. --- --- Moose will automatically create one instance of the DATABASE class into the **global** object _DATABASE. --- Moose refers to _DATABASE within the framework extensively, but you can also refer to the _DATABASE object within your missions if required. --- --- 1.1) DATABASE iterators --- ----------------------- --- You can iterate the database with the available iterator methods. --- The iterator methods will walk the DATABASE set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the DATABASE: --- --- * @{#DATABASE.ForEachUnit}: Calls a function for each @{UNIT} it finds within the DATABASE. --- * @{#DATABASE.ForEachGroup}: Calls a function for each @{GROUP} it finds within the DATABASE. --- * @{#DATABASE.ForEachPlayer}: Calls a function for each alive player it finds within the DATABASE. --- * @{#DATABASE.ForEachPlayerJoined}: Calls a function for each joined player it finds within the DATABASE. --- * @{#DATABASE.ForEachClient}: Calls a function for each @{CLIENT} it finds within the DATABASE. --- * @{#DATABASE.ForEachClientAlive}: Calls a function for each alive @{CLIENT} it finds within the DATABASE. --- --- === --- --- @module Database --- @author FlightControl - ---- DATABASE class --- @type DATABASE --- @extends Core.Base#BASE -DATABASE = { - ClassName = "DATABASE", - Templates = { - Units = {}, - Groups = {}, - ClientsByName = {}, - ClientsByID = {}, - }, - UNITS = {}, - STATICS = {}, - GROUPS = {}, - PLAYERS = {}, - PLAYERSJOINED = {}, - CLIENTS = {}, - AIRBASES = {}, - COUNTRY_ID = {}, - COUNTRY_NAME = {}, - NavPoints = {}, -} - -local _DATABASECoalition = - { - [1] = "Red", - [2] = "Blue", - } - -local _DATABASECategory = - { - ["plane"] = Unit.Category.AIRPLANE, - ["helicopter"] = Unit.Category.HELICOPTER, - ["vehicle"] = Unit.Category.GROUND_UNIT, - ["ship"] = Unit.Category.SHIP, - ["static"] = Unit.Category.STRUCTURE, - } - - ---- Creates a new DATABASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #DATABASE self --- @return #DATABASE --- @usage --- -- Define a new DATABASE Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE. --- DBObject = DATABASE:New() -function DATABASE:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) - - self:SetEventPriority( 1 ) - - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - - -- Follow alive players and clients - self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) - - self:_RegisterTemplates() - self:_RegisterGroupsAndUnits() - self:_RegisterClients() - self:_RegisterStatics() - self:_RegisterPlayers() - self:_RegisterAirbases() - - return self -end - ---- Finds a Unit based on the Unit Name. --- @param #DATABASE self --- @param #string UnitName --- @return Wrapper.Unit#UNIT The found Unit. -function DATABASE:FindUnit( UnitName ) - - local UnitFound = self.UNITS[UnitName] - return UnitFound -end - - ---- Adds a Unit based on the Unit Name in the DATABASE. --- @param #DATABASE self -function DATABASE:AddUnit( DCSUnitName ) - - if not self.UNITS[DCSUnitName] then - local UnitRegister = UNIT:Register( DCSUnitName ) - self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName ) - end - - return self.UNITS[DCSUnitName] -end - - ---- Deletes a Unit from the DATABASE based on the Unit Name. --- @param #DATABASE self -function DATABASE:DeleteUnit( DCSUnitName ) - - --self.UNITS[DCSUnitName] = nil -end - ---- Adds a Static based on the Static Name in the DATABASE. --- @param #DATABASE self -function DATABASE:AddStatic( DCSStaticName ) - - if not self.STATICS[DCSStaticName] then - self.STATICS[DCSStaticName] = STATIC:Register( DCSStaticName ) - end -end - - ---- Deletes a Static from the DATABASE based on the Static Name. --- @param #DATABASE self -function DATABASE:DeleteStatic( DCSStaticName ) - - --self.STATICS[DCSStaticName] = nil -end - ---- Finds a STATIC based on the StaticName. --- @param #DATABASE self --- @param #string StaticName --- @return Wrapper.Static#STATIC The found STATIC. -function DATABASE:FindStatic( StaticName ) - - local StaticFound = self.STATICS[StaticName] - return StaticFound -end - ---- Adds a Airbase based on the Airbase Name in the DATABASE. --- @param #DATABASE self -function DATABASE:AddAirbase( DCSAirbaseName ) - - if not self.AIRBASES[DCSAirbaseName] then - self.AIRBASES[DCSAirbaseName] = AIRBASE:Register( DCSAirbaseName ) - end -end - - ---- Deletes a Airbase from the DATABASE based on the Airbase Name. --- @param #DATABASE self -function DATABASE:DeleteAirbase( DCSAirbaseName ) - - --self.AIRBASES[DCSAirbaseName] = nil -end - ---- Finds a AIRBASE based on the AirbaseName. --- @param #DATABASE self --- @param #string AirbaseName --- @return Wrapper.Airbase#AIRBASE The found AIRBASE. -function DATABASE:FindAirbase( AirbaseName ) - - local AirbaseFound = self.AIRBASES[AirbaseName] - return AirbaseFound -end - - ---- Finds a CLIENT based on the ClientName. --- @param #DATABASE self --- @param #string ClientName --- @return Wrapper.Client#CLIENT The found CLIENT. -function DATABASE:FindClient( ClientName ) - - local ClientFound = self.CLIENTS[ClientName] - return ClientFound -end - - ---- Adds a CLIENT based on the ClientName in the DATABASE. --- @param #DATABASE self -function DATABASE:AddClient( ClientName ) - - if not self.CLIENTS[ClientName] then - self.CLIENTS[ClientName] = CLIENT:Register( ClientName ) - end - - return self.CLIENTS[ClientName] -end - - ---- Finds a GROUP based on the GroupName. --- @param #DATABASE self --- @param #string GroupName --- @return Wrapper.Group#GROUP The found GROUP. -function DATABASE:FindGroup( GroupName ) - - local GroupFound = self.GROUPS[GroupName] - return GroupFound -end - - ---- Adds a GROUP based on the GroupName in the DATABASE. --- @param #DATABASE self -function DATABASE:AddGroup( GroupName ) - - if not self.GROUPS[GroupName] then - self:E( { "Add GROUP:", GroupName } ) - self.GROUPS[GroupName] = GROUP:Register( GroupName ) - end - - return self.GROUPS[GroupName] -end - ---- Adds a player based on the Player Name in the DATABASE. --- @param #DATABASE self -function DATABASE:AddPlayer( UnitName, PlayerName ) - - if PlayerName then - self:E( { "Add player for unit:", UnitName, PlayerName } ) - self.PLAYERS[PlayerName] = self:FindUnit( UnitName ) - self.PLAYERSJOINED[PlayerName] = PlayerName - end -end - ---- Deletes a player from the DATABASE based on the Player Name. --- @param #DATABASE self -function DATABASE:DeletePlayer( PlayerName ) - - if PlayerName then - self:E( { "Clean player:", PlayerName } ) - self.PLAYERS[PlayerName] = nil - end -end - - ---- Instantiate new Groups within the DCSRTE. --- This method expects EXACTLY the same structure as a structure within the ME, and needs 2 additional fields defined: --- SpawnCountryID, SpawnCategoryID --- This method is used by the SPAWN class. --- @param #DATABASE self --- @param #table SpawnTemplate --- @return #DATABASE self -function DATABASE:Spawn( SpawnTemplate ) - self:F( SpawnTemplate.name ) - - self:T( { SpawnTemplate.SpawnCountryID, SpawnTemplate.SpawnCategoryID } ) - - -- Copy the spawn variables of the template in temporary storage, nullify, and restore the spawn variables. - local SpawnCoalitionID = SpawnTemplate.CoalitionID - local SpawnCountryID = SpawnTemplate.CountryID - local SpawnCategoryID = SpawnTemplate.CategoryID - - -- Nullify - SpawnTemplate.CoalitionID = nil - SpawnTemplate.CountryID = nil - SpawnTemplate.CategoryID = nil - - self:_RegisterTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID ) - - self:T3( SpawnTemplate ) - coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate ) - - -- Restore - SpawnTemplate.CoalitionID = SpawnCoalitionID - SpawnTemplate.CountryID = SpawnCountryID - SpawnTemplate.CategoryID = SpawnCategoryID - - local SpawnGroup = self:AddGroup( SpawnTemplate.name ) - return SpawnGroup -end - ---- Set a status to a Group within the Database, this to check crossing events for example. -function DATABASE:SetStatusGroup( GroupName, Status ) - self:F2( Status ) - - self.Templates.Groups[GroupName].Status = Status -end - ---- Get a status to a Group within the Database, this to check crossing events for example. -function DATABASE:GetStatusGroup( GroupName ) - self:F2( Status ) - - if self.Templates.Groups[GroupName] then - return self.Templates.Groups[GroupName].Status - else - return "" - end -end - ---- Private method that registers new Group Templates within the DATABASE Object. --- @param #DATABASE self --- @param #table GroupTemplate --- @return #DATABASE self -function DATABASE:_RegisterTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID ) - - local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name) - - local TraceTable = {} - - if not self.Templates.Groups[GroupTemplateName] then - self.Templates.Groups[GroupTemplateName] = {} - self.Templates.Groups[GroupTemplateName].Status = nil - end - - -- Delete the spans from the route, it is not needed and takes memory. - if GroupTemplate.route and GroupTemplate.route.spans then - GroupTemplate.route.spans = nil - end - - GroupTemplate.CategoryID = CategoryID - GroupTemplate.CoalitionID = CoalitionID - GroupTemplate.CountryID = CountryID - - self.Templates.Groups[GroupTemplateName].GroupName = GroupTemplateName - self.Templates.Groups[GroupTemplateName].Template = GroupTemplate - self.Templates.Groups[GroupTemplateName].groupId = GroupTemplate.groupId - self.Templates.Groups[GroupTemplateName].UnitCount = #GroupTemplate.units - self.Templates.Groups[GroupTemplateName].Units = GroupTemplate.units - self.Templates.Groups[GroupTemplateName].CategoryID = CategoryID - self.Templates.Groups[GroupTemplateName].CoalitionID = CoalitionID - self.Templates.Groups[GroupTemplateName].CountryID = CountryID - - - TraceTable[#TraceTable+1] = "Group" - TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].GroupName - - TraceTable[#TraceTable+1] = "Coalition" - TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CoalitionID - TraceTable[#TraceTable+1] = "Category" - TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CategoryID - TraceTable[#TraceTable+1] = "Country" - TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CountryID - - TraceTable[#TraceTable+1] = "Units" - - for unit_num, UnitTemplate in pairs( GroupTemplate.units ) do - - UnitTemplate.name = env.getValueDictByKey(UnitTemplate.name) - - self.Templates.Units[UnitTemplate.name] = {} - self.Templates.Units[UnitTemplate.name].UnitName = UnitTemplate.name - self.Templates.Units[UnitTemplate.name].Template = UnitTemplate - self.Templates.Units[UnitTemplate.name].GroupName = GroupTemplateName - self.Templates.Units[UnitTemplate.name].GroupTemplate = GroupTemplate - self.Templates.Units[UnitTemplate.name].GroupId = GroupTemplate.groupId - self.Templates.Units[UnitTemplate.name].CategoryID = CategoryID - self.Templates.Units[UnitTemplate.name].CoalitionID = CoalitionID - self.Templates.Units[UnitTemplate.name].CountryID = CountryID - - if UnitTemplate.skill and (UnitTemplate.skill == "Client" or UnitTemplate.skill == "Player") then - self.Templates.ClientsByName[UnitTemplate.name] = UnitTemplate - self.Templates.ClientsByName[UnitTemplate.name].CategoryID = CategoryID - self.Templates.ClientsByName[UnitTemplate.name].CoalitionID = CoalitionID - self.Templates.ClientsByName[UnitTemplate.name].CountryID = CountryID - self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate - end - - TraceTable[#TraceTable+1] = self.Templates.Units[UnitTemplate.name].UnitName - end - - self:E( TraceTable ) -end - -function DATABASE:GetGroupTemplate( GroupName ) - local GroupTemplate = self.Templates.Groups[GroupName].Template - GroupTemplate.SpawnCoalitionID = self.Templates.Groups[GroupName].CoalitionID - GroupTemplate.SpawnCategoryID = self.Templates.Groups[GroupName].CategoryID - GroupTemplate.SpawnCountryID = self.Templates.Groups[GroupName].CountryID - return GroupTemplate -end - -function DATABASE:GetGroupNameFromUnitName( UnitName ) - return self.Templates.Units[UnitName].GroupName -end - -function DATABASE:GetGroupTemplateFromUnitName( UnitName ) - return self.Templates.Units[UnitName].GroupTemplate -end - -function DATABASE:GetCoalitionFromClientTemplate( ClientName ) - return self.Templates.ClientsByName[ClientName].CoalitionID -end - -function DATABASE:GetCategoryFromClientTemplate( ClientName ) - return self.Templates.ClientsByName[ClientName].CategoryID -end - -function DATABASE:GetCountryFromClientTemplate( ClientName ) - return self.Templates.ClientsByName[ClientName].CountryID -end - ---- Airbase - -function DATABASE:GetCoalitionFromAirbase( AirbaseName ) - return self.AIRBASES[AirbaseName]:GetCoalition() -end - -function DATABASE:GetCategoryFromAirbase( AirbaseName ) - return self.AIRBASES[AirbaseName]:GetCategory() -end - - - ---- Private method that registers all alive players in the mission. --- @param #DATABASE self --- @return #DATABASE self -function DATABASE:_RegisterPlayers() - - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - for UnitId, UnitData in pairs( CoalitionData ) do - self:T3( { "UnitData:", UnitData } ) - if UnitData and UnitData:isExist() then - local UnitName = UnitData:getName() - local PlayerName = UnitData:getPlayerName() - if not self.PLAYERS[PlayerName] then - self:E( { "Add player for unit:", UnitName, PlayerName } ) - self:AddPlayer( UnitName, PlayerName ) - end - end - end - end - - return self -end - - ---- Private method that registers all Groups and Units within in the mission. --- @param #DATABASE self --- @return #DATABASE self -function DATABASE:_RegisterGroupsAndUnits() - - local CoalitionsData = { GroupsRed = coalition.getGroups( coalition.side.RED ), GroupsBlue = coalition.getGroups( coalition.side.BLUE ) } - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - for DCSGroupId, DCSGroup in pairs( CoalitionData ) do - - if DCSGroup:isExist() then - local DCSGroupName = DCSGroup:getName() - - self:E( { "Register Group:", DCSGroupName } ) - self:AddGroup( DCSGroupName ) - - for DCSUnitId, DCSUnit in pairs( DCSGroup:getUnits() ) do - - local DCSUnitName = DCSUnit:getName() - self:E( { "Register Unit:", DCSUnitName } ) - self:AddUnit( DCSUnitName ) - end - else - self:E( { "Group does not exist: ", DCSGroup } ) - end - - end - end - - return self -end - ---- Private method that registers all Units of skill Client or Player within in the mission. --- @param #DATABASE self --- @return #DATABASE self -function DATABASE:_RegisterClients() - - for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do - self:E( { "Register Client:", ClientName } ) - self:AddClient( ClientName ) - end - - return self -end - ---- @param #DATABASE self -function DATABASE:_RegisterStatics() - - local CoalitionsData = { GroupsRed = coalition.getStaticObjects( coalition.side.RED ), GroupsBlue = coalition.getStaticObjects( coalition.side.BLUE ) } - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - for DCSStaticId, DCSStatic in pairs( CoalitionData ) do - - if DCSStatic:isExist() then - local DCSStaticName = DCSStatic:getName() - - self:E( { "Register Static:", DCSStaticName } ) - self:AddStatic( DCSStaticName ) - else - self:E( { "Static does not exist: ", DCSStatic } ) - end - end - end - - return self -end - ---- @param #DATABASE self -function DATABASE:_RegisterAirbases() - - local CoalitionsData = { AirbasesRed = coalition.getAirbases( coalition.side.RED ), AirbasesBlue = coalition.getAirbases( coalition.side.BLUE ), AirbasesNeutral = coalition.getAirbases( coalition.side.NEUTRAL ) } - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - for DCSAirbaseId, DCSAirbase in pairs( CoalitionData ) do - - local DCSAirbaseName = DCSAirbase:getName() - - self:E( { "Register Airbase:", DCSAirbaseName } ) - self:AddAirbase( DCSAirbaseName ) - end - end - - return self -end - - ---- Events - ---- Handles the OnBirth event for the alive units set. --- @param #DATABASE self --- @param Core.Event#EVENTDATA Event -function DATABASE:_EventOnBirth( Event ) - self:F2( { Event } ) - - if Event.IniDCSUnit then - if Event.IniObjectCategory == 3 then - self:AddStatic( Event.IniDCSUnitName ) - else - if Event.IniObjectCategory == 1 then - self:AddUnit( Event.IniDCSUnitName ) - self:AddGroup( Event.IniDCSGroupName ) - end - end - self:_EventOnPlayerEnterUnit( Event ) - end -end - - ---- Handles the OnDead or OnCrash event for alive units set. --- @param #DATABASE self --- @param Core.Event#EVENTDATA Event -function DATABASE:_EventOnDeadOrCrash( Event ) - self:F2( { Event } ) - - if Event.IniDCSUnit then - if Event.IniObjectCategory == 3 then - if self.STATICS[Event.IniDCSUnitName] then - self:DeleteStatic( Event.IniDCSUnitName ) - end - else - if Event.IniObjectCategory == 1 then - if self.UNITS[Event.IniDCSUnitName] then - self:DeleteUnit( Event.IniDCSUnitName ) - end - end - end - end -end - - ---- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied). --- @param #DATABASE self --- @param Core.Event#EVENTDATA Event -function DATABASE:_EventOnPlayerEnterUnit( Event ) - self:F2( { Event } ) - - if Event.IniUnit then - if Event.IniObjectCategory == 1 then - self:AddUnit( Event.IniDCSUnitName ) - self:AddGroup( Event.IniDCSGroupName ) - local PlayerName = Event.IniUnit:GetPlayerName() - if not self.PLAYERS[PlayerName] then - self:AddPlayer( Event.IniUnitName, PlayerName ) - end - end - end -end - - ---- Handles the OnPlayerLeaveUnit event to clean the active players table. --- @param #DATABASE self --- @param Core.Event#EVENTDATA Event -function DATABASE:_EventOnPlayerLeaveUnit( Event ) - self:F2( { Event } ) - - if Event.IniUnit then - if Event.IniObjectCategory == 1 then - local PlayerName = Event.IniUnit:GetPlayerName() - if self.PLAYERS[PlayerName] then - self:DeletePlayer( PlayerName ) - end - end - end -end - ---- Iterators - ---- Iterate the DATABASE and call an iterator function for the given set, providing the Object for each element within the set and optional parameters. --- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive player in the database. --- @return #DATABASE self -function DATABASE:ForEach( IteratorFunction, FinalizeFunction, arg, Set ) - self:F2( arg ) - - local function CoRoutine() - local Count = 0 - for ObjectID, Object in pairs( Set ) do - self:T2( Object ) - IteratorFunction( Object, unpack( arg ) ) - Count = Count + 1 --- if Count % 100 == 0 then --- coroutine.yield( false ) --- end - end - return true - end - --- local co = coroutine.create( CoRoutine ) - local co = CoRoutine - - local function Schedule() - --- local status, res = coroutine.resume( co ) - local status, res = co() - self:T3( { status, res } ) - - if status == false then - error( res ) - end - if res == false then - return true -- resume next time the loop - end - if FinalizeFunction then - FinalizeFunction( unpack( arg ) ) - end - return false - end - - local Scheduler = SCHEDULER:New( self, Schedule, {}, 0.001, 0.001, 0 ) - - return self -end - - ---- Iterate the DATABASE and call an iterator function for each **alive** UNIT, providing the UNIT and optional parameters. --- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the database. The function needs to accept a UNIT parameter. --- @return #DATABASE self -function DATABASE:ForEachUnit( IteratorFunction, FinalizeFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, FinalizeFunction, arg, self.UNITS ) - - return self -end - ---- Iterate the DATABASE and call an iterator function for each **alive** GROUP, providing the GROUP and optional parameters. --- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the database. The function needs to accept a GROUP parameter. --- @return #DATABASE self -function DATABASE:ForEachGroup( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.GROUPS ) - - return self -end - - ---- Iterate the DATABASE and call an iterator function for each **ALIVE** player, providing the player name and optional parameters. --- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an player in the database. The function needs to accept the player name. --- @return #DATABASE self -function DATABASE:ForEachPlayer( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.PLAYERS ) - - return self -end - - ---- Iterate the DATABASE and call an iterator function for each player who has joined the mission, providing the Unit of the player and optional parameters. --- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is was a player in the database. The function needs to accept a UNIT parameter. --- @return #DATABASE self -function DATABASE:ForEachPlayerJoined( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.PLAYERSJOINED ) - - return self -end - ---- Iterate the DATABASE and call an iterator function for each CLIENT, providing the CLIENT to the function and optional parameters. --- @param #DATABASE self --- @param #function IteratorFunction The function that will be called when there is an alive player in the database. The function needs to accept a CLIENT parameter. --- @return #DATABASE self -function DATABASE:ForEachClient( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.CLIENTS ) - - return self -end - - -function DATABASE:_RegisterTemplates() - self:F2() - - self.Navpoints = {} - self.UNITS = {} - --Build routines.db.units and self.Navpoints - for CoalitionName, coa_data in pairs(env.mission.coalition) do - - if (CoalitionName == 'red' or CoalitionName == 'blue') and type(coa_data) == 'table' then - --self.Units[coa_name] = {} - - local CoalitionSide = coalition.side[string.upper(CoalitionName)] - - ---------------------------------------------- - -- build nav points DB - self.Navpoints[CoalitionName] = {} - if coa_data.nav_points then --navpoints - for nav_ind, nav_data in pairs(coa_data.nav_points) do - - if type(nav_data) == 'table' then - self.Navpoints[CoalitionName][nav_ind] = routines.utils.deepCopy(nav_data) - - self.Navpoints[CoalitionName][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory. - self.Navpoints[CoalitionName][nav_ind]['point'] = {} -- point is used by SSE, support it. - self.Navpoints[CoalitionName][nav_ind]['point']['x'] = nav_data.x - self.Navpoints[CoalitionName][nav_ind]['point']['y'] = 0 - self.Navpoints[CoalitionName][nav_ind]['point']['z'] = nav_data.y - end - end - end - ------------------------------------------------- - if coa_data.country then --there is a country table - for cntry_id, cntry_data in pairs(coa_data.country) do - - local CountryName = string.upper(cntry_data.name) - local CountryID = cntry_data.id - - self.COUNTRY_ID[CountryName] = CountryID - self.COUNTRY_NAME[CountryID] = CountryName - - --self.Units[coa_name][countryName] = {} - --self.Units[coa_name][countryName]["countryId"] = cntry_data.id - - if type(cntry_data) == 'table' then --just making sure - - for obj_type_name, obj_type_data in pairs(cntry_data) do - - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then --should be an unncessary check - - local CategoryName = obj_type_name - - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - - --self.Units[coa_name][countryName][category] = {} - - for group_num, GroupTemplate in pairs(obj_type_data.group) do - - if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group - self:_RegisterTemplate( - GroupTemplate, - CoalitionSide, - _DATABASECategory[string.lower(CategoryName)], - CountryID - ) - end --if GroupTemplate and GroupTemplate.units then - end --for group_num, GroupTemplate in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do - end --if type(cntry_data) == 'table' then - end --for cntry_id, cntry_data in pairs(coa_data.country) do - end --if coa_data.country then --there is a country table - end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then - end --for coa_name, coa_data in pairs(mission.coalition) do - - return self -end - - - - ---- **Core** - SET_ classes define **collections** of objects to perform **bulk actions** and logically **group** objects. --- --- ![Banner Image](..\Presentations\SET\Dia1.JPG) --- --- === --- --- SET_ classes group objects of the same type into a collection, which is either: --- --- * Manually managed using the **:Add...()** or **:Remove...()** methods. The initial SET can be filtered with the **@{#SET_BASE.FilterOnce}()** method --- * Dynamically updated when new objects are created or objects are destroyed using the **@{#SET_BASE.FilterStart}()** method. --- --- Various types of SET_ classes are available: --- --- * @{#SET_UNIT}: Defines a colleciton of @{Unit}s filtered by filter criteria. --- * @{#SET_GROUP}: Defines a collection of @{Group}s filtered by filter criteria. --- * @{#SET_CLIENT}: Defines a collection of @{Client}s filterd by filter criteria. --- * @{#SET_AIRBASE}: Defines a collection of @{Airbase}s filtered by filter criteria. --- --- These classes are derived from @{#SET_BASE}, which contains the main methods to manage SETs. --- --- A multitude of other methods are available in SET_ classes that allow to: --- --- * Validate the presence of objects in the SET. --- * Trigger events when objects in the SET change a zone presence. --- --- ### Authors: --- --- * FlightControl : Design & Programming --- --- ### Contributions: --- --- --- @module Set - - ---- @type SET_BASE --- @field #table Filter --- @field #table Set --- @field #table List --- @field Core.Scheduler#SCHEDULER CallScheduler --- @extends Core.Base#BASE - - ---- # 1) SET_BASE class, extends @{Base#BASE} --- The @{Set#SET_BASE} class defines the core functions that define a collection of objects. --- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. --- In this way, large loops can be done while not blocking the simulator main processing loop. --- The default **"yield interval"** is after 10 objects processed. --- The default **"time interval"** is after 0.001 seconds. --- --- ## 1.1) Add or remove objects from the SET --- --- Some key core functions are @{Set#SET_BASE.Add} and @{Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. --- --- ## 1.2) Define the SET iterator **"yield interval"** and the **"time interval"** --- --- Modify the iterator intervals with the @{Set#SET_BASE.SetInteratorIntervals} method. --- You can set the **"yield interval"**, and the **"time interval"**. (See above). --- --- @field #SET_BASE SET_BASE -SET_BASE = { - ClassName = "SET_BASE", - Filter = {}, - Set = {}, - List = {}, - Index = {}, -} - - ---- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #SET_BASE self --- @return #SET_BASE --- @usage --- -- Define a new SET_BASE Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE. --- DBObject = SET_BASE:New() -function SET_BASE:New( Database ) - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) -- Core.Set#SET_BASE - - self.Database = Database - - self.YieldInterval = 10 - self.TimeInterval = 0.001 - - self.Set = {} - - self.List = {} - self.List.__index = self.List - self.List = setmetatable( { Count = 0 }, self.List ) - - self.Index = {} - - self.CallScheduler = SCHEDULER:New( self ) - - self:SetEventPriority( 2 ) - - return self -end - ---- Finds an @{Base#BASE} object based on the object Name. --- @param #SET_BASE self --- @param #string ObjectName --- @return Core.Base#BASE The Object found. -function SET_BASE:_Find( ObjectName ) - - local ObjectFound = self.Set[ObjectName] - return ObjectFound -end - - ---- Gets the Set. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:GetSet() - self:F2() - - return self.Set -end - ---- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using a given ObjectName as the index. --- @param #SET_BASE self --- @param #string ObjectName --- @param Core.Base#BASE Object --- @return Core.Base#BASE The added BASE Object. -function SET_BASE:Add( ObjectName, Object ) - self:F2( ObjectName ) - - local t = { _ = Object } - - if self.List.last then - self.List.last._next = t - t._prev = self.List.last - self.List.last = t - else - -- this is the first node - self.List.first = t - self.List.last = t - end - - self.List.Count = self.List.Count + 1 - - self.Set[ObjectName] = t._ - - table.insert( self.Index, ObjectName ) - -end - ---- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index. --- @param #SET_BASE self --- @param Wrapper.Object#OBJECT Object --- @return Core.Base#BASE The added BASE Object. -function SET_BASE:AddObject( Object ) - self:F2( Object.ObjectName ) - - self:T( Object.UnitName ) - self:T( Object.ObjectName ) - self:Add( Object.ObjectName, Object ) - -end - - - ---- Removes a @{Base#BASE} object from the @{Set#SET_BASE} and derived classes, based on the Object Name. --- @param #SET_BASE self --- @param #string ObjectName -function SET_BASE:Remove( ObjectName ) - self:F( ObjectName ) - - local t = self.Set[ObjectName] - - self:E( { ObjectName, t } ) - - if t then - if t._next then - if t._prev then - t._next._prev = t._prev - t._prev._next = t._next - else - -- this was the first node - t._next._prev = nil - self.List._first = t._next - end - elseif t._prev then - -- this was the last node - t._prev._next = nil - self.List._last = t._prev - else - -- this was the only node - self.List._first = nil - self.List._last = nil - end - - t._next = nil - t._prev = nil - self.List.Count = self.List.Count - 1 - - for Index, Key in ipairs( self.Index ) do - if Key == ObjectName then - table.remove( self.Index, Index ) - break - end - end - - self.Set[ObjectName] = nil - - end - -end - ---- Gets a @{Base#BASE} object from the @{Set#SET_BASE} and derived classes, based on the Object Name. --- @param #SET_BASE self --- @param #string ObjectName --- @return Core.Base#BASE -function SET_BASE:Get( ObjectName ) - self:F( ObjectName ) - - local t = self.Set[ObjectName] - - self:T3( { ObjectName, t } ) - - return t - -end - ---- Gets the first object from the @{Set#SET_BASE} and derived classes. --- @param #SET_BASE self --- @return Core.Base#BASE -function SET_BASE:GetFirst() - self:F() - - local ObjectName = self.Index[1] - local FirstObject = self.Set[ObjectName] - self:T3( { FirstObject } ) - return FirstObject -end - ---- Gets the last object from the @{Set#SET_BASE} and derived classes. --- @param #SET_BASE self --- @return Core.Base#BASE -function SET_BASE:GetLast() - self:F() - - local ObjectName = self.Index[#self.Index] - local LastObject = self.Set[ObjectName] - self:T3( { LastObject } ) - return LastObject -end - ---- Gets a random object from the @{Set#SET_BASE} and derived classes. --- @param #SET_BASE self --- @return Core.Base#BASE -function SET_BASE:GetRandom() - self:F() - - local RandomItem = self.Set[self.Index[math.random(#self.Index)]] - - self:T3( { RandomItem } ) - - return RandomItem -end - - ---- Retrieves the amount of objects in the @{Set#SET_BASE} and derived classes. --- @param #SET_BASE self --- @return #number Count -function SET_BASE:Count() - - return #self.Index or 0 -end - - - ---- Copies the Filter criteria from a given Set (for rebuilding a new Set based on an existing Set). --- @param #SET_BASE self --- @param #SET_BASE BaseSet --- @return #SET_BASE -function SET_BASE:SetDatabase( BaseSet ) - - -- Copy the filter criteria of the BaseSet - local OtherFilter = routines.utils.deepCopy( BaseSet.Filter ) - self.Filter = OtherFilter - - -- Now base the new Set on the BaseSet - self.Database = BaseSet:GetSet() - return self -end - - - ---- Define the SET iterator **"yield interval"** and the **"time interval"**. --- @param #SET_BASE self --- @param #number YieldInterval Sets the frequency when the iterator loop will yield after the number of objects processed. The default frequency is 10 objects processed. --- @param #number TimeInterval Sets the time in seconds when the main logic will resume the iterator loop. The default time is 0.001 seconds. --- @return #SET_BASE self -function SET_BASE:SetIteratorIntervals( YieldInterval, TimeInterval ) - - self.YieldInterval = YieldInterval - self.TimeInterval = TimeInterval - - return self -end - - ---- Filters for the defined collection. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:FilterOnce() - - for ObjectName, Object in pairs( self.Database ) do - - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) - end - end - - return self -end - ---- Starts the filtering for the defined collection. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:_FilterStart() - - for ObjectName, Object in pairs( self.Database ) do - - if self:IsIncludeObject( Object ) then - self:E( { "Adding Object:", ObjectName } ) - self:Add( ObjectName, Object ) - end - end - - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - - -- Follow alive players and clients - self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) - - - return self -end - ---- Stops the filtering for the defined collection. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:FilterStop() - - self:UnHandleEvent( EVENTS.Birth ) - self:UnHandleEvent( EVENTS.Dead ) - self:UnHandleEvent( EVENTS.Crash ) - - return self -end - ---- Iterate the SET_BASE while identifying the nearest object from a @{Point#POINT_VEC2}. --- @param #SET_BASE self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest object in the set. --- @return Core.Base#BASE The closest object. -function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 ) - self:F2( PointVec2 ) - - local NearestObject = nil - local ClosestDistance = nil - - for ObjectID, ObjectData in pairs( self.Set ) do - if NearestObject == nil then - NearestObject = ObjectData - ClosestDistance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) - else - local Distance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) - if Distance < ClosestDistance then - NearestObject = ObjectData - ClosestDistance = Distance - end - end - end - - return NearestObject -end - - - ------ Private method that registers all alive players in the mission. ----- @param #SET_BASE self ----- @return #SET_BASE self ---function SET_BASE:_RegisterPlayers() --- --- local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } --- for CoalitionId, CoalitionData in pairs( CoalitionsData ) do --- for UnitId, UnitData in pairs( CoalitionData ) do --- self:T3( { "UnitData:", UnitData } ) --- if UnitData and UnitData:isExist() then --- local UnitName = UnitData:getName() --- if not self.PlayersAlive[UnitName] then --- self:E( { "Add player for unit:", UnitName, UnitData:getPlayerName() } ) --- self.PlayersAlive[UnitName] = UnitData:getPlayerName() --- end --- end --- end --- end --- --- return self ---end - ---- Events - ---- Handles the OnBirth event for the Set. --- @param #SET_BASE self --- @param Core.Event#EVENTDATA Event -function SET_BASE:_EventOnBirth( Event ) - self:F3( { Event } ) - - if Event.IniDCSUnit then - local ObjectName, Object = self:AddInDatabase( Event ) - self:T3( ObjectName, Object ) - if Object and self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) - --self:_EventOnPlayerEnterUnit( Event ) - end - end -end - ---- Handles the OnDead or OnCrash event for alive units set. --- @param #SET_BASE self --- @param Core.Event#EVENTDATA Event -function SET_BASE:_EventOnDeadOrCrash( Event ) - self:F3( { Event } ) - - if Event.IniDCSUnit then - local ObjectName, Object = self:FindInDatabase( Event ) - if ObjectName and Object ~= nil then - self:Remove( ObjectName ) - end - end -end - ---- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied). --- @param #SET_BASE self --- @param Core.Event#EVENTDATA Event -function SET_BASE:_EventOnPlayerEnterUnit( Event ) - self:F3( { Event } ) - - if Event.IniDCSUnit then - local ObjectName, Object = self:AddInDatabase( Event ) - self:T3( ObjectName, Object ) - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) - --self:_EventOnPlayerEnterUnit( Event ) - end - end -end - ---- Handles the OnPlayerLeaveUnit event to clean the active players table. --- @param #SET_BASE self --- @param Core.Event#EVENTDATA Event -function SET_BASE:_EventOnPlayerLeaveUnit( Event ) - self:F3( { Event } ) - - local ObjectName = Event.IniDCSUnit - if Event.IniDCSUnit then - if Event.IniDCSGroup then - local GroupUnits = Event.IniDCSGroup:getUnits() - local PlayerCount = 0 - for _, DCSUnit in pairs( GroupUnits ) do - if DCSUnit ~= Event.IniDCSUnit then - if DCSUnit:getPlayer() ~= nil then - PlayerCount = PlayerCount + 1 - end - end - end - self:E(PlayerCount) - if PlayerCount == 0 then - self:Remove( Event.IniDCSGroupName ) - end - end - end -end - --- Iterators - ---- Iterate the SET_BASE and derived classes and call an iterator function for the given SET_BASE, providing the Object for each element within the set and optional parameters. --- @param #SET_BASE self --- @param #function IteratorFunction The function that will be called. --- @return #SET_BASE self -function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArguments ) - self:F3( arg ) - - Set = Set or self:GetSet() - arg = arg or {} - - local function CoRoutine() - local Count = 0 - for ObjectID, ObjectData in pairs( Set ) do - local Object = ObjectData - self:T3( Object ) - if Function then - if Function( unpack( FunctionArguments ), Object ) == true then - IteratorFunction( Object, unpack( arg ) ) - end - else - IteratorFunction( Object, unpack( arg ) ) - end - Count = Count + 1 --- if Count % self.YieldInterval == 0 then --- coroutine.yield( false ) --- end - end - return true - end - --- local co = coroutine.create( CoRoutine ) - local co = CoRoutine - - local function Schedule() - --- local status, res = coroutine.resume( co ) - local status, res = co() - self:T3( { status, res } ) - - if status == false then - error( res ) - end - if res == false then - return true -- resume next time the loop - end - - return false - end - - --self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) - Schedule() - - return self -end - - ------ Iterate the SET_BASE and call an interator function for each **alive** unit, providing the Unit and optional parameters. ----- @param #SET_BASE self ----- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter. ----- @return #SET_BASE self ---function SET_BASE:ForEachDCSUnitAlive( IteratorFunction, ... ) --- self:F3( arg ) --- --- self:ForEach( IteratorFunction, arg, self.DCSUnitsAlive ) --- --- return self ---end --- ------ Iterate the SET_BASE and call an interator function for each **alive** player, providing the Unit of the player and optional parameters. ----- @param #SET_BASE self ----- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter. ----- @return #SET_BASE self ---function SET_BASE:ForEachPlayer( IteratorFunction, ... ) --- self:F3( arg ) --- --- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) --- --- return self ---end --- --- ------ Iterate the SET_BASE and call an interator function for each client, providing the Client to the function and optional parameters. ----- @param #SET_BASE self ----- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter. ----- @return #SET_BASE self ---function SET_BASE:ForEachClient( IteratorFunction, ... ) --- self:F3( arg ) --- --- self:ForEach( IteratorFunction, arg, self.Clients ) --- --- return self ---end - - ---- Decides whether to include the Object --- @param #SET_BASE self --- @param #table Object --- @return #SET_BASE self -function SET_BASE:IsIncludeObject( Object ) - self:F3( Object ) - - return true -end - ---- Flushes the current SET_BASE contents in the log ... (for debugging reasons). --- @param #SET_BASE self --- @return #string A string with the names of the objects. -function SET_BASE:Flush() - self:F3() - - local ObjectNames = "" - for ObjectName, Object in pairs( self.Set ) do - ObjectNames = ObjectNames .. ObjectName .. ", " - end - self:E( { "Objects in Set:", ObjectNames } ) - - return ObjectNames -end - - ---- @type SET_GROUP --- @extends Core.Set#SET_BASE - ---- # 2) SET_GROUP class, extends @{Set#SET_BASE} --- --- Mission designers can use the @{Set#SET_GROUP} class to build sets of groups belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Starting with certain prefix strings. --- --- ## 2.1) SET_GROUP constructor --- --- Create a new SET_GROUP object with the @{#SET_GROUP.New} method: --- --- * @{#SET_GROUP.New}: Creates a new SET_GROUP object. --- --- ## 2.2) Add or Remove GROUP(s) from SET_GROUP --- --- GROUPS can be added and removed using the @{Set#SET_GROUP.AddGroupsByName} and @{Set#SET_GROUP.RemoveGroupsByName} respectively. --- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. --- --- ## 2.3) SET_GROUP filter criteria --- --- You can set filter criteria to define the set of groups within the SET_GROUP. --- Filter criteria are defined by: --- --- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s). --- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). --- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies). --- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s). --- --- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: --- --- * @{#SET_GROUP.FilterStart}: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects **dynamically**. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Zone#ZONE}. --- --- ## 2.4) SET_GROUP iterators --- --- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. --- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_GROUP: --- --- * @{#SET_GROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_GROUP. --- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupPartlyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- --- === --- @field #SET_GROUP SET_GROUP -SET_GROUP = { - ClassName = "SET_GROUP", - Filter = { - Coalitions = nil, - Categories = nil, - Countries = nil, - GroupPrefixes = nil, - }, - FilterMeta = { - Coalitions = { - red = coalition.side.RED, - blue = coalition.side.BLUE, - neutral = coalition.side.NEUTRAL, - }, - Categories = { - plane = Group.Category.AIRPLANE, - helicopter = Group.Category.HELICOPTER, - ground = Group.Category.GROUND_UNIT, - ship = Group.Category.SHIP, - structure = Group.Category.STRUCTURE, - }, - }, -} - - ---- Creates a new SET_GROUP object, building a set of groups belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #SET_GROUP self --- @return #SET_GROUP --- @usage --- -- Define a new SET_GROUP Object. This DBObject will contain a reference to all alive GROUPS. --- DBObject = SET_GROUP:New() -function SET_GROUP:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.GROUPS ) ) - - return self -end - ---- Add GROUP(s) to SET_GROUP. --- @param Core.Set#SET_GROUP self --- @param #string AddGroupNames A single name or an array of GROUP names. --- @return self -function SET_GROUP:AddGroupsByName( AddGroupNames ) - - local AddGroupNamesArray = ( type( AddGroupNames ) == "table" ) and AddGroupNames or { AddGroupNames } - - for AddGroupID, AddGroupName in pairs( AddGroupNamesArray ) do - self:Add( AddGroupName, GROUP:FindByName( AddGroupName ) ) - end - - return self -end - ---- Remove GROUP(s) from SET_GROUP. --- @param Core.Set#SET_GROUP self --- @param Wrapper.Group#GROUP RemoveGroupNames A single name or an array of GROUP names. --- @return self -function SET_GROUP:RemoveGroupsByName( RemoveGroupNames ) - - local RemoveGroupNamesArray = ( type( RemoveGroupNames ) == "table" ) and RemoveGroupNames or { RemoveGroupNames } - - for RemoveGroupID, RemoveGroupName in pairs( RemoveGroupNamesArray ) do - self:Remove( RemoveGroupName.GroupName ) - end - - return self -end - - - - ---- Finds a Group based on the Group Name. --- @param #SET_GROUP self --- @param #string GroupName --- @return Wrapper.Group#GROUP The found Group. -function SET_GROUP:FindGroup( GroupName ) - - local GroupFound = self.Set[GroupName] - return GroupFound -end - - - ---- Builds a set of groups of coalitions. --- Possible current coalitions are red, blue and neutral. --- @param #SET_GROUP self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #SET_GROUP self -function SET_GROUP:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self -end - - ---- Builds a set of groups out of categories. --- Possible current categories are plane, helicopter, ground, ship. --- @param #SET_GROUP self --- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". --- @return #SET_GROUP self -function SET_GROUP:FilterCategories( Categories ) - if not self.Filter.Categories then - self.Filter.Categories = {} - end - if type( Categories ) ~= "table" then - Categories = { Categories } - end - for CategoryID, Category in pairs( Categories ) do - self.Filter.Categories[Category] = Category - end - return self -end - ---- Builds a set of groups of defined countries. --- Possible current countries are those known within DCS world. --- @param #SET_GROUP self --- @param #string Countries Can take those country strings known within DCS world. --- @return #SET_GROUP self -function SET_GROUP:FilterCountries( Countries ) - if not self.Filter.Countries then - self.Filter.Countries = {} - end - if type( Countries ) ~= "table" then - Countries = { Countries } - end - for CountryID, Country in pairs( Countries ) do - self.Filter.Countries[Country] = Country - end - return self -end - - ---- Builds a set of groups of defined GROUP prefixes. --- All the groups starting with the given prefixes will be included within the set. --- @param #SET_GROUP self --- @param #string Prefixes The prefix of which the group name starts with. --- @return #SET_GROUP self -function SET_GROUP:FilterPrefixes( Prefixes ) - if not self.Filter.GroupPrefixes then - self.Filter.GroupPrefixes = {} - end - if type( Prefixes ) ~= "table" then - Prefixes = { Prefixes } - end - for PrefixID, Prefix in pairs( Prefixes ) do - self.Filter.GroupPrefixes[Prefix] = Prefix - end - return self -end - - ---- Starts the filtering. --- @param #SET_GROUP self --- @return #SET_GROUP self -function SET_GROUP:FilterStart() - - if _DATABASE then - self:_FilterStart() - end - - - - return self -end - ---- Handles the Database to check on an event (birth) that the Object was added in the Database. --- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! --- @param #SET_GROUP self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the GROUP --- @return #table The GROUP -function SET_GROUP:AddInDatabase( Event ) - self:F3( { Event } ) - - if Event.IniObjectCategory == 1 then - if not self.Database[Event.IniDCSGroupName] then - self.Database[Event.IniDCSGroupName] = GROUP:Register( Event.IniDCSGroupName ) - self:T3( self.Database[Event.IniDCSGroupName] ) - end - end - - return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName] -end - ---- Handles the Database to check on any event that Object exists in the Database. --- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! --- @param #SET_GROUP self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the GROUP --- @return #table The GROUP -function SET_GROUP:FindInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName] -end - ---- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP, providing the GROUP and optional parameters. --- @param #SET_GROUP self --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroup( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set ) - - return self -end - ---- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroupCompletelyInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsCompletelyInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroupPartlyInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsPartlyInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroupNotInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsNotInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - - ------ Iterate the SET_GROUP and call an interator function for each **alive** player, providing the Group of the player and optional parameters. ----- @param #SET_GROUP self ----- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter. ----- @return #SET_GROUP self ---function SET_GROUP:ForEachPlayer( IteratorFunction, ... ) --- self:F2( arg ) --- --- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) --- --- return self ---end --- --- ------ Iterate the SET_GROUP and call an interator function for each client, providing the Client to the function and optional parameters. ----- @param #SET_GROUP self ----- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter. ----- @return #SET_GROUP self ---function SET_GROUP:ForEachClient( IteratorFunction, ... ) --- self:F2( arg ) --- --- self:ForEach( IteratorFunction, arg, self.Clients ) --- --- return self ---end - - ---- --- @param #SET_GROUP self --- @param Wrapper.Group#GROUP MooseGroup --- @return #SET_GROUP self -function SET_GROUP:IsIncludeObject( MooseGroup ) - self:F2( MooseGroup ) - local MooseGroupInclude = true - - if self.Filter.Coalitions then - local MooseGroupCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - self:T3( { "Coalition:", MooseGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MooseGroup:GetCoalition() then - MooseGroupCoalition = true - end - end - MooseGroupInclude = MooseGroupInclude and MooseGroupCoalition - end - - if self.Filter.Categories then - local MooseGroupCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - self:T3( { "Category:", MooseGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MooseGroup:GetCategory() then - MooseGroupCategory = true - end - end - MooseGroupInclude = MooseGroupInclude and MooseGroupCategory - end - - if self.Filter.Countries then - local MooseGroupCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - self:T3( { "Country:", MooseGroup:GetCountry(), CountryName } ) - if country.id[CountryName] == MooseGroup:GetCountry() then - MooseGroupCountry = true - end - end - MooseGroupInclude = MooseGroupInclude and MooseGroupCountry - end - - if self.Filter.GroupPrefixes then - local MooseGroupPrefix = false - for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do - self:T3( { "Prefix:", string.find( MooseGroup:GetName(), GroupPrefix, 1 ), GroupPrefix } ) - if string.find( MooseGroup:GetName(), GroupPrefix, 1 ) then - MooseGroupPrefix = true - end - end - MooseGroupInclude = MooseGroupInclude and MooseGroupPrefix - end - - self:T2( MooseGroupInclude ) - return MooseGroupInclude -end - ---- @type SET_UNIT --- @extends Core.Set#SET_BASE - ---- # 3) SET_UNIT class, extends @{Set#SET_BASE} --- --- Mission designers can use the SET_UNIT class to build sets of units belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Unit types --- * Starting with certain prefix strings. --- --- ## 3.1) SET_UNIT constructor --- --- Create a new SET_UNIT object with the @{#SET_UNIT.New} method: --- --- * @{#SET_UNIT.New}: Creates a new SET_UNIT object. --- --- ## 3.2) Add or Remove UNIT(s) from SET_UNIT --- --- UNITs can be added and removed using the @{Set#SET_UNIT.AddUnitsByName} and @{Set#SET_UNIT.RemoveUnitsByName} respectively. --- These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT. --- --- ## 3.3) SET_UNIT filter criteria --- --- You can set filter criteria to define the set of units within the SET_UNIT. --- Filter criteria are defined by: --- --- * @{#SET_UNIT.FilterCoalitions}: Builds the SET_UNIT with the units belonging to the coalition(s). --- * @{#SET_UNIT.FilterCategories}: Builds the SET_UNIT with the units belonging to the category(ies). --- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s). --- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies). --- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units starting with the same prefix string(s). --- --- Once the filter criteria have been set for the SET_UNIT, you can start filtering using: --- --- * @{#SET_UNIT.FilterStart}: Starts the filtering of the units within the SET_UNIT. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Zone#ZONE}. --- --- ## 3.4) SET_UNIT iterators --- --- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. --- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_UNIT: --- --- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT. --- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- --- Planned iterators methods in development are (so these are not yet available): --- --- * @{#SET_UNIT.ForEachUnitInUnit}: Calls a function for each unit contained within the SET_UNIT. --- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. --- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function. --- --- ## 3.5 ) SET_UNIT atomic methods --- --- Various methods exist for a SET_UNIT to perform actions or calculations and retrieve results from the SET_UNIT: --- --- * @{#SET_UNIT.GetTypeNames}(): Retrieve the type names of the @{Unit}s in the SET, delimited by a comma. --- --- === --- @field #SET_UNIT SET_UNIT -SET_UNIT = { - ClassName = "SET_UNIT", - Units = {}, - Filter = { - Coalitions = nil, - Categories = nil, - Types = nil, - Countries = nil, - UnitPrefixes = nil, - }, - FilterMeta = { - Coalitions = { - red = coalition.side.RED, - blue = coalition.side.BLUE, - neutral = coalition.side.NEUTRAL, - }, - Categories = { - plane = Unit.Category.AIRPLANE, - helicopter = Unit.Category.HELICOPTER, - ground = Unit.Category.GROUND_UNIT, - ship = Unit.Category.SHIP, - structure = Unit.Category.STRUCTURE, - }, - }, -} - - ---- Creates a new SET_UNIT object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #SET_UNIT self --- @return #SET_UNIT --- @usage --- -- Define a new SET_UNIT Object. This DBObject will contain a reference to all alive Units. --- DBObject = SET_UNIT:New() -function SET_UNIT:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.UNITS ) ) - - return self -end - ---- Add UNIT(s) to SET_UNIT. --- @param #SET_UNIT self --- @param #string AddUnit A single UNIT. --- @return #SET_UNIT self -function SET_UNIT:AddUnit( AddUnit ) - self:F2( AddUnit:GetName() ) - - self:Add( AddUnit:GetName(), AddUnit ) - - return self -end - - ---- Add UNIT(s) to SET_UNIT. --- @param #SET_UNIT self --- @param #string AddUnitNames A single name or an array of UNIT names. --- @return #SET_UNIT self -function SET_UNIT:AddUnitsByName( AddUnitNames ) - - local AddUnitNamesArray = ( type( AddUnitNames ) == "table" ) and AddUnitNames or { AddUnitNames } - - self:T( AddUnitNamesArray ) - for AddUnitID, AddUnitName in pairs( AddUnitNamesArray ) do - self:Add( AddUnitName, UNIT:FindByName( AddUnitName ) ) - end - - return self -end - ---- Remove UNIT(s) from SET_UNIT. --- @param Core.Set#SET_UNIT self --- @param Wrapper.Unit#UNIT RemoveUnitNames A single name or an array of UNIT names. --- @return self -function SET_UNIT:RemoveUnitsByName( RemoveUnitNames ) - - local RemoveUnitNamesArray = ( type( RemoveUnitNames ) == "table" ) and RemoveUnitNames or { RemoveUnitNames } - - for RemoveUnitID, RemoveUnitName in pairs( RemoveUnitNamesArray ) do - self:Remove( RemoveUnitName ) - end - - return self -end - - ---- Finds a Unit based on the Unit Name. --- @param #SET_UNIT self --- @param #string UnitName --- @return Wrapper.Unit#UNIT The found Unit. -function SET_UNIT:FindUnit( UnitName ) - - local UnitFound = self.Set[UnitName] - return UnitFound -end - - - ---- Builds a set of units of coalitions. --- Possible current coalitions are red, blue and neutral. --- @param #SET_UNIT self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #SET_UNIT self -function SET_UNIT:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self -end - - ---- Builds a set of units out of categories. --- Possible current categories are plane, helicopter, ground, ship. --- @param #SET_UNIT self --- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". --- @return #SET_UNIT self -function SET_UNIT:FilterCategories( Categories ) - if not self.Filter.Categories then - self.Filter.Categories = {} - end - if type( Categories ) ~= "table" then - Categories = { Categories } - end - for CategoryID, Category in pairs( Categories ) do - self.Filter.Categories[Category] = Category - end - return self -end - - ---- Builds a set of units of defined unit types. --- Possible current types are those types known within DCS world. --- @param #SET_UNIT self --- @param #string Types Can take those type strings known within DCS world. --- @return #SET_UNIT self -function SET_UNIT:FilterTypes( Types ) - if not self.Filter.Types then - self.Filter.Types = {} - end - if type( Types ) ~= "table" then - Types = { Types } - end - for TypeID, Type in pairs( Types ) do - self.Filter.Types[Type] = Type - end - return self -end - - ---- Builds a set of units of defined countries. --- Possible current countries are those known within DCS world. --- @param #SET_UNIT self --- @param #string Countries Can take those country strings known within DCS world. --- @return #SET_UNIT self -function SET_UNIT:FilterCountries( Countries ) - if not self.Filter.Countries then - self.Filter.Countries = {} - end - if type( Countries ) ~= "table" then - Countries = { Countries } - end - for CountryID, Country in pairs( Countries ) do - self.Filter.Countries[Country] = Country - end - return self -end - - ---- Builds a set of units of defined unit prefixes. --- All the units starting with the given prefixes will be included within the set. --- @param #SET_UNIT self --- @param #string Prefixes The prefix of which the unit name starts with. --- @return #SET_UNIT self -function SET_UNIT:FilterPrefixes( Prefixes ) - if not self.Filter.UnitPrefixes then - self.Filter.UnitPrefixes = {} - end - if type( Prefixes ) ~= "table" then - Prefixes = { Prefixes } - end - for PrefixID, Prefix in pairs( Prefixes ) do - self.Filter.UnitPrefixes[Prefix] = Prefix - end - return self -end - ---- Builds a set of units having a radar of give types. --- All the units having a radar of a given type will be included within the set. --- @param #SET_UNIT self --- @param #table RadarTypes The radar types. --- @return #SET_UNIT self -function SET_UNIT:FilterHasRadar( RadarTypes ) - - self.Filter.RadarTypes = self.Filter.RadarTypes or {} - if type( RadarTypes ) ~= "table" then - RadarTypes = { RadarTypes } - end - for RadarTypeID, RadarType in pairs( RadarTypes ) do - self.Filter.RadarTypes[RadarType] = RadarType - end - return self -end - ---- Builds a set of SEADable units. --- @param #SET_UNIT self --- @return #SET_UNIT self -function SET_UNIT:FilterHasSEAD() - - self.Filter.SEAD = true - return self -end - - - ---- Starts the filtering. --- @param #SET_UNIT self --- @return #SET_UNIT self -function SET_UNIT:FilterStart() - - if _DATABASE then - self:_FilterStart() - end - - return self -end - ---- Handles the Database to check on an event (birth) that the Object was added in the Database. --- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! --- @param #SET_UNIT self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the UNIT --- @return #table The UNIT -function SET_UNIT:AddInDatabase( Event ) - self:F3( { Event } ) - - if Event.IniObjectCategory == 1 then - if not self.Database[Event.IniDCSUnitName] then - self.Database[Event.IniDCSUnitName] = UNIT:Register( Event.IniDCSUnitName ) - self:T3( self.Database[Event.IniDCSUnitName] ) - end - end - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Handles the Database to check on any event that Object exists in the Database. --- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! --- @param #SET_UNIT self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the UNIT --- @return #table The UNIT -function SET_UNIT:FindInDatabase( Event ) - self:E( { Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event } ) - - - return Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName] -end - ---- Iterate the SET_UNIT and call an interator function for each **alive** UNIT, providing the UNIT and optional parameters. --- @param #SET_UNIT self --- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. --- @return #SET_UNIT self -function SET_UNIT:ForEachUnit( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set ) - - return self -end - ---- Iterate the SET_UNIT and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. --- @param #SET_UNIT self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. --- @return #SET_UNIT self -function SET_UNIT:ForEachUnitCompletelyInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Unit#UNIT UnitObject - function( ZoneObject, UnitObject ) - if UnitObject:IsCompletelyInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- Iterate the SET_UNIT and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function. --- @param #SET_UNIT self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. --- @return #SET_UNIT self -function SET_UNIT:ForEachUnitNotInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Unit#UNIT UnitObject - function( ZoneObject, UnitObject ) - if UnitObject:IsNotInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- Returns map of unit types. --- @param #SET_UNIT self --- @return #map<#string,#number> A map of the unit types found. The key is the UnitTypeName and the value is the amount of unit types found. -function SET_UNIT:GetUnitTypes() - self:F2() - - local MT = {} -- Message Text - local UnitTypes = {} - - for UnitID, UnitData in pairs( self:GetSet() ) do - local TextUnit = UnitData -- Wrapper.Unit#UNIT - if TextUnit:IsAlive() then - local UnitType = TextUnit:GetTypeName() - - if not UnitTypes[UnitType] then - UnitTypes[UnitType] = 1 - else - UnitTypes[UnitType] = UnitTypes[UnitType] + 1 - end - end - end - - for UnitTypeID, UnitType in pairs( UnitTypes ) do - MT[#MT+1] = UnitType .. " of " .. UnitTypeID - end - - return UnitTypes -end - - ---- Returns a comma separated string of the unit types with a count in the @{Set}. --- @param #SET_UNIT self --- @return #string The unit types string -function SET_UNIT:GetUnitTypesText() - self:F2() - - local MT = {} -- Message Text - local UnitTypes = self:GetUnitTypes() - - for UnitTypeID, UnitType in pairs( UnitTypes ) do - MT[#MT+1] = UnitType .. " of " .. UnitTypeID - end - - return table.concat( MT, ", " ) -end - ---- Returns map of unit threat levels. --- @param #SET_UNIT self --- @return #table. -function SET_UNIT:GetUnitThreatLevels() - self:F2() - - local UnitThreatLevels = {} - - for UnitID, UnitData in pairs( self:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - if ThreatUnit:IsAlive() then - local UnitThreatLevel, UnitThreatLevelText = ThreatUnit:GetThreatLevel() - local ThreatUnitName = ThreatUnit:GetName() - - UnitThreatLevels[UnitThreatLevel] = UnitThreatLevels[UnitThreatLevel] or {} - UnitThreatLevels[UnitThreatLevel].UnitThreatLevelText = UnitThreatLevelText - UnitThreatLevels[UnitThreatLevel].Units = UnitThreatLevels[UnitThreatLevel].Units or {} - UnitThreatLevels[UnitThreatLevel].Units[ThreatUnitName] = ThreatUnit - end - end - - return UnitThreatLevels -end - ---- Calculate the maxium A2G threat level of the SET_UNIT. --- @param #SET_UNIT self -function SET_UNIT:CalculateThreatLevelA2G() - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( self:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end - - self:T3( MaxThreatLevelA2G ) - return MaxThreatLevelA2G - -end - - ---- Returns if the @{Set} has targets having a radar (of a given type). --- @param #SET_UNIT self --- @param Dcs.DCSWrapper.Unit#Unit.RadarType RadarType --- @return #number The amount of radars in the Set with the given type -function SET_UNIT:HasRadar( RadarType ) - self:F2( RadarType ) - - local RadarCount = 0 - for UnitID, UnitData in pairs( self:GetSet()) do - local UnitSensorTest = UnitData -- Wrapper.Unit#UNIT - local HasSensors - if RadarType then - HasSensors = UnitSensorTest:HasSensors( Unit.SensorType.RADAR, RadarType ) - else - HasSensors = UnitSensorTest:HasSensors( Unit.SensorType.RADAR ) - end - self:T3(HasSensors) - if HasSensors then - RadarCount = RadarCount + 1 - end - end - - return RadarCount -end - ---- Returns if the @{Set} has targets that can be SEADed. --- @param #SET_UNIT self --- @return #number The amount of SEADable units in the Set -function SET_UNIT:HasSEAD() - self:F2() - - local SEADCount = 0 - for UnitID, UnitData in pairs( self:GetSet()) do - local UnitSEAD = UnitData -- Wrapper.Unit#UNIT - if UnitSEAD:IsAlive() then - local UnitSEADAttributes = UnitSEAD:GetDesc().attributes - - local HasSEAD = UnitSEAD:HasSEAD() - - self:T3(HasSEAD) - if HasSEAD then - SEADCount = SEADCount + 1 - end - end - end - - return SEADCount -end - ---- Returns if the @{Set} has ground targets. --- @param #SET_UNIT self --- @return #number The amount of ground targets in the Set. -function SET_UNIT:HasGroundUnits() - self:F2() - - local GroundUnitCount = 0 - for UnitID, UnitData in pairs( self:GetSet()) do - local UnitTest = UnitData -- Wrapper.Unit#UNIT - if UnitTest:IsGround() then - GroundUnitCount = GroundUnitCount + 1 - end - end - - return GroundUnitCount -end - ---- Returns if the @{Set} has friendly ground units. --- @param #SET_UNIT self --- @return #number The amount of ground targets in the Set. -function SET_UNIT:HasFriendlyUnits( FriendlyCoalition ) - self:F2() - - local FriendlyUnitCount = 0 - for UnitID, UnitData in pairs( self:GetSet()) do - local UnitTest = UnitData -- Wrapper.Unit#UNIT - if UnitTest:IsFriendly( FriendlyCoalition ) then - FriendlyUnitCount = FriendlyUnitCount + 1 - end - end - - return FriendlyUnitCount -end - - - ------ Iterate the SET_UNIT and call an interator function for each **alive** player, providing the Unit of the player and optional parameters. ----- @param #SET_UNIT self ----- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter. ----- @return #SET_UNIT self ---function SET_UNIT:ForEachPlayer( IteratorFunction, ... ) --- self:F2( arg ) --- --- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) --- --- return self ---end --- --- ------ Iterate the SET_UNIT and call an interator function for each client, providing the Client to the function and optional parameters. ----- @param #SET_UNIT self ----- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter. ----- @return #SET_UNIT self ---function SET_UNIT:ForEachClient( IteratorFunction, ... ) --- self:F2( arg ) --- --- self:ForEach( IteratorFunction, arg, self.Clients ) --- --- return self ---end - - ---- --- @param #SET_UNIT self --- @param Wrapper.Unit#UNIT MUnit --- @return #SET_UNIT self -function SET_UNIT:IsIncludeObject( MUnit ) - self:F2( MUnit ) - local MUnitInclude = true - - if self.Filter.Coalitions then - local MUnitCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - self:T3( { "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MUnit:GetCoalition() then - MUnitCoalition = true - end - end - MUnitInclude = MUnitInclude and MUnitCoalition - end - - if self.Filter.Categories then - local MUnitCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - self:T3( { "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MUnit:GetDesc().category then - MUnitCategory = true - end - end - MUnitInclude = MUnitInclude and MUnitCategory - end - - if self.Filter.Types then - local MUnitType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - self:T3( { "Type:", MUnit:GetTypeName(), TypeName } ) - if TypeName == MUnit:GetTypeName() then - MUnitType = true - end - end - MUnitInclude = MUnitInclude and MUnitType - end - - if self.Filter.Countries then - local MUnitCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - self:T3( { "Country:", MUnit:GetCountry(), CountryName } ) - if country.id[CountryName] == MUnit:GetCountry() then - MUnitCountry = true - end - end - MUnitInclude = MUnitInclude and MUnitCountry - end - - if self.Filter.UnitPrefixes then - local MUnitPrefix = false - for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do - self:T3( { "Prefix:", string.find( MUnit:GetName(), UnitPrefix, 1 ), UnitPrefix } ) - if string.find( MUnit:GetName(), UnitPrefix, 1 ) then - MUnitPrefix = true - end - end - MUnitInclude = MUnitInclude and MUnitPrefix - end - - if self.Filter.RadarTypes then - local MUnitRadar = false - for RadarTypeID, RadarType in pairs( self.Filter.RadarTypes ) do - self:T3( { "Radar:", RadarType } ) - if MUnit:HasSensors( Unit.SensorType.RADAR, RadarType ) == true then - if MUnit:GetRadar() == true then -- This call is necessary to evaluate the SEAD capability. - self:T3( "RADAR Found" ) - end - MUnitRadar = true - end - end - MUnitInclude = MUnitInclude and MUnitRadar - end - - if self.Filter.SEAD then - local MUnitSEAD = false - if MUnit:HasSEAD() == true then - self:T3( "SEAD Found" ) - MUnitSEAD = true - end - MUnitInclude = MUnitInclude and MUnitSEAD - end - - self:T2( MUnitInclude ) - return MUnitInclude -end - - ---- Retrieve the type names of the @{Unit}s in the SET, delimited by an optional delimiter. --- @param #SET_UNIT self --- @param #string Delimiter (optional) The delimiter, which is default a comma. --- @return #string The types of the @{Unit}s delimited. -function SET_UNIT:GetTypeNames( Delimiter ) - - Delimiter = Delimiter or ", " - local TypeReport = REPORT:New() - local Types = {} - - for UnitName, UnitData in pairs( self:GetSet() ) do - - local Unit = UnitData -- Wrapper.Unit#UNIT - local UnitTypeName = Unit:GetTypeName() - - if not Types[UnitTypeName] then - Types[UnitTypeName] = UnitTypeName - TypeReport:Add( UnitTypeName ) - end - end - - return TypeReport:Text( Delimiter ) -end - - ---- SET_CLIENT - - ---- @type SET_CLIENT --- @extends Core.Set#SET_BASE - - - ---- # 4) SET_CLIENT class, extends @{Set#SET_BASE} --- --- Mission designers can use the @{Set#SET_CLIENT} class to build sets of units belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Client types --- * Starting with certain prefix strings. --- --- ## 4.1) SET_CLIENT constructor --- --- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method: --- --- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object. --- --- ## 4.2) Add or Remove CLIENT(s) from SET_CLIENT --- --- CLIENTs can be added and removed using the @{Set#SET_CLIENT.AddClientsByName} and @{Set#SET_CLIENT.RemoveClientsByName} respectively. --- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. --- --- ## 4.3) SET_CLIENT filter criteria --- --- You can set filter criteria to define the set of clients within the SET_CLIENT. --- Filter criteria are defined by: --- --- * @{#SET_CLIENT.FilterCoalitions}: Builds the SET_CLIENT with the clients belonging to the coalition(s). --- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies). --- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s). --- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). --- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s). --- --- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: --- --- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients within the SET_CLIENT. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Zone#ZONE}. --- --- ## 4.4) SET_CLIENT iterators --- --- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. --- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_CLIENT: --- --- * @{#SET_CLIENT.ForEachClient}: Calls a function for each alive client it finds within the SET_CLIENT. --- --- === --- @field #SET_CLIENT SET_CLIENT -SET_CLIENT = { - ClassName = "SET_CLIENT", - Clients = {}, - Filter = { - Coalitions = nil, - Categories = nil, - Types = nil, - Countries = nil, - ClientPrefixes = nil, - }, - FilterMeta = { - Coalitions = { - red = coalition.side.RED, - blue = coalition.side.BLUE, - neutral = coalition.side.NEUTRAL, - }, - Categories = { - plane = Unit.Category.AIRPLANE, - helicopter = Unit.Category.HELICOPTER, - ground = Unit.Category.GROUND_UNIT, - ship = Unit.Category.SHIP, - structure = Unit.Category.STRUCTURE, - }, - }, -} - - ---- Creates a new SET_CLIENT object, building a set of clients belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #SET_CLIENT self --- @return #SET_CLIENT --- @usage --- -- Define a new SET_CLIENT Object. This DBObject will contain a reference to all Clients. --- DBObject = SET_CLIENT:New() -function SET_CLIENT:New() - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CLIENTS ) ) - - return self -end - ---- Add CLIENT(s) to SET_CLIENT. --- @param Core.Set#SET_CLIENT self --- @param #string AddClientNames A single name or an array of CLIENT names. --- @return self -function SET_CLIENT:AddClientsByName( AddClientNames ) - - local AddClientNamesArray = ( type( AddClientNames ) == "table" ) and AddClientNames or { AddClientNames } - - for AddClientID, AddClientName in pairs( AddClientNamesArray ) do - self:Add( AddClientName, CLIENT:FindByName( AddClientName ) ) - end - - return self -end - ---- Remove CLIENT(s) from SET_CLIENT. --- @param Core.Set#SET_CLIENT self --- @param Wrapper.Client#CLIENT RemoveClientNames A single name or an array of CLIENT names. --- @return self -function SET_CLIENT:RemoveClientsByName( RemoveClientNames ) - - local RemoveClientNamesArray = ( type( RemoveClientNames ) == "table" ) and RemoveClientNames or { RemoveClientNames } - - for RemoveClientID, RemoveClientName in pairs( RemoveClientNamesArray ) do - self:Remove( RemoveClientName.ClientName ) - end - - return self -end - - ---- Finds a Client based on the Client Name. --- @param #SET_CLIENT self --- @param #string ClientName --- @return Wrapper.Client#CLIENT The found Client. -function SET_CLIENT:FindClient( ClientName ) - - local ClientFound = self.Set[ClientName] - return ClientFound -end - - - ---- Builds a set of clients of coalitions. --- Possible current coalitions are red, blue and neutral. --- @param #SET_CLIENT self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #SET_CLIENT self -function SET_CLIENT:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self -end - - ---- Builds a set of clients out of categories. --- Possible current categories are plane, helicopter, ground, ship. --- @param #SET_CLIENT self --- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". --- @return #SET_CLIENT self -function SET_CLIENT:FilterCategories( Categories ) - if not self.Filter.Categories then - self.Filter.Categories = {} - end - if type( Categories ) ~= "table" then - Categories = { Categories } - end - for CategoryID, Category in pairs( Categories ) do - self.Filter.Categories[Category] = Category - end - return self -end - - ---- Builds a set of clients of defined client types. --- Possible current types are those types known within DCS world. --- @param #SET_CLIENT self --- @param #string Types Can take those type strings known within DCS world. --- @return #SET_CLIENT self -function SET_CLIENT:FilterTypes( Types ) - if not self.Filter.Types then - self.Filter.Types = {} - end - if type( Types ) ~= "table" then - Types = { Types } - end - for TypeID, Type in pairs( Types ) do - self.Filter.Types[Type] = Type - end - return self -end - - ---- Builds a set of clients of defined countries. --- Possible current countries are those known within DCS world. --- @param #SET_CLIENT self --- @param #string Countries Can take those country strings known within DCS world. --- @return #SET_CLIENT self -function SET_CLIENT:FilterCountries( Countries ) - if not self.Filter.Countries then - self.Filter.Countries = {} - end - if type( Countries ) ~= "table" then - Countries = { Countries } - end - for CountryID, Country in pairs( Countries ) do - self.Filter.Countries[Country] = Country - end - return self -end - - ---- Builds a set of clients of defined client prefixes. --- All the clients starting with the given prefixes will be included within the set. --- @param #SET_CLIENT self --- @param #string Prefixes The prefix of which the client name starts with. --- @return #SET_CLIENT self -function SET_CLIENT:FilterPrefixes( Prefixes ) - if not self.Filter.ClientPrefixes then - self.Filter.ClientPrefixes = {} - end - if type( Prefixes ) ~= "table" then - Prefixes = { Prefixes } - end - for PrefixID, Prefix in pairs( Prefixes ) do - self.Filter.ClientPrefixes[Prefix] = Prefix - end - return self -end - - - - ---- Starts the filtering. --- @param #SET_CLIENT self --- @return #SET_CLIENT self -function SET_CLIENT:FilterStart() - - if _DATABASE then - self:_FilterStart() - end - - return self -end - ---- Handles the Database to check on an event (birth) that the Object was added in the Database. --- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! --- @param #SET_CLIENT self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the CLIENT --- @return #table The CLIENT -function SET_CLIENT:AddInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Handles the Database to check on any event that Object exists in the Database. --- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! --- @param #SET_CLIENT self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the CLIENT --- @return #table The CLIENT -function SET_CLIENT:FindInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Iterate the SET_CLIENT and call an interator function for each **alive** CLIENT, providing the CLIENT and optional parameters. --- @param #SET_CLIENT self --- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. --- @return #SET_CLIENT self -function SET_CLIENT:ForEachClient( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set ) - - return self -end - ---- Iterate the SET_CLIENT and call an iterator function for each **alive** CLIENT presence completely in a @{Zone}, providing the CLIENT and optional parameters to the called function. --- @param #SET_CLIENT self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. --- @return #SET_CLIENT self -function SET_CLIENT:ForEachClientInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- Iterate the SET_CLIENT and call an iterator function for each **alive** CLIENT presence not in a @{Zone}, providing the CLIENT and optional parameters to the called function. --- @param #SET_CLIENT self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. --- @return #SET_CLIENT self -function SET_CLIENT:ForEachClientNotInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set, - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsNotInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- --- @param #SET_CLIENT self --- @param Wrapper.Client#CLIENT MClient --- @return #SET_CLIENT self -function SET_CLIENT:IsIncludeObject( MClient ) - self:F2( MClient ) - - local MClientInclude = true - - if MClient then - local MClientName = MClient.UnitName - - if self.Filter.Coalitions then - local MClientCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName ) - self:T3( { "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == ClientCoalitionID then - MClientCoalition = true - end - end - self:T( { "Evaluated Coalition", MClientCoalition } ) - MClientInclude = MClientInclude and MClientCoalition - end - - if self.Filter.Categories then - local MClientCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) - self:T3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then - MClientCategory = true - end - end - self:T( { "Evaluated Category", MClientCategory } ) - MClientInclude = MClientInclude and MClientCategory - end - - if self.Filter.Types then - local MClientType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - self:T3( { "Type:", MClient:GetTypeName(), TypeName } ) - if TypeName == MClient:GetTypeName() then - MClientType = true - end - end - self:T( { "Evaluated Type", MClientType } ) - MClientInclude = MClientInclude and MClientType - end - - if self.Filter.Countries then - local MClientCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - local ClientCountryID = _DATABASE:GetCountryFromClientTemplate(MClientName) - self:T3( { "Country:", ClientCountryID, country.id[CountryName], CountryName } ) - if country.id[CountryName] and country.id[CountryName] == ClientCountryID then - MClientCountry = true - end - end - self:T( { "Evaluated Country", MClientCountry } ) - MClientInclude = MClientInclude and MClientCountry - end - - if self.Filter.ClientPrefixes then - local MClientPrefix = false - for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do - self:T3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } ) - if string.find( MClient.UnitName, ClientPrefix, 1 ) then - MClientPrefix = true - end - end - self:T( { "Evaluated Prefix", MClientPrefix } ) - MClientInclude = MClientInclude and MClientPrefix - end - end - - self:T2( MClientInclude ) - return MClientInclude -end - ---- @type SET_AIRBASE --- @extends Core.Set#SET_BASE - ---- # 5) SET_AIRBASE class, extends @{Set#SET_BASE} --- --- Mission designers can use the @{Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: --- --- * Coalitions --- --- ## 5.1) SET_AIRBASE constructor --- --- Create a new SET_AIRBASE object with the @{#SET_AIRBASE.New} method: --- --- * @{#SET_AIRBASE.New}: Creates a new SET_AIRBASE object. --- --- ## 5.2) Add or Remove AIRBASEs from SET_AIRBASE --- --- AIRBASEs can be added and removed using the @{Set#SET_AIRBASE.AddAirbasesByName} and @{Set#SET_AIRBASE.RemoveAirbasesByName} respectively. --- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE. --- --- ## 5.3) SET_AIRBASE filter criteria --- --- You can set filter criteria to define the set of clients within the SET_AIRBASE. --- Filter criteria are defined by: --- --- * @{#SET_AIRBASE.FilterCoalitions}: Builds the SET_AIRBASE with the airbases belonging to the coalition(s). --- --- Once the filter criteria have been set for the SET_AIRBASE, you can start filtering using: --- --- * @{#SET_AIRBASE.FilterStart}: Starts the filtering of the airbases within the SET_AIRBASE. --- --- ## 5.4) SET_AIRBASE iterators --- --- Once the filters have been defined and the SET_AIRBASE has been built, you can iterate the SET_AIRBASE with the available iterator methods. --- The iterator methods will walk the SET_AIRBASE set, and call for each airbase within the set a function that you provide. --- The following iterator methods are currently available within the SET_AIRBASE: --- --- * @{#SET_AIRBASE.ForEachAirbase}: Calls a function for each airbase it finds within the SET_AIRBASE. --- --- === --- @field #SET_AIRBASE SET_AIRBASE -SET_AIRBASE = { - ClassName = "SET_AIRBASE", - Airbases = {}, - Filter = { - Coalitions = nil, - }, - FilterMeta = { - Coalitions = { - red = coalition.side.RED, - blue = coalition.side.BLUE, - neutral = coalition.side.NEUTRAL, - }, - Categories = { - airdrome = Airbase.Category.AIRDROME, - helipad = Airbase.Category.HELIPAD, - ship = Airbase.Category.SHIP, - }, - }, -} - - ---- Creates a new SET_AIRBASE object, building a set of airbases belonging to a coalitions and categories. --- @param #SET_AIRBASE self --- @return #SET_AIRBASE self --- @usage --- -- Define a new SET_AIRBASE Object. The DatabaseSet will contain a reference to all Airbases. --- DatabaseSet = SET_AIRBASE:New() -function SET_AIRBASE:New() - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.AIRBASES ) ) - - return self -end - ---- Add AIRBASEs to SET_AIRBASE. --- @param Core.Set#SET_AIRBASE self --- @param #string AddAirbaseNames A single name or an array of AIRBASE names. --- @return self -function SET_AIRBASE:AddAirbasesByName( AddAirbaseNames ) - - local AddAirbaseNamesArray = ( type( AddAirbaseNames ) == "table" ) and AddAirbaseNames or { AddAirbaseNames } - - for AddAirbaseID, AddAirbaseName in pairs( AddAirbaseNamesArray ) do - self:Add( AddAirbaseName, AIRBASE:FindByName( AddAirbaseName ) ) - end - - return self -end - ---- Remove AIRBASEs from SET_AIRBASE. --- @param Core.Set#SET_AIRBASE self --- @param Wrapper.Airbase#AIRBASE RemoveAirbaseNames A single name or an array of AIRBASE names. --- @return self -function SET_AIRBASE:RemoveAirbasesByName( RemoveAirbaseNames ) - - local RemoveAirbaseNamesArray = ( type( RemoveAirbaseNames ) == "table" ) and RemoveAirbaseNames or { RemoveAirbaseNames } - - for RemoveAirbaseID, RemoveAirbaseName in pairs( RemoveAirbaseNamesArray ) do - self:Remove( RemoveAirbaseName.AirbaseName ) - end - - return self -end - - ---- Finds a Airbase based on the Airbase Name. --- @param #SET_AIRBASE self --- @param #string AirbaseName --- @return Wrapper.Airbase#AIRBASE The found Airbase. -function SET_AIRBASE:FindAirbase( AirbaseName ) - - local AirbaseFound = self.Set[AirbaseName] - return AirbaseFound -end - - - ---- Builds a set of airbases of coalitions. --- Possible current coalitions are red, blue and neutral. --- @param #SET_AIRBASE self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #SET_AIRBASE self -function SET_AIRBASE:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self -end - - ---- Builds a set of airbases out of categories. --- Possible current categories are plane, helicopter, ground, ship. --- @param #SET_AIRBASE self --- @param #string Categories Can take the following values: "airdrome", "helipad", "ship". --- @return #SET_AIRBASE self -function SET_AIRBASE:FilterCategories( Categories ) - if not self.Filter.Categories then - self.Filter.Categories = {} - end - if type( Categories ) ~= "table" then - Categories = { Categories } - end - for CategoryID, Category in pairs( Categories ) do - self.Filter.Categories[Category] = Category - end - return self -end - ---- Starts the filtering. --- @param #SET_AIRBASE self --- @return #SET_AIRBASE self -function SET_AIRBASE:FilterStart() - - if _DATABASE then - self:_FilterStart() - end - - return self -end - - ---- Handles the Database to check on an event (birth) that the Object was added in the Database. --- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! --- @param #SET_AIRBASE self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the AIRBASE --- @return #table The AIRBASE -function SET_AIRBASE:AddInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Handles the Database to check on any event that Object exists in the Database. --- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! --- @param #SET_AIRBASE self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the AIRBASE --- @return #table The AIRBASE -function SET_AIRBASE:FindInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Iterate the SET_AIRBASE and call an interator function for each AIRBASE, providing the AIRBASE and optional parameters. --- @param #SET_AIRBASE self --- @param #function IteratorFunction The function that will be called when there is an alive AIRBASE in the SET_AIRBASE. The function needs to accept a AIRBASE parameter. --- @return #SET_AIRBASE self -function SET_AIRBASE:ForEachAirbase( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self.Set ) - - return self -end - ---- Iterate the SET_AIRBASE while identifying the nearest @{Airbase#AIRBASE} from a @{Point#POINT_VEC2}. --- @param #SET_AIRBASE self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest @{Airbase#AIRBASE}. --- @return Wrapper.Airbase#AIRBASE The closest @{Airbase#AIRBASE}. -function SET_AIRBASE:FindNearestAirbaseFromPointVec2( PointVec2 ) - self:F2( PointVec2 ) - - local NearestAirbase = self:FindNearestObjectFromPointVec2( PointVec2 ) - return NearestAirbase -end - - - ---- --- @param #SET_AIRBASE self --- @param Wrapper.Airbase#AIRBASE MAirbase --- @return #SET_AIRBASE self -function SET_AIRBASE:IsIncludeObject( MAirbase ) - self:F2( MAirbase ) - - local MAirbaseInclude = true - - if MAirbase then - local MAirbaseName = MAirbase:GetName() - - if self.Filter.Coalitions then - local MAirbaseCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - local AirbaseCoalitionID = _DATABASE:GetCoalitionFromAirbase( MAirbaseName ) - self:T3( { "Coalition:", AirbaseCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == AirbaseCoalitionID then - MAirbaseCoalition = true - end - end - self:T( { "Evaluated Coalition", MAirbaseCoalition } ) - MAirbaseInclude = MAirbaseInclude and MAirbaseCoalition - end - - if self.Filter.Categories then - local MAirbaseCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase( MAirbaseName ) - self:T3( { "Category:", AirbaseCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == AirbaseCategoryID then - MAirbaseCategory = true - end - end - self:T( { "Evaluated Category", MAirbaseCategory } ) - MAirbaseInclude = MAirbaseInclude and MAirbaseCategory - end - end - - self:T2( MAirbaseInclude ) - return MAirbaseInclude -end ---- **Core** - **POINT\_VEC** classes define an **extensive API** to **manage 3D points** in the simulation space. --- --- 1) @{Point#POINT_VEC3} class, extends @{Base#BASE} --- ================================================== --- The @{Point#POINT_VEC3} class defines a 3D point in the simulator. --- --- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts. --- In order to keep the credibility of the the author, I want to emphasize that the of the MIST framework was created by Grimes, who you can find on the Eagle Dynamics Forums. --- --- ## 1.1) POINT_VEC3 constructor --- --- A new POINT_VEC3 instance can be created with: --- --- * @{Point#POINT_VEC3.New}(): a 3D point. --- * @{Point#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}. --- --- ## 1.2) Manupulate the X, Y, Z coordinates of the point --- --- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate. --- Methods exist to manupulate these coordinates. --- --- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively. --- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value. --- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}() --- to add or substract a value from the current respective axis value. --- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example: --- --- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3() --- --- ## 1.3) Create waypoints for routes --- --- A POINT_VEC3 can prepare waypoints for Ground, Air and Naval groups to be embedded into a Route. --- --- --- ## 1.5) Smoke, flare, explode, illuminate --- --- At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods: --- --- ### 1.5.1) Smoke --- --- * @{#POINT_VEC3.Smoke}(): To smoke the point in a certain color. --- * @{#POINT_VEC3.SmokeBlue}(): To smoke the point in blue. --- * @{#POINT_VEC3.SmokeRed}(): To smoke the point in red. --- * @{#POINT_VEC3.SmokeOrange}(): To smoke the point in orange. --- * @{#POINT_VEC3.SmokeWhite}(): To smoke the point in white. --- * @{#POINT_VEC3.SmokeGreen}(): To smoke the point in green. --- --- ### 1.5.2) Flare --- --- * @{#POINT_VEC3.Flare}(): To flare the point in a certain color. --- * @{#POINT_VEC3.FlareRed}(): To flare the point in red. --- * @{#POINT_VEC3.FlareYellow}(): To flare the point in yellow. --- * @{#POINT_VEC3.FlareWhite}(): To flare the point in white. --- * @{#POINT_VEC3.FlareGreen}(): To flare the point in green. --- --- ### 1.5.3) Explode --- --- * @{#POINT_VEC3.Explosion}(): To explode the point with a certain intensity. --- --- ### 1.5.4) Illuminate --- --- * @{#POINT_VEC3.IlluminationBomb}(): To illuminate the point. --- --- --- 2) @{Point#POINT_VEC2} class, extends @{Point#POINT_VEC3} --- ========================================================= --- The @{Point#POINT_VEC2} class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified. --- --- 2.1) POINT_VEC2 constructor --- --------------------------- --- A new POINT_VEC2 instance can be created with: --- --- * @{Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter. --- * @{Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}. --- --- ## 1.2) Manupulate the X, Altitude, Y coordinates of the 2D point --- --- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate. --- Methods exist to manupulate these coordinates. --- --- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively. --- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value. --- The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods @{#POINT_VEC2.GetLat}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetLon}() respectively. --- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}() --- to add or substract a value from the current respective axis value. --- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example: --- --- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2() --- --- === --- --- **API CHANGE HISTORY** --- ====================== --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-03: POINT\_VEC3:**Explosion( ExplosionIntensity )** added. --- 2017-03-03: POINT\_VEC3:**IlluminationBomb()** added. --- --- 2017-02-18: POINT\_VEC3:**NewFromVec2( Vec2, LandHeightAdd )** added. --- --- 2016-08-12: POINT\_VEC3:**Translate( Distance, Angle )** added. --- --- 2016-08-06: Made PointVec3 and Vec3, PointVec2 and Vec2 terminology used in the code consistent. --- --- * Replaced method _Point_Vec3() to **Vec3**() where the code manages a Vec3. Replaced all references to the method. --- * Replaced method _Point_Vec2() to **Vec2**() where the code manages a Vec2. Replaced all references to the method. --- * Replaced method Random_Point_Vec3() to **RandomVec3**() where the code manages a Vec3. Replaced all references to the method. --- . --- === --- --- ### Authors: --- --- * FlightControl : Design & Programming --- --- ### Contributions: --- --- @module Point - ---- The POINT_VEC3 class --- @type POINT_VEC3 --- @field #number x The x coordinate in 3D space. --- @field #number y The y coordinate in 3D space. --- @field #number z The z coordiante in 3D space. --- @field Utilities.Utils#SMOKECOLOR SmokeColor --- @field Utilities.Utils#FLARECOLOR FlareColor --- @field #POINT_VEC3.RoutePointAltType RoutePointAltType --- @field #POINT_VEC3.RoutePointType RoutePointType --- @field #POINT_VEC3.RoutePointAction RoutePointAction --- @extends Core.Base#BASE -POINT_VEC3 = { - ClassName = "POINT_VEC3", - Metric = true, - RoutePointAltType = { - BARO = "BARO", - }, - RoutePointType = { - TakeOffParking = "TakeOffParking", - TurningPoint = "Turning Point", - }, - RoutePointAction = { - FromParkingArea = "From Parking Area", - TurningPoint = "Turning Point", - }, -} - ---- The POINT_VEC2 class --- @type POINT_VEC2 --- @field Dcs.DCSTypes#Distance x The x coordinate in meters. --- @field Dcs.DCSTypes#Distance y the y coordinate in meters. --- @extends Core.Point#POINT_VEC3 -POINT_VEC2 = { - ClassName = "POINT_VEC2", -} - - -do -- POINT_VEC3 - ---- RoutePoint AltTypes --- @type POINT_VEC3.RoutePointAltType --- @field BARO "BARO" - ---- RoutePoint Types --- @type POINT_VEC3.RoutePointType --- @field TakeOffParking "TakeOffParking" --- @field TurningPoint "Turning Point" - ---- RoutePoint Actions --- @type POINT_VEC3.RoutePointAction --- @field FromParkingArea "From Parking Area" --- @field TurningPoint "Turning Point" - --- Constructor. - ---- Create a new POINT_VEC3 object. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North. --- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing Upwards. --- @param Dcs.DCSTypes#Distance z The z coordinate of the Vec3 point, pointing to the Right. --- @return Core.Point#POINT_VEC3 self -function POINT_VEC3:New( x, y, z ) - - local self = BASE:Inherit( self, BASE:New() ) - self.x = x - self.y = y - self.z = z - - return self -end - ---- Create a new POINT_VEC3 object from Vec2 coordinates. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. --- @return Core.Point#POINT_VEC3 self -function POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd ) - - local LandHeight = land.getHeight( Vec2 ) - - LandHeightAdd = LandHeightAdd or 0 - LandHeight = LandHeight + LandHeightAdd - - self = self:New( Vec2.x, LandHeight, Vec2.y ) - - self:F2( self ) - - return self -end - ---- Create a new POINT_VEC3 object from Vec3 coordinates. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point. --- @return Core.Point#POINT_VEC3 self -function POINT_VEC3:NewFromVec3( Vec3 ) - - self = self:New( Vec3.x, Vec3.y, Vec3.z ) - self:F2( self ) - return self -end - - ---- Return the coordinates of the POINT_VEC3 in Vec3 format. --- @param #POINT_VEC3 self --- @return Dcs.DCSTypes#Vec3 The Vec3 coodinate. -function POINT_VEC3:GetVec3() - return { x = self.x, y = self.y, z = self.z } -end - ---- Return the coordinates of the POINT_VEC3 in Vec2 format. --- @param #POINT_VEC3 self --- @return Dcs.DCSTypes#Vec2 The Vec2 coodinate. -function POINT_VEC3:GetVec2() - return { x = self.x, y = self.z } -end - - ---- Return the x coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #number The x coodinate. -function POINT_VEC3:GetX() - return self.x -end - ---- Return the y coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #number The y coodinate. -function POINT_VEC3:GetY() - return self.y -end - ---- Return the z coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #number The z coodinate. -function POINT_VEC3:GetZ() - return self.z -end - ---- Set the x coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number x The x coordinate. --- @return #POINT_VEC3 -function POINT_VEC3:SetX( x ) - self.x = x - return self -end - ---- Set the y coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number y The y coordinate. --- @return #POINT_VEC3 -function POINT_VEC3:SetY( y ) - self.y = y - return self -end - ---- Set the z coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number z The z coordinate. --- @return #POINT_VEC3 -function POINT_VEC3:SetZ( z ) - self.z = z - return self -end - ---- Add to the x coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number x The x coordinate value to add to the current x coodinate. --- @return #POINT_VEC3 -function POINT_VEC3:AddX( x ) - self.x = self.x + x - return self -end - ---- Add to the y coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number y The y coordinate value to add to the current y coodinate. --- @return #POINT_VEC3 -function POINT_VEC3:AddY( y ) - self.y = self.y + y - return self -end - ---- Add to the z coordinate of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #number z The z coordinate value to add to the current z coodinate. --- @return #POINT_VEC3 -function POINT_VEC3:AddZ( z ) - self.z = self.z +z - return self -end - ---- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance OuterRadius --- @param Dcs.DCSTypes#Distance InnerRadius --- @return Dcs.DCSTypes#Vec2 Vec2 -function POINT_VEC3:GetRandomVec2InRadius( OuterRadius, InnerRadius ) - self:F2( { OuterRadius, InnerRadius } ) - - local Theta = 2 * math.pi * math.random() - local Radials = math.random() + math.random() - if Radials > 1 then - Radials = 2 - Radials - end - - local RadialMultiplier - if InnerRadius and InnerRadius <= OuterRadius then - RadialMultiplier = ( OuterRadius - InnerRadius ) * Radials + InnerRadius - else - RadialMultiplier = OuterRadius * Radials - end - - local RandomVec2 - if OuterRadius > 0 then - RandomVec2 = { x = math.cos( Theta ) * RadialMultiplier + self:GetX(), y = math.sin( Theta ) * RadialMultiplier + self:GetZ() } - else - RandomVec2 = { x = self:GetX(), y = self:GetZ() } - end - - return RandomVec2 -end - ---- Return a random POINT_VEC2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance OuterRadius --- @param Dcs.DCSTypes#Distance InnerRadius --- @return #POINT_VEC2 -function POINT_VEC3:GetRandomPointVec2InRadius( OuterRadius, InnerRadius ) - self:F2( { OuterRadius, InnerRadius } ) - - return POINT_VEC2:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) ) -end - ---- Return a random Vec3 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance OuterRadius --- @param Dcs.DCSTypes#Distance InnerRadius --- @return Dcs.DCSTypes#Vec3 Vec3 -function POINT_VEC3:GetRandomVec3InRadius( OuterRadius, InnerRadius ) - - local RandomVec2 = self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) - local y = self:GetY() + math.random( InnerRadius, OuterRadius ) - local RandomVec3 = { x = RandomVec2.x, y = y, z = RandomVec2.y } - - return RandomVec3 -end - ---- Return a random POINT_VEC3 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance OuterRadius --- @param Dcs.DCSTypes#Distance InnerRadius --- @return #POINT_VEC3 -function POINT_VEC3:GetRandomPointVec3InRadius( OuterRadius, InnerRadius ) - - return POINT_VEC3:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) ) -end - - ---- Return a direction vector Vec3 from POINT_VEC3 to the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3. --- @return Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. -function POINT_VEC3:GetDirectionVec3( TargetPointVec3 ) - return { x = TargetPointVec3:GetX() - self:GetX(), y = TargetPointVec3:GetY() - self:GetY(), z = TargetPointVec3:GetZ() - self:GetZ() } -end - ---- Get a correction in radians of the real magnetic north of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #number CorrectionRadians The correction in radians. -function POINT_VEC3:GetNorthCorrectionRadians() - local TargetVec3 = self:GetVec3() - local lat, lon = coord.LOtoLL(TargetVec3) - local north_posit = coord.LLtoLO(lat + 1, lon) - return math.atan2( north_posit.z - TargetVec3.z, north_posit.x - TargetVec3.x ) -end - - ---- Return a direction in radians from the POINT_VEC3 using a direction vector in Vec3 format. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. --- @return #number DirectionRadians The direction in radians. -function POINT_VEC3:GetDirectionRadians( DirectionVec3 ) - local DirectionRadians = math.atan2( DirectionVec3.z, DirectionVec3.x ) - --DirectionRadians = DirectionRadians + self:GetNorthCorrectionRadians() - if DirectionRadians < 0 then - DirectionRadians = DirectionRadians + 2 * math.pi -- put dir in range of 0 to 2*pi ( the full circle ) - end - return DirectionRadians -end - ---- Return the 2D distance in meters between the target POINT_VEC3 and the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3. --- @return Dcs.DCSTypes#Distance Distance The distance in meters. -function POINT_VEC3:Get2DDistance( TargetPointVec3 ) - local TargetVec3 = TargetPointVec3:GetVec3() - local SourceVec3 = self:GetVec3() - return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5 -end - ---- Return the 3D distance in meters between the target POINT_VEC3 and the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3. --- @return Dcs.DCSTypes#Distance Distance The distance in meters. -function POINT_VEC3:Get3DDistance( TargetPointVec3 ) - local TargetVec3 = TargetPointVec3:GetVec3() - local SourceVec3 = self:GetVec3() - return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5 -end - ---- Provides a Bearing / Range string --- @param #POINT_VEC3 self --- @param #number AngleRadians The angle in randians --- @param #number Distance The distance --- @return #string The BR Text -function POINT_VEC3:ToStringBR( AngleRadians, Distance ) - - AngleRadians = UTILS.Round( UTILS.ToDegree( AngleRadians ), 0 ) - if self:IsMetric() then - Distance = UTILS.Round( Distance / 1000, 2 ) - else - Distance = UTILS.Round( UTILS.MetersToNM( Distance ), 2 ) - end - - local s = string.format( '%03d', AngleRadians ) .. ' for ' .. Distance - - s = s .. self:GetAltitudeText() -- When the POINT is a VEC2, there will be no altitude shown. - - return s -end - ---- Provides a Bearing / Range string --- @param #POINT_VEC3 self --- @param #number AngleRadians The angle in randians --- @param #number Distance The distance --- @return #string The BR Text -function POINT_VEC3:ToStringLL( acc, DMS ) - - acc = acc or 3 - local lat, lon = coord.LOtoLL( self:GetVec3() ) - return UTILS.tostringLL(lat, lon, acc, DMS) -end - ---- Return the altitude text of the POINT_VEC3. --- @param #POINT_VEC3 self --- @return #string Altitude text. -function POINT_VEC3:GetAltitudeText() - if self:IsMetric() then - return ' at ' .. UTILS.Round( self:GetY(), 0 ) - else - return ' at ' .. UTILS.Round( UTILS.MetersToFeet( self:GetY() ), 0 ) - end -end - ---- Return a BR string from a POINT_VEC3 to the POINT_VEC3. --- @param #POINT_VEC3 self --- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3. --- @return #string The BR text. -function POINT_VEC3:GetBRText( TargetPointVec3 ) - local DirectionVec3 = self:GetDirectionVec3( TargetPointVec3 ) - local AngleRadians = self:GetDirectionRadians( DirectionVec3 ) - local Distance = self:Get2DDistance( TargetPointVec3 ) - return self:ToStringBR( AngleRadians, Distance ) -end - ---- Sets the POINT_VEC3 metric or NM. --- @param #POINT_VEC3 self --- @param #boolean Metric true means metric, false means NM. -function POINT_VEC3:SetMetric( Metric ) - self.Metric = Metric -end - ---- Gets if the POINT_VEC3 is metric or NM. --- @param #POINT_VEC3 self --- @return #boolean Metric true means metric, false means NM. -function POINT_VEC3:IsMetric() - return self.Metric -end - ---- Add a Distance in meters from the POINT_VEC3 horizontal plane, with the given angle, and calculate the new POINT_VEC3. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters. --- @param Dcs.DCSTypes#Angle Angle The Angle in degrees. --- @return #POINT_VEC3 The new calculated POINT_VEC3. -function POINT_VEC3:Translate( Distance, Angle ) - local SX = self:GetX() - local SZ = self:GetZ() - local Radians = Angle / 180 * math.pi - local TX = Distance * math.cos( Radians ) + SX - local TZ = Distance * math.sin( Radians ) + SZ - - return POINT_VEC3:New( TX, self:GetY(), TZ ) -end - - - ---- Build an air type route point. --- @param #POINT_VEC3 self --- @param #POINT_VEC3.RoutePointAltType AltType The altitude type. --- @param #POINT_VEC3.RoutePointType Type The route point type. --- @param #POINT_VEC3.RoutePointAction Action The route point action. --- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. --- @param #boolean SpeedLocked true means the speed is locked. --- @return #table The route point. -function POINT_VEC3:RoutePointAir( AltType, Type, Action, Speed, SpeedLocked ) - self:F2( { AltType, Type, Action, Speed, SpeedLocked } ) - - local RoutePoint = {} - RoutePoint.x = self.x - RoutePoint.y = self.z - RoutePoint.alt = self.y - RoutePoint.alt_type = AltType - - RoutePoint.type = Type - RoutePoint.action = Action - - RoutePoint.speed = Speed / 3.6 - RoutePoint.speed_locked = true - --- ["task"] = --- { --- ["id"] = "ComboTask", --- ["params"] = --- { --- ["tasks"] = --- { --- }, -- end of ["tasks"] --- }, -- end of ["params"] --- }, -- end of ["task"] - - - RoutePoint.task = {} - RoutePoint.task.id = "ComboTask" - RoutePoint.task.params = {} - RoutePoint.task.params.tasks = {} - - - return RoutePoint -end - ---- Build an ground type route point. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Speed Speed Speed in km/h. --- @param #POINT_VEC3.RoutePointAction Formation The route point Formation. --- @return #table The route point. -function POINT_VEC3:RoutePointGround( Speed, Formation ) - self:F2( { Formation, Speed } ) - - local RoutePoint = {} - RoutePoint.x = self.x - RoutePoint.y = self.z - - RoutePoint.action = Formation or "" - - - RoutePoint.speed = Speed / 3.6 - RoutePoint.speed_locked = true - --- ["task"] = --- { --- ["id"] = "ComboTask", --- ["params"] = --- { --- ["tasks"] = --- { --- }, -- end of ["tasks"] --- }, -- end of ["params"] --- }, -- end of ["task"] - - - RoutePoint.task = {} - RoutePoint.task.id = "ComboTask" - RoutePoint.task.params = {} - RoutePoint.task.params.tasks = {} - - - return RoutePoint -end - ---- Creates an explosion at the point of a certain intensity. --- @param #POINT_VEC3 self --- @param #number ExplosionIntensity -function POINT_VEC3:Explosion( ExplosionIntensity ) - self:F2( { ExplosionIntensity } ) - trigger.action.explosion( self:GetVec3(), ExplosionIntensity ) -end - ---- Creates an illumination bomb at the point. --- @param #POINT_VEC3 self -function POINT_VEC3:IlluminationBomb() - self:F2() - trigger.action.illuminationBomb( self:GetVec3() ) -end - - ---- Smokes the point in a color. --- @param #POINT_VEC3 self --- @param Utilities.Utils#SMOKECOLOR SmokeColor -function POINT_VEC3:Smoke( SmokeColor ) - self:F2( { SmokeColor } ) - trigger.action.smoke( self:GetVec3(), SmokeColor ) -end - ---- Smoke the POINT_VEC3 Green. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeGreen() - self:F2() - self:Smoke( SMOKECOLOR.Green ) -end - ---- Smoke the POINT_VEC3 Red. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeRed() - self:F2() - self:Smoke( SMOKECOLOR.Red ) -end - ---- Smoke the POINT_VEC3 White. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeWhite() - self:F2() - self:Smoke( SMOKECOLOR.White ) -end - ---- Smoke the POINT_VEC3 Orange. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeOrange() - self:F2() - self:Smoke( SMOKECOLOR.Orange ) -end - ---- Smoke the POINT_VEC3 Blue. --- @param #POINT_VEC3 self -function POINT_VEC3:SmokeBlue() - self:F2() - self:Smoke( SMOKECOLOR.Blue ) -end - ---- Flares the point in a color. --- @param #POINT_VEC3 self --- @param Utilities.Utils#FLARECOLOR FlareColor --- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -function POINT_VEC3:Flare( FlareColor, Azimuth ) - self:F2( { FlareColor } ) - trigger.action.signalFlare( self:GetVec3(), FlareColor, Azimuth and Azimuth or 0 ) -end - ---- Flare the POINT_VEC3 White. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -function POINT_VEC3:FlareWhite( Azimuth ) - self:F2( Azimuth ) - self:Flare( FLARECOLOR.White, Azimuth ) -end - ---- Flare the POINT_VEC3 Yellow. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -function POINT_VEC3:FlareYellow( Azimuth ) - self:F2( Azimuth ) - self:Flare( FLARECOLOR.Yellow, Azimuth ) -end - ---- Flare the POINT_VEC3 Green. --- @param #POINT_VEC3 self --- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -function POINT_VEC3:FlareGreen( Azimuth ) - self:F2( Azimuth ) - self:Flare( FLARECOLOR.Green, Azimuth ) -end - ---- Flare the POINT_VEC3 Red. --- @param #POINT_VEC3 self -function POINT_VEC3:FlareRed( Azimuth ) - self:F2( Azimuth ) - self:Flare( FLARECOLOR.Red, Azimuth ) -end - -end - -do -- POINT_VEC2 - - - ---- POINT_VEC2 constructor. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North. --- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing to the Right. --- @param Dcs.DCSTypes#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height. --- @return Core.Point#POINT_VEC2 -function POINT_VEC2:New( x, y, LandHeightAdd ) - - local LandHeight = land.getHeight( { ["x"] = x, ["y"] = y } ) - - LandHeightAdd = LandHeightAdd or 0 - LandHeight = LandHeight + LandHeightAdd - - self = BASE:Inherit( self, POINT_VEC3:New( x, LandHeight, y ) ) - self:F2( self ) - - return self -end - ---- Create a new POINT_VEC2 object from Vec2 coordinates. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. --- @return Core.Point#POINT_VEC2 self -function POINT_VEC2:NewFromVec2( Vec2, LandHeightAdd ) - - local LandHeight = land.getHeight( Vec2 ) - - LandHeightAdd = LandHeightAdd or 0 - LandHeight = LandHeight + LandHeightAdd - - self = BASE:Inherit( self, POINT_VEC3:New( Vec2.x, LandHeight, Vec2.y ) ) - self:F2( self ) - - return self -end - ---- Create a new POINT_VEC2 object from Vec3 coordinates. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point. --- @return Core.Point#POINT_VEC2 self -function POINT_VEC2:NewFromVec3( Vec3 ) - - local self = BASE:Inherit( self, BASE:New() ) - local Vec2 = { x = Vec3.x, y = Vec3.z } - - local LandHeight = land.getHeight( Vec2 ) - - self = BASE:Inherit( self, POINT_VEC3:New( Vec2.x, LandHeight, Vec2.y ) ) - self:F2( self ) - - return self -end - ---- Return the x coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @return #number The x coodinate. -function POINT_VEC2:GetX() - return self.x -end - ---- Return the y coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @return #number The y coodinate. -function POINT_VEC2:GetY() - return self.z -end - ---- Return the altitude (height) of the land at the POINT_VEC2. --- @param #POINT_VEC2 self --- @return #number The land altitude. -function POINT_VEC2:GetAlt() - return land.getHeight( { x = self.x, y = self.z } ) -end - ---- Return Return the Lat(itude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.x). --- @param #POINT_VEC2 self --- @return #number The x coodinate. -function POINT_VEC2:GetLat() - return self.x -end - ---- Return the Lon(gitude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.z). --- @param #POINT_VEC2 self --- @return #number The y coodinate. -function POINT_VEC2:GetLon() - return self.z -end - ---- Set the x coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number x The x coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:SetX( x ) - self.x = x - return self -end - ---- Set the y coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number y The y coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:SetY( y ) - self.z = y - return self -end - ---- Set the Lat(itude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.x). --- @param #POINT_VEC2 self --- @param #number x The x coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:SetLat( x ) - self.x = x - return self -end - ---- Set the altitude of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number Altitude The land altitude. If nothing (nil) is given, then the current land altitude is set. --- @return #POINT_VEC2 -function POINT_VEC2:SetAlt( Altitude ) - self.y = Altitude or land.getHeight( { x = self.x, y = self.z } ) - return self -end - ---- Set the Lon(gitude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.z). --- @param #POINT_VEC2 self --- @param #number y The y coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:SetLon( z ) - self.z = z - return self -end - ---- Add to the x coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number x The x coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:AddX( x ) - self.x = self.x + x - return self -end - ---- Add to the y coordinate of the POINT_VEC2. --- @param #POINT_VEC2 self --- @param #number y The y coordinate. --- @return #POINT_VEC2 -function POINT_VEC2:AddY( y ) - self.z = self.z + y - return self -end - ---- Add to the current land height an altitude. --- @param #POINT_VEC2 self --- @param #number Altitude The Altitude to add. If nothing (nil) is given, then the current land altitude is set. --- @return #POINT_VEC2 -function POINT_VEC2:AddAlt( Altitude ) - self.y = land.getHeight( { x = self.x, y = self.z } ) + Altitude or 0 - return self -end - - - ---- Calculate the distance from a reference @{#POINT_VEC2}. --- @param #POINT_VEC2 self --- @param #POINT_VEC2 PointVec2Reference The reference @{#POINT_VEC2}. --- @return Dcs.DCSTypes#Distance The distance from the reference @{#POINT_VEC2} in meters. -function POINT_VEC2:DistanceFromPointVec2( PointVec2Reference ) - self:F2( PointVec2Reference ) - - local Distance = ( ( PointVec2Reference:GetX() - self:GetX() ) ^ 2 + ( PointVec2Reference:GetY() - self:GetY() ) ^2 ) ^0.5 - - self:T2( Distance ) - return Distance -end - ---- Calculate the distance from a reference @{DCSTypes#Vec2}. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Vec2 Vec2Reference The reference @{DCSTypes#Vec2}. --- @return Dcs.DCSTypes#Distance The distance from the reference @{DCSTypes#Vec2} in meters. -function POINT_VEC2:DistanceFromVec2( Vec2Reference ) - self:F2( Vec2Reference ) - - local Distance = ( ( Vec2Reference.x - self:GetX() ) ^ 2 + ( Vec2Reference.y - self:GetY() ) ^2 ) ^0.5 - - self:T2( Distance ) - return Distance -end - - ---- Return no text for the altitude of the POINT_VEC2. --- @param #POINT_VEC2 self --- @return #string Empty string. -function POINT_VEC2:GetAltitudeText() - return '' -end - ---- Add a Distance in meters from the POINT_VEC2 orthonormal plane, with the given angle, and calculate the new POINT_VEC2. --- @param #POINT_VEC2 self --- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters. --- @param Dcs.DCSTypes#Angle Angle The Angle in degrees. --- @return #POINT_VEC2 The new calculated POINT_VEC2. -function POINT_VEC2:Translate( Distance, Angle ) - local SX = self:GetX() - local SY = self:GetY() - local Radians = Angle / 180 * math.pi - local TX = Distance * math.cos( Radians ) + SX - local TY = Distance * math.sin( Radians ) + SY - - return POINT_VEC2:New( TX, TY ) -end - -end - - ---- **Core** - MESSAGE class takes are of the **real-time notifications** and **messages to players** during a simulation. --- --- ![Banner Image](..\Presentations\MESSAGE\Dia1.JPG) --- --- === --- --- # 1) @{Message#MESSAGE} class, extends @{Base#BASE} --- --- Message System to display Messages to Clients, Coalitions or All. --- Messages are shown on the display panel for an amount of seconds, and will then disappear. --- Messages can contain a category which is indicating the category of the message. --- --- ## 1.1) MESSAGE construction --- --- Messages are created with @{Message#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet. --- To send messages, you need to use the To functions. --- --- ## 1.2) Send messages to an audience --- --- Messages are sent: --- --- * To a @{Client} using @{Message#MESSAGE.ToClient}(). --- * To a @{Group} using @{Message#MESSAGE.ToGroup}() --- * To a coalition using @{Message#MESSAGE.ToCoalition}(). --- * To the red coalition using @{Message#MESSAGE.ToRed}(). --- * To the blue coalition using @{Message#MESSAGE.ToBlue}(). --- * To all Players using @{Message#MESSAGE.ToAll}(). --- --- ## 1.3) Send conditionally to an audience --- --- Messages can be sent conditionally to an audience (when a condition is true): --- --- * To all players using @{Message#MESSAGE.ToAllIf}(). --- * To a coalition using @{Message#MESSAGE.ToCoalitionIf}(). --- --- --- @module Message - ---- The MESSAGE class --- @type MESSAGE --- @extends Core.Base#BASE -MESSAGE = { - ClassName = "MESSAGE", - MessageCategory = 0, - MessageID = 0, -} - - ---- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients. --- @param self --- @param #string MessageText is the text of the Message. --- @param #number MessageDuration is a number in seconds of how long the MESSAGE should be shown on the display panel. --- @param #string MessageCategory (optional) is a string expressing the "category" of the Message. The category will be shown as the first text in the message followed by a ": ". --- @return #MESSAGE --- @usage --- -- Create a series of new Messages. --- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score". --- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win". --- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score". --- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score". --- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" ) --- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" ) --- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" ) --- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score") -function MESSAGE:New( MessageText, MessageDuration, MessageCategory ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( { MessageText, MessageDuration, MessageCategory } ) - - -- When no MessageCategory is given, we don't show it as a title... - if MessageCategory and MessageCategory ~= "" then - if MessageCategory:sub(-1) ~= "\n" then - self.MessageCategory = MessageCategory .. ": " - else - self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n" - end - else - self.MessageCategory = "" - end - - self.MessageDuration = MessageDuration or 5 - self.MessageTime = timer.getTime() - self.MessageText = MessageText - - self.MessageSent = false - self.MessageGroup = false - self.MessageCoalition = false - - return self -end - ---- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player". --- @param #MESSAGE self --- @param Wrapper.Client#CLIENT Client is the Group of the Client. --- @return #MESSAGE --- @usage --- -- Send the 2 messages created with the @{New} method to the Client Group. --- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1. --- ClientGroup = Group.getByName( "ClientGroup" ) --- --- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup ) --- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup ) --- or --- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup ) --- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup ) --- or --- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ) --- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ) --- MessageClient1:ToClient( ClientGroup ) --- MessageClient2:ToClient( ClientGroup ) -function MESSAGE:ToClient( Client ) - self:F( Client ) - - if Client and Client:GetClientGroupID() then - - local ClientGroupID = Client:GetClientGroupID() - self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) - trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration ) - end - - return self -end - ---- Sends a MESSAGE to a Group. --- @param #MESSAGE self --- @param Wrapper.Group#GROUP Group is the Group. --- @return #MESSAGE -function MESSAGE:ToGroup( Group ) - self:F( Group.GroupName ) - - if Group then - - self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) - trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration ) - end - - return self -end ---- Sends a MESSAGE to the Blue coalition. --- @param #MESSAGE self --- @return #MESSAGE --- @usage --- -- Send a message created with the @{New} method to the BLUE coalition. --- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue() --- or --- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue() --- or --- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) --- MessageBLUE:ToBlue() -function MESSAGE:ToBlue() - self:F() - - self:ToCoalition( coalition.side.BLUE ) - - return self -end - ---- Sends a MESSAGE to the Red Coalition. --- @param #MESSAGE self --- @return #MESSAGE --- @usage --- -- Send a message created with the @{New} method to the RED coalition. --- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed() --- or --- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed() --- or --- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) --- MessageRED:ToRed() -function MESSAGE:ToRed( ) - self:F() - - self:ToCoalition( coalition.side.RED ) - - return self -end - ---- Sends a MESSAGE to a Coalition. --- @param #MESSAGE self --- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}. --- @return #MESSAGE --- @usage --- -- Send a message created with the @{New} method to the RED coalition. --- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED ) --- or --- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED ) --- or --- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ) --- MessageRED:ToCoalition( coalition.side.RED ) -function MESSAGE:ToCoalition( CoalitionSide ) - self:F( CoalitionSide ) - - if CoalitionSide then - self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) - trigger.action.outTextForCoalition( CoalitionSide, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration ) - end - - return self -end - ---- Sends a MESSAGE to a Coalition if the given Condition is true. --- @param #MESSAGE self --- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}. --- @return #MESSAGE -function MESSAGE:ToCoalitionIf( CoalitionSide, Condition ) - self:F( CoalitionSide ) - - if Condition and Condition == true then - self:ToCoalition( CoalitionSide ) - end - - return self -end - ---- Sends a MESSAGE to all players. --- @param #MESSAGE self --- @return #MESSAGE --- @usage --- -- Send a message created to all players. --- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll() --- or --- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll() --- or --- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ) --- MessageAll:ToAll() -function MESSAGE:ToAll() - self:F() - - self:ToCoalition( coalition.side.RED ) - self:ToCoalition( coalition.side.BLUE ) - - return self -end - - ---- Sends a MESSAGE to all players if the given Condition is true. --- @param #MESSAGE self --- @return #MESSAGE -function MESSAGE:ToAllIf( Condition ) - - if Condition and Condition == true then - self:ToCoalition( coalition.side.RED ) - self:ToCoalition( coalition.side.BLUE ) - end - - return self -end ---- **Core** - The **FSM** (**F**inite **S**tate **M**achine) class and derived **FSM\_** classes --- are design patterns allowing efficient (long-lasting) processes and workflows. --- --- ![Banner Image](..\Presentations\FSM\Dia1.JPG) --- --- === --- --- A FSM can only be in one of a finite number of states. --- The machine is in only one state at a time; the state it is in at any given time is called the **current state**. --- It can change from one state to another when initiated by an **__internal__ or __external__ triggering event**, which is called a **transition**. --- An **FSM implementation** is defined by **a list of its states**, **its initial state**, and **the triggering events** for **each possible transition**. --- An FSM implementation is composed out of **two parts**, a set of **state transition rules**, and an implementation set of **state transition handlers**, implementing those transitions. --- --- The FSM class supports a **hierarchical implementation of a Finite State Machine**, --- that is, it allows to **embed existing FSM implementations in a master FSM**. --- FSM hierarchies allow for efficient FSM re-use, **not having to re-invent the wheel every time again** when designing complex processes. --- --- ![Workflow Example](..\Presentations\FSM\Dia2.JPG) --- --- The above diagram shows a graphical representation of a FSM implementation for a **Task**, which guides a Human towards a Zone, --- orders him to destroy x targets and account the results. --- Other examples of ready made FSM could be: --- --- * route a plane to a zone flown by a human --- * detect targets by an AI and report to humans --- * account for destroyed targets by human players --- * handle AI infantry to deploy from or embark to a helicopter or airplane or vehicle --- * let an AI patrol a zone --- --- The **MOOSE framework** uses extensively the FSM class and derived FSM\_ classes, --- because **the goal of MOOSE is to simplify mission design complexity for mission building**. --- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes. --- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used, --- and tailored** by mission designers through **the implementation of Transition Handlers**. --- Each of these FSM implementation classes start either with: --- --- * an acronym **AI\_**, which indicates an FSM implementation directing **AI controlled** @{GROUP} and/or @{UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class. --- * an acronym **TASK\_**, which indicates an FSM implementation executing a @{TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class. --- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{TASK}, seated in a @{CLIENT} (slot) or a @{UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class. --- --- Detailed explanations and API specifics are further below clarified and FSM derived class specifics are described in those class documentation sections. --- --- ##__Dislaimer:__ --- The FSM class development is based on a finite state machine implementation made by Conroy Kyle. --- The state machine can be found on [github](https://github.com/kyleconroy/lua-state-machine) --- I've reworked this development (taken the concept), and created a **hierarchical state machine** out of it, embedded within the DCS simulator. --- Additionally, I've added extendability and created an API that allows seamless FSM implementation. --- --- The following derived classes are available in the MOOSE framework, that implement a specialised form of a FSM: --- --- * @{#FSM_TASK}: Models Finite State Machines for @{Task}s. --- * @{#FSM_PROCESS}: Models Finite State Machines for @{Task} actions, which control @{Client}s. --- * @{#FSM_CONTROLLABLE}: Models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s. --- * @{#FSM_SET}: Models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here --- for multiple objects or the position of the state machine in the process. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params ) --- YYYY-MM-DD: CLASS:**NewFunction( Params )** added --- --- Hereby the change log: --- --- * 2016-12-18: Released. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * [**Pikey**](https://forums.eagle.ru/member.php?u=62835): Review of documentation & advice for improvements. --- --- ### Authors: --- --- * [**FlightControl**](https://forums.eagle.ru/member.php?u=89536): Design & Programming & documentation. --- --- @module Fsm - -do -- FSM - - --- FSM class - --- @type FSM - -- @extends Core.Base#BASE - - - --- # 1) FSM class, extends @{Base#BASE} - -- - -- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG) - -- - -- The FSM class is the base class of all FSM\_ derived classes. It implements the main functionality to define and execute Finite State Machines. - -- The derived FSM\_ classes extend the Finite State Machine functionality to run a workflow process for a specific purpose or component. - -- - -- Finite State Machines have **Transition Rules**, **Transition Handlers** and **Event Triggers**. - -- - -- The **Transition Rules** define the "Process Flow Boundaries", that is, - -- the path that can be followed hopping from state to state upon triggered events. - -- If an event is triggered, and there is no valid path found for that event, - -- an error will be raised and the FSM will stop functioning. - -- - -- The **Transition Handlers** are special methods that can be defined by the mission designer, following a defined syntax. - -- If the FSM object finds a method of such a handler, then the method will be called by the FSM, passing specific parameters. - -- The method can then define its own custom logic to implement the FSM workflow, and to conduct other actions. - -- - -- The **Event Triggers** are methods that are defined by the FSM, which the mission designer can use to implement the workflow. - -- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine. - -- - -- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation. - -- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**. - -- - -- ## 1.1) FSM Linear Transitions - -- - -- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**. - -- The Lineair transition rule evaluation will always be done from the **current state** of the FSM. - -- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop. - -- - -- ### 1.1.1) FSM Transition Rules - -- - -- The FSM has transition rules that it follows and validates, as it walks the process. - -- These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event. - -- - -- The method @{#FSM.AddTransition}() specifies a new possible Transition Rule for the FSM. - -- - -- The initial state can be defined using the method @{#FSM.SetStartState}(). The default start state of an FSM is "None". - -- - -- Find below an example of a Linear Transition Rule definition for an FSM. - -- - -- local Fsm3Switch = FSM:New() -- #FsmDemo - -- FsmSwitch:SetStartState( "Off" ) - -- FsmSwitch:AddTransition( "Off", "SwitchOn", "On" ) - -- FsmSwitch:AddTransition( "Off", "SwitchMiddle", "Middle" ) - -- FsmSwitch:AddTransition( "On", "SwitchOff", "Off" ) - -- FsmSwitch:AddTransition( "Middle", "SwitchOff", "Off" ) - -- - -- The above code snippet models a 3-way switch Linear Transition: - -- - -- * It can be switched **On** by triggering event **SwitchOn**. - -- * It can be switched to the **Middle** position, by triggering event **SwitchMiddle**. - -- * It can be switched **Off** by triggering event **SwitchOff**. - -- * Note that once the Switch is **On** or **Middle**, it can only be switched **Off**. - -- - -- ### Some additional comments: - -- - -- Note that Linear Transition Rules **can be declared in a few variations**: - -- - -- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**. - -- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM. - -- - -- The below code snippet shows how the two last lines can be rewritten and consensed. - -- - -- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" ) - -- - -- ### 1.1.2) Transition Handling - -- - -- ![Transition Handlers](..\Presentations\FSM\Dia4.JPG) - -- - -- An FSM transitions in **4 moments** when an Event is being triggered and processed. - -- The mission designer can define for each moment specific logic within methods implementations following a defined API syntax. - -- These methods define the flow of the FSM process; because in those methods the FSM Internal Events will be triggered. - -- - -- * To handle **State** transition moments, create methods starting with OnLeave or OnEnter concatenated with the State name. - -- * To handle **Event** transition moments, create methods starting with OnBefore or OnAfter concatenated with the Event name. - -- - -- **The OnLeave and OnBefore transition methods may return false, which will cancel the transition!** - -- - -- Transition Handler methods need to follow the above specified naming convention, but are also passed parameters from the FSM. - -- These parameters are on the correct order: From, Event, To: - -- - -- * From = A string containing the From state. - -- * Event = A string containing the Event name that was triggered. - -- * To = A string containing the To state. - -- - -- On top, each of these methods can have a variable amount of parameters passed. See the example in section [1.1.3](#1.1.3\)-event-triggers). - -- - -- ### 1.1.3) Event Triggers - -- - -- ![Event Triggers](..\Presentations\FSM\Dia5.JPG) - -- - -- The FSM creates for each Event two **Event Trigger methods**. - -- There are two modes how Events can be triggered, which is **synchronous** and **asynchronous**: - -- - -- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**. - -- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__. - -- - -- The destinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time. - -- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact. - -- - -- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering. - -- - -- function FSM:OnAfterEvent( From, Event, To, Amount ) - -- self:T( { Amount = Amount } ) - -- end - -- - -- local Amount = 1 - -- FSM:__Event( 5, Amount ) - -- - -- Amount = Amount + 1 - -- FSM:Event( Text, Amount ) - -- - -- In this example, the **:OnAfterEvent**() Transition Handler implementation will get called when **Event** is being triggered. - -- Before we go into more detail, let's look at the last 4 lines of the example. - -- The last line triggers synchronously the **Event**, and passes Amount as a parameter. - -- The 3rd last line of the example triggers asynchronously **Event**. - -- Event will be processed after 5 seconds, and Amount is given as a parameter. - -- - -- The output of this little code fragment will be: - -- - -- * Amount = 2 - -- * Amount = 2 - -- - -- Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing! - -- - -- ### 1.1.4) Linear Transition Example - -- - -- This example is fully implemented in the MOOSE test mission on GITHUB: [FSM-100 - Transition Explanation](https://github.com/FlightControl-Master/MOOSE/blob/master/Moose%20Test%20Missions/FSM%20-%20Finite%20State%20Machine/FSM-100%20-%20Transition%20Explanation/FSM-100%20-%20Transition%20Explanation.lua) - -- - -- It models a unit standing still near Batumi, and flaring every 5 seconds while switching between a Green flare and a Red flare. - -- The purpose of this example is not to show how exciting flaring is, but it demonstrates how a Linear Transition FSM can be build. - -- Have a look at the source code. The source code is also further explained below in this section. - -- - -- The example creates a new FsmDemo object from class FSM. - -- It will set the start state of FsmDemo to state **Green**. - -- Two Linear Transition Rules are created, where upon the event **Switch**, - -- the FsmDemo will transition from state **Green** to **Red** and from **Red** back to **Green**. - -- - -- ![Transition Example](..\Presentations\FSM\Dia6.JPG) - -- - -- local FsmDemo = FSM:New() -- #FsmDemo - -- FsmDemo:SetStartState( "Green" ) - -- FsmDemo:AddTransition( "Green", "Switch", "Red" ) - -- FsmDemo:AddTransition( "Red", "Switch", "Green" ) - -- - -- In the above example, the FsmDemo could flare every 5 seconds a Green or a Red flare into the air. - -- The next code implements this through the event handling method **OnAfterSwitch**. - -- - -- ![Transition Flow](..\Presentations\FSM\Dia7.JPG) - -- - -- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) - -- self:T( { From, Event, To, FsmUnit } ) - -- - -- if From == "Green" then - -- FsmUnit:Flare(FLARECOLOR.Green) - -- else - -- if From == "Red" then - -- FsmUnit:Flare(FLARECOLOR.Red) - -- end - -- end - -- self:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. - -- end - -- - -- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the first Switch event to happen in 5 seconds. - -- - -- The OnAfterSwitch implements a loop. The last line of the code fragment triggers the Switch Event within 5 seconds. - -- Upon the event execution (after 5 seconds), the OnAfterSwitch method is called of FsmDemo (cfr. the double point notation!!! ":"). - -- The OnAfterSwitch method receives from the FSM the 3 transition parameter details ( From, Event, To ), - -- and one additional parameter that was given when the event was triggered, which is in this case the Unit that is used within OnSwitchAfter. - -- - -- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) - -- - -- For debugging reasons the received parameters are traced within the DCS.log. - -- - -- self:T( { From, Event, To, FsmUnit } ) - -- - -- The method will check if the From state received is either "Green" or "Red" and will flare the respective color from the FsmUnit. - -- - -- if From == "Green" then - -- FsmUnit:Flare(FLARECOLOR.Green) - -- else - -- if From == "Red" then - -- FsmUnit:Flare(FLARECOLOR.Red) - -- end - -- end - -- - -- It is important that the Switch event is again triggered, otherwise, the FsmDemo would stop working after having the first Event being handled. - -- - -- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds. - -- - -- The below code fragment extends the FsmDemo, demonstrating multiple **From states declared as a table**, adding a **Linear Transition Rule**. - -- The new event **Stop** will cancel the Switching process. - -- The transition for event Stop can be executed if the current state of the FSM is either "Red" or "Green". - -- - -- local FsmDemo = FSM:New() -- #FsmDemo - -- FsmDemo:SetStartState( "Green" ) - -- FsmDemo:AddTransition( "Green", "Switch", "Red" ) - -- FsmDemo:AddTransition( "Red", "Switch", "Green" ) - -- FsmDemo:AddTransition( { "Red", "Green" }, "Stop", "Stopped" ) - -- - -- The transition for event Stop can also be simplified, as any current state of the FSM is valid. - -- - -- FsmDemo:AddTransition( "*", "Stop", "Stopped" ) - -- - -- So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped. - -- And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt. - -- - -- ## 1.5) FSM Hierarchical Transitions - -- - -- Hierarchical Transitions allow to re-use readily available and implemented FSMs. - -- This becomes in very useful for mission building, where mission designers build complex processes and workflows, - -- combining smaller FSMs to one single FSM. - -- - -- The FSM can embed **Sub-FSMs** that will execute and return **multiple possible Return (End) States**. - -- Depending upon **which state is returned**, the main FSM can continue the flow **triggering specific events**. - -- - -- The method @{#FSM.AddProcess}() adds a new Sub-FSM to the FSM. - -- - -- === - -- - -- @field #FSM FSM - -- - FSM = { - ClassName = "FSM", - } - - --- Creates a new FSM object. - -- @param #FSM self - -- @return #FSM - function FSM:New( FsmT ) - - -- Inherits from BASE - self = BASE:Inherit( self, BASE:New() ) - - self.options = options or {} - self.options.subs = self.options.subs or {} - self.current = self.options.initial or 'none' - self.Events = {} - self.subs = {} - self.endstates = {} - - self.Scores = {} - - self._StartState = "none" - self._Transitions = {} - self._Processes = {} - self._EndStates = {} - self._Scores = {} - self._EventSchedules = {} - - self.CallScheduler = SCHEDULER:New( self ) - - - return self - end - - - --- Sets the start state of the FSM. - -- @param #FSM self - -- @param #string State A string defining the start state. - function FSM:SetStartState( State ) - - self._StartState = State - self.current = State - end - - - --- Returns the start state of the FSM. - -- @param #FSM self - -- @return #string A string containing the start state. - function FSM:GetStartState() - - return self._StartState or {} - end - - --- Add a new transition rule to the FSM. - -- A transition rule defines when and if the FSM can transition from a state towards another state upon a triggered event. - -- @param #FSM self - -- @param #table From Can contain a string indicating the From state or a table of strings containing multiple From states. - -- @param #string Event The Event name. - -- @param #string To The To state. - function FSM:AddTransition( From, Event, To ) - - local Transition = {} - Transition.From = From - Transition.Event = Event - Transition.To = To - - self:T( Transition ) - - self._Transitions[Transition] = Transition - self:_eventmap( self.Events, Transition ) - end - - - --- Returns a table of the transition rules defined within the FSM. - -- @return #table - function FSM:GetTransitions() - - return self._Transitions or {} - end - - --- Set the default @{Process} template with key ProcessName providing the ProcessClass and the process object when it is assigned to a @{Controllable} by the task. - -- @param #FSM self - -- @param #table From Can contain a string indicating the From state or a table of strings containing multiple From states. - -- @param #string Event The Event name. - -- @param Core.Fsm#FSM_PROCESS Process An sub-process FSM. - -- @param #table ReturnEvents A table indicating for which returned events of the SubFSM which Event must be triggered in the FSM. - -- @return Core.Fsm#FSM_PROCESS The SubFSM. - function FSM:AddProcess( From, Event, Process, ReturnEvents ) - self:T( { From, Event, Process, ReturnEvents } ) - - local Sub = {} - Sub.From = From - Sub.Event = Event - Sub.fsm = Process - Sub.StartEvent = "Start" - Sub.ReturnEvents = ReturnEvents - - self._Processes[Sub] = Sub - - self:_submap( self.subs, Sub, nil ) - - self:AddTransition( From, Event, From ) - - return Process - end - - - --- Returns a table of the SubFSM rules defined within the FSM. - -- @return #table - function FSM:GetProcesses() - - return self._Processes or {} - end - - function FSM:GetProcess( From, Event ) - - for ProcessID, Process in pairs( self:GetProcesses() ) do - if Process.From == From and Process.Event == Event then - return Process.fsm - end - end - - error( "Sub-Process from state " .. From .. " with event " .. Event .. " not found!" ) - end - - --- Adds an End state. - function FSM:AddEndState( State ) - - self._EndStates[State] = State - self.endstates[State] = State - end - - --- Returns the End states. - function FSM:GetEndStates() - - return self._EndStates or {} - end - - - --- Adds a score for the FSM to be achieved. - -- @param #FSM self - -- @param #string State is the state of the process when the score needs to be given. (See the relevant state descriptions of the process). - -- @param #string ScoreText is a text describing the score that is given according the status. - -- @param #number Score is a number providing the score of the status. - -- @return #FSM self - function FSM:AddScore( State, ScoreText, Score ) - self:F( { State, ScoreText, Score } ) - - self._Scores[State] = self._Scores[State] or {} - self._Scores[State].ScoreText = ScoreText - self._Scores[State].Score = Score - - return self - end - - --- Adds a score for the FSM_PROCESS to be achieved. - -- @param #FSM self - -- @param #string From is the From State of the main process. - -- @param #string Event is the Event of the main process. - -- @param #string State is the state of the process when the score needs to be given. (See the relevant state descriptions of the process). - -- @param #string ScoreText is a text describing the score that is given according the status. - -- @param #number Score is a number providing the score of the status. - -- @return #FSM self - function FSM:AddScoreProcess( From, Event, State, ScoreText, Score ) - self:F( { From, Event, State, ScoreText, Score } ) - - local Process = self:GetProcess( From, Event ) - - Process._Scores[State] = Process._Scores[State] or {} - Process._Scores[State].ScoreText = ScoreText - Process._Scores[State].Score = Score - - self:T( Process._Scores ) - - return Process - end - - --- Returns a table with the scores defined. - function FSM:GetScores() - - return self._Scores or {} - end - - --- Returns a table with the Subs defined. - function FSM:GetSubs() - - return self.options.subs - end - - - function FSM:LoadCallBacks( CallBackTable ) - - for name, callback in pairs( CallBackTable or {} ) do - self[name] = callback - end - - end - - function FSM:_eventmap( Events, EventStructure ) - - local Event = EventStructure.Event - local __Event = "__" .. EventStructure.Event - self[Event] = self[Event] or self:_create_transition(Event) - self[__Event] = self[__Event] or self:_delayed_transition(Event) - self:T( "Added methods: " .. Event .. ", " .. __Event ) - Events[Event] = self.Events[Event] or { map = {} } - self:_add_to_map( Events[Event].map, EventStructure ) - - end - - function FSM:_submap( subs, sub, name ) - self:F( { sub = sub, name = name } ) - subs[sub.From] = subs[sub.From] or {} - subs[sub.From][sub.Event] = subs[sub.From][sub.Event] or {} - - -- Make the reference table weak. - -- setmetatable( subs[sub.From][sub.Event], { __mode = "k" } ) - - subs[sub.From][sub.Event][sub] = {} - subs[sub.From][sub.Event][sub].fsm = sub.fsm - subs[sub.From][sub.Event][sub].StartEvent = sub.StartEvent - subs[sub.From][sub.Event][sub].ReturnEvents = sub.ReturnEvents or {} -- these events need to be given to find the correct continue event ... if none given, the processing will stop. - subs[sub.From][sub.Event][sub].name = name - subs[sub.From][sub.Event][sub].fsmparent = self - end - - - function FSM:_call_handler( handler, params, EventName ) - - local ErrorHandler = function( errmsg ) - - env.info( "Error in SCHEDULER function:" .. errmsg ) - if debug ~= nil then - env.info( debug.traceback() ) - end - - return errmsg - end - if self[handler] then - self:T( "Calling " .. handler ) - self._EventSchedules[EventName] = nil - local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler ) - return Value - end - end - - function FSM._handler( self, EventName, ... ) - - local Can, to = self:can( EventName ) - - if to == "*" then - to = self.current - end - - if Can then - local from = self.current - local params = { from, EventName, to, ... } - - if self.Controllable then - self:T( "FSM Transition for " .. self.Controllable.ControllableName .. " :" .. self.current .. " --> " .. EventName .. " --> " .. to ) - else - self:T( "FSM Transition:" .. self.current .. " --> " .. EventName .. " --> " .. to ) - end - - if ( self:_call_handler("onbefore" .. EventName, params, EventName ) == false ) - or ( self:_call_handler("OnBefore" .. EventName, params, EventName ) == false ) - or ( self:_call_handler("onleave" .. from, params, EventName ) == false ) - or ( self:_call_handler("OnLeave" .. from, params, EventName ) == false ) then - self:T( "Cancel Transition" ) - return false - end - - self.current = to - - local execute = true - - local subtable = self:_gosub( from, EventName ) - for _, sub in pairs( subtable ) do - --if sub.nextevent then - -- self:F2( "nextevent = " .. sub.nextevent ) - -- self[sub.nextevent]( self ) - --end - self:T( "calling sub start event: " .. sub.StartEvent ) - sub.fsm.fsmparent = self - sub.fsm.ReturnEvents = sub.ReturnEvents - sub.fsm[sub.StartEvent]( sub.fsm ) - execute = false - end - - local fsmparent, Event = self:_isendstate( to ) - if fsmparent and Event then - self:F2( { "end state: ", fsmparent, Event } ) - self:_call_handler("onenter" .. to, params, EventName ) - self:_call_handler("OnEnter" .. to, params, EventName ) - self:_call_handler("onafter" .. EventName, params, EventName ) - self:_call_handler("OnAfter" .. EventName, params, EventName ) - self:_call_handler("onstatechange", params, EventName ) - fsmparent[Event]( fsmparent ) - execute = false - end - - if execute then - -- only execute the call if the From state is not equal to the To state! Otherwise this function should never execute! - --if from ~= to then - self:_call_handler("onenter" .. to, params, EventName ) - self:_call_handler("OnEnter" .. to, params, EventName ) - --end - - self:_call_handler("onafter" .. EventName, params, EventName ) - self:_call_handler("OnAfter" .. EventName, params, EventName ) - - self:_call_handler("onstatechange", params, EventName ) - end - else - self:T( "Cannot execute transition." ) - self:T( { From = self.current, Event = EventName, To = to, Can = Can } ) - end - - return nil - end - - function FSM:_delayed_transition( EventName ) - return function( self, DelaySeconds, ... ) - self:T2( "Delayed Event: " .. EventName ) - local CallID = 0 - if DelaySeconds ~= nil then - if DelaySeconds < 0 then -- Only call the event ONCE! - DelaySeconds = math.abs( DelaySeconds ) - if not self._EventSchedules[EventName] then - CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1 ) - self._EventSchedules[EventName] = CallID - else - -- reschedule - end - else - CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1 ) - end - else - error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." ) - end - self:T2( { CallID = CallID } ) - end - end - - function FSM:_create_transition( EventName ) - return function( self, ... ) return self._handler( self, EventName , ... ) end - end - - function FSM:_gosub( ParentFrom, ParentEvent ) - local fsmtable = {} - if self.subs[ParentFrom] and self.subs[ParentFrom][ParentEvent] then - self:T( { ParentFrom, ParentEvent, self.subs[ParentFrom], self.subs[ParentFrom][ParentEvent] } ) - return self.subs[ParentFrom][ParentEvent] - else - return {} - end - end - - function FSM:_isendstate( Current ) - local FSMParent = self.fsmparent - if FSMParent and self.endstates[Current] then - self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } ) - FSMParent.current = Current - local ParentFrom = FSMParent.current - self:T( ParentFrom ) - self:T( self.ReturnEvents ) - local Event = self.ReturnEvents[Current] - self:T( { ParentFrom, Event, self.ReturnEvents } ) - if Event then - return FSMParent, Event - else - self:T( { "Could not find parent event name for state ", ParentFrom } ) - end - end - - return nil - end - - function FSM:_add_to_map( Map, Event ) - self:F3( { Map, Event } ) - if type(Event.From) == 'string' then - Map[Event.From] = Event.To - else - for _, From in ipairs(Event.From) do - Map[From] = Event.To - end - end - self:T3( { Map, Event } ) - end - - function FSM:GetState() - return self.current - end - - - function FSM:Is( State ) - return self.current == State - end - - function FSM:is(state) - return self.current == state - end - - function FSM:can(e) - local Event = self.Events[e] - self:F3( { self.current, Event } ) - local To = Event and Event.map[self.current] or Event.map['*'] - return To ~= nil, To - end - - function FSM:cannot(e) - return not self:can(e) - end - -end - -do -- FSM_CONTROLLABLE - - --- @type FSM_CONTROLLABLE - -- @field Wrapper.Controllable#CONTROLLABLE Controllable - -- @extends Core.Fsm#FSM - - --- # FSM_CONTROLLABLE, extends @{#FSM} - -- - -- FSM_CONTROLLABLE class models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s. - -- - -- === - -- - -- @field #FSM_CONTROLLABLE FSM_CONTROLLABLE - -- - FSM_CONTROLLABLE = { - ClassName = "FSM_CONTROLLABLE", - } - - --- Creates a new FSM_CONTROLLABLE object. - -- @param #FSM_CONTROLLABLE self - -- @param #table FSMT Finite State Machine Table - -- @param Wrapper.Controllable#CONTROLLABLE Controllable (optional) The CONTROLLABLE object that the FSM_CONTROLLABLE governs. - -- @return #FSM_CONTROLLABLE - function FSM_CONTROLLABLE:New( FSMT, Controllable ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM:New( FSMT ) ) -- Core.Fsm#FSM_CONTROLLABLE - - if Controllable then - self:SetControllable( Controllable ) - end - - self:AddTransition( "*", "Stop", "Stopped" ) - - --- OnBefore Transition Handler for Event Stop. - -- @function [parent=#FSM_CONTROLLABLE] OnBeforeStop - -- @param #FSM_CONTROLLABLE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Stop. - -- @function [parent=#FSM_CONTROLLABLE] OnAfterStop - -- @param #FSM_CONTROLLABLE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Stop. - -- @function [parent=#FSM_CONTROLLABLE] Stop - -- @param #FSM_CONTROLLABLE self - - --- Asynchronous Event Trigger for Event Stop. - -- @function [parent=#FSM_CONTROLLABLE] __Stop - -- @param #FSM_CONTROLLABLE self - -- @param #number Delay The delay in seconds. - - --- OnLeave Transition Handler for State Stopped. - -- @function [parent=#FSM_CONTROLLABLE] OnLeaveStopped - -- @param #FSM_CONTROLLABLE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Stopped. - -- @function [parent=#FSM_CONTROLLABLE] OnEnterStopped - -- @param #FSM_CONTROLLABLE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - return self - end - - --- OnAfter Transition Handler for Event Stop. - -- @function [parent=#FSM_CONTROLLABLE] OnAfterStop - -- @param #FSM_CONTROLLABLE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - function FSM_CONTROLLABLE:OnAfterStop(Controllable,From,Event,To) - - -- Clear all pending schedules - self.CallScheduler:Clear() - end - - --- Sets the CONTROLLABLE object that the FSM_CONTROLLABLE governs. - -- @param #FSM_CONTROLLABLE self - -- @param Wrapper.Controllable#CONTROLLABLE FSMControllable - -- @return #FSM_CONTROLLABLE - function FSM_CONTROLLABLE:SetControllable( FSMControllable ) - self:F( FSMControllable ) - self.Controllable = FSMControllable - end - - --- Gets the CONTROLLABLE object that the FSM_CONTROLLABLE governs. - -- @param #FSM_CONTROLLABLE self - -- @return Wrapper.Controllable#CONTROLLABLE - function FSM_CONTROLLABLE:GetControllable() - return self.Controllable - end - - function FSM_CONTROLLABLE:_call_handler( handler, params, EventName ) - - local ErrorHandler = function( errmsg ) - - env.info( "Error in SCHEDULER function:" .. errmsg ) - if debug ~= nil then - env.info( debug.traceback() ) - end - - return errmsg - end - - if self[handler] then - self:F3( "Calling " .. handler ) - self._EventSchedules[EventName] = nil - local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, unpack( params ) ) end, ErrorHandler ) - return Value - --return self[handler]( self, self.Controllable, unpack( params ) ) - end - end - -end - -do -- FSM_PROCESS - - --- @type FSM_PROCESS - -- @field Tasking.Task#TASK Task - -- @extends Core.Fsm#FSM_CONTROLLABLE - - - --- # FSM_PROCESS, extends @{#FSM} - -- - -- FSM_PROCESS class models Finite State Machines for @{Task} actions, which control @{Client}s. - -- - -- === - -- - -- @field #FSM_PROCESS FSM_PROCESS - -- - FSM_PROCESS = { - ClassName = "FSM_PROCESS", - } - - --- Creates a new FSM_PROCESS object. - -- @param #FSM_PROCESS self - -- @return #FSM_PROCESS - function FSM_PROCESS:New( Controllable, Task ) - - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_PROCESS - - self:F( Controllable, Task ) - - self:Assign( Controllable, Task ) - - return self - end - - function FSM_PROCESS:Init( FsmProcess ) - self:T( "No Initialisation" ) - end - - function FSM_PROCESS:_call_handler( handler, params, EventName ) - - local ErrorHandler = function( errmsg ) - - env.info( "Error in FSM_PROCESS call handler:" .. errmsg ) - if debug ~= nil then - env.info( debug.traceback() ) - end - - return errmsg - end - - if self[handler] then - self:F3( "Calling " .. handler ) - self._EventSchedules[EventName] = nil - local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler ) - return Value - --return self[handler]( self, self.Controllable, unpack( params ) ) - end - end - - --- Creates a new FSM_PROCESS object based on this FSM_PROCESS. - -- @param #FSM_PROCESS self - -- @return #FSM_PROCESS - function FSM_PROCESS:Copy( Controllable, Task ) - self:T( { self:GetClassNameAndID() } ) - - - local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS - - NewFsm:Assign( Controllable, Task ) - - -- Polymorphic call to initialize the new FSM_PROCESS based on self FSM_PROCESS - NewFsm:Init( self ) - - -- Set Start State - NewFsm:SetStartState( self:GetStartState() ) - - -- Copy Transitions - for TransitionID, Transition in pairs( self:GetTransitions() ) do - NewFsm:AddTransition( Transition.From, Transition.Event, Transition.To ) - end - - -- Copy Processes - for ProcessID, Process in pairs( self:GetProcesses() ) do - self:E( { Process} ) - local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents ) - end - - -- Copy End States - for EndStateID, EndState in pairs( self:GetEndStates() ) do - self:T( EndState ) - NewFsm:AddEndState( EndState ) - end - - -- Copy the score tables - for ScoreID, Score in pairs( self:GetScores() ) do - self:T( Score ) - NewFsm:AddScore( ScoreID, Score.ScoreText, Score.Score ) - end - - return NewFsm - end - - --- Removes an FSM_PROCESS object. - -- @param #FSM_PROCESS self - -- @return #FSM_PROCESS - function FSM_PROCESS:Remove() - self:T( { self:GetClassNameAndID() } ) - - -- Copy Processes - for ProcessID, Process in pairs( self:GetProcesses() ) do - self:E( { Process} ) - Process.fsm:Remove() - Process.fsm = nil - end - - return self - end - - --- Sets the task of the process. - -- @param #FSM_PROCESS self - -- @param Tasking.Task#TASK Task - -- @return #FSM_PROCESS - function FSM_PROCESS:SetTask( Task ) - - self.Task = Task - - return self - end - - --- Gets the task of the process. - -- @param #FSM_PROCESS self - -- @return Tasking.Task#TASK - function FSM_PROCESS:GetTask() - - return self.Task - end - - --- Gets the mission of the process. - -- @param #FSM_PROCESS self - -- @return Tasking.Mission#MISSION - function FSM_PROCESS:GetMission() - - return self.Task.Mission - end - - --- Gets the mission of the process. - -- @param #FSM_PROCESS self - -- @return Tasking.CommandCenter#COMMANDCENTER - function FSM_PROCESS:GetCommandCenter() - - return self:GetTask():GetMission():GetCommandCenter() - end - --- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP. - - --- Send a message of the @{Task} to the Group of the Unit. --- @param #FSM_PROCESS self -function FSM_PROCESS:Message( Message ) - self:F( { Message = Message } ) - - local CC = self:GetCommandCenter() - local TaskGroup = self.Controllable:GetGroup() - - local PlayerName = self.Controllable:GetPlayerName() -- Only for a unit - PlayerName = PlayerName and " (" .. PlayerName .. ")" or "" -- If PlayerName is nil, then keep it nil, otherwise add brackets. - local Callsign = self.Controllable:GetCallsign() - local Prefix = Callsign and " @ " .. Callsign .. PlayerName or "" - - Message = Prefix .. ": " .. Message - CC:MessageToGroup( Message, TaskGroup ) -end - - - - - --- Assign the process to a @{Unit} and activate the process. - -- @param #FSM_PROCESS self - -- @param Task.Tasking#TASK Task - -- @param Wrapper.Unit#UNIT ProcessUnit - -- @return #FSM_PROCESS self - function FSM_PROCESS:Assign( ProcessUnit, Task ) - self:T( { Task, ProcessUnit } ) - - self:SetControllable( ProcessUnit ) - self:SetTask( Task ) - - --self.ProcessGroup = ProcessUnit:GetGroup() - - return self - end - - function FSM_PROCESS:onenterAssigned( ProcessUnit ) - self:T( "Assign" ) - - self.Task:Assign() - end - - function FSM_PROCESS:onenterFailed( ProcessUnit ) - self:T( "Failed" ) - - self.Task:Fail() - end - - function FSM_PROCESS:onenterSuccess( ProcessUnit ) - self:T( "Success" ) - - self.Task:Success() - end - - --- StateMachine callback function for a FSM_PROCESS - -- @param #FSM_PROCESS self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy ) - self:T( { ProcessUnit, From, Event, To, Dummy, self:IsTrace() } ) - - if self:IsTrace() then - MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() - end - - self:T( { Scores = self._Scores, To = To } ) - -- TODO: This needs to be reworked with a callback functions allocated within Task, and set within the mission script from the Task Objects... - if self._Scores[To] then - - local Task = self.Task - local Scoring = Task:GetScoring() - if Scoring then - Scoring:_AddMissionTaskScore( Task.Mission, ProcessUnit, self._Scores[To].ScoreText, self._Scores[To].Score ) - end - end - end - -end - -do -- FSM_TASK - - --- FSM_TASK class - -- @type FSM_TASK - -- @field Tasking.Task#TASK Task - -- @extends Core.Fsm#FSM - - --- # FSM_TASK, extends @{#FSM} - -- - -- FSM_TASK class models Finite State Machines for @{Task}s. - -- - -- === - -- - -- @field #FSM_TASK FSM_TASK - -- - FSM_TASK = { - ClassName = "FSM_TASK", - } - - --- Creates a new FSM_TASK object. - -- @param #FSM_TASK self - -- @param #table FSMT - -- @param Tasking.Task#TASK Task - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #FSM_TASK - function FSM_TASK:New( FSMT ) - - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( FSMT ) ) -- Core.Fsm#FSM_TASK - - self["onstatechange"] = self.OnStateChange - - return self - end - - function FSM_TASK:_call_handler( handler, params, EventName ) - if self[handler] then - self:T( "Calling " .. handler ) - self._EventSchedules[EventName] = nil - return self[handler]( self, unpack( params ) ) - end - end - -end -- FSM_TASK - -do -- FSM_SET - - --- FSM_SET class - -- @type FSM_SET - -- @field Core.Set#SET_BASE Set - -- @extends Core.Fsm#FSM - - - --- # FSM_SET, extends @{#FSM} - -- - -- FSM_SET class models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here - -- for multiple objects or the position of the state machine in the process. - -- - -- === - -- - -- @field #FSM_SET FSM_SET - -- - FSM_SET = { - ClassName = "FSM_SET", - } - - --- Creates a new FSM_SET object. - -- @param #FSM_SET self - -- @param #table FSMT Finite State Machine Table - -- @param Set_SET_BASE FSMSet (optional) The Set object that the FSM_SET governs. - -- @return #FSM_SET - function FSM_SET:New( FSMSet ) - - -- Inherits from BASE - self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM_SET - - if FSMSet then - self:Set( FSMSet ) - end - - return self - end - - --- Sets the SET_BASE object that the FSM_SET governs. - -- @param #FSM_SET self - -- @param Core.Set#SET_BASE FSMSet - -- @return #FSM_SET - function FSM_SET:Set( FSMSet ) - self:F( FSMSet ) - self.Set = FSMSet - end - - --- Gets the SET_BASE object that the FSM_SET governs. - -- @param #FSM_SET self - -- @return Core.Set#SET_BASE - function FSM_SET:Get() - return self.Controllable - end - - function FSM_SET:_call_handler( handler, params, EventName ) - if self[handler] then - self:T( "Calling " .. handler ) - self._EventSchedules[EventName] = nil - return self[handler]( self, self.Set, unpack( params ) ) - end - end - -end -- FSM_SET - ---- **Core** - The RADIO class is responsible for **transmitting radio communications**. --- --- --- bitmap --- --- === --- --- What are radio communications in DCS ? --- --- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM), --- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**. --- --- How to supply DCS my own Sound Files ? --- --- * Your sound files need to be encoded in **.ogg** or .wav, --- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings, --- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file), --- * For simplicty sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission. --- --- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Unit#UNIT} or a @{Group#GROUP} or by any other @{Positionable#POSITIONABLE} --- --- * If the transmitter is a @{Unit#UNIT} or a @{Group#GROUP}, DCS will set the power of the transmission automatically, --- * If the transmitter is any other @{Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped. --- --- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft, --- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below). --- If a FC3 airacraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. --- --- === --- --- ### Authors: Hugues "Grey_Echo" Bousquet --- --- @module Radio - ---- # 1) RADIO class, extends @{Base#BASE} --- --- ## 1.1) RADIO usage --- --- There are 3 steps to a successful radio transmission. --- --- * First, you need to **"add" a @{#RADIO} object** to your @{Positionable#POSITIONABLE}. This is done using the @{Positionable#POSITIONABLE.GetRadio}() function, --- * Then, you will **set the relevant parameters** to the transmission (see below), --- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{Positionable#POSITIONABLE.Broadcast}() function. --- --- Methods to set relevant parameters for both a @{Unit#UNIT} or a @{Group#GROUP} or any other @{Positionable#POSITIONABLE} --- --- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"), --- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission, --- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission. --- --- Additional Methods to set relevant parameters if the transmiter is a @{Unit#UNIT} or a @{Group#GROUP} --- --- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped, --- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration, --- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call --- --- Additional Methods to set relevant parameters if the transmiter is any other @{Wrapper.Positionable#POSITIONABLE} --- --- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts --- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call --- --- What is this power thing ? --- --- * If your transmission is sent by a @{Positionable#POSITIONABLE} other than a @{Unit#UNIT} or a @{Group#GROUP}, you can set the power of the antenna, --- * Otherwise, DCS sets it automatically, depending on what's available on your Unit, --- * If the player gets **too far** from the transmiter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**, --- * This an automated DCS calculation you have no say on, --- * For reference, a standard VOR station has a 100W antenna, a standard AA TACAN has a 120W antenna, and civilian ATC's antenna usually range between 300 and 500W, --- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission. --- --- @type RADIO --- @field Wrapper.Positionable#POSITIONABLE Positionable The transmiter --- @field #string FileName Name of the sound file --- @field #number Frequency Frequency of the transmission in Hz --- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM) --- @field #string Subtitle Subtitle of the transmission --- @field #number SubtitleDuration Duration of the Subtitle in seconds --- @field #number Power Power of the antenna is Watts --- @field #boolean Loop --- @extends Core.Base#BASE -RADIO = { - ClassName = "RADIO", - FileName = "", - Frequency = 0, - Modulation = radio.modulation.AM, - Subtitle = "", - SubtitleDuration = 0, - Power = 100, - Loop = 0, -} - ---- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast --- @param #RADIO self --- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. --- @return #RADIO Radio --- @return #nil If Positionable is invalid --- @usage --- -- If you want to create a RADIO, you probably should use @{Positionable#POSITIONABLE.GetRadio}() instead -function RADIO:New(Positionable) - local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO - - self:F(Positionable) - - if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid - self.Positionable = Positionable - return self - end - - self:E({"The passed positionable is invalid, no RADIO created", Positionable}) - return nil -end - ---- Check validity of the filename passed and sets RADIO.FileName --- @param #RADIO self --- @param #string FileName File name of the sound file (i.e. "Noise.ogg") --- @return #RADIO self -function RADIO:SetFileName(FileName) - self:F2(FileName) - - if type(FileName) == "string" then - if FileName:find(".ogg") or FileName:find(".wav") then - if not FileName:find("l10n/DEFAULT/") then - FileName = "l10n/DEFAULT/" .. FileName - end - self.FileName = FileName - return self - end - end - - self:E({"File name invalid. Maybe something wrong with the extension ?", self.FileName}) - return self -end - ---- Check validity of the frequency passed and sets RADIO.Frequency --- @param #RADIO self --- @param #number Frequency in MHz (Ranges allowed for radio transmissions in DCS : 30-88 / 108-152 / 225-400MHz) --- @return #RADIO self -function RADIO:SetFrequency(Frequency) - self:F2(Frequency) - if type(Frequency) == "number" then - -- If frequency is in range - if (Frequency >= 30 and Frequency < 88) or (Frequency >= 108 and Frequency < 152) or (Frequency >= 225 and Frequency < 400) then - self.Frequency = Frequency * 1000000 -- Conversion in Hz - -- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency - if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then - self.Positionable:SetCommand({ - id = "SetFrequency", - params = { - frequency = self.Frequency, - modulation = self.Modulation, - } - }) - end - return self - end - end - self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", self.Frequency}) - return self -end - ---- Check validity of the frequency passed and sets RADIO.Modulation --- @param #RADIO self --- @param #number Modulation either radio.modulation.AM or radio.modulation.FM --- @return #RADIO self -function RADIO:SetModulation(Modulation) - self:F2(Modulation) - if type(Modulation) == "number" then - if Modulation == radio.modulation.AM or Modulation == radio.modulation.FM then --TODO Maybe make this future proof if ED decides to add an other modulation ? - self.Modulation = Modulation - return self - end - end - self:E({"Modulation is invalid. Use DCS's enum radio.modulation. Modulation unchanged.", self.Modulation}) - return self -end - ---- Check validity of the power passed and sets RADIO.Power --- @param #RADIO self --- @param #number Power in W --- @return #RADIO self -function RADIO:SetPower(Power) - self:F2(Power) - if type(Power) == "number" then - self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that - return self - end - self:E({"Power is invalid. Power unchanged.", self.Power}) - return self -end - ---- Check validity of the loop passed and sets RADIO.Loop --- @param #RADIO self --- @param #boolean Loop --- @return #RADIO self --- @usage -function RADIO:SetLoop(Loop) - self:F2(Loop) - if type(Loop) == "boolean" then - self.Loop = Loop - return self - end - self:E({"Loop is invalid. Loop unchanged.", self.Loop}) - return self -end - ---- Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration --- @param #RADIO self --- @param #string Subtitle --- @param #number SubtitleDuration in s --- @return #RADIO self --- @usage --- -- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration -function RADIO:SetSubtitle(Subtitle, SubtitleDuration) - self:F2({Subtitle, SubtitleDuration}) - if type(Subtitle) == "string" then - self.Subtitle = Subtitle - else - self.Subtitle = "" - self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle}) - end - if type(SubtitleDuration) == "number" then - if math.floor(math.abs(SubtitleDuration)) == SubtitleDuration then - self.SubtitleDuration = SubtitleDuration - return self - end - end - self.SubtitleDuration = 0 - self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration}) -end - ---- Create a new transmission, that is to say, populate the RADIO with relevant data --- @param #RADIO self --- @param #string FileName --- @param #number Frequency in MHz --- @param #number Modulation either radio.modulation.AM or radio.modulation.FM --- @param #number Power in W --- @return #RADIO self --- @usage --- -- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP, --- but it will work with a UNIT or a GROUP anyway --- -- Only the RADIO and the Filename are mandatory -function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power) - self:F({FileName, Frequency, Modulation, Power}) - - self:SetFileName(FileName) - if Frequency then self:SetFrequency(Frequency) end - if Modulation then self:SetModulation(Modulation) end - if Power then self:SetPower(Power) end - - return self -end - - ---- Create a new transmission, that is to say, populate the RADIO with relevant data --- @param #RADIO self --- @param #string FileName --- @param #string Subtitle --- @param #number SubtitleDuration in s --- @param #number Frequency in MHz --- @param #number Modulation either radio.modulation.AM or radio.modulation.FM --- @param #boolean Loop --- @return #RADIO self --- @usage --- -- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP, --- but it will work for any POSITIONABLE --- -- Only the RADIO and the Filename are mandatory -function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop) - self:F({FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop}) - - self:SetFileName(FileName) - if Subtitle then self:SetSubtitle(Subtitle) end - if SubtitleDuration then self:SetSubtitleDuration(SubtitleDuration) end - if Frequency then self:SetFrequency(Frequency) end - if Modulation then self:SetModulation(Modulation) end - if Loop then self:SetLoop(Loop) end - - return self -end - ---- Actually Broadcast the transmission --- @param #RADIO self --- @return #RADIO self --- @usage --- -- The Radio has to be populated with the new transmission before broadcasting. --- -- Please use RADIO setters or either @{Radio#RADIO.NewGenericTransmission} or @{Radio#RADIO.NewUnitTransmission} --- -- This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE --- -- If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission() --- -- If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command --- -- If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored. --- -- If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration and Loop are ignored -function RADIO:Broadcast() - self:F() - -- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system - if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then - self:T2("Broadcasting from a UNIT or a GROUP") - self.Positionable:SetCommand({ - id = "TransmitMessage", - params = { - file = self.FileName, - duration = self.SubtitleDuration, - subtitle = self.Subtitle, - loop = self.Loop, - } - }) - else - -- If the POSITIONABLE is anything else, we revert to the general singleton function - self:T2("Broadcasting from a POSITIONABLE") - trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, false, self.Frequency, self.Power) - end - return self -end - ---- Stops a transmission --- @param #RADIO self --- @return #RADIO self --- @usage --- -- Especially usefull to stop the broadcast of looped transmissions --- -- Only works with broadcasts from UNIT or GROUP -function RADIO:StopBroadcast() - self:F() - -- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command - if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then - self.Positionable:SetCommand({ - id = "StopTransmission", - params = {} - }) - else - self:E("This broadcast can't be stopped. It's not looped either, so please wait for the end of the sound file playback") - end - return self -end--- This module contains the OBJECT class. --- --- 1) @{Object#OBJECT} class, extends @{Base#BASE} --- =========================================================== --- The @{Object#OBJECT} class is a wrapper class to handle the DCS Object objects: --- --- * Support all DCS Object APIs. --- * Enhance with Object specific APIs not in the DCS Object API set. --- * Manage the "state" of the DCS Object. --- --- 1.1) OBJECT constructor: --- ------------------------------ --- The OBJECT class provides the following functions to construct a OBJECT instance: --- --- * @{Object#OBJECT.New}(): Create a OBJECT instance. --- --- 1.2) OBJECT methods: --- -------------------------- --- The following methods can be used to identify an Object object: --- --- * @{Object#OBJECT.GetID}(): Returns the ID of the Object object. --- --- === --- --- @module Object - ---- The OBJECT class --- @type OBJECT --- @extends Core.Base#BASE --- @field #string ObjectName The name of the Object. -OBJECT = { - ClassName = "OBJECT", - ObjectName = "", -} - ---- A DCSObject --- @type DCSObject --- @field id_ The ID of the controllable in DCS - ---- Create a new OBJECT from a DCSObject --- @param #OBJECT self --- @param Dcs.DCSWrapper.Object#Object ObjectName The Object name --- @return #OBJECT self -function OBJECT:New( ObjectName, Test ) - local self = BASE:Inherit( self, BASE:New() ) - self:F2( ObjectName ) - self.ObjectName = ObjectName - - return self -end - - ---- Returns the unit's unique identifier. --- @param Wrapper.Object#OBJECT self --- @return Dcs.DCSWrapper.Object#Object.ID ObjectID --- @return #nil The DCS Object is not existing or alive. -function OBJECT:GetID() - self:F2( self.ObjectName ) - - local DCSObject = self:GetDCSObject() - - if DCSObject then - local ObjectID = DCSObject:getID() - return ObjectID - end - - return nil -end - ---- Destroys the OBJECT. --- @param #OBJECT self --- @return #nil The DCS Unit is not existing or alive. -function OBJECT:Destroy() - self:F2( self.ObjectName ) - - local DCSObject = self:GetDCSObject() - - if DCSObject then - - DCSObject:destroy() - end - - return nil -end - - - - ---- This module contains the IDENTIFIABLE class. --- --- 1) @{#IDENTIFIABLE} class, extends @{Object#OBJECT} --- =============================================================== --- The @{#IDENTIFIABLE} class is a wrapper class to handle the DCS Identifiable objects: --- --- * Support all DCS Identifiable APIs. --- * Enhance with Identifiable specific APIs not in the DCS Identifiable API set. --- * Manage the "state" of the DCS Identifiable. --- --- 1.1) IDENTIFIABLE constructor: --- ------------------------------ --- The IDENTIFIABLE class provides the following functions to construct a IDENTIFIABLE instance: --- --- * @{#IDENTIFIABLE.New}(): Create a IDENTIFIABLE instance. --- --- 1.2) IDENTIFIABLE methods: --- -------------------------- --- The following methods can be used to identify an identifiable object: --- --- * @{#IDENTIFIABLE.GetName}(): Returns the name of the Identifiable. --- * @{#IDENTIFIABLE.IsAlive}(): Returns if the Identifiable is alive. --- * @{#IDENTIFIABLE.GetTypeName}(): Returns the type name of the Identifiable. --- * @{#IDENTIFIABLE.GetCoalition}(): Returns the coalition of the Identifiable. --- * @{#IDENTIFIABLE.GetCountry}(): Returns the country of the Identifiable. --- * @{#IDENTIFIABLE.GetDesc}(): Returns the descriptor structure of the Identifiable. --- --- --- === --- --- @module Identifiable - ---- The IDENTIFIABLE class --- @type IDENTIFIABLE --- @extends Wrapper.Object#OBJECT --- @field #string IdentifiableName The name of the identifiable. -IDENTIFIABLE = { - ClassName = "IDENTIFIABLE", - IdentifiableName = "", -} - -local _CategoryName = { - [Unit.Category.AIRPLANE] = "Airplane", - [Unit.Category.HELICOPTER] = "Helicoper", - [Unit.Category.GROUND_UNIT] = "Ground Identifiable", - [Unit.Category.SHIP] = "Ship", - [Unit.Category.STRUCTURE] = "Structure", - } - ---- Create a new IDENTIFIABLE from a DCSIdentifiable --- @param #IDENTIFIABLE self --- @param Dcs.DCSWrapper.Identifiable#Identifiable IdentifiableName The DCS Identifiable name --- @return #IDENTIFIABLE self -function IDENTIFIABLE:New( IdentifiableName ) - local self = BASE:Inherit( self, OBJECT:New( IdentifiableName ) ) - self:F2( IdentifiableName ) - self.IdentifiableName = IdentifiableName - return self -end - ---- Returns if the Identifiable is alive. --- If the Identifiable is not alive, nil is returned. --- If the Identifiable is alive, true is returned. --- @param #IDENTIFIABLE self --- @return #boolean true if Identifiable is alive. --- @return #nil if the Identifiable is not existing or is not alive. -function IDENTIFIABLE:IsAlive() - self:F3( self.IdentifiableName ) - - local DCSIdentifiable = self:GetDCSObject() -- Dcs.DCSObject#Object - - if DCSIdentifiable then - local IdentifiableIsAlive = DCSIdentifiable:isExist() - return IdentifiableIsAlive - end - - return false -end - - - - ---- Returns DCS Identifiable object name. --- The function provides access to non-activated objects too. --- @param #IDENTIFIABLE self --- @return #string The name of the DCS Identifiable. --- @return #nil The DCS Identifiable is not existing or alive. -function IDENTIFIABLE:GetName() - self:F2( self.IdentifiableName ) - - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableName = self.IdentifiableName - return IdentifiableName - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil -end - - ---- Returns the type name of the DCS Identifiable. --- @param #IDENTIFIABLE self --- @return #string The type name of the DCS Identifiable. --- @return #nil The DCS Identifiable is not existing or alive. -function IDENTIFIABLE:GetTypeName() - self:F2( self.IdentifiableName ) - - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableTypeName = DCSIdentifiable:getTypeName() - self:T3( IdentifiableTypeName ) - return IdentifiableTypeName - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil -end - - ---- Returns category of the DCS Identifiable. --- @param #IDENTIFIABLE self --- @return Dcs.DCSWrapper.Object#Object.Category The category ID -function IDENTIFIABLE:GetCategory() - self:F2( self.ObjectName ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - local ObjectCategory = DCSObject:getCategory() - self:T3( ObjectCategory ) - return ObjectCategory - end - - return nil -end - - ---- Returns the DCS Identifiable category name as defined within the DCS Identifiable Descriptor. --- @param #IDENTIFIABLE self --- @return #string The DCS Identifiable Category Name -function IDENTIFIABLE:GetCategoryName() - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableCategoryName = _CategoryName[ self:GetDesc().category ] - return IdentifiableCategoryName - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil -end - ---- Returns coalition of the Identifiable. --- @param #IDENTIFIABLE self --- @return Dcs.DCSCoalitionWrapper.Object#coalition.side The side of the coalition. --- @return #nil The DCS Identifiable is not existing or alive. -function IDENTIFIABLE:GetCoalition() - self:F2( self.IdentifiableName ) - - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableCoalition = DCSIdentifiable:getCoalition() - self:T3( IdentifiableCoalition ) - return IdentifiableCoalition - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil -end - ---- Returns country of the Identifiable. --- @param #IDENTIFIABLE self --- @return Dcs.DCScountry#country.id The country identifier. --- @return #nil The DCS Identifiable is not existing or alive. -function IDENTIFIABLE:GetCountry() - self:F2( self.IdentifiableName ) - - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableCountry = DCSIdentifiable:getCountry() - self:T3( IdentifiableCountry ) - return IdentifiableCountry - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil -end - - - ---- Returns Identifiable descriptor. Descriptor type depends on Identifiable category. --- @param #IDENTIFIABLE self --- @return Dcs.DCSWrapper.Identifiable#Identifiable.Desc The Identifiable descriptor. --- @return #nil The DCS Identifiable is not existing or alive. -function IDENTIFIABLE:GetDesc() - self:F2( self.IdentifiableName ) - - local DCSIdentifiable = self:GetDCSObject() - - if DCSIdentifiable then - local IdentifiableDesc = DCSIdentifiable:getDesc() - self:T2( IdentifiableDesc ) - return IdentifiableDesc - end - - self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) - return nil -end - ---- Gets the CallSign of the IDENTIFIABLE, which is a blank by default. --- @param #IDENTIFIABLE self --- @return #string The CallSign of the IDENTIFIABLE. -function IDENTIFIABLE:GetCallsign() - return '' -end - - -function IDENTIFIABLE:GetThreatLevel() - - return 0, "Scenery" -end ---- This module contains the POSITIONABLE class. --- --- 1) @{Positionable#POSITIONABLE} class, extends @{Identifiable#IDENTIFIABLE} --- =========================================================== --- The @{Positionable#POSITIONABLE} class is a wrapper class to handle the POSITIONABLE objects: --- --- * Support all DCS APIs. --- * Enhance with POSITIONABLE specific APIs not in the DCS API set. --- * Manage the "state" of the POSITIONABLE. --- --- 1.1) POSITIONABLE constructor: --- ------------------------------ --- The POSITIONABLE class provides the following functions to construct a POSITIONABLE instance: --- --- * @{Positionable#POSITIONABLE.New}(): Create a POSITIONABLE instance. --- --- 1.2) POSITIONABLE methods: --- -------------------------- --- The following methods can be used to identify an measurable object: --- --- * @{Positionable#POSITIONABLE.GetID}(): Returns the ID of the measurable object. --- * @{Positionable#POSITIONABLE.GetName}(): Returns the name of the measurable object. --- --- === --- --- @module Positionable - ---- The POSITIONABLE class --- @type POSITIONABLE --- @extends Wrapper.Identifiable#IDENTIFIABLE --- @field #string PositionableName The name of the measurable. -POSITIONABLE = { - ClassName = "POSITIONABLE", - PositionableName = "", -} - ---- A DCSPositionable --- @type DCSPositionable --- @field id_ The ID of the controllable in DCS - ---- Create a new POSITIONABLE from a DCSPositionable --- @param #POSITIONABLE self --- @param Dcs.DCSWrapper.Positionable#Positionable PositionableName The POSITIONABLE name --- @return #POSITIONABLE self -function POSITIONABLE:New( PositionableName ) - local self = BASE:Inherit( self, IDENTIFIABLE:New( PositionableName ) ) - - self.PositionableName = PositionableName - return self -end - ---- Returns the @{DCSTypes#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Position The 3D position vectors of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetPositionVec3() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionablePosition = DCSPositionable:getPosition().p - self:T3( PositionablePosition ) - return PositionablePosition - end - - return nil -end - ---- Returns the @{DCSTypes#Vec2} vector indicating the point in 2D of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Vec2 The 2D point vector of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetVec2() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionableVec3 = DCSPositionable:getPosition().p - - local PositionableVec2 = {} - PositionableVec2.x = PositionableVec3.x - PositionableVec2.y = PositionableVec3.z - - self:T2( PositionableVec2 ) - return PositionableVec2 - end - - return nil -end - ---- Returns a POINT_VEC2 object indicating the point in 2D of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Core.Point#POINT_VEC2 The 2D point vector of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetPointVec2() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionableVec3 = DCSPositionable:getPosition().p - - local PositionablePointVec2 = POINT_VEC2:NewFromVec3( PositionableVec3 ) - - self:T2( PositionablePointVec2 ) - return PositionablePointVec2 - end - - return nil -end - ---- Returns a POINT_VEC3 object indicating the point in 3D of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Core.Point#POINT_VEC3 The 3D point vector of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetPointVec3() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionableVec3 = self:GetPositionVec3() - - local PositionablePointVec3 = POINT_VEC3:NewFromVec3( PositionableVec3 ) - - self:T2( PositionablePointVec3 ) - return PositionablePointVec3 - end - - return nil -end - - ---- Returns a random @{DCSTypes#Vec3} vector within a range, indicating the point in 3D of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @param #number Radius --- @return Dcs.DCSTypes#Vec3 The 3D point vector of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. --- @usage --- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP -function POSITIONABLE:GetRandomVec3( Radius ) - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionablePointVec3 = DCSPositionable:getPosition().p - - if Radius then - local PositionableRandomVec3 = {} - local angle = math.random() * math.pi*2; - PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius; - PositionableRandomVec3.y = PositionablePointVec3.y - PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius; - - self:T3( PositionableRandomVec3 ) - return PositionableRandomVec3 - else - self:E("Radius is nil, returning the PointVec3 of the POSITIONABLE", PositionablePointVec3) - return PositionablePointVec3 - end - end - - return nil -end - ---- Returns the @{DCSTypes#Vec3} vector indicating the 3D vector of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Vec3 The 3D point vector of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetVec3() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionableVec3 = DCSPositionable:getPosition().p - self:T3( PositionableVec3 ) - return PositionableVec3 - end - - return nil -end - ---- Returns the altitude of the POSITIONABLE. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Distance The altitude of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetAltitude() - self:F2() - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionablePointVec3 = DCSPositionable:getPoint() --Dcs.DCSTypes#Vec3 - return PositionablePointVec3.y - end - - return nil -end - ---- Returns if the Positionable is located above a runway. --- @param Wrapper.Positionable#POSITIONABLE self --- @return #boolean true if Positionable is above a runway. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:IsAboveRunway() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - - local Vec2 = self:GetVec2() - local SurfaceType = land.getSurfaceType( Vec2 ) - local IsAboveRunway = SurfaceType == land.SurfaceType.RUNWAY - - self:T2( IsAboveRunway ) - return IsAboveRunway - end - - return nil -end - - - ---- Returns the POSITIONABLE heading in degrees. --- @param Wrapper.Positionable#POSITIONABLE self --- @return #number The POSTIONABLE heading --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetHeading() - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - - local PositionablePosition = DCSPositionable:getPosition() - if PositionablePosition then - local PositionableHeading = math.atan2( PositionablePosition.x.z, PositionablePosition.x.x ) - if PositionableHeading < 0 then - PositionableHeading = PositionableHeading + 2 * math.pi - end - PositionableHeading = PositionableHeading * 180 / math.pi - self:T2( PositionableHeading ) - return PositionableHeading - end - end - - return nil -end - - ---- Returns true if the POSITIONABLE is in the air. --- Polymorphic, is overridden in GROUP and UNIT. --- @param Wrapper.Positionable#POSITIONABLE self --- @return #boolean true if in the air. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:InAir() - self:F2( self.PositionableName ) - - return nil -end - - ---- Returns the POSITIONABLE velocity vector. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Vec3 The velocity vector --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetVelocity() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionableVelocityVec3 = DCSPositionable:getVelocity() - self:T3( PositionableVelocityVec3 ) - return PositionableVelocityVec3 - end - - return nil -end - ---- Returns the POSITIONABLE velocity in km/h. --- @param Wrapper.Positionable#POSITIONABLE self --- @return #number The velocity in km/h --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetVelocityKMH() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local VelocityVec3 = self:GetVelocity() - local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec - local Velocity = Velocity * 3.6 -- now it is in km/h. - self:T3( Velocity ) - return Velocity - end - - return nil -end - ---- Returns a message with the callsign embedded (if there is one). --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. --- @return Core.Message#MESSAGE -function POSITIONABLE:GetMessage( Message, Duration, Name ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - Name = Name or self:GetTypeName() - return MESSAGE:New( Message, Duration, self:GetCallsign() .. " (" .. Name .. ")" ) - end - - return nil -end - ---- Send a message to all coalitions. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToAll( Message, Duration, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - self:GetMessage( Message, Duration, Name ):ToAll() - end - - return nil -end - ---- Send a message to a coalition. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTYpes#Duration Duration The duration of the message. --- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - self:GetMessage( Message, Duration, Name ):ToCoalition( MessageCoalition ) - end - - return nil -end - - ---- Send a message to the red coalition. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTYpes#Duration Duration The duration of the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToRed( Message, Duration, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - self:GetMessage( Message, Duration, Name ):ToRed() - end - - return nil -end - ---- Send a message to the blue coalition. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToBlue( Message, Duration, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - self:GetMessage( Message, Duration, Name ):ToBlue() - end - - return nil -end - ---- Send a message to a client. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. --- @param Wrapper.Client#CLIENT Client The client object receiving the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToClient( Message, Duration, Client, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - self:GetMessage( Message, Duration, Name ):ToClient( Client ) - end - - return nil -end - ---- Send a message to a @{Group}. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. --- @param Wrapper.Group#GROUP MessageGroup The GROUP object receiving the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - if DCSObject:isExist() then - self:GetMessage( Message, Duration, Name ):ToGroup( MessageGroup ) - end - end - - return nil -end - ---- Send a message to the players in the @{Group}. --- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. --- @param #POSITIONABLE self --- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. --- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -function POSITIONABLE:Message( Message, Duration, Name ) - self:F2( { Message, Duration } ) - - local DCSObject = self:GetDCSObject() - if DCSObject then - self:GetMessage( Message, Duration, Name ):ToGroup( self ) - end - - return nil -end - ---- Create a @{Radio#RADIO}, to allow radio transmission for this POSITIONABLE. --- Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message --- @param #POSITIONABLE self --- @return #RADIO Radio -function POSITIONABLE:GetRadio() - self:F2(self) - return RADIO:New(self) -end ---- This module contains the CONTROLLABLE class. --- --- 1) @{Controllable#CONTROLLABLE} class, extends @{Positionable#POSITIONABLE} --- =========================================================== --- The @{Controllable#CONTROLLABLE} class is a wrapper class to handle the DCS Controllable objects: --- --- * Support all DCS Controllable APIs. --- * Enhance with Controllable specific APIs not in the DCS Controllable API set. --- * Handle local Controllable Controller. --- * Manage the "state" of the DCS Controllable. --- --- 1.1) CONTROLLABLE constructor --- ----------------------------- --- The CONTROLLABLE class provides the following functions to construct a CONTROLLABLE instance: --- --- * @{#CONTROLLABLE.New}(): Create a CONTROLLABLE instance. --- --- 1.2) CONTROLLABLE task methods --- ------------------------------ --- Several controllable task methods are available that help you to prepare tasks. --- These methods return a string consisting of the task description, which can then be given to either a @{Controllable#CONTROLLABLE.PushTask} or @{Controllable#SetTask} method to assign the task to the CONTROLLABLE. --- Tasks are specific for the category of the CONTROLLABLE, more specific, for AIR, GROUND or AIR and GROUND. --- Each task description where applicable indicates for which controllable category the task is valid. --- There are 2 main subdivisions of tasks: Assigned tasks and EnRoute tasks. --- --- ### 1.2.1) Assigned task methods --- --- Assigned task methods make the controllable execute the task where the location of the (possible) targets of the task are known before being detected. --- This is different from the EnRoute tasks, where the targets of the task need to be detected before the task can be executed. --- --- Find below a list of the **assigned task** methods: --- --- * @{#CONTROLLABLE.TaskAttackGroup}: (AIR) Attack a Controllable. --- * @{#CONTROLLABLE.TaskAttackMapObject}: (AIR) Attacking the map object (building, structure, e.t.c). --- * @{#CONTROLLABLE.TaskAttackUnit}: (AIR) Attack the Unit. --- * @{#CONTROLLABLE.TaskBombing}: (AIR) Delivering weapon at the point on the ground. --- * @{#CONTROLLABLE.TaskBombingRunway}: (AIR) Delivering weapon on the runway. --- * @{#CONTROLLABLE.TaskEmbarking}: (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable. --- * @{#CONTROLLABLE.TaskEmbarkToTransport}: (GROUND) Embark to a Transport landed at a location. --- * @{#CONTROLLABLE.TaskEscort}: (AIR) Escort another airborne controllable. --- * @{#CONTROLLABLE.TaskFAC_AttackGroup}: (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction. --- * @{#CONTROLLABLE.TaskFireAtPoint}: (GROUND) Fire some or all ammunition at a VEC2 point. --- * @{#CONTROLLABLE.TaskFollow}: (AIR) Following another airborne controllable. --- * @{#CONTROLLABLE.TaskHold}: (GROUND) Hold ground controllable from moving. --- * @{#CONTROLLABLE.TaskHoldPosition}: (AIR) Hold position at the current position of the first unit of the controllable. --- * @{#CONTROLLABLE.TaskLand}: (AIR HELICOPTER) Landing at the ground. For helicopters only. --- * @{#CONTROLLABLE.TaskLandAtZone}: (AIR) Land the controllable at a @{Zone#ZONE_RADIUS). --- * @{#CONTROLLABLE.TaskOrbitCircle}: (AIR) Orbit at the current position of the first unit of the controllable at a specified alititude. --- * @{#CONTROLLABLE.TaskOrbitCircleAtVec2}: (AIR) Orbit at a specified position at a specified alititude during a specified duration with a specified speed. --- * @{#CONTROLLABLE.TaskRefueling}: (AIR) Refueling from the nearest tanker. No parameters. --- * @{#CONTROLLABLE.TaskRoute}: (AIR + GROUND) Return a Misson task to follow a given route defined by Points. --- * @{#CONTROLLABLE.TaskRouteToVec2}: (AIR + GROUND) Make the Controllable move to a given point. --- * @{#CONTROLLABLE.TaskRouteToVec3}: (AIR + GROUND) Make the Controllable move to a given point. --- * @{#CONTROLLABLE.TaskRouteToZone}: (AIR + GROUND) Route the controllable to a given zone. --- * @{#CONTROLLABLE.TaskReturnToBase}: (AIR) Route the controllable to an airbase. --- --- ### 1.2.2) EnRoute task methods --- --- EnRoute tasks require the targets of the task need to be detected by the controllable (using its sensors) before the task can be executed: --- --- * @{#CONTROLLABLE.EnRouteTaskAWACS}: (AIR) Aircraft will act as an AWACS for friendly units (will provide them with information about contacts). No parameters. --- * @{#CONTROLLABLE.EnRouteTaskEngageControllable}: (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now; it just allows the unit/controllable to engage the target controllable as well as other assigned targets. --- * @{#CONTROLLABLE.EnRouteTaskEngageTargets}: (AIR) Engaging targets of defined types. --- * @{#CONTROLLABLE.EnRouteTaskEWR}: (AIR) Attack the Unit. --- * @{#CONTROLLABLE.EnRouteTaskFAC}: (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose a targets (enemy ground controllable) around as well as other assigned targets. --- * @{#CONTROLLABLE.EnRouteTaskFAC_EngageControllable}: (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose the target (enemy ground controllable) as well as other assigned targets. --- * @{#CONTROLLABLE.EnRouteTaskTanker}: (AIR) Aircraft will act as a tanker for friendly units. No parameters. --- --- ### 1.2.3) Preparation task methods --- --- There are certain task methods that allow to tailor the task behaviour: --- --- * @{#CONTROLLABLE.TaskWrappedAction}: Return a WrappedAction Task taking a Command. --- * @{#CONTROLLABLE.TaskCombo}: Return a Combo Task taking an array of Tasks. --- * @{#CONTROLLABLE.TaskCondition}: Return a condition section for a controlled task. --- * @{#CONTROLLABLE.TaskControlled}: Return a Controlled Task taking a Task and a TaskCondition. --- --- ### 1.2.4) Obtain the mission from controllable templates --- --- Controllable templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a controllable and assign it to another: --- --- * @{#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. --- --- 1.3) CONTROLLABLE Command methods --- -------------------------- --- Controllable **command methods** prepare the execution of commands using the @{#CONTROLLABLE.SetCommand} method: --- --- * @{#CONTROLLABLE.CommandDoScript}: Do Script command. --- * @{#CONTROLLABLE.CommandSwitchWayPoint}: Perform a switch waypoint command. --- --- 1.4) CONTROLLABLE Option methods --- ------------------------- --- Controllable **Option methods** change the behaviour of the Controllable while being alive. --- --- ### 1.4.1) Rule of Engagement: --- --- * @{#CONTROLLABLE.OptionROEWeaponFree} --- * @{#CONTROLLABLE.OptionROEOpenFire} --- * @{#CONTROLLABLE.OptionROEReturnFire} --- * @{#CONTROLLABLE.OptionROEEvadeFire} --- --- To check whether an ROE option is valid for a specific controllable, use: --- --- * @{#CONTROLLABLE.OptionROEWeaponFreePossible} --- * @{#CONTROLLABLE.OptionROEOpenFirePossible} --- * @{#CONTROLLABLE.OptionROEReturnFirePossible} --- * @{#CONTROLLABLE.OptionROEEvadeFirePossible} --- --- ### 1.4.2) Rule on thread: --- --- * @{#CONTROLLABLE.OptionROTNoReaction} --- * @{#CONTROLLABLE.OptionROTPassiveDefense} --- * @{#CONTROLLABLE.OptionROTEvadeFire} --- * @{#CONTROLLABLE.OptionROTVertical} --- --- To test whether an ROT option is valid for a specific controllable, use: --- --- * @{#CONTROLLABLE.OptionROTNoReactionPossible} --- * @{#CONTROLLABLE.OptionROTPassiveDefensePossible} --- * @{#CONTROLLABLE.OptionROTEvadeFirePossible} --- * @{#CONTROLLABLE.OptionROTVerticalPossible} --- --- === --- --- @module Controllable - ---- The CONTROLLABLE class --- @type CONTROLLABLE --- @extends Wrapper.Positionable#POSITIONABLE --- @field Dcs.DCSWrapper.Controllable#Controllable DCSControllable The DCS controllable class. --- @field #string ControllableName The name of the controllable. -CONTROLLABLE = { - ClassName = "CONTROLLABLE", - ControllableName = "", - WayPointFunctions = {}, -} - ---- Create a new CONTROLLABLE from a DCSControllable --- @param #CONTROLLABLE self --- @param Dcs.DCSWrapper.Controllable#Controllable ControllableName The DCS Controllable name --- @return #CONTROLLABLE self -function CONTROLLABLE:New( ControllableName ) - local self = BASE:Inherit( self, POSITIONABLE:New( ControllableName ) ) - self:F2( ControllableName ) - self.ControllableName = ControllableName - - self.TaskScheduler = SCHEDULER:New( self ) - return self -end - --- DCS Controllable methods support. - ---- Get the controller for the CONTROLLABLE. --- @param #CONTROLLABLE self --- @return Dcs.DCSController#Controller -function CONTROLLABLE:_GetController() - self:F2( { self.ControllableName } ) - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local ControllableController = DCSControllable:getController() - self:T3( ControllableController ) - return ControllableController - end - - return nil -end - --- Get methods - ---- Returns the UNITs wrappers of the DCS Units of the Controllable (default is a GROUP). --- @param #CONTROLLABLE self --- @return #list The UNITs wrappers. -function CONTROLLABLE:GetUnits() - self:F2( { self.ControllableName } ) - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local DCSUnits = DCSControllable:getUnits() - local Units = {} - for Index, UnitData in pairs( DCSUnits ) do - Units[#Units+1] = UNIT:Find( UnitData ) - end - self:T3( Units ) - return Units - end - - return nil -end - - ---- Returns the health. Dead controllables have health <= 1.0. --- @param #CONTROLLABLE self --- @return #number The controllable health value (unit or group average). --- @return #nil The controllable is not existing or alive. -function CONTROLLABLE:GetLife() - self:F2( self.ControllableName ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local UnitLife = 0 - local Units = self:GetUnits() - if #Units == 1 then - local Unit = Units[1] -- Wrapper.Unit#UNIT - UnitLife = Unit:GetLife() - else - local UnitLifeTotal = 0 - for UnitID, Unit in pairs( Units ) do - local Unit = Unit -- Wrapper.Unit#UNIT - UnitLifeTotal = UnitLifeTotal + Unit:GetLife() - end - UnitLife = UnitLifeTotal / #Units - end - return UnitLife - end - - return nil -end - - - --- Tasks - ---- Clear all tasks from the controllable. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE -function CONTROLLABLE:ClearTasks() - self:F2() - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local Controller = self:_GetController() - Controller:resetTask() - return self - end - - return nil -end - - ---- Popping current Task from the controllable. --- @param #CONTROLLABLE self --- @return Wrapper.Controllable#CONTROLLABLE self -function CONTROLLABLE:PopCurrentTask() - self:F2() - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local Controller = self:_GetController() - Controller:popTask() - return self - end - - return nil -end - ---- Pushing Task on the queue from the controllable. --- @param #CONTROLLABLE self --- @return Wrapper.Controllable#CONTROLLABLE self -function CONTROLLABLE:PushTask( DCSTask, WaitTime ) - self:F2() - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local Controller = self:_GetController() - - -- When a controllable SPAWNs, it takes about a second to get the controllable in the simulator. Setting tasks to unspawned controllables provides unexpected results. - -- Therefore we schedule the functions to set the mission and options for the Controllable. - -- Controller:pushTask( DCSTask ) - - if WaitTime then - self.TaskScheduler:Schedule( Controller, Controller.pushTask, { DCSTask }, WaitTime ) - else - Controller:pushTask( DCSTask ) - end - - return self - end - - return nil -end - ---- Clearing the Task Queue and Setting the Task on the queue from the controllable. --- @param #CONTROLLABLE self --- @return Wrapper.Controllable#CONTROLLABLE self -function CONTROLLABLE:SetTask( DCSTask, WaitTime ) - self:F2( { DCSTask } ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - - local Controller = self:_GetController() - self:T3( Controller ) - - -- When a controllable SPAWNs, it takes about a second to get the controllable in the simulator. Setting tasks to unspawned controllables provides unexpected results. - -- Therefore we schedule the functions to set the mission and options for the Controllable. - -- Controller.setTask( Controller, DCSTask ) - - if not WaitTime then - Controller:setTask( DCSTask ) - else - self.TaskScheduler:Schedule( Controller, Controller.setTask, { DCSTask }, WaitTime ) - end - - return self - end - - return nil -end - - ---- Return a condition section for a controlled task. --- @param #CONTROLLABLE self --- @param Dcs.DCSTime#Time time --- @param #string userFlag --- @param #boolean userFlagValue --- @param #string condition --- @param Dcs.DCSTime#Time duration --- @param #number lastWayPoint --- return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:TaskCondition( time, userFlag, userFlagValue, condition, duration, lastWayPoint ) - self:F2( { time, userFlag, userFlagValue, condition, duration, lastWayPoint } ) - - local DCSStopCondition = {} - DCSStopCondition.time = time - DCSStopCondition.userFlag = userFlag - DCSStopCondition.userFlagValue = userFlagValue - DCSStopCondition.condition = condition - DCSStopCondition.duration = duration - DCSStopCondition.lastWayPoint = lastWayPoint - - self:T3( { DCSStopCondition } ) - return DCSStopCondition -end - ---- Return a Controlled Task taking a Task and a TaskCondition. --- @param #CONTROLLABLE self --- @param Dcs.DCSTasking.Task#Task DCSTask --- @param #DCSStopCondition DCSStopCondition --- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:TaskControlled( DCSTask, DCSStopCondition ) - self:F2( { DCSTask, DCSStopCondition } ) - - local DCSTaskControlled - - DCSTaskControlled = { - id = 'ControlledTask', - params = { - task = DCSTask, - stopCondition = DCSStopCondition - } - } - - self:T3( { DCSTaskControlled } ) - return DCSTaskControlled -end - ---- Return a Combo Task taking an array of Tasks. --- @param #CONTROLLABLE self --- @param Dcs.DCSTasking.Task#TaskArray DCSTasks Array of @{DCSTasking.Task#Task} --- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:TaskCombo( DCSTasks ) - self:F2( { DCSTasks } ) - - local DCSTaskCombo - - DCSTaskCombo = { - id = 'ComboTask', - params = { - tasks = DCSTasks - } - } - - for TaskID, Task in ipairs( DCSTasks ) do - self:E( Task ) - end - - self:T3( { DCSTaskCombo } ) - return DCSTaskCombo -end - ---- Return a WrappedAction Task taking a Command. --- @param #CONTROLLABLE self --- @param Dcs.DCSCommand#Command DCSCommand --- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:TaskWrappedAction( DCSCommand, Index ) - self:F2( { DCSCommand } ) - - local DCSTaskWrappedAction - - DCSTaskWrappedAction = { - id = "WrappedAction", - enabled = true, - number = Index, - auto = false, - params = { - action = DCSCommand, - }, - } - - self:T3( { DCSTaskWrappedAction } ) - return DCSTaskWrappedAction -end - ---- Executes a command action --- @param #CONTROLLABLE self --- @param Dcs.DCSCommand#Command DCSCommand --- @return #CONTROLLABLE self -function CONTROLLABLE:SetCommand( DCSCommand ) - self:F2( DCSCommand ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local Controller = self:_GetController() - Controller:setCommand( DCSCommand ) - return self - end - - return nil -end - ---- Perform a switch waypoint command --- @param #CONTROLLABLE self --- @param #number FromWayPoint --- @param #number ToWayPoint --- @return Dcs.DCSTasking.Task#Task --- @usage --- --- This test demonstrates the use(s) of the SwitchWayPoint method of the GROUP class. --- HeliGroup = GROUP:FindByName( "Helicopter" ) --- --- --- Route the helicopter back to the FARP after 60 seconds. --- -- We use the SCHEDULER class to do this. --- SCHEDULER:New( nil, --- function( HeliGroup ) --- local CommandRTB = HeliGroup:CommandSwitchWayPoint( 2, 8 ) --- HeliGroup:SetCommand( CommandRTB ) --- end, { HeliGroup }, 90 --- ) -function CONTROLLABLE:CommandSwitchWayPoint( FromWayPoint, ToWayPoint ) - self:F2( { FromWayPoint, ToWayPoint } ) - - local CommandSwitchWayPoint = { - id = 'SwitchWaypoint', - params = { - fromWaypointIndex = FromWayPoint, - goToWaypointIndex = ToWayPoint, - }, - } - - self:T3( { CommandSwitchWayPoint } ) - return CommandSwitchWayPoint -end - ---- Create a stop route command, which returns a string containing the command. --- Use the result in the method @{#CONTROLLABLE.SetCommand}(). --- A value of true will make the ground group stop, a value of false will make it continue. --- Note that this can only work on GROUP level, although individual UNITs can be commanded, the whole GROUP will react. --- --- Example missions: --- --- * GRP-310 --- --- @param #CONTROLLABLE self --- @param #boolean StopRoute true if the ground unit needs to stop, false if it needs to continue to move. --- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:CommandStopRoute( StopRoute ) - self:F2( { StopRoute } ) - - local CommandStopRoute = { - id = 'StopRoute', - params = { - value = StopRoute, - }, - } - - self:T3( { CommandStopRoute } ) - return CommandStopRoute -end - - --- TASKS FOR AIR CONTROLLABLES - - ---- (AIR) Attack a Controllable. --- @param #CONTROLLABLE self --- @param Wrapper.Controllable#CONTROLLABLE AttackGroup The Controllable to be attacked. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param Dcs.DCSTypes#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude. --- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit ) - self:F2( { self.ControllableName, AttackGroup, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit } ) - - -- AttackGroup = { - -- id = 'AttackGroup', - -- params = { - -- groupId = Group.ID, - -- weaponType = number, - -- expend = enum AI.Task.WeaponExpend, - -- attackQty = number, - -- directionEnabled = boolean, - -- direction = Azimuth, - -- altitudeEnabled = boolean, - -- altitude = Distance, - -- attackQtyLimit = boolean, - -- } - -- } - - local DirectionEnabled = nil - if Direction then - DirectionEnabled = true - end - - local AltitudeEnabled = nil - if Altitude then - AltitudeEnabled = true - end - - local DCSTask - DCSTask = { id = 'AttackGroup', - params = { - groupId = AttackGroup:GetID(), - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - directionEnabled = DirectionEnabled, - direction = Direction, - altitudeEnabled = AltitudeEnabled, - altitude = Altitude, - attackQtyLimit = AttackQtyLimit, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - ---- (AIR) Attack the Unit. --- @param #CONTROLLABLE self --- @param Wrapper.Unit#UNIT AttackUnit The UNIT. --- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #number Altitude (optional) The altitude from where to attack. --- @param #boolean Visible (optional) not a clue. --- @param #number WeaponType (optional) The WeaponType. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType ) - self:F2( { self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType } ) - - local DCSTask - DCSTask = { - id = 'AttackUnit', - params = { - unitId = AttackUnit:GetID(), - groupAttack = GroupAttack or false, - visible = Visible or false, - expend = WeaponExpend or "Auto", - directionEnabled = Direction and true or false, - direction = Direction, - altitudeEnabled = Altitude and true or false, - altitude = Altitude or 30, - attackQtyLimit = AttackQty and true or false, - attackQty = AttackQty, - weaponType = WeaponType - } - } - - self:E( DCSTask ) - - return DCSTask -end - - ---- (AIR) Delivering weapon at the point on the ground. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) Desired quantity of passes. The parameter is not the same in AttackGroup and AttackUnit tasks. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskBombing( Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack ) - self:F2( { self.ControllableName, Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack } ) - --- Bombing = { --- id = 'Bombing', --- params = { --- point = Vec2, --- weaponType = number, --- expend = enum AI.Task.WeaponExpend, --- attackQty = number, --- direction = Azimuth, --- controllableAttack = boolean, --- } --- } - - local DCSTask - DCSTask = { id = 'Bombing', - params = { - point = Vec2, - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - direction = Direction, - controllableAttack = ControllableAttack, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - ---- (AIR) Orbit at a specified position at a specified alititude during a specified duration with a specified speed. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Point The point to hold the position. --- @param #number Altitude The altitude to hold the position. --- @param #number Speed The speed flying when holding the position. --- @return #CONTROLLABLE self -function CONTROLLABLE:TaskOrbitCircleAtVec2( Point, Altitude, Speed ) - self:F2( { self.ControllableName, Point, Altitude, Speed } ) - - -- pattern = enum AI.Task.OribtPattern, - -- point = Vec2, - -- point2 = Vec2, - -- speed = Distance, - -- altitude = Distance - - local LandHeight = land.getHeight( Point ) - - self:T3( { LandHeight } ) - - local DCSTask = { id = 'Orbit', - params = { pattern = AI.Task.OrbitPattern.CIRCLE, - point = Point, - speed = Speed, - altitude = Altitude + LandHeight - } - } - - - -- local AITask = { id = 'ControlledTask', - -- params = { task = { id = 'Orbit', - -- params = { pattern = AI.Task.OrbitPattern.CIRCLE, - -- point = Point, - -- speed = Speed, - -- altitude = Altitude + LandHeight - -- } - -- }, - -- stopCondition = { duration = Duration - -- } - -- } - -- } - -- ) - - return DCSTask -end - ---- (AIR) Orbit at the current position of the first unit of the controllable at a specified alititude. --- @param #CONTROLLABLE self --- @param #number Altitude The altitude to hold the position. --- @param #number Speed The speed flying when holding the position. --- @return #CONTROLLABLE self -function CONTROLLABLE:TaskOrbitCircle( Altitude, Speed ) - self:F2( { self.ControllableName, Altitude, Speed } ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local ControllablePoint = self:GetVec2() - return self:TaskOrbitCircleAtVec2( ControllablePoint, Altitude, Speed ) - end - - return nil -end - - - ---- (AIR) Hold position at the current position of the first unit of the controllable. --- @param #CONTROLLABLE self --- @param #number Duration The maximum duration in seconds to hold the position. --- @return #CONTROLLABLE self -function CONTROLLABLE:TaskHoldPosition() - self:F2( { self.ControllableName } ) - - return self:TaskOrbitCircle( 30, 10 ) -end - - - - ---- (AIR) Attacking the map object (building, structure, e.t.c). --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the point the map object is closest to. The distance between the point and the map object must not be greater than 2000 meters. Object id is not used here because Mission Editor doesn't support map object identificators. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskAttackMapObject( Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack ) - self:F2( { self.ControllableName, Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack } ) - --- AttackMapObject = { --- id = 'AttackMapObject', --- params = { --- point = Vec2, --- weaponType = number, --- expend = enum AI.Task.WeaponExpend, --- attackQty = number, --- direction = Azimuth, --- controllableAttack = boolean, --- } --- } - - local DCSTask - DCSTask = { id = 'AttackMapObject', - params = { - point = Vec2, - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - direction = Direction, - controllableAttack = ControllableAttack, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR) Delivering weapon on the runway. --- @param #CONTROLLABLE self --- @param Wrapper.Airbase#AIRBASE Airbase Airbase to attack. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskBombingRunway( Airbase, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack ) - self:F2( { self.ControllableName, Airbase, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack } ) - --- BombingRunway = { --- id = 'BombingRunway', --- params = { --- runwayId = AirdromeId, --- weaponType = number, --- expend = enum AI.Task.WeaponExpend, --- attackQty = number, --- direction = Azimuth, --- controllableAttack = boolean, --- } --- } - - local DCSTask - DCSTask = { id = 'BombingRunway', - params = { - point = Airbase:GetID(), - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - direction = Direction, - controllableAttack = ControllableAttack, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR) Refueling from the nearest tanker. No parameters. --- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskRefueling() - self:F2( { self.ControllableName } ) - --- Refueling = { --- id = 'Refueling', --- params = {} --- } - - local DCSTask - DCSTask = { id = 'Refueling', - params = { - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR HELICOPTER) Landing at the ground. For helicopters only. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Point The point where to land. --- @param #number Duration The duration in seconds to stay on the ground. --- @return #CONTROLLABLE self -function CONTROLLABLE:TaskLandAtVec2( Point, Duration ) - self:F2( { self.ControllableName, Point, Duration } ) - --- Land = { --- id= 'Land', --- params = { --- point = Vec2, --- durationFlag = boolean, --- duration = Time --- } --- } - - local DCSTask - if Duration and Duration > 0 then - DCSTask = { id = 'Land', - params = { - point = Point, - durationFlag = true, - duration = Duration, - }, - } - else - DCSTask = { id = 'Land', - params = { - point = Point, - durationFlag = false, - }, - } - end - - self:T3( DCSTask ) - return DCSTask -end - ---- (AIR) Land the controllable at a @{Zone#ZONE_RADIUS). --- @param #CONTROLLABLE self --- @param Core.Zone#ZONE Zone The zone where to land. --- @param #number Duration The duration in seconds to stay on the ground. --- @return #CONTROLLABLE self -function CONTROLLABLE:TaskLandAtZone( Zone, Duration, RandomPoint ) - self:F2( { self.ControllableName, Zone, Duration, RandomPoint } ) - - local Point - if RandomPoint then - Point = Zone:GetRandomVec2() - else - Point = Zone:GetVec2() - end - - local DCSTask = self:TaskLandAtVec2( Point, Duration ) - - self:T3( DCSTask ) - return DCSTask -end - - - ---- (AIR) Following another airborne controllable. --- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders. --- If another controllable is on land the unit / controllable will orbit around. --- @param #CONTROLLABLE self --- @param Wrapper.Controllable#CONTROLLABLE FollowControllable The controllable to be followed. --- @param Dcs.DCSTypes#Vec3 Vec3 Position of the unit / lead unit of the controllable relative lead unit of another controllable in frame reference oriented by course of lead unit of another controllable. If another controllable is on land the unit / controllable will orbit around. --- @param #number LastWaypointIndex Detach waypoint of another controllable. Once reached the unit / controllable Follow task is finished. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskFollow( FollowControllable, Vec3, LastWaypointIndex ) - self:F2( { self.ControllableName, FollowControllable, Vec3, LastWaypointIndex } ) - --- Follow = { --- id = 'Follow', --- params = { --- groupId = Group.ID, --- pos = Vec3, --- lastWptIndexFlag = boolean, --- lastWptIndex = number --- } --- } - - local LastWaypointIndexFlag = false - if LastWaypointIndex then - LastWaypointIndexFlag = true - end - - local DCSTask - DCSTask = { - id = 'Follow', - params = { - groupId = FollowControllable:GetID(), - pos = Vec3, - lastWptIndexFlag = LastWaypointIndexFlag, - lastWptIndex = LastWaypointIndex - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR) Escort another airborne controllable. --- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders. --- The unit / controllable will also protect that controllable from threats of specified types. --- @param #CONTROLLABLE self --- @param Wrapper.Controllable#CONTROLLABLE EscortControllable The controllable to be escorted. --- @param Dcs.DCSTypes#Vec3 Vec3 Position of the unit / lead unit of the controllable relative lead unit of another controllable in frame reference oriented by course of lead unit of another controllable. If another controllable is on land the unit / controllable will orbit around. --- @param #number LastWaypointIndex Detach waypoint of another controllable. Once reached the unit / controllable Follow task is finished. --- @param #number EngagementDistanceMax Maximal distance from escorted controllable to threat. If the threat is already engaged by escort escort will disengage if the distance becomes greater than 1.5 * engagementDistMax. --- @param Dcs.DCSTypes#AttributeNameArray TargetTypes Array of AttributeName that is contains threat categories allowed to engage. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskEscort( FollowControllable, Vec3, LastWaypointIndex, EngagementDistance, TargetTypes ) - self:F2( { self.ControllableName, FollowControllable, Vec3, LastWaypointIndex, EngagementDistance, TargetTypes } ) - --- Escort = { --- id = 'Escort', --- params = { --- groupId = Group.ID, --- pos = Vec3, --- lastWptIndexFlag = boolean, --- lastWptIndex = number, --- engagementDistMax = Distance, --- targetTypes = array of AttributeName, --- } --- } - - local LastWaypointIndexFlag = false - if LastWaypointIndex then - LastWaypointIndexFlag = true - end - - local DCSTask - DCSTask = { id = 'Escort', - params = { - groupId = FollowControllable:GetID(), - pos = Vec3, - lastWptIndexFlag = LastWaypointIndexFlag, - lastWptIndex = LastWaypointIndex, - engagementDistMax = EngagementDistance, - targetTypes = TargetTypes, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - - --- GROUND TASKS - ---- (GROUND) Fire at a VEC2 point until ammunition is finished. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 The point to fire at. --- @param Dcs.DCSTypes#Distance Radius The radius of the zone to deploy the fire at. --- @param #number AmmoCount (optional) Quantity of ammunition to expand (omit to fire until ammunition is depleted). --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount ) - self:F2( { self.ControllableName, Vec2, Radius, AmmoCount } ) - - -- FireAtPoint = { - -- id = 'FireAtPoint', - -- params = { - -- point = Vec2, - -- radius = Distance, - -- expendQty = number, - -- expendQtyEnabled = boolean, - -- } - -- } - - local DCSTask - DCSTask = { id = 'FireAtPoint', - params = { - point = Vec2, - radius = Radius, - expendQty = 100, -- dummy value - expendQtyEnabled = false, - } - } - - if AmmoCount then - DCSTask.params.expendQty = AmmoCount - DCSTask.params.expendQtyEnabled = true - end - - self:T3( { DCSTask } ) - return DCSTask -end - ---- (GROUND) Hold ground controllable from moving. --- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskHold() - self:F2( { self.ControllableName } ) - --- Hold = { --- id = 'Hold', --- params = { --- } --- } - - local DCSTask - DCSTask = { id = 'Hold', - params = { - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - --- TASKS FOR AIRBORNE AND GROUND UNITS/CONTROLLABLES - ---- (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction. --- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC. --- If the task is assigned to the controllable lead unit will be a FAC. --- @param #CONTROLLABLE self --- @param Wrapper.Controllable#CONTROLLABLE AttackGroup Target CONTROLLABLE. --- @param #number WeaponType Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.Designation Designation (optional) Designation type. --- @param #boolean Datalink (optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink ) - self:F2( { self.ControllableName, AttackGroup, WeaponType, Designation, Datalink } ) - --- FAC_AttackGroup = { --- id = 'FAC_AttackGroup', --- params = { --- groupId = Group.ID, --- weaponType = number, --- designation = enum AI.Task.Designation, --- datalink = boolean --- } --- } - - local DCSTask - DCSTask = { id = 'FAC_AttackGroup', - params = { - groupId = AttackGroup:GetID(), - weaponType = WeaponType, - designation = Designation, - datalink = Datalink, - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - --- EN-ACT_ROUTE TASKS FOR AIRBORNE CONTROLLABLES - ---- (AIR) Engaging targets of defined types. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Distance Distance Maximal distance from the target to a route leg. If the target is on a greater distance it will be ignored. --- @param Dcs.DCSTypes#AttributeNameArray TargetTypes Array of target categories allowed to engage. --- @param #number Priority All enroute tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskEngageTargets( Distance, TargetTypes, Priority ) - self:F2( { self.ControllableName, Distance, TargetTypes, Priority } ) - --- EngageTargets ={ --- id = 'EngageTargets', --- params = { --- maxDist = Distance, --- targetTypes = array of AttributeName, --- priority = number --- } --- } - - local DCSTask - DCSTask = { id = 'EngageTargets', - params = { - maxDist = Distance, - targetTypes = TargetTypes, - priority = Priority - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - - ---- (AIR) Engaging a targets of defined types at circle-shaped zone. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the zone. --- @param Dcs.DCSTypes#Distance Radius Radius of the zone. --- @param Dcs.DCSTypes#AttributeNameArray TargetTypes Array of target categories allowed to engage. --- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskEngageTargets( Vec2, Radius, TargetTypes, Priority ) - self:F2( { self.ControllableName, Vec2, Radius, TargetTypes, Priority } ) - --- EngageTargetsInZone = { --- id = 'EngageTargetsInZone', --- params = { --- point = Vec2, --- zoneRadius = Distance, --- targetTypes = array of AttributeName, --- priority = number --- } --- } - - local DCSTask - DCSTask = { id = 'EngageTargetsInZone', - params = { - point = Vec2, - zoneRadius = Radius, - targetTypes = TargetTypes, - priority = Priority - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now; it just allows the unit/controllable to engage the target controllable as well as other assigned targets. --- @param #CONTROLLABLE self --- @param Wrapper.Controllable#CONTROLLABLE AttackGroup The Controllable to be attacked. --- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param Dcs.DCSTypes#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude. --- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit ) - self:F2( { self.ControllableName, AttackGroup, Priority, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit } ) - - -- EngageControllable = { - -- id = 'EngageControllable ', - -- params = { - -- groupId = Group.ID, - -- weaponType = number, - -- expend = enum AI.Task.WeaponExpend, - -- attackQty = number, - -- directionEnabled = boolean, - -- direction = Azimuth, - -- altitudeEnabled = boolean, - -- altitude = Distance, - -- attackQtyLimit = boolean, - -- priority = number, - -- } - -- } - - local DirectionEnabled = nil - if Direction then - DirectionEnabled = true - end - - local AltitudeEnabled = nil - if Altitude then - AltitudeEnabled = true - end - - local DCSTask - DCSTask = { id = 'EngageControllable', - params = { - groupId = AttackGroup:GetID(), - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - directionEnabled = DirectionEnabled, - direction = Direction, - altitudeEnabled = AltitudeEnabled, - altitude = Altitude, - attackQtyLimit = AttackQtyLimit, - priority = Priority, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR) Search and attack the Unit. --- @param #CONTROLLABLE self --- @param Wrapper.Unit#UNIT EngageUnit The UNIT. --- @param #number Priority (optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param Dcs.DCSTypes#Distance Altitude (optional) Desired altitude to perform the unit engagement. --- @param #boolean Visible (optional) Unit must be visible. --- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskEngageUnit( EngageUnit, Priority, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack ) - self:F2( { self.ControllableName, EngageUnit, Priority, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack } ) - - -- EngageUnit = { - -- id = 'EngageUnit', - -- params = { - -- unitId = Unit.ID, - -- weaponType = number, - -- expend = enum AI.Task.WeaponExpend - -- attackQty = number, - -- direction = Azimuth, - -- attackQtyLimit = boolean, - -- controllableAttack = boolean, - -- priority = number, - -- } - -- } - - local DCSTask - DCSTask = { id = 'EngageUnit', - params = { - unitId = EngageUnit:GetID(), - priority = Priority or 1, - groupAttack = GroupAttack or false, - visible = Visible or false, - expend = WeaponExpend or "Auto", - directionEnabled = Direction and true or false, - direction = Direction, - altitudeEnabled = Altitude and true or false, - altitude = Altitude, - attackQtyLimit = AttackQty and true or false, - attackQty = AttackQty, - controllableAttack = ControllableAttack, - }, - }, - - self:T3( { DCSTask } ) - return DCSTask -end - - - ---- (AIR) Aircraft will act as an AWACS for friendly units (will provide them with information about contacts). No parameters. --- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskAWACS( ) - self:F2( { self.ControllableName } ) - --- AWACS = { --- id = 'AWACS', --- params = { --- } --- } - - local DCSTask - DCSTask = { id = 'AWACS', - params = { - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR) Aircraft will act as a tanker for friendly units. No parameters. --- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskTanker( ) - self:F2( { self.ControllableName } ) - --- Tanker = { --- id = 'Tanker', --- params = { --- } --- } - - local DCSTask - DCSTask = { id = 'Tanker', - params = { - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - --- En-route tasks for ground units/controllables - ---- (GROUND) Ground unit (EW-radar) will act as an EWR for friendly units (will provide them with information about contacts). No parameters. --- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskEWR( ) - self:F2( { self.ControllableName } ) - --- EWR = { --- id = 'EWR', --- params = { --- } --- } - - local DCSTask - DCSTask = { id = 'EWR', - params = { - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - --- En-route tasks for airborne and ground units/controllables - ---- (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose the target (enemy ground controllable) as well as other assigned targets. --- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC. --- If the task is assigned to the controllable lead unit will be a FAC. --- @param #CONTROLLABLE self --- @param Wrapper.Controllable#CONTROLLABLE AttackGroup Target CONTROLLABLE. --- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @param #number WeaponType Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.Designation Designation (optional) Designation type. --- @param #boolean Datalink (optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskFAC_EngageGroup( AttackGroup, Priority, WeaponType, Designation, Datalink ) - self:F2( { self.ControllableName, AttackGroup, WeaponType, Priority, Designation, Datalink } ) - --- FAC_EngageControllable = { --- id = 'FAC_EngageControllable', --- params = { --- groupId = Group.ID, --- weaponType = number, --- designation = enum AI.Task.Designation, --- datalink = boolean, --- priority = number, --- } --- } - - local DCSTask - DCSTask = { id = 'FAC_EngageControllable', - params = { - groupId = AttackGroup:GetID(), - weaponType = WeaponType, - designation = Designation, - datalink = Datalink, - priority = Priority, - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - ---- (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose a targets (enemy ground controllable) around as well as other assigned targets. --- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC. --- If the task is assigned to the controllable lead unit will be a FAC. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Distance Radius The maximal distance from the FAC to a target. --- @param #number Priority All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:EnRouteTaskFAC( Radius, Priority ) - self:F2( { self.ControllableName, Radius, Priority } ) - --- FAC = { --- id = 'FAC', --- params = { --- radius = Distance, --- priority = number --- } --- } - - local DCSTask - DCSTask = { id = 'FAC', - params = { - radius = Radius, - priority = Priority - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - - - ---- (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Point The point where to wait. --- @param #number Duration The duration in seconds to wait. --- @param #CONTROLLABLE EmbarkingControllable The controllable to be embarked. --- @return Dcs.DCSTasking.Task#Task The DCS task structure -function CONTROLLABLE:TaskEmbarking( Point, Duration, EmbarkingControllable ) - self:F2( { self.ControllableName, Point, Duration, EmbarkingControllable.DCSControllable } ) - - local DCSTask - DCSTask = { id = 'Embarking', - params = { x = Point.x, - y = Point.y, - duration = Duration, - controllablesForEmbarking = { EmbarkingControllable.ControllableID }, - durationFlag = true, - distributionFlag = false, - distribution = {}, - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - ---- (GROUND) Embark to a Transport landed at a location. - ---- Move to a defined Vec2 Point, and embark to a controllable when arrived within a defined Radius. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Point The point where to wait. --- @param #number Radius The radius of the embarking zone around the Point. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskEmbarkToTransport( Point, Radius ) - self:F2( { self.ControllableName, Point, Radius } ) - - local DCSTask --Dcs.DCSTasking.Task#Task - DCSTask = { id = 'EmbarkToTransport', - params = { x = Point.x, - y = Point.y, - zoneRadius = Radius, - } - } - - self:T3( { DCSTask } ) - return DCSTask -end - - - ---- (AIR + GROUND) Return a mission task from a mission template. --- @param #CONTROLLABLE self --- @param #table TaskMission A table containing the mission task. --- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:TaskMission( TaskMission ) - self:F2( Points ) - - local DCSTask - DCSTask = { id = 'Mission', params = { TaskMission, }, } - - self:T3( { DCSTask } ) - return DCSTask -end - ---- Return a Misson task to follow a given route defined by Points. --- @param #CONTROLLABLE self --- @param #table Points A table of route points. --- @return Dcs.DCSTasking.Task#Task -function CONTROLLABLE:TaskRoute( Points ) - self:F2( Points ) - - local DCSTask - DCSTask = { id = 'Mission', params = { route = { points = Points, }, }, } - - self:T3( { DCSTask } ) - return DCSTask -end - ---- (AIR + GROUND) Make the Controllable move to fly to a given point. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format. --- @param #number Speed The speed to travel. --- @return #CONTROLLABLE self -function CONTROLLABLE:RouteToVec2( Point, Speed ) - self:F2( { Point, Speed } ) - - local ControllablePoint = self:GetUnit( 1 ):GetVec2() - - local PointFrom = {} - PointFrom.x = ControllablePoint.x - PointFrom.y = ControllablePoint.y - PointFrom.type = "Turning Point" - PointFrom.action = "Turning Point" - PointFrom.speed = Speed - PointFrom.speed_locked = true - PointFrom.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - - - local PointTo = {} - PointTo.x = Point.x - PointTo.y = Point.y - PointTo.type = "Turning Point" - PointTo.action = "Fly Over Point" - PointTo.speed = Speed - PointTo.speed_locked = true - PointTo.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - - - local Points = { PointFrom, PointTo } - - self:T3( Points ) - - self:Route( Points ) - - return self -end - ---- (AIR + GROUND) Make the Controllable move to a given point. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format. --- @param #number Speed The speed to travel. --- @return #CONTROLLABLE self -function CONTROLLABLE:RouteToVec3( Point, Speed ) - self:F2( { Point, Speed } ) - - local ControllableVec3 = self:GetUnit( 1 ):GetVec3() - - local PointFrom = {} - PointFrom.x = ControllableVec3.x - PointFrom.y = ControllableVec3.z - PointFrom.alt = ControllableVec3.y - PointFrom.alt_type = "BARO" - PointFrom.type = "Turning Point" - PointFrom.action = "Turning Point" - PointFrom.speed = Speed - PointFrom.speed_locked = true - PointFrom.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - - - local PointTo = {} - PointTo.x = Point.x - PointTo.y = Point.z - PointTo.alt = Point.y - PointTo.alt_type = "BARO" - PointTo.type = "Turning Point" - PointTo.action = "Fly Over Point" - PointTo.speed = Speed - PointTo.speed_locked = true - PointTo.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - - - local Points = { PointFrom, PointTo } - - self:T3( Points ) - - self:Route( Points ) - - return self -end - - - ---- Make the controllable to follow a given route. --- @param #CONTROLLABLE self --- @param #table GoPoints A table of Route Points. --- @return #CONTROLLABLE self -function CONTROLLABLE:Route( GoPoints ) - self:F2( GoPoints ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local Points = routines.utils.deepCopy( GoPoints ) - local MissionTask = { id = 'Mission', params = { route = { points = Points, }, }, } - local Controller = self:_GetController() - --Controller.setTask( Controller, MissionTask ) - self.TaskScheduler:Schedule( Controller, Controller.setTask, { MissionTask }, 1 ) - return self - end - - return nil -end - - - ---- (AIR + GROUND) Route the controllable to a given zone. --- The controllable final destination point can be randomized. --- A speed can be given in km/h. --- A given formation can be given. --- @param #CONTROLLABLE self --- @param Core.Zone#ZONE Zone The zone where to route to. --- @param #boolean Randomize Defines whether to target point gets randomized within the Zone. --- @param #number Speed The speed. --- @param Base#FORMATION Formation The formation string. -function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation ) - self:F2( Zone ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - - local ControllablePoint = self:GetVec2() - - local PointFrom = {} - PointFrom.x = ControllablePoint.x - PointFrom.y = ControllablePoint.y - PointFrom.type = "Turning Point" - PointFrom.action = Formation or "Cone" - PointFrom.speed = 20 / 1.6 - - - local PointTo = {} - local ZonePoint - - if Randomize then - ZonePoint = Zone:GetRandomVec2() - else - ZonePoint = Zone:GetVec2() - end - - PointTo.x = ZonePoint.x - PointTo.y = ZonePoint.y - PointTo.type = "Turning Point" - - if Formation then - PointTo.action = Formation - else - PointTo.action = "Cone" - end - - if Speed then - PointTo.speed = Speed - else - PointTo.speed = 20 / 1.6 - end - - local Points = { PointFrom, PointTo } - - self:T3( Points ) - - self:Route( Points ) - - return self - end - - return nil -end - - --- Commands - ---- Do Script command --- @param #CONTROLLABLE self --- @param #string DoScript --- @return #DCSCommand -function CONTROLLABLE:CommandDoScript( DoScript ) - - local DCSDoScript = { - id = "Script", - params = { - command = DoScript, - }, - } - - self:T3( DCSDoScript ) - return DCSDoScript -end - - ---- Return the mission template of the controllable. --- @param #CONTROLLABLE self --- @return #table The MissionTemplate --- TODO: Rework the method how to retrieve a template ... -function CONTROLLABLE:GetTaskMission() - self:F2( self.ControllableName ) - - return routines.utils.deepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template ) -end - ---- Return the mission route of the controllable. --- @param #CONTROLLABLE self --- @return #table The mission route defined by points. -function CONTROLLABLE:GetTaskRoute() - self:F2( self.ControllableName ) - - return routines.utils.deepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template.route.points ) -end - - - ---- Return the route of a controllable by using the @{Database#DATABASE} class. --- @param #CONTROLLABLE self --- @param #number Begin The route point from where the copy will start. The base route point is 0. --- @param #number End The route point where the copy will end. The End point is the last point - the End point. The last point has base 0. --- @param #boolean Randomize Randomization of the route, when true. --- @param #number Radius When randomization is on, the randomization is within the radius. -function CONTROLLABLE:CopyRoute( Begin, End, Randomize, Radius ) - self:F2( { Begin, End } ) - - local Points = {} - - -- Could be a Spawned Controllable - local ControllableName = string.match( self:GetName(), ".*#" ) - if ControllableName then - ControllableName = ControllableName:sub( 1, -2 ) - else - ControllableName = self:GetName() - end - - self:T3( { ControllableName } ) - - local Template = _DATABASE.Templates.Controllables[ControllableName].Template - - if Template then - if not Begin then - Begin = 0 - end - if not End then - End = 0 - end - - for TPointID = Begin + 1, #Template.route.points - End do - if Template.route.points[TPointID] then - Points[#Points+1] = routines.utils.deepCopy( Template.route.points[TPointID] ) - if Randomize then - if not Radius then - Radius = 500 - end - Points[#Points].x = Points[#Points].x + math.random( Radius * -1, Radius ) - Points[#Points].y = Points[#Points].y + math.random( Radius * -1, Radius ) - end - end - end - return Points - else - error( "Template not found for Controllable : " .. ControllableName ) - end - - return nil -end - - ---- Return the detected targets of the controllable. --- The optional parametes specify the detection methods that can be applied. --- If no detection method is given, the detection will use all the available methods by default. --- @param Wrapper.Controllable#CONTROLLABLE self --- @param #boolean DetectVisual (optional) --- @param #boolean DetectOptical (optional) --- @param #boolean DetectRadar (optional) --- @param #boolean DetectIRST (optional) --- @param #boolean DetectRWR (optional) --- @param #boolean DetectDLINK (optional) --- @return #table DetectedTargets -function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK ) - self:F2( self.ControllableName ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local DetectionVisual = ( DetectVisual and DetectVisual == true ) and Controller.Detection.VISUAL or nil - local DetectionOptical = ( DetectOptical and DetectOptical == true ) and Controller.Detection.OPTICAL or nil - local DetectionRadar = ( DetectRadar and DetectRadar == true ) and Controller.Detection.RADAR or nil - local DetectionIRST = ( DetectIRST and DetectIRST == true ) and Controller.Detection.IRST or nil - local DetectionRWR = ( DetectRWR and DetectRWR == true ) and Controller.Detection.RWR or nil - local DetectionDLINK = ( DetectDLINK and DetectDLINK == true ) and Controller.Detection.DLINK or nil - - - return self:_GetController():getDetectedTargets( DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK ) - end - - return nil -end - -function CONTROLLABLE:IsTargetDetected( DCSObject ) - self:F2( self.ControllableName ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - - local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity - = self:_GetController().isTargetDetected( self:_GetController(), DCSObject, - Controller.Detection.VISUAL, - Controller.Detection.OPTIC, - Controller.Detection.RADAR, - Controller.Detection.IRST, - Controller.Detection.RWR, - Controller.Detection.DLINK - ) - return TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity - end - - return nil -end - --- Options - ---- Can the CONTROLLABLE hold their weapons? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROEHoldFirePossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() or self:IsGround() or self:IsShip() then - return true - end - - return false - end - - return nil -end - ---- Holding weapons. --- @param Wrapper.Controllable#CONTROLLABLE self --- @return Wrapper.Controllable#CONTROLLABLE self -function CONTROLLABLE:OptionROEHoldFire() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) - elseif self:IsGround() then - Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.WEAPON_HOLD ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE attack returning on enemy fire? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROEReturnFirePossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() or self:IsGround() or self:IsShip() then - return true - end - - return false - end - - return nil -end - ---- Return fire. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROEReturnFire() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.RETURN_FIRE ) - elseif self:IsGround() then - Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.RETURN_FIRE ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.RETURN_FIRE ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE attack designated targets? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROEOpenFirePossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() or self:IsGround() or self:IsShip() then - return true - end - - return false - end - - return nil -end - ---- Openfire. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROEOpenFire() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) - elseif self:IsGround() then - Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.OPEN_FIRE ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.OPEN_FIRE ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE attack targets of opportunity? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROEWeaponFreePossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() then - return true - end - - return false - end - - return nil -end - ---- Weapon free. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROEWeaponFree() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_FREE ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE ignore enemy fire? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROTNoReactionPossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() then - return true - end - - return false - end - - return nil -end - - ---- No evasion on enemy threats. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROTNoReaction() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.NO_REACTION ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE evade using passive defenses? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROTPassiveDefensePossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() then - return true - end - - return false - end - - return nil -end - ---- Evasion passive defense. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROTPassiveDefense() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.PASSIVE_DEFENCE ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE evade on enemy fire? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROTEvadeFirePossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() then - return true - end - - return false - end - - return nil -end - - ---- Evade on fire. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROTEvadeFire() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) - end - - return self - end - - return nil -end - ---- Can the CONTROLLABLE evade on fire using vertical manoeuvres? --- @param #CONTROLLABLE self --- @return #boolean -function CONTROLLABLE:OptionROTVerticalPossible() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - if self:IsAir() then - return true - end - - return false - end - - return nil -end - - ---- Evade on fire using vertical manoeuvres. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE self -function CONTROLLABLE:OptionROTVertical() - self:F2( { self.ControllableName } ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local Controller = self:_GetController() - - if self:IsAir() then - Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) - end - - return self - end - - return nil -end - ---- Retrieve the controllable mission and allow to place function hooks within the mission waypoint plan. --- Use the method @{Controllable#CONTROLLABLE:WayPointFunction} to define the hook functions for specific waypoints. --- Use the method @{Controllable@CONTROLLABLE:WayPointExecute) to start the execution of the new mission plan. --- Note that when WayPointInitialize is called, the Mission of the controllable is RESTARTED! --- @param #CONTROLLABLE self --- @param #table WayPoints If WayPoints is given, then use the route. --- @return #CONTROLLABLE -function CONTROLLABLE:WayPointInitialize( WayPoints ) - self:F( { WayPoints } ) - - if WayPoints then - self.WayPoints = WayPoints - else - self.WayPoints = self:GetTaskRoute() - end - - return self -end - ---- Get the current WayPoints set with the WayPoint functions( Note that the WayPoints can be nil, although there ARE waypoints). --- @param #CONTROLLABLE self --- @return #table WayPoints If WayPoints is given, then return the WayPoints structure. -function CONTROLLABLE:GetWayPoints() - self:F( ) - - if self.WayPoints then - return self.WayPoints - end - - return nil -end - ---- Registers a waypoint function that will be executed when the controllable moves over the WayPoint. --- @param #CONTROLLABLE self --- @param #number WayPoint The waypoint number. Note that the start waypoint on the route is WayPoint 1! --- @param #number WayPointIndex When defining multiple WayPoint functions for one WayPoint, use WayPointIndex to set the sequence of actions. --- @param #function WayPointFunction The waypoint function to be called when the controllable moves over the waypoint. The waypoint function takes variable parameters. --- @return #CONTROLLABLE -function CONTROLLABLE:WayPointFunction( WayPoint, WayPointIndex, WayPointFunction, ... ) - self:F2( { WayPoint, WayPointIndex, WayPointFunction } ) - - table.insert( self.WayPoints[WayPoint].task.params.tasks, WayPointIndex ) - self.WayPoints[WayPoint].task.params.tasks[WayPointIndex] = self:TaskFunction( WayPoint, WayPointIndex, WayPointFunction, arg ) - return self -end - - -function CONTROLLABLE:TaskFunction( WayPoint, WayPointIndex, FunctionString, FunctionArguments ) - self:F2( { WayPoint, WayPointIndex, FunctionString, FunctionArguments } ) - - local DCSTask - - local DCSScript = {} - DCSScript[#DCSScript+1] = "local MissionControllable = GROUP:Find( ... ) " - - if FunctionArguments and #FunctionArguments > 0 then - DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable, " .. table.concat( FunctionArguments, "," ) .. ")" - else - DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable )" - end - - DCSTask = self:TaskWrappedAction( - self:CommandDoScript( - table.concat( DCSScript ) - ), WayPointIndex - ) - - self:T3( DCSTask ) - - return DCSTask - -end - ---- Executes the WayPoint plan. --- The function gets a WayPoint parameter, that you can use to restart the mission at a specific WayPoint. --- Note that when the WayPoint parameter is used, the new start mission waypoint of the controllable will be 1! --- @param #CONTROLLABLE self --- @param #number WayPoint The WayPoint from where to execute the mission. --- @param #number WaitTime The amount seconds to wait before initiating the mission. --- @return #CONTROLLABLE -function CONTROLLABLE:WayPointExecute( WayPoint, WaitTime ) - self:F( { WayPoint, WaitTime } ) - - if not WayPoint then - WayPoint = 1 - end - - -- When starting the mission from a certain point, the TaskPoints need to be deleted before the given WayPoint. - for TaskPointID = 1, WayPoint - 1 do - table.remove( self.WayPoints, 1 ) - end - - self:T3( self.WayPoints ) - - self:SetTask( self:TaskRoute( self.WayPoints ), WaitTime ) - - return self -end - --- Message APIs--- **Wrapper** -- GROUP is a wrapper class for the DCS Class Group. --- --- === --- --- The @{#GROUP} class is a wrapper class to handle the DCS Group objects: --- --- * Support all DCS Group APIs. --- * Enhance with Group specific APIs not in the DCS Group API set. --- * Handle local Group Controller. --- * Manage the "state" of the DCS Group. --- --- **IMPORTANT: ONE SHOULD NEVER SANATIZE these GROUP OBJECT REFERENCES! (make the GROUP object references nil).** --- --- See the detailed documentation on the GROUP class. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-26: GROUP:**RouteRTB( RTBAirbase, Speed )** added. --- --- 2017-03-07: GROUP:**HandleEvent( Event, EventFunction )** added. --- 2017-03-07: GROUP:**UnHandleEvent( Event )** added. --- --- 2017-01-24: GROUP:**SetAIOnOff( AIOnOff )** added. --- --- 2017-01-24: GROUP:**SetAIOn()** added. --- --- 2017-01-24: GROUP:**SetAIOff()** added. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * [**Entropy**](https://forums.eagle.ru/member.php?u=111471), **Afinegan**: Came up with the requirement for AIOnOff(). --- --- ### Authors: --- --- * **FlightControl**: Design & Programming --- --- @module Group --- @author FlightControl - ---- @type GROUP --- @extends Wrapper.Controllable#CONTROLLABLE --- @field #string GroupName The name of the group. - ---- --- # GROUP class, extends @{Controllable#CONTROLLABLE} --- --- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class). --- --- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference --- using the DCS Group or the DCS GroupName. --- --- Another thing to know is that GROUP objects do not "contain" the DCS Group object. --- The GROUP methods will reference the DCS Group object by name when it is needed during API execution. --- If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file. --- --- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance: --- --- * @{#GROUP.Find}(): Find a GROUP instance from the _DATABASE object using a DCS Group object. --- * @{#GROUP.FindByName}(): Find a GROUP instance from the _DATABASE object using a DCS Group name. --- --- ## GROUP task methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} task methods section for a description of the task methods. --- --- ### Obtain the mission from group templates --- --- Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another: --- --- * @{Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. --- --- ## GROUP Command methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} command methods section for a description of the command methods. --- --- ## GROUP option methods --- --- A GROUP is a @{Controllable}. See the @{Controllable} option methods section for a description of the option methods. --- --- ## GROUP Zone validation methods --- --- The group can be validated whether it is completely, partly or not within a @{Zone}. --- Use the following Zone validation methods on the group: --- --- * @{#GROUP.IsCompletelyInZone}: Returns true if all units of the group are within a @{Zone}. --- * @{#GROUP.IsPartlyInZone}: Returns true if some units of the group are within a @{Zone}. --- * @{#GROUP.IsNotInZone}: Returns true if none of the group units of the group are within a @{Zone}. --- --- The zone can be of any @{Zone} class derived from @{Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on. --- --- ## GROUP AI methods --- --- A GROUP has AI methods to control the AI activation. --- --- * @{#GROUP.SetAIOnOff}(): Turns the GROUP AI On or Off. --- * @{#GROUP.SetAIOn}(): Turns the GROUP AI On. --- * @{#GROUP.SetAIOff}(): Turns the GROUP AI Off. --- --- @field #GROUP GROUP -GROUP = { - ClassName = "GROUP", -} - ---- Create a new GROUP from a DCSGroup --- @param #GROUP self --- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name --- @return #GROUP self -function GROUP:Register( GroupName ) - self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) - self:F2( GroupName ) - self.GroupName = GroupName - - self:SetEventPriority( 4 ) - return self -end - --- Reference methods. - ---- Find the GROUP wrapper class instance using the DCS Group. --- @param #GROUP self --- @param Dcs.DCSWrapper.Group#Group DCSGroup The DCS Group. --- @return #GROUP The GROUP. -function GROUP:Find( DCSGroup ) - - local GroupName = DCSGroup:getName() -- Wrapper.Group#GROUP - local GroupFound = _DATABASE:FindGroup( GroupName ) - return GroupFound -end - ---- Find the created GROUP using the DCS Group Name. --- @param #GROUP self --- @param #string GroupName The DCS Group Name. --- @return #GROUP The GROUP. -function GROUP:FindByName( GroupName ) - - local GroupFound = _DATABASE:FindGroup( GroupName ) - return GroupFound -end - --- DCS Group methods support. - ---- Returns the DCS Group. --- @param #GROUP self --- @return Dcs.DCSWrapper.Group#Group The DCS Group. -function GROUP:GetDCSObject() - local DCSGroup = Group.getByName( self.GroupName ) - - if DCSGroup then - return DCSGroup - end - - return nil -end - ---- Returns the @{DCSTypes#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. --- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Position The 3D position vectors of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3() - self:F2( self.PositionableName ) - - local DCSPositionable = self:GetDCSObject() - - if DCSPositionable then - local PositionablePosition = DCSPositionable:getUnits()[1]:getPosition().p - self:T3( PositionablePosition ) - return PositionablePosition - end - - return nil -end - ---- Returns if the Group is alive. --- The Group must: --- --- * Exist at run-time. --- * Has at least one unit. --- --- When the first @{Unit} of the Group is active, it will return true. --- If the first @{Unit} of the Group is inactive, it will return false. --- --- @param #GROUP self --- @return #boolean true if the Group is alive and active. --- @return #boolean false if the Group is alive but inactive. --- @return #nil if the group does not exist anymore. -function GROUP:IsAlive() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() -- Dcs.DCSGroup#Group - - if DCSGroup then - if DCSGroup:isExist() then - local DCSUnit = DCSGroup:getUnit(1) -- Dcs.DCSUnit#Unit - if DCSUnit then - local GroupIsAlive = DCSUnit:isActive() - self:T3( GroupIsAlive ) - return GroupIsAlive - end - end - end - - return nil -end - ---- Destroys the DCS Group and all of its DCS Units. --- Note that this destroy method also raises a destroy event at run-time. --- So all event listeners will catch the destroy event of this DCS Group. --- @param #GROUP self -function GROUP:Destroy() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - for Index, UnitData in pairs( DCSGroup:getUnits() ) do - self:CreateEventCrash( timer.getTime(), UnitData ) - end - DCSGroup:destroy() - DCSGroup = nil - end - - return nil -end - ---- Returns category of the DCS Group. --- @param #GROUP self --- @return Dcs.DCSWrapper.Group#Group.Category The category ID -function GROUP:GetCategory() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - if DCSGroup then - local GroupCategory = DCSGroup:getCategory() - self:T3( GroupCategory ) - return GroupCategory - end - - return nil -end - ---- Returns the category name of the #GROUP. --- @param #GROUP self --- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship -function GROUP:GetCategoryName() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - if DCSGroup then - local CategoryNames = { - [Group.Category.AIRPLANE] = "Airplane", - [Group.Category.HELICOPTER] = "Helicopter", - [Group.Category.GROUND] = "Ground Unit", - [Group.Category.SHIP] = "Ship", - } - local GroupCategory = DCSGroup:getCategory() - self:T3( GroupCategory ) - - return CategoryNames[GroupCategory] - end - - return nil -end - - ---- Returns the coalition of the DCS Group. --- @param #GROUP self --- @return Dcs.DCSCoalitionWrapper.Object#coalition.side The coalition side of the DCS Group. -function GROUP:GetCoalition() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - if DCSGroup then - local GroupCoalition = DCSGroup:getCoalition() - self:T3( GroupCoalition ) - return GroupCoalition - end - - return nil -end - ---- Returns the country of the DCS Group. --- @param #GROUP self --- @return Dcs.DCScountry#country.id The country identifier. --- @return #nil The DCS Group is not existing or alive. -function GROUP:GetCountry() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - if DCSGroup then - local GroupCountry = DCSGroup:getUnit(1):getCountry() - self:T3( GroupCountry ) - return GroupCountry - end - - return nil -end - ---- Returns the UNIT wrapper class with number UnitNumber. --- If the underlying DCS Unit does not exist, the method will return nil. . --- @param #GROUP self --- @param #number UnitNumber The number of the UNIT wrapper class to be returned. --- @return Wrapper.Unit#UNIT The UNIT wrapper class. -function GROUP:GetUnit( UnitNumber ) - self:F2( { self.GroupName, UnitNumber } ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local UnitFound = UNIT:Find( DCSGroup:getUnit( UnitNumber ) ) - self:T2( UnitFound ) - return UnitFound - end - - return nil -end - ---- Returns the DCS Unit with number UnitNumber. --- If the underlying DCS Unit does not exist, the method will return nil. . --- @param #GROUP self --- @param #number UnitNumber The number of the DCS Unit to be returned. --- @return Dcs.DCSWrapper.Unit#Unit The DCS Unit. -function GROUP:GetDCSUnit( UnitNumber ) - self:F2( { self.GroupName, UnitNumber } ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local DCSUnitFound = DCSGroup:getUnit( UnitNumber ) - self:T3( DCSUnitFound ) - return DCSUnitFound - end - - return nil -end - ---- Returns current size of the DCS Group. --- If some of the DCS Units of the DCS Group are destroyed the size of the DCS Group is changed. --- @param #GROUP self --- @return #number The DCS Group size. -function GROUP:GetSize() - self:F2( { self.GroupName } ) - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupSize = DCSGroup:getSize() - self:T3( GroupSize ) - return GroupSize - end - - return nil -end - ---- ---- Returns the initial size of the DCS Group. --- If some of the DCS Units of the DCS Group are destroyed, the initial size of the DCS Group is unchanged. --- @param #GROUP self --- @return #number The DCS Group initial size. -function GROUP:GetInitialSize() - self:F2( { self.GroupName } ) - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupInitialSize = DCSGroup:getInitialSize() - self:T3( GroupInitialSize ) - return GroupInitialSize - end - - return nil -end - - ---- Returns the DCS Units of the DCS Group. --- @param #GROUP self --- @return #table The DCS Units. -function GROUP:GetDCSUnits() - self:F2( { self.GroupName } ) - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local DCSUnits = DCSGroup:getUnits() - self:T3( DCSUnits ) - return DCSUnits - end - - return nil -end - - ---- Activates a GROUP. --- @param #GROUP self -function GROUP:Activate() - self:F2( { self.GroupName } ) - trigger.action.activateGroup( self:GetDCSObject() ) - return self:GetDCSObject() -end - - ---- Gets the type name of the group. --- @param #GROUP self --- @return #string The type name of the group. -function GROUP:GetTypeName() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupTypeName = DCSGroup:getUnit(1):getTypeName() - self:T3( GroupTypeName ) - return( GroupTypeName ) - end - - return nil -end - ---- Gets the CallSign of the first DCS Unit of the DCS Group. --- @param #GROUP self --- @return #string The CallSign of the first DCS Unit of the DCS Group. -function GROUP:GetCallsign() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupCallSign = DCSGroup:getUnit(1):getCallsign() - self:T3( GroupCallSign ) - return GroupCallSign - end - - return nil -end - ---- Returns the current point (Vec2 vector) of the first DCS Unit in the DCS Group. --- @param #GROUP self --- @return Dcs.DCSTypes#Vec2 Current Vec2 point of the first DCS Unit of the DCS Group. -function GROUP:GetVec2() - self:F2( self.GroupName ) - - local UnitPoint = self:GetUnit(1) - UnitPoint:GetVec2() - local GroupPointVec2 = UnitPoint:GetVec2() - self:T3( GroupPointVec2 ) - return GroupPointVec2 -end - ---- Returns the current Vec3 vector of the first DCS Unit in the GROUP. --- @param #GROUP self --- @return Dcs.DCSTypes#Vec3 Current Vec3 of the first DCS Unit of the GROUP. -function GROUP:GetVec3() - self:F2( self.GroupName ) - - local GroupVec3 = self:GetUnit(1):GetVec3() - self:T3( GroupVec3 ) - return GroupVec3 -end - ---- Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission. --- @param #GROUP self --- @return Core.Point#POINT_VEC2 The 2D point vector of the first DCS Unit of the GROUP. --- @return #nil The first UNIT is not existing or alive. -function GROUP:GetPointVec2() - self:F2(self.GroupName) - - local FirstUnit = self:GetUnit(1) - - if FirstUnit then - local FirstUnitPointVec2 = FirstUnit:GetPointVec2() - self:T3(FirstUnitPointVec2) - return FirstUnitPointVec2 - end - - return nil -end - ---- Returns a random @{DCSTypes#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. --- @param #GROUP self --- @param #number Radius --- @return Dcs.DCSTypes#Vec3 The random 3D point vector around the first UNIT of the GROUP. --- @return #nil The GROUP is invalid or empty --- @usage --- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP -function GROUP:GetRandomVec3(Radius) - self:F2(self.GroupName) - - local FirstUnit = self:GetUnit(1) - - if FirstUnit then - local FirstUnitRandomPointVec3 = FirstUnit:GetRandomVec3(Radius) - self:T3(FirstUnitRandomPointVec3) - return FirstUnitRandomPointVec3 - end - - return nil -end - ---- Returns the mean heading of every UNIT in the GROUP in degrees --- @param #GROUP self --- @return #number mean heading of the GROUP --- @return #nil The first UNIT is not existing or alive. -function GROUP:GetHeading() - self:F2(self.GroupName) - - local GroupSize = self:GetSize() - local HeadingAccumulator = 0 - - if GroupSize then - for i = 1, GroupSize do - HeadingAccumulator = HeadingAccumulator + self:GetUnit(i):GetHeading() - end - return math.floor(HeadingAccumulator / GroupSize) - end - - return nil - -end - -do -- Is Zone methods - ---- Returns true if all units of the group are within a @{Zone}. --- @param #GROUP self --- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE} -function GROUP:IsCompletelyInZone( Zone ) - self:F2( { self.GroupName, Zone } ) - - for UnitID, UnitData in pairs( self:GetUnits() ) do - local Unit = UnitData -- Wrapper.Unit#UNIT - if Zone:IsVec3InZone( Unit:GetVec3() ) then - else - return false - end - end - - return true -end - ---- Returns true if some units of the group are within a @{Zone}. --- @param #GROUP self --- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE} -function GROUP:IsPartlyInZone( Zone ) - self:F2( { self.GroupName, Zone } ) - - for UnitID, UnitData in pairs( self:GetUnits() ) do - local Unit = UnitData -- Wrapper.Unit#UNIT - if Zone:IsVec3InZone( Unit:GetVec3() ) then - return true - end - end - - return false -end - ---- Returns true if none of the group units of the group are within a @{Zone}. --- @param #GROUP self --- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE} -function GROUP:IsNotInZone( Zone ) - self:F2( { self.GroupName, Zone } ) - - for UnitID, UnitData in pairs( self:GetUnits() ) do - local Unit = UnitData -- Wrapper.Unit#UNIT - if Zone:IsVec3InZone( Unit:GetVec3() ) then - return false - end - end - - return true -end - ---- Returns if the group is of an air category. --- If the group is a helicopter or a plane, then this method will return true, otherwise false. --- @param #GROUP self --- @return #boolean Air category evaluation result. -function GROUP:IsAir() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local IsAirResult = DCSGroup:getCategory() == Group.Category.AIRPLANE or DCSGroup:getCategory() == Group.Category.HELICOPTER - self:T3( IsAirResult ) - return IsAirResult - end - - return nil -end - ---- Returns if the DCS Group contains Helicopters. --- @param #GROUP self --- @return #boolean true if DCS Group contains Helicopters. -function GROUP:IsHelicopter() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupCategory = DCSGroup:getCategory() - self:T2( GroupCategory ) - return GroupCategory == Group.Category.HELICOPTER - end - - return nil -end - ---- Returns if the DCS Group contains AirPlanes. --- @param #GROUP self --- @return #boolean true if DCS Group contains AirPlanes. -function GROUP:IsAirPlane() - self:F2() - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupCategory = DCSGroup:getCategory() - self:T2( GroupCategory ) - return GroupCategory == Group.Category.AIRPLANE - end - - return nil -end - ---- Returns if the DCS Group contains Ground troops. --- @param #GROUP self --- @return #boolean true if DCS Group contains Ground troops. -function GROUP:IsGround() - self:F2() - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupCategory = DCSGroup:getCategory() - self:T2( GroupCategory ) - return GroupCategory == Group.Category.GROUND - end - - return nil -end - ---- Returns if the DCS Group contains Ships. --- @param #GROUP self --- @return #boolean true if DCS Group contains Ships. -function GROUP:IsShip() - self:F2() - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupCategory = DCSGroup:getCategory() - self:T2( GroupCategory ) - return GroupCategory == Group.Category.SHIP - end - - return nil -end - ---- Returns if all units of the group are on the ground or landed. --- If all units of this group are on the ground, this function will return true, otherwise false. --- @param #GROUP self --- @return #boolean All units on the ground result. -function GROUP:AllOnGround() - self:F2() - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local AllOnGroundResult = true - - for Index, UnitData in pairs( DCSGroup:getUnits() ) do - if UnitData:inAir() then - AllOnGroundResult = false - end - end - - self:T3( AllOnGroundResult ) - return AllOnGroundResult - end - - return nil -end - -end - -do -- AI methods - - --- Turns the AI On or Off for the GROUP. - -- @param #GROUP self - -- @param #boolean AIOnOff The value true turns the AI On, the value false turns the AI Off. - -- @return #GROUP The GROUP. - function GROUP:SetAIOnOff( AIOnOff ) - - local DCSGroup = self:GetDCSObject() -- Dcs.DCSGroup#Group - - if DCSGroup then - local DCSController = DCSGroup:getController() -- Dcs.DCSController#Controller - if DCSController then - DCSController:setOnOff( AIOnOff ) - return self - end - end - - return nil - end - - --- Turns the AI On for the GROUP. - -- @param #GROUP self - -- @return #GROUP The GROUP. - function GROUP:SetAIOn() - - return self:SetAIOnOff( true ) - end - - --- Turns the AI Off for the GROUP. - -- @param #GROUP self - -- @return #GROUP The GROUP. - function GROUP:SetAIOff() - - return self:SetAIOnOff( false ) - end - -end - - - ---- Returns the current maximum velocity of the group. --- Each unit within the group gets evaluated, and the maximum velocity (= the unit which is going the fastest) is returned. --- @param #GROUP self --- @return #number Maximum velocity found. -function GROUP:GetMaxVelocity() - self:F2() - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local GroupVelocityMax = 0 - - for Index, UnitData in pairs( DCSGroup:getUnits() ) do - - local UnitVelocityVec3 = UnitData:getVelocity() - local UnitVelocity = math.abs( UnitVelocityVec3.x ) + math.abs( UnitVelocityVec3.y ) + math.abs( UnitVelocityVec3.z ) - - if UnitVelocity > GroupVelocityMax then - GroupVelocityMax = UnitVelocity - end - end - - return GroupVelocityMax - end - - return nil -end - ---- Returns the current minimum height of the group. --- Each unit within the group gets evaluated, and the minimum height (= the unit which is the lowest elevated) is returned. --- @param #GROUP self --- @return #number Minimum height found. -function GROUP:GetMinHeight() - self:F2() - -end - ---- Returns the current maximum height of the group. --- Each unit within the group gets evaluated, and the maximum height (= the unit which is the highest elevated) is returned. --- @param #GROUP self --- @return #number Maximum height found. -function GROUP:GetMaxHeight() - self:F2() - -end - --- SPAWNING - ---- Respawn the @{GROUP} using a (tweaked) template of the Group. --- The template must be retrieved with the @{Group#GROUP.GetTemplate}() function. --- The template contains all the definitions as declared within the mission file. --- To understand templates, do the following: --- --- * unpack your .miz file into a directory using 7-zip. --- * browse in the directory created to the file **mission**. --- * open the file and search for the country group definitions. --- --- Your group template will contain the fields as described within the mission file. --- --- This function will: --- --- * Get the current position and heading of the group. --- * When the group is alive, it will tweak the template x, y and heading coordinates of the group and the embedded units to the current units positions. --- * Then it will destroy the current alive group. --- * And it will respawn the group using your new template definition. --- @param Wrapper.Group#GROUP self --- @param #table Template The template of the Group retrieved with GROUP:GetTemplate() -function GROUP:Respawn( Template ) - - local Vec3 = self:GetVec3() - Template.x = Vec3.x - Template.y = Vec3.z - --Template.x = nil - --Template.y = nil - - self:E( #Template.units ) - for UnitID, UnitData in pairs( self:GetUnits() ) do - local GroupUnit = UnitData -- Wrapper.Unit#UNIT - self:E( GroupUnit:GetName() ) - if GroupUnit:IsAlive() then - local GroupUnitVec3 = GroupUnit:GetVec3() - local GroupUnitHeading = GroupUnit:GetHeading() - Template.units[UnitID].alt = GroupUnitVec3.y - Template.units[UnitID].x = GroupUnitVec3.x - Template.units[UnitID].y = GroupUnitVec3.z - Template.units[UnitID].heading = GroupUnitHeading - self:E( { UnitID, Template.units[UnitID], Template.units[UnitID] } ) - end - end - - self:Destroy() - _DATABASE:Spawn( Template ) -end - ---- Returns the group template from the @{DATABASE} (_DATABASE object). --- @param #GROUP self --- @return #table -function GROUP:GetTemplate() - local GroupName = self:GetName() - self:E( GroupName ) - return _DATABASE:GetGroupTemplate( GroupName ) -end - ---- Sets the controlled status in a Template. --- @param #GROUP self --- @param #boolean Controlled true is controlled, false is uncontrolled. --- @return #table -function GROUP:SetTemplateControlled( Template, Controlled ) - Template.uncontrolled = not Controlled - return Template -end - ---- Sets the CountryID of the group in a Template. --- @param #GROUP self --- @param Dcs.DCScountry#country.id CountryID The country ID. --- @return #table -function GROUP:SetTemplateCountry( Template, CountryID ) - Template.CountryID = CountryID - return Template -end - ---- Sets the CoalitionID of the group in a Template. --- @param #GROUP self --- @param Dcs.DCSCoalitionWrapper.Object#coalition.side CoalitionID The coalition ID. --- @return #table -function GROUP:SetTemplateCoalition( Template, CoalitionID ) - Template.CoalitionID = CoalitionID - return Template -end - - - - ---- Return the mission template of the group. --- @param #GROUP self --- @return #table The MissionTemplate -function GROUP:GetTaskMission() - self:F2( self.GroupName ) - - return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template ) -end - ---- Return the mission route of the group. --- @param #GROUP self --- @return #table The mission route defined by points. -function GROUP:GetTaskRoute() - self:F2( self.GroupName ) - - return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template.route.points ) -end - ---- Return the route of a group by using the @{Database#DATABASE} class. --- @param #GROUP self --- @param #number Begin The route point from where the copy will start. The base route point is 0. --- @param #number End The route point where the copy will end. The End point is the last point - the End point. The last point has base 0. --- @param #boolean Randomize Randomization of the route, when true. --- @param #number Radius When randomization is on, the randomization is within the radius. -function GROUP:CopyRoute( Begin, End, Randomize, Radius ) - self:F2( { Begin, End } ) - - local Points = {} - - -- Could be a Spawned Group - local GroupName = string.match( self:GetName(), ".*#" ) - if GroupName then - GroupName = GroupName:sub( 1, -2 ) - else - GroupName = self:GetName() - end - - self:T3( { GroupName } ) - - local Template = _DATABASE.Templates.Groups[GroupName].Template - - if Template then - if not Begin then - Begin = 0 - end - if not End then - End = 0 - end - - for TPointID = Begin + 1, #Template.route.points - End do - if Template.route.points[TPointID] then - Points[#Points+1] = routines.utils.deepCopy( Template.route.points[TPointID] ) - if Randomize then - if not Radius then - Radius = 500 - end - Points[#Points].x = Points[#Points].x + math.random( Radius * -1, Radius ) - Points[#Points].y = Points[#Points].y + math.random( Radius * -1, Radius ) - end - end - end - return Points - else - error( "Template not found for Group : " .. GroupName ) - end - - return nil -end - ---- Calculate the maxium A2G threat level of the Group. --- @param #GROUP self -function GROUP:CalculateThreatLevelA2G() - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( self:GetUnits() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end - - self:T3( MaxThreatLevelA2G ) - return MaxThreatLevelA2G -end - ---- Returns true if the first unit of the GROUP is in the air. --- @param Wrapper.Group#GROUP self --- @return #boolean true if in the first unit of the group is in the air. --- @return #nil The GROUP is not existing or not alive. -function GROUP:InAir() - self:F2( self.GroupName ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - local DCSUnit = DCSGroup:getUnit(1) - if DCSUnit then - local GroupInAir = DCSGroup:getUnit(1):inAir() - self:T3( GroupInAir ) - return GroupInAir - end - end - - return nil -end - -do -- Route methods - - --- (AIR) Return the Group to an @{Airbase#AIRBASE}. - -- The following things are to be taken into account: - -- - -- * The group is respawned to achieve the RTB, there may be side artefacts as a result of this. (Like weapons suddenly come back). - -- * A group consisting out of more than one unit, may rejoin formation when respawned. - -- * A speed can be given in km/h. If no speed is specified, the maximum speed of the first unit will be taken to return to base. - -- * When there is no @{Airbase} object specified, the group will return to the home base if the route of the group is pinned at take-off or at landing to a base. - -- * When there is no @{Airbase} object specified and the group route is not pinned to any airbase, it will return to the nearest airbase. - -- - -- @param #GROUP self - -- @param Wrapper.Airbase#AIRBASE RTBAirbase (optional) The @{Airbase} to return to. If blank, the controllable will return to the nearest friendly airbase. - -- @param #number Speed (optional) The Speed, if no Speed is given, the maximum Speed of the first unit is selected. - -- @return #GROUP - function GROUP:RouteRTB( RTBAirbase, Speed ) - self:F2( { RTBAirbase, Speed } ) - - local DCSGroup = self:GetDCSObject() - - if DCSGroup then - - if RTBAirbase then - - local GroupPoint = self:GetVec2() - local GroupVelocity = self:GetUnit(1):GetDesc().speedMax - - local PointFrom = {} - PointFrom.x = GroupPoint.x - PointFrom.y = GroupPoint.y - PointFrom.type = "Turning Point" - PointFrom.action = "Turning Point" - PointFrom.speed = GroupVelocity - - - local PointTo = {} - local AirbasePointVec2 = RTBAirbase:GetPointVec2() - local AirbaseAirPoint = AirbasePointVec2:RoutePointAir( - POINT_VEC3.RoutePointAltType.BARO, - "Land", - "Landing", - Speed or self:GetUnit(1):GetDesc().speedMax - ) - - AirbaseAirPoint["airdromeId"] = RTBAirbase:GetID() - AirbaseAirPoint["speed_locked"] = true, - - self:E(AirbaseAirPoint ) - - local Points = { PointFrom, AirbaseAirPoint } - - self:T3( Points ) - - local Template = self:GetTemplate() - Template.route.points = Points - self:Respawn( Template ) - - self:Route( Points ) - - self:Respawn(Template) - else - self:ClearTasks() - end - end - - return self - end - -end - -function GROUP:OnReSpawn( ReSpawnFunction ) - - self.ReSpawnFunction = ReSpawnFunction -end - -do -- Event Handling - - --- Subscribe to a DCS Event. - -- @param #GROUP self - -- @param Core.Event#EVENTS Event - -- @param #function EventFunction (optional) The function to be called when the event occurs for the GROUP. - -- @return #GROUP - function GROUP:HandleEvent( Event, EventFunction ) - - self:EventDispatcher():OnEventForGroup( self:GetName(), EventFunction, self, Event ) - - return self - end - - --- UnSubscribe to a DCS event. - -- @param #GROUP self - -- @param Core.Event#EVENTS Event - -- @return #GROUP - function GROUP:UnHandleEvent( Event ) - - self:EventDispatcher():RemoveForGroup( self:GetName(), self, Event ) - - return self - end - -end - -do -- Players - - --- Get player names - -- @param #GROUP self - -- @return #table The group has players, an array of player names is returned. - -- @return #nil The group has no players - function GROUP:GetPlayerNames() - - local PlayerNames = nil - - local Units = self:GetUnits() - for UnitID, UnitData in pairs( Units ) do - local Unit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = Unit:GetPlayerName() - if PlayerName and PlayerName ~= "" then - PlayerNames = PlayerNames or {} - table.insert( PlayerNames, PlayerName ) - end - end - - self:F( PlayerNames ) - return PlayerNames - end - -end--- **Wrapper** - UNIT is a wrapper class for the DCS Class Unit. --- --- === --- --- The @{#UNIT} class is a wrapper class to handle the DCS Unit objects: --- --- * Support all DCS Unit APIs. --- * Enhance with Unit specific APIs not in the DCS Unit API set. --- * Handle local Unit Controller. --- * Manage the "state" of the DCS Unit. --- --- @module Unit - ---- @type UNIT --- @extends Wrapper.Controllable#CONTROLLABLE - ---- --- # UNIT class, extends @{Controllable#CONTROLLABLE} --- --- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Unit objects are spawned (using the @{SPAWN} class). --- --- The UNIT class **does not contain a :New()** method, rather it provides **:Find()** methods to retrieve the object reference --- using the DCS Unit or the DCS UnitName. --- --- Another thing to know is that UNIT objects do not "contain" the DCS Unit object. --- The UNIT methods will reference the DCS Unit object by name when it is needed during API execution. --- If the DCS Unit object does not exist or is nil, the UNIT methods will return nil and log an exception in the DCS.log file. --- --- The UNIT class provides the following functions to retrieve quickly the relevant UNIT instance: --- --- * @{#UNIT.Find}(): Find a UNIT instance from the _DATABASE object using a DCS Unit object. --- * @{#UNIT.FindByName}(): Find a UNIT instance from the _DATABASE object using a DCS Unit name. --- --- IMPORTANT: ONE SHOULD NEVER SANATIZE these UNIT OBJECT REFERENCES! (make the UNIT object references nil). --- --- ## DCS UNIT APIs --- --- The DCS Unit APIs are used extensively within MOOSE. The UNIT class has for each DCS Unit API a corresponding method. --- To be able to distinguish easily in your code the difference between a UNIT API call and a DCS Unit API call, --- the first letter of the method is also capitalized. So, by example, the DCS Unit method @{DCSWrapper.Unit#Unit.getName}() --- is implemented in the UNIT class as @{#UNIT.GetName}(). --- --- ## Smoke, Flare Units --- --- The UNIT class provides methods to smoke or flare units easily. --- The @{#UNIT.SmokeBlue}(), @{#UNIT.SmokeGreen}(),@{#UNIT.SmokeOrange}(), @{#UNIT.SmokeRed}(), @{#UNIT.SmokeRed}() methods --- will smoke the unit in the corresponding color. Note that smoking a unit is done at the current position of the DCS Unit. --- When the DCS Unit moves for whatever reason, the smoking will still continue! --- The @{#UNIT.FlareGreen}(), @{#UNIT.FlareRed}(), @{#UNIT.FlareWhite}(), @{#UNIT.FlareYellow}() --- methods will fire off a flare in the air with the corresponding color. Note that a flare is a one-off shot and its effect is of very short duration. --- --- ## Location Position, Point --- --- The UNIT class provides methods to obtain the current point or position of the DCS Unit. --- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetVec3}() will obtain the current **location** of the DCS Unit in a Vec2 (2D) or a **point** in a Vec3 (3D) vector respectively. --- If you want to obtain the complete **3D position** including ori�ntation and direction vectors, consult the @{#UNIT.GetPositionVec3}() method respectively. --- --- ## Test if alive --- --- The @{#UNIT.IsAlive}(), @{#UNIT.IsActive}() methods determines if the DCS Unit is alive, meaning, it is existing and active. --- --- ## Test for proximity --- --- The UNIT class contains methods to test the location or proximity against zones or other objects. --- --- ### Zones --- --- To test whether the Unit is within a **zone**, use the @{#UNIT.IsInZone}() or the @{#UNIT.IsNotInZone}() methods. Any zone can be tested on, but the zone must be derived from @{Zone#ZONE_BASE}. --- --- ### Units --- --- Test if another DCS Unit is within a given radius of the current DCS Unit, use the @{#UNIT.OtherUnitInRadius}() method. --- --- @field #UNIT UNIT -UNIT = { - ClassName="UNIT", -} - - ---- Unit.SensorType --- @type Unit.SensorType --- @field OPTIC --- @field RADAR --- @field IRST --- @field RWR - - --- Registration. - ---- Create a new UNIT from DCSUnit. --- @param #UNIT self --- @param #string UnitName The name of the DCS unit. --- @return #UNIT -function UNIT:Register( UnitName ) - local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) ) - self.UnitName = UnitName - - self:SetEventPriority( 3 ) - return self -end - --- Reference methods. - ---- Finds a UNIT from the _DATABASE using a DCSUnit object. --- @param #UNIT self --- @param Dcs.DCSWrapper.Unit#Unit DCSUnit An existing DCS Unit object reference. --- @return #UNIT self -function UNIT:Find( DCSUnit ) - - local UnitName = DCSUnit:getName() - local UnitFound = _DATABASE:FindUnit( UnitName ) - return UnitFound -end - ---- Find a UNIT in the _DATABASE using the name of an existing DCS Unit. --- @param #UNIT self --- @param #string UnitName The Unit Name. --- @return #UNIT self -function UNIT:FindByName( UnitName ) - - local UnitFound = _DATABASE:FindUnit( UnitName ) - return UnitFound -end - ---- Return the name of the UNIT. --- @param #UNIT self --- @return #string The UNIT name. -function UNIT:Name() - - return self.UnitName -end - - ---- @param #UNIT self --- @return Dcs.DCSWrapper.Unit#Unit -function UNIT:GetDCSObject() - - local DCSUnit = Unit.getByName( self.UnitName ) - - if DCSUnit then - return DCSUnit - end - - return nil -end - ---- Respawn the @{Unit} using a (tweaked) template of the parent Group. --- --- This function will: --- --- * Get the current position and heading of the group. --- * When the unit is alive, it will tweak the template x, y and heading coordinates of the group and the embedded units to the current units positions. --- * Then it will respawn the re-modelled group. --- --- @param #UNIT self --- @param Dcs.DCSTypes#Vec3 SpawnVec3 The position where to Spawn the new Unit at. --- @param #number Heading The heading of the unit respawn. -function UNIT:ReSpawn( SpawnVec3, Heading ) - - local SpawnGroupTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplateFromUnitName( self:Name() ) ) - self:T( SpawnGroupTemplate ) - - local SpawnGroup = self:GetGroup() - - if SpawnGroup then - - local Vec3 = SpawnGroup:GetVec3() - SpawnGroupTemplate.x = SpawnVec3.x - SpawnGroupTemplate.y = SpawnVec3.z - - self:E( #SpawnGroupTemplate.units ) - for UnitID, UnitData in pairs( SpawnGroup:GetUnits() ) do - local GroupUnit = UnitData -- #UNIT - self:E( GroupUnit:GetName() ) - if GroupUnit:IsAlive() then - local GroupUnitVec3 = GroupUnit:GetVec3() - local GroupUnitHeading = GroupUnit:GetHeading() - SpawnGroupTemplate.units[UnitID].alt = GroupUnitVec3.y - SpawnGroupTemplate.units[UnitID].x = GroupUnitVec3.x - SpawnGroupTemplate.units[UnitID].y = GroupUnitVec3.z - SpawnGroupTemplate.units[UnitID].heading = GroupUnitHeading - self:E( { UnitID, SpawnGroupTemplate.units[UnitID], SpawnGroupTemplate.units[UnitID] } ) - end - end - end - - for UnitTemplateID, UnitTemplateData in pairs( SpawnGroupTemplate.units ) do - self:T( UnitTemplateData.name ) - if UnitTemplateData.name == self:Name() then - self:T("Adjusting") - SpawnGroupTemplate.units[UnitTemplateID].alt = SpawnVec3.y - SpawnGroupTemplate.units[UnitTemplateID].x = SpawnVec3.x - SpawnGroupTemplate.units[UnitTemplateID].y = SpawnVec3.z - SpawnGroupTemplate.units[UnitTemplateID].heading = Heading - self:E( { UnitTemplateID, SpawnGroupTemplate.units[UnitTemplateID], SpawnGroupTemplate.units[UnitTemplateID] } ) - else - self:E( SpawnGroupTemplate.units[UnitTemplateID].name ) - local GroupUnit = UNIT:FindByName( SpawnGroupTemplate.units[UnitTemplateID].name ) -- #UNIT - if GroupUnit and GroupUnit:IsAlive() then - local GroupUnitVec3 = GroupUnit:GetVec3() - local GroupUnitHeading = GroupUnit:GetHeading() - UnitTemplateData.alt = GroupUnitVec3.y - UnitTemplateData.x = GroupUnitVec3.x - UnitTemplateData.y = GroupUnitVec3.z - UnitTemplateData.heading = GroupUnitHeading - else - if SpawnGroupTemplate.units[UnitTemplateID].name ~= self:Name() then - self:T("nilling") - SpawnGroupTemplate.units[UnitTemplateID].delete = true - end - end - end - end - - -- Remove obscolete units from the group structure - local i = 1 - while i <= #SpawnGroupTemplate.units do - - local UnitTemplateData = SpawnGroupTemplate.units[i] - self:T( UnitTemplateData.name ) - - if UnitTemplateData.delete then - table.remove( SpawnGroupTemplate.units, i ) - else - i = i + 1 - end - end - - _DATABASE:Spawn( SpawnGroupTemplate ) -end - - - ---- Returns if the unit is activated. --- @param #UNIT self --- @return #boolean true if Unit is activated. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:IsActive() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - - local UnitIsActive = DCSUnit:isActive() - return UnitIsActive - end - - return nil -end - ---- Returns if the Unit is alive. --- If the Unit is not alive, nil is returned. --- If the Unit is alive and active, true is returned. --- If the Unit is alive but not active, false is returned. --- @param #UNIT self --- @return #boolean true if Unit is alive and active. --- @return #boolean false if Unit is alive but not active. --- @return #nil if the Unit is not existing or is not alive. -function UNIT:IsAlive() - self:F3( self.UnitName ) - - local DCSUnit = self:GetDCSObject() -- Dcs.DCSUnit#Unit - - if DCSUnit then - local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() - return UnitIsAlive - end - - return nil -end - - - ---- Returns the Unit's callsign - the localized string. --- @param #UNIT self --- @return #string The Callsign of the Unit. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetCallsign() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitCallSign = DCSUnit:getCallsign() - return UnitCallSign - end - - self:E( self.ClassName .. " " .. self.UnitName .. " not found!" ) - return nil -end - - ---- Returns name of the player that control the unit or nil if the unit is controlled by A.I. --- @param #UNIT self --- @return #string Player Name --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetPlayerName() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - - local PlayerName = DCSUnit:getPlayerName() - if PlayerName == nil then - PlayerName = "" - end - return PlayerName - end - - return nil -end - ---- Returns the unit's number in the group. --- The number is the same number the unit has in ME. --- It may not be changed during the mission. --- If any unit in the group is destroyed, the numbers of another units will not be changed. --- @param #UNIT self --- @return #number The Unit number. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetNumber() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitNumber = DCSUnit:getNumber() - return UnitNumber - end - - return nil -end - ---- Returns the unit's group if it exist and nil otherwise. --- @param Wrapper.Unit#UNIT self --- @return Wrapper.Group#GROUP The Group of the Unit. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetGroup() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitGroup = GROUP:Find( DCSUnit:getGroup() ) - return UnitGroup - end - - return nil -end - - --- Need to add here functions to check if radar is on and which object etc. - ---- Returns the prefix name of the DCS Unit. A prefix name is a part of the name before a '#'-sign. --- DCS Units spawned with the @{SPAWN} class contain a '#'-sign to indicate the end of the (base) DCS Unit name. --- The spawn sequence number and unit number are contained within the name after the '#' sign. --- @param #UNIT self --- @return #string The name of the DCS Unit. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetPrefix() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 ) - self:T3( UnitPrefix ) - return UnitPrefix - end - - return nil -end - ---- Returns the Unit's ammunition. --- @param #UNIT self --- @return Dcs.DCSWrapper.Unit#Unit.Ammo --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetAmmo() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitAmmo = DCSUnit:getAmmo() - return UnitAmmo - end - - return nil -end - ---- Returns the unit sensors. --- @param #UNIT self --- @return Dcs.DCSWrapper.Unit#Unit.Sensors --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetSensors() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitSensors = DCSUnit:getSensors() - return UnitSensors - end - - return nil -end - --- Need to add here a function per sensortype --- unit:hasSensors(Unit.SensorType.RADAR, Unit.RadarType.AS) - ---- Returns if the unit has sensors of a certain type. --- @param #UNIT self --- @return #boolean returns true if the unit has specified types of sensors. This function is more preferable than Unit.getSensors() if you don't want to get information about all the unit's sensors, and just want to check if the unit has specified types of sensors. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:HasSensors( ... ) - self:F2( arg ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local HasSensors = DCSUnit:hasSensors( unpack( arg ) ) - return HasSensors - end - - return nil -end - ---- Returns if the unit is SEADable. --- @param #UNIT self --- @return #boolean returns true if the unit is SEADable. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:HasSEAD() - self:F2() - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitSEADAttributes = DCSUnit:getDesc().attributes - - local HasSEAD = false - if UnitSEADAttributes["RADAR_BAND1_FOR_ARM"] and UnitSEADAttributes["RADAR_BAND1_FOR_ARM"] == true or - UnitSEADAttributes["RADAR_BAND2_FOR_ARM"] and UnitSEADAttributes["RADAR_BAND2_FOR_ARM"] == true then - HasSEAD = true - end - return HasSEAD - end - - return nil -end - ---- Returns two values: --- --- * First value indicates if at least one of the unit's radar(s) is on. --- * Second value is the object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target. --- @param #UNIT self --- @return #boolean Indicates if at least one of the unit's radar(s) is on. --- @return Dcs.DCSWrapper.Object#Object The object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetRadar() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitRadarOn, UnitRadarObject = DCSUnit:getRadar() - return UnitRadarOn, UnitRadarObject - end - - return nil, nil -end - ---- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. --- @param #UNIT self --- @return #number The relative amount of fuel (from 0.0 to 1.0). --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetFuel() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitFuel = DCSUnit:getFuel() - return UnitFuel - end - - return nil -end - ---- Returns the UNIT in a UNIT list of one element. --- @param #UNIT self --- @return #list The UNITs wrappers. -function UNIT:GetUnits() - self:F2( { self.UnitName } ) - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local DCSUnits = DCSUnit:getUnits() - local Units = {} - Units[1] = UNIT:Find( DCSUnit ) - self:T3( Units ) - return Units - end - - return nil -end - - ---- Returns the unit's health. Dead units has health <= 1.0. --- @param #UNIT self --- @return #number The Unit's health value. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetLife() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitLife = DCSUnit:getLife() - return UnitLife - end - - return nil -end - ---- Returns the Unit's initial health. --- @param #UNIT self --- @return #number The Unit's initial health value. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetLife0() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitLife0 = DCSUnit:getLife0() - return UnitLife0 - end - - return nil -end - ---- Returns the category name of the #UNIT. --- @param #UNIT self --- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship -function UNIT:GetCategoryName() - self:F3( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - if DCSUnit then - local CategoryNames = { - [Unit.Category.AIRPLANE] = "Airplane", - [Unit.Category.HELICOPTER] = "Helicopter", - [Unit.Category.GROUND_UNIT] = "Ground Unit", - [Unit.Category.SHIP] = "Ship", - [Unit.Category.STRUCTURE] = "Structure", - } - local UnitCategory = DCSUnit:getDesc().category - self:T3( UnitCategory ) - - return CategoryNames[UnitCategory] - end - - return nil -end - - ---- Returns the Unit's A2G threat level on a scale from 1 to 10 ... --- The following threat levels are foreseen: --- --- * Threat level 0: Unit is unarmed. --- * Threat level 1: Unit is infantry. --- * Threat level 2: Unit is an infantry vehicle. --- * Threat level 3: Unit is ground artillery. --- * Threat level 4: Unit is a tank. --- * Threat level 5: Unit is a modern tank or ifv with ATGM. --- * Threat level 6: Unit is a AAA. --- * Threat level 7: Unit is a SAM or manpad, IR guided. --- * Threat level 8: Unit is a Short Range SAM, radar guided. --- * Threat level 9: Unit is a Medium Range SAM, radar guided. --- * Threat level 10: Unit is a Long Range SAM, radar guided. --- @param #UNIT self -function UNIT:GetThreatLevel() - - local Attributes = self:GetDesc().attributes - self:T( Attributes ) - - local ThreatLevel = 0 - local ThreatText = "" - - if self:IsGround() then - - self:T( "Ground" ) - - local ThreatLevels = { - "Unarmed", - "Infantry", - "Old Tanks & APCs", - "Tanks & IFVs without ATGM", - "Tanks & IFV with ATGM", - "Modern Tanks", - "AAA", - "IR Guided SAMs", - "SR SAMs", - "MR SAMs", - "LR SAMs" - } - - - if Attributes["LR SAM"] then ThreatLevel = 10 - elseif Attributes["MR SAM"] then ThreatLevel = 9 - elseif Attributes["SR SAM"] and - not Attributes["IR Guided SAM"] then ThreatLevel = 8 - elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and - Attributes["IR Guided SAM"] then ThreatLevel = 7 - elseif Attributes["AAA"] then ThreatLevel = 6 - elseif Attributes["Modern Tanks"] then ThreatLevel = 5 - elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and - Attributes["ATGM"] then ThreatLevel = 4 - elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and - not Attributes["ATGM"] then ThreatLevel = 3 - elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2 - elseif Attributes["Infantry"] then ThreatLevel = 1 - end - - ThreatText = ThreatLevels[ThreatLevel+1] - end - - if self:IsAir() then - - self:T( "Air" ) - - local ThreatLevels = { - "Unarmed", - "Tanker", - "AWACS", - "Transport Helicpter", - "UAV", - "Bomber", - "Strategic Bomber", - "Attack Helicopter", - "Interceptor", - "Multirole Fighter", - "Fighter" - } - - - if Attributes["Fighters"] then ThreatLevel = 10 - elseif Attributes["Multirole fighters"] then ThreatLevel = 9 - elseif Attributes["Battleplanes"] then ThreatLevel = 8 - elseif Attributes["Attack helicopters"] then ThreatLevel = 7 - elseif Attributes["Strategic bombers"] then ThreatLevel = 6 - elseif Attributes["Bombers"] then ThreatLevel = 5 - elseif Attributes["UAVs"] then ThreatLevel = 4 - elseif Attributes["Transport helicopters"] then ThreatLevel = 3 - elseif Attributes["AWACS"] then ThreatLevel = 2 - elseif Attributes["Tankers"] then ThreatLevel = 1 - end - - ThreatText = ThreatLevels[ThreatLevel+1] - end - - if self:IsShip() then - - self:T( "Ship" ) - ---["Aircraft Carriers"] = {"Heavy armed ships",}, ---["Cruisers"] = {"Heavy armed ships",}, ---["Destroyers"] = {"Heavy armed ships",}, ---["Frigates"] = {"Heavy armed ships",}, ---["Corvettes"] = {"Heavy armed ships",}, ---["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",}, ---["Light armed ships"] = {"Armed ships","NonArmoredUnits"}, ---["Armed ships"] = {"Ships"}, ---["Unarmed ships"] = {"Ships","HeavyArmoredUnits",}, - - local ThreatLevels = { - "Unarmed ship", - "Light armed ships", - "Corvettes", - "", - "Frigates", - "", - "Cruiser", - "", - "Destroyer", - "", - "Aircraft Carrier" - } - - - if Attributes["Aircraft Carriers"] then ThreatLevel = 10 - elseif Attributes["Destroyers"] then ThreatLevel = 8 - elseif Attributes["Cruisers"] then ThreatLevel = 6 - elseif Attributes["Frigates"] then ThreatLevel = 4 - elseif Attributes["Corvettes"] then ThreatLevel = 2 - elseif Attributes["Light armed ships"] then ThreatLevel = 1 - end - - ThreatText = ThreatLevels[ThreatLevel+1] - end - - self:T2( ThreatLevel ) - return ThreatLevel, ThreatText - -end - - --- Is functions - ---- Returns true if the unit is within a @{Zone}. --- @param #UNIT self --- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the unit is within the @{Zone#ZONE_BASE} -function UNIT:IsInZone( Zone ) - self:F2( { self.UnitName, Zone } ) - - if self:IsAlive() then - local IsInZone = Zone:IsVec3InZone( self:GetVec3() ) - - self:T( { IsInZone } ) - return IsInZone - end - - return false -end - ---- Returns true if the unit is not within a @{Zone}. --- @param #UNIT self --- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the unit is not within the @{Zone#ZONE_BASE} -function UNIT:IsNotInZone( Zone ) - self:F2( { self.UnitName, Zone } ) - - if self:IsAlive() then - local IsInZone = not Zone:IsVec3InZone( self:GetVec3() ) - - self:T( { IsInZone } ) - return IsInZone - else - return false - end -end - - ---- Returns true if there is an **other** DCS Unit within a radius of the current 2D point of the DCS Unit. --- @param #UNIT self --- @param #UNIT AwaitUnit The other UNIT wrapper object. --- @param Radius The radius in meters with the DCS Unit in the centre. --- @return true If the other DCS Unit is within the radius of the 2D point of the DCS Unit. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:OtherUnitInRadius( AwaitUnit, Radius ) - self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitVec3 = self:GetVec3() - local AwaitUnitVec3 = AwaitUnit:GetVec3() - - if (((UnitVec3.x - AwaitUnitVec3.x)^2 + (UnitVec3.z - AwaitUnitVec3.z)^2)^0.5 <= Radius) then - self:T3( "true" ) - return true - else - self:T3( "false" ) - return false - end - end - - return nil -end - - - ---- Signal a flare at the position of the UNIT. --- @param #UNIT self --- @param Utilities.Utils#FLARECOLOR FlareColor -function UNIT:Flare( FlareColor ) - self:F2() - trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 ) -end - ---- Signal a white flare at the position of the UNIT. --- @param #UNIT self -function UNIT:FlareWhite() - self:F2() - trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 ) -end - ---- Signal a yellow flare at the position of the UNIT. --- @param #UNIT self -function UNIT:FlareYellow() - self:F2() - trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 ) -end - ---- Signal a green flare at the position of the UNIT. --- @param #UNIT self -function UNIT:FlareGreen() - self:F2() - trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 ) -end - ---- Signal a red flare at the position of the UNIT. --- @param #UNIT self -function UNIT:FlareRed() - self:F2() - local Vec3 = self:GetVec3() - if Vec3 then - trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 ) - end -end - ---- Smoke the UNIT. --- @param #UNIT self -function UNIT:Smoke( SmokeColor, Range ) - self:F2() - if Range then - trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor ) - else - trigger.action.smoke( self:GetVec3(), SmokeColor ) - end - -end - ---- Smoke the UNIT Green. --- @param #UNIT self -function UNIT:SmokeGreen() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green ) -end - ---- Smoke the UNIT Red. --- @param #UNIT self -function UNIT:SmokeRed() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red ) -end - ---- Smoke the UNIT White. --- @param #UNIT self -function UNIT:SmokeWhite() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White ) -end - ---- Smoke the UNIT Orange. --- @param #UNIT self -function UNIT:SmokeOrange() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange ) -end - ---- Smoke the UNIT Blue. --- @param #UNIT self -function UNIT:SmokeBlue() - self:F2() - trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue ) -end - --- Is methods - ---- Returns if the unit is of an air category. --- If the unit is a helicopter or a plane, then this method will return true, otherwise false. --- @param #UNIT self --- @return #boolean Air category evaluation result. -function UNIT:IsAir() - self:F2() - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitDescriptor = DCSUnit:getDesc() - self:T3( { UnitDescriptor.category, Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) - - local IsAirResult = ( UnitDescriptor.category == Unit.Category.AIRPLANE ) or ( UnitDescriptor.category == Unit.Category.HELICOPTER ) - - self:T3( IsAirResult ) - return IsAirResult - end - - return nil -end - ---- Returns if the unit is of an ground category. --- If the unit is a ground vehicle or infantry, this method will return true, otherwise false. --- @param #UNIT self --- @return #boolean Ground category evaluation result. -function UNIT:IsGround() - self:F2() - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitDescriptor = DCSUnit:getDesc() - self:T3( { UnitDescriptor.category, Unit.Category.GROUND_UNIT } ) - - local IsGroundResult = ( UnitDescriptor.category == Unit.Category.GROUND_UNIT ) - - self:T3( IsGroundResult ) - return IsGroundResult - end - - return nil -end - ---- Returns if the unit is a friendly unit. --- @param #UNIT self --- @return #boolean IsFriendly evaluation result. -function UNIT:IsFriendly( FriendlyCoalition ) - self:F2() - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitCoalition = DCSUnit:getCoalition() - self:T3( { UnitCoalition, FriendlyCoalition } ) - - local IsFriendlyResult = ( UnitCoalition == FriendlyCoalition ) - - self:E( IsFriendlyResult ) - return IsFriendlyResult - end - - return nil -end - ---- Returns if the unit is of a ship category. --- If the unit is a ship, this method will return true, otherwise false. --- @param #UNIT self --- @return #boolean Ship category evaluation result. -function UNIT:IsShip() - self:F2() - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitDescriptor = DCSUnit:getDesc() - self:T3( { UnitDescriptor.category, Unit.Category.SHIP } ) - - local IsShipResult = ( UnitDescriptor.category == Unit.Category.SHIP ) - - self:T3( IsShipResult ) - return IsShipResult - end - - return nil -end - ---- Returns true if the UNIT is in the air. --- @param Wrapper.Positionable#UNIT self --- @return #boolean true if in the air. --- @return #nil The UNIT is not existing or alive. -function UNIT:InAir() - self:F2( self.UnitName ) - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitInAir = DCSUnit:inAir() - self:T3( UnitInAir ) - return UnitInAir - end - - return nil -end - -do -- Event Handling - - --- Subscribe to a DCS Event. - -- @param #UNIT self - -- @param Core.Event#EVENTS Event - -- @param #function EventFunction (optional) The function to be called when the event occurs for the unit. - -- @return #UNIT - function UNIT:HandleEvent( Event, EventFunction ) - - self:EventDispatcher():OnEventForUnit( self:GetName(), EventFunction, self, Event ) - - return self - end - - --- UnSubscribe to a DCS event. - -- @param #UNIT self - -- @param Core.Event#EVENTS Event - -- @return #UNIT - function UNIT:UnHandleEvent( Event ) - - self:EventDispatcher():RemoveForUnit( self:GetName(), self, Event ) - - return self - end - -end--- This module contains the CLIENT class. --- --- 1) @{Client#CLIENT} class, extends @{Unit#UNIT} --- =============================================== --- Clients are those **Units** defined within the Mission Editor that have the skillset defined as __Client__ or __Player__. --- Note that clients are NOT the same as Units, they are NOT necessarily alive. --- The @{Client#CLIENT} class is a wrapper class to handle the DCS Unit objects that have the skillset defined as __Client__ or __Player__: --- --- * Wraps the DCS Unit objects with skill level set to Player or Client. --- * Support all DCS Unit APIs. --- * Enhance with Unit specific APIs not in the DCS Group API set. --- * When player joins Unit, execute alive init logic. --- * Handles messages to players. --- * Manage the "state" of the DCS Unit. --- --- Clients are being used by the @{MISSION} class to follow players and register their successes. --- --- 1.1) CLIENT reference methods --- ----------------------------- --- For each DCS Unit having skill level Player or Client, a CLIENT wrapper object (instance) will be created within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts). --- --- The CLIENT class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference --- using the DCS Unit or the DCS UnitName. --- --- Another thing to know is that CLIENT objects do not "contain" the DCS Unit object. --- The CLIENT methods will reference the DCS Unit object by name when it is needed during API execution. --- If the DCS Unit object does not exist or is nil, the CLIENT methods will return nil and log an exception in the DCS.log file. --- --- The CLIENT class provides the following functions to retrieve quickly the relevant CLIENT instance: --- --- * @{#CLIENT.Find}(): Find a CLIENT instance from the _DATABASE object using a DCS Unit object. --- * @{#CLIENT.FindByName}(): Find a CLIENT instance from the _DATABASE object using a DCS Unit name. --- --- IMPORTANT: ONE SHOULD NEVER SANATIZE these CLIENT OBJECT REFERENCES! (make the CLIENT object references nil). --- --- @module Client - ---- The CLIENT class --- @type CLIENT --- @extends Wrapper.Unit#UNIT -CLIENT = { - ONBOARDSIDE = { - NONE = 0, - LEFT = 1, - RIGHT = 2, - BACK = 3, - FRONT = 4 - }, - ClassName = "CLIENT", - ClientName = nil, - ClientAlive = false, - ClientTransport = false, - ClientBriefingShown = false, - _Menus = {}, - _Tasks = {}, - Messages = { - } -} - - ---- Finds a CLIENT from the _DATABASE using the relevant DCS Unit. --- @param #CLIENT self --- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor. --- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client. --- @return #CLIENT --- @usage --- -- Create new Clients. --- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' ) --- Mission:AddGoal( DeploySA6TroopsGoal ) --- --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() ) -function CLIENT:Find( DCSUnit, Error ) - local ClientName = DCSUnit:getName() - local ClientFound = _DATABASE:FindClient( ClientName ) - - if ClientFound then - ClientFound:F( ClientName ) - return ClientFound - end - - if not Error then - error( "CLIENT not found for: " .. ClientName ) - end -end - - ---- Finds a CLIENT from the _DATABASE using the relevant Client Unit Name. --- As an optional parameter, a briefing text can be given also. --- @param #CLIENT self --- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor. --- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client. --- @param #boolean Error A flag that indicates whether an error should be raised if the CLIENT cannot be found. By default an error will be raised. --- @return #CLIENT --- @usage --- -- Create new Clients. --- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' ) --- Mission:AddGoal( DeploySA6TroopsGoal ) --- --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() ) --- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() ) -function CLIENT:FindByName( ClientName, ClientBriefing, Error ) - local ClientFound = _DATABASE:FindClient( ClientName ) - - if ClientFound then - ClientFound:F( { ClientName, ClientBriefing } ) - ClientFound:AddBriefing( ClientBriefing ) - ClientFound.MessageSwitch = true - - return ClientFound - end - - if not Error then - error( "CLIENT not found for: " .. ClientName ) - end -end - -function CLIENT:Register( ClientName ) - local self = BASE:Inherit( self, UNIT:Register( ClientName ) ) - - self:F( ClientName ) - self.ClientName = ClientName - self.MessageSwitch = true - self.ClientAlive2 = false - - --self.AliveCheckScheduler = routines.scheduleFunction( self._AliveCheckScheduler, { self }, timer.getTime() + 1, 5 ) - self.AliveCheckScheduler = SCHEDULER:New( self, self._AliveCheckScheduler, { "Client Alive " .. ClientName }, 1, 5 ) - - self:E( self ) - return self -end - - ---- Transport defines that the Client is a Transport. Transports show cargo. --- @param #CLIENT self --- @return #CLIENT -function CLIENT:Transport() - self:F() - - self.ClientTransport = true - return self -end - ---- AddBriefing adds a briefing to a CLIENT when a player joins a mission. --- @param #CLIENT self --- @param #string ClientBriefing is the text defining the Mission briefing. --- @return #CLIENT self -function CLIENT:AddBriefing( ClientBriefing ) - self:F( ClientBriefing ) - self.ClientBriefing = ClientBriefing - self.ClientBriefingShown = false - - return self -end - ---- Show the briefing of a CLIENT. --- @param #CLIENT self --- @return #CLIENT self -function CLIENT:ShowBriefing() - self:F( { self.ClientName, self.ClientBriefingShown } ) - - if not self.ClientBriefingShown then - self.ClientBriefingShown = true - local Briefing = "" - if self.ClientBriefing then - Briefing = Briefing .. self.ClientBriefing - end - Briefing = Briefing .. " Press [LEFT ALT]+[B] to view the complete mission briefing." - self:Message( Briefing, 60, "Briefing" ) - end - - return self -end - ---- Show the mission briefing of a MISSION to the CLIENT. --- @param #CLIENT self --- @param #string MissionBriefing --- @return #CLIENT self -function CLIENT:ShowMissionBriefing( MissionBriefing ) - self:F( { self.ClientName } ) - - if MissionBriefing then - self:Message( MissionBriefing, 60, "Mission Briefing" ) - end - - return self -end - - - ---- Resets a CLIENT. --- @param #CLIENT self --- @param #string ClientName Name of the Group as defined within the Mission Editor. The Group must have a Unit with the type Client. -function CLIENT:Reset( ClientName ) - self:F() - self._Menus = {} -end - --- Is Functions - ---- Checks if the CLIENT is a multi-seated UNIT. --- @param #CLIENT self --- @return #boolean true if multi-seated. -function CLIENT:IsMultiSeated() - self:F( self.ClientName ) - - local ClientMultiSeatedTypes = { - ["Mi-8MT"] = "Mi-8MT", - ["UH-1H"] = "UH-1H", - ["P-51B"] = "P-51B" - } - - if self:IsAlive() then - local ClientTypeName = self:GetClientGroupUnit():GetTypeName() - if ClientMultiSeatedTypes[ClientTypeName] then - return true - end - end - - return false -end - ---- Checks for a client alive event and calls a function on a continuous basis. --- @param #CLIENT self --- @param #function CallBackFunction Create a function that will be called when a player joins the slot. --- @return #CLIENT -function CLIENT:Alive( CallBackFunction, ... ) - self:F() - - self.ClientCallBack = CallBackFunction - self.ClientParameters = arg - - return self -end - ---- @param #CLIENT self -function CLIENT:_AliveCheckScheduler( SchedulerName ) - self:F3( { SchedulerName, self.ClientName, self.ClientAlive2, self.ClientBriefingShown, self.ClientCallBack } ) - - if self:IsAlive() then - if self.ClientAlive2 == false then - self:ShowBriefing() - if self.ClientCallBack then - self:T("Calling Callback function") - self.ClientCallBack( self, unpack( self.ClientParameters ) ) - end - self.ClientAlive2 = true - end - else - if self.ClientAlive2 == true then - self.ClientAlive2 = false - end - end - - return true -end - ---- Return the DCSGroup of a Client. --- This function is modified to deal with a couple of bugs in DCS 1.5.3 --- @param #CLIENT self --- @return Dcs.DCSWrapper.Group#Group -function CLIENT:GetDCSGroup() - self:F3() - --- local ClientData = Group.getByName( self.ClientName ) --- if ClientData and ClientData:isExist() then --- self:T( self.ClientName .. " : group found!" ) --- return ClientData --- else --- return nil --- end - - local ClientUnit = Unit.getByName( self.ClientName ) - - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } - for CoalitionId, CoalitionData in pairs( CoalitionsData ) do - self:T3( { "CoalitionData:", CoalitionData } ) - for UnitId, UnitData in pairs( CoalitionData ) do - self:T3( { "UnitData:", UnitData } ) - if UnitData and UnitData:isExist() then - - --self:E(self.ClientName) - if ClientUnit then - local ClientGroup = ClientUnit:getGroup() - if ClientGroup then - self:T3( "ClientGroup = " .. self.ClientName ) - if ClientGroup:isExist() and UnitData:getGroup():isExist() then - if ClientGroup:getID() == UnitData:getGroup():getID() then - self:T3( "Normal logic" ) - self:T3( self.ClientName .. " : group found!" ) - self.ClientGroupID = ClientGroup:getID() - self.ClientGroupName = ClientGroup:getName() - return ClientGroup - end - else - -- Now we need to resolve the bugs in DCS 1.5 ... - -- Consult the database for the units of the Client Group. (ClientGroup:getUnits() returns nil) - self:T3( "Bug 1.5 logic" ) - local ClientGroupTemplate = _DATABASE.Templates.Units[self.ClientName].GroupTemplate - self.ClientGroupID = ClientGroupTemplate.groupId - self.ClientGroupName = _DATABASE.Templates.Units[self.ClientName].GroupName - self:T3( self.ClientName .. " : group found in bug 1.5 resolvement logic!" ) - return ClientGroup - end - -- else - -- error( "Client " .. self.ClientName .. " not found!" ) - end - else - --self:E( { "Client not found!", self.ClientName } ) - end - end - end - end - - -- For non player clients - if ClientUnit then - local ClientGroup = ClientUnit:getGroup() - if ClientGroup then - self:T3( "ClientGroup = " .. self.ClientName ) - if ClientGroup:isExist() then - self:T3( "Normal logic" ) - self:T3( self.ClientName .. " : group found!" ) - return ClientGroup - end - end - end - - self.ClientGroupID = nil - self.ClientGroupUnit = nil - - return nil -end - - --- TODO: Check Dcs.DCSTypes#Group.ID ---- Get the group ID of the client. --- @param #CLIENT self --- @return Dcs.DCSTypes#Group.ID -function CLIENT:GetClientGroupID() - - local ClientGroup = self:GetDCSGroup() - - --self:E( self.ClientGroupID ) -- Determined in GetDCSGroup() - return self.ClientGroupID -end - - ---- Get the name of the group of the client. --- @param #CLIENT self --- @return #string -function CLIENT:GetClientGroupName() - - local ClientGroup = self:GetDCSGroup() - - self:T( self.ClientGroupName ) -- Determined in GetDCSGroup() - return self.ClientGroupName -end - ---- Returns the UNIT of the CLIENT. --- @param #CLIENT self --- @return Wrapper.Unit#UNIT -function CLIENT:GetClientGroupUnit() - self:F2() - - local ClientDCSUnit = Unit.getByName( self.ClientName ) - - self:T( self.ClientDCSUnit ) - if ClientDCSUnit and ClientDCSUnit:isExist() then - local ClientUnit = _DATABASE:FindUnit( self.ClientName ) - self:T2( ClientUnit ) - return ClientUnit - end -end - ---- Returns the DCSUnit of the CLIENT. --- @param #CLIENT self --- @return Dcs.DCSTypes#Unit -function CLIENT:GetClientGroupDCSUnit() - self:F2() - - local ClientDCSUnit = Unit.getByName( self.ClientName ) - - if ClientDCSUnit and ClientDCSUnit:isExist() then - self:T2( ClientDCSUnit ) - return ClientDCSUnit - end -end - - ---- Evaluates if the CLIENT is a transport. --- @param #CLIENT self --- @return #boolean true is a transport. -function CLIENT:IsTransport() - self:F() - return self.ClientTransport -end - ---- Shows the @{AI_Cargo#CARGO} contained within the CLIENT to the player as a message. --- The @{AI_Cargo#CARGO} is shown using the @{Message#MESSAGE} distribution system. --- @param #CLIENT self -function CLIENT:ShowCargo() - self:F() - - local CargoMsg = "" - - for CargoName, Cargo in pairs( CARGOS ) do - if self == Cargo:IsLoadedInClient() then - CargoMsg = CargoMsg .. Cargo.CargoName .. " Type:" .. Cargo.CargoType .. " Weight: " .. Cargo.CargoWeight .. "\n" - end - end - - if CargoMsg == "" then - CargoMsg = "empty" - end - - self:Message( CargoMsg, 15, "Co-Pilot: Cargo Status", 30 ) - -end - --- TODO (1) I urgently need to revise this. ---- A local function called by the DCS World Menu system to switch off messages. -function CLIENT.SwitchMessages( PrmTable ) - PrmTable[1].MessageSwitch = PrmTable[2] -end - ---- The main message driver for the CLIENT. --- This function displays various messages to the Player logged into the CLIENT through the DCS World Messaging system. --- @param #CLIENT self --- @param #string Message is the text describing the message. --- @param #number MessageDuration is the duration in seconds that the Message should be displayed. --- @param #string MessageCategory is the category of the message (the title). --- @param #number MessageInterval is the interval in seconds between the display of the @{Message#MESSAGE} when the CLIENT is in the air. --- @param #string MessageID is the identifier of the message when displayed with intervals. -function CLIENT:Message( Message, MessageDuration, MessageCategory, MessageInterval, MessageID ) - self:F( { Message, MessageDuration, MessageCategory, MessageInterval } ) - - if self.MessageSwitch == true then - if MessageCategory == nil then - MessageCategory = "Messages" - end - if MessageID ~= nil then - if self.Messages[MessageID] == nil then - self.Messages[MessageID] = {} - self.Messages[MessageID].MessageId = MessageID - self.Messages[MessageID].MessageTime = timer.getTime() - self.Messages[MessageID].MessageDuration = MessageDuration - if MessageInterval == nil then - self.Messages[MessageID].MessageInterval = 600 - else - self.Messages[MessageID].MessageInterval = MessageInterval - end - MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self ) - else - if self:GetClientGroupDCSUnit() and not self:GetClientGroupDCSUnit():inAir() then - if timer.getTime() - self.Messages[MessageID].MessageTime >= self.Messages[MessageID].MessageDuration + 10 then - MESSAGE:New( Message, MessageDuration , MessageCategory):ToClient( self ) - self.Messages[MessageID].MessageTime = timer.getTime() - end - else - if timer.getTime() - self.Messages[MessageID].MessageTime >= self.Messages[MessageID].MessageDuration + self.Messages[MessageID].MessageInterval then - MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self ) - self.Messages[MessageID].MessageTime = timer.getTime() - end - end - end - else - MESSAGE:New( Message, MessageDuration, MessageCategory ):ToClient( self ) - end - end -end ---- This module contains the STATIC class. --- --- 1) @{Static#STATIC} class, extends @{Positionable#POSITIONABLE} --- =============================================================== --- Statics are **Static Units** defined within the Mission Editor. --- Note that Statics are almost the same as Units, but they don't have a controller. --- The @{Static#STATIC} class is a wrapper class to handle the DCS Static objects: --- --- * Wraps the DCS Static objects. --- * Support all DCS Static APIs. --- * Enhance with Static specific APIs not in the DCS API set. --- --- 1.1) STATIC reference methods --- ----------------------------- --- For each DCS Static will have a STATIC wrapper object (instance) within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts). --- --- The STATIC class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference --- using the Static Name. --- --- Another thing to know is that STATIC objects do not "contain" the DCS Static object. --- The STATIc methods will reference the DCS Static object by name when it is needed during API execution. --- If the DCS Static object does not exist or is nil, the STATIC methods will return nil and log an exception in the DCS.log file. --- --- The STATIc class provides the following functions to retrieve quickly the relevant STATIC instance: --- --- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name. --- --- IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil). --- --- @module Static --- @author FlightControl - - - - - - ---- The STATIC class --- @type STATIC --- @extends Wrapper.Positionable#POSITIONABLE -STATIC = { - ClassName = "STATIC", -} - - ---- Finds a STATIC from the _DATABASE using the relevant Static Name. --- As an optional parameter, a briefing text can be given also. --- @param #STATIC self --- @param #string StaticName Name of the DCS **Static** as defined within the Mission Editor. --- @param #boolean RaiseError Raise an error if not found. --- @return #STATIC -function STATIC:FindByName( StaticName, RaiseError ) - local StaticFound = _DATABASE:FindStatic( StaticName ) - - self.StaticName = StaticName - - if StaticFound then - StaticFound:F3( { StaticName } ) - - return StaticFound - end - - if RaiseError == nil or RaiseError == true then - error( "STATIC not found for: " .. StaticName ) - end - - return nil -end - -function STATIC:Register( StaticName ) - local self = BASE:Inherit( self, POSITIONABLE:New( StaticName ) ) - self.StaticName = StaticName - return self -end - - -function STATIC:GetDCSObject() - local DCSStatic = StaticObject.getByName( self.StaticName ) - - if DCSStatic then - return DCSStatic - end - - return nil -end - -function STATIC:GetThreatLevel() - - return 1, "Static" -end--- This module contains the AIRBASE classes. --- --- === --- --- 1) @{Airbase#AIRBASE} class, extends @{Positionable#POSITIONABLE} --- ================================================================= --- The @{AIRBASE} class is a wrapper class to handle the DCS Airbase objects: --- --- * Support all DCS Airbase APIs. --- * Enhance with Airbase specific APIs not in the DCS Airbase API set. --- --- --- 1.1) AIRBASE reference methods --- ------------------------------ --- For each DCS Airbase object alive within a running mission, a AIRBASE wrapper object (instance) will be created within the _@{DATABASE} object. --- This is done at the beginning of the mission (when the mission starts). --- --- The AIRBASE class **does not contain a :New()** method, rather it provides **:Find()** methods to retrieve the object reference --- using the DCS Airbase or the DCS AirbaseName. --- --- Another thing to know is that AIRBASE objects do not "contain" the DCS Airbase object. --- The AIRBASE methods will reference the DCS Airbase object by name when it is needed during API execution. --- If the DCS Airbase object does not exist or is nil, the AIRBASE methods will return nil and log an exception in the DCS.log file. --- --- The AIRBASE class provides the following functions to retrieve quickly the relevant AIRBASE instance: --- --- * @{#AIRBASE.Find}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase object. --- * @{#AIRBASE.FindByName}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase name. --- --- IMPORTANT: ONE SHOULD NEVER SANATIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil). --- --- 1.2) DCS AIRBASE APIs --- --------------------- --- The DCS Airbase APIs are used extensively within MOOSE. The AIRBASE class has for each DCS Airbase API a corresponding method. --- To be able to distinguish easily in your code the difference between a AIRBASE API call and a DCS Airbase API call, --- the first letter of the method is also capitalized. So, by example, the DCS Airbase method @{DCSWrapper.Airbase#Airbase.getName}() --- is implemented in the AIRBASE class as @{#AIRBASE.GetName}(). --- --- More functions will be added --- ---------------------------- --- During the MOOSE development, more functions will be added. --- --- @module Airbase --- @author FlightControl - - - - - ---- The AIRBASE class --- @type AIRBASE --- @extends Wrapper.Positionable#POSITIONABLE -AIRBASE = { - ClassName="AIRBASE", - CategoryName = { - [Airbase.Category.AIRDROME] = "Airdrome", - [Airbase.Category.HELIPAD] = "Helipad", - [Airbase.Category.SHIP] = "Ship", - }, - } - --- Registration. - ---- Create a new AIRBASE from DCSAirbase. --- @param #AIRBASE self --- @param #string AirbaseName The name of the airbase. --- @return Wrapper.Airbase#AIRBASE -function AIRBASE:Register( AirbaseName ) - - local self = BASE:Inherit( self, POSITIONABLE:New( AirbaseName ) ) - self.AirbaseName = AirbaseName - return self -end - --- Reference methods. - ---- Finds a AIRBASE from the _DATABASE using a DCSAirbase object. --- @param #AIRBASE self --- @param Dcs.DCSWrapper.Airbase#Airbase DCSAirbase An existing DCS Airbase object reference. --- @return Wrapper.Airbase#AIRBASE self -function AIRBASE:Find( DCSAirbase ) - - local AirbaseName = DCSAirbase:getName() - local AirbaseFound = _DATABASE:FindAirbase( AirbaseName ) - return AirbaseFound -end - ---- Find a AIRBASE in the _DATABASE using the name of an existing DCS Airbase. --- @param #AIRBASE self --- @param #string AirbaseName The Airbase Name. --- @return Wrapper.Airbase#AIRBASE self -function AIRBASE:FindByName( AirbaseName ) - - local AirbaseFound = _DATABASE:FindAirbase( AirbaseName ) - return AirbaseFound -end - -function AIRBASE:GetDCSObject() - local DCSAirbase = Airbase.getByName( self.AirbaseName ) - - if DCSAirbase then - return DCSAirbase - end - - return nil -end - - - ---- This module contains the SCENERY class. --- --- 1) @{Scenery#SCENERY} class, extends @{Positionable#POSITIONABLE} --- =============================================================== --- Scenery objects are defined on the map. --- The @{Scenery#SCENERY} class is a wrapper class to handle the DCS Scenery objects: --- --- * Wraps the DCS Scenery objects. --- * Support all DCS Scenery APIs. --- * Enhance with Scenery specific APIs not in the DCS API set. --- --- @module Scenery --- @author FlightControl - - - ---- The SCENERY class --- @type SCENERY --- @extends Wrapper.Positionable#POSITIONABLE -SCENERY = { - ClassName = "SCENERY", -} - - -function SCENERY:Register( SceneryName, SceneryObject ) - local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) ) - self.SceneryName = SceneryName - self.SceneryObject = SceneryObject - return self -end - -function SCENERY:GetDCSObject() - return self.SceneryObject -end - -function SCENERY:GetThreatLevel() - - return 0, "Scenery" -end ---- Single-Player:**Yes** / Multi-Player:**Yes** / Core:**Yes** -- **Administer the scoring of player achievements, --- and create a CSV file logging the scoring events for use at team or squadron websites.** --- --- ![Banner Image](..\Presentations\SCORING\Dia1.JPG) --- --- === --- --- The @{#SCORING} class administers the scoring of player achievements, --- and creates a CSV file logging the scoring events and results for use at team or squadron websites. --- --- SCORING automatically calculates the threat level of the objects hit and destroyed by players, --- which can be @{Unit}, @{Static) and @{Scenery} objects. --- --- Positive score points are granted when enemy or neutral targets are destroyed. --- Negative score points or penalties are given when a friendly target is hit or destroyed. --- This brings a lot of dynamism in the scoring, where players need to take care to inflict damage on the right target. --- By default, penalties weight heavier in the scoring, to ensure that players don't commit fratricide. --- The total score of the player is calculated by **adding the scores minus the penalties**. --- --- ![Banner Image](..\Presentations\SCORING\Dia4.JPG) --- --- The score value is calculated based on the **threat level of the player** and the **threat level of the target**. --- A calculated score takes the threat level of the target divided by a balanced threat level of the player unit. --- As such, if the threat level of the target is high, and the player threat level is low, a higher score will be given than --- if the threat level of the player would be high too. --- --- ![Banner Image](..\Presentations\SCORING\Dia5.JPG) --- --- When multiple players hit the same target, and finally succeed in destroying the target, then each player who contributed to the target --- destruction, will receive a score. This is important for targets that require significant damage before it can be destroyed, like --- ships or heavy planes. --- --- ![Banner Image](..\Presentations\SCORING\Dia13.JPG) --- --- Optionally, the score values can be **scaled** by a **scale**. Specific scales can be set for positive cores or negative penalties. --- The default range of the scores granted is a value between 0 and 10. The default range of penalties given is a value between 0 and 30. --- --- ![Banner Image](..\Presentations\SCORING\Dia7.JPG) --- --- **Additional scores** can be granted to **specific objects**, when the player(s) destroy these objects. --- --- ![Banner Image](..\Presentations\SCORING\Dia9.JPG) --- --- Various @{Zone}s can be defined for which scores are also granted when objects in that @{Zone} are destroyed. --- This is **specifically useful** to designate **scenery targets on the map** that will generate points when destroyed. --- --- With a small change in MissionScripting.lua, the scoring results can also be logged in a **CSV file**. --- These CSV files can be used to: --- --- * Upload scoring to a database or a BI tool to publish the scoring results to the player community. --- * Upload scoring in an (online) Excel like tool, using pivot tables and pivot charts to show mission results. --- * Share scoring amoung players after the mission to discuss mission results. --- --- Scores can be **reported**. **Menu options** are automatically added to **each player group** when a player joins a client slot or a CA unit. --- Use the radio menu F10 to consult the scores while running the mission. --- Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission. --- --- # 1) @{Scoring#SCORING} class, extends @{Base#BASE} --- --- ## 1.1) Set the destroy score or penalty scale --- --- Score scales can be set for scores granted when enemies or friendlies are destroyed. --- Use the method @{#SCORING.SetScaleDestroyScore}() to set the scale of enemy destroys (positive destroys). --- Use the method @{#SCORING.SetScaleDestroyPenalty}() to set the scale of friendly destroys (negative destroys). --- --- local Scoring = SCORING:New( "Scoring File" ) --- Scoring:SetScaleDestroyScore( 10 ) --- Scoring:SetScaleDestroyPenalty( 40 ) --- --- The above sets the scale for valid scores to 10. So scores will be given in a scale from 0 to 10. --- The penalties will be given in a scale from 0 to 40. --- --- ## 1.2) Define special targets that will give extra scores. --- --- Special targets can be set that will give extra scores to the players when these are destroyed. --- Use the methods @{#SCORING.AddUnitScore}() and @{#SCORING.RemoveUnitScore}() to specify a special additional score for a specific @{Unit}s. --- Use the methods @{#SCORING.AddStaticScore}() and @{#SCORING.RemoveStaticScore}() to specify a special additional score for a specific @{Static}s. --- Use the method @{#SCORING.SetGroupGroup}() to specify a special additional score for a specific @{Group}s. --- --- local Scoring = SCORING:New( "Scoring File" ) --- Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 ) --- Scoring:AddStaticScore( STATIC:FindByName( "Static #1" ), 100 ) --- --- The above grants an additional score of 200 points for Unit #001 and an additional 100 points of Static #1 if these are destroyed. --- Note that later in the mission, one can remove these scores set, for example, when the a goal achievement time limit is over. --- For example, this can be done as follows: --- --- Scoring:RemoveUnitScore( UNIT:FindByName( "Unit #001" ) ) --- --- ## 1.3) Define destruction zones that will give extra scores. --- --- Define zones of destruction. Any object destroyed within the zone of the given category will give extra points. --- Use the method @{#SCORING.AddZoneScore}() to add a @{Zone} for additional scoring. --- Use the method @{#SCORING.RemoveZoneScore}() to remove a @{Zone} for additional scoring. --- There are interesting variations that can be achieved with this functionality. For example, if the @{Zone} is a @{Zone#ZONE_UNIT}, --- then the zone is a moving zone, and anything destroyed within that @{Zone} will generate points. --- The other implementation could be to designate a scenery target (a building) in the mission editor surrounded by a @{Zone}, --- just large enough around that building. --- --- ## 1.4) Add extra Goal scores upon an event or a condition. --- --- A mission has goals and achievements. The scoring system provides an API to set additional scores when a goal or achievement event happens. --- Use the method @{#SCORING.AddGoalScore}() to add a score for a Player at any time in your mission. --- --- ## 1.5) Configure fratricide level. --- --- When a player commits too much damage to friendlies, his penalty score will reach a certain level. --- Use the method @{#SCORING.SetFratricide}() to define the level when a player gets kicked. --- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. --- --- ## 1.6) Penalty score when a player changes the coalition. --- --- When a player changes the coalition, he can receive a penalty score. --- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition. --- By default, the penalty for changing coalition is the default penalty scale. --- --- ## 1.8) Define output CSV files. --- --- The CSV file is given the name of the string given in the @{#SCORING.New}{} constructor, followed by the .csv extension. --- The file is incrementally saved in the **\\Saved Games\\DCS\\Logs** folder, and has a time stamp indicating each mission run. --- See the following example: --- --- local ScoringFirstMission = SCORING:New( "FirstMission" ) --- local ScoringSecondMission = SCORING:New( "SecondMission" ) --- --- The above documents that 2 Scoring objects are created, ScoringFirstMission and ScoringSecondMission. --- --- ## 1.9) Configure messages. --- --- When players hit or destroy targets, messages are sent. --- Various methods exist to configure: --- --- * Which messages are sent upon the event. --- * Which audience receives the message. --- --- ### 1.9.1) Configure the messages sent upon the event. --- --- Use the following methods to configure when to send messages. By default, all messages are sent. --- --- * @{#SCORING.SetMessagesHit}(): Configure to send messages after a target has been hit. --- * @{#SCORING.SetMessagesDestroy}(): Configure to send messages after a target has been destroyed. --- * @{#SCORING.SetMessagesAddon}(): Configure to send messages for additional score, after a target has been destroyed. --- * @{#SCORING.SetMessagesZone}(): Configure to send messages for additional score, after a target has been destroyed within a given zone. --- --- ### 1.9.2) Configure the audience of the messages. --- --- Use the following methods to configure the audience of the messages. By default, the messages are sent to all players in the mission. --- --- * @{#SCORING.SetMessagesToAll}(): Configure to send messages to all players. --- * @{#SCORING.SetMessagesToCoalition}(): Configure to send messages to only those players within the same coalition as the player. --- --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-02-26: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **Wingthor (TAW)**: Testing & Advice. --- * **Dutch-Baron (TAW)**: Testing & Advice. --- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing and Advice. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module Scoring - - ---- The Scoring class --- @type SCORING --- @field Players A collection of the current players that have joined the game. --- @extends Core.Base#BASE -SCORING = { - ClassName = "SCORING", - ClassID = 0, - Players = {}, -} - -local _SCORINGCoalition = - { - [1] = "Red", - [2] = "Blue", - } - -local _SCORINGCategory = - { - [Unit.Category.AIRPLANE] = "Plane", - [Unit.Category.HELICOPTER] = "Helicopter", - [Unit.Category.GROUND_UNIT] = "Vehicle", - [Unit.Category.SHIP] = "Ship", - [Unit.Category.STRUCTURE] = "Structure", - } - ---- Creates a new SCORING object to administer the scoring achieved by players. --- @param #SCORING self --- @param #string GameName The name of the game. This name is also logged in the CSV score file. --- @return #SCORING self --- @usage --- -- Define a new scoring object for the mission Gori Valley. --- ScoringObject = SCORING:New( "Gori Valley" ) -function SCORING:New( GameName ) - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) -- #SCORING - - if GameName then - self.GameName = GameName - else - error( "A game name must be given to register the scoring results" ) - end - - - -- Additional Object scores - self.ScoringObjects = {} - - -- Additional Zone scores. - self.ScoringZones = {} - - -- Configure Messages - self:SetMessagesToAll() - self:SetMessagesHit( true ) - self:SetMessagesDestroy( true ) - self:SetMessagesScore( true ) - self:SetMessagesZone( true ) - - -- Scales - self:SetScaleDestroyScore( 10 ) - self:SetScaleDestroyPenalty( 30 ) - - -- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked). - self:SetFratricide( self.ScaleDestroyPenalty * 3 ) - - -- Default penalty when a player changes coalition. - self:SetCoalitionChangePenalty( self.ScaleDestroyPenalty ) - - -- Event handlers - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Hit, self._EventOnHit ) - self:HandleEvent( EVENTS.PlayerEnterUnit ) - self:HandleEvent( EVENTS.PlayerLeaveUnit ) - - -- Create the CSV file. - self:OpenCSV( GameName ) - - return self - -end - ---- Set the scale for scoring valid destroys (enemy destroys). --- A default calculated score is a value between 1 and 10. --- The scale magnifies the scores given to the players. --- @param #SCORING self --- @param #number Scale The scale of the score given. -function SCORING:SetScaleDestroyScore( Scale ) - - self.ScaleDestroyScore = Scale - - return self -end - ---- Set the scale for scoring penalty destroys (friendly destroys). --- A default calculated penalty is a value between 1 and 10. --- The scale magnifies the scores given to the players. --- @param #SCORING self --- @param #number Scale The scale of the score given. --- @return #SCORING -function SCORING:SetScaleDestroyPenalty( Scale ) - - self.ScaleDestroyPenalty = Scale - - return self -end - ---- Add a @{Unit} for additional scoring when the @{Unit} is destroyed. --- Note that if there was already a @{Unit} declared within the scoring with the same name, --- then the old @{Unit} will be replaced with the new @{Unit}. --- @param #SCORING self --- @param Wrapper.Unit#UNIT ScoreUnit The @{Unit} for which the Score needs to be given. --- @param #number Score The Score value. --- @return #SCORING -function SCORING:AddUnitScore( ScoreUnit, Score ) - - local UnitName = ScoreUnit:GetName() - - self.ScoringObjects[UnitName] = Score - - return self -end - ---- Removes a @{Unit} for additional scoring when the @{Unit} is destroyed. --- @param #SCORING self --- @param Wrapper.Unit#UNIT ScoreUnit The @{Unit} for which the Score needs to be given. --- @return #SCORING -function SCORING:RemoveUnitScore( ScoreUnit ) - - local UnitName = ScoreUnit:GetName() - - self.ScoringObjects[UnitName] = nil - - return self -end - ---- Add a @{Static} for additional scoring when the @{Static} is destroyed. --- Note that if there was already a @{Static} declared within the scoring with the same name, --- then the old @{Static} will be replaced with the new @{Static}. --- @param #SCORING self --- @param Wrapper.Static#UNIT ScoreStatic The @{Static} for which the Score needs to be given. --- @param #number Score The Score value. --- @return #SCORING -function SCORING:AddStaticScore( ScoreStatic, Score ) - - local StaticName = ScoreStatic:GetName() - - self.ScoringObjects[StaticName] = Score - - return self -end - ---- Removes a @{Static} for additional scoring when the @{Static} is destroyed. --- @param #SCORING self --- @param Wrapper.Static#UNIT ScoreStatic The @{Static} for which the Score needs to be given. --- @return #SCORING -function SCORING:RemoveStaticScore( ScoreStatic ) - - local StaticName = ScoreStatic:GetName() - - self.ScoringObjects[StaticName] = nil - - return self -end - - ---- Specify a special additional score for a @{Group}. --- @param #SCORING self --- @param Wrapper.Group#GROUP ScoreGroup The @{Group} for which each @{Unit} a Score is given. --- @param #number Score The Score value. --- @return #SCORING -function SCORING:AddScoreGroup( ScoreGroup, Score ) - - local ScoreUnits = ScoreGroup:GetUnits() - - for ScoreUnitID, ScoreUnit in pairs( ScoreUnits ) do - local UnitName = ScoreUnit:GetName() - self.ScoringObjects[UnitName] = Score - end - - return self -end - ---- Add a @{Zone} to define additional scoring when any object is destroyed in that zone. --- Note that if a @{Zone} with the same name is already within the scoring added, the @{Zone} (type) and Score will be replaced! --- This allows for a dynamic destruction zone evolution within your mission. --- @param #SCORING self --- @param Core.Zone#ZONE_BASE ScoreZone The @{Zone} which defines the destruction score perimeters. --- Note that a zone can be a polygon or a moving zone. --- @param #number Score The Score value. --- @return #SCORING -function SCORING:AddZoneScore( ScoreZone, Score ) - - local ZoneName = ScoreZone:GetName() - - self.ScoringZones[ZoneName] = {} - self.ScoringZones[ZoneName].ScoreZone = ScoreZone - self.ScoringZones[ZoneName].Score = Score - - return self -end - ---- Remove a @{Zone} for additional scoring. --- The scoring will search if any @{Zone} is added with the given name, and will remove that zone from the scoring. --- This allows for a dynamic destruction zone evolution within your mission. --- @param #SCORING self --- @param Core.Zone#ZONE_BASE ScoreZone The @{Zone} which defines the destruction score perimeters. --- Note that a zone can be a polygon or a moving zone. --- @return #SCORING -function SCORING:RemoveZoneScore( ScoreZone ) - - local ZoneName = ScoreZone:GetName() - - self.ScoringZones[ZoneName] = nil - - return self -end - - ---- Configure to send messages after a target has been hit. --- @param #SCORING self --- @param #boolean OnOff If true is given, the messages are sent. --- @return #SCORING -function SCORING:SetMessagesHit( OnOff ) - - self.MessagesHit = OnOff - return self -end - ---- If to send messages after a target has been hit. --- @param #SCORING self --- @return #boolean -function SCORING:IfMessagesHit() - - return self.MessagesHit -end - ---- Configure to send messages after a target has been destroyed. --- @param #SCORING self --- @param #boolean OnOff If true is given, the messages are sent. --- @return #SCORING -function SCORING:SetMessagesDestroy( OnOff ) - - self.MessagesDestroy = OnOff - return self -end - ---- If to send messages after a target has been destroyed. --- @param #SCORING self --- @return #boolean -function SCORING:IfMessagesDestroy() - - return self.MessagesDestroy -end - ---- Configure to send messages after a target has been destroyed and receives additional scores. --- @param #SCORING self --- @param #boolean OnOff If true is given, the messages are sent. --- @return #SCORING -function SCORING:SetMessagesScore( OnOff ) - - self.MessagesScore = OnOff - return self -end - ---- If to send messages after a target has been destroyed and receives additional scores. --- @param #SCORING self --- @return #boolean -function SCORING:IfMessagesScore() - - return self.MessagesScore -end - ---- Configure to send messages after a target has been hit in a zone, and additional score is received. --- @param #SCORING self --- @param #boolean OnOff If true is given, the messages are sent. --- @return #SCORING -function SCORING:SetMessagesZone( OnOff ) - - self.MessagesZone = OnOff - return self -end - ---- If to send messages after a target has been hit in a zone, and additional score is received. --- @param #SCORING self --- @return #boolean -function SCORING:IfMessagesZone() - - return self.MessagesZone -end - ---- Configure to send messages to all players. --- @param #SCORING self --- @return #SCORING -function SCORING:SetMessagesToAll() - - self.MessagesAudience = 1 - return self -end - ---- If to send messages to all players. --- @param #SCORING self --- @return #boolean -function SCORING:IfMessagesToAll() - - return self.MessagesAudience == 1 -end - ---- Configure to send messages to only those players within the same coalition as the player. --- @param #SCORING self --- @return #SCORING -function SCORING:SetMessagesToCoalition() - - self.MessagesAudience = 2 - return self -end - ---- If to send messages to only those players within the same coalition as the player. --- @param #SCORING self --- @return #boolean -function SCORING:IfMessagesToCoalition() - - return self.MessagesAudience == 2 -end - - ---- When a player commits too much damage to friendlies, his penalty score will reach a certain level. --- Use this method to define the level when a player gets kicked. --- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. --- @param #SCORING self --- @param #number Fratricide The amount of maximum penalty that may be inflicted by a friendly player before he gets kicked. --- @return #SCORING -function SCORING:SetFratricide( Fratricide ) - - self.Fratricide = Fratricide - return self -end - - ---- When a player changes the coalition, he can receive a penalty score. --- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition. --- By default, the penalty for changing coalition is the default penalty scale. --- @param #SCORING self --- @param #number CoalitionChangePenalty The amount of penalty that is given. --- @return #SCORING -function SCORING:SetCoalitionChangePenalty( CoalitionChangePenalty ) - - self.CoalitionChangePenalty = CoalitionChangePenalty - return self -end - - ---- Add a new player entering a Unit. --- @param #SCORING self --- @param Wrapper.Unit#UNIT UnitData -function SCORING:_AddPlayerFromUnit( UnitData ) - self:F( UnitData ) - - if UnitData:IsAlive() then - local UnitName = UnitData:GetName() - local PlayerName = UnitData:GetPlayerName() - local UnitDesc = UnitData:GetDesc() - local UnitCategory = UnitDesc.category - local UnitCoalition = UnitData:GetCoalition() - local UnitTypeName = UnitData:GetTypeName() - local UnitThreatLevel, UnitThreatType = UnitData:GetThreatLevel() - - self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } ) - - if self.Players[PlayerName] == nil then -- I believe this is the place where a Player gets a life in a mission when he enters a unit ... - self.Players[PlayerName] = {} - self.Players[PlayerName].Hit = {} - self.Players[PlayerName].Destroy = {} - self.Players[PlayerName].Goals = {} - self.Players[PlayerName].Mission = {} - - -- for CategoryID, CategoryName in pairs( SCORINGCategory ) do - -- self.Players[PlayerName].Hit[CategoryID] = {} - -- self.Players[PlayerName].Destroy[CategoryID] = {} - -- end - self.Players[PlayerName].HitPlayers = {} - self.Players[PlayerName].Score = 0 - self.Players[PlayerName].Penalty = 0 - self.Players[PlayerName].PenaltyCoalition = 0 - self.Players[PlayerName].PenaltyWarning = 0 - end - - if not self.Players[PlayerName].UnitCoalition then - self.Players[PlayerName].UnitCoalition = UnitCoalition - else - if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then - self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50 - self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1 - MESSAGE:New( "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] .. - "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.", - 2 - ):ToAll() - self:ScoreCSV( PlayerName, "", "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, - UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() ) - end - end - self.Players[PlayerName].UnitName = UnitName - self.Players[PlayerName].UnitCoalition = UnitCoalition - self.Players[PlayerName].UnitCategory = UnitCategory - self.Players[PlayerName].UnitType = UnitTypeName - self.Players[PlayerName].UNIT = UnitData - self.Players[PlayerName].ThreatLevel = UnitThreatLevel - self.Players[PlayerName].ThreatType = UnitThreatType - - if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then - if self.Players[PlayerName].PenaltyWarning < 1 then - MESSAGE:New( "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, - 30 - ):ToAll() - self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1 - end - end - - if self.Players[PlayerName].Penalty > self.Fratricide then - UnitData:Destroy() - MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", - 10 - ):ToAll() - end - - end -end - - ---- Add a goal score for a player. --- The method takes the PlayerUnit for which the Goal score needs to be set. --- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal. --- A free text can be given that is shown to the players. --- The Score can be both positive and negative. --- @param #SCORING self --- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the Player. Other Properties for the scoring are taken from this PlayerUnit, like coalition, type etc. --- @param #string GoalTag The string or identifier that is used in the CSV file to identify the goal (sort or group later in Excel). --- @param #string Text A free text that is shown to the players. --- @param #number Score The score can be both positive or negative ( Penalty ). -function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score ) - - local PlayerName = PlayerUnit:GetPlayerName() - - self:E( { PlayerUnit.UnitName, PlayerName, GoalTag, Text, Score } ) - - -- PlayerName can be nil, if the Unit with the player crashed or due to another reason. - if PlayerName then - local PlayerData = self.Players[PlayerName] - - PlayerData.Goals[GoalTag] = PlayerData.Goals[GoalTag] or { Score = 0 } - PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score - PlayerData.Score = PlayerData.Score + Score - - MESSAGE:New( Text, 30 ):ToAll() - - self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() ) - end -end - - ---- Registers Scores the players completing a Mission Task. --- @param #SCORING self --- @param Tasking.Mission#MISSION Mission --- @param Wrapper.Unit#UNIT PlayerUnit --- @param #string Text --- @param #number Score -function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score ) - - local PlayerName = PlayerUnit:GetPlayerName() - local MissionName = Mission:GetName() - - self:E( { Mission:GetName(), PlayerUnit.UnitName, PlayerName, Text, Score } ) - - -- PlayerName can be nil, if the Unit with the player crashed or due to another reason. - if PlayerName then - local PlayerData = self.Players[PlayerName] - - if not PlayerData.Mission[MissionName] then - PlayerData.Mission[MissionName] = {} - PlayerData.Mission[MissionName].ScoreTask = 0 - PlayerData.Mission[MissionName].ScoreMission = 0 - end - - self:T( PlayerName ) - self:T( PlayerData.Mission[MissionName] ) - - PlayerData.Score = self.Players[PlayerName].Score + Score - PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score - - MESSAGE:New( "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " .. - Score .. " task score!", - 30 ):ToAll() - - self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() ) - end -end - - ---- Registers Mission Scores for possible multiple players that contributed in the Mission. --- @param #SCORING self --- @param Tasking.Mission#MISSION Mission --- @param Wrapper.Unit#UNIT PlayerUnit --- @param #string Text --- @param #number Score -function SCORING:_AddMissionScore( Mission, Text, Score ) - - local MissionName = Mission:GetName() - - self:E( { Mission, Text, Score } ) - self:E( self.Players ) - - for PlayerName, PlayerData in pairs( self.Players ) do - - self:E( PlayerData ) - if PlayerData.Mission[MissionName] then - - PlayerData.Score = PlayerData.Score + Score - PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score - - MESSAGE:New( "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " .. - Score .. " mission score!", - 60 ):ToAll() - - self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score ) - end - end -end - - ---- Handles the OnPlayerEnterUnit event for the scoring. --- @param #SCORING self --- @param Core.Event#EVENTDATA Event -function SCORING:OnEventPlayerEnterUnit( Event ) - if Event.IniUnit then - self:_AddPlayerFromUnit( Event.IniUnit ) - local Menu = MENU_GROUP:New( Event.IniGroup, 'Scoring' ) - local ReportGroupSummary = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Summary report players in group', Menu, SCORING.ReportScoreGroupSummary, self, Event.IniGroup ) - local ReportGroupDetailed = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Detailed report players in group', Menu, SCORING.ReportScoreGroupDetailed, self, Event.IniGroup ) - local ReportToAllSummary = MENU_GROUP_COMMAND:New( Event.IniGroup, 'Summary report all players', Menu, SCORING.ReportScoreAllSummary, self, Event.IniGroup ) - self:SetState( Event.IniUnit, "ScoringMenu", Menu ) - end -end - ---- Handles the OnPlayerLeaveUnit event for the scoring. --- @param #SCORING self --- @param Core.Event#EVENTDATA Event -function SCORING:OnEventPlayerLeaveUnit( Event ) - if Event.IniUnit then - local Menu = self:GetState( Event.IniUnit, "ScoringMenu" ) -- Core.Menu#MENU_GROUP - if Menu then - Menu:Remove() - end - end -end - - ---- Handles the OnHit event for the scoring. --- @param #SCORING self --- @param Core.Event#EVENTDATA Event -function SCORING:_EventOnHit( Event ) - self:F( { Event } ) - - local InitUnit = nil - local InitUNIT = nil - local InitUnitName = "" - local InitGroup = nil - local InitGroupName = "" - local InitPlayerName = nil - - local InitCoalition = nil - local InitCategory = nil - local InitType = nil - local InitUnitCoalition = nil - local InitUnitCategory = nil - local InitUnitType = nil - - local TargetUnit = nil - local TargetUNIT = nil - local TargetUnitName = "" - local TargetGroup = nil - local TargetGroupName = "" - local TargetPlayerName = nil - - local TargetCoalition = nil - local TargetCategory = nil - local TargetType = nil - local TargetUnitCoalition = nil - local TargetUnitCategory = nil - local TargetUnitType = nil - - if Event.IniDCSUnit then - - InitUnit = Event.IniDCSUnit - InitUNIT = Event.IniUnit - InitUnitName = Event.IniDCSUnitName - InitGroup = Event.IniDCSGroup - InitGroupName = Event.IniDCSGroupName - InitPlayerName = Event.IniPlayerName - - InitCoalition = Event.IniCoalition - --TODO: Workaround Client DCS Bug - --InitCategory = InitUnit:getCategory() - --InitCategory = InitUnit:getDesc().category - InitCategory = Event.IniCategory - InitType = Event.IniTypeName - - InitUnitCoalition = _SCORINGCoalition[InitCoalition] - InitUnitCategory = _SCORINGCategory[InitCategory] - InitUnitType = InitType - - self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType , InitUnitCoalition, InitUnitCategory, InitUnitType } ) - end - - - if Event.TgtDCSUnit then - - TargetUnit = Event.TgtDCSUnit - TargetUNIT = Event.TgtUnit - TargetUnitName = Event.TgtDCSUnitName - TargetGroup = Event.TgtDCSGroup - TargetGroupName = Event.TgtDCSGroupName - TargetPlayerName = Event.TgtPlayerName - - TargetCoalition = Event.TgtCoalition - --TODO: Workaround Client DCS Bug - --TargetCategory = TargetUnit:getCategory() - --TargetCategory = TargetUnit:getDesc().category - TargetCategory = Event.TgtCategory - TargetType = Event.TgtTypeName - - TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] - TargetUnitCategory = _SCORINGCategory[TargetCategory] - TargetUnitType = TargetType - - self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType, TargetUnitCoalition, TargetUnitCategory, TargetUnitType } ) - end - - if InitPlayerName ~= nil then -- It is a player that is hitting something - self:_AddPlayerFromUnit( InitUNIT ) - if self.Players[InitPlayerName] then -- This should normally not happen, but i'll test it anyway. - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - self:_AddPlayerFromUnit( TargetUNIT ) - end - - self:T( "Hitting Something" ) - - -- What is he hitting? - if TargetCategory then - - -- A target got hit, score it. - -- Player contains the score data from self.Players[InitPlayerName] - local Player = self.Players[InitPlayerName] - - -- Ensure there is a hit table per TargetCategory and TargetUnitName. - Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {} - Player.Hit[TargetCategory][TargetUnitName] = Player.Hit[TargetCategory][TargetUnitName] or {} - - -- PlayerHit contains the score counters and data per unit that was hit. - local PlayerHit = Player.Hit[TargetCategory][TargetUnitName] - - PlayerHit.Score = PlayerHit.Score or 0 - PlayerHit.Penalty = PlayerHit.Penalty or 0 - PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0 - PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 - PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 - PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT - PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() - - -- Only grant hit scores if there was more than one second between the last hit. - if timer.getTime() - PlayerHit.TimeStamp > 1 then - PlayerHit.TimeStamp = timer.getTime() - - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - - -- Ensure there is a Player to Player hit reference table. - Player.HitPlayers[TargetPlayerName] = true - end - - local Score = 0 - - if InitCoalition then -- A coalition object was hit. - if InitCoalition == TargetCoalition then - Player.Penalty = Player.Penalty + 10 - PlayerHit.Penalty = PlayerHit.Penalty + 10 - PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 - - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. - "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - else - MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit a friendly target " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. - "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - end - self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - Player.Score = Player.Score + 1 - PlayerHit.Score = PlayerHit.Score + 1 - PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. - "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - else - MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit an enemy target " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. - "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - end - self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - else -- A scenery object was hit. - MESSAGE - :New( "Player '" .. InitPlayerName .. "' hit a scenery object.", - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 0, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) - end - end - end - end - elseif InitPlayerName == nil then -- It is an AI hitting a player??? - - end - - -- It is a weapon initiated by a player, that is hitting something - -- This seems to occur only with scenery and static objects. - if Event.WeaponPlayerName ~= nil then - self:_AddPlayerFromUnit( Event.WeaponUNIT ) - if self.Players[Event.WeaponPlayerName] then -- This should normally not happen, but i'll test it anyway. - if TargetPlayerName ~= nil then -- It is a player hitting another player ... - self:_AddPlayerFromUnit( TargetUNIT ) - end - - self:T( "Hitting Scenery" ) - - -- What is he hitting? - if TargetCategory then - - -- A scenery or static got hit, score it. - -- Player contains the score data from self.Players[WeaponPlayerName] - local Player = self.Players[Event.WeaponPlayerName] - - -- Ensure there is a hit table per TargetCategory and TargetUnitName. - Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {} - Player.Hit[TargetCategory][TargetUnitName] = Player.Hit[TargetCategory][TargetUnitName] or {} - - -- PlayerHit contains the score counters and data per unit that was hit. - local PlayerHit = Player.Hit[TargetCategory][TargetUnitName] - - PlayerHit.Score = PlayerHit.Score or 0 - PlayerHit.Penalty = PlayerHit.Penalty or 0 - PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0 - PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 - PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 - PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT - PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() - - -- Only grant hit scores if there was more than one second between the last hit. - if timer.getTime() - PlayerHit.TimeStamp > 1 then - PlayerHit.TimeStamp = timer.getTime() - - local Score = 0 - - if InitCoalition then -- A coalition object was hit, probably a static. - if InitCoalition == TargetCoalition then - -- TODO: Penalty according scale - Player.Penalty = Player.Penalty + 10 - PlayerHit.Penalty = PlayerHit.Penalty + 10 - PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 - - MESSAGE - :New( "Player '" .. Event.WeaponPlayerName .. "' hit a friendly target " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. - "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - Player.Score = Player.Score + 1 - PlayerHit.Score = PlayerHit.Score + 1 - PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 - MESSAGE - :New( "Player '" .. Event.WeaponPlayerName .. "' hit an enemy target " .. - TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. - "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - end - else -- A scenery object was hit. - MESSAGE - :New( "Player '" .. Event.WeaponPlayerName .. "' hit a scenery object.", - 2 - ) - :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) - self:ScoreCSV( Event.WeaponPlayerName, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType ) - end - end - end - end - end -end - ---- Track DEAD or CRASH events for the scoring. --- @param #SCORING self --- @param Core.Event#EVENTDATA Event -function SCORING:_EventOnDeadOrCrash( Event ) - self:F( { Event } ) - - local TargetUnit = nil - local TargetGroup = nil - local TargetUnitName = "" - local TargetGroupName = "" - local TargetPlayerName = "" - local TargetCoalition = nil - local TargetCategory = nil - local TargetType = nil - local TargetUnitCoalition = nil - local TargetUnitCategory = nil - local TargetUnitType = nil - - if Event.IniDCSUnit then - - TargetUnit = Event.IniUnit - TargetUnitName = Event.IniDCSUnitName - TargetGroup = Event.IniDCSGroup - TargetGroupName = Event.IniDCSGroupName - TargetPlayerName = Event.IniPlayerName - - TargetCoalition = Event.IniCoalition - --TargetCategory = TargetUnit:getCategory() - --TargetCategory = TargetUnit:getDesc().category -- Workaround - TargetCategory = Event.IniCategory - TargetType = Event.IniTypeName - - TargetUnitCoalition = _SCORINGCoalition[TargetCoalition] - TargetUnitCategory = _SCORINGCategory[TargetCategory] - TargetUnitType = TargetType - - self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) - end - - -- Player contains the score and reference data for the player. - for PlayerName, Player in pairs( self.Players ) do - if Player then -- This should normally not happen, but i'll test it anyway. - self:T( "Something got destroyed" ) - - -- Some variables - local InitUnitName = Player.UnitName - local InitUnitType = Player.UnitType - local InitCoalition = Player.UnitCoalition - local InitCategory = Player.UnitCategory - local InitUnitCoalition = _SCORINGCoalition[InitCoalition] - local InitUnitCategory = _SCORINGCategory[InitCategory] - - self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } ) - - local Destroyed = false - - -- What is the player destroying? - if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 then -- Was there a hit for this unit for this player before registered??? - - local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel - local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType - - Player.Destroy[TargetCategory] = Player.Destroy[TargetCategory] or {} - Player.Destroy[TargetCategory][TargetType] = Player.Destroy[TargetCategory][TargetType] or {} - - -- PlayerDestroy contains the destroy score data per category and target type of the player. - local TargetDestroy = Player.Destroy[TargetCategory][TargetType] - TargetDestroy.Score = TargetDestroy.Score or 0 - TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy or 0 - TargetDestroy.Penalty = TargetDestroy.Penalty or 0 - TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy or 0 - - if TargetCoalition then - if InitCoalition == TargetCoalition then - local ThreatLevelTarget = TargetThreatLevel - local ThreatTypeTarget = TargetThreatType - local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1 - local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyPenalty / 10 ) - self:E( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) - - Player.Penalty = Player.Penalty + ThreatPenalty - TargetDestroy.Penalty = TargetDestroy.Penalty + ThreatPenalty - TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1 - - if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player - MESSAGE - :New( "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. - TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " .. - "Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, - 15 - ) - :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) - else - MESSAGE - :New( "Player '" .. PlayerName .. "' destroyed a friendly target " .. - TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " .. - "Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, - 15 - ) - :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) - end - - Destroyed = true - self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - else - - local ThreatLevelTarget = TargetThreatLevel - local ThreatTypeTarget = TargetThreatType - local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1 - local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) - - self:E( { ThreatLevel = ThreatScore, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) - - Player.Score = Player.Score + ThreatScore - TargetDestroy.Score = TargetDestroy.Score + ThreatScore - TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1 - if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player - MESSAGE - :New( "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. - TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " .. - "Score: " .. TargetDestroy.Score .. ". Score Total:" .. Player.Score - Player.Penalty, - 15 - ) - :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) - else - MESSAGE - :New( "Player '" .. PlayerName .. "' destroyed an enemy " .. - TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " .. - "Score: " .. TargetDestroy.Score .. ". Total:" .. Player.Score - Player.Penalty, - 15 - ) - :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) - end - Destroyed = true - self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - - local UnitName = TargetUnit:GetName() - local Score = self.ScoringObjects[UnitName] - if Score then - Player.Score = Player.Score + Score - TargetDestroy.Score = TargetDestroy.Score + Score - MESSAGE - :New( "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " .. - "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty, - 15 - ) - :ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() ) - self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - Destroyed = true - end - - -- Check if there are Zones where the destruction happened. - for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do - self:E( { ScoringZone = ScoreZoneData } ) - local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE - local Score = ScoreZoneData.Score - if ScoreZone:IsVec2InZone( TargetUnit:GetVec2() ) then - Player.Score = Player.Score + Score - TargetDestroy.Score = TargetDestroy.Score + Score - MESSAGE - :New( "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." .. - "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. - "Total: " .. Player.Score - Player.Penalty, - 15 ) - :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) - self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - Destroyed = true - end - end - - end - else - -- Check if there are Zones where the destruction happened. - for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do - self:E( { ScoringZone = ScoreZoneData } ) - local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE - local Score = ScoreZoneData.Score - if ScoreZone:IsVec2InZone( TargetUnit:GetVec2() ) then - Player.Score = Player.Score + Score - TargetDestroy.Score = TargetDestroy.Score + Score - MESSAGE - :New( "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." .. - "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. - "Total: " .. Player.Score - Player.Penalty, - 15 - ) - :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) - :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) - Destroyed = true - self:ScoreCSV( PlayerName, "", "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) - end - end - end - - -- Delete now the hit cache if the target was destroyed. - -- Otherwise points will be granted every time a target gets killed by the players that hit that target. - -- This is only relevant for player to player destroys. - if Destroyed then - Player.Hit[TargetCategory][TargetUnitName].TimeStamp = 0 - end - end - end - end -end - - ---- Produce detailed report of player hit scores. --- @param #SCORING self --- @param #string PlayerName The name of the player. --- @return #string The report. -function SCORING:ReportDetailedPlayerHits( PlayerName ) - - local ScoreMessage = "" - local PlayerScore = 0 - local PlayerPenalty = 0 - - local PlayerData = self.Players[PlayerName] - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) - - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local ScoreMessageHits = "" - for CategoryID, CategoryName in pairs( _SCORINGCategory ) do - self:T( CategoryName ) - if PlayerData.Hit[CategoryID] then - self:T( "Hit scores exist for player " .. PlayerName ) - local Score = 0 - local ScoreHit = 0 - local Penalty = 0 - local PenaltyHit = 0 - for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do - Score = Score + UnitData.Score - ScoreHit = ScoreHit + UnitData.ScoreHit - Penalty = Penalty + UnitData.Penalty - PenaltyHit = UnitData.PenaltyHit - end - local ScoreMessageHit = string.format( "%s:%d ", CategoryName, Score - Penalty ) - self:T( ScoreMessageHit ) - ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageHits ~= "" then - ScoreMessage = "Hits: " .. ScoreMessageHits - end - end - - return ScoreMessage, PlayerScore, PlayerPenalty -end - - ---- Produce detailed report of player destroy scores. --- @param #SCORING self --- @param #string PlayerName The name of the player. --- @return #string The report. -function SCORING:ReportDetailedPlayerDestroys( PlayerName ) - - local ScoreMessage = "" - local PlayerScore = 0 - local PlayerPenalty = 0 - - local PlayerData = self.Players[PlayerName] - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) - - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local ScoreMessageDestroys = "" - for CategoryID, CategoryName in pairs( _SCORINGCategory ) do - if PlayerData.Destroy[CategoryID] then - self:T( "Destroy scores exist for player " .. PlayerName ) - local Score = 0 - local ScoreDestroy = 0 - local Penalty = 0 - local PenaltyDestroy = 0 - - for UnitName, UnitData in pairs( PlayerData.Destroy[CategoryID] ) do - self:E( { UnitData = UnitData } ) - if UnitData ~= {} then - Score = Score + UnitData.Score - ScoreDestroy = ScoreDestroy + UnitData.ScoreDestroy - Penalty = Penalty + UnitData.Penalty - PenaltyDestroy = PenaltyDestroy + UnitData.PenaltyDestroy - end - end - - local ScoreMessageDestroy = string.format( " %s:%d ", CategoryName, Score - Penalty ) - self:T( ScoreMessageDestroy ) - ScoreMessageDestroys = ScoreMessageDestroys .. ScoreMessageDestroy - - PlayerScore = PlayerScore + Score - PlayerPenalty = PlayerPenalty + Penalty - else - --ScoreMessageDestroys = ScoreMessageDestroys .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) - end - end - if ScoreMessageDestroys ~= "" then - ScoreMessage = "Destroys: " .. ScoreMessageDestroys - end - end - - return ScoreMessage, PlayerScore, PlayerPenalty -end - ---- Produce detailed report of player penalty scores because of changing the coalition. --- @param #SCORING self --- @param #string PlayerName The name of the player. --- @return #string The report. -function SCORING:ReportDetailedPlayerCoalitionChanges( PlayerName ) - - local ScoreMessage = "" - local PlayerScore = 0 - local PlayerPenalty = 0 - - local PlayerData = self.Players[PlayerName] - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) - - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local ScoreMessageCoalitionChangePenalties = "" - if PlayerData.PenaltyCoalition ~= 0 then - ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition ) - PlayerPenalty = PlayerPenalty + PlayerData.Penalty - end - if ScoreMessageCoalitionChangePenalties ~= "" then - ScoreMessage = ScoreMessage .. "Coalition Penalties: " .. ScoreMessageCoalitionChangePenalties - end - end - - return ScoreMessage, PlayerScore, PlayerPenalty -end - ---- Produce detailed report of player goal scores. --- @param #SCORING self --- @param #string PlayerName The name of the player. --- @return #string The report. -function SCORING:ReportDetailedPlayerGoals( PlayerName ) - - local ScoreMessage = "" - local PlayerScore = 0 - local PlayerPenalty = 0 - - local PlayerData = self.Players[PlayerName] - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) - - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local ScoreMessageGoal = "" - local ScoreGoal = 0 - local ScoreTask = 0 - for GoalName, GoalData in pairs( PlayerData.Goals ) do - ScoreGoal = ScoreGoal + GoalData.Score - ScoreMessageGoal = ScoreMessageGoal .. "'" .. GoalName .. "':" .. GoalData.Score .. "; " - end - PlayerScore = PlayerScore + ScoreGoal - - if ScoreMessageGoal ~= "" then - ScoreMessage = "Goals: " .. ScoreMessageGoal - end - end - - return ScoreMessage, PlayerScore, PlayerPenalty -end - ---- Produce detailed report of player penalty scores because of changing the coalition. --- @param #SCORING self --- @param #string PlayerName The name of the player. --- @return #string The report. -function SCORING:ReportDetailedPlayerMissions( PlayerName ) - - local ScoreMessage = "" - local PlayerScore = 0 - local PlayerPenalty = 0 - - local PlayerData = self.Players[PlayerName] - if PlayerData then -- This should normally not happen, but i'll test it anyway. - self:T( "Score Player: " .. PlayerName ) - - -- Some variables - local InitUnitCoalition = _SCORINGCoalition[PlayerData.UnitCoalition] - local InitUnitCategory = _SCORINGCategory[PlayerData.UnitCategory] - local InitUnitType = PlayerData.UnitType - local InitUnitName = PlayerData.UnitName - - local ScoreMessageMission = "" - local ScoreMission = 0 - local ScoreTask = 0 - for MissionName, MissionData in pairs( PlayerData.Mission ) do - ScoreMission = ScoreMission + MissionData.ScoreMission - ScoreTask = ScoreTask + MissionData.ScoreTask - ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; " - end - PlayerScore = PlayerScore + ScoreMission + ScoreTask - - if ScoreMessageMission ~= "" then - ScoreMessage = "Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ")" - end - end - - return ScoreMessage, PlayerScore, PlayerPenalty -end - - ---- Report Group Score Summary --- @param #SCORING self --- @param Wrapper.Group#GROUP PlayerGroup The player group. -function SCORING:ReportScoreGroupSummary( PlayerGroup ) - - local PlayerMessage = "" - - self:T( "Report Score Group Summary" ) - - local PlayerUnits = PlayerGroup:GetUnits() - for UnitID, PlayerUnit in pairs( PlayerUnits ) do - local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT - local PlayerName = PlayerUnit:GetPlayerName() - - if PlayerName then - - local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) - ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits - self:E( { ReportHits, ScoreHits, PenaltyHits } ) - - local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) - ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys - self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) - - local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) - ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges - self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) - - local ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) - ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals - self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) - - local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) - ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions - self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) - - local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions - local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions - - PlayerMessage = - string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", - PlayerName, - PlayerScore - PlayerPenalty, - PlayerScore, - PlayerPenalty - ) - MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) - end - end - -end - ---- Report Group Score Detailed --- @param #SCORING self --- @param Wrapper.Group#GROUP PlayerGroup The player group. -function SCORING:ReportScoreGroupDetailed( PlayerGroup ) - - local PlayerMessage = "" - - self:T( "Report Score Group Detailed" ) - - local PlayerUnits = PlayerGroup:GetUnits() - for UnitID, PlayerUnit in pairs( PlayerUnits ) do - local PlayerUnit = PlayerUnit -- Wrapper.Unit#UNIT - local PlayerName = PlayerUnit:GetPlayerName() - - if PlayerName then - - local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) - ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits - self:E( { ReportHits, ScoreHits, PenaltyHits } ) - - local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) - ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys - self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) - - local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) - ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges - self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) - - local ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) - ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals - self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) - - local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) - ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions - self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) - - local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions - local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions - - PlayerMessage = - string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s", - PlayerName, - PlayerScore - PlayerPenalty, - PlayerScore, - PlayerPenalty, - ReportHits, - ReportDestroys, - ReportCoalitionChanges, - ReportGoals, - ReportMissions - ) - MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) - end - end - -end - ---- Report all players score --- @param #SCORING self --- @param Wrapper.Group#GROUP PlayerGroup The player group. -function SCORING:ReportScoreAllSummary( PlayerGroup ) - - local PlayerMessage = "" - - self:T( "Report Score All Players" ) - - for PlayerName, PlayerData in pairs( self.Players ) do - - if PlayerName then - - local ReportHits, ScoreHits, PenaltyHits = self:ReportDetailedPlayerHits( PlayerName ) - ReportHits = ReportHits ~= "" and "\n- " .. ReportHits or ReportHits - self:E( { ReportHits, ScoreHits, PenaltyHits } ) - - local ReportDestroys, ScoreDestroys, PenaltyDestroys = self:ReportDetailedPlayerDestroys( PlayerName ) - ReportDestroys = ReportDestroys ~= "" and "\n- " .. ReportDestroys or ReportDestroys - self:E( { ReportDestroys, ScoreDestroys, PenaltyDestroys } ) - - local ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges = self:ReportDetailedPlayerCoalitionChanges( PlayerName ) - ReportCoalitionChanges = ReportCoalitionChanges ~= "" and "\n- " .. ReportCoalitionChanges or ReportCoalitionChanges - self:E( { ReportCoalitionChanges, ScoreCoalitionChanges, PenaltyCoalitionChanges } ) - - local ReportGoals, ScoreGoals, PenaltyGoals = self:ReportDetailedPlayerGoals( PlayerName ) - ReportGoals = ReportGoals ~= "" and "\n- " .. ReportGoals or ReportGoals - self:E( { ReportGoals, ScoreGoals, PenaltyGoals } ) - - local ReportMissions, ScoreMissions, PenaltyMissions = self:ReportDetailedPlayerMissions( PlayerName ) - ReportMissions = ReportMissions ~= "" and "\n- " .. ReportMissions or ReportMissions - self:E( { ReportMissions, ScoreMissions, PenaltyMissions } ) - - local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions - local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions - - PlayerMessage = - string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", - PlayerName, - PlayerScore - PlayerPenalty, - PlayerScore, - PlayerPenalty - ) - MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) - end - end - -end - - -function SCORING:SecondsToClock(sSeconds) - local nSeconds = sSeconds - if nSeconds == 0 then - --return nil; - return "00:00:00"; - else - nHours = string.format("%02.f", math.floor(nSeconds/3600)); - nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60))); - nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60)); - return nHours..":"..nMins..":"..nSecs - end -end - ---- Opens a score CSV file to log the scores. --- @param #SCORING self --- @param #string ScoringCSV --- @return #SCORING self --- @usage --- -- Open a new CSV file to log the scores of the game Gori Valley. Let the name of the CSV file begin with "Player Scores". --- ScoringObject = SCORING:New( "Gori Valley" ) --- ScoringObject:OpenCSV( "Player Scores" ) -function SCORING:OpenCSV( ScoringCSV ) - self:F( ScoringCSV ) - - if lfs and io and os then - if ScoringCSV then - self.ScoringCSV = ScoringCSV - local fdir = lfs.writedir() .. [[Logs\]] .. self.ScoringCSV .. " " .. os.date( "%Y-%m-%d %H-%M-%S" ) .. ".csv" - - self.CSVFile, self.err = io.open( fdir, "w+" ) - if not self.CSVFile then - error( "Error: Cannot open CSV file in " .. lfs.writedir() ) - end - - self.CSVFile:write( '"GameName","RunTime","Time","PlayerName","TargetPlayerName","ScoreType","PlayerUnitCoaltion","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n' ) - - self.RunTime = os.date("%y-%m-%d_%H-%M-%S") - else - error( "A string containing the CSV file name must be given." ) - end - else - self:E( "The MissionScripting.lua file has not been changed to allow lfs, io and os modules to be used..." ) - end - return self -end - - ---- Registers a score for a player. --- @param #SCORING self --- @param #string PlayerName The name of the player. --- @param #string TargetPlayerName The name of the target player. --- @param #string ScoreType The type of the score. --- @param #string ScoreTimes The amount of scores achieved. --- @param #string ScoreAmount The score given. --- @param #string PlayerUnitName The unit name of the player. --- @param #string PlayerUnitCoalition The coalition of the player unit. --- @param #string PlayerUnitCategory The category of the player unit. --- @param #string PlayerUnitType The type of the player unit. --- @param #string TargetUnitName The name of the target unit. --- @param #string TargetUnitCoalition The coalition of the target unit. --- @param #string TargetUnitCategory The category of the target unit. --- @param #string TargetUnitType The type of the target unit. --- @return #SCORING self -function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes, ScoreAmount, PlayerUnitName, PlayerUnitCoalition, PlayerUnitCategory, PlayerUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) - --write statistic information to file - local ScoreTime = self:SecondsToClock( timer.getTime() ) - PlayerName = PlayerName:gsub( '"', '_' ) - - TargetPlayerName = TargetPlayerName or "" - TargetPlayerName = TargetPlayerName:gsub( '"', '_' ) - - if PlayerUnitName and PlayerUnitName ~= '' then - local PlayerUnit = Unit.getByName( PlayerUnitName ) - - if PlayerUnit then - if not PlayerUnitCategory then - --PlayerUnitCategory = SCORINGCategory[PlayerUnit:getCategory()] - PlayerUnitCategory = _SCORINGCategory[PlayerUnit:getDesc().category] - end - - if not PlayerUnitCoalition then - PlayerUnitCoalition = _SCORINGCoalition[PlayerUnit:getCoalition()] - end - - if not PlayerUnitType then - PlayerUnitType = PlayerUnit:getTypeName() - end - else - PlayerUnitName = '' - PlayerUnitCategory = '' - PlayerUnitCoalition = '' - PlayerUnitType = '' - end - else - PlayerUnitName = '' - PlayerUnitCategory = '' - PlayerUnitCoalition = '' - PlayerUnitType = '' - end - - TargetUnitCoalition = TargetUnitCoalition or "" - TargetUnitCategory = TargetUnitCategory or "" - TargetUnitType = TargetUnitType or "" - TargetUnitName = TargetUnitName or "" - - if lfs and io and os then - self.CSVFile:write( - '"' .. self.GameName .. '"' .. ',' .. - '"' .. self.RunTime .. '"' .. ',' .. - '' .. ScoreTime .. '' .. ',' .. - '"' .. PlayerName .. '"' .. ',' .. - '"' .. TargetPlayerName .. '"' .. ',' .. - '"' .. ScoreType .. '"' .. ',' .. - '"' .. PlayerUnitCoalition .. '"' .. ',' .. - '"' .. PlayerUnitCategory .. '"' .. ',' .. - '"' .. PlayerUnitType .. '"' .. ',' .. - '"' .. PlayerUnitName .. '"' .. ',' .. - '"' .. TargetUnitCoalition .. '"' .. ',' .. - '"' .. TargetUnitCategory .. '"' .. ',' .. - '"' .. TargetUnitType .. '"' .. ',' .. - '"' .. TargetUnitName .. '"' .. ',' .. - '' .. ScoreTimes .. '' .. ',' .. - '' .. ScoreAmount - ) - - self.CSVFile:write( "\n" ) - end -end - - -function SCORING:CloseCSV() - if lfs and io and os then - self.CSVFile:close() - end -end - ---- The CLEANUP class keeps an area clean of crashing or colliding airplanes. It also prevents airplanes from firing within this area. --- @module CleanUp --- @author Flightcontrol - - - - - - - ---- The CLEANUP class. --- @type CLEANUP --- @extends Core.Base#BASE -CLEANUP = { - ClassName = "CLEANUP", - ZoneNames = {}, - TimeInterval = 300, - CleanUpList = {}, -} - ---- Creates the main object which is handling the cleaning of the debris within the given Zone Names. --- @param #CLEANUP self --- @param #table ZoneNames Is a table of zone names where the debris should be cleaned. Also a single string can be passed with one zone name. --- @param #number TimeInterval The interval in seconds when the clean activity takes place. The default is 300 seconds, thus every 5 minutes. --- @return #CLEANUP --- @usage --- -- Clean these Zones. --- CleanUpAirports = CLEANUP:New( { 'CLEAN Tbilisi', 'CLEAN Kutaisi' }, 150 ) --- or --- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 ) --- CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 ) -function CLEANUP:New( ZoneNames, TimeInterval ) - - local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP - self:F( { ZoneNames, TimeInterval } ) - - if type( ZoneNames ) == 'table' then - self.ZoneNames = ZoneNames - else - self.ZoneNames = { ZoneNames } - end - if TimeInterval then - self.TimeInterval = TimeInterval - end - - self:HandleEvent( EVENTS.Birth ) - - self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval ) - - return self -end - - ---- Destroys a group from the simulator, but checks first if it is still existing! --- @param #CLEANUP self --- @param Dcs.DCSWrapper.Group#Group GroupObject The object to be destroyed. --- @param #string CleanUpGroupName The groupname... -function CLEANUP:_DestroyGroup( GroupObject, CleanUpGroupName ) - self:F( { GroupObject, CleanUpGroupName } ) - - if GroupObject then -- and GroupObject:isExist() then - trigger.action.deactivateGroup(GroupObject) - self:T( { "GroupObject Destroyed", GroupObject } ) - end -end - ---- Destroys a @{DCSWrapper.Unit#Unit} from the simulator, but checks first if it is still existing! --- @param #CLEANUP self --- @param Dcs.DCSWrapper.Unit#Unit CleanUpUnit The object to be destroyed. --- @param #string CleanUpUnitName The Unit name ... -function CLEANUP:_DestroyUnit( CleanUpUnit, CleanUpUnitName ) - self:F( { CleanUpUnit, CleanUpUnitName } ) - - if CleanUpUnit then - local CleanUpGroup = Unit.getGroup(CleanUpUnit) - -- TODO Client bug in 1.5.3 - if CleanUpGroup and CleanUpGroup:isExist() then - local CleanUpGroupUnits = CleanUpGroup:getUnits() - if #CleanUpGroupUnits == 1 then - local CleanUpGroupName = CleanUpGroup:getName() - --self:CreateEventCrash( timer.getTime(), CleanUpUnit ) - CleanUpGroup:destroy() - self:T( { "Destroyed Group:", CleanUpGroupName } ) - else - CleanUpUnit:destroy() - self:T( { "Destroyed Unit:", CleanUpUnitName } ) - end - self.CleanUpList[CleanUpUnitName] = nil -- Cleaning from the list - CleanUpUnit = nil - end - end -end - --- TODO check Dcs.DCSTypes#Weapon ---- Destroys a missile from the simulator, but checks first if it is still existing! --- @param #CLEANUP self --- @param Dcs.DCSTypes#Weapon MissileObject -function CLEANUP:_DestroyMissile( MissileObject ) - self:F( { MissileObject } ) - - if MissileObject and MissileObject:isExist() then - MissileObject:destroy() - self:T( "MissileObject Destroyed") - end -end - ---- @param #CLEANUP self --- @param Core.Event#EVENTDATA EventData -function CLEANUP:_OnEventBirth( EventData ) - self:F( { EventData } ) - - self.CleanUpList[EventData.IniDCSUnitName] = {} - self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit - self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup - self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName - self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName - - EventData.IniUnit:HandleEvent( EVENTS.EngineShutdown , self._EventAddForCleanUp ) - EventData.IniUnit:HandleEvent( EVENTS.EngineStartup, self._EventAddForCleanUp ) - EventData.IniUnit:HandleEvent( EVENTS.Hit, self._EventAddForCleanUp ) - EventData.IniUnit:HandleEvent( EVENTS.PilotDead, self._EventCrash ) - EventData.IniUnit:HandleEvent( EVENTS.Dead, self._EventCrash ) - EventData.IniUnit:HandleEvent( EVENTS.Crash, self._EventCrash ) - EventData.IniUnit:HandleEvent( EVENTS.Shot, self._EventShot ) - -end - ---- Detects if a crash event occurs. --- Crashed units go into a CleanUpList for removal. --- @param #CLEANUP self --- @param Dcs.DCSTypes#Event event -function CLEANUP:_EventCrash( Event ) - self:F( { Event } ) - - --TODO: This stuff is not working due to a DCS bug. Burning units cannot be destroyed. - -- self:T("before getGroup") - -- local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired - -- self:T("after getGroup") - -- _grp:destroy() - -- self:T("after deactivateGroup") - -- event.initiator:destroy() - - self.CleanUpList[Event.IniDCSUnitName] = {} - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup - self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName - self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName - -end - ---- Detects if a unit shoots a missile. --- If this occurs within one of the zones, then the weapon used must be destroyed. --- @param #CLEANUP self --- @param Dcs.DCSTypes#Event event -function CLEANUP:_EventShot( Event ) - self:F( { Event } ) - - -- Test if the missile was fired within one of the CLEANUP.ZoneNames. - local CurrentLandingZoneID = 0 - CurrentLandingZoneID = routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) - if ( CurrentLandingZoneID ) then - -- Okay, the missile was fired within the CLEANUP.ZoneNames, destroy the fired weapon. - --_SEADmissile:destroy() - SCHEDULER:New( self, CLEANUP._DestroyMissile, { Event.Weapon }, 0.1 ) - end -end - - ---- Detects if the Unit has an S_EVENT_HIT within the given ZoneNames. If this is the case, destroy the unit. --- @param #CLEANUP self --- @param Dcs.DCSTypes#Event event -function CLEANUP:_EventHitCleanUp( Event ) - self:F( { Event } ) - - if Event.IniDCSUnit then - if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then - self:T( { "Life: ", Event.IniDCSUnitName, ' = ', Event.IniDCSUnit:getLife(), "/", Event.IniDCSUnit:getLife0() } ) - if Event.IniDCSUnit:getLife() < Event.IniDCSUnit:getLife0() then - self:T( "CleanUp: Destroy: " .. Event.IniDCSUnitName ) - SCHEDULER:New( self, CLEANUP._DestroyUnit, { Event.IniDCSUnit }, 0.1 ) - end - end - end - - if Event.TgtDCSUnit then - if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then - self:T( { "Life: ", Event.TgtDCSUnitName, ' = ', Event.TgtDCSUnit:getLife(), "/", Event.TgtDCSUnit:getLife0() } ) - if Event.TgtDCSUnit:getLife() < Event.TgtDCSUnit:getLife0() then - self:T( "CleanUp: Destroy: " .. Event.TgtDCSUnitName ) - SCHEDULER:New( self, CLEANUP._DestroyUnit, { Event.TgtDCSUnit }, 0.1 ) - end - end - end -end - ---- Add the @{DCSWrapper.Unit#Unit} to the CleanUpList for CleanUp. -function CLEANUP:_AddForCleanUp( CleanUpUnit, CleanUpUnitName ) - self:F( { CleanUpUnit, CleanUpUnitName } ) - - self.CleanUpList[CleanUpUnitName] = {} - self.CleanUpList[CleanUpUnitName].CleanUpUnit = CleanUpUnit - self.CleanUpList[CleanUpUnitName].CleanUpUnitName = CleanUpUnitName - self.CleanUpList[CleanUpUnitName].CleanUpGroup = Unit.getGroup(CleanUpUnit) - self.CleanUpList[CleanUpUnitName].CleanUpGroupName = Unit.getGroup(CleanUpUnit):getName() - self.CleanUpList[CleanUpUnitName].CleanUpTime = timer.getTime() - self.CleanUpList[CleanUpUnitName].CleanUpMoved = false - - self:T( { "CleanUp: Add to CleanUpList: ", Unit.getGroup(CleanUpUnit):getName(), CleanUpUnitName } ) - -end - ---- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given ZoneNames. If this is the case, add the Group to the CLEANUP List. --- @param #CLEANUP self --- @param Dcs.DCSTypes#Event event -function CLEANUP:_EventAddForCleanUp( Event ) - - if Event.IniDCSUnit then - if self.CleanUpList[Event.IniDCSUnitName] == nil then - if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then - self:_AddForCleanUp( Event.IniDCSUnit, Event.IniDCSUnitName ) - end - end - end - - if Event.TgtDCSUnit then - if self.CleanUpList[Event.TgtDCSUnitName] == nil then - if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then - self:_AddForCleanUp( Event.TgtDCSUnit, Event.TgtDCSUnitName ) - end - end - end - -end - -local CleanUpSurfaceTypeText = { - "LAND", - "SHALLOW_WATER", - "WATER", - "ROAD", - "RUNWAY" - } - ---- At the defined time interval, CleanUp the Groups within the CleanUpList. --- @param #CLEANUP self -function CLEANUP:_CleanUpScheduler() - self:F( { "CleanUp Scheduler" } ) - - local CleanUpCount = 0 - for CleanUpUnitName, UnitData in pairs( self.CleanUpList ) do - CleanUpCount = CleanUpCount + 1 - - self:T( { CleanUpUnitName, UnitData } ) - local CleanUpUnit = Unit.getByName(UnitData.CleanUpUnitName) - local CleanUpGroupName = UnitData.CleanUpGroupName - local CleanUpUnitName = UnitData.CleanUpUnitName - if CleanUpUnit then - self:T( { "CleanUp Scheduler", "Checking:", CleanUpUnitName } ) - if _DATABASE:GetStatusGroup( CleanUpGroupName ) ~= "ReSpawn" then - local CleanUpUnitVec3 = CleanUpUnit:getPoint() - --self:T( CleanUpUnitVec3 ) - local CleanUpUnitVec2 = {} - CleanUpUnitVec2.x = CleanUpUnitVec3.x - CleanUpUnitVec2.y = CleanUpUnitVec3.z - --self:T( CleanUpUnitVec2 ) - local CleanUpSurfaceType = land.getSurfaceType(CleanUpUnitVec2) - --self:T( CleanUpSurfaceType ) - - if CleanUpUnit and CleanUpUnit:getLife() <= CleanUpUnit:getLife0() * 0.95 then - if CleanUpSurfaceType == land.SurfaceType.RUNWAY then - if CleanUpUnit:inAir() then - local CleanUpLandHeight = land.getHeight(CleanUpUnitVec2) - local CleanUpUnitHeight = CleanUpUnitVec3.y - CleanUpLandHeight - self:T( { "CleanUp Scheduler", "Height = " .. CleanUpUnitHeight } ) - if CleanUpUnitHeight < 30 then - self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because below safe height and damaged." } ) - self:_DestroyUnit(CleanUpUnit, CleanUpUnitName) - end - else - self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because on runway and damaged." } ) - self:_DestroyUnit(CleanUpUnit, CleanUpUnitName) - end - end - end - -- Clean Units which are waiting for a very long time in the CleanUpZone. - if CleanUpUnit then - local CleanUpUnitVelocity = CleanUpUnit:getVelocity() - local CleanUpUnitVelocityTotal = math.abs(CleanUpUnitVelocity.x) + math.abs(CleanUpUnitVelocity.y) + math.abs(CleanUpUnitVelocity.z) - if CleanUpUnitVelocityTotal < 1 then - if UnitData.CleanUpMoved then - if UnitData.CleanUpTime + 180 <= timer.getTime() then - self:T( { "CleanUp Scheduler", "Destroy due to not moving anymore " .. CleanUpUnitName } ) - self:_DestroyUnit(CleanUpUnit, CleanUpUnitName) - end - end - else - UnitData.CleanUpTime = timer.getTime() - UnitData.CleanUpMoved = true - end - end - - else - -- Do nothing ... - self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE - end - else - self:T( "CleanUp: Group " .. CleanUpUnitName .. " cannot be found in DCS RTE, removing ..." ) - self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE - end - end - self:T(CleanUpCount) - - return true -end - ---- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** -- --- **Spawn groups of units dynamically in your missions.** --- --- ![Banner Image](..\Presentations\SPAWN\SPAWN.JPG) --- --- === --- --- # 1) @{#SPAWN} class, extends @{Base#BASE} --- --- The @{#SPAWN} class allows to spawn dynamically new groups, based on pre-defined initialization settings, modifying the behaviour when groups are spawned. --- For each group to be spawned, within the mission editor, a group has to be created with the "late activation flag" set. We call this group the *"Spawn Template"* of the SPAWN object. --- A reference to this Spawn Template needs to be provided when constructing the SPAWN object, by indicating the name of the group within the mission editor in the constructor methods. --- --- Within the SPAWN object, there is an internal index that keeps track of which group from the internal group list was spawned. --- When new groups get spawned by using the SPAWN methods (see below), it will be validated whether the Limits (@{#SPAWN.Limit}) of the SPAWN object are not reached. --- When all is valid, a new group will be created by the spawning methods, and the internal index will be increased with 1. --- --- Regarding the name of new spawned groups, a _SpawnPrefix_ will be assigned for each new group created. --- If you want to have the Spawn Template name to be used as the _SpawnPrefix_ name, use the @{#SPAWN.New} constructor. --- However, when the @{#SPAWN.NewWithAlias} constructor was used, the Alias name will define the _SpawnPrefix_ name. --- Groups will follow the following naming structure when spawned at run-time: --- --- 1. Spawned groups will have the name _SpawnPrefix_#ggg, where ggg is a counter from 0 to 999. --- 2. Spawned units will have the name _SpawnPrefix_#ggg-uu, where uu is a counter from 0 to 99 for each new spawned unit belonging to the group. --- --- Some additional notes that need to be remembered: --- --- * Templates are actually groups defined within the mission editor, with the flag "Late Activation" set. As such, these groups are never used within the mission, but are used by the @{#SPAWN} module. --- * It is important to defined BEFORE you spawn new groups, a proper initialization of the SPAWN instance is done with the options you want to use. --- * When designing a mission, NEVER name groups using a "#" within the name of the group Spawn Template(s), or the SPAWN module logic won't work anymore. --- --- ## 1.1) SPAWN construction methods --- --- Create a new SPAWN object with the @{#SPAWN.New}() or the @{#SPAWN.NewWithAlias}() methods: --- --- * @{#SPAWN.New}(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition). --- * @{#SPAWN.NewWithAlias}(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition), and gives each spawned @{Group} an different name. --- --- It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned. --- The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons. --- So in principle, the group list will contain all parameters and configurations after initialization, and when groups get actually spawned, this spawning can be done quickly and efficient. --- --- ## 1.2) SPAWN initialization methods --- --- A spawn object will behave differently based on the usage of **initialization** methods, which all start with the **Init** prefix: --- --- * @{#SPAWN.InitKeepUnitNames}(): Keeps the unit names as defined within the mission editor, but note that anything after a # mark is ignored, and any spaces before and after the resulting name are removed. IMPORTANT! This method MUST be the first used after :New !!! --- * @{#SPAWN.InitLimit}(): Limits the amount of groups that can be alive at the same time and that can be dynamically spawned. --- * @{#SPAWN.InitRandomizeRoute}(): Randomize the routes of spawned groups, and for air groups also optionally the height. --- * @{#SPAWN.InitRandomizeTemplate}(): Randomize the group templates so that when a new group is spawned, a random group template is selected from one of the templates defined. --- * @{#SPAWN.InitUnControlled}(): Spawn plane groups uncontrolled. --- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a batallion in an array. --- * @{#SPAWN.InitRepeat}(): Re-spawn groups when they land at the home base. Similar methods are @{#SPAWN.InitRepeatOnLanding} and @{#SPAWN.InitRepeatOnEngineShutDown}. --- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens. --- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Unit}s in the @{Group} that is spawned within a **radius band**, given an Outer and Inner radius. --- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Zone}s that are declared using this function. Each zone can be given a probability factor. --- * @{#SPAWN.InitAIOn}(): Turns the AI On when spawning the new @{Group} object. --- * @{#SPAWN.InitAIOff}(): Turns the AI Off when spawning the new @{Group} object. --- * @{#SPAWN.InitAIOnOff}(): Turns the AI On or Off when spawning the new @{Group} object. --- --- ## 1.3) SPAWN spawning methods --- --- Groups can be spawned at different times and methods: --- --- * @{#SPAWN.Spawn}(): Spawn one new group based on the last spawned index. --- * @{#SPAWN.ReSpawn}(): Re-spawn a group based on a given index. --- * @{#SPAWN.SpawnScheduled}(): Spawn groups at scheduled but randomized intervals. You can use @{#SPAWN.SpawnScheduleStart}() and @{#SPAWN.SpawnScheduleStop}() to start and stop the schedule respectively. --- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air). --- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ). --- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}. --- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}. --- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}. --- --- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object. --- You can use the @{GROUP} object to do further actions with the DCSGroup. --- --- ## 1.4) Retrieve alive GROUPs spawned by the SPAWN object --- --- The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution. --- Every time a SPAWN object spawns a new GROUP object, a reference to the GROUP object is added to an internal table of GROUPS. --- SPAWN provides methods to iterate through that internal GROUP object reference table: --- --- * @{#SPAWN.GetFirstAliveGroup}(): Will find the first alive GROUP it has spawned, and return the alive GROUP object and the first Index where the first alive GROUP object has been found. --- * @{#SPAWN.GetNextAliveGroup}(): Will find the next alive GROUP object from a given Index, and return a reference to the alive GROUP object and the next Index where the alive GROUP has been found. --- * @{#SPAWN.GetLastAliveGroup}(): Will find the last alive GROUP object, and will return a reference to the last live GROUP object and the last Index where the last alive GROUP object has been found. --- --- You can use the methods @{#SPAWN.GetFirstAliveGroup}() and sequently @{#SPAWN.GetNextAliveGroup}() to iterate through the alive GROUPS within the SPAWN object, and to actions... See the respective methods for an example. --- The method @{#SPAWN.GetGroupFromIndex}() will return the GROUP object reference from the given Index, dead or alive... --- --- ## 1.5) SPAWN object cleaning --- --- Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive. --- In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't, --- and it may occur that no new groups are or can be spawned as limits are reached. --- To prevent this, a @{#SPAWN.InitCleanUp}() initialization method has been defined that will silently monitor the status of each spawned group. --- Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time. --- There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"... --- In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically. --- This models AI that has succesfully returned to their airbase, to restart their combat activities. --- Check the @{#SPAWN.InitCleanUp}() for further info. --- --- ## 1.6) Catch the @{Group} spawn event in a callback function! --- --- When using the SpawnScheduled method, new @{Group}s are created following the schedule timing parameters. --- When a new @{Group} is spawned, you maybe want to execute actions with that group spawned at the spawn event. --- To SPAWN class supports this functionality through the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method, which takes a function as a parameter that you can define locally. --- Whenever a new @{Group} is spawned, the given function is called, and the @{Group} that was just spawned, is given as a parameter. --- As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned @{Group} object. --- A coding example is provided at the description of the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-14: SPAWN:**InitKeepUnitNames()** added. --- 2017-03-14: SPAWN:**InitRandomizePosition( RandomizePosition, OuterRadious, InnerRadius )** added. --- --- 2017-02-04: SPAWN:InitUnControlled( **UnControlled** ) replaces SPAWN:InitUnControlled(). --- --- 2017-01-24: SPAWN:**InitAIOnOff( AIOnOff )** added. --- --- 2017-01-24: SPAWN:**InitAIOn()** added. --- --- 2017-01-24: SPAWN:**InitAIOff()** added. --- --- 2016-08-15: SPAWN:**InitCleanUp**( SpawnCleanUpInterval ) replaces SPAWN:_CleanUp_( SpawnCleanUpInterval ). --- --- 2016-08-15: SPAWN:**InitRandomizeZones( SpawnZones )** added. --- --- 2016-08-14: SPAWN:**OnSpawnGroup**( SpawnCallBackFunction, ... ) replaces SPAWN:_SpawnFunction_( SpawnCallBackFunction, ... ). --- --- 2016-08-14: SPAWN.SpawnInZone( Zone, __RandomizeGroup__, SpawnIndex ) replaces SpawnInZone( Zone, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ). --- --- 2016-08-14: SPAWN.SpawnFromVec3( Vec3, SpawnIndex ) replaces SpawnFromVec3( Vec3, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ): --- --- 2016-08-14: SPAWN.SpawnFromVec2( Vec2, SpawnIndex ) replaces SpawnFromVec2( Vec2, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ): --- --- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromUnit( SpawnUnit, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ): --- --- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromStatic( SpawnStatic, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ): --- --- 2016-08-14: SPAWN.**InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )** added: --- --- 2016-08-14: SPAWN.**Init**Limit( SpawnMaxUnitsAlive, SpawnMaxGroups ) replaces SPAWN._Limit_( SpawnMaxUnitsAlive, SpawnMaxGroups ): --- --- 2016-08-14: SPAWN.**Init**Array( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) replaces SPAWN._Array_( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ). --- --- 2016-08-14: SPAWN.**Init**RandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) replaces SPAWN._RandomizeRoute_( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ). --- --- 2016-08-14: SPAWN.**Init**RandomizeTemplate( SpawnTemplatePrefixTable ) replaces SPAWN._RandomizeTemplate_( SpawnTemplatePrefixTable ). --- --- 2016-08-14: SPAWN.**Init**UnControlled() replaces SPAWN._UnControlled_(). --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **Aaron**: Posed the idea for Group position randomization at SpawnInZone and make the Unit randomization separate from the Group randomization. --- * [**Entropy**](https://forums.eagle.ru/member.php?u=111471), **Afinegan**: Came up with the requirement for AIOnOff(). --- --- ### Authors: --- --- * **FlightControl**: Design & Programming --- --- @module Spawn - - - ---- SPAWN Class --- @type SPAWN --- @extends Core.Base#BASE --- @field ClassName --- @field #string SpawnTemplatePrefix --- @field #string SpawnAliasPrefix --- @field #number AliveUnits --- @field #number MaxAliveUnits --- @field #number SpawnIndex --- @field #number MaxAliveGroups --- @field #SPAWN.SpawnZoneTable SpawnZoneTable -SPAWN = { - ClassName = "SPAWN", - SpawnTemplatePrefix = nil, - SpawnAliasPrefix = nil, -} - - ---- @type SPAWN.SpawnZoneTable --- @list SpawnZone - - ---- Creates the main object to spawn a @{Group} defined in the DCS ME. --- @param #SPAWN self --- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix. --- @return #SPAWN --- @usage --- -- NATO helicopters engaging in the battle field. --- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ) --- @usage local Plane = SPAWN:New( "Plane" ) -- Creates a new local variable that can initiate new planes with the name "Plane#ddd" using the template "Plane" as defined within the ME. -function SPAWN:New( SpawnTemplatePrefix ) - local self = BASE:Inherit( self, BASE:New() ) -- #SPAWN - self:F( { SpawnTemplatePrefix } ) - - local TemplateGroup = Group.getByName( SpawnTemplatePrefix ) - if TemplateGroup then - self.SpawnTemplatePrefix = SpawnTemplatePrefix - self.SpawnIndex = 0 - self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. - self.AliveUnits = 0 -- Contains the counter how many units are currently alive - self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. - self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! - self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. - self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. - self.SpawnInitLimit = false -- By default, no InitLimit - self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. - self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. - self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. - self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. - self.AIOnOff = true -- The AI is on by default when spawning a group. - self.SpawnUnControlled = false - self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. - - self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. - else - error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) - end - - self:SetEventPriority( 5 ) - - return self -end - ---- Creates a new SPAWN instance to create new groups based on the defined template and using a new alias for each new group. --- @param #SPAWN self --- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. --- @param #string SpawnAliasPrefix is the name that will be given to the Group at runtime. --- @return #SPAWN --- @usage --- -- NATO helicopters engaging in the battle field. --- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' ) --- @usage local PlaneWithAlias = SPAWN:NewWithAlias( "Plane", "Bomber" ) -- Creates a new local variable that can instantiate new planes with the name "Bomber#ddd" using the template "Plane" as defined within the ME. -function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( { SpawnTemplatePrefix, SpawnAliasPrefix } ) - - local TemplateGroup = Group.getByName( SpawnTemplatePrefix ) - if TemplateGroup then - self.SpawnTemplatePrefix = SpawnTemplatePrefix - self.SpawnAliasPrefix = SpawnAliasPrefix - self.SpawnIndex = 0 - self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. - self.AliveUnits = 0 -- Contains the counter how many units are currently alive - self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. - self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! - self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. - self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. - self.SpawnInitLimit = false -- By default, no InitLimit - self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. - self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. - self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. - self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. - self.AIOnOff = true -- The AI is on by default when spawning a group. - self.SpawnUnControlled = false - self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. - - self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. - else - error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) - end - - self:SetEventPriority( 5 ) - - return self -end - - ---- Limits the Maximum amount of Units that can be alive at the same time, and the maximum amount of groups that can be spawned. --- Note that this method is exceptionally important to balance the performance of the mission. Depending on the machine etc, a mission can only process a maximum amount of units. --- If the time interval must be short, but there should not be more Units or Groups alive than a maximum amount of units, then this method should be used... --- When a @{#SPAWN.New} is executed and the limit of the amount of units alive is reached, then no new spawn will happen of the group, until some of these units of the spawn object will be destroyed. --- @param #SPAWN self --- @param #number SpawnMaxUnitsAlive The maximum amount of units that can be alive at runtime. --- @param #number SpawnMaxGroups The maximum amount of groups that can be spawned. When the limit is reached, then no more actual spawns will happen of the group. --- This parameter is useful to define a maximum amount of airplanes, ground troops, helicopters, ships etc within a supply area. --- This parameter accepts the value 0, which defines that there are no maximum group limits, but there are limits on the maximum of units that can be alive at the same time. --- @return #SPAWN self --- @usage --- -- NATO helicopters engaging in the battle field. --- -- This helicopter group consists of one Unit. So, this group will SPAWN maximum 2 groups simultaneously within the DCSRTE. --- -- There will be maximum 24 groups spawned during the whole mission lifetime. --- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitLimit( 2, 24 ) -function SPAWN:InitLimit( SpawnMaxUnitsAlive, SpawnMaxGroups ) - self:F( { self.SpawnTemplatePrefix, SpawnMaxUnitsAlive, SpawnMaxGroups } ) - - self.SpawnInitLimit = true - self.SpawnMaxUnitsAlive = SpawnMaxUnitsAlive -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. - self.SpawnMaxGroups = SpawnMaxGroups -- The maximum amount of groups that can be spawned. - - for SpawnGroupID = 1, self.SpawnMaxGroups do - self:_InitializeSpawnGroups( SpawnGroupID ) - end - - return self -end - ---- Keeps the unit names as defined within the mission editor, --- but note that anything after a # mark is ignored, --- and any spaces before and after the resulting name are removed. --- IMPORTANT! This method MUST be the first used after :New !!! --- @param #SPAWN self --- @return #SPAWN self -function SPAWN:InitKeepUnitNames() - self:F( ) - - self.SpawnInitKeepUnitNames = true - - return self -end - - ---- Randomizes the defined route of the SpawnTemplatePrefix group in the ME. This is very useful to define extra variation of the behaviour of groups. --- @param #SPAWN self --- @param #number SpawnStartPoint is the waypoint where the randomization begins. --- Note that the StartPoint = 0 equaling the point where the group is spawned. --- @param #number SpawnEndPoint is the waypoint where the randomization ends counting backwards. --- This parameter is useful to avoid randomization to end at a waypoint earlier than the last waypoint on the route. --- @param #number SpawnRadius is the radius in meters in which the randomization of the new waypoints, with the original waypoint of the original template located in the middle ... --- @param #number SpawnHeight (optional) Specifies the **additional** height in meters that can be added to the base height specified at each waypoint in the ME. --- @return #SPAWN --- @usage --- -- NATO helicopters engaging in the battle field. --- -- The KA-50 has waypoints Start point ( =0 or SP ), 1, 2, 3, 4, End point (= 5 or DP). --- -- Waypoints 2 and 3 will only be randomized. The others will remain on their original position with each new spawn of the helicopter. --- -- The randomization of waypoint 2 and 3 will take place within a radius of 2000 meters. --- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeRoute( 2, 2, 2000 ) -function SPAWN:InitRandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) - self:F( { self.SpawnTemplatePrefix, SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight } ) - - self.SpawnRandomizeRoute = true - self.SpawnRandomizeRouteStartPoint = SpawnStartPoint - self.SpawnRandomizeRouteEndPoint = SpawnEndPoint - self.SpawnRandomizeRouteRadius = SpawnRadius - self.SpawnRandomizeRouteHeight = SpawnHeight - - for GroupID = 1, self.SpawnMaxGroups do - self:_RandomizeRoute( GroupID ) - end - - return self -end - ---- Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens. --- @param #SPAWN self --- @param #boolean RandomizePosition If true, SPAWN will perform the randomization of the @{Group}s position between a given outer and inner radius. --- @param Dcs.DCSTypes#Distance OuterRadius (optional) The outer radius in meters where the new group will be spawned. --- @param Dcs.DCSTypes#Distance InnerRadius (optional) The inner radius in meters where the new group will NOT be spawned. --- @return #SPAWN -function SPAWN:InitRandomizePosition( RandomizePosition, OuterRadius, InnerRadius ) - self:F( { self.SpawnTemplatePrefix, RandomizePosition, OuterRadius, InnerRadius } ) - - self.SpawnRandomizePosition = RandomizePosition or false - self.SpawnRandomizePositionOuterRadius = OuterRadius or 0 - self.SpawnRandomizePositionInnerRadius = InnerRadius or 0 - - for GroupID = 1, self.SpawnMaxGroups do - self:_RandomizeRoute( GroupID ) - end - - return self -end - - ---- Randomizes the UNITs that are spawned within a radius band given an Outer and Inner radius. --- @param #SPAWN self --- @param #boolean RandomizeUnits If true, SPAWN will perform the randomization of the @{UNIT}s position within the group between a given outer and inner radius. --- @param Dcs.DCSTypes#Distance OuterRadius (optional) The outer radius in meters where the new group will be spawned. --- @param Dcs.DCSTypes#Distance InnerRadius (optional) The inner radius in meters where the new group will NOT be spawned. --- @return #SPAWN --- @usage --- -- NATO helicopters engaging in the battle field. --- -- The KA-50 has waypoints Start point ( =0 or SP ), 1, 2, 3, 4, End point (= 5 or DP). --- -- Waypoints 2 and 3 will only be randomized. The others will remain on their original position with each new spawn of the helicopter. --- -- The randomization of waypoint 2 and 3 will take place within a radius of 2000 meters. --- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeRoute( 2, 2, 2000 ) -function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius ) - self:F( { self.SpawnTemplatePrefix, RandomizeUnits, OuterRadius, InnerRadius } ) - - self.SpawnRandomizeUnits = RandomizeUnits or false - self.SpawnOuterRadius = OuterRadius or 0 - self.SpawnInnerRadius = InnerRadius or 0 - - for GroupID = 1, self.SpawnMaxGroups do - self:_RandomizeRoute( GroupID ) - end - - return self -end - ---- This method is rather complicated to understand. But I'll try to explain. --- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor, --- but they will all follow the same Template route and have the same prefix name. --- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group. --- @param #SPAWN self --- @param #string SpawnTemplatePrefixTable A table with the names of the groups defined within the mission editor, from which one will be choosen when a new group will be spawned. --- @return #SPAWN --- @usage --- -- NATO Tank Platoons invading Gori. --- -- Choose between 13 different 'US Tank Platoon' configurations for each new SPAWN the Group to be spawned for the --- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SpawnTemplatePrefixes. --- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and --- -- with a limit set of maximum 12 Units alive simulteneously and 150 Groups to be spawned during the whole mission. --- Spawn_US_Platoon = { 'US Tank Platoon 1', 'US Tank Platoon 2', 'US Tank Platoon 3', 'US Tank Platoon 4', 'US Tank Platoon 5', --- 'US Tank Platoon 6', 'US Tank Platoon 7', 'US Tank Platoon 8', 'US Tank Platoon 9', 'US Tank Platoon 10', --- 'US Tank Platoon 11', 'US Tank Platoon 12', 'US Tank Platoon 13' } --- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 ) --- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 ) --- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplate( Spawn_US_Platoon ):InitRandomizeRoute( 3, 3, 2000 ) -function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable ) - self:F( { self.SpawnTemplatePrefix, SpawnTemplatePrefixTable } ) - - self.SpawnTemplatePrefixTable = SpawnTemplatePrefixTable - self.SpawnRandomizeTemplate = true - - for SpawnGroupID = 1, self.SpawnMaxGroups do - self:_RandomizeTemplate( SpawnGroupID ) - end - - return self -end - ---TODO: Add example. ---- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types. --- @param #SPAWN self --- @param #table SpawnZoneTable A table with @{Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Zone}s objects. --- @return #SPAWN --- @usage --- -- NATO Tank Platoons invading Gori. --- -- Choose between 3 different zones for each new SPAWN the Group to be executed, regardless of the zone type. -function SPAWN:InitRandomizeZones( SpawnZoneTable ) - self:F( { self.SpawnTemplatePrefix, SpawnZoneTable } ) - - self.SpawnZoneTable = SpawnZoneTable - self.SpawnRandomizeZones = true - - for SpawnGroupID = 1, self.SpawnMaxGroups do - self:_RandomizeZones( SpawnGroupID ) - end - - return self -end - - - - - ---- For planes and helicopters, when these groups go home and land on their home airbases and farps, they normally would taxi to the parking spot, shut-down their engines and wait forever until the Group is removed by the runtime environment. --- This method is used to re-spawn automatically (so no extra call is needed anymore) the same group after it has landed. --- This will enable a spawned group to be re-spawned after it lands, until it is destroyed... --- Note: When the group is respawned, it will re-spawn from the original airbase where it took off. --- So ensure that the routes for groups that respawn, always return to the original airbase, or players may get confused ... --- @param #SPAWN self --- @return #SPAWN self --- @usage --- -- RU Su-34 - AI Ship Attack --- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically. --- SpawnRU_SU34 = SPAWN:New( 'TF1 RU Su-34 Krymsk@AI - Attack Ships' ):Schedule( 2, 3, 1800, 0.4 ):SpawnUncontrolled():InitRandomizeRoute( 1, 1, 3000 ):RepeatOnEngineShutDown() -function SPAWN:InitRepeat() - self:F( { self.SpawnTemplatePrefix, self.SpawnIndex } ) - - self.Repeat = true - self.RepeatOnEngineShutDown = false - self.RepeatOnLanding = true - - return self -end - ---- Respawn group after landing. --- @param #SPAWN self --- @return #SPAWN self -function SPAWN:InitRepeatOnLanding() - self:F( { self.SpawnTemplatePrefix } ) - - self:InitRepeat() - self.RepeatOnEngineShutDown = false - self.RepeatOnLanding = true - - return self -end - - ---- Respawn after landing when its engines have shut down. --- @param #SPAWN self --- @return #SPAWN self -function SPAWN:InitRepeatOnEngineShutDown() - self:F( { self.SpawnTemplatePrefix } ) - - self:InitRepeat() - self.RepeatOnEngineShutDown = true - self.RepeatOnLanding = false - - return self -end - - ---- CleanUp groups when they are still alive, but inactive. --- When groups are still alive and have become inactive due to damage and are unable to contribute anything, then this group will be removed at defined intervals in seconds. --- @param #SPAWN self --- @param #string SpawnCleanUpInterval The interval to check for inactive groups within seconds. --- @return #SPAWN self --- @usage Spawn_Helicopter:CleanUp( 20 ) -- CleanUp the spawning of the helicopters every 20 seconds when they become inactive. -function SPAWN:InitCleanUp( SpawnCleanUpInterval ) - self:F( { self.SpawnTemplatePrefix, SpawnCleanUpInterval } ) - - self.SpawnCleanUpInterval = SpawnCleanUpInterval - self.SpawnCleanUpTimeStamps = {} - - local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup() - self:T( { "CleanUp Scheduler:", SpawnGroup } ) - - --self.CleanUpFunction = routines.scheduleFunction( self._SpawnCleanUpScheduler, { self }, timer.getTime() + 1, SpawnCleanUpInterval ) - self.CleanUpScheduler = SCHEDULER:New( self, self._SpawnCleanUpScheduler, {}, 1, SpawnCleanUpInterval, 0.2 ) - return self -end - - - ---- Makes the groups visible before start (like a batallion). --- The method will take the position of the group as the first position in the array. --- @param #SPAWN self --- @param #number SpawnAngle The angle in degrees how the groups and each unit of the group will be positioned. --- @param #number SpawnWidth The amount of Groups that will be positioned on the X axis. --- @param #number SpawnDeltaX The space between each Group on the X-axis. --- @param #number SpawnDeltaY The space between each Group on the Y-axis. --- @return #SPAWN self --- @usage --- -- Define an array of Groups. --- Spawn_BE_Ground = SPAWN:New( 'BE Ground' ):InitLimit( 2, 24 ):InitArray( 90, "Diamond", 10, 100, 50 ) -function SPAWN:InitArray( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) - self:F( { self.SpawnTemplatePrefix, SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY } ) - - self.SpawnVisible = true -- When the first Spawn executes, all the Groups need to be made visible before start. - - local SpawnX = 0 - local SpawnY = 0 - local SpawnXIndex = 0 - local SpawnYIndex = 0 - - for SpawnGroupID = 1, self.SpawnMaxGroups do - self:T( { SpawnX, SpawnY, SpawnXIndex, SpawnYIndex } ) - - self.SpawnGroups[SpawnGroupID].Visible = true - self.SpawnGroups[SpawnGroupID].Spawned = false - - SpawnXIndex = SpawnXIndex + 1 - if SpawnWidth and SpawnWidth ~= 0 then - if SpawnXIndex >= SpawnWidth then - SpawnXIndex = 0 - SpawnYIndex = SpawnYIndex + 1 - end - end - - local SpawnRootX = self.SpawnGroups[SpawnGroupID].SpawnTemplate.x - local SpawnRootY = self.SpawnGroups[SpawnGroupID].SpawnTemplate.y - - self:_TranslateRotate( SpawnGroupID, SpawnRootX, SpawnRootY, SpawnX, SpawnY, SpawnAngle ) - - self.SpawnGroups[SpawnGroupID].SpawnTemplate.lateActivation = true - self.SpawnGroups[SpawnGroupID].SpawnTemplate.visible = true - - self.SpawnGroups[SpawnGroupID].Visible = true - - self:HandleEvent( EVENTS.Birth, self._OnBirth ) - self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) - if self.Repeat then - self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) - self:HandleEvent( EVENTS.Land, self._OnLand ) - end - if self.RepeatOnEngineShutDown then - self:HandleEvent( EVENTS.EngineShutdown, self._OnEngineShutDown ) - end - - self.SpawnGroups[SpawnGroupID].Group = _DATABASE:Spawn( self.SpawnGroups[SpawnGroupID].SpawnTemplate ) - - SpawnX = SpawnXIndex * SpawnDeltaX - SpawnY = SpawnYIndex * SpawnDeltaY - end - - return self -end - -do -- AI methods - --- Turns the AI On or Off for the @{Group} when spawning. - -- @param #SPAWN self - -- @param #boolean AIOnOff A value of true sets the AI On, a value of false sets the AI Off. - -- @return #SPAWN The SPAWN object - function SPAWN:InitAIOnOff( AIOnOff ) - - self.AIOnOff = AIOnOff - return self - end - - --- Turns the AI On for the @{Group} when spawning. - -- @param #SPAWN self - -- @return #SPAWN The SPAWN object - function SPAWN:InitAIOn() - - return self:InitAIOnOff( true ) - end - - --- Turns the AI Off for the @{Group} when spawning. - -- @param #SPAWN self - -- @return #SPAWN The SPAWN object - function SPAWN:InitAIOff() - - return self:InitAIOnOff( false ) - end - -end -- AI methods - ---- Will spawn a group based on the internal index. --- Note: Uses @{DATABASE} module defined in MOOSE. --- @param #SPAWN self --- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions. -function SPAWN:Spawn() - self:F( { self.SpawnTemplatePrefix, self.SpawnIndex, self.AliveUnits } ) - - return self:SpawnWithIndex( self.SpawnIndex + 1 ) -end - ---- Will re-spawn a group based on a given index. --- Note: Uses @{DATABASE} module defined in MOOSE. --- @param #SPAWN self --- @param #string SpawnIndex The index of the group to be spawned. --- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions. -function SPAWN:ReSpawn( SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, SpawnIndex } ) - - if not SpawnIndex then - SpawnIndex = 1 - end - --- TODO: This logic makes DCS crash and i don't know why (yet). - local SpawnGroup = self:GetGroupFromIndex( SpawnIndex ) - local WayPoints = SpawnGroup and SpawnGroup.WayPoints or nil - if SpawnGroup then - local SpawnDCSGroup = SpawnGroup:GetDCSObject() - if SpawnDCSGroup then - SpawnGroup:Destroy() - end - end - - local SpawnGroup = self:SpawnWithIndex( SpawnIndex ) - if SpawnGroup and WayPoints then - -- If there were WayPoints set, then Re-Execute those WayPoints! - SpawnGroup:WayPointInitialize( WayPoints ) - SpawnGroup:WayPointExecute( 1, 5 ) - end - - if SpawnGroup.ReSpawnFunction then - SpawnGroup:ReSpawnFunction() - end - - return SpawnGroup -end - ---- Will spawn a group with a specified index number. --- Uses @{DATABASE} global object defined in MOOSE. --- @param #SPAWN self --- @param #string SpawnIndex The index of the group to be spawned. --- @return Wrapper.Group#GROUP The group that was spawned. You can use this group for further actions. -function SPAWN:SpawnWithIndex( SpawnIndex ) - self:F2( { SpawnTemplatePrefix = self.SpawnTemplatePrefix, SpawnIndex = SpawnIndex, AliveUnits = self.AliveUnits, SpawnMaxGroups = self.SpawnMaxGroups } ) - - if self:_GetSpawnIndex( SpawnIndex ) then - - if self.SpawnGroups[self.SpawnIndex].Visible then - self.SpawnGroups[self.SpawnIndex].Group:Activate() - else - - local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate - self:T( SpawnTemplate.name ) - - if SpawnTemplate then - - local PointVec3 = POINT_VEC3:New( SpawnTemplate.route.points[1].x, SpawnTemplate.route.points[1].alt, SpawnTemplate.route.points[1].y ) - self:T( { "Current point of ", self.SpawnTemplatePrefix, PointVec3 } ) - - -- If RandomizePosition, then Randomize the formation in the zone band, keeping the template. - if self.SpawnRandomizePosition then - local RandomVec2 = PointVec3:GetRandomVec2InRadius( self.SpawnRandomizePositionOuterRadius, self.SpawnRandomizePositionInnerRadius ) - local CurrentX = SpawnTemplate.units[1].x - local CurrentY = SpawnTemplate.units[1].y - SpawnTemplate.x = RandomVec2.x - SpawnTemplate.y = RandomVec2.y - for UnitID = 1, #SpawnTemplate.units do - SpawnTemplate.units[UnitID].x = SpawnTemplate.units[UnitID].x + ( RandomVec2.x - CurrentX ) - SpawnTemplate.units[UnitID].y = SpawnTemplate.units[UnitID].y + ( RandomVec2.y - CurrentY ) - self:T( 'SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) - end - end - - -- If RandomizeUnits, then Randomize the formation at the start point. - if self.SpawnRandomizeUnits then - for UnitID = 1, #SpawnTemplate.units do - local RandomVec2 = PointVec3:GetRandomVec2InRadius( self.SpawnOuterRadius, self.SpawnInnerRadius ) - SpawnTemplate.units[UnitID].x = RandomVec2.x - SpawnTemplate.units[UnitID].y = RandomVec2.y - self:T( 'SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) - end - end - - if SpawnTemplate.CategoryID == Group.Category.HELICOPTER or SpawnTemplate.CategoryID == Group.Category.AIRPLANE then - if SpawnTemplate.route.points[1].type == "TakeOffParking" then - SpawnTemplate.uncontrolled = self.SpawnUnControlled - end - end - end - - self:HandleEvent( EVENTS.Birth, self._OnBirth ) - self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) - if self.Repeat then - self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) - self:HandleEvent( EVENTS.Land, self._OnLand ) - end - if self.RepeatOnEngineShutDown then - self:HandleEvent( EVENTS.EngineShutdown, self._OnEngineShutDown ) - end - - self.SpawnGroups[self.SpawnIndex].Group = _DATABASE:Spawn( SpawnTemplate ) - - local SpawnGroup = self.SpawnGroups[self.SpawnIndex].Group -- Wrapper.Group#GROUP - - --TODO: Need to check if this function doesn't need to be scheduled, as the group may not be immediately there! - if SpawnGroup then - - SpawnGroup:SetAIOnOff( self.AIOnOff ) - end - - self:T3( SpawnTemplate.name ) - - -- If there is a SpawnFunction hook defined, call it. - if self.SpawnFunctionHook then - self.SpawnFunctionHook( self.SpawnGroups[self.SpawnIndex].Group, unpack( self.SpawnFunctionArguments ) ) - end - -- TODO: Need to fix this by putting an "R" in the name of the group when the group repeats. - --if self.Repeat then - -- _DATABASE:SetStatusGroup( SpawnTemplate.name, "ReSpawn" ) - --end - end - - self.SpawnGroups[self.SpawnIndex].Spawned = true - return self.SpawnGroups[self.SpawnIndex].Group - else - --self:E( { self.SpawnTemplatePrefix, "No more Groups to Spawn:", SpawnIndex, self.SpawnMaxGroups } ) - end - - return nil -end - ---- Spawns new groups at varying time intervals. --- This is useful if you want to have continuity within your missions of certain (AI) groups to be present (alive) within your missions. --- @param #SPAWN self --- @param #number SpawnTime The time interval defined in seconds between each new spawn of new groups. --- @param #number SpawnTimeVariation The variation to be applied on the defined time interval between each new spawn. --- The variation is a number between 0 and 1, representing the %-tage of variation to be applied on the time interval. --- @return #SPAWN self --- @usage --- -- NATO helicopters engaging in the battle field. --- -- The time interval is set to SPAWN new helicopters between each 600 seconds, with a time variation of 50%. --- -- The time variation in this case will be between 450 seconds and 750 seconds. --- -- This is calculated as follows: --- -- Low limit: 600 * ( 1 - 0.5 / 2 ) = 450 --- -- High limit: 600 * ( 1 + 0.5 / 2 ) = 750 --- -- Between these two values, a random amount of seconds will be choosen for each new spawn of the helicopters. --- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):Schedule( 600, 0.5 ) -function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation ) - self:F( { SpawnTime, SpawnTimeVariation } ) - - if SpawnTime ~= nil and SpawnTimeVariation ~= nil then - self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, 1, SpawnTime, SpawnTimeVariation ) - end - - return self -end - ---- Will re-start the spawning scheduler. --- Note: This method is only required to be called when the schedule was stopped. -function SPAWN:SpawnScheduleStart() - self:F( { self.SpawnTemplatePrefix } ) - - self.SpawnScheduler:Start() -end - ---- Will stop the scheduled spawning scheduler. -function SPAWN:SpawnScheduleStop() - self:F( { self.SpawnTemplatePrefix } ) - - self.SpawnScheduler:Stop() -end - - ---- Allows to place a CallFunction hook when a new group spawns. --- The provided method will be called when a new group is spawned, including its given parameters. --- The first parameter of the SpawnFunction is the @{Group#GROUP} that was spawned. --- @param #SPAWN self --- @param #function SpawnCallBackFunction The function to be called when a group spawns. --- @param SpawnFunctionArguments A random amount of arguments to be provided to the function when the group spawns. --- @return #SPAWN --- @usage --- -- Declare SpawnObject and call a function when a new Group is spawned. --- local SpawnObject = SPAWN --- :New( "SpawnObject" ) --- :InitLimit( 2, 10 ) --- :OnSpawnGroup( --- function( SpawnGroup ) --- SpawnGroup:E( "I am spawned" ) --- end --- ) --- :SpawnScheduled( 300, 0.3 ) -function SPAWN:OnSpawnGroup( SpawnCallBackFunction, ... ) - self:F( "OnSpawnGroup" ) - - self.SpawnFunctionHook = SpawnCallBackFunction - self.SpawnFunctionArguments = {} - if arg then - self.SpawnFunctionArguments = arg - end - - return self -end - - ---- Will spawn a group from a Vec3 in 3D space. --- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes. --- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. --- You can use the returned group to further define the route to be followed. --- @param #SPAWN self --- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 coordinates where to spawn the group. --- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. --- @return Wrapper.Group#GROUP that was spawned. --- @return #nil Nothing was spawned. -function SPAWN:SpawnFromVec3( Vec3, SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, Vec3, SpawnIndex } ) - - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - self:T2(PointVec3) - - if SpawnIndex then - else - SpawnIndex = self.SpawnIndex + 1 - end - - if self:_GetSpawnIndex( SpawnIndex ) then - - local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate - - if SpawnTemplate then - - self:T( { "Current point of ", self.SpawnTemplatePrefix, Vec3 } ) - - -- Translate the position of the Group Template to the Vec3. - for UnitID = 1, #SpawnTemplate.units do - self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) - local UnitTemplate = SpawnTemplate.units[UnitID] - local SX = UnitTemplate.x - local SY = UnitTemplate.y - local BX = SpawnTemplate.route.points[1].x - local BY = SpawnTemplate.route.points[1].y - local TX = Vec3.x + ( SX - BX ) - local TY = Vec3.z + ( SY - BY ) - SpawnTemplate.units[UnitID].x = TX - SpawnTemplate.units[UnitID].y = TY - SpawnTemplate.units[UnitID].alt = Vec3.y - self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) - end - - SpawnTemplate.route.points[1].x = Vec3.x - SpawnTemplate.route.points[1].y = Vec3.z - SpawnTemplate.route.points[1].alt = Vec3.y - - SpawnTemplate.x = Vec3.x - SpawnTemplate.y = Vec3.z - - return self:SpawnWithIndex( self.SpawnIndex ) - end - end - - return nil -end - ---- Will spawn a group from a Vec2 in 3D space. --- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles. --- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. --- You can use the returned group to further define the route to be followed. --- @param #SPAWN self --- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 coordinates where to spawn the group. --- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. --- @return Wrapper.Group#GROUP that was spawned. --- @return #nil Nothing was spawned. -function SPAWN:SpawnFromVec2( Vec2, SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, Vec2, SpawnIndex } ) - - local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) - return self:SpawnFromVec3( PointVec2:GetVec3(), SpawnIndex ) -end - - ---- Will spawn a group from a hosting unit. This method is mostly advisable to be used if you want to simulate spawning from air units, like helicopters, which are dropping infantry into a defined Landing Zone. --- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. --- You can use the returned group to further define the route to be followed. --- @param #SPAWN self --- @param Wrapper.Unit#UNIT HostUnit The air or ground unit dropping or unloading the group. --- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. --- @return Wrapper.Group#GROUP that was spawned. --- @return #nil Nothing was spawned. -function SPAWN:SpawnFromUnit( HostUnit, SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, HostUnit, SpawnIndex } ) - - if HostUnit and HostUnit:IsAlive() then -- and HostUnit:getUnit(1):inAir() == false then - return self:SpawnFromVec3( HostUnit:GetVec3(), SpawnIndex ) - end - - return nil -end - ---- Will spawn a group from a hosting static. This method is mostly advisable to be used if you want to simulate spawning from buldings and structures (static buildings). --- You can use the returned group to further define the route to be followed. --- @param #SPAWN self --- @param Wrapper.Static#STATIC HostStatic The static dropping or unloading the group. --- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. --- @return Wrapper.Group#GROUP that was spawned. --- @return #nil Nothing was spawned. -function SPAWN:SpawnFromStatic( HostStatic, SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, HostStatic, SpawnIndex } ) - - if HostStatic and HostStatic:IsAlive() then - return self:SpawnFromVec3( HostStatic:GetVec3(), SpawnIndex ) - end - - return nil -end - ---- Will spawn a Group within a given @{Zone}. --- The @{Zone} can be of any type derived from @{Zone#ZONE_BASE}. --- Once the @{Group} is spawned within the zone, the @{Group} will continue on its route. --- The **first waypoint** (where the group is spawned) is replaced with the zone location coordinates. --- @param #SPAWN self --- @param Core.Zone#ZONE Zone The zone where the group is to be spawned. --- @param #boolean RandomizeGroup (optional) Randomization of the @{Group} position in the zone. --- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. --- @return Wrapper.Group#GROUP that was spawned. --- @return #nil when nothing was spawned. -function SPAWN:SpawnInZone( Zone, RandomizeGroup, SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, Zone, RandomizeGroup, SpawnIndex } ) - - if Zone then - if RandomizeGroup then - return self:SpawnFromVec2( Zone:GetRandomVec2(), SpawnIndex ) - else - return self:SpawnFromVec2( Zone:GetVec2(), SpawnIndex ) - end - end - - return nil -end - ---- (**AIR**) Will spawn a plane group in UnControlled or Controlled mode... --- This will be similar to the uncontrolled flag setting in the ME. --- You can use UnControlled mode to simulate planes startup and ready for take-off but aren't moving (yet). --- ReSpawn the plane in Controlled mode, and the plane will move... --- @param #SPAWN self --- @param #boolean UnControlled true if UnControlled, false if Controlled. --- @return #SPAWN self -function SPAWN:InitUnControlled( UnControlled ) - self:F2( { self.SpawnTemplatePrefix, UnControlled } ) - - self.SpawnUnControlled = UnControlled - - for SpawnGroupID = 1, self.SpawnMaxGroups do - self.SpawnGroups[SpawnGroupID].UnControlled = UnControlled - end - - return self -end - - - ---- Will return the SpawnGroupName either with with a specific count number or without any count. --- @param #SPAWN self --- @param #number SpawnIndex Is the number of the Group that is to be spawned. --- @return #string SpawnGroupName -function SPAWN:SpawnGroupName( SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, SpawnIndex } ) - - local SpawnPrefix = self.SpawnTemplatePrefix - if self.SpawnAliasPrefix then - SpawnPrefix = self.SpawnAliasPrefix - end - - if SpawnIndex then - local SpawnName = string.format( '%s#%03d', SpawnPrefix, SpawnIndex ) - self:T( SpawnName ) - return SpawnName - else - self:T( SpawnPrefix ) - return SpawnPrefix - end - -end - ---- Will find the first alive @{Group} it has spawned, and return the alive @{Group} object and the first Index where the first alive @{Group} object has been found. --- @param #SPAWN self --- @return Wrapper.Group#GROUP, #number The @{Group} object found, the new Index where the group was found. --- @return #nil, #nil When no group is found, #nil is returned. --- @usage --- -- Find the first alive @{Group} object of the SpawnPlanes SPAWN object @{Group} collection that it has spawned during the mission. --- local GroupPlane, Index = SpawnPlanes:GetFirstAliveGroup() --- while GroupPlane ~= nil do --- -- Do actions with the GroupPlane object. --- GroupPlane, Index = SpawnPlanes:GetNextAliveGroup( Index ) --- end -function SPAWN:GetFirstAliveGroup() - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) - - for SpawnIndex = 1, self.SpawnCount do - local SpawnGroup = self:GetGroupFromIndex( SpawnIndex ) - if SpawnGroup and SpawnGroup:IsAlive() then - return SpawnGroup, SpawnIndex - end - end - - return nil, nil -end - - ---- Will find the next alive @{Group} object from a given Index, and return a reference to the alive @{Group} object and the next Index where the alive @{Group} has been found. --- @param #SPAWN self --- @param #number SpawnIndexStart A Index holding the start position to search from. This method can also be used to find the first alive @{Group} object from the given Index. --- @return Wrapper.Group#GROUP, #number The next alive @{Group} object found, the next Index where the next alive @{Group} object was found. --- @return #nil, #nil When no alive @{Group} object is found from the start Index position, #nil is returned. --- @usage --- -- Find the first alive @{Group} object of the SpawnPlanes SPAWN object @{Group} collection that it has spawned during the mission. --- local GroupPlane, Index = SpawnPlanes:GetFirstAliveGroup() --- while GroupPlane ~= nil do --- -- Do actions with the GroupPlane object. --- GroupPlane, Index = SpawnPlanes:GetNextAliveGroup( Index ) --- end -function SPAWN:GetNextAliveGroup( SpawnIndexStart ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnIndexStart } ) - - SpawnIndexStart = SpawnIndexStart + 1 - for SpawnIndex = SpawnIndexStart, self.SpawnCount do - local SpawnGroup = self:GetGroupFromIndex( SpawnIndex ) - if SpawnGroup and SpawnGroup:IsAlive() then - return SpawnGroup, SpawnIndex - end - end - - return nil, nil -end - ---- Will find the last alive @{Group} object, and will return a reference to the last live @{Group} object and the last Index where the last alive @{Group} object has been found. --- @param #SPAWN self --- @return Wrapper.Group#GROUP, #number The last alive @{Group} object found, the last Index where the last alive @{Group} object was found. --- @return #nil, #nil When no alive @{Group} object is found, #nil is returned. --- @usage --- -- Find the last alive @{Group} object of the SpawnPlanes SPAWN object @{Group} collection that it has spawned during the mission. --- local GroupPlane, Index = SpawnPlanes:GetLastAliveGroup() --- if GroupPlane then -- GroupPlane can be nil!!! --- -- Do actions with the GroupPlane object. --- end -function SPAWN:GetLastAliveGroup() - self:F( { self.SpawnTemplatePrefixself.SpawnAliasPrefix } ) - - self.SpawnIndex = self:_GetLastIndex() - for SpawnIndex = self.SpawnIndex, 1, -1 do - local SpawnGroup = self:GetGroupFromIndex( SpawnIndex ) - if SpawnGroup and SpawnGroup:IsAlive() then - self.SpawnIndex = SpawnIndex - return SpawnGroup - end - end - - self.SpawnIndex = nil - return nil -end - - - ---- Get the group from an index. --- Returns the group from the SpawnGroups list. --- If no index is given, it will return the first group in the list. --- @param #SPAWN self --- @param #number SpawnIndex The index of the group to return. --- @return Wrapper.Group#GROUP self -function SPAWN:GetGroupFromIndex( SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnIndex } ) - - if not SpawnIndex then - SpawnIndex = 1 - end - - if self.SpawnGroups and self.SpawnGroups[SpawnIndex] then - local SpawnGroup = self.SpawnGroups[SpawnIndex].Group - return SpawnGroup - else - return nil - end -end - - ---- Return the prefix of a SpawnUnit. --- The method will search for a #-mark, and will return the text before the #-mark. --- It will return nil of no prefix was found. --- @param #SPAWN self --- @param Dcs.DCSWrapper.Unit#UNIT DCSUnit The @{DCSUnit} to be searched. --- @return #string The prefix --- @return #nil Nothing found -function SPAWN:_GetPrefixFromGroup( SpawnGroup ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) - - local GroupName = SpawnGroup:GetName() - if GroupName then - local SpawnPrefix = string.match( GroupName, ".*#" ) - if SpawnPrefix then - SpawnPrefix = SpawnPrefix:sub( 1, -2 ) - end - return SpawnPrefix - end - - return nil -end - - ---- Get the index from a given group. --- The function will search the name of the group for a #, and will return the number behind the #-mark. -function SPAWN:GetSpawnIndexFromGroup( SpawnGroup ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) - - local IndexString = string.match( SpawnGroup:GetName(), "#(%d*)$" ):sub( 2 ) - local Index = tonumber( IndexString ) - - self:T3( IndexString, Index ) - return Index - -end - ---- Return the last maximum index that can be used. -function SPAWN:_GetLastIndex() - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) - - return self.SpawnMaxGroups -end - ---- Initalize the SpawnGroups collection. --- @param #SPAWN self -function SPAWN:_InitializeSpawnGroups( SpawnIndex ) - self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnIndex } ) - - if not self.SpawnGroups[SpawnIndex] then - self.SpawnGroups[SpawnIndex] = {} - self.SpawnGroups[SpawnIndex].Visible = false - self.SpawnGroups[SpawnIndex].Spawned = false - self.SpawnGroups[SpawnIndex].UnControlled = false - self.SpawnGroups[SpawnIndex].SpawnTime = 0 - - self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix = self.SpawnTemplatePrefix - self.SpawnGroups[SpawnIndex].SpawnTemplate = self:_Prepare( self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix, SpawnIndex ) - end - - self:_RandomizeTemplate( SpawnIndex ) - self:_RandomizeRoute( SpawnIndex ) - --self:_TranslateRotate( SpawnIndex ) - - return self.SpawnGroups[SpawnIndex] -end - - - ---- Gets the CategoryID of the Group with the given SpawnPrefix -function SPAWN:_GetGroupCategoryID( SpawnPrefix ) - local TemplateGroup = Group.getByName( SpawnPrefix ) - - if TemplateGroup then - return TemplateGroup:getCategory() - else - return nil - end -end - ---- Gets the CoalitionID of the Group with the given SpawnPrefix -function SPAWN:_GetGroupCoalitionID( SpawnPrefix ) - local TemplateGroup = Group.getByName( SpawnPrefix ) - - if TemplateGroup then - return TemplateGroup:getCoalition() - else - return nil - end -end - ---- Gets the CountryID of the Group with the given SpawnPrefix -function SPAWN:_GetGroupCountryID( SpawnPrefix ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnPrefix } ) - - local TemplateGroup = Group.getByName( SpawnPrefix ) - - if TemplateGroup then - local TemplateUnits = TemplateGroup:getUnits() - return TemplateUnits[1]:getCountry() - else - return nil - end -end - ---- Gets the Group Template from the ME environment definition. --- This method used the @{DATABASE} object, which contains ALL initial and new spawned object in MOOSE. --- @param #SPAWN self --- @param #string SpawnTemplatePrefix --- @return @SPAWN self -function SPAWN:_GetTemplate( SpawnTemplatePrefix ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnTemplatePrefix } ) - - local SpawnTemplate = nil - - SpawnTemplate = routines.utils.deepCopy( _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template ) - - if SpawnTemplate == nil then - error( 'No Template returned for SpawnTemplatePrefix = ' .. SpawnTemplatePrefix ) - end - - --SpawnTemplate.SpawnCoalitionID = self:_GetGroupCoalitionID( SpawnTemplatePrefix ) - --SpawnTemplate.SpawnCategoryID = self:_GetGroupCategoryID( SpawnTemplatePrefix ) - --SpawnTemplate.SpawnCountryID = self:_GetGroupCountryID( SpawnTemplatePrefix ) - - self:T3( { SpawnTemplate } ) - return SpawnTemplate -end - ---- Prepares the new Group Template. --- @param #SPAWN self --- @param #string SpawnTemplatePrefix --- @param #number SpawnIndex --- @return #SPAWN self -function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) - - local SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) - SpawnTemplate.name = self:SpawnGroupName( SpawnIndex ) - - SpawnTemplate.groupId = nil - --SpawnTemplate.lateActivation = false - SpawnTemplate.lateActivation = false - - if SpawnTemplate.CategoryID == Group.Category.GROUND then - self:T3( "For ground units, visible needs to be false..." ) - SpawnTemplate.visible = false - end - - if self.SpawnInitKeepUnitNames == false then - for UnitID = 1, #SpawnTemplate.units do - SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID ) - SpawnTemplate.units[UnitID].unitId = nil - end - else - for UnitID = 1, #SpawnTemplate.units do - local UnitPrefix, Rest = string.match( SpawnTemplate.units[UnitID].name, "^([^#]+)#?" ):gsub( "^%s*(.-)%s*$", "%1" ) - self:T( { UnitPrefix, Rest } ) - - SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID ) - SpawnTemplate.units[UnitID].unitId = nil - end - end - - self:T3( { "Template:", SpawnTemplate } ) - return SpawnTemplate - -end - ---- Private method randomizing the routes. --- @param #SPAWN self --- @param #number SpawnIndex The index of the group to be spawned. --- @return #SPAWN -function SPAWN:_RandomizeRoute( SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnRandomizeRoute, self.SpawnRandomizeRouteStartPoint, self.SpawnRandomizeRouteEndPoint, self.SpawnRandomizeRouteRadius } ) - - if self.SpawnRandomizeRoute then - local SpawnTemplate = self.SpawnGroups[SpawnIndex].SpawnTemplate - local RouteCount = #SpawnTemplate.route.points - - for t = self.SpawnRandomizeRouteStartPoint + 1, ( RouteCount - self.SpawnRandomizeRouteEndPoint ) do - - SpawnTemplate.route.points[t].x = SpawnTemplate.route.points[t].x + math.random( self.SpawnRandomizeRouteRadius * -1, self.SpawnRandomizeRouteRadius ) - SpawnTemplate.route.points[t].y = SpawnTemplate.route.points[t].y + math.random( self.SpawnRandomizeRouteRadius * -1, self.SpawnRandomizeRouteRadius ) - - -- Manage randomization of altitude for airborne units ... - if SpawnTemplate.CategoryID == Group.Category.AIRPLANE or SpawnTemplate.CategoryID == Group.Category.HELICOPTER then - if SpawnTemplate.route.points[t].alt and self.SpawnRandomizeRouteHeight then - SpawnTemplate.route.points[t].alt = SpawnTemplate.route.points[t].alt + math.random( 1, self.SpawnRandomizeRouteHeight ) - end - else - SpawnTemplate.route.points[t].alt = nil - end - - self:T( 'SpawnTemplate.route.points[' .. t .. '].x = ' .. SpawnTemplate.route.points[t].x .. ', SpawnTemplate.route.points[' .. t .. '].y = ' .. SpawnTemplate.route.points[t].y ) - end - end - - self:_RandomizeZones( SpawnIndex ) - - return self -end - ---- Private method that randomizes the template of the group. --- @param #SPAWN self --- @param #number SpawnIndex --- @return #SPAWN self -function SPAWN:_RandomizeTemplate( SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnRandomizeTemplate } ) - - if self.SpawnRandomizeTemplate then - self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix = self.SpawnTemplatePrefixTable[ math.random( 1, #self.SpawnTemplatePrefixTable ) ] - self.SpawnGroups[SpawnIndex].SpawnTemplate = self:_Prepare( self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix, SpawnIndex ) - self.SpawnGroups[SpawnIndex].SpawnTemplate.route = routines.utils.deepCopy( self.SpawnTemplate.route ) - self.SpawnGroups[SpawnIndex].SpawnTemplate.x = self.SpawnTemplate.x - self.SpawnGroups[SpawnIndex].SpawnTemplate.y = self.SpawnTemplate.y - self.SpawnGroups[SpawnIndex].SpawnTemplate.start_time = self.SpawnTemplate.start_time - local OldX = self.SpawnGroups[SpawnIndex].SpawnTemplate.units[1].x - local OldY = self.SpawnGroups[SpawnIndex].SpawnTemplate.units[1].y - for UnitID = 1, #self.SpawnGroups[SpawnIndex].SpawnTemplate.units do - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].heading = self.SpawnTemplate.units[1].heading - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x = self.SpawnTemplate.units[1].x + ( self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].x - OldX ) - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y = self.SpawnTemplate.units[1].y + ( self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].y - OldY ) - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[UnitID].alt = self.SpawnTemplate.units[1].alt - end - end - - self:_RandomizeRoute( SpawnIndex ) - - return self -end - ---- Private method that randomizes the @{Zone}s where the Group will be spawned. --- @param #SPAWN self --- @param #number SpawnIndex --- @return #SPAWN self -function SPAWN:_RandomizeZones( SpawnIndex ) - self:F( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnRandomizeZones } ) - - if self.SpawnRandomizeZones then - local SpawnZone = nil -- Core.Zone#ZONE_BASE - while not SpawnZone do - self:T( { SpawnZoneTableCount = #self.SpawnZoneTable, self.SpawnZoneTable } ) - local ZoneID = math.random( #self.SpawnZoneTable ) - self:T( ZoneID ) - SpawnZone = self.SpawnZoneTable[ ZoneID ]:GetZoneMaybe() - end - - self:T( "Preparing Spawn in Zone", SpawnZone:GetName() ) - - local SpawnVec2 = SpawnZone:GetRandomVec2() - - self:T( { SpawnVec2 = SpawnVec2 } ) - - local SpawnTemplate = self.SpawnGroups[SpawnIndex].SpawnTemplate - - self:T( { Route = SpawnTemplate.route } ) - - for UnitID = 1, #SpawnTemplate.units do - local UnitTemplate = SpawnTemplate.units[UnitID] - self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y ) - local SX = UnitTemplate.x - local SY = UnitTemplate.y - local BX = SpawnTemplate.route.points[1].x - local BY = SpawnTemplate.route.points[1].y - local TX = SpawnVec2.x + ( SX - BX ) - local TY = SpawnVec2.y + ( SY - BY ) - UnitTemplate.x = TX - UnitTemplate.y = TY - -- TODO: Manage altitude based on landheight... - --SpawnTemplate.units[UnitID].alt = SpawnVec2: - self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y ) - end - SpawnTemplate.x = SpawnVec2.x - SpawnTemplate.y = SpawnVec2.y - SpawnTemplate.route.points[1].x = SpawnVec2.x - SpawnTemplate.route.points[1].y = SpawnVec2.y - end - - return self - -end - -function SPAWN:_TranslateRotate( SpawnIndex, SpawnRootX, SpawnRootY, SpawnX, SpawnY, SpawnAngle ) - self:F( { self.SpawnTemplatePrefix, SpawnIndex, SpawnRootX, SpawnRootY, SpawnX, SpawnY, SpawnAngle } ) - - -- Translate - local TranslatedX = SpawnX - local TranslatedY = SpawnY - - -- Rotate - -- From Wikipedia: https://en.wikipedia.org/wiki/Rotation_matrix#Common_rotations - -- x' = x \cos \theta - y \sin \theta\ - -- y' = x \sin \theta + y \cos \theta\ - local RotatedX = - TranslatedX * math.cos( math.rad( SpawnAngle ) ) - + TranslatedY * math.sin( math.rad( SpawnAngle ) ) - local RotatedY = TranslatedX * math.sin( math.rad( SpawnAngle ) ) - + TranslatedY * math.cos( math.rad( SpawnAngle ) ) - - -- Assign - self.SpawnGroups[SpawnIndex].SpawnTemplate.x = SpawnRootX - RotatedX - self.SpawnGroups[SpawnIndex].SpawnTemplate.y = SpawnRootY + RotatedY - - - local SpawnUnitCount = table.getn( self.SpawnGroups[SpawnIndex].SpawnTemplate.units ) - for u = 1, SpawnUnitCount do - - -- Translate - local TranslatedX = SpawnX - local TranslatedY = SpawnY - 10 * ( u - 1 ) - - -- Rotate - local RotatedX = - TranslatedX * math.cos( math.rad( SpawnAngle ) ) - + TranslatedY * math.sin( math.rad( SpawnAngle ) ) - local RotatedY = TranslatedX * math.sin( math.rad( SpawnAngle ) ) - + TranslatedY * math.cos( math.rad( SpawnAngle ) ) - - -- Assign - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[u].x = SpawnRootX - RotatedX - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[u].y = SpawnRootY + RotatedY - self.SpawnGroups[SpawnIndex].SpawnTemplate.units[u].heading = self.SpawnGroups[SpawnIndex].SpawnTemplate.units[u].heading + math.rad( SpawnAngle ) - end - - return self -end - ---- Get the next index of the groups to be spawned. This method is complicated, as it is used at several spaces. -function SPAWN:_GetSpawnIndex( SpawnIndex ) - self:F2( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnMaxGroups, self.SpawnMaxUnitsAlive, self.AliveUnits, #self.SpawnTemplate.units } ) - - if ( self.SpawnMaxGroups == 0 ) or ( SpawnIndex <= self.SpawnMaxGroups ) then - if ( self.SpawnMaxUnitsAlive == 0 ) or ( self.AliveUnits + #self.SpawnTemplate.units <= self.SpawnMaxUnitsAlive ) or self.UnControlled == true then - if SpawnIndex and SpawnIndex >= self.SpawnCount + 1 then - self.SpawnCount = self.SpawnCount + 1 - SpawnIndex = self.SpawnCount - end - self.SpawnIndex = SpawnIndex - if not self.SpawnGroups[self.SpawnIndex] then - self:_InitializeSpawnGroups( self.SpawnIndex ) - end - else - return nil - end - else - return nil - end - - return self.SpawnIndex -end - - --- TODO Need to delete this... _DATABASE does this now ... - ---- @param #SPAWN self --- @param Core.Event#EVENTDATA EventData -function SPAWN:_OnBirth( EventData ) - self:F( self.SpawnTemplatePrefix ) - - local SpawnGroup = EventData.IniGroup - - if SpawnGroup then - local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self.AliveUnits = self.AliveUnits + 1 - self:T( "Alive Units: " .. self.AliveUnits ) - end - end - -end - ---- Obscolete --- @todo Need to delete this... _DATABASE does this now ... - ---- @param #SPAWN self --- @param Core.Event#EVENTDATA EventData -function SPAWN:_OnDeadOrCrash( EventData ) - self:F( self.SpawnTemplatePrefix ) - - local SpawnGroup = EventData.IniGroup - - if SpawnGroup then - local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "Dead event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self.AliveUnits = self.AliveUnits - 1 - self:T( "Alive Units: " .. self.AliveUnits ) - end - end -end - ---- Will detect AIR Units taking off... When the event takes place, the spawned Group is registered as airborne... --- This is needed to ensure that Re-SPAWNing only is done for landed AIR Groups. --- @param #SPAWN self --- @param Core.Event#EVENTDATA EventData -function SPAWN:_OnTakeOff( EventData ) - self:F( self.SpawnTemplatePrefix ) - - local SpawnGroup = EventData.IniGroup - if SpawnGroup then - local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "TakeOff event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - self:T( "self.Landed = false" ) - SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", false ) - end - end -end - ---- Will detect AIR Units landing... When the event takes place, the spawned Group is registered as landed. --- This is needed to ensure that Re-SPAWNing is only done for landed AIR Groups. --- @param #SPAWN self --- @param Core.Event#EVENTDATA EventData -function SPAWN:_OnLand( EventData ) - self:F( self.SpawnTemplatePrefix ) - - local SpawnGroup = EventData.IniGroup - if SpawnGroup then - local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "Land event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - -- TODO: Check if this is the last unit of the group that lands. - SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", true ) - if self.RepeatOnLanding then - local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) - self:T( { "Landed:", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) - self:ReSpawn( SpawnGroupIndex ) - end - end - end -end - ---- Will detect AIR Units shutting down their engines ... --- When the event takes place, and the method @{RepeatOnEngineShutDown} was called, the spawned Group will Re-SPAWN. --- But only when the Unit was registered to have landed. --- @param #SPAWN self --- @param Core.Event#EVENTDATA EventData -function SPAWN:_OnEngineShutDown( EventData ) - self:F( self.SpawnTemplatePrefix ) - - local SpawnGroup = EventData.IniGroup - if SpawnGroup then - local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup ) - self:T( { "EngineShutdown event: " .. EventPrefix } ) - if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then - -- todo: test if on the runway - local Landed = SpawnGroup:GetState( SpawnGroup, "Spawn_Landed" ) - if Landed and self.RepeatOnEngineShutDown then - local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup ) - self:T( { "EngineShutDown: ", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } ) - self:ReSpawn( SpawnGroupIndex ) - end - end - end -end - ---- This function is called automatically by the Spawning scheduler. --- It is the internal worker method SPAWNing new Groups on the defined time intervals. -function SPAWN:_Scheduler() - self:F2( { "_Scheduler", self.SpawnTemplatePrefix, self.SpawnAliasPrefix, self.SpawnIndex, self.SpawnMaxGroups, self.SpawnMaxUnitsAlive } ) - - -- Validate if there are still groups left in the batch... - self:Spawn() - - return true -end - ---- Schedules the CleanUp of Groups --- @param #SPAWN self --- @return #boolean True = Continue Scheduler -function SPAWN:_SpawnCleanUpScheduler() - self:F( { "CleanUp Scheduler:", self.SpawnTemplatePrefix } ) - - local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup() - self:T( { "CleanUp Scheduler:", SpawnGroup, SpawnCursor } ) - - while SpawnGroup do - - local SpawnUnits = SpawnGroup:GetUnits() - - for UnitID, UnitData in pairs( SpawnUnits ) do - - local SpawnUnit = UnitData -- Wrapper.Unit#UNIT - local SpawnUnitName = SpawnUnit:GetName() - - - self.SpawnCleanUpTimeStamps[SpawnUnitName] = self.SpawnCleanUpTimeStamps[SpawnUnitName] or {} - local Stamp = self.SpawnCleanUpTimeStamps[SpawnUnitName] - self:T( { SpawnUnitName, Stamp } ) - - if Stamp.Vec2 then - if SpawnUnit:InAir() == false and SpawnUnit:GetVelocityKMH() < 1 then - local NewVec2 = SpawnUnit:GetVec2() - if Stamp.Vec2.x == NewVec2.x and Stamp.Vec2.y == NewVec2.y then - -- If the plane is not moving, and is on the ground, assign it with a timestamp... - if Stamp.Time + self.SpawnCleanUpInterval < timer.getTime() then - self:T( { "CleanUp Scheduler:", "ReSpawning:", SpawnGroup:GetName() } ) - self:ReSpawn( SpawnCursor ) - Stamp.Vec2 = nil - Stamp.Time = nil - end - else - Stamp.Time = timer.getTime() - Stamp.Vec2 = SpawnUnit:GetVec2() - end - else - Stamp.Vec2 = nil - Stamp.Time = nil - end - else - if SpawnUnit:InAir() == false then - Stamp.Vec2 = SpawnUnit:GetVec2() - if SpawnUnit:GetVelocityKMH() < 1 then - Stamp.Time = timer.getTime() - end - else - Stamp.Time = nil - Stamp.Vec2 = nil - end - end - end - - SpawnGroup, SpawnCursor = self:GetNextAliveGroup( SpawnCursor ) - - self:T( { "CleanUp Scheduler:", SpawnGroup, SpawnCursor } ) - - end - - return true -- Repeat - -end ---- Limit the simultaneous movement of Groups within a running Mission. --- This module is defined to improve the performance in missions, and to bring additional realism for GROUND vehicles. --- Performance: If in a DCSRTE there are a lot of moving GROUND units, then in a multi player mission, this WILL create lag if --- the main DCS execution core of your CPU is fully utilized. So, this class will limit the amount of simultaneous moving GROUND units --- on defined intervals (currently every minute). --- @module Movement - ---- the MOVEMENT class --- @type MOVEMENT --- @extends Core.Base#BASE -MOVEMENT = { - ClassName = "MOVEMENT", -} - ---- Creates the main object which is handling the GROUND forces movement. --- @param table{string,...}|string MovePrefixes is a table of the Prefixes (names) of the GROUND Groups that need to be controlled by the MOVEMENT Object. --- @param number MoveMaximum is a number that defines the maximum amount of GROUND Units to be moving during one minute. --- @return MOVEMENT --- @usage --- -- Limit the amount of simultaneous moving units on the ground to prevent lag. --- Movement_US_Platoons = MOVEMENT:New( { 'US Tank Platoon Left', 'US Tank Platoon Middle', 'US Tank Platoon Right', 'US CH-47D Troops' }, 15 ) - -function MOVEMENT:New( MovePrefixes, MoveMaximum ) - local self = BASE:Inherit( self, BASE:New() ) -- #MOVEMENT - self:F( { MovePrefixes, MoveMaximum } ) - - if type( MovePrefixes ) == 'table' then - self.MovePrefixes = MovePrefixes - else - self.MovePrefixes = { MovePrefixes } - end - self.MoveCount = 0 -- The internal counter of the amount of Moveing the has happened since MoveStart. - self.MoveMaximum = MoveMaximum -- Contains the Maximum amount of units that are allowed to move... - self.AliveUnits = 0 -- Contains the counter how many units are currently alive - self.MoveUnits = {} -- Reflects if the Moving for this MovePrefixes is going to be scheduled or not. - - self:HandleEvent( EVENTS.Birth ) - --- self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth ) --- --- self:EnableEvents() - - self:ScheduleStart() - - return self -end - ---- Call this function to start the MOVEMENT scheduling. -function MOVEMENT:ScheduleStart() - self:F() - --self.MoveFunction = routines.scheduleFunction( self._Scheduler, { self }, timer.getTime() + 1, 120 ) - self.MoveFunction = SCHEDULER:New( self, self._Scheduler, {}, 1, 120 ) -end - ---- Call this function to stop the MOVEMENT scheduling. --- @todo need to implement it ... Forgot. -function MOVEMENT:ScheduleStop() - self:F() - -end - ---- Captures the birth events when new Units were spawned. --- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. --- @param #MOVEMENT self --- @param Core.Event#EVENTDATA self -function MOVEMENT:OnEventBirth( EventData ) - self:F( { EventData } ) - - if timer.getTime0() < timer.getAbsTime() then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line - if EventData.IniDCSUnit then - self:T( "Birth object : " .. EventData.IniDCSUnitName ) - if EventData.IniDCSGroup and EventData.IniDCSGroup:isExist() then - for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do - if string.find( EventData.IniDCSUnitName, MovePrefix, 1, true ) then - self.AliveUnits = self.AliveUnits + 1 - self.MoveUnits[EventData.IniDCSUnitName] = EventData.IniDCSGroupName - self:T( self.AliveUnits ) - end - end - end - end - - EventData.IniUnit:HandleEvent( EVENTS.DEAD, self.OnDeadOrCrash ) - end - -end - ---- Captures the Dead or Crash events when Units crash or are destroyed. --- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration. -function MOVEMENT:OnDeadOrCrash( Event ) - self:F( { Event } ) - - if Event.IniDCSUnit then - self:T( "Dead object : " .. Event.IniDCSUnitName ) - for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do - if string.find( Event.IniDCSUnitName, MovePrefix, 1, true ) then - self.AliveUnits = self.AliveUnits - 1 - self.MoveUnits[Event.IniDCSUnitName] = nil - self:T( self.AliveUnits ) - end - end - end -end - ---- This function is called automatically by the MOVEMENT scheduler. A new function is scheduled when MoveScheduled is true. -function MOVEMENT:_Scheduler() - self:F( { self.MovePrefixes, self.MoveMaximum, self.AliveUnits, self.MovementGroups } ) - - if self.AliveUnits > 0 then - local MoveProbability = ( self.MoveMaximum * 100 ) / self.AliveUnits - self:T( 'Move Probability = ' .. MoveProbability ) - - for MovementUnitName, MovementGroupName in pairs( self.MoveUnits ) do - local MovementGroup = Group.getByName( MovementGroupName ) - if MovementGroup and MovementGroup:isExist() then - local MoveOrStop = math.random( 1, 100 ) - self:T( 'MoveOrStop = ' .. MoveOrStop ) - if MoveOrStop <= MoveProbability then - self:T( 'Group continues moving = ' .. MovementGroupName ) - trigger.action.groupContinueMoving( MovementGroup ) - else - self:T( 'Group stops moving = ' .. MovementGroupName ) - trigger.action.groupStopMoving( MovementGroup ) - end - else - self.MoveUnits[MovementUnitName] = nil - end - end - end - return true -end ---- Provides defensive behaviour to a set of SAM sites within a running Mission. --- @module Sead --- @author to be searched on the forum --- @author (co) Flightcontrol (Modified and enriched with functionality) - ---- The SEAD class --- @type SEAD --- @extends Core.Base#BASE -SEAD = { - ClassName = "SEAD", - TargetSkill = { - Average = { Evade = 50, DelayOff = { 10, 25 }, DelayOn = { 10, 30 } } , - Good = { Evade = 30, DelayOff = { 8, 20 }, DelayOn = { 20, 40 } } , - High = { Evade = 15, DelayOff = { 5, 17 }, DelayOn = { 30, 50 } } , - Excellent = { Evade = 10, DelayOff = { 3, 10 }, DelayOn = { 30, 60 } } - }, - SEADGroupPrefixes = {} -} - ---- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. --- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions... --- Chances are big that the missile will miss. --- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCSRTE on which evasive actions need to be taken. --- @return SEAD --- @usage --- -- CCCP SEAD Defenses --- -- Defends the Russian SA installations from SEAD attacks. --- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } ) -function SEAD:New( SEADGroupPrefixes ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( SEADGroupPrefixes ) - if type( SEADGroupPrefixes ) == 'table' then - for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do - self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix - end - else - self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes - end - - self:HandleEvent( EVENTS.Shot ) - - return self -end - ---- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. --- @see SEAD --- @param #SEAD --- @param Core.Event#EVENTDATA EventData -function SEAD:OnEventShot( EventData ) - self:F( { EventData } ) - - local SEADUnit = EventData.IniDCSUnit - local SEADUnitName = EventData.IniDCSUnitName - local SEADWeapon = EventData.Weapon -- Identify the weapon fired - local SEADWeaponName = EventData.WeaponName -- return weapon type - -- Start of the 2nd loop - self:T( "Missile Launched = " .. SEADWeaponName ) - if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD - local _evade = math.random (1,100) -- random number for chance of evading action - local _targetMim = EventData.Weapon:getTarget() -- Identify target - local _targetMimname = Unit.getName(_targetMim) - local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) - local _targetMimgroupName = _targetMimgroup:getName() - local _targetMimcont= _targetMimgroup:getController() - local _targetskill = _DATABASE.Templates.Units[_targetMimname].Template.skill - self:T( self.SEADGroupPrefixes ) - self:T( _targetMimgroupName ) - local SEADGroupFound = false - for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do - if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then - SEADGroupFound = true - self:T( 'Group Found' ) - break - end - end - if SEADGroupFound == true then - if _targetskill == "Random" then -- when skill is random, choose a skill - local Skills = { "Average", "Good", "High", "Excellent" } - _targetskill = Skills[ math.random(1,4) ] - end - self:T( _targetskill ) - if self.TargetSkill[_targetskill] then - if (_evade > self.TargetSkill[_targetskill].Evade) then - self:T( string.format("Evading, target skill " ..string.format(_targetskill)) ) - local _targetMim = Weapon.getTarget(SEADWeapon) - local _targetMimname = Unit.getName(_targetMim) - local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) - local _targetMimcont= _targetMimgroup:getController() - routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly - local SuppressedGroups1 = {} -- unit suppressed radar off for a random time - local function SuppressionEnd1(id) - id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN) - SuppressedGroups1[id.groupName] = nil - end - local id = { - groupName = _targetMimgroup, - ctrl = _targetMimcont - } - local delay1 = math.random(self.TargetSkill[_targetskill].DelayOff[1], self.TargetSkill[_targetskill].DelayOff[2]) - if SuppressedGroups1[id.groupName] == nil then - SuppressedGroups1[id.groupName] = { - SuppressionEndTime1 = timer.getTime() + delay1, - SuppressionEndN1 = SuppressionEndCounter1 --Store instance of SuppressionEnd() scheduled function - } - Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN) - timer.scheduleFunction(SuppressionEnd1, id, SuppressedGroups1[id.groupName].SuppressionEndTime1) --Schedule the SuppressionEnd() function - --trigger.action.outText( string.format("Radar Off " ..string.format(delay1)), 20) - end - - local SuppressedGroups = {} - local function SuppressionEnd(id) - id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED) - SuppressedGroups[id.groupName] = nil - end - local id = { - groupName = _targetMimgroup, - ctrl = _targetMimcont - } - local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2]) - if SuppressedGroups[id.groupName] == nil then - SuppressedGroups[id.groupName] = { - SuppressionEndTime = timer.getTime() + delay, - SuppressionEndN = SuppressionEndCounter --Store instance of SuppressionEnd() scheduled function - } - timer.scheduleFunction(SuppressionEnd, id, SuppressedGroups[id.groupName].SuppressionEndTime) --Schedule the SuppressionEnd() function - --trigger.action.outText( string.format("Radar On " ..string.format(delay)), 20) - end - end - end - end - end -end ---- Taking the lead of AI escorting your flight. --- --- @{#ESCORT} class --- ================ --- The @{#ESCORT} class allows you to interact with escorting AI on your flight and take the lead. --- Each escorting group can be commanded with a whole set of radio commands (radio menu in your flight, and then F10). --- --- The radio commands will vary according the category of the group. The richest set of commands are with Helicopters and AirPlanes. --- Ships and Ground troops will have a more limited set, but they can provide support through the bombing of targets designated by the other escorts. --- --- RADIO MENUs that can be created: --- ================================ --- Find a summary below of the current available commands: --- --- Navigation ...: --- --------------- --- Escort group navigation functions: --- --- * **"Join-Up and Follow at x meters":** The escort group fill follow you at about x meters, and they will follow you. --- * **"Flare":** Provides menu commands to let the escort group shoot a flare in the air in a color. --- * **"Smoke":** Provides menu commands to let the escort group smoke the air in a color. Note that smoking is only available for ground and naval troops. --- --- Hold position ...: --- ------------------ --- Escort group navigation functions: --- --- * **"At current location":** Stops the escort group and they will hover 30 meters above the ground at the position they stopped. --- * **"At client location":** Stops the escort group and they will hover 30 meters above the ground at the position they stopped. --- --- Report targets ...: --- ------------------- --- Report targets will make the escort group to report any target that it identifies within a 8km range. Any detected target can be attacked using the 4. Attack nearby targets function. (see below). --- --- * **"Report now":** Will report the current detected targets. --- * **"Report targets on":** Will make the escort group to report detected targets and will fill the "Attack nearby targets" menu list. --- * **"Report targets off":** Will stop detecting targets. --- --- Scan targets ...: --- ----------------- --- Menu items to pop-up the escort group for target scanning. After scanning, the escort group will resume with the mission or defined task. --- --- * **"Scan targets 30 seconds":** Scan 30 seconds for targets. --- * **"Scan targets 60 seconds":** Scan 60 seconds for targets. --- --- Attack targets ...: --- ------------------- --- This menu item will list all detected targets within a 15km range. Depending on the level of detection (known/unknown) and visuality, the targets type will also be listed. --- --- Request assistance from ...: --- ---------------------------- --- This menu item will list all detected targets within a 15km range, as with the menu item **Attack Targets**. --- This menu item allows to request attack support from other escorts supporting the current client group. --- eg. the function allows a player to request support from the Ship escort to attack a target identified by the Plane escort with its Tomahawk missiles. --- eg. the function allows a player to request support from other Planes escorting to bomb the unit with illumination missiles or bombs, so that the main plane escort can attack the area. --- --- ROE ...: --- -------- --- Sets the Rules of Engagement (ROE) of the escort group when in flight. --- --- * **"Hold Fire":** The escort group will hold fire. --- * **"Return Fire":** The escort group will return fire. --- * **"Open Fire":** The escort group will open fire on designated targets. --- * **"Weapon Free":** The escort group will engage with any target. --- --- Evasion ...: --- ------------ --- Will define the evasion techniques that the escort group will perform during flight or combat. --- --- * **"Fight until death":** The escort group will have no reaction to threats. --- * **"Use flares, chaff and jammers":** The escort group will use passive defense using flares and jammers. No evasive manoeuvres are executed. --- * **"Evade enemy fire":** The rescort group will evade enemy fire before firing. --- * **"Go below radar and evade fire":** The escort group will perform evasive vertical manoeuvres. --- --- Resume Mission ...: --- ------------------- --- Escort groups can have their own mission. This menu item will allow the escort group to resume their Mission from a given waypoint. --- Note that this is really fantastic, as you now have the dynamic of taking control of the escort groups, and allowing them to resume their path or mission. --- --- ESCORT construction methods. --- ============================ --- Create a new SPAWN object with the @{#ESCORT.New} method: --- --- * @{#ESCORT.New}: Creates a new ESCORT object from a @{Group#GROUP} for a @{Client#CLIENT}, with an optional briefing text. --- --- ESCORT initialization methods. --- ============================== --- The following menus are created within the RADIO MENU (F10) of an active unit hosted by a player: --- --- * @{#ESCORT.MenuFollowAt}: Creates a menu to make the escort follow the client. --- * @{#ESCORT.MenuHoldAtEscortPosition}: Creates a menu to hold the escort at its current position. --- * @{#ESCORT.MenuHoldAtLeaderPosition}: Creates a menu to hold the escort at the client position. --- * @{#ESCORT.MenuScanForTargets}: Creates a menu so that the escort scans targets. --- * @{#ESCORT.MenuFlare}: Creates a menu to disperse flares. --- * @{#ESCORT.MenuSmoke}: Creates a menu to disparse smoke. --- * @{#ESCORT.MenuReportTargets}: Creates a menu so that the escort reports targets. --- * @{#ESCORT.MenuReportPosition}: Creates a menu so that the escort reports its current position from bullseye. --- * @{#ESCORT.MenuAssistedAttack: Creates a menu so that the escort supportes assisted attack from other escorts with the client. --- * @{#ESCORT.MenuROE: Creates a menu structure to set the rules of engagement of the escort. --- * @{#ESCORT.MenuEvasion: Creates a menu structure to set the evasion techniques when the escort is under threat. --- * @{#ESCORT.MenuResumeMission}: Creates a menu structure so that the escort can resume from a waypoint. --- --- --- @usage --- -- Declare a new EscortPlanes object as follows: --- --- -- First find the GROUP object and the CLIENT object. --- local EscortClient = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor. --- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client. --- --- -- Now use these 2 objects to construct the new EscortPlanes object. --- EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) --- --- --- --- @module Escort --- @author FlightControl - ---- ESCORT class --- @type ESCORT --- @extends Core.Base#BASE --- @field Wrapper.Client#CLIENT EscortClient --- @field Wrapper.Group#GROUP EscortGroup --- @field #string EscortName --- @field #ESCORT.MODE EscortMode The mode the escort is in. --- @field Core.Scheduler#SCHEDULER FollowScheduler The instance of the SCHEDULER class. --- @field #number FollowDistance The current follow distance. --- @field #boolean ReportTargets If true, nearby targets are reported. --- @Field Dcs.DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the EscortGroup. --- @field Dcs.DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the EscortGroup. --- @field Core.Menu#MENU_CLIENT EscortMenuResumeMission --- @field Functional.Detection#DETECTION_BASE Detection -ESCORT = { - ClassName = "ESCORT", - EscortName = nil, -- The Escort Name - EscortClient = nil, - EscortGroup = nil, - EscortMode = 1, - MODE = { - FOLLOW = 1, - MISSION = 2, - }, - Targets = {}, -- The identified targets - FollowScheduler = nil, - ReportTargets = true, - OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE, - OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION, - SmokeDirectionVector = false, - TaskPoints = {} -} - ---- ESCORT.Mode class --- @type ESCORT.MODE --- @field #number FOLLOW --- @field #number MISSION - ---- MENUPARAM type --- @type MENUPARAM --- @field #ESCORT ParamSelf --- @field #Distance ParamDistance --- @field #function ParamFunction --- @field #string ParamMessage - ---- ESCORT class constructor for an AI group --- @param #ESCORT self --- @param Wrapper.Client#CLIENT EscortClient The client escorted by the EscortGroup. --- @param Wrapper.Group#GROUP EscortGroup The group AI escorting the EscortClient. --- @param #string EscortName Name of the escort. --- @param #string EscortBriefing A text showing the ESCORT briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown. --- @return #ESCORT self --- @usage --- -- Declare a new EscortPlanes object as follows: --- --- -- First find the GROUP object and the CLIENT object. --- local EscortClient = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor. --- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client. --- --- -- Now use these 2 objects to construct the new EscortPlanes object. --- EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." ) -function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing ) - - local self = BASE:Inherit( self, BASE:New() ) -- #ESCORT - self:F( { EscortClient, EscortGroup, EscortName } ) - - self.EscortClient = EscortClient -- Wrapper.Client#CLIENT - self.EscortGroup = EscortGroup -- Wrapper.Group#GROUP - self.EscortName = EscortName - self.EscortBriefing = EscortBriefing - - self.EscortSetGroup = SET_GROUP:New() - self.EscortSetGroup:AddObject( self.EscortGroup ) - self.EscortSetGroup:Flush() - self.Detection = DETECTION_UNITS:New( self.EscortSetGroup, 15000 ) - - self.EscortGroup.Detection = self.Detection - - -- Set EscortGroup known at EscortClient. - if not self.EscortClient._EscortGroups then - self.EscortClient._EscortGroups = {} - end - - if not self.EscortClient._EscortGroups[EscortGroup:GetName()] then - self.EscortClient._EscortGroups[EscortGroup:GetName()] = {} - self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup - self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName - self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection = self.EscortGroup.Detection - end - - self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName ) - - self.EscortGroup:WayPointInitialize(1) - - self.EscortGroup:OptionROTVertical() - self.EscortGroup:OptionROEOpenFire() - - if not EscortBriefing then - EscortGroup:MessageToClient( EscortGroup:GetCategoryName() .. " '" .. EscortName .. "' (" .. EscortGroup:GetCallsign() .. ") reporting! " .. - "We're escorting your flight. " .. - "Use the Radio Menu and F10 and use the options under + " .. EscortName .. "\n", - 60, EscortClient - ) - else - EscortGroup:MessageToClient( EscortGroup:GetCategoryName() .. " '" .. EscortName .. "' (" .. EscortGroup:GetCallsign() .. ") " .. EscortBriefing, - 60, EscortClient - ) - end - - self.FollowDistance = 100 - self.CT1 = 0 - self.GT1 = 0 - - self.FollowScheduler, self.FollowSchedule = SCHEDULER:New( self, self._FollowScheduler, {}, 1, .5, .01 ) - self.FollowScheduler:Stop( self.FollowSchedule ) - - self.EscortMode = ESCORT.MODE.MISSION - - - return self -end - ---- Set a Detection method for the EscortClient to be reported upon. --- Detection methods are based on the derived classes from DETECTION_BASE. --- @param #ESCORT self --- @param Function.Detection#DETECTION_BASE Detection -function ESCORT:SetDetection( Detection ) - - self.Detection = Detection - self.EscortGroup.Detection = self.Detection - self.EscortClient._EscortGroups[self.EscortGroup:GetName()].Detection = self.EscortGroup.Detection - - Detection:__Start( 1 ) - -end - ---- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to. --- This allows to visualize where the escort is flying to. --- @param #ESCORT self --- @param #boolean SmokeDirection If true, then the direction vector will be smoked. -function ESCORT:TestSmokeDirectionVector( SmokeDirection ) - self.SmokeDirectionVector = ( SmokeDirection == true ) and true or false -end - - ---- Defines the default menus --- @param #ESCORT self --- @return #ESCORT -function ESCORT:Menus() - self:F() - - self:MenuFollowAt( 100 ) - self:MenuFollowAt( 200 ) - self:MenuFollowAt( 300 ) - self:MenuFollowAt( 400 ) - - self:MenuScanForTargets( 100, 60 ) - - self:MenuHoldAtEscortPosition( 30 ) - self:MenuHoldAtLeaderPosition( 30 ) - - self:MenuFlare() - self:MenuSmoke() - - self:MenuReportTargets( 60 ) - self:MenuAssistedAttack() - self:MenuROE() - self:MenuEvasion() - self:MenuResumeMission() - - - return self -end - - - ---- Defines a menu slot to let the escort Join and Follow you at a certain distance. --- This menu will appear under **Navigation**. --- @param #ESCORT self --- @param Dcs.DCSTypes#Distance Distance The distance in meters that the escort needs to follow the client. --- @return #ESCORT -function ESCORT:MenuFollowAt( Distance ) - self:F(Distance) - - if self.EscortGroup:IsAir() then - if not self.EscortMenuReportNavigation then - self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu ) - end - - if not self.EscortMenuJoinUpAndFollow then - self.EscortMenuJoinUpAndFollow = {} - end - - self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, self, Distance ) - - self.EscortMode = ESCORT.MODE.FOLLOW - end - - return self -end - ---- Defines a menu slot to let the escort hold at their current position and stay low with a specified height during a specified time in seconds. --- This menu will appear under **Hold position**. --- @param #ESCORT self --- @param Dcs.DCSTypes#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. --- @param Dcs.DCSTypes#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. --- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed. --- @return #ESCORT --- TODO: Implement Seconds parameter. Challenge is to first develop the "continue from last activity" function. -function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat ) - self:F( { Height, Seconds, MenuTextFormat } ) - - if self.EscortGroup:IsAir() then - - if not self.EscortMenuHold then - self.EscortMenuHold = MENU_CLIENT:New( self.EscortClient, "Hold position", self.EscortMenu ) - end - - if not Height then - Height = 30 - end - - if not Seconds then - Seconds = 0 - end - - local MenuText = "" - if not MenuTextFormat then - if Seconds == 0 then - MenuText = string.format( "Hold at %d meter", Height ) - else - MenuText = string.format( "Hold at %d meter for %d seconds", Height, Seconds ) - end - else - if Seconds == 0 then - MenuText = string.format( MenuTextFormat, Height ) - else - MenuText = string.format( MenuTextFormat, Height, Seconds ) - end - end - - if not self.EscortMenuHoldPosition then - self.EscortMenuHoldPosition = {} - end - - self.EscortMenuHoldPosition[#self.EscortMenuHoldPosition+1] = MENU_CLIENT_COMMAND - :New( - self.EscortClient, - MenuText, - self.EscortMenuHold, - ESCORT._HoldPosition, - self, - self.EscortGroup, - Height, - Seconds - ) - end - - return self -end - - ---- Defines a menu slot to let the escort hold at the client position and stay low with a specified height during a specified time in seconds. --- This menu will appear under **Navigation**. --- @param #ESCORT self --- @param Dcs.DCSTypes#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. --- @param Dcs.DCSTypes#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. --- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain one or two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed. --- @return #ESCORT --- TODO: Implement Seconds parameter. Challenge is to first develop the "continue from last activity" function. -function ESCORT:MenuHoldAtLeaderPosition( Height, Seconds, MenuTextFormat ) - self:F( { Height, Seconds, MenuTextFormat } ) - - if self.EscortGroup:IsAir() then - - if not self.EscortMenuHold then - self.EscortMenuHold = MENU_CLIENT:New( self.EscortClient, "Hold position", self.EscortMenu ) - end - - if not Height then - Height = 30 - end - - if not Seconds then - Seconds = 0 - end - - local MenuText = "" - if not MenuTextFormat then - if Seconds == 0 then - MenuText = string.format( "Rejoin and hold at %d meter", Height ) - else - MenuText = string.format( "Rejoin and hold at %d meter for %d seconds", Height, Seconds ) - end - else - if Seconds == 0 then - MenuText = string.format( MenuTextFormat, Height ) - else - MenuText = string.format( MenuTextFormat, Height, Seconds ) - end - end - - if not self.EscortMenuHoldAtLeaderPosition then - self.EscortMenuHoldAtLeaderPosition = {} - end - - self.EscortMenuHoldAtLeaderPosition[#self.EscortMenuHoldAtLeaderPosition+1] = MENU_CLIENT_COMMAND - :New( - self.EscortClient, - MenuText, - self.EscortMenuHold, - ESCORT._HoldPosition, - { ParamSelf = self, - ParamOrbitGroup = self.EscortClient, - ParamHeight = Height, - ParamSeconds = Seconds - } - ) - end - - return self -end - ---- Defines a menu slot to let the escort scan for targets at a certain height for a certain time in seconds. --- This menu will appear under **Scan targets**. --- @param #ESCORT self --- @param Dcs.DCSTypes#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. --- @param Dcs.DCSTypes#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. --- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain one or two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed. --- @return #ESCORT -function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat ) - self:F( { Height, Seconds, MenuTextFormat } ) - - if self.EscortGroup:IsAir() then - if not self.EscortMenuScan then - self.EscortMenuScan = MENU_CLIENT:New( self.EscortClient, "Scan for targets", self.EscortMenu ) - end - - if not Height then - Height = 100 - end - - if not Seconds then - Seconds = 30 - end - - local MenuText = "" - if not MenuTextFormat then - if Seconds == 0 then - MenuText = string.format( "At %d meter", Height ) - else - MenuText = string.format( "At %d meter for %d seconds", Height, Seconds ) - end - else - if Seconds == 0 then - MenuText = string.format( MenuTextFormat, Height ) - else - MenuText = string.format( MenuTextFormat, Height, Seconds ) - end - end - - if not self.EscortMenuScanForTargets then - self.EscortMenuScanForTargets = {} - end - - self.EscortMenuScanForTargets[#self.EscortMenuScanForTargets+1] = MENU_CLIENT_COMMAND - :New( - self.EscortClient, - MenuText, - self.EscortMenuScan, - ESCORT._ScanTargets, - self, - 30 - ) - end - - return self -end - - - ---- Defines a menu slot to let the escort disperse a flare in a certain color. --- This menu will appear under **Navigation**. --- The flare will be fired from the first unit in the group. --- @param #ESCORT self --- @param #string MenuTextFormat Optional parameter that shows the menu option text. If no text is given, the default text will be displayed. --- @return #ESCORT -function ESCORT:MenuFlare( MenuTextFormat ) - self:F() - - if not self.EscortMenuReportNavigation then - self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu ) - end - - local MenuText = "" - if not MenuTextFormat then - MenuText = "Flare" - else - MenuText = MenuTextFormat - end - - if not self.EscortMenuFlare then - self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, self ) - self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Green, "Released a green flare!" ) - self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Red, "Released a red flare!" ) - self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.White, "Released a white flare!" ) - self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Yellow, "Released a yellow flare!" ) - end - - return self -end - ---- Defines a menu slot to let the escort disperse a smoke in a certain color. --- This menu will appear under **Navigation**. --- Note that smoke menu options will only be displayed for ships and ground units. Not for air units. --- The smoke will be fired from the first unit in the group. --- @param #ESCORT self --- @param #string MenuTextFormat Optional parameter that shows the menu option text. If no text is given, the default text will be displayed. --- @return #ESCORT -function ESCORT:MenuSmoke( MenuTextFormat ) - self:F() - - if not self.EscortGroup:IsAir() then - if not self.EscortMenuReportNavigation then - self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu ) - end - - local MenuText = "" - if not MenuTextFormat then - MenuText = "Smoke" - else - MenuText = MenuTextFormat - end - - if not self.EscortMenuSmoke then - self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, self ) - self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Green, "Releasing green smoke!" ) - self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Red, "Releasing red smoke!" ) - self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.White, "Releasing white smoke!" ) - self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Orange, "Releasing orange smoke!" ) - self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Blue, "Releasing blue smoke!" ) - end - end - - return self -end - ---- Defines a menu slot to let the escort report their current detected targets with a specified time interval in seconds. --- This menu will appear under **Report targets**. --- Note that if a report targets menu is not specified, no targets will be detected by the escort, and the attack and assisted attack menus will not be displayed. --- @param #ESCORT self --- @param Dcs.DCSTypes#Time Seconds Optional parameter that lets the escort report their current detected targets after specified time interval in seconds. The default time is 30 seconds. --- @return #ESCORT -function ESCORT:MenuReportTargets( Seconds ) - self:F( { Seconds } ) - - if not self.EscortMenuReportNearbyTargets then - self.EscortMenuReportNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Report targets", self.EscortMenu ) - end - - if not Seconds then - Seconds = 30 - end - - -- Report Targets - self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, self ) - self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, true ) - self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, false ) - - -- Attack Targets - self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu ) - - - self.ReportTargetsScheduler = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, Seconds ) - - return self -end - ---- Defines a menu slot to let the escort attack its detected targets using assisted attack from another escort joined also with the client. --- This menu will appear under **Request assistance from**. --- Note that this method needs to be preceded with the method MenuReportTargets. --- @param #ESCORT self --- @return #ESCORT -function ESCORT:MenuAssistedAttack() - self:F() - - -- Request assistance from other escorts. - -- This is very useful to let f.e. an escorting ship attack a target detected by an escorting plane... - self.EscortMenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, "Request assistance from", self.EscortMenu ) - - return self -end - ---- Defines a menu to let the escort set its rules of engagement. --- All rules of engagement will appear under the menu **ROE**. --- @param #ESCORT self --- @return #ESCORT -function ESCORT:MenuROE( MenuTextFormat ) - self:F( MenuTextFormat ) - - if not self.EscortMenuROE then - -- Rules of Engagement - self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu ) - if self.EscortGroup:OptionROEHoldFirePossible() then - self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEHoldFire(), "Holding weapons!" ) - end - if self.EscortGroup:OptionROEReturnFirePossible() then - self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEReturnFire(), "Returning fire!" ) - end - if self.EscortGroup:OptionROEOpenFirePossible() then - self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEOpenFire(), "Opening fire on designated targets!!" ) - end - if self.EscortGroup:OptionROEWeaponFreePossible() then - self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEWeaponFree(), "Opening fire on targets of opportunity!" ) - end - end - - return self -end - - ---- Defines a menu to let the escort set its evasion when under threat. --- All rules of engagement will appear under the menu **Evasion**. --- @param #ESCORT self --- @return #ESCORT -function ESCORT:MenuEvasion( MenuTextFormat ) - self:F( MenuTextFormat ) - - if self.EscortGroup:IsAir() then - if not self.EscortMenuEvasion then - -- Reaction to Threats - self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu ) - if self.EscortGroup:OptionROTNoReactionPossible() then - self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTNoReaction(), "Fighting until death!" ) - end - if self.EscortGroup:OptionROTPassiveDefensePossible() then - self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTPassiveDefense(), "Defending using jammers, chaff and flares!" ) - end - if self.EscortGroup:OptionROTEvadeFirePossible() then - self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTEvadeFire(), "Evading on enemy fire!" ) - end - if self.EscortGroup:OptionROTVerticalPossible() then - self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTVertical(), "Evading on enemy fire with vertical manoeuvres!" ) - end - end - end - - return self -end - ---- Defines a menu to let the escort resume its mission from a waypoint on its route. --- All rules of engagement will appear under the menu **Resume mission from**. --- @param #ESCORT self --- @return #ESCORT -function ESCORT:MenuResumeMission() - self:F() - - if not self.EscortMenuResumeMission then - -- Mission Resume Menu Root - self.EscortMenuResumeMission = MENU_CLIENT:New( self.EscortClient, "Resume mission from", self.EscortMenu ) - end - - return self -end - - ---- @param #MENUPARAM MenuParam -function ESCORT:_HoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - local OrbitUnit = OrbitGroup:GetUnit(1) -- Wrapper.Unit#UNIT - - self.FollowScheduler:Stop( self.FollowSchedule ) - - local PointFrom = {} - local GroupVec3 = EscortGroup:GetUnit(1):GetVec3() - PointFrom = {} - PointFrom.x = GroupVec3.x - PointFrom.y = GroupVec3.z - PointFrom.speed = 250 - PointFrom.type = AI.Task.WaypointType.TURNING_POINT - PointFrom.alt = GroupVec3.y - PointFrom.alt_type = AI.Task.AltitudeType.BARO - - local OrbitPoint = OrbitUnit:GetVec2() - local PointTo = {} - PointTo.x = OrbitPoint.x - PointTo.y = OrbitPoint.y - PointTo.speed = 250 - PointTo.type = AI.Task.WaypointType.TURNING_POINT - PointTo.alt = OrbitHeight - PointTo.alt_type = AI.Task.AltitudeType.BARO - PointTo.task = EscortGroup:TaskOrbitCircleAtVec2( OrbitPoint, OrbitHeight, 0 ) - - local Points = { PointFrom, PointTo } - - EscortGroup:OptionROEHoldFire() - EscortGroup:OptionROTPassiveDefense() - - EscortGroup:SetTask( EscortGroup:TaskRoute( Points ) ) - EscortGroup:MessageToClient( "Orbiting at location.", 10, EscortClient ) - -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_JoinUpAndFollow( Distance ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - self.Distance = Distance - - self:JoinUpAndFollow( EscortGroup, EscortClient, self.Distance ) -end - ---- JoinsUp and Follows a CLIENT. --- @param Functional.Escort#ESCORT self --- @param Wrapper.Group#GROUP EscortGroup --- @param Wrapper.Client#CLIENT EscortClient --- @param Dcs.DCSTypes#Distance Distance -function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) - self:F( { EscortGroup, EscortClient, Distance } ) - - self.FollowScheduler:Stop( self.FollowSchedule ) - - EscortGroup:OptionROEHoldFire() - EscortGroup:OptionROTPassiveDefense() - - self.EscortMode = ESCORT.MODE.FOLLOW - - self.CT1 = 0 - self.GT1 = 0 - self.FollowScheduler:Start( self.FollowSchedule ) - - EscortGroup:MessageToClient( "Rejoining and Following at " .. Distance .. "!", 30, EscortClient ) -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_Flare( Color, Message ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - EscortGroup:GetUnit(1):Flare( Color ) - EscortGroup:MessageToClient( Message, 10, EscortClient ) -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_Smoke( Color, Message ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - EscortGroup:GetUnit(1):Smoke( Color ) - EscortGroup:MessageToClient( Message, 10, EscortClient ) -end - - ---- @param #MENUPARAM MenuParam -function ESCORT:_ReportNearbyTargetsNow() - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - self:_ReportTargetsScheduler() - -end - -function ESCORT:_SwitchReportNearbyTargets( ReportTargets ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - self.ReportTargets = ReportTargets - - if self.ReportTargets then - if not self.ReportTargetsScheduler then - self.ReportTargetsScheduler:Schedule( self, self._ReportTargetsScheduler, {}, 1, 30 ) - end - else - routines.removeFunction( self.ReportTargetsScheduler ) - self.ReportTargetsScheduler = nil - end -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_ScanTargets( ScanDuration ) - - local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP - local EscortClient = self.EscortClient - - self.FollowScheduler:Stop( self.FollowSchedule ) - - if EscortGroup:IsHelicopter() then - EscortGroup:PushTask( - EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 200, 20 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ), 1 ) - elseif EscortGroup:IsAirPlane() then - EscortGroup:PushTask( - EscortGroup:TaskControlled( - EscortGroup:TaskOrbitCircle( 1000, 500 ), - EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil ) - ), 1 ) - end - - EscortGroup:MessageToClient( "Scanning targets for " .. ScanDuration .. " seconds.", ScanDuration, EscortClient ) - - if self.EscortMode == ESCORT.MODE.FOLLOW then - self.FollowScheduler:Start( self.FollowSchedule ) - end - -end - ---- @param Wrapper.Group#GROUP EscortGroup -function _Resume( EscortGroup ) - env.info( '_Resume' ) - - local Escort = EscortGroup:GetState( EscortGroup, "Escort" ) - env.info( "EscortMode = " .. Escort.EscortMode ) - if Escort.EscortMode == ESCORT.MODE.FOLLOW then - Escort:JoinUpAndFollow( EscortGroup, Escort.EscortClient, Escort.Distance ) - end - -end - ---- @param #ESCORT self --- @param #number DetectedItemID -function ESCORT:_AttackTarget( DetectedItemID ) - - local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP - self:E( EscortGroup ) - - local EscortClient = self.EscortClient - - self.FollowScheduler:Stop( self.FollowSchedule ) - - if EscortGroup:IsAir() then - EscortGroup:OptionROEOpenFire() - EscortGroup:OptionROTPassiveDefense() - EscortGroup:SetState( EscortGroup, "Escort", self ) - - local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) - - local Tasks = {} - - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit, Tasks ) - if DetectedUnit:IsAlive() then - Tasks[#Tasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit ) - end - end, Tasks - ) - - Tasks[#Tasks+1] = EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } ) - - EscortGroup:SetTask( - EscortGroup:TaskCombo( - Tasks - ), 1 - ) - - else - - local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) - - local Tasks = {} - - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit, Tasks ) - if DetectedUnit:IsAlive() then - Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) - end - end, Tasks - ) - - EscortGroup:SetTask( - EscortGroup:TaskCombo( - Tasks - ), 1 - ) - - end - - EscortGroup:MessageToClient( "Engaging Designated Unit!", 10, EscortClient ) - -end - ---- --- @param #number DetectedItemID -function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItemID ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - self.FollowScheduler:Stop( self.FollowSchedule ) - - if EscortGroupAttack:IsAir() then - EscortGroupAttack:OptionROEOpenFire() - EscortGroupAttack:OptionROTVertical() - - local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) - - local Tasks = {} - - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit, Tasks ) - if DetectedUnit:IsAlive() then - Tasks[#Tasks+1] = EscortGroupAttack:TaskAttackUnit( DetectedUnit ) - end - end, Tasks - ) - - Tasks[#Tasks+1] = EscortGroupAttack:TaskOrbitCircle( 500, 350 ) - - EscortGroupAttack:SetTask( - EscortGroupAttack:TaskCombo( - Tasks - ), 1 - ) - - else - local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID ) - - local Tasks = {} - - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit, Tasks ) - if DetectedUnit:IsAlive() then - Tasks[#Tasks+1] = EscortGroupAttack:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 ) - end - end, Tasks - ) - - EscortGroupAttack:SetTask( - EscortGroupAttack:TaskCombo( - Tasks - ), 1 - ) - - end - - EscortGroupAttack:MessageToClient( "Assisting with the destroying the enemy unit!", 10, EscortClient ) - -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_ROE( EscortROEFunction, EscortROEMessage ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - pcall( function() EscortROEFunction() end ) - EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient ) -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_ROT( EscortROTFunction, EscortROTMessage ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - pcall( function() EscortROTFunction() end ) - EscortGroup:MessageToClient( EscortROTMessage, 10, EscortClient ) -end - ---- @param #MENUPARAM MenuParam -function ESCORT:_ResumeMission( WayPoint ) - - local EscortGroup = self.EscortGroup - local EscortClient = self.EscortClient - - self.FollowScheduler:Stop( self.FollowSchedule ) - - local WayPoints = EscortGroup:GetTaskRoute() - self:T( WayPoint, WayPoints ) - - for WayPointIgnore = 1, WayPoint do - table.remove( WayPoints, 1 ) - end - - SCHEDULER:New( EscortGroup, EscortGroup.SetTask, { EscortGroup:TaskRoute( WayPoints ) }, 1 ) - - EscortGroup:MessageToClient( "Resuming mission from waypoint " .. WayPoint .. ".", 10, EscortClient ) -end - ---- Registers the waypoints --- @param #ESCORT self --- @return #table -function ESCORT:RegisterRoute() - self:F() - - local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP - - local TaskPoints = EscortGroup:GetTaskRoute() - - self:T( TaskPoints ) - - return TaskPoints -end - ---- @param Functional.Escort#ESCORT self -function ESCORT:_FollowScheduler() - self:F( { self.FollowDistance } ) - - self:T( {self.EscortClient.UnitName, self.EscortGroup.GroupName } ) - if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then - - local ClientUnit = self.EscortClient:GetClientGroupUnit() - local GroupUnit = self.EscortGroup:GetUnit( 1 ) - local FollowDistance = self.FollowDistance - - self:T( {ClientUnit.UnitName, GroupUnit.UnitName } ) - - if self.CT1 == 0 and self.GT1 == 0 then - self.CV1 = ClientUnit:GetVec3() - self:T( { "self.CV1", self.CV1 } ) - self.CT1 = timer.getTime() - self.GV1 = GroupUnit:GetVec3() - self.GT1 = timer.getTime() - else - local CT1 = self.CT1 - local CT2 = timer.getTime() - local CV1 = self.CV1 - local CV2 = ClientUnit:GetVec3() - self.CT1 = CT2 - self.CV1 = CV2 - - local CD = ( ( CV2.x - CV1.x )^2 + ( CV2.y - CV1.y )^2 + ( CV2.z - CV1.z )^2 ) ^ 0.5 - local CT = CT2 - CT1 - - local CS = ( 3600 / CT ) * ( CD / 1000 ) - - self:T2( { "Client:", CS, CD, CT, CV2, CV1, CT2, CT1 } ) - - local GT1 = self.GT1 - local GT2 = timer.getTime() - local GV1 = self.GV1 - local GV2 = GroupUnit:GetVec3() - self.GT1 = GT2 - self.GV1 = GV2 - - local GD = ( ( GV2.x - GV1.x )^2 + ( GV2.y - GV1.y )^2 + ( GV2.z - GV1.z )^2 ) ^ 0.5 - local GT = GT2 - GT1 - - local GS = ( 3600 / GT ) * ( GD / 1000 ) - - self:T2( { "Group:", GS, GD, GT, GV2, GV1, GT2, GT1 } ) - - -- Calculate the group direction vector - local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z } - - -- Calculate GH2, GH2 with the same height as CV2. - local GH2 = { x = GV2.x, y = CV2.y, z = GV2.z } - - -- Calculate the angle of GV to the orthonormal plane - local alpha = math.atan2( GV.z, GV.x ) - - -- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2. - -- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2)) - local CVI = { x = CV2.x + FollowDistance * math.cos(alpha), - y = GH2.y, - z = CV2.z + FollowDistance * math.sin(alpha), - } - - -- Calculate the direction vector DV of the escort group. We use CVI as the base and CV2 as the direction. - local DV = { x = CV2.x - CVI.x, y = CV2.y - CVI.y, z = CV2.z - CVI.z } - - -- We now calculate the unary direction vector DVu, so that we can multiply DVu with the speed, which is expressed in meters / s. - -- We need to calculate this vector to predict the point the escort group needs to fly to according its speed. - -- The distance of the destination point should be far enough not to have the aircraft starting to swipe left to right... - local DVu = { x = DV.x / FollowDistance, y = DV.y / FollowDistance, z = DV.z / FollowDistance } - - -- Now we can calculate the group destination vector GDV. - local GDV = { x = DVu.x * CS * 8 + CVI.x, y = CVI.y, z = DVu.z * CS * 8 + CVI.z } - - if self.SmokeDirectionVector == true then - trigger.action.smoke( GDV, trigger.smokeColor.Red ) - end - - self:T2( { "CV2:", CV2 } ) - self:T2( { "CVI:", CVI } ) - self:T2( { "GDV:", GDV } ) - - -- Measure distance between client and group - local CatchUpDistance = ( ( GDV.x - GV2.x )^2 + ( GDV.y - GV2.y )^2 + ( GDV.z - GV2.z )^2 ) ^ 0.5 - - -- The calculation of the Speed would simulate that the group would take 30 seconds to overcome - -- the requested Distance). - local Time = 10 - local CatchUpSpeed = ( CatchUpDistance - ( CS * 8.4 ) ) / Time - - local Speed = CS + CatchUpSpeed - if Speed < 0 then - Speed = 0 - end - - self:T( { "Client Speed, Escort Speed, Speed, FollowDistance, Time:", CS, GS, Speed, FollowDistance, Time } ) - - -- Now route the escort to the desired point with the desired speed. - self.EscortGroup:RouteToVec3( GDV, Speed / 3.6 ) -- DCS models speed in Mps (Miles per second) - end - - return true - end - - return false -end - - ---- Report Targets Scheduler. --- @param #ESCORT self -function ESCORT:_ReportTargetsScheduler() - self:F( self.EscortGroup:GetName() ) - - if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then - - if true then - - local EscortGroupName = self.EscortGroup:GetName() - - self.EscortMenuAttackNearbyTargets:RemoveSubMenus() - - if self.EscortMenuTargetAssistance then - self.EscortMenuTargetAssistance:RemoveSubMenus() - end - - local DetectedItems = self.Detection:GetDetectedItems() - self:E( DetectedItems ) - - local DetectedTargets = false - - local DetectedMsgs = {} - - for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do - - local ClientEscortTargets = EscortGroupData.Detection - - for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do - self:E( { DetectedItemID, DetectedItem } ) - -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. - - local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID ) - - if ClientEscortGroupName == EscortGroupName then - - DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary - - MENU_CLIENT_COMMAND:New( self.EscortClient, - DetectedItemReportSummary, - self.EscortMenuAttackNearbyTargets, - ESCORT._AttackTarget, - self, - DetectedItemID - ) - else - if self.EscortMenuTargetAssistance then - - self:T( DetectedItemReportSummary ) - local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) - MENU_CLIENT_COMMAND:New( self.EscortClient, - DetectedItemReportSummary, - MenuTargetAssistance, - ESCORT._AssistTarget, - self, - EscortGroupData.EscortGroup, - DetectedItemID - ) - end - end - - DetectedTargets = true - - end - end - self:E( DetectedMsgs ) - if DetectedTargets then - self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient ) - else - self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient ) - end - - return true - else --- local EscortGroupName = self.EscortGroup:GetName() --- local EscortTargets = self.EscortGroup:GetDetectedTargets() --- --- local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets --- --- local EscortTargetMessages = "" --- for EscortTargetID, EscortTarget in pairs( EscortTargets ) do --- local EscortObject = EscortTarget.object --- self:T( EscortObject ) --- if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then --- --- local EscortTargetUnit = UNIT:Find( EscortObject ) --- local EscortTargetUnitName = EscortTargetUnit:GetName() --- --- --- --- -- local EscortTargetIsDetected, --- -- EscortTargetIsVisible, --- -- EscortTargetLastTime, --- -- EscortTargetKnowType, --- -- EscortTargetKnowDistance, --- -- EscortTargetLastPos, --- -- EscortTargetLastVelocity --- -- = self.EscortGroup:IsTargetDetected( EscortObject ) --- -- --- -- self:T( { EscortTargetIsDetected, --- -- EscortTargetIsVisible, --- -- EscortTargetLastTime, --- -- EscortTargetKnowType, --- -- EscortTargetKnowDistance, --- -- EscortTargetLastPos, --- -- EscortTargetLastVelocity } ) --- --- --- local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3() --- local EscortVec3 = self.EscortGroup:GetVec3() --- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + --- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + --- ( EscortTargetUnitVec3.z - EscortVec3.z )^2 --- ) ^ 0.5 / 1000 --- --- self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } ) --- --- if Distance <= 15 then --- --- if not ClientEscortTargets[EscortTargetUnitName] then --- ClientEscortTargets[EscortTargetUnitName] = {} --- end --- ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit --- ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible --- ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type --- ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance --- else --- if ClientEscortTargets[EscortTargetUnitName] then --- ClientEscortTargets[EscortTargetUnitName] = nil --- end --- end --- end --- end --- --- self:T( { "Sorting Targets Table:", ClientEscortTargets } ) --- table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end ) --- self:T( { "Sorted Targets Table:", ClientEscortTargets } ) --- --- -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup. --- self.EscortMenuAttackNearbyTargets:RemoveSubMenus() --- --- if self.EscortMenuTargetAssistance then --- self.EscortMenuTargetAssistance:RemoveSubMenus() --- end --- --- --for MenuIndex = 1, #self.EscortMenuAttackTargets do --- -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } ) --- -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove() --- --end --- --- --- if ClientEscortTargets then --- for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do --- --- for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do --- --- if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then --- --- local EscortTargetMessage = "" --- local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName() --- local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName() --- if ClientEscortTargetData.type then --- EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at " --- else --- EscortTargetMessage = EscortTargetMessage .. "Unknown target at " --- end --- --- local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3() --- local EscortVec3 = self.EscortGroup:GetVec3() --- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 + --- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 + --- ( EscortTargetUnitVec3.z - EscortVec3.z )^2 --- ) ^ 0.5 / 1000 --- --- self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } ) --- if ClientEscortTargetData.visible == false then --- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km" --- else --- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km" --- end --- --- if ClientEscortTargetData.visible then --- EscortTargetMessage = EscortTargetMessage .. ", visual" --- end --- --- if ClientEscortGroupName == EscortGroupName then --- --- MENU_CLIENT_COMMAND:New( self.EscortClient, --- EscortTargetMessage, --- self.EscortMenuAttackNearbyTargets, --- ESCORT._AttackTarget, --- { ParamSelf = self, --- ParamUnit = ClientEscortTargetData.AttackUnit --- } --- ) --- EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage --- else --- if self.EscortMenuTargetAssistance then --- local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance ) --- MENU_CLIENT_COMMAND:New( self.EscortClient, --- EscortTargetMessage, --- MenuTargetAssistance, --- ESCORT._AssistTarget, --- self, --- EscortGroupData.EscortGroup, --- ClientEscortTargetData.AttackUnit --- ) --- end --- end --- else --- ClientEscortTargetData = nil --- end --- end --- end --- --- if EscortTargetMessages ~= "" and self.ReportTargets == true then --- self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient ) --- else --- self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient ) --- end --- end --- --- if self.EscortMenuResumeMission then --- self.EscortMenuResumeMission:RemoveSubMenus() --- --- -- if self.EscortMenuResumeWayPoints then --- -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do --- -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } ) --- -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove() --- -- end --- -- end --- --- local TaskPoints = self:RegisterRoute() --- for WayPointID, WayPoint in pairs( TaskPoints ) do --- local EscortVec3 = self.EscortGroup:GetVec3() --- local Distance = ( ( WayPoint.x - EscortVec3.x )^2 + --- ( WayPoint.y - EscortVec3.z )^2 --- ) ^ 0.5 / 1000 --- MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } ) --- end --- end --- --- return true - end - end - - return false -end ---- This module contains the MISSILETRAINER class. --- --- === --- --- 1) @{MissileTrainer#MISSILETRAINER} class, extends @{Base#BASE} --- =============================================================== --- The @{#MISSILETRAINER} class uses the DCS world messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft, --- the class will destroy the missile within a certain range, to avoid damage to your aircraft. --- It suports the following functionality: --- --- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes. --- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range � --- * Provide alerts when a missile would have killed your aircraft. --- * Provide alerts when the missile self destructs. --- * Enable / Disable and Configure the Missile Trainer using the various menu options. --- --- When running a mission where MISSILETRAINER is used, the following radio menu structure ( 'Radio Menu' -> 'Other (F10)' -> 'MissileTrainer' ) options are available for the players: --- --- * **Messages**: Menu to configure all messages. --- * **Messages On**: Show all messages. --- * **Messages Off**: Disable all messages. --- * **Tracking**: Menu to configure missile tracking messages. --- * **To All**: Shows missile tracking messages to all players. --- * **To Target**: Shows missile tracking messages only to the player where the missile is targetted at. --- * **Tracking On**: Show missile tracking messages. --- * **Tracking Off**: Disable missile tracking messages. --- * **Frequency Increase**: Increases the missile tracking message frequency with one second. --- * **Frequency Decrease**: Decreases the missile tracking message frequency with one second. --- * **Alerts**: Menu to configure alert messages. --- * **To All**: Shows alert messages to all players. --- * **To Target**: Shows alert messages only to the player where the missile is (was) targetted at. --- * **Hits On**: Show missile hit alert messages. --- * **Hits Off**: Disable missile hit alert messages. --- * **Launches On**: Show missile launch messages. --- * **Launches Off**: Disable missile launch messages. --- * **Details**: Menu to configure message details. --- * **Range On**: Shows range information when a missile is fired to a target. --- * **Range Off**: Disable range information when a missile is fired to a target. --- * **Bearing On**: Shows bearing information when a missile is fired to a target. --- * **Bearing Off**: Disable bearing information when a missile is fired to a target. --- * **Distance**: Menu to configure the distance when a missile needs to be destroyed when near to a player, during tracking. This will improve/influence hit calculation accuracy, but has the risk of damaging the aircraft when the missile reaches the aircraft before the distance is measured. --- * **50 meter**: Destroys the missile when the distance to the aircraft is below or equal to 50 meter. --- * **100 meter**: Destroys the missile when the distance to the aircraft is below or equal to 100 meter. --- * **150 meter**: Destroys the missile when the distance to the aircraft is below or equal to 150 meter. --- * **200 meter**: Destroys the missile when the distance to the aircraft is below or equal to 200 meter. --- --- --- 1.1) MISSILETRAINER construction methods: --- ----------------------------------------- --- Create a new MISSILETRAINER object with the @{#MISSILETRAINER.New} method: --- --- * @{#MISSILETRAINER.New}: Creates a new MISSILETRAINER object taking the maximum distance to your aircraft to evaluate when a missile needs to be destroyed. --- --- MISSILETRAINER will collect each unit declared in the mission with a skill level "Client" and "Player", and will monitor the missiles shot at those. --- --- 1.2) MISSILETRAINER initialization methods: --- ------------------------------------------- --- A MISSILETRAINER object will behave differently based on the usage of initialization methods: --- --- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF. --- * @{#MISSILETRAINER.InitTrackingToAll}: Sets by default the missile tracking report for all players or only for those missiles targetted to you. --- * @{#MISSILETRAINER.InitTrackingOnOff}: Sets by default the display of missile tracking report to be ON or OFF. --- * @{#MISSILETRAINER.InitTrackingFrequency}: Increases, decreases the missile tracking message display frequency with the provided time interval in seconds. --- * @{#MISSILETRAINER.InitAlertsToAll}: Sets by default the display of alerts to be shown to all players or only to you. --- * @{#MISSILETRAINER.InitAlertsHitsOnOff}: Sets by default the display of hit alerts ON or OFF. --- * @{#MISSILETRAINER.InitAlertsLaunchesOnOff}: Sets by default the display of launch alerts ON or OFF. --- * @{#MISSILETRAINER.InitRangeOnOff}: Sets by default the display of range information of missiles ON of OFF. --- * @{#MISSILETRAINER.InitBearingOnOff}: Sets by default the display of bearing information of missiles ON of OFF. --- * @{#MISSILETRAINER.InitMenusOnOff}: Allows to configure the options through the radio menu. --- --- === --- --- CREDITS --- ======= --- **Stuka (Danny)** Who you can search on the Eagle Dynamics Forums. --- Working together with Danny has resulted in the MISSILETRAINER class. --- Danny has shared his ideas and together we made a design. --- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback! --- --- @module MissileTrainer --- @author FlightControl - - ---- The MISSILETRAINER class --- @type MISSILETRAINER --- @field Core.Set#SET_CLIENT DBClients --- @extends Core.Base#BASE -MISSILETRAINER = { - ClassName = "MISSILETRAINER", - TrackingMissiles = {}, -} - -function MISSILETRAINER._Alive( Client, self ) - - if self.Briefing then - Client:Message( self.Briefing, 15, "Trainer" ) - end - - if self.MenusOnOff == true then - Client:Message( "Use the 'Radio Menu' -> 'Other (F10)' -> 'Missile Trainer' menu options to change the Missile Trainer settings (for all players).", 15, "Trainer" ) - - Client.MainMenu = MENU_CLIENT:New( Client, "Missile Trainer", nil ) -- Menu#MENU_CLIENT - - Client.MenuMessages = MENU_CLIENT:New( Client, "Messages", Client.MainMenu ) - Client.MenuOn = MENU_CLIENT_COMMAND:New( Client, "Messages On", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = true } ) - Client.MenuOff = MENU_CLIENT_COMMAND:New( Client, "Messages Off", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = false } ) - - Client.MenuTracking = MENU_CLIENT:New( Client, "Tracking", Client.MainMenu ) - Client.MenuTrackingToAll = MENU_CLIENT_COMMAND:New( Client, "To All", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = true } ) - Client.MenuTrackingToTarget = MENU_CLIENT_COMMAND:New( Client, "To Target", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = false } ) - Client.MenuTrackOn = MENU_CLIENT_COMMAND:New( Client, "Tracking On", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingOnOff = true } ) - Client.MenuTrackOff = MENU_CLIENT_COMMAND:New( Client, "Tracking Off", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingOnOff = false } ) - Client.MenuTrackIncrease = MENU_CLIENT_COMMAND:New( Client, "Frequency Increase", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingFrequency = -1 } ) - Client.MenuTrackDecrease = MENU_CLIENT_COMMAND:New( Client, "Frequency Decrease", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingFrequency = 1 } ) - - Client.MenuAlerts = MENU_CLIENT:New( Client, "Alerts", Client.MainMenu ) - Client.MenuAlertsToAll = MENU_CLIENT_COMMAND:New( Client, "To All", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = true } ) - Client.MenuAlertsToTarget = MENU_CLIENT_COMMAND:New( Client, "To Target", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = false } ) - Client.MenuHitsOn = MENU_CLIENT_COMMAND:New( Client, "Hits On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHitsOnOff = true } ) - Client.MenuHitsOff = MENU_CLIENT_COMMAND:New( Client, "Hits Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHitsOnOff = false } ) - Client.MenuLaunchesOn = MENU_CLIENT_COMMAND:New( Client, "Launches On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunchesOnOff = true } ) - Client.MenuLaunchesOff = MENU_CLIENT_COMMAND:New( Client, "Launches Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunchesOnOff = false } ) - - Client.MenuDetails = MENU_CLIENT:New( Client, "Details", Client.MainMenu ) - Client.MenuDetailsDistanceOn = MENU_CLIENT_COMMAND:New( Client, "Range On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRangeOnOff = true } ) - Client.MenuDetailsDistanceOff = MENU_CLIENT_COMMAND:New( Client, "Range Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRangeOnOff = false } ) - Client.MenuDetailsBearingOn = MENU_CLIENT_COMMAND:New( Client, "Bearing On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearingOnOff = true } ) - Client.MenuDetailsBearingOff = MENU_CLIENT_COMMAND:New( Client, "Bearing Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearingOnOff = false } ) - - Client.MenuDistance = MENU_CLIENT:New( Client, "Set distance to plane", Client.MainMenu ) - Client.MenuDistance50 = MENU_CLIENT_COMMAND:New( Client, "50 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 50 / 1000 } ) - Client.MenuDistance100 = MENU_CLIENT_COMMAND:New( Client, "100 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 100 / 1000 } ) - Client.MenuDistance150 = MENU_CLIENT_COMMAND:New( Client, "150 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 150 / 1000 } ) - Client.MenuDistance200 = MENU_CLIENT_COMMAND:New( Client, "200 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 200 / 1000 } ) - else - if Client.MainMenu then - Client.MainMenu:Remove() - end - end - - local ClientID = Client:GetID() - self:T( ClientID ) - if not self.TrackingMissiles[ClientID] then - self.TrackingMissiles[ClientID] = {} - end - self.TrackingMissiles[ClientID].Client = Client - if not self.TrackingMissiles[ClientID].MissileData then - self.TrackingMissiles[ClientID].MissileData = {} - end -end - ---- Creates the main object which is handling missile tracking. --- When a missile is fired a SCHEDULER is set off that follows the missile. When near a certain a client player, the missile will be destroyed. --- @param #MISSILETRAINER self --- @param #number Distance The distance in meters when a tracked missile needs to be destroyed when close to a player. --- @param #string Briefing (Optional) Will show a text to the players when starting their mission. Can be used for briefing purposes. --- @return #MISSILETRAINER -function MISSILETRAINER:New( Distance, Briefing ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( Distance ) - - if Briefing then - self.Briefing = Briefing - end - - self.Schedulers = {} - self.SchedulerID = 0 - - self.MessageInterval = 2 - self.MessageLastTime = timer.getTime() - - self.Distance = Distance / 1000 - - self:HandleEvent( EVENTS.Shot ) - - self.DBClients = SET_CLIENT:New():FilterStart() - - --- for ClientID, Client in pairs( self.DBClients.Database ) do --- self:E( "ForEach:" .. Client.UnitName ) --- Client:Alive( self._Alive, self ) --- end --- - self.DBClients:ForEachClient( - function( Client ) - self:E( "ForEach:" .. Client.UnitName ) - Client:Alive( self._Alive, self ) - end - ) - - - --- self.DB:ForEachClient( --- --- @param Wrapper.Client#CLIENT Client --- function( Client ) --- --- ... actions ... --- --- end --- ) - - self.MessagesOnOff = true - - self.TrackingToAll = false - self.TrackingOnOff = true - self.TrackingFrequency = 3 - - self.AlertsToAll = true - self.AlertsHitsOnOff = true - self.AlertsLaunchesOnOff = true - - self.DetailsRangeOnOff = true - self.DetailsBearingOnOff = true - - self.MenusOnOff = true - - self.TrackingMissiles = {} - - self.TrackingScheduler = SCHEDULER:New( self, self._TrackMissiles, {}, 0.5, 0.05, 0 ) - - return self -end - --- Initialization methods. - - - ---- Sets by default the display of any message to be ON or OFF. --- @param #MISSILETRAINER self --- @param #boolean MessagesOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitMessagesOnOff( MessagesOnOff ) - self:F( MessagesOnOff ) - - self.MessagesOnOff = MessagesOnOff - if self.MessagesOnOff == true then - MESSAGE:New( "Messages ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Messages OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the missile tracking report for all players or only for those missiles targetted to you. --- @param #MISSILETRAINER self --- @param #boolean TrackingToAll true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitTrackingToAll( TrackingToAll ) - self:F( TrackingToAll ) - - self.TrackingToAll = TrackingToAll - if self.TrackingToAll == true then - MESSAGE:New( "Missile tracking to all players ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Missile tracking to all players OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the display of missile tracking report to be ON or OFF. --- @param #MISSILETRAINER self --- @param #boolean TrackingOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitTrackingOnOff( TrackingOnOff ) - self:F( TrackingOnOff ) - - self.TrackingOnOff = TrackingOnOff - if self.TrackingOnOff == true then - MESSAGE:New( "Missile tracking ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Missile tracking OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Increases, decreases the missile tracking message display frequency with the provided time interval in seconds. --- The default frequency is a 3 second interval, so the Tracking Frequency parameter specifies the increase or decrease from the default 3 seconds or the last frequency update. --- @param #MISSILETRAINER self --- @param #number TrackingFrequency Provide a negative or positive value in seconds to incraese or decrease the display frequency. --- @return #MISSILETRAINER self -function MISSILETRAINER:InitTrackingFrequency( TrackingFrequency ) - self:F( TrackingFrequency ) - - self.TrackingFrequency = self.TrackingFrequency + TrackingFrequency - if self.TrackingFrequency < 0.5 then - self.TrackingFrequency = 0.5 - end - if self.TrackingFrequency then - MESSAGE:New( "Missile tracking frequency is " .. self.TrackingFrequency .. " seconds.", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the display of alerts to be shown to all players or only to you. --- @param #MISSILETRAINER self --- @param #boolean AlertsToAll true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitAlertsToAll( AlertsToAll ) - self:F( AlertsToAll ) - - self.AlertsToAll = AlertsToAll - if self.AlertsToAll == true then - MESSAGE:New( "Alerts to all players ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Alerts to all players OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the display of hit alerts ON or OFF. --- @param #MISSILETRAINER self --- @param #boolean AlertsHitsOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitAlertsHitsOnOff( AlertsHitsOnOff ) - self:F( AlertsHitsOnOff ) - - self.AlertsHitsOnOff = AlertsHitsOnOff - if self.AlertsHitsOnOff == true then - MESSAGE:New( "Alerts Hits ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Alerts Hits OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the display of launch alerts ON or OFF. --- @param #MISSILETRAINER self --- @param #boolean AlertsLaunchesOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitAlertsLaunchesOnOff( AlertsLaunchesOnOff ) - self:F( AlertsLaunchesOnOff ) - - self.AlertsLaunchesOnOff = AlertsLaunchesOnOff - if self.AlertsLaunchesOnOff == true then - MESSAGE:New( "Alerts Launches ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Alerts Launches OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the display of range information of missiles ON of OFF. --- @param #MISSILETRAINER self --- @param #boolean DetailsRangeOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitRangeOnOff( DetailsRangeOnOff ) - self:F( DetailsRangeOnOff ) - - self.DetailsRangeOnOff = DetailsRangeOnOff - if self.DetailsRangeOnOff == true then - MESSAGE:New( "Range display ON", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Range display OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Sets by default the display of bearing information of missiles ON of OFF. --- @param #MISSILETRAINER self --- @param #boolean DetailsBearingOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitBearingOnOff( DetailsBearingOnOff ) - self:F( DetailsBearingOnOff ) - - self.DetailsBearingOnOff = DetailsBearingOnOff - if self.DetailsBearingOnOff == true then - MESSAGE:New( "Bearing display OFF", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Bearing display OFF", 15, "Menu" ):ToAll() - end - - return self -end - ---- Enables / Disables the menus. --- @param #MISSILETRAINER self --- @param #boolean MenusOnOff true or false --- @return #MISSILETRAINER self -function MISSILETRAINER:InitMenusOnOff( MenusOnOff ) - self:F( MenusOnOff ) - - self.MenusOnOff = MenusOnOff - if self.MenusOnOff == true then - MESSAGE:New( "Menus are ENABLED (only when a player rejoins a slot)", 15, "Menu" ):ToAll() - else - MESSAGE:New( "Menus are DISABLED", 15, "Menu" ):ToAll() - end - - return self -end - - --- Menu functions - -function MISSILETRAINER._MenuMessages( MenuParameters ) - - local self = MenuParameters.MenuSelf - - if MenuParameters.MessagesOnOff ~= nil then - self:InitMessagesOnOff( MenuParameters.MessagesOnOff ) - end - - if MenuParameters.TrackingToAll ~= nil then - self:InitTrackingToAll( MenuParameters.TrackingToAll ) - end - - if MenuParameters.TrackingOnOff ~= nil then - self:InitTrackingOnOff( MenuParameters.TrackingOnOff ) - end - - if MenuParameters.TrackingFrequency ~= nil then - self:InitTrackingFrequency( MenuParameters.TrackingFrequency ) - end - - if MenuParameters.AlertsToAll ~= nil then - self:InitAlertsToAll( MenuParameters.AlertsToAll ) - end - - if MenuParameters.AlertsHitsOnOff ~= nil then - self:InitAlertsHitsOnOff( MenuParameters.AlertsHitsOnOff ) - end - - if MenuParameters.AlertsLaunchesOnOff ~= nil then - self:InitAlertsLaunchesOnOff( MenuParameters.AlertsLaunchesOnOff ) - end - - if MenuParameters.DetailsRangeOnOff ~= nil then - self:InitRangeOnOff( MenuParameters.DetailsRangeOnOff ) - end - - if MenuParameters.DetailsBearingOnOff ~= nil then - self:InitBearingOnOff( MenuParameters.DetailsBearingOnOff ) - end - - if MenuParameters.Distance ~= nil then - self.Distance = MenuParameters.Distance - MESSAGE:New( "Hit detection distance set to " .. self.Distance .. " meters", 15, "Menu" ):ToAll() - end - -end - ---- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. --- @param #MISSILETRAINER self --- @param Core.Event#EVENTDATA EventData -function MISSILETRAINER:OnEventShot( EVentData ) - self:F( { EVentData } ) - - local TrainerSourceDCSUnit = EVentData.IniDCSUnit - local TrainerSourceDCSUnitName = EVentData.IniDCSUnitName - local TrainerWeapon = EVentData.Weapon -- Identify the weapon fired - local TrainerWeaponName = EVentData.WeaponName -- return weapon type - - self:T( "Missile Launched = " .. TrainerWeaponName ) - - local TrainerTargetDCSUnit = TrainerWeapon:getTarget() -- Identify target - if TrainerTargetDCSUnit then - local TrainerTargetDCSUnitName = Unit.getName( TrainerTargetDCSUnit ) - local TrainerTargetSkill = _DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill - - self:T(TrainerTargetDCSUnitName ) - - local Client = self.DBClients:FindClient( TrainerTargetDCSUnitName ) - if Client then - - local TrainerSourceUnit = UNIT:Find( TrainerSourceDCSUnit ) - local TrainerTargetUnit = UNIT:Find( TrainerTargetDCSUnit ) - - if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then - - local Message = MESSAGE:New( - string.format( "%s launched a %s", - TrainerSourceUnit:GetTypeName(), - TrainerWeaponName - ) .. self:_AddRange( Client, TrainerWeapon ) .. self:_AddBearing( Client, TrainerWeapon ), 5, "Launch Alert" ) - - if self.AlertsToAll then - Message:ToAll() - else - Message:ToClient( Client ) - end - end - - local ClientID = Client:GetID() - self:T( ClientID ) - local MissileData = {} - MissileData.TrainerSourceUnit = TrainerSourceUnit - MissileData.TrainerWeapon = TrainerWeapon - MissileData.TrainerTargetUnit = TrainerTargetUnit - MissileData.TrainerWeaponTypeName = TrainerWeapon:getTypeName() - MissileData.TrainerWeaponLaunched = true - table.insert( self.TrackingMissiles[ClientID].MissileData, MissileData ) - --self:T( self.TrackingMissiles ) - end - else - -- TODO: some weapons don't know the target unit... Need to develop a workaround for this. - if ( TrainerWeapon:getTypeName() == "9M311" ) then - SCHEDULER:New( TrainerWeapon, TrainerWeapon.destroy, {}, 1 ) - else - end - end -end - -function MISSILETRAINER:_AddRange( Client, TrainerWeapon ) - - local RangeText = "" - - if self.DetailsRangeOnOff then - - local PositionMissile = TrainerWeapon:getPoint() - local TargetVec3 = Client:GetVec3() - - local Range = ( ( PositionMissile.x - TargetVec3.x )^2 + - ( PositionMissile.y - TargetVec3.y )^2 + - ( PositionMissile.z - TargetVec3.z )^2 - ) ^ 0.5 / 1000 - - RangeText = string.format( ", at %4.2fkm", Range ) - end - - return RangeText -end - -function MISSILETRAINER:_AddBearing( Client, TrainerWeapon ) - - local BearingText = "" - - if self.DetailsBearingOnOff then - - local PositionMissile = TrainerWeapon:getPoint() - local TargetVec3 = Client:GetVec3() - - self:T2( { TargetVec3, PositionMissile }) - - local DirectionVector = { x = PositionMissile.x - TargetVec3.x, y = PositionMissile.y - TargetVec3.y, z = PositionMissile.z - TargetVec3.z } - local DirectionRadians = math.atan2( DirectionVector.z, DirectionVector.x ) - --DirectionRadians = DirectionRadians + routines.getNorthCorrection( PositionTarget ) - if DirectionRadians < 0 then - DirectionRadians = DirectionRadians + 2 * math.pi - end - local DirectionDegrees = DirectionRadians * 180 / math.pi - - BearingText = string.format( ", %d degrees", DirectionDegrees ) - end - - return BearingText -end - - -function MISSILETRAINER:_TrackMissiles() - self:F2() - - - local ShowMessages = false - if self.MessagesOnOff and self.MessageLastTime + self.TrackingFrequency <= timer.getTime() then - self.MessageLastTime = timer.getTime() - ShowMessages = true - end - - -- ALERTS PART - - -- Loop for all Player Clients to check the alerts and deletion of missiles. - for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do - - local Client = ClientData.Client - self:T2( { Client:GetName() } ) - - for MissileDataID, MissileData in pairs( ClientData.MissileData ) do - self:T3( MissileDataID ) - - local TrainerSourceUnit = MissileData.TrainerSourceUnit - local TrainerWeapon = MissileData.TrainerWeapon - local TrainerTargetUnit = MissileData.TrainerTargetUnit - local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName - local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched - - if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then - local PositionMissile = TrainerWeapon:getPosition().p - local TargetVec3 = Client:GetVec3() - - local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 + - ( PositionMissile.y - TargetVec3.y )^2 + - ( PositionMissile.z - TargetVec3.z )^2 - ) ^ 0.5 / 1000 - - if Distance <= self.Distance then - -- Hit alert - TrainerWeapon:destroy() - if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then - - self:T( "killed" ) - - local Message = MESSAGE:New( - string.format( "%s launched by %s killed %s", - TrainerWeapon:getTypeName(), - TrainerSourceUnit:GetTypeName(), - TrainerTargetUnit:GetPlayerName() - ), 15, "Hit Alert" ) - - if self.AlertsToAll == true then - Message:ToAll() - else - Message:ToClient( Client ) - end - - MissileData = nil - table.remove( ClientData.MissileData, MissileDataID ) - self:T(ClientData.MissileData) - end - end - else - if not ( TrainerWeapon and TrainerWeapon:isExist() ) then - if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then - -- Weapon does not exist anymore. Delete from Table - local Message = MESSAGE:New( - string.format( "%s launched by %s self destructed!", - TrainerWeaponTypeName, - TrainerSourceUnit:GetTypeName() - ), 5, "Tracking" ) - - if self.AlertsToAll == true then - Message:ToAll() - else - Message:ToClient( Client ) - end - end - MissileData = nil - table.remove( ClientData.MissileData, MissileDataID ) - self:T( ClientData.MissileData ) - end - end - end - end - - if ShowMessages == true and self.MessagesOnOff == true and self.TrackingOnOff == true then -- Only do this when tracking information needs to be displayed. - - -- TRACKING PART - - -- For the current client, the missile range and bearing details are displayed To the Player Client. - -- For the other clients, the missile range and bearing details are displayed To the other Player Clients. - -- To achieve this, a cross loop is done for each Player Client <-> Other Player Client missile information. - - -- Main Player Client loop - for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do - - local Client = ClientData.Client - self:T2( { Client:GetName() } ) - - - ClientData.MessageToClient = "" - ClientData.MessageToAll = "" - - -- Other Players Client loop - for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do - - for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do - self:T3( MissileDataID ) - - local TrainerSourceUnit = MissileData.TrainerSourceUnit - local TrainerWeapon = MissileData.TrainerWeapon - local TrainerTargetUnit = MissileData.TrainerTargetUnit - local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName - local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched - - if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then - - if ShowMessages == true then - local TrackingTo - TrackingTo = string.format( " -> %s", - TrainerWeaponTypeName - ) - - if ClientDataID == TrackingDataID then - if ClientData.MessageToClient == "" then - ClientData.MessageToClient = "Missiles to You:\n" - end - ClientData.MessageToClient = ClientData.MessageToClient .. TrackingTo .. self:_AddRange( ClientData.Client, TrainerWeapon ) .. self:_AddBearing( ClientData.Client, TrainerWeapon ) .. "\n" - else - if self.TrackingToAll == true then - if ClientData.MessageToAll == "" then - ClientData.MessageToAll = "Missiles to other Players:\n" - end - ClientData.MessageToAll = ClientData.MessageToAll .. TrackingTo .. self:_AddRange( ClientData.Client, TrainerWeapon ) .. self:_AddBearing( ClientData.Client, TrainerWeapon ) .. " ( " .. TrainerTargetUnit:GetPlayerName() .. " )\n" - end - end - end - end - end - end - - -- Once the Player Client and the Other Player Client tracking messages are prepared, show them. - if ClientData.MessageToClient ~= "" or ClientData.MessageToAll ~= "" then - local Message = MESSAGE:New( ClientData.MessageToClient .. ClientData.MessageToAll, 1, "Tracking" ):ToClient( Client ) - end - end - end - - return true -end ---- This module contains the AIRBASEPOLICE classes. --- --- === --- --- 1) @{AirbasePolice#AIRBASEPOLICE_BASE} class, extends @{Base#BASE} --- ================================================================== --- The @{AirbasePolice#AIRBASEPOLICE_BASE} class provides the main methods to monitor CLIENT behaviour at airbases. --- CLIENTS should not be allowed to: --- --- * Don't taxi faster than 40 km/h. --- * Don't take-off on taxiways. --- * Avoid to hit other planes on the airbase. --- * Obey ground control orders. --- --- 2) @{AirbasePolice#AIRBASEPOLICE_CAUCASUS} class, extends @{AirbasePolice#AIRBASEPOLICE_BASE} --- ============================================================================================= --- All the airbases on the caucasus map can be monitored using this class. --- If you want to monitor specific airbases, you need to use the @{#AIRBASEPOLICE_BASE.Monitor}() method, which takes a table or airbase names. --- The following names can be given: --- * AnapaVityazevo --- * Batumi --- * Beslan --- * Gelendzhik --- * Gudauta --- * Kobuleti --- * KrasnodarCenter --- * KrasnodarPashkovsky --- * Krymsk --- * Kutaisi --- * MaykopKhanskaya --- * MineralnyeVody --- * Mozdok --- * Nalchik --- * Novorossiysk --- * SenakiKolkhi --- * SochiAdler --- * Soganlug --- * SukhumiBabushara --- * TbilisiLochini --- * Vaziani --- --- 3) @{AirbasePolice#AIRBASEPOLICE_NEVADA} class, extends @{AirbasePolice#AIRBASEPOLICE_BASE} --- ============================================================================================= --- All the airbases on the NEVADA map can be monitored using this class. --- If you want to monitor specific airbases, you need to use the @{#AIRBASEPOLICE_BASE.Monitor}() method, which takes a table or airbase names. --- The following names can be given: --- * Nellis --- * McCarran --- * Creech --- * Groom Lake --- --- ### Contributions: Dutch Baron - Concept & Testing --- ### Author: FlightControl - Framework Design & Programming --- --- @module AirbasePolice - - - - - ---- @type AIRBASEPOLICE_BASE --- @field Core.Set#SET_CLIENT SetClient --- @extends Core.Base#BASE - -AIRBASEPOLICE_BASE = { - ClassName = "AIRBASEPOLICE_BASE", - SetClient = nil, - Airbases = nil, - AirbaseNames = nil, -} - - ---- Creates a new AIRBASEPOLICE_BASE object. --- @param #AIRBASEPOLICE_BASE self --- @param SetClient A SET_CLIENT object that will contain the CLIENT objects to be monitored if they follow the rules of the airbase. --- @param Airbases A table of Airbase Names. --- @return #AIRBASEPOLICE_BASE self -function AIRBASEPOLICE_BASE:New( SetClient, Airbases ) - - -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) - self:E( { self.ClassName, SetClient, Airbases } ) - - self.SetClient = SetClient - self.Airbases = Airbases - - for AirbaseID, Airbase in pairs( self.Airbases ) do - Airbase.ZoneBoundary = ZONE_POLYGON_BASE:New( "Boundary", Airbase.PointsBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - for PointsRunwayID, PointsRunway in pairs( Airbase.PointsRunways ) do - Airbase.ZoneRunways[PointsRunwayID] = ZONE_POLYGON_BASE:New( "Runway " .. PointsRunwayID, PointsRunway ):SmokeZone(SMOKECOLOR.Red):Flush() - end - end - --- -- Template --- local TemplateBoundary = GROUP:FindByName( "Template Boundary" ) --- self.Airbases.Template.ZoneBoundary = ZONE_POLYGON:New( "Template Boundary", TemplateBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local TemplateRunway1 = GROUP:FindByName( "Template Runway 1" ) --- self.Airbases.Template.ZoneRunways[1] = ZONE_POLYGON:New( "Template Runway 1", TemplateRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - - self.SetClient:ForEachClient( - --- @param Wrapper.Client#CLIENT Client - function( Client ) - Client:SetState( self, "Speeding", false ) - Client:SetState( self, "Warnings", 0) - Client:SetState( self, "Taxi", false ) - end - ) - - self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, {}, 0, 2, 0.05 ) - - return self -end - ---- @type AIRBASEPOLICE_BASE.AirbaseNames --- @list <#string> - ---- Monitor a table of airbase names. --- @param #AIRBASEPOLICE_BASE self --- @param #AIRBASEPOLICE_BASE.AirbaseNames AirbaseNames A list of AirbaseNames to monitor. If this parameters is nil, then all airbases will be monitored. --- @return #AIRBASEPOLICE_BASE self -function AIRBASEPOLICE_BASE:Monitor( AirbaseNames ) - - if AirbaseNames then - if type( AirbaseNames ) == "table" then - self.AirbaseNames = AirbaseNames - else - self.AirbaseNames = { AirbaseNames } - end - end -end - ---- @param #AIRBASEPOLICE_BASE self -function AIRBASEPOLICE_BASE:_AirbaseMonitor() - - for AirbaseID, Airbase in pairs( self.Airbases ) do - - if not self.AirbaseNames or self.AirbaseNames[AirbaseID] then - - self:E( AirbaseID ) - - self.SetClient:ForEachClientInZone( Airbase.ZoneBoundary, - - --- @param Wrapper.Client#CLIENT Client - function( Client ) - - self:E( Client.UnitName ) - if Client:IsAlive() then - local NotInRunwayZone = true - for ZoneRunwayID, ZoneRunway in pairs( Airbase.ZoneRunways ) do - NotInRunwayZone = ( Client:IsNotInZone( ZoneRunway ) == true ) and NotInRunwayZone or false - end - - if NotInRunwayZone then - local Taxi = self:GetState( self, "Taxi" ) - self:E( Taxi ) - if Taxi == false then - Client:Message( "Welcome at " .. AirbaseID .. ". The maximum taxiing speed is " .. Airbase.MaximumSpeed " km/h.", 20, "ATC" ) - self:SetState( self, "Taxi", true ) - end - - -- TODO: GetVelocityKMH function usage - local VelocityVec3 = Client:GetVelocity() - local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec - local Velocity = Velocity * 3.6 -- now it is in km/h. - -- MESSAGE:New( "Velocity = " .. Velocity, 1 ):ToAll() - local IsAboveRunway = Client:IsAboveRunway() - local IsOnGround = Client:InAir() == false - self:T( IsAboveRunway, IsOnGround ) - - if IsAboveRunway and IsOnGround then - - if Velocity > Airbase.MaximumSpeed then - local IsSpeeding = Client:GetState( self, "Speeding" ) - - if IsSpeeding == true then - local SpeedingWarnings = Client:GetState( self, "Warnings" ) - self:T( SpeedingWarnings ) - - if SpeedingWarnings <= 3 then - Client:Message( "You are speeding on the taxiway! Slow down or you will be removed from this airbase! Your current velocity is " .. string.format( "%2.0f km/h", Velocity ), 5, "Warning " .. SpeedingWarnings .. " / 3" ) - Client:SetState( self, "Warnings", SpeedingWarnings + 1 ) - else - MESSAGE:New( "Player " .. Client:GetPlayerName() .. " has been removed from the airbase, due to a speeding violation ...", 10, "Airbase Police" ):ToAll() - Client:Destroy() - trigger.action.setUserFlag( "AIRCRAFT_"..Client:GetID(), 100) - Client:SetState( self, "Speeding", false ) - Client:SetState( self, "Warnings", 0 ) - end - - else - Client:Message( "You are speeding on the taxiway, slow down now! Your current velocity is " .. string.format( "%2.0f km/h", Velocity ), 5, "Attention! " ) - Client:SetState( self, "Speeding", true ) - Client:SetState( self, "Warnings", 1 ) - end - - else - Client:SetState( self, "Speeding", false ) - Client:SetState( self, "Warnings", 0 ) - end - end - - else - Client:SetState( self, "Speeding", false ) - Client:SetState( self, "Warnings", 0 ) - local Taxi = self:GetState( self, "Taxi" ) - if Taxi == true then - Client:Message( "You have progressed to the runway ... Await take-off clearance ...", 20, "ATC" ) - self:SetState( self, "Taxi", false ) - end - end - end - end - ) - end - end - - return true -end - - ---- @type AIRBASEPOLICE_CAUCASUS --- @field Core.Set#SET_CLIENT SetClient --- @extends #AIRBASEPOLICE_BASE - -AIRBASEPOLICE_CAUCASUS = { - ClassName = "AIRBASEPOLICE_CAUCASUS", - Airbases = { - AnapaVityazevo = { - PointsBoundary = { - [1]={["y"]=242234.85714287,["x"]=-6616.5714285726,}, - [2]={["y"]=241060.57142858,["x"]=-5585.142857144,}, - [3]={["y"]=243806.2857143,["x"]=-3962.2857142868,}, - [4]={["y"]=245240.57142858,["x"]=-4816.5714285726,}, - [5]={["y"]=244783.42857144,["x"]=-5630.8571428583,}, - [6]={["y"]=243800.57142858,["x"]=-5065.142857144,}, - [7]={["y"]=242232.00000001,["x"]=-6622.2857142868,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=242140.57142858,["x"]=-6478.8571428583,}, - [2]={["y"]=242188.57142858,["x"]=-6522.0000000011,}, - [3]={["y"]=244124.2857143,["x"]=-4344.0000000011,}, - [4]={["y"]=244068.2857143,["x"]=-4296.5714285726,}, - [5]={["y"]=242140.57142858,["x"]=-6480.0000000011,} - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Batumi = { - PointsBoundary = { - [1]={["y"]=617567.14285714,["x"]=-355313.14285715,}, - [2]={["y"]=616181.42857142,["x"]=-354800.28571429,}, - [3]={["y"]=616007.14285714,["x"]=-355128.85714286,}, - [4]={["y"]=618230,["x"]=-356914.57142858,}, - [5]={["y"]=618727.14285714,["x"]=-356166,}, - [6]={["y"]=617572.85714285,["x"]=-355308.85714286,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=616442.28571429,["x"]=-355090.28571429,}, - [2]={["y"]=618450.57142857,["x"]=-356522,}, - [3]={["y"]=618407.71428571,["x"]=-356584.85714286,}, - [4]={["y"]=618361.99999999,["x"]=-356554.85714286,}, - [5]={["y"]=618324.85714285,["x"]=-356599.14285715,}, - [6]={["y"]=618250.57142856,["x"]=-356543.42857143,}, - [7]={["y"]=618257.7142857,["x"]=-356496.28571429,}, - [8]={["y"]=618237.7142857,["x"]=-356459.14285715,}, - [9]={["y"]=616555.71428571,["x"]=-355258.85714286,}, - [10]={["y"]=616486.28571428,["x"]=-355280.57142858,}, - [11]={["y"]=616410.57142856,["x"]=-355227.71428572,}, - [12]={["y"]=616441.99999999,["x"]=-355179.14285715,}, - [13]={["y"]=616401.99999999,["x"]=-355147.71428572,}, - [14]={["y"]=616441.42857142,["x"]=-355092.57142858,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Beslan = { - PointsBoundary = { - [1]={["y"]=842082.57142857,["x"]=-148445.14285715,}, - [2]={["y"]=845237.71428572,["x"]=-148639.71428572,}, - [3]={["y"]=845232,["x"]=-148765.42857143,}, - [4]={["y"]=844220.57142857,["x"]=-149168.28571429,}, - [5]={["y"]=843274.85714286,["x"]=-149125.42857143,}, - [6]={["y"]=842077.71428572,["x"]=-148554,}, - [7]={["y"]=842083.42857143,["x"]=-148445.42857143,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=842104.57142857,["x"]=-148460.57142857,}, - [2]={["y"]=845225.71428572,["x"]=-148656,}, - [3]={["y"]=845220.57142858,["x"]=-148750,}, - [4]={["y"]=842098.85714286,["x"]=-148556.28571429,}, - [5]={["y"]=842104,["x"]=-148460.28571429,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Gelendzhik = { - PointsBoundary = { - [1]={["y"]=297856.00000001,["x"]=-51151.428571429,}, - [2]={["y"]=299044.57142858,["x"]=-49720.000000001,}, - [3]={["y"]=298861.71428572,["x"]=-49580.000000001,}, - [4]={["y"]=298198.85714286,["x"]=-49842.857142858,}, - [5]={["y"]=297990.28571429,["x"]=-50151.428571429,}, - [6]={["y"]=297696.00000001,["x"]=-51054.285714286,}, - [7]={["y"]=297850.28571429,["x"]=-51160.000000001,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=297834.00000001,["x"]=-51107.428571429,}, - [2]={["y"]=297786.57142858,["x"]=-51068.857142858,}, - [3]={["y"]=298946.57142858,["x"]=-49686.000000001,}, - [4]={["y"]=298993.14285715,["x"]=-49725.714285715,}, - [5]={["y"]=297835.14285715,["x"]=-51107.714285715,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Gudauta = { - PointsBoundary = { - [1]={["y"]=517246.57142857,["x"]=-197850.28571429,}, - [2]={["y"]=516749.42857142,["x"]=-198070.28571429,}, - [3]={["y"]=515755.14285714,["x"]=-197598.85714286,}, - [4]={["y"]=515369.42857142,["x"]=-196538.85714286,}, - [5]={["y"]=515623.71428571,["x"]=-195618.85714286,}, - [6]={["y"]=515946.57142857,["x"]=-195510.28571429,}, - [7]={["y"]=517243.71428571,["x"]=-197858.85714286,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=517096.57142857,["x"]=-197804.57142857,}, - [2]={["y"]=515880.85714285,["x"]=-195590.28571429,}, - [3]={["y"]=515812.28571428,["x"]=-195628.85714286,}, - [4]={["y"]=517036.57142857,["x"]=-197834.57142857,}, - [5]={["y"]=517097.99999999,["x"]=-197807.42857143,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Kobuleti = { - PointsBoundary = { - [1]={["y"]=634427.71428571,["x"]=-318290.28571429,}, - [2]={["y"]=635033.42857143,["x"]=-317550.2857143,}, - [3]={["y"]=635864.85714286,["x"]=-317333.14285715,}, - [4]={["y"]=636967.71428571,["x"]=-317261.71428572,}, - [5]={["y"]=637144.85714286,["x"]=-317913.14285715,}, - [6]={["y"]=634630.57142857,["x"]=-318687.42857144,}, - [7]={["y"]=634424.85714286,["x"]=-318290.2857143,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=634509.71428571,["x"]=-318339.42857144,}, - [2]={["y"]=636767.42857143,["x"]=-317516.57142858,}, - [3]={["y"]=636790,["x"]=-317575.71428572,}, - [4]={["y"]=634531.42857143,["x"]=-318398.00000001,}, - [5]={["y"]=634510.28571429,["x"]=-318339.71428572,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - KrasnodarCenter = { - PointsBoundary = { - [1]={["y"]=366680.28571429,["x"]=11699.142857142,}, - [2]={["y"]=366654.28571429,["x"]=11225.142857142,}, - [3]={["y"]=367497.14285715,["x"]=11082.285714285,}, - [4]={["y"]=368025.71428572,["x"]=10396.57142857,}, - [5]={["y"]=369854.28571429,["x"]=11367.999999999,}, - [6]={["y"]=369840.00000001,["x"]=11910.857142856,}, - [7]={["y"]=366682.57142858,["x"]=11697.999999999,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=369205.42857144,["x"]=11789.142857142,}, - [2]={["y"]=369209.71428572,["x"]=11714.857142856,}, - [3]={["y"]=366699.71428572,["x"]=11581.714285713,}, - [4]={["y"]=366698.28571429,["x"]=11659.142857142,}, - [5]={["y"]=369208.85714286,["x"]=11788.57142857,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - KrasnodarPashkovsky = { - PointsBoundary = { - [1]={["y"]=386754,["x"]=6476.5714285703,}, - [2]={["y"]=389182.57142858,["x"]=8722.2857142846,}, - [3]={["y"]=388832.57142858,["x"]=9086.5714285703,}, - [4]={["y"]=386961.14285715,["x"]=7707.9999999989,}, - [5]={["y"]=385404,["x"]=9179.4285714274,}, - [6]={["y"]=383239.71428572,["x"]=7386.5714285703,}, - [7]={["y"]=383954,["x"]=6486.5714285703,}, - [8]={["y"]=385775.42857143,["x"]=8097.9999999989,}, - [9]={["y"]=386804,["x"]=7319.4285714274,}, - [10]={["y"]=386375.42857143,["x"]=6797.9999999989,}, - [11]={["y"]=386746.85714286,["x"]=6472.2857142846,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=385891.14285715,["x"]=8416.5714285703,}, - [2]={["y"]=385842.28571429,["x"]=8467.9999999989,}, - [3]={["y"]=384180.85714286,["x"]=6917.1428571417,}, - [4]={["y"]=384228.57142858,["x"]=6867.7142857132,}, - [5]={["y"]=385891.14285715,["x"]=8416.5714285703,}, - }, - [2] = { - [1]={["y"]=386714.85714286,["x"]=6674.857142856,}, - [2]={["y"]=386757.71428572,["x"]=6627.7142857132,}, - [3]={["y"]=389028.57142858,["x"]=8741.4285714275,}, - [4]={["y"]=388981.71428572,["x"]=8790.5714285703,}, - [5]={["y"]=386714.57142858,["x"]=6674.5714285703,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Krymsk = { - PointsBoundary = { - [1]={["y"]=293338.00000001,["x"]=-7575.4285714297,}, - [2]={["y"]=295199.42857144,["x"]=-5434.0000000011,}, - [3]={["y"]=295595.14285715,["x"]=-6239.7142857154,}, - [4]={["y"]=294152.2857143,["x"]=-8325.4285714297,}, - [5]={["y"]=293345.14285715,["x"]=-7596.8571428582,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=293522.00000001,["x"]=-7567.4285714297,}, - [2]={["y"]=293578.57142858,["x"]=-7616.0000000011,}, - [3]={["y"]=295246.00000001,["x"]=-5591.142857144,}, - [4]={["y"]=295187.71428573,["x"]=-5546.0000000011,}, - [5]={["y"]=293523.14285715,["x"]=-7568.2857142868,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Kutaisi = { - PointsBoundary = { - [1]={["y"]=682087.42857143,["x"]=-284512.85714286,}, - [2]={["y"]=685387.42857143,["x"]=-283662.85714286,}, - [3]={["y"]=685294.57142857,["x"]=-284977.14285715,}, - [4]={["y"]=682744.57142857,["x"]=-286505.71428572,}, - [5]={["y"]=682094.57142857,["x"]=-284527.14285715,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=682638,["x"]=-285202.28571429,}, - [2]={["y"]=685050.28571429,["x"]=-284507.42857144,}, - [3]={["y"]=685068.85714286,["x"]=-284578.85714286,}, - [4]={["y"]=682657.42857143,["x"]=-285264.28571429,}, - [5]={["y"]=682638.28571429,["x"]=-285202.85714286,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - MaykopKhanskaya = { - PointsBoundary = { - [1]={["y"]=456876.28571429,["x"]=-27665.42857143,}, - [2]={["y"]=457800,["x"]=-28392.857142858,}, - [3]={["y"]=459368.57142857,["x"]=-26378.571428573,}, - [4]={["y"]=459425.71428572,["x"]=-25242.857142858,}, - [5]={["y"]=458961.42857143,["x"]=-24964.285714287,}, - [6]={["y"]=456878.57142857,["x"]=-27667.714285715,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=457005.42857143,["x"]=-27668.000000001,}, - [2]={["y"]=459028.85714286,["x"]=-25168.857142858,}, - [3]={["y"]=459082.57142857,["x"]=-25216.857142858,}, - [4]={["y"]=457060,["x"]=-27714.285714287,}, - [5]={["y"]=457004.57142857,["x"]=-27669.714285715,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - MineralnyeVody = { - PointsBoundary = { - [1]={["y"]=703857.14285714,["x"]=-50226.000000002,}, - [2]={["y"]=707385.71428571,["x"]=-51911.714285716,}, - [3]={["y"]=707595.71428571,["x"]=-51434.857142859,}, - [4]={["y"]=707900,["x"]=-51568.857142859,}, - [5]={["y"]=707542.85714286,["x"]=-52326.000000002,}, - [6]={["y"]=706628.57142857,["x"]=-52568.857142859,}, - [7]={["y"]=705142.85714286,["x"]=-51790.285714288,}, - [8]={["y"]=703678.57142857,["x"]=-50611.714285716,}, - [9]={["y"]=703857.42857143,["x"]=-50226.857142859,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=703904,["x"]=-50352.571428573,}, - [2]={["y"]=707596.28571429,["x"]=-52094.571428573,}, - [3]={["y"]=707560.57142858,["x"]=-52161.714285716,}, - [4]={["y"]=703871.71428572,["x"]=-50420.571428573,}, - [5]={["y"]=703902,["x"]=-50352.000000002,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Mozdok = { - PointsBoundary = { - [1]={["y"]=832123.42857143,["x"]=-83608.571428573,}, - [2]={["y"]=835916.28571429,["x"]=-83144.285714288,}, - [3]={["y"]=835474.28571429,["x"]=-84170.571428573,}, - [4]={["y"]=832911.42857143,["x"]=-84470.571428573,}, - [5]={["y"]=832487.71428572,["x"]=-85565.714285716,}, - [6]={["y"]=831573.42857143,["x"]=-85351.42857143,}, - [7]={["y"]=832123.71428572,["x"]=-83610.285714288,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=832201.14285715,["x"]=-83699.428571431,}, - [2]={["y"]=832212.57142857,["x"]=-83780.571428574,}, - [3]={["y"]=835730.28571429,["x"]=-83335.714285717,}, - [4]={["y"]=835718.85714286,["x"]=-83246.571428574,}, - [5]={["y"]=832200.57142857,["x"]=-83700.000000002,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Nalchik = { - PointsBoundary = { - [1]={["y"]=759370,["x"]=-125502.85714286,}, - [2]={["y"]=761384.28571429,["x"]=-124177.14285714,}, - [3]={["y"]=761472.85714286,["x"]=-124325.71428572,}, - [4]={["y"]=761092.85714286,["x"]=-125048.57142857,}, - [5]={["y"]=760295.71428572,["x"]=-125685.71428572,}, - [6]={["y"]=759444.28571429,["x"]=-125734.28571429,}, - [7]={["y"]=759375.71428572,["x"]=-125511.42857143,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=759454.28571429,["x"]=-125551.42857143,}, - [2]={["y"]=759492.85714286,["x"]=-125610.85714286,}, - [3]={["y"]=761406.28571429,["x"]=-124304.28571429,}, - [4]={["y"]=761361.14285714,["x"]=-124239.71428572,}, - [5]={["y"]=759456,["x"]=-125552.57142857,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Novorossiysk = { - PointsBoundary = { - [1]={["y"]=278677.71428573,["x"]=-41656.571428572,}, - [2]={["y"]=278446.2857143,["x"]=-41453.714285715,}, - [3]={["y"]=278989.14285716,["x"]=-40188.000000001,}, - [4]={["y"]=279717.71428573,["x"]=-39968.000000001,}, - [5]={["y"]=280020.57142859,["x"]=-40208.000000001,}, - [6]={["y"]=278674.85714287,["x"]=-41660.857142858,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=278673.14285716,["x"]=-41615.142857144,}, - [2]={["y"]=278625.42857144,["x"]=-41570.571428572,}, - [3]={["y"]=279835.42857144,["x"]=-40226.000000001,}, - [4]={["y"]=279882.2857143,["x"]=-40270.000000001,}, - [5]={["y"]=278672.00000001,["x"]=-41614.857142858,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - SenakiKolkhi = { - PointsBoundary = { - [1]={["y"]=646036.57142857,["x"]=-281778.85714286,}, - [2]={["y"]=646045.14285714,["x"]=-281191.71428571,}, - [3]={["y"]=647032.28571429,["x"]=-280598.85714285,}, - [4]={["y"]=647669.42857143,["x"]=-281273.14285714,}, - [5]={["y"]=648323.71428571,["x"]=-281370.28571428,}, - [6]={["y"]=648520.85714286,["x"]=-281978.85714285,}, - [7]={["y"]=646039.42857143,["x"]=-281783.14285714,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=646060.85714285,["x"]=-281736,}, - [2]={["y"]=646056.57142857,["x"]=-281631.71428571,}, - [3]={["y"]=648442.28571428,["x"]=-281840.28571428,}, - [4]={["y"]=648432.28571428,["x"]=-281918.85714286,}, - [5]={["y"]=646063.71428571,["x"]=-281738.85714286,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - SochiAdler = { - PointsBoundary = { - [1]={["y"]=460642.28571428,["x"]=-164861.71428571,}, - [2]={["y"]=462820.85714285,["x"]=-163368.85714286,}, - [3]={["y"]=463649.42857142,["x"]=-163340.28571429,}, - [4]={["y"]=463835.14285714,["x"]=-164040.28571429,}, - [5]={["y"]=462535.14285714,["x"]=-165654.57142857,}, - [6]={["y"]=460678,["x"]=-165247.42857143,}, - [7]={["y"]=460635.14285714,["x"]=-164876,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=460831.42857143,["x"]=-165180,}, - [2]={["y"]=460878.57142857,["x"]=-165257.14285714,}, - [3]={["y"]=463663.71428571,["x"]=-163793.14285714,}, - [4]={["y"]=463612.28571428,["x"]=-163697.42857143,}, - [5]={["y"]=460831.42857143,["x"]=-165177.14285714,}, - }, - [2] = { - [1]={["y"]=460831.42857143,["x"]=-165180,}, - [2]={["y"]=460878.57142857,["x"]=-165257.14285714,}, - [3]={["y"]=463663.71428571,["x"]=-163793.14285714,}, - [4]={["y"]=463612.28571428,["x"]=-163697.42857143,}, - [5]={["y"]=460831.42857143,["x"]=-165177.14285714,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Soganlug = { - PointsBoundary = { - [1]={["y"]=894530.85714286,["x"]=-316928.28571428,}, - [2]={["y"]=896422.28571428,["x"]=-318622.57142857,}, - [3]={["y"]=896090.85714286,["x"]=-318934,}, - [4]={["y"]=894019.42857143,["x"]=-317119.71428571,}, - [5]={["y"]=894533.71428571,["x"]=-316925.42857143,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=894525.71428571,["x"]=-316964,}, - [2]={["y"]=896363.14285714,["x"]=-318634.28571428,}, - [3]={["y"]=896299.14285714,["x"]=-318702.85714286,}, - [4]={["y"]=894464,["x"]=-317031.71428571,}, - [5]={["y"]=894524.57142857,["x"]=-316963.71428571,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - SukhumiBabushara = { - PointsBoundary = { - [1]={["y"]=562541.14285714,["x"]=-219852.28571429,}, - [2]={["y"]=562691.14285714,["x"]=-219395.14285714,}, - [3]={["y"]=564326.85714286,["x"]=-219523.71428571,}, - [4]={["y"]=566262.57142857,["x"]=-221166.57142857,}, - [5]={["y"]=566069.71428571,["x"]=-221580.85714286,}, - [6]={["y"]=562534,["x"]=-219873.71428571,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=562684,["x"]=-219779.71428571,}, - [2]={["y"]=562717.71428571,["x"]=-219718,}, - [3]={["y"]=566046.85714286,["x"]=-221376.57142857,}, - [4]={["y"]=566012.28571428,["x"]=-221446.57142857,}, - [5]={["y"]=562684.57142857,["x"]=-219782.57142857,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - TbilisiLochini = { - PointsBoundary = { - [1]={["y"]=895172.85714286,["x"]=-314667.42857143,}, - [2]={["y"]=895337.42857143,["x"]=-314143.14285714,}, - [3]={["y"]=895990.28571429,["x"]=-314036,}, - [4]={["y"]=897730.28571429,["x"]=-315284.57142857,}, - [5]={["y"]=897901.71428571,["x"]=-316284.57142857,}, - [6]={["y"]=897684.57142857,["x"]=-316618.85714286,}, - [7]={["y"]=895173.14285714,["x"]=-314667.42857143,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=895261.14285715,["x"]=-314652.28571428,}, - [2]={["y"]=897654.57142857,["x"]=-316523.14285714,}, - [3]={["y"]=897711.71428571,["x"]=-316450.28571429,}, - [4]={["y"]=895327.42857143,["x"]=-314568.85714286,}, - [5]={["y"]=895261.71428572,["x"]=-314656,}, - }, - [2] = { - [1]={["y"]=895605.71428572,["x"]=-314724.57142857,}, - [2]={["y"]=897639.71428572,["x"]=-316148,}, - [3]={["y"]=897683.42857143,["x"]=-316087.14285714,}, - [4]={["y"]=895650,["x"]=-314660,}, - [5]={["y"]=895606,["x"]=-314724.85714286,} - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Vaziani = { - PointsBoundary = { - [1]={["y"]=902122,["x"]=-318163.71428572,}, - [2]={["y"]=902678.57142857,["x"]=-317594,}, - [3]={["y"]=903275.71428571,["x"]=-317405.42857143,}, - [4]={["y"]=903418.57142857,["x"]=-317891.14285714,}, - [5]={["y"]=904292.85714286,["x"]=-318748.28571429,}, - [6]={["y"]=904542,["x"]=-319740.85714286,}, - [7]={["y"]=904042,["x"]=-320166.57142857,}, - [8]={["y"]=902121.42857143,["x"]=-318164.85714286,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=902239.14285714,["x"]=-318190.85714286,}, - [2]={["y"]=904014.28571428,["x"]=-319994.57142857,}, - [3]={["y"]=904064.85714285,["x"]=-319945.14285715,}, - [4]={["y"]=902294.57142857,["x"]=-318146,}, - [5]={["y"]=902247.71428571,["x"]=-318190.85714286,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - }, -} - ---- Creates a new AIRBASEPOLICE_CAUCASUS object. --- @param #AIRBASEPOLICE_CAUCASUS self --- @param SetClient A SET_CLIENT object that will contain the CLIENT objects to be monitored if they follow the rules of the airbase. --- @return #AIRBASEPOLICE_CAUCASUS self -function AIRBASEPOLICE_CAUCASUS:New( SetClient ) - - -- Inherits from BASE - local self = BASE:Inherit( self, AIRBASEPOLICE_BASE:New( SetClient, self.Airbases ) ) - - -- -- AnapaVityazevo - -- local AnapaVityazevoBoundary = GROUP:FindByName( "AnapaVityazevo Boundary" ) - -- self.Airbases.AnapaVityazevo.ZoneBoundary = ZONE_POLYGON:New( "AnapaVityazevo Boundary", AnapaVityazevoBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local AnapaVityazevoRunway1 = GROUP:FindByName( "AnapaVityazevo Runway 1" ) - -- self.Airbases.AnapaVityazevo.ZoneRunways[1] = ZONE_POLYGON:New( "AnapaVityazevo Runway 1", AnapaVityazevoRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Batumi - -- local BatumiBoundary = GROUP:FindByName( "Batumi Boundary" ) - -- self.Airbases.Batumi.ZoneBoundary = ZONE_POLYGON:New( "Batumi Boundary", BatumiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local BatumiRunway1 = GROUP:FindByName( "Batumi Runway 1" ) - -- self.Airbases.Batumi.ZoneRunways[1] = ZONE_POLYGON:New( "Batumi Runway 1", BatumiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Beslan - -- local BeslanBoundary = GROUP:FindByName( "Beslan Boundary" ) - -- self.Airbases.Beslan.ZoneBoundary = ZONE_POLYGON:New( "Beslan Boundary", BeslanBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local BeslanRunway1 = GROUP:FindByName( "Beslan Runway 1" ) - -- self.Airbases.Beslan.ZoneRunways[1] = ZONE_POLYGON:New( "Beslan Runway 1", BeslanRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Gelendzhik - -- local GelendzhikBoundary = GROUP:FindByName( "Gelendzhik Boundary" ) - -- self.Airbases.Gelendzhik.ZoneBoundary = ZONE_POLYGON:New( "Gelendzhik Boundary", GelendzhikBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local GelendzhikRunway1 = GROUP:FindByName( "Gelendzhik Runway 1" ) - -- self.Airbases.Gelendzhik.ZoneRunways[1] = ZONE_POLYGON:New( "Gelendzhik Runway 1", GelendzhikRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Gudauta - -- local GudautaBoundary = GROUP:FindByName( "Gudauta Boundary" ) - -- self.Airbases.Gudauta.ZoneBoundary = ZONE_POLYGON:New( "Gudauta Boundary", GudautaBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local GudautaRunway1 = GROUP:FindByName( "Gudauta Runway 1" ) - -- self.Airbases.Gudauta.ZoneRunways[1] = ZONE_POLYGON:New( "Gudauta Runway 1", GudautaRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Kobuleti - -- local KobuletiBoundary = GROUP:FindByName( "Kobuleti Boundary" ) - -- self.Airbases.Kobuleti.ZoneBoundary = ZONE_POLYGON:New( "Kobuleti Boundary", KobuletiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KobuletiRunway1 = GROUP:FindByName( "Kobuleti Runway 1" ) - -- self.Airbases.Kobuleti.ZoneRunways[1] = ZONE_POLYGON:New( "Kobuleti Runway 1", KobuletiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- KrasnodarCenter - -- local KrasnodarCenterBoundary = GROUP:FindByName( "KrasnodarCenter Boundary" ) - -- self.Airbases.KrasnodarCenter.ZoneBoundary = ZONE_POLYGON:New( "KrasnodarCenter Boundary", KrasnodarCenterBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KrasnodarCenterRunway1 = GROUP:FindByName( "KrasnodarCenter Runway 1" ) - -- self.Airbases.KrasnodarCenter.ZoneRunways[1] = ZONE_POLYGON:New( "KrasnodarCenter Runway 1", KrasnodarCenterRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- KrasnodarPashkovsky - -- local KrasnodarPashkovskyBoundary = GROUP:FindByName( "KrasnodarPashkovsky Boundary" ) - -- self.Airbases.KrasnodarPashkovsky.ZoneBoundary = ZONE_POLYGON:New( "KrasnodarPashkovsky Boundary", KrasnodarPashkovskyBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KrasnodarPashkovskyRunway1 = GROUP:FindByName( "KrasnodarPashkovsky Runway 1" ) - -- self.Airbases.KrasnodarPashkovsky.ZoneRunways[1] = ZONE_POLYGON:New( "KrasnodarPashkovsky Runway 1", KrasnodarPashkovskyRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- local KrasnodarPashkovskyRunway2 = GROUP:FindByName( "KrasnodarPashkovsky Runway 2" ) - -- self.Airbases.KrasnodarPashkovsky.ZoneRunways[2] = ZONE_POLYGON:New( "KrasnodarPashkovsky Runway 2", KrasnodarPashkovskyRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Krymsk - -- local KrymskBoundary = GROUP:FindByName( "Krymsk Boundary" ) - -- self.Airbases.Krymsk.ZoneBoundary = ZONE_POLYGON:New( "Krymsk Boundary", KrymskBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KrymskRunway1 = GROUP:FindByName( "Krymsk Runway 1" ) - -- self.Airbases.Krymsk.ZoneRunways[1] = ZONE_POLYGON:New( "Krymsk Runway 1", KrymskRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Kutaisi - -- local KutaisiBoundary = GROUP:FindByName( "Kutaisi Boundary" ) - -- self.Airbases.Kutaisi.ZoneBoundary = ZONE_POLYGON:New( "Kutaisi Boundary", KutaisiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local KutaisiRunway1 = GROUP:FindByName( "Kutaisi Runway 1" ) - -- self.Airbases.Kutaisi.ZoneRunways[1] = ZONE_POLYGON:New( "Kutaisi Runway 1", KutaisiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- MaykopKhanskaya - -- local MaykopKhanskayaBoundary = GROUP:FindByName( "MaykopKhanskaya Boundary" ) - -- self.Airbases.MaykopKhanskaya.ZoneBoundary = ZONE_POLYGON:New( "MaykopKhanskaya Boundary", MaykopKhanskayaBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local MaykopKhanskayaRunway1 = GROUP:FindByName( "MaykopKhanskaya Runway 1" ) - -- self.Airbases.MaykopKhanskaya.ZoneRunways[1] = ZONE_POLYGON:New( "MaykopKhanskaya Runway 1", MaykopKhanskayaRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- MineralnyeVody - -- local MineralnyeVodyBoundary = GROUP:FindByName( "MineralnyeVody Boundary" ) - -- self.Airbases.MineralnyeVody.ZoneBoundary = ZONE_POLYGON:New( "MineralnyeVody Boundary", MineralnyeVodyBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local MineralnyeVodyRunway1 = GROUP:FindByName( "MineralnyeVody Runway 1" ) - -- self.Airbases.MineralnyeVody.ZoneRunways[1] = ZONE_POLYGON:New( "MineralnyeVody Runway 1", MineralnyeVodyRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Mozdok - -- local MozdokBoundary = GROUP:FindByName( "Mozdok Boundary" ) - -- self.Airbases.Mozdok.ZoneBoundary = ZONE_POLYGON:New( "Mozdok Boundary", MozdokBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local MozdokRunway1 = GROUP:FindByName( "Mozdok Runway 1" ) - -- self.Airbases.Mozdok.ZoneRunways[1] = ZONE_POLYGON:New( "Mozdok Runway 1", MozdokRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Nalchik - -- local NalchikBoundary = GROUP:FindByName( "Nalchik Boundary" ) - -- self.Airbases.Nalchik.ZoneBoundary = ZONE_POLYGON:New( "Nalchik Boundary", NalchikBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local NalchikRunway1 = GROUP:FindByName( "Nalchik Runway 1" ) - -- self.Airbases.Nalchik.ZoneRunways[1] = ZONE_POLYGON:New( "Nalchik Runway 1", NalchikRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Novorossiysk - -- local NovorossiyskBoundary = GROUP:FindByName( "Novorossiysk Boundary" ) - -- self.Airbases.Novorossiysk.ZoneBoundary = ZONE_POLYGON:New( "Novorossiysk Boundary", NovorossiyskBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local NovorossiyskRunway1 = GROUP:FindByName( "Novorossiysk Runway 1" ) - -- self.Airbases.Novorossiysk.ZoneRunways[1] = ZONE_POLYGON:New( "Novorossiysk Runway 1", NovorossiyskRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- SenakiKolkhi - -- local SenakiKolkhiBoundary = GROUP:FindByName( "SenakiKolkhi Boundary" ) - -- self.Airbases.SenakiKolkhi.ZoneBoundary = ZONE_POLYGON:New( "SenakiKolkhi Boundary", SenakiKolkhiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local SenakiKolkhiRunway1 = GROUP:FindByName( "SenakiKolkhi Runway 1" ) - -- self.Airbases.SenakiKolkhi.ZoneRunways[1] = ZONE_POLYGON:New( "SenakiKolkhi Runway 1", SenakiKolkhiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- SochiAdler - -- local SochiAdlerBoundary = GROUP:FindByName( "SochiAdler Boundary" ) - -- self.Airbases.SochiAdler.ZoneBoundary = ZONE_POLYGON:New( "SochiAdler Boundary", SochiAdlerBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local SochiAdlerRunway1 = GROUP:FindByName( "SochiAdler Runway 1" ) - -- self.Airbases.SochiAdler.ZoneRunways[1] = ZONE_POLYGON:New( "SochiAdler Runway 1", SochiAdlerRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- local SochiAdlerRunway2 = GROUP:FindByName( "SochiAdler Runway 2" ) - -- self.Airbases.SochiAdler.ZoneRunways[2] = ZONE_POLYGON:New( "SochiAdler Runway 2", SochiAdlerRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Soganlug - -- local SoganlugBoundary = GROUP:FindByName( "Soganlug Boundary" ) - -- self.Airbases.Soganlug.ZoneBoundary = ZONE_POLYGON:New( "Soganlug Boundary", SoganlugBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local SoganlugRunway1 = GROUP:FindByName( "Soganlug Runway 1" ) - -- self.Airbases.Soganlug.ZoneRunways[1] = ZONE_POLYGON:New( "Soganlug Runway 1", SoganlugRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- SukhumiBabushara - -- local SukhumiBabusharaBoundary = GROUP:FindByName( "SukhumiBabushara Boundary" ) - -- self.Airbases.SukhumiBabushara.ZoneBoundary = ZONE_POLYGON:New( "SukhumiBabushara Boundary", SukhumiBabusharaBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local SukhumiBabusharaRunway1 = GROUP:FindByName( "SukhumiBabushara Runway 1" ) - -- self.Airbases.SukhumiBabushara.ZoneRunways[1] = ZONE_POLYGON:New( "SukhumiBabushara Runway 1", SukhumiBabusharaRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- TbilisiLochini - -- local TbilisiLochiniBoundary = GROUP:FindByName( "TbilisiLochini Boundary" ) - -- self.Airbases.TbilisiLochini.ZoneBoundary = ZONE_POLYGON:New( "TbilisiLochini Boundary", TbilisiLochiniBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local TbilisiLochiniRunway1 = GROUP:FindByName( "TbilisiLochini Runway 1" ) - -- self.Airbases.TbilisiLochini.ZoneRunways[1] = ZONE_POLYGON:New( "TbilisiLochini Runway 1", TbilisiLochiniRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- local TbilisiLochiniRunway2 = GROUP:FindByName( "TbilisiLochini Runway 2" ) - -- self.Airbases.TbilisiLochini.ZoneRunways[2] = ZONE_POLYGON:New( "TbilisiLochini Runway 2", TbilisiLochiniRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - -- -- Vaziani - -- local VazianiBoundary = GROUP:FindByName( "Vaziani Boundary" ) - -- self.Airbases.Vaziani.ZoneBoundary = ZONE_POLYGON:New( "Vaziani Boundary", VazianiBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local VazianiRunway1 = GROUP:FindByName( "Vaziani Runway 1" ) - -- self.Airbases.Vaziani.ZoneRunways[1] = ZONE_POLYGON:New( "Vaziani Runway 1", VazianiRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - -- - -- - -- - - - -- Template - -- local TemplateBoundary = GROUP:FindByName( "Template Boundary" ) - -- self.Airbases.Template.ZoneBoundary = ZONE_POLYGON:New( "Template Boundary", TemplateBoundary ):SmokeZone(SMOKECOLOR.White):Flush() - -- - -- local TemplateRunway1 = GROUP:FindByName( "Template Runway 1" ) - -- self.Airbases.Template.ZoneRunways[1] = ZONE_POLYGON:New( "Template Runway 1", TemplateRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() - - return self - -end - - - - ---- @type AIRBASEPOLICE_NEVADA --- @extends Functional.AirbasePolice#AIRBASEPOLICE_BASE -AIRBASEPOLICE_NEVADA = { - ClassName = "AIRBASEPOLICE_NEVADA", - Airbases = { - Nellis = { - PointsBoundary = { - [1]={["y"]=-17814.714285714,["x"]=-399823.14285714,}, - [2]={["y"]=-16875.857142857,["x"]=-398763.14285714,}, - [3]={["y"]=-16251.571428571,["x"]=-398988.85714286,}, - [4]={["y"]=-16163,["x"]=-398693.14285714,}, - [5]={["y"]=-16328.714285714,["x"]=-398034.57142857,}, - [6]={["y"]=-15943,["x"]=-397571.71428571,}, - [7]={["y"]=-15711.571428571,["x"]=-397551.71428571,}, - [8]={["y"]=-15748.714285714,["x"]=-396806,}, - [9]={["y"]=-16288.714285714,["x"]=-396517.42857143,}, - [10]={["y"]=-16751.571428571,["x"]=-396308.85714286,}, - [11]={["y"]=-17263,["x"]=-396234.57142857,}, - [12]={["y"]=-17577.285714286,["x"]=-396640.28571429,}, - [13]={["y"]=-17614.428571429,["x"]=-397400.28571429,}, - [14]={["y"]=-19405.857142857,["x"]=-399428.85714286,}, - [15]={["y"]=-19234.428571429,["x"]=-399683.14285714,}, - [16]={["y"]=-18708.714285714,["x"]=-399408.85714286,}, - [17]={["y"]=-18397.285714286,["x"]=-399657.42857143,}, - [18]={["y"]=-17814.428571429,["x"]=-399823.42857143,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=-18687,["x"]=-399380.28571429,}, - [2]={["y"]=-18620.714285714,["x"]=-399436.85714286,}, - [3]={["y"]=-16217.857142857,["x"]=-396596.85714286,}, - [4]={["y"]=-16300.142857143,["x"]=-396530,}, - [5]={["y"]=-18687,["x"]=-399380.85714286,}, - }, - [2] = { - [1]={["y"]=-18451.571428572,["x"]=-399580.57142857,}, - [2]={["y"]=-18392.142857143,["x"]=-399628.57142857,}, - [3]={["y"]=-16011,["x"]=-396806.85714286,}, - [4]={["y"]=-16074.714285714,["x"]=-396751.71428572,}, - [5]={["y"]=-18451.571428572,["x"]=-399580.85714285,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - McCarran = { - PointsBoundary = { - [1]={["y"]=-29455.285714286,["x"]=-416277.42857142,}, - [2]={["y"]=-28860.142857143,["x"]=-416492,}, - [3]={["y"]=-25044.428571429,["x"]=-416344.85714285,}, - [4]={["y"]=-24580.142857143,["x"]=-415959.14285714,}, - [5]={["y"]=-25073,["x"]=-415630.57142857,}, - [6]={["y"]=-25087.285714286,["x"]=-415130.57142857,}, - [7]={["y"]=-25830.142857143,["x"]=-414866.28571428,}, - [8]={["y"]=-26658.714285715,["x"]=-414880.57142857,}, - [9]={["y"]=-26973,["x"]=-415273.42857142,}, - [10]={["y"]=-27380.142857143,["x"]=-415187.71428571,}, - [11]={["y"]=-27715.857142857,["x"]=-414144.85714285,}, - [12]={["y"]=-27551.571428572,["x"]=-413473.42857142,}, - [13]={["y"]=-28630.142857143,["x"]=-413201.99999999,}, - [14]={["y"]=-29494.428571429,["x"]=-415437.71428571,}, - [15]={["y"]=-29455.571428572,["x"]=-416277.71428571,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=-29408.428571429,["x"]=-416016.28571428,}, - [2]={["y"]=-29408.142857144,["x"]=-416105.42857142,}, - [3]={["y"]=-24680.714285715,["x"]=-416003.14285713,}, - [4]={["y"]=-24681.857142858,["x"]=-415926.57142856,}, - [5]={["y"]=-29408.42857143,["x"]=-416016.57142856,}, - }, - [2] = { - [1]={["y"]=-28575.571428572,["x"]=-416303.14285713,}, - [2]={["y"]=-28575.571428572,["x"]=-416382.57142856,}, - [3]={["y"]=-25111.000000001,["x"]=-416309.7142857,}, - [4]={["y"]=-25111.000000001,["x"]=-416249.14285713,}, - [5]={["y"]=-28575.571428572,["x"]=-416303.7142857,}, - }, - [3] = { - [1]={["y"]=-29331.000000001,["x"]=-416275.42857141,}, - [2]={["y"]=-29259.000000001,["x"]=-416306.85714284,}, - [3]={["y"]=-28005.571428572,["x"]=-413449.7142857,}, - [4]={["y"]=-28068.714285715,["x"]=-413422.85714284,}, - [5]={["y"]=-29331.000000001,["x"]=-416275.7142857,}, - }, - [4] = { - [1]={["y"]=-29073.285714286,["x"]=-416386.57142856,}, - [2]={["y"]=-28997.285714286,["x"]=-416417.42857141,}, - [3]={["y"]=-27697.571428572,["x"]=-413464.57142856,}, - [4]={["y"]=-27767.857142858,["x"]=-413434.28571427,}, - [5]={["y"]=-29073.000000001,["x"]=-416386.85714284,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - Creech = { - PointsBoundary = { - [1]={["y"]=-74522.714285715,["x"]=-360887.99999998,}, - [2]={["y"]=-74197,["x"]=-360556.57142855,}, - [3]={["y"]=-74402.714285715,["x"]=-359639.42857141,}, - [4]={["y"]=-74637,["x"]=-359279.42857141,}, - [5]={["y"]=-75759.857142857,["x"]=-359005.14285712,}, - [6]={["y"]=-75834.142857143,["x"]=-359045.14285712,}, - [7]={["y"]=-75902.714285714,["x"]=-359782.28571427,}, - [8]={["y"]=-76099.857142857,["x"]=-360399.42857141,}, - [9]={["y"]=-77314.142857143,["x"]=-360219.42857141,}, - [10]={["y"]=-77728.428571429,["x"]=-360445.14285713,}, - [11]={["y"]=-77585.571428571,["x"]=-360585.14285713,}, - [12]={["y"]=-76471.285714286,["x"]=-360819.42857141,}, - [13]={["y"]=-76325.571428571,["x"]=-360942.28571427,}, - [14]={["y"]=-74671.857142857,["x"]=-360927.7142857,}, - [15]={["y"]=-74522.714285714,["x"]=-360888.85714284,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=-74237.571428571,["x"]=-360591.7142857,}, - [2]={["y"]=-74234.428571429,["x"]=-360493.71428571,}, - [3]={["y"]=-77605.285714286,["x"]=-360399.14285713,}, - [4]={["y"]=-77608.714285715,["x"]=-360498.85714285,}, - [5]={["y"]=-74237.857142857,["x"]=-360591.7142857,}, - }, - [2] = { - [1]={["y"]=-75807.571428572,["x"]=-359073.42857142,}, - [2]={["y"]=-74770.142857144,["x"]=-360581.71428571,}, - [3]={["y"]=-74641.285714287,["x"]=-360585.42857142,}, - [4]={["y"]=-75734.142857144,["x"]=-359023.14285714,}, - [5]={["y"]=-75807.285714287,["x"]=-359073.42857142,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - GroomLake = { - PointsBoundary = { - [1]={["y"]=-88916.714285714,["x"]=-289102.28571425,}, - [2]={["y"]=-87023.571428572,["x"]=-290388.57142857,}, - [3]={["y"]=-85916.428571429,["x"]=-290674.28571428,}, - [4]={["y"]=-87645.000000001,["x"]=-286567.14285714,}, - [5]={["y"]=-88380.714285715,["x"]=-286388.57142857,}, - [6]={["y"]=-89670.714285715,["x"]=-283524.28571428,}, - [7]={["y"]=-89797.857142858,["x"]=-283567.14285714,}, - [8]={["y"]=-88635.000000001,["x"]=-286749.99999999,}, - [9]={["y"]=-89177.857142858,["x"]=-287207.14285714,}, - [10]={["y"]=-89092.142857144,["x"]=-288892.85714285,}, - [11]={["y"]=-88917.000000001,["x"]=-289102.85714285,}, - }, - PointsRunways = { - [1] = { - [1]={["y"]=-86039.000000001,["x"]=-290606.28571428,}, - [2]={["y"]=-85965.285714287,["x"]=-290573.99999999,}, - [3]={["y"]=-87692.714285715,["x"]=-286634.85714285,}, - [4]={["y"]=-87756.714285715,["x"]=-286663.99999999,}, - [5]={["y"]=-86038.714285715,["x"]=-290606.85714285,}, - }, - [2] = { - [1]={["y"]=-86808.428571429,["x"]=-290375.7142857,}, - [2]={["y"]=-86732.714285715,["x"]=-290344.28571427,}, - [3]={["y"]=-89672.714285714,["x"]=-283546.57142855,}, - [4]={["y"]=-89772.142857143,["x"]=-283587.71428569,}, - [5]={["y"]=-86808.142857143,["x"]=-290375.7142857,}, - }, - }, - ZoneBoundary = {}, - ZoneRunways = {}, - MaximumSpeed = 50, - }, - }, -} - ---- Creates a new AIRBASEPOLICE_NEVADA object. --- @param #AIRBASEPOLICE_NEVADA self --- @param SetClient A SET_CLIENT object that will contain the CLIENT objects to be monitored if they follow the rules of the airbase. --- @return #AIRBASEPOLICE_NEVADA self -function AIRBASEPOLICE_NEVADA:New( SetClient ) - - -- Inherits from BASE - local self = BASE:Inherit( self, AIRBASEPOLICE_BASE:New( SetClient, self.Airbases ) ) - --- -- Nellis --- local NellisBoundary = GROUP:FindByName( "Nellis Boundary" ) --- self.Airbases.Nellis.ZoneBoundary = ZONE_POLYGON:New( "Nellis Boundary", NellisBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local NellisRunway1 = GROUP:FindByName( "Nellis Runway 1" ) --- self.Airbases.Nellis.ZoneRunways[1] = ZONE_POLYGON:New( "Nellis Runway 1", NellisRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local NellisRunway2 = GROUP:FindByName( "Nellis Runway 2" ) --- self.Airbases.Nellis.ZoneRunways[2] = ZONE_POLYGON:New( "Nellis Runway 2", NellisRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- -- McCarran --- local McCarranBoundary = GROUP:FindByName( "McCarran Boundary" ) --- self.Airbases.McCarran.ZoneBoundary = ZONE_POLYGON:New( "McCarran Boundary", McCarranBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local McCarranRunway1 = GROUP:FindByName( "McCarran Runway 1" ) --- self.Airbases.McCarran.ZoneRunways[1] = ZONE_POLYGON:New( "McCarran Runway 1", McCarranRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local McCarranRunway2 = GROUP:FindByName( "McCarran Runway 2" ) --- self.Airbases.McCarran.ZoneRunways[2] = ZONE_POLYGON:New( "McCarran Runway 2", McCarranRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local McCarranRunway3 = GROUP:FindByName( "McCarran Runway 3" ) --- self.Airbases.McCarran.ZoneRunways[3] = ZONE_POLYGON:New( "McCarran Runway 3", McCarranRunway3 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local McCarranRunway4 = GROUP:FindByName( "McCarran Runway 4" ) --- self.Airbases.McCarran.ZoneRunways[4] = ZONE_POLYGON:New( "McCarran Runway 4", McCarranRunway4 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- -- Creech --- local CreechBoundary = GROUP:FindByName( "Creech Boundary" ) --- self.Airbases.Creech.ZoneBoundary = ZONE_POLYGON:New( "Creech Boundary", CreechBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local CreechRunway1 = GROUP:FindByName( "Creech Runway 1" ) --- self.Airbases.Creech.ZoneRunways[1] = ZONE_POLYGON:New( "Creech Runway 1", CreechRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local CreechRunway2 = GROUP:FindByName( "Creech Runway 2" ) --- self.Airbases.Creech.ZoneRunways[2] = ZONE_POLYGON:New( "Creech Runway 2", CreechRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- -- Groom Lake --- local GroomLakeBoundary = GROUP:FindByName( "GroomLake Boundary" ) --- self.Airbases.GroomLake.ZoneBoundary = ZONE_POLYGON:New( "GroomLake Boundary", GroomLakeBoundary ):SmokeZone(SMOKECOLOR.White):Flush() --- --- local GroomLakeRunway1 = GROUP:FindByName( "GroomLake Runway 1" ) --- self.Airbases.GroomLake.ZoneRunways[1] = ZONE_POLYGON:New( "GroomLake Runway 1", GroomLakeRunway1 ):SmokeZone(SMOKECOLOR.Red):Flush() --- --- local GroomLakeRunway2 = GROUP:FindByName( "GroomLake Runway 2" ) --- self.Airbases.GroomLake.ZoneRunways[2] = ZONE_POLYGON:New( "GroomLake Runway 2", GroomLakeRunway2 ):SmokeZone(SMOKECOLOR.Red):Flush() - -end - - - - - - --- **Functional** - DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods. --- --- ![Banner Image](..\Presentations\DETECTION\Dia1.JPG) --- --- === --- --- DETECTION classes facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units). --- DETECTION uses the in-built detection capabilities of DCS World, but adds new functionalities. --- --- Please watch this [youtube video](https://youtu.be/C7p81dUwP-E) that explains the detection concepts. --- --- --- ### Contributions: --- --- * Mechanist : Early concept of DETECTION_AREAS. --- --- ### Authors: --- --- * FlightControl : Analysis, Design, Programming, Testing --- --- @module Detection - - -do -- DETECTION_BASE - - --- # 1) DETECTION_BASE class, extends @{Fsm#FSM} - -- - -- The DETECTION_BASE class defines the core functions to administer detected objects. - -- The DETECTION_BASE class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). - -- - -- ## 1.1) DETECTION_BASE constructor - -- - -- Construct a new DETECTION_BASE instance using the @{#DETECTION_BASE.New}() method. - -- - -- ## 1.2) DETECTION_BASE initialization - -- - -- By default, detection will return detected objects with all the detection sensors available. - -- However, you can ask how the objects were found with specific detection methods. - -- If you use one of the below methods, the detection will work with the detection method specified. - -- You can specify to apply multiple detection methods. - -- - -- Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK: - -- - -- * @{#DETECTION_BASE.InitDetectVisual}(): Detected using Visual. - -- * @{#DETECTION_BASE.InitDetectOptical}(): Detected using Optical. - -- * @{#DETECTION_BASE.InitDetectRadar}(): Detected using Radar. - -- * @{#DETECTION_BASE.InitDetectIRST}(): Detected using IRST. - -- * @{#DETECTION_BASE.InitDetectRWR}(): Detected using RWR. - -- * @{#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK. - -- - -- ## 1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list - -- - -- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later - -- of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains - -- a SET_UNIT object that contains the detected units that belong to that group. - -- - -- Derived classes will apply different methods to group the detected units. - -- Examples are per area, per quadrant, per distance, per type. - -- See further the derived DETECTION classes on which grouping methods are currently supported. - -- - -- Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class: - -- - -- * The method @{Detection#DETECTION_BASE.GetDetectedItems}() retrieves the DetectedItems[] list. - -- * A DetectedItem from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedItem}( DetectedItemIndex ). - -- Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information - -- about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem. - -- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ). - -- This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ). - -- - -- ## 1.4) Apply additional Filters to fine-tune the detected objects - -- - -- By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. - -- That being said, the DCS World detection algorithm can sometimes be unrealistic. - -- Especially for a visual detection, DCS World is able to report within 1 second a detailed detection of a group of 20 units (including types of the units) that are 10 kilometers away, using only visual capabilities. - -- Additionally, trees and other obstacles are not accounted during the DCS World detection. - -- - -- Therefore, an additional (optional) filtering has been built into the DETECTION_BASE class, that can be set for visual detected units. - -- For electronic detection, this filtering is not applied, only for visually detected targets. - -- - -- The following additional filtering can be applied for visual filtering: - -- - -- * A probability factor per kilometer distance. - -- * A probability factor based on the alpha angle between the detected object and the unit detecting. - -- A detection from a higher altitude allows for better detection than when on the ground. - -- * Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. - -- The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone. - -- - -- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. - -- Only when you experience unrealistic behaviour in your missions, these filters could be applied. - -- - -- ### 1.4.1 ) Distance visual detection probability - -- - -- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. - -- Also, the speed of accurate detection plays a role. - -- - -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. - -- - -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: - -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. - -- - -- Note that based on this probability factor, not only the detection but also the **type** of the unit will be applied! - -- - -- Use the method @{Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance. - -- - -- ### 1.4.2 ) Alpha Angle visual detection probability - -- - -- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. - -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. - -- - -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. - -- - -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: - -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% - -- - -- Use the method @{Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°. - -- - -- ### 1.4.3 ) Cloudy Zones detection probability - -- - -- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. - -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission - -- zones that reflect cloudy areas where detected units may not be so easily visually detected. - -- - -- Use the method @{Detection#DETECTION_BASE.SetZoneProbability}() to set for a defined number of zones, the probability factors. - -- - -- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take - -- from the DETECTION_BASE to calculate the presence of the detected unit within each zone. - -- Expecially for ZONE_POLYGON, try to limit the amount of nodes of the polygon! - -- - -- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for - -- AI not to detect so easily targets within a forrest or village rich area. - -- - -- ## 1.5 ) Accept / Reject detected units - -- - -- DETECTION_BASE can accept or reject successful detections based on the location of the detected object, - -- if it is located in range or located inside or outside of specific zones. - -- - -- ### 1.5.1 ) Detection acceptance of within range limit - -- - -- A range can be set that will limit a successful detection for a unit. - -- Use the method @{Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted. - -- - -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. - -- - -- -- Build a detect object. - -- local Detection = DETECTION_BASE:New( SetGroup ) - -- - -- -- This will accept detected units if the range is below 5000 meters. - -- Detection:SetAcceptRange( 5000 ) - -- - -- -- Start the Detection. - -- Detection:Start() - -- - -- - -- ### 1.5.2 ) Detection acceptance if within zone(s). - -- - -- Specific ZONE_BASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONE_BASE object(s). - -- Use the method @{Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones. - -- - -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. - -- - -- -- Search fo the zones where units are to be accepted. - -- local ZoneAccept1 = ZONE:New( "AcceptZone1" ) - -- local ZoneAccept2 = ZONE:New( "AcceptZone2" ) - -- - -- -- Build a detect object. - -- local Detection = DETECTION_BASE:New( SetGroup ) - -- - -- -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2. - -- Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) - -- - -- -- Start the Detection. - -- Detection:Start() - -- - -- ### 1.5.3 ) Detection rejectance if within zone(s). - -- - -- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s). - -- Use the method @{Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. - -- An example of how to use the method is shown below. - -- - -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. - -- - -- -- Search fo the zones where units are to be rejected. - -- local ZoneReject1 = ZONE:New( "RejectZone1" ) - -- local ZoneReject2 = ZONE:New( "RejectZone2" ) - -- - -- -- Build a detect object. - -- local Detection = DETECTION_BASE:New( SetGroup ) - -- - -- -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2. - -- Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) - -- - -- -- Start the Detection. - -- Detection:Start() - -- - -- ## 1.6) DETECTION_BASE is a Finite State Machine - -- - -- Various Events and State Transitions can be tailored using DETECTION_BASE. - -- - -- ### 1.6.1) DETECTION_BASE States - -- - -- * **Detecting**: The detection is running. - -- * **Stopped**: The detection is stopped. - -- - -- ### 1.6.2) DETECTION_BASE Events - -- - -- * **Start**: Start the detection process. - -- * **Detect**: Detect new units. - -- * **Detected**: New units have been detected. - -- * **Stop**: Stop the detection process. - -- - -- @type DETECTION_BASE - -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. - -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. - -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. - -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. - -- @field #number DetectionRun - -- @extends Core.Fsm#FSM - DETECTION_BASE = { - ClassName = "DETECTION_BASE", - DetectionSetGroup = nil, - DetectionRange = nil, - DetectedObjects = {}, - DetectionRun = 0, - DetectedObjectsIdentified = {}, - DetectedItems = {}, - } - - --- @type DETECTION_BASE.DetectedObjects - -- @list <#DETECTION_BASE.DetectedObject> - - --- @type DETECTION_BASE.DetectedObject - -- @field #string Name - -- @field #boolean Visible - -- @field #string Type - -- @field #number Distance - -- @field #boolean Identified - - --- @type DETECTION_BASE.DetectedItems - -- @list <#DETECTION_BASE.DetectedItem> - - --- @type DETECTION_BASE.DetectedItem - -- @field Core.Set#SET_UNIT Set - -- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. - -- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. - -- @field #boolean Changed Documents if the detected area has changes. - -- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes). - -- @field #number ItemID -- The identifier of the detected area. - -- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. - -- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. - - - --- DETECTION constructor. - -- @param #DETECTION_BASE self - -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. - -- @return #DETECTION_BASE self - function DETECTION_BASE:New( DetectionSetGroup ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_BASE - - self.DetectedItemCount = 0 - self.DetectedItemMax = 0 - self.DetectedItems = {} - - self.DetectionSetGroup = DetectionSetGroup - - self.DetectionInterval = 30 - - self:InitDetectVisual( true ) - self:InitDetectOptical( false ) - self:InitDetectRadar( false ) - self:InitDetectRWR( false ) - self:InitDetectIRST( false ) - self:InitDetectDLINK( false ) - - -- Create FSM transitions. - - self:SetStartState( "Stopped" ) - self.CountryID = DetectionSetGroup:GetFirst():GetCountry() - - self:AddTransition( "Stopped", "Start", "Detecting") - - --- OnLeave Transition Handler for State Stopped. - -- @function [parent=#DETECTION_BASE] OnLeaveStopped - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Stopped. - -- @function [parent=#DETECTION_BASE] OnEnterStopped - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Start. - -- @function [parent=#DETECTION_BASE] OnBeforeStart - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Start. - -- @function [parent=#DETECTION_BASE] OnAfterStart - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Start. - -- @function [parent=#DETECTION_BASE] Start - -- @param #DETECTION_BASE self - - --- Asynchronous Event Trigger for Event Start. - -- @function [parent=#DETECTION_BASE] __Start - -- @param #DETECTION_BASE self - -- @param #number Delay The delay in seconds. - - --- OnLeave Transition Handler for State Detecting. - -- @function [parent=#DETECTION_BASE] OnLeaveDetecting - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Detecting. - -- @function [parent=#DETECTION_BASE] OnEnterDetecting - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - self:AddTransition( "Detecting", "Detect", "Detecting" ) - self:AddTransition( "Detecting", "DetectionGroup", "Detecting" ) - - --- OnBefore Transition Handler for Event Detect. - -- @function [parent=#DETECTION_BASE] OnBeforeDetect - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Detect. - -- @function [parent=#DETECTION_BASE] OnAfterDetect - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Detect. - -- @function [parent=#DETECTION_BASE] Detect - -- @param #DETECTION_BASE self - - --- Asynchronous Event Trigger for Event Detect. - -- @function [parent=#DETECTION_BASE] __Detect - -- @param #DETECTION_BASE self - -- @param #number Delay The delay in seconds. - - - self:AddTransition( "Detecting", "Detected", "Detecting" ) - - --- OnBefore Transition Handler for Event Detected. - -- @function [parent=#DETECTION_BASE] OnBeforeDetected - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Detected. - -- @function [parent=#DETECTION_BASE] OnAfterDetected - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Detected. - -- @function [parent=#DETECTION_BASE] Detected - -- @param #DETECTION_BASE self - - --- Asynchronous Event Trigger for Event Detected. - -- @function [parent=#DETECTION_BASE] __Detected - -- @param #DETECTION_BASE self - -- @param #number Delay The delay in seconds. - - - self:AddTransition( "*", "Stop", "Stopped" ) - - --- OnBefore Transition Handler for Event Stop. - -- @function [parent=#DETECTION_BASE] OnBeforeStop - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Stop. - -- @function [parent=#DETECTION_BASE] OnAfterStop - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Stop. - -- @function [parent=#DETECTION_BASE] Stop - -- @param #DETECTION_BASE self - - --- Asynchronous Event Trigger for Event Stop. - -- @function [parent=#DETECTION_BASE] __Stop - -- @param #DETECTION_BASE self - -- @param #number Delay The delay in seconds. - - --- OnLeave Transition Handler for State Stopped. - -- @function [parent=#DETECTION_BASE] OnLeaveStopped - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Stopped. - -- @function [parent=#DETECTION_BASE] OnEnterStopped - -- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - return self - end - - do -- State Transition Handling - - --- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - function DETECTION_BASE:onafterStart(From,Event,To) - self:__Detect(0.1) - end - - --- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - function DETECTION_BASE:onafterDetect(From,Event,To) - self:E( {From,Event,To}) - - local DetectDelay = 0.1 - self.DetectionCount = 0 - self.DetectionRun = 0 - self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table - - self.DetectionSetGroup:Flush() - - for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - self:E( {DetectionGroupData}) - self:__DetectionGroup( DetectDelay, DetectionGroupData ) -- Process each detection asynchronously. - self.DetectionCount = self.DetectionCount + 1 - DetectDelay = DetectDelay + 0.1 - end - end - - --- @param #DETECTION_BASE self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. - function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup ) - self:E( {From,Event,To}) - - self.DetectionRun = self.DetectionRun + 1 - - local HasDetectedObjects = false - - if DetectionGroup:IsAlive() then - - self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) - - local DetectionGroupName = DetectionGroup:GetName() - - local DetectedUnits = {} - - local DetectedTargets = DetectionGroup:GetDetectedTargets( - self.DetectVisual, - self.DetectOptical, - self.DetectRadar, - self.DetectIRST, - self.DetectRWR, - self.DetectDLINK - ) - - self:T( DetectedTargets ) - - for DetectionObjectID, Detection in pairs( DetectedTargets ) do - local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object - self:T2( DetectedObject ) - - if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then - - local DetectionAccepted = true - - local DetectedObjectName = DetectedObject:getName() - - local DetectedObjectVec3 = DetectedObject:getPoint() - local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z } - local DetectionGroupVec3 = DetectionGroup:GetVec3() - local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z } - - local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 + - ( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 + - ( DetectedObjectVec3.z - DetectionGroupVec3.z )^2 - ) ^ 0.5 / 1000 - - self:T( { "Detected Target", DetectionGroupName, DetectedObjectName, Distance } ) - - -- Calculate Acceptance - - if self.AcceptRange and Distance > self.AcceptRange then - DetectionAccepted = false - end - - if self.AcceptZones then - for AcceptZoneID, AcceptZone in pairs( self.AcceptZones ) do - local AcceptZone = AcceptZone -- Core.Zone#ZONE_BASE - if AcceptZone:IsPointVec2InZone( DetectedObjectVec2 ) == false then - DetectionAccepted = false - end - end - end - - if self.RejectZones then - for RejectZoneID, RejectZone in pairs( self.RejectZones ) do - local RejectZone = RejectZone -- Core.Zone#ZONE_BASE - if RejectZone:IsPointVec2InZone( DetectedObjectVec2 ) == true then - DetectionAccepted = false - end - end - end - - -- Calculate additional probabilities - - if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.DistanceProbability then - local DistanceFactor = Distance / 4 - local DistanceProbabilityReversed = ( 1 - self.DistanceProbability ) * DistanceFactor - local DistanceProbability = 1 - DistanceProbabilityReversed - DistanceProbability = DistanceProbability * 30 / 300 - local Probability = math.random() -- Selects a number between 0 and 1 - self:T( { Probability, DistanceProbability } ) - if Probability > DistanceProbability then - DetectionAccepted = false - end - end - - if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.AlphaAngleProbability then - local NormalVec2 = { x = DetectedObjectVec2.x - DetectionGroupVec2.x, y = DetectedObjectVec2.y - DetectionGroupVec2.y } - local AlphaAngle = math.atan2( NormalVec2.y, NormalVec2.x ) - local Sinus = math.sin( AlphaAngle ) - local AlphaAngleProbabilityReversed = ( 1 - self.AlphaAngleProbability ) * ( 1 - Sinus ) - local AlphaAngleProbability = 1 - AlphaAngleProbabilityReversed - - AlphaAngleProbability = AlphaAngleProbability * 30 / 300 - - local Probability = math.random() -- Selects a number between 0 and 1 - self:T( { Probability, AlphaAngleProbability } ) - if Probability > AlphaAngleProbability then - DetectionAccepted = false - end - - end - - if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.ZoneProbability then - - for ZoneDataID, ZoneData in pairs( self.ZoneProbability ) do - self:E({ZoneData}) - local ZoneObject = ZoneData[1] -- Core.Zone#ZONE_BASE - local ZoneProbability = ZoneData[2] -- #number - ZoneProbability = ZoneProbability * 30 / 300 - - if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then - local Probability = math.random() -- Selects a number between 0 and 1 - self:T( { Probability, ZoneProbability } ) - if Probability > ZoneProbability then - DetectionAccepted = false - break - end - end - end - end - - if DetectionAccepted then - - HasDetectedObjects = true - - if not self.DetectedObjects[DetectedObjectName] then - self.DetectedObjects[DetectedObjectName] = {} - end - self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName - self.DetectedObjects[DetectedObjectName].Visible = Detection.visible - self.DetectedObjects[DetectedObjectName].Type = Detection.type - self.DetectedObjects[DetectedObjectName].Distance = Distance - - local DetectedUnit = UNIT:FindByName( DetectedObjectName ) - - DetectedUnits[DetectedObjectName] = DetectedUnit - else - -- if beyond the DetectionRange then nullify... - if self.DetectedObjects[DetectedObjectName] then - self.DetectedObjects[DetectedObjectName] = nil - end - end - end - - self:T2( self.DetectedObjects ) - end - - if HasDetectedObjects then - self:__Detected( 0.1, DetectedUnits ) - end - - end - - if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then - self:__Detect( self.DetectionInterval ) - - self:T( "--> Create Detection Sets" ) - self:CreateDetectionSets() - end - - end - - - end - - do -- Initialization methods - - --- Detect Visual. - -- @param #DETECTION_BASE self - -- @param #boolean DetectVisual - -- @return #DETECTION_BASE self - function DETECTION_BASE:InitDetectVisual( DetectVisual ) - - self.DetectVisual = DetectVisual - end - - --- Detect Optical. - -- @param #DETECTION_BASE self - -- @param #boolean DetectOptical - -- @return #DETECTION_BASE self - function DETECTION_BASE:InitDetectOptical( DetectOptical ) - self:F2() - - self.DetectOptical = DetectOptical - end - - --- Detect Radar. - -- @param #DETECTION_BASE self - -- @param #boolean DetectRadar - -- @return #DETECTION_BASE self - function DETECTION_BASE:InitDetectRadar( DetectRadar ) - self:F2() - - self.DetectRadar = DetectRadar - end - - --- Detect IRST. - -- @param #DETECTION_BASE self - -- @param #boolean DetectIRST - -- @return #DETECTION_BASE self - function DETECTION_BASE:InitDetectIRST( DetectIRST ) - self:F2() - - self.DetectIRST = DetectIRST - end - - --- Detect RWR. - -- @param #DETECTION_BASE self - -- @param #boolean DetectRWR - -- @return #DETECTION_BASE self - function DETECTION_BASE:InitDetectRWR( DetectRWR ) - self:F2() - - self.DetectRWR = DetectRWR - end - - --- Detect DLINK. - -- @param #DETECTION_BASE self - -- @param #boolean DetectDLINK - -- @return #DETECTION_BASE self - function DETECTION_BASE:InitDetectDLINK( DetectDLINK ) - self:F2() - - self.DetectDLINK = DetectDLINK - end - - end - - do - - --- Set the detection interval time in seconds. - -- @param #DETECTION_BASE self - -- @param #number DetectionInterval Interval in seconds. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetDetectionInterval( DetectionInterval ) - self:F2() - - self.DetectionInterval = DetectionInterval - - return self - end - - end - - do -- Accept / Reject detected units - - --- Accept detections if within a range in meters. - -- @param #DETECTION_BASE self - -- @param #number AcceptRange Accept a detection if the unit is within the AcceptRange in meters. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetAcceptRange( AcceptRange ) - self:F2() - - self.AcceptRange = AcceptRange - - return self - end - - --- Accept detections if within the specified zone(s). - -- @param #DETECTION_BASE self - -- @param AcceptZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetAcceptZones( AcceptZones ) - self:F2() - - if type( AcceptZones ) == "table" then - self.AcceptZones = AcceptZones - else - self.AcceptZones = { AcceptZones } - end - - return self - end - - --- Reject detections if within the specified zone(s). - -- @param #DETECTION_BASE self - -- @param RejectZones Can be a list or ZONE_BASE objects, or a single ZONE_BASE object. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetRejectZones( RejectZones ) - self:F2() - - if type( RejectZones ) == "table" then - self.RejectZones = RejectZones - else - self.RejectZones = { RejectZones } - end - - return self - end - - end - - do -- Probability methods - - --- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly. - -- Also, the speed of accurate detection plays a role. - -- A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance. - -- For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: - -- 1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%. - -- @param #DETECTION_BASE self - -- @param DistanceProbability The probability factor. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetDistanceProbability( DistanceProbability ) - self:F2() - - self.DistanceProbability = DistanceProbability - - return self - end - - - --- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. - -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. - -- - -- A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle. - -- - -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: - -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% - -- @param #DETECTION_BASE self - -- @param AlphaAngleProbability The probability factor. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetAlphaAngleProbability( AlphaAngleProbability ) - self:F2() - - self.AlphaAngleProbability = AlphaAngleProbability - - return self - end - - --- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. - -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission - -- zones that reflect cloudy areas where detected units may not be so easily visually detected. - -- @param #DETECTION_BASE self - -- @param ZoneArray Aray of a The ZONE_BASE object and a ZoneProbability pair.. - -- @return #DETECTION_BASE self - function DETECTION_BASE:SetZoneProbability( ZoneArray ) - self:F2() - - self.ZoneProbability = ZoneArray - - return self - end - - - end - - do -- Change processing - - --- Accepts changes from the detected item. - -- @param #DETECTION_BASE self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @return #DETECTION_BASE self - function DETECTION_BASE:AcceptChanges( DetectedItem ) - - DetectedItem.Changed = false - DetectedItem.Changes = {} - - return self - end - - --- Add a change to the detected zone. - -- @param #DETECTION_BASE self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @param #string ChangeCode - -- @return #DETECTION_BASE self - function DETECTION_BASE:AddChangeItem( DetectedItem, ChangeCode, ItemUnitType ) - - DetectedItem.Changed = true - local ItemID = DetectedItem.ItemID - - DetectedItem.Changes = DetectedItem.Changes or {} - DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} - DetectedItem.Changes[ChangeCode].ItemID = ItemID - DetectedItem.Changes[ChangeCode].ItemUnitType = ItemUnitType - - self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ItemUnitType } ) - - return self - end - - - --- Add a change to the detected zone. - -- @param #DETECTION_BASE self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @param #string ChangeCode - -- @param #string ChangeUnitType - -- @return #DETECTION_BASE self - function DETECTION_BASE:AddChangeUnit( DetectedItem, ChangeCode, ChangeUnitType ) - - DetectedItem.Changed = true - local ItemID = DetectedItem.ItemID - - DetectedItem.Changes = DetectedItem.Changes or {} - DetectedItem.Changes[ChangeCode] = DetectedItem.Changes[ChangeCode] or {} - DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] or 0 - DetectedItem.Changes[ChangeCode][ChangeUnitType] = DetectedItem.Changes[ChangeCode][ChangeUnitType] + 1 - DetectedItem.Changes[ChangeCode].ItemID = ItemID - - self:T( { "Change on Detection Item:", DetectedItem.ItemID, ChangeCode, ChangeUnitType } ) - - return self - end - - - end - - do -- Threat - - --- Returns if there are friendlies nearby the FAC units ... - -- @param #DETECTION_BASE self - -- @return #boolean trhe if there are friendlies nearby - function DETECTION_BASE:IsFriendliesNearBy( DetectedItem ) - - self:T3( DetectedItem.FriendliesNearBy ) - return DetectedItem.FriendliesNearBy or false - end - - --- Background worker function to determine if there are friendlies nearby ... - -- @param #DETECTION_BASE self - function DETECTION_BASE:ReportFriendliesNearBy( ReportGroupData ) - self:F2() - - local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem - local DetectedSet = ReportGroupData.DetectedItem.Set - local DetectedUnit = DetectedSet:GetFirst() - - DetectedItem.FriendliesNearBy = false - - if DetectedUnit then - - - local SphereSearch = { - id = world.VolumeType.SPHERE, - params = { - point = DetectedUnit:GetVec3(), - radius = 6000, - } - - } - - --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit - -- @param Wrapper.Group#GROUP ReportGroup - -- @param Set#SET_GROUP ReportSetGroup - local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) - - local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem - local DetectedSet = ReportGroupData.DetectedItem.Set - local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT - local ReportSetGroup = ReportGroupData.ReportSetGroup - - local EnemyCoalition = DetectedUnit:GetCoalition() - - local FoundUnitCoalition = FoundDCSUnit:getCoalition() - local FoundUnitName = FoundDCSUnit:getName() - local FoundUnitGroupName = FoundDCSUnit:getGroup():getName() - local EnemyUnitName = DetectedUnit:GetName() - local FoundUnitInReportSetGroup = ReportSetGroup:FindGroup( FoundUnitGroupName ) ~= nil - - self:T3( { "Friendlies search:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) - - if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then - DetectedItem.FriendliesNearBy = true - return false - end - - return true - end - - world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, ReportGroupData ) - end - end - - end - - --- Determines if a detected object has already been identified during detection processing. - -- @param #DETECTION_BASE self - -- @param #DETECTION_BASE.DetectedObject DetectedObject - -- @return #boolean true if already identified. - function DETECTION_BASE:IsDetectedObjectIdentified( DetectedObject ) - self:F3( DetectedObject.Name ) - - local DetectedObjectName = DetectedObject.Name - local DetectedObjectIdentified = self.DetectedObjectsIdentified[DetectedObjectName] == true - self:T3( DetectedObjectIdentified ) - return DetectedObjectIdentified - end - - --- Identifies a detected object during detection processing. - -- @param #DETECTION_BASE self - -- @param #DETECTION_BASE.DetectedObject DetectedObject - function DETECTION_BASE:IdentifyDetectedObject( DetectedObject ) - self:F( { "Identified:", DetectedObject.Name } ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = true - end - - --- UnIdentify a detected object during detection processing. - -- @param #DETECTION_BASE self - -- @param #DETECTION_BASE.DetectedObject DetectedObject - function DETECTION_BASE:UnIdentifyDetectedObject( DetectedObject ) - - local DetectedObjectName = DetectedObject.Name - self.DetectedObjectsIdentified[DetectedObjectName] = false - end - - --- UnIdentify all detected objects during detection processing. - -- @param #DETECTION_BASE self - function DETECTION_BASE:UnIdentifyAllDetectedObjects() - - self.DetectedObjectsIdentified = {} -- Table will be garbage collected. - end - - --- Gets a detected object with a given name. - -- @param #DETECTION_BASE self - -- @param #string ObjectName - -- @return #DETECTION_BASE.DetectedObject - function DETECTION_BASE:GetDetectedObject( ObjectName ) - self:F( ObjectName ) - - if ObjectName then - local DetectedObject = self.DetectedObjects[ObjectName] - - -- Only return detected objects that are alive! - local DetectedUnit = UNIT:FindByName( ObjectName ) - if DetectedUnit and DetectedUnit:IsAlive() then - if self:IsDetectedObjectIdentified( DetectedObject ) == false then - return DetectedObject - end - end - end - - return nil - end - - - --- Adds a new DetectedItem to the DetectedItems list. - -- The DetectedItem is a table and contains a SET_UNIT in the field Set. - -- @param #DETECTION_BASE self - -- @param #string DetectedItemIndex The index of the DetectedItem. - -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. - -- @return #DETECTION_BASE.DetectedItem - function DETECTION_BASE:AddDetectedItem( DetectedItemIndex, Set ) - - local DetectedItem = {} - self.DetectedItemCount = self.DetectedItemCount + 1 - self.DetectedItemMax = self.DetectedItemMax + 1 - - if DetectedItemIndex then - self.DetectedItems[DetectedItemIndex] = DetectedItem - else - self.DetectedItems[self.DetectedItemCount] = DetectedItem - end - - DetectedItem.Set = Set or SET_UNIT:New() - DetectedItem.ItemID = self.DetectedItemMax - DetectedItem.Removed = false - - return DetectedItem - end - - --- Adds a new DetectedItem to the DetectedItems list. - -- The DetectedItem is a table and contains a SET_UNIT in the field Set. - -- @param #DETECTION_BASE self - -- @param #string DetectedItemIndex The index of the DetectedItem. - -- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added. - -- @param Core.Zone#ZONE_UNIT Zone (optional) The Zone to be added where the Units are located. - -- @return #DETECTION_BASE.DetectedItem - function DETECTION_BASE:AddDetectedItemZone( DetectedItemIndex, Set, Zone ) - - local DetectedItem = self:AddDetectedItem( DetectedItemIndex, Set ) - - DetectedItem.Zone = Zone - - return DetectedItem - end - - --- Removes an existing DetectedItem from the DetectedItems list. - -- The DetectedItem is a table and contains a SET_UNIT in the field Set. - -- @param #DETECTION_BASE self - -- @param #number DetectedItemIndex The index or position in the DetectedItems list where the item needs to be removed. - function DETECTION_BASE:RemoveDetectedItem( DetectedItemIndex ) - - self.DetectedItemCount = self.DetectedItemCount - 1 - self.DetectedItems[DetectedItemIndex] = nil - end - - - --- Get the detected @{Set#SET_BASE}s. - -- @param #DETECTION_BASE self - -- @return #DETECTION_BASE.DetectedItems - function DETECTION_BASE:GetDetectedItems() - - return self.DetectedItems - end - - --- Get the amount of SETs with detected objects. - -- @param #DETECTION_BASE self - -- @return #number Count - function DETECTION_BASE:GetDetectedItemsCount() - - local DetectedCount = self.DetectedItemCount - return DetectedCount - end - - --- Get a detected item using a given numeric index. - -- @param #DETECTION_BASE self - -- @param #number Index - -- @return #DETECTION_BASE.DetectedItem - function DETECTION_BASE:GetDetectedItem( Index ) - - local DetectedItem = self.DetectedItems[Index] - if DetectedItem then - return DetectedItem - end - - return nil - end - - --- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. - -- @param #DETECTION_BASE self - -- @param #number Index - -- @return Core.Set#SET_UNIT DetectedSet - function DETECTION_BASE:GetDetectedSet( Index ) - - local DetectedItem = self:GetDetectedItem( Index ) - local DetectedSetUnit = DetectedItem.Set - if DetectedSetUnit then - return DetectedSetUnit - end - - return nil - end - - do -- Zones - - --- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. - -- @param #DETECTION_BASE self - -- @param #number Index - -- @return Core.Zone#ZONE_UNIT DetectedZone - function DETECTION_BASE:GetDetectedZone( Index ) - - local DetectedZone = self.DetectedItems[Index].Zone - if DetectedZone then - return DetectedZone - end - - return nil - end - - end - - - --- Report summary of a detected item using a given numeric index. - -- @param #DETECTION_BASE self - -- @param Index - -- @return #string - function DETECTION_BASE:DetectedItemReportSummary( Index ) - self:F( Index ) - return nil - end - - --- Report detailed of a detectedion result. - -- @param #DETECTION_BASE self - -- @return #string - function DETECTION_BASE:DetectedReportDetailed() - self:F() - return nil - end - - --- Get the detection Groups. - -- @param #DETECTION_BASE self - -- @return Wrapper.Group#GROUP - function DETECTION_BASE:GetDetectionSetGroup() - - local DetectionSetGroup = self.DetectionSetGroup - return DetectionSetGroup - end - - --- Make a DetectionSet table. This function will be overridden in the derived clsses. - -- @param #DETECTION_BASE self - -- @return #DETECTION_BASE self - function DETECTION_BASE:CreateDetectionSets() - self:F2() - - self:E( "Error, in DETECTION_BASE class..." ) - - end - - - --- Schedule the DETECTION construction. - -- @param #DETECTION_BASE self - -- @param #number DelayTime The delay in seconds to wait the reporting. - -- @param #number RepeatInterval The repeat interval in seconds for the reporting to happen repeatedly. - -- @return #DETECTION_BASE self - function DETECTION_BASE:Schedule( DelayTime, RepeatInterval ) - self:F2() - - self.ScheduleDelayTime = DelayTime - self.ScheduleRepeatInterval = RepeatInterval - - self.DetectionScheduler = SCHEDULER:New( self, self._DetectionScheduler, { self, "Detection" }, DelayTime, RepeatInterval ) - return self - end - -end - -do -- DETECTION_UNITS - - --- # 2) DETECTION_UNITS class, extends @{Detection#DETECTION_BASE} - -- - -- The DETECTION_UNITS class will detect units within the battle zone. - -- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. - -- Beware that when the amount of units detected is large, the DetectedItems list will be large also. - -- - -- @type DETECTION_UNITS - -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are detected. - -- @extends #DETECTION_BASE - DETECTION_UNITS = { - ClassName = "DETECTION_UNITS", - DetectionRange = nil, - } - - --- DETECTION_UNITS constructor. - -- @param Functional.Detection#DETECTION_UNITS self - -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. - -- @return Functional.Detection#DETECTION_UNITS self - function DETECTION_UNITS:New( DetectionSetGroup ) - - -- Inherits from DETECTION_BASE - local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_UNITS - - self._SmokeDetectedUnits = false - self._FlareDetectedUnits = false - self._SmokeDetectedZones = false - self._FlareDetectedZones = false - self._BoundDetectedZones = false - - return self - end - - --- Make text documenting the changes of the detected zone. - -- @param #DETECTION_UNITS self - -- @param #DETECTION_UNITS.DetectedItem DetectedItem - -- @return #string The Changes text - function DETECTION_UNITS:GetChangeText( DetectedItem ) - self:F( DetectedItem ) - - local MT = {} - - for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do - - if ChangeCode == "AU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." - end - - if ChangeCode == "RU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." - end - - end - - return table.concat( MT, "\n" ) - - end - - - --- Create the DetectedItems list from the DetectedObjects table. - -- For each DetectedItem, a one field array is created containing the Unit detected. - -- @param #DETECTION_UNITS self - -- @return #DETECTION_UNITS self - function DETECTION_UNITS:CreateDetectionSets() - self:F2( #self.DetectedObjects ) - - -- Loop the current detected items, and check if each object still exists and is detected. - - for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do - - local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT - local DetectedTypeName = DetectedItem.Type - - for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - - local DetectedObject = nil - self:E( DetectedUnit ) - if DetectedUnit:IsAlive() then - --self:E(DetectedUnit:GetName()) - DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) - end - if DetectedObject then - - -- Yes, the DetectedUnit is still detected or exists. Flag as identified. - self:IdentifyDetectedObject( DetectedObject ) - else - -- There was no DetectedObject, remove DetectedUnit from the Set. - self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) - DetectedItemSet:Remove( DetectedUnitName ) - end - end - end - - - -- Now we need to loop through the unidentified detected units and add these... These are all new items. - for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - - local DetectedObject = self:GetDetectedObject( DetectedUnitName ) - if DetectedObject then - self:T( { "Detected Unit #", DetectedUnitName } ) - - local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT - - if DetectedUnit then - local DetectedTypeName = DetectedUnit:GetTypeName() - local DetectedItem = self:GetDetectedItem( DetectedUnitName ) - if not DetectedItem then - self:T( "Added new DetectedItem" ) - DetectedItem = self:AddDetectedItem( DetectedUnitName ) - DetectedItem.Type = DetectedUnit:GetTypeName() - DetectedItem.Name = DetectedObjectData.Name - DetectedItem.Visible = DetectedObjectData.Visible - DetectedItem.Distance = DetectedObjectData.Distance - end - - DetectedItem.Set:AddUnit( DetectedUnit ) - self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) - end - end - end - - for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do - - local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem - local DetectedSet = DetectedItem.Set - - self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - --self:NearestFAC( DetectedItem ) - end - - end - - --- Report summary of a DetectedItem using a given numeric index. - -- @param #DETECTION_UNITS self - -- @param Index - -- @return #string - function DETECTION_UNITS:DetectedItemReportSummary( Index ) - self:F( Index ) - - local DetectedItem = self:GetDetectedItem( Index ) - local DetectedSet = self:GetDetectedSet( Index ) - - self:T( DetectedSet ) - if DetectedSet then - local ReportSummary = "" - local UnitDistanceText = "" - local UnitCategoryText = "" - - local DetectedItemUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT - - if DetectedItemUnit and DetectedItemUnit:IsAlive() then - self:T(DetectedItemUnit) - - local UnitCategoryName = DetectedItemUnit:GetCategoryName() or "" - local UnitCategoryType = DetectedItemUnit:GetTypeName() or "" - - if DetectedItem.Type and UnitCategoryName and UnitCategoryType then - UnitCategoryText = UnitCategoryName .. " (" .. UnitCategoryType .. ") at " - else - UnitCategoryText = "Unknown target at " - end - - if DetectedItem.Visible == false then - UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " estimated km" - else - UnitDistanceText = string.format( "%.2f", DetectedItem.Distance ) .. " km, visual contact" - end - - local DetectedItemPointVec3 = DetectedItemUnit:GetPointVec3() - local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) - - local ThreatLevelA2G = DetectedItemUnit:GetThreatLevel( DetectedItem ) - - ReportSummary = string.format( - "%s - Threat [%s] (%2d) - %s%s", - DetectedItemPointLL, - string.rep( "■", ThreatLevelA2G ), - ThreatLevelA2G, - UnitCategoryText, - UnitDistanceText - ) - end - - self:T( ReportSummary ) - - return ReportSummary - end - end - - --- Report detailed of a detection result. - -- @param #DETECTION_UNITS self - -- @return #string - function DETECTION_UNITS:DetectedReportDetailed() - self:F() - - local Report = REPORT:New( "Detected units:" ) - for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do - local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem - local ReportSummary = self:DetectedItemReportSummary( DetectedItemID ) - Report:Add( ReportSummary ) - end - - local ReportText = Report:Text() - - return ReportText - end - -end - -do -- DETECTION_TYPES - - --- # 3) DETECTION_TYPES class, extends @{Detection#DETECTION_BASE} - -- - -- The DETECTION_TYPES class will detect units within the battle zone. - -- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. - -- Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. - -- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also. - -- - -- @type DETECTION_TYPES - -- @extends #DETECTION_BASE - DETECTION_TYPES = { - ClassName = "DETECTION_TYPES", - DetectionRange = nil, - } - - --- DETECTION_TYPES constructor. - -- @param Functional.Detection#DETECTION_TYPES self - -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Recce role. - -- @return Functional.Detection#DETECTION_TYPES self - function DETECTION_TYPES:New( DetectionSetGroup ) - - -- Inherits from DETECTION_BASE - local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) -- #DETECTION_TYPES - - self._SmokeDetectedUnits = false - self._FlareDetectedUnits = false - self._SmokeDetectedZones = false - self._FlareDetectedZones = false - self._BoundDetectedZones = false - - return self - end - - --- Make text documenting the changes of the detected zone. - -- @param #DETECTION_TYPES self - -- @param #DETECTION_TYPES.DetectedItem DetectedItem - -- @return #string The Changes text - function DETECTION_TYPES:GetChangeText( DetectedItem ) - self:F( DetectedItem ) - - local MT = {} - - for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do - - if ChangeCode == "AU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = " New target(s) detected: " .. table.concat( MTUT, ", " ) .. "." - end - - if ChangeCode == "RU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = " Invisible or destroyed target(s): " .. table.concat( MTUT, ", " ) .. "." - end - - end - - return table.concat( MT, "\n" ) - - end - - - --- Create the DetectedItems list from the DetectedObjects table. - -- For each DetectedItem, a one field array is created containing the Unit detected. - -- @param #DETECTION_TYPES self - -- @return #DETECTION_TYPES self - function DETECTION_TYPES:CreateDetectionSets() - self:F2( #self.DetectedObjects ) - - -- Loop the current detected items, and check if each object still exists and is detected. - - for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do - - local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT - local DetectedTypeName = DetectedItem.Type - - for DetectedUnitName, DetectedUnitData in pairs( DetectedItemSet:GetSet() ) do - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - - local DetectedObject = nil - if DetectedUnit:IsAlive() then - --self:E(DetectedUnit:GetName()) - DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) - end - if DetectedObject then - - -- Yes, the DetectedUnit is still detected or exists. Flag as identified. - self:IdentifyDetectedObject( DetectedObject ) - else - -- There was no DetectedObject, remove DetectedUnit from the Set. - self:AddChangeUnit( DetectedItem, "RU", DetectedUnitName ) - DetectedItemSet:Remove( DetectedUnitName ) - end - end - end - - - -- Now we need to loop through the unidentified detected units and add these... These are all new items. - for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - - local DetectedObject = self:GetDetectedObject( DetectedUnitName ) - if DetectedObject then - self:T( { "Detected Unit #", DetectedUnitName } ) - - local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT - - if DetectedUnit then - local DetectedTypeName = DetectedUnit:GetTypeName() - local DetectedItem = self:GetDetectedItem( DetectedTypeName ) - if not DetectedItem then - DetectedItem = self:AddDetectedItem( DetectedTypeName ) - DetectedItem.Type = DetectedUnit:GetTypeName() - end - - DetectedItem.Set:AddUnit( DetectedUnit ) - self:AddChangeUnit( DetectedItem, "AU", DetectedTypeName ) - end - end - end - - for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do - - local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem - local DetectedSet = DetectedItem.Set - - self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - --self:NearestFAC( DetectedItem ) - end - - end - - --- Report summary of a DetectedItem using a given numeric index. - -- @param #DETECTION_TYPES self - -- @param Index - -- @return #string - function DETECTION_TYPES:DetectedItemReportSummary( DetectedTypeName ) - self:F( DetectedTypeName ) - - local DetectedItem = self:GetDetectedItem( DetectedTypeName ) - local DetectedSet = self:GetDetectedSet( DetectedTypeName ) - - self:T( DetectedItem ) - if DetectedItem then - - local ThreatLevelA2G = DetectedSet:CalculateThreatLevelA2G() - local DetectedItemsCount = DetectedSet:Count() - local DetectedItemType = DetectedItem.Type - - local ReportSummary = string.format( - "Threat [%s] (%2d) - %2d of %s", - string.rep( "■", ThreatLevelA2G ), - ThreatLevelA2G, - DetectedItemsCount, - DetectedItemType - ) - self:T( ReportSummary ) - - return ReportSummary - end - end - - --- Report detailed of a detection result. - -- @param #DETECTION_TYPES self - -- @return #string - function DETECTION_TYPES:DetectedReportDetailed() - self:F() - - local Report = REPORT:New( "Detected types:" ) - for DetectedItemTypeName, DetectedItem in pairs( self.DetectedItems ) do - local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem - local ReportSummary = self:DetectedItemReportSummary( DetectedItemTypeName ) - Report:Add( ReportSummary ) - end - - local ReportText = Report:Text() - - return ReportText - end - -end - - -do -- DETECTION_AREAS - - --- # 4) DETECTION_AREAS class, extends @{Detection#DETECTION_BASE} - -- - -- The DETECTION_AREAS class will detect units within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s), - -- and will build a list (table) of @{Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. - -- The class is group the detected units within zones given a DetectedZoneRange parameter. - -- A set with multiple detected zones will be created as there are groups of units detected. - -- - -- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones - -- - -- The methods to manage the DetectedItems[].Set(s) are implemented in @{Detection#DECTECTION_BASE} and - -- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Detection#DETECTION_AREAS}. - -- - -- Retrieve the DetectedItems[].Set with the method @{Detection#DETECTION_BASE.GetDetectedSet}(). A @{Set#SET_UNIT} object will be returned. - -- - -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). - -- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). - -- If you want to obtain a specific zone from the DetectedZones, use the method @{Detection#DETECTION_BASE.GetDetectionZone}() with a given index. - -- - -- ## 4.4) Flare or Smoke detected units - -- - -- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. - -- - -- ## 4.5) Flare or Smoke or Bound detected zones - -- - -- Use the methods: - -- - -- * @{Detection#DETECTION_AREAS.FlareDetectedZones}() to flare in a color - -- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to smoke in a color - -- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to bound with a tire with a white flag - -- - -- the detected zones when a new detection has taken place. - -- - -- @type DETECTION_AREAS - -- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. - -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. - -- @extends #DETECTION_BASE - DETECTION_AREAS = { - ClassName = "DETECTION_AREAS", - DetectionZoneRange = nil, - } - - - --- DETECTION_AREAS constructor. - -- @param #DETECTION_AREAS self - -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. - -- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. - -- @return #DETECTION_AREAS - function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange ) - - -- Inherits from DETECTION_BASE - local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) ) - - self.DetectionZoneRange = DetectionZoneRange - - self._SmokeDetectedUnits = false - self._FlareDetectedUnits = false - self._SmokeDetectedZones = false - self._FlareDetectedZones = false - self._BoundDetectedZones = false - - return self - end - - --- Report summary of a detected item using a given numeric index. - -- @param #DETECTION_AREAS self - -- @param Index - -- @return #string - function DETECTION_AREAS:DetectedItemReportSummary( Index ) - self:F( Index ) - - local DetectedItem = self:GetDetectedItem( Index ) - if DetectedItem then - local DetectedSet = self:GetDetectedSet( Index ) - local ReportSummaryItem - - local DetectedZone = self:GetDetectedZone( Index ) - local DetectedItemPointVec3 = DetectedZone:GetPointVec3() - local DetectedItemPointLL = DetectedItemPointVec3:ToStringLL( 3, true ) - - local ThreatLevelA2G = self:GetTreatLevelA2G( DetectedItem ) - local DetectedItemsCount = DetectedSet:Count() - local DetectedItemsTypes = DetectedSet:GetTypeNames() - - local ReportSummary = string.format( - "%s - Threat [%s] (%2d) - %2d of %s", - DetectedItemPointLL, - string.rep( "■", ThreatLevelA2G ), - ThreatLevelA2G, - DetectedItemsCount, - DetectedItemsTypes - ) - - return ReportSummary - end - - return nil - end - - - --- Returns if there are friendlies nearby the FAC units ... - -- @param #DETECTION_AREAS self - -- @return #boolean trhe if there are friendlies nearby - function DETECTION_AREAS:IsFriendliesNearBy( DetectedItem ) - - self:T3( DetectedItem.FriendliesNearBy ) - return DetectedItem.FriendliesNearBy or false - end - - --- Calculate the maxium A2G threat level of the DetectedItem. - -- @param #DETECTION_AREAS self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - function DETECTION_AREAS:CalculateThreatLevelA2G( DetectedItem ) - - local MaxThreatLevelA2G = 0 - for UnitName, UnitData in pairs( DetectedItem.Set:GetSet() ) do - local ThreatUnit = UnitData -- Wrapper.Unit#UNIT - local ThreatLevelA2G = ThreatUnit:GetThreatLevel() - if ThreatLevelA2G > MaxThreatLevelA2G then - MaxThreatLevelA2G = ThreatLevelA2G - end - end - - self:T3( MaxThreatLevelA2G ) - DetectedItem.MaxThreatLevelA2G = MaxThreatLevelA2G - - end - - --- Find the nearest FAC of the DetectedItem. - -- @param #DETECTION_AREAS self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @return Wrapper.Unit#UNIT The nearest FAC unit - function DETECTION_AREAS:NearestFAC( DetectedItem ) - - local NearestFAC = nil - local MinDistance = 1000000000 -- Units are not further than 1000000 km away from an area :-) - - for FACGroupName, FACGroupData in pairs( self.DetectionSetGroup:GetSet() ) do - for FACUnit, FACUnitData in pairs( FACGroupData:GetUnits() ) do - local FACUnit = FACUnitData -- Wrapper.Unit#UNIT - if FACUnit:IsActive() then - local Vec3 = FACUnit:GetVec3() - local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) - local Distance = PointVec3:Get2DDistance(POINT_VEC3:NewFromVec3( FACUnit:GetVec3() ) ) - if Distance < MinDistance then - MinDistance = Distance - NearestFAC = FACUnit - end - end - end - end - - DetectedItem.NearestFAC = NearestFAC - - end - - --- Returns the A2G threat level of the units in the DetectedItem - -- @param #DETECTION_AREAS self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @return #number a scale from 0 to 10. - function DETECTION_AREAS:GetTreatLevelA2G( DetectedItem ) - - self:T3( DetectedItem.MaxThreatLevelA2G ) - return DetectedItem.MaxThreatLevelA2G - end - - - - --- Smoke the detected units - -- @param #DETECTION_AREAS self - -- @return #DETECTION_AREAS self - function DETECTION_AREAS:SmokeDetectedUnits() - self:F2() - - self._SmokeDetectedUnits = true - return self - end - - --- Flare the detected units - -- @param #DETECTION_AREAS self - -- @return #DETECTION_AREAS self - function DETECTION_AREAS:FlareDetectedUnits() - self:F2() - - self._FlareDetectedUnits = true - return self - end - - --- Smoke the detected zones - -- @param #DETECTION_AREAS self - -- @return #DETECTION_AREAS self - function DETECTION_AREAS:SmokeDetectedZones() - self:F2() - - self._SmokeDetectedZones = true - return self - end - - --- Flare the detected zones - -- @param #DETECTION_AREAS self - -- @return #DETECTION_AREAS self - function DETECTION_AREAS:FlareDetectedZones() - self:F2() - - self._FlareDetectedZones = true - return self - end - - --- Bound the detected zones - -- @param #DETECTION_AREAS self - -- @return #DETECTION_AREAS self - function DETECTION_AREAS:BoundDetectedZones() - self:F2() - - self._BoundDetectedZones = true - return self - end - - --- Make text documenting the changes of the detected zone. - -- @param #DETECTION_AREAS self - -- @param #DETECTION_BASE.DetectedItem DetectedItem - -- @return #string The Changes text - function DETECTION_AREAS:GetChangeText( DetectedItem ) - self:F( DetectedItem ) - - local MT = {} - - for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do - - if ChangeCode == "AA" then - MT[#MT+1] = "Detected new area " .. ChangeData.ItemID .. ". The center target is a " .. ChangeData.ItemUnitType .. "." - end - - if ChangeCode == "RAU" then - MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". Removed the center target." - end - - if ChangeCode == "AAU" then - MT[#MT+1] = "Changed area " .. ChangeData.ItemID .. ". The new center target is a " .. ChangeData.ItemUnitType .. "." - end - - if ChangeCode == "RA" then - MT[#MT+1] = "Removed old area " .. ChangeData.ItemID .. ". No more targets in this area." - end - - if ChangeCode == "AU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Detected for area " .. ChangeData.ItemID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - if ChangeCode == "RU" then - local MTUT = {} - for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do - if ChangeUnitType ~= "ItemID" then - MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType - end - end - MT[#MT+1] = "Removed for area " .. ChangeData.ItemID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "." - end - - end - - return table.concat( MT, "\n" ) - - end - - - --- Make a DetectionSet table. This function will be overridden in the derived clsses. - -- @param #DETECTION_AREAS self - -- @return #DETECTION_AREAS self - function DETECTION_AREAS:CreateDetectionSets() - self:F2() - - - self:T( "Checking Detected Items for new Detected Units ..." ) - -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. - -- Regroup when needed, split groups when needed. - for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do - - local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem - if DetectedItem then - - self:T( { "Detected Item ID:", DetectedItemID } ) - - - local DetectedSet = DetectedItem.Set - - local AreaExists = false -- This flag will determine of the detected area is still existing. - - -- First test if the center unit is detected in the detection area. - self:T3( { "Zone Center Unit:", DetectedItem.Zone.ZoneUNIT.UnitName } ) - local DetectedZoneObject = self:GetDetectedObject( DetectedItem.Zone.ZoneUNIT.UnitName ) - self:T3( { "Detected Zone Object:", DetectedItem.Zone:GetName(), DetectedZoneObject } ) - - if DetectedZoneObject then - - --self:IdentifyDetectedObject( DetectedZoneObject ) - AreaExists = true - - - - else - -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. - -- First remove the center unit from the set. - DetectedSet:RemoveUnitsByName( DetectedItem.Zone.ZoneUNIT.UnitName ) - - self:AddChangeItem( DetectedItem, 'RAU', "Dummy" ) - - -- Then search for a new center area unit within the set. Note that the new area unit candidate must be within the area range. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = self:GetDetectedObject( DetectedUnit.UnitName ) - - -- The DetectedObject can be nil when the DetectedUnit is not alive anymore or it is not in the DetectedObjects map. - -- If the DetectedUnit was already identified, DetectedObject will be nil. - if DetectedObject then - self:IdentifyDetectedObject( DetectedObject ) - AreaExists = true - - DetectedItem.Zone:BoundZone( 12, self.CountryID, true) - - -- Assign the Unit as the new center unit of the detected area. - DetectedItem.Zone = ZONE_UNIT:New( DetectedUnit:GetName(), DetectedUnit, self.DetectionZoneRange ) - - self:AddChangeItem( DetectedItem, "AAU", DetectedItem.Zone.ZoneUNIT:GetTypeName() ) - - -- We don't need to add the DetectedObject to the area set, because it is already there ... - break - end - end - end - - -- Now we've determined the center unit of the area, now we can iterate the units in the detected area. - -- Note that the position of the area may have moved due to the center unit repositioning. - -- If no center unit was identified, then the detected area does not exist anymore and should be deleted, as there are no valid units that can be the center unit. - if AreaExists then - - -- ok, we found the center unit of the area, now iterate through the detected area set and see which units are still within the center unit zone ... - -- Those units within the zone are flagged as Identified. - -- If a unit was not found in the set, remove it from the set. This may be added later to other existing or new sets. - for DetectedUnitName, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - local DetectedObject = nil - if DetectedUnit:IsAlive() then - --self:E(DetectedUnit:GetName()) - DetectedObject = self:GetDetectedObject( DetectedUnit:GetName() ) - end - if DetectedObject then - - -- Check if the DetectedUnit is within the DetectedItem.Zone - if DetectedUnit:IsInZone( DetectedItem.Zone ) then - - -- Yes, the DetectedUnit is within the DetectedItem.Zone, no changes, DetectedUnit can be kept within the Set. - self:IdentifyDetectedObject( DetectedObject ) - - else - -- No, the DetectedUnit is not within the DetectedItem.Zone, remove DetectedUnit from the Set. - DetectedSet:Remove( DetectedUnitName ) - self:AddChangeUnit( DetectedItem, "RU", DetectedUnit:GetTypeName() ) - end - - else - -- There was no DetectedObject, remove DetectedUnit from the Set. - self:AddChangeUnit( DetectedItem, "RU", "destroyed target" ) - DetectedSet:Remove( DetectedUnitName ) - - -- The DetectedObject has been identified, because it does not exist ... - -- self:IdentifyDetectedObject( DetectedObject ) - end - end - else - DetectedItem.Zone:BoundZone( 12, self.CountryID, true) - self:RemoveDetectedItem( DetectedItemID ) - self:AddChangeItem( DetectedItem, "RA" ) - end - end - end - - -- We iterated through the existing detection areas and: - -- - We checked which units are still detected in each detection area. Those units were flagged as Identified. - -- - We recentered the detection area to new center units where it was needed. - -- - -- Now we need to loop through the unidentified detected units and see where they belong: - -- - They can be added to a new detection area and become the new center unit. - -- - They can be added to a new detection area. - for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do - - local DetectedObject = self:GetDetectedObject( DetectedUnitName ) - - if DetectedObject then - - -- We found an unidentified unit outside of any existing detection area. - local DetectedUnit = UNIT:FindByName( DetectedUnitName ) -- Wrapper.Unit#UNIT - - local AddedToDetectionArea = false - - for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do - - local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem - if DetectedItem then - self:T( "Detection Area #" .. DetectedItem.ItemID ) - local DetectedSet = DetectedItem.Set - if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedItem.Zone ) then - self:IdentifyDetectedObject( DetectedObject ) - DetectedSet:AddUnit( DetectedUnit ) - AddedToDetectionArea = true - self:AddChangeUnit( DetectedItem, "AU", DetectedUnit:GetTypeName() ) - end - end - end - - if AddedToDetectionArea == false then - - -- New detection area - local DetectedItem = self:AddDetectedItemZone( nil, - SET_UNIT:New(), - ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange ) - ) - --self:E( DetectedItem.Zone.ZoneUNIT.UnitName ) - DetectedItem.Set:AddUnit( DetectedUnit ) - self:AddChangeItem( DetectedItem, "AA", DetectedUnit:GetTypeName() ) - end - end - end - - -- Now all the tests should have been build, now make some smoke and flares... - -- We also report here the friendlies within the detected areas. - - for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do - - local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem - local DetectedSet = DetectedItem.Set - local DetectedZone = DetectedItem.Zone - - self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table - self:CalculateThreatLevelA2G( DetectedItem ) -- Calculate A2G threat level - self:NearestFAC( DetectedItem ) - - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedZone.ZoneUNIT:SmokeRed() - end - DetectedSet:ForEachUnit( - --- @param Wrapper.Unit#UNIT DetectedUnit - function( DetectedUnit ) - if DetectedUnit:IsAlive() then - self:T( "Detected Set #" .. DetectedItem.ItemID .. ":" .. DetectedUnit:GetName() ) - if DETECTION_AREAS._FlareDetectedUnits or self._FlareDetectedUnits then - DetectedUnit:FlareGreen() - end - if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then - DetectedUnit:SmokeGreen() - end - end - end - ) - if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then - DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) ) - end - if DETECTION_AREAS._SmokeDetectedZones or self._SmokeDetectedZones then - DetectedZone:SmokeZone( SMOKECOLOR.White, 30 ) - end - - if DETECTION_AREAS._BoundDetectedZones or self._BoundDetectedZones then - DetectedZone:BoundZone( 12, self.CountryID ) - end - end - - end - -end ---- Single-Player:**No** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** -- **AI Balancing will replace in multi player missions --- non-occupied human slots with AI groups, in order to provide an engaging simulation environment, --- even when there are hardly any players in the mission.** --- --- ![Banner Image](..\Presentations\AI_Balancer\Dia1.JPG) --- --- === --- --- # 1) @{AI_Balancer#AI_BALANCER} class, extends @{Fsm#FSM_SET} --- --- The @{AI_Balancer#AI_BALANCER} class monitors and manages as many replacement AI groups as there are --- CLIENTS in a SET_CLIENT collection, which are not occupied by human players. --- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions. --- --- The parent class @{Fsm#FSM_SET} manages the functionality to control the Finite State Machine (FSM). --- The mission designer can tailor the behaviour of the AI_BALANCER, by defining event and state transition methods. --- An explanation about state and event transition methods can be found in the @{FSM} module documentation. --- --- The mission designer can tailor the AI_BALANCER behaviour, by implementing a state or event handling method for the following: --- --- * **@{#AI_BALANCER.OnAfterSpawned}**( AISet, From, Event, To, AIGroup ): Define to add extra logic when an AI is spawned. --- --- ## 1.1) AI_BALANCER construction --- --- Create a new AI_BALANCER object with the @{#AI_BALANCER.New}() method: --- --- ## 1.2) AI_BALANCER is a FSM --- --- ![Process](..\Presentations\AI_Balancer\Dia13.JPG) --- --- ### 1.2.1) AI_BALANCER States --- --- * **Monitoring** ( Set ): Monitoring the Set if all AI is spawned for the Clients. --- * **Spawning** ( Set, ClientName ): There is a new AI group spawned with ClientName as the name of reference. --- * **Spawned** ( Set, AIGroup ): A new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes. --- * **Destroying** ( Set, AIGroup ): The AI is being destroyed. --- * **Returning** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. Handle this state to customize the return behaviour of the AI, if any. --- --- ### 1.2.2) AI_BALANCER Events --- --- * **Monitor** ( Set ): Every 10 seconds, the Monitor event is triggered to monitor the Set. --- * **Spawn** ( Set, ClientName ): Triggers when there is a new AI group to be spawned with ClientName as the name of reference. --- * **Spawned** ( Set, AIGroup ): Triggers when a new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes. --- * **Destroy** ( Set, AIGroup ): The AI is being destroyed. --- * **Return** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. --- --- ## 1.3) AI_BALANCER spawn interval for replacement AI --- --- Use the method @{#AI_BALANCER.InitSpawnInterval}() to set the earliest and latest interval in seconds that is waited until a new replacement AI is spawned. --- --- ## 1.4) AI_BALANCER returns AI to Airbases --- --- By default, When a human player joins a slot that is AI_BALANCED, the AI group will be destroyed by default. --- However, there are 2 additional options that you can use to customize the destroy behaviour. --- When a human player joins a slot, you can configure to let the AI return to: --- --- * @{#AI_BALANCER.ReturnToHomeAirbase}: Returns the AI to the **home** @{Airbase#AIRBASE}. --- * @{#AI_BALANCER.ReturnToNearestAirbases}: Returns the AI to the **nearest friendly** @{Airbase#AIRBASE}. --- --- Note that when AI returns to an airbase, the AI_BALANCER will trigger the **Return** event and the AI will return, --- otherwise the AI_BALANCER will trigger a **Destroy** event, and the AI will be destroyed. --- --- === --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-17: There is still a problem with AI being destroyed, but not respawned. Need to check further upon that. --- --- 2017-01-08: AI_BALANCER:**InitSpawnInterval( Earliest, Latest )** added. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) --- * **SNAFU**: Had a couple of mails with the guys to validate, if the same concept in the GCI/CAP script could be reworked within MOOSE. None of the script code has been used however within the new AI_BALANCER moose class. --- --- ### Authors: --- --- * FlightControl: Framework Design & Programming and Documentation. --- --- @module AI_Balancer - ---- AI_BALANCER class --- @type AI_BALANCER --- @field Core.Set#SET_CLIENT SetClient --- @field Functional.Spawn#SPAWN SpawnAI --- @field Wrapper.Group#GROUP Test --- @extends Core.Fsm#FSM_SET -AI_BALANCER = { - ClassName = "AI_BALANCER", - PatrolZones = {}, - AIGroups = {}, - Earliest = 5, -- Earliest a new AI can be spawned is in 5 seconds. - Latest = 60, -- Latest a new AI can be spawned is in 60 seconds. -} - - - ---- Creates a new AI_BALANCER object --- @param #AI_BALANCER self --- @param Core.Set#SET_CLIENT SetClient A SET\_CLIENT object that will contain the CLIENT objects to be monitored if they are alive or not (joined by a player). --- @param Functional.Spawn#SPAWN SpawnAI The default Spawn object to spawn new AI Groups when needed. --- @return #AI_BALANCER -function AI_BALANCER:New( SetClient, SpawnAI ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM_SET:New( SET_GROUP:New() ) ) -- AI.AI_Balancer#AI_BALANCER - - -- TODO: Define the OnAfterSpawned event - self:SetStartState( "None" ) - self:AddTransition( "*", "Monitor", "Monitoring" ) - self:AddTransition( "*", "Spawn", "Spawning" ) - self:AddTransition( "Spawning", "Spawned", "Spawned" ) - self:AddTransition( "*", "Destroy", "Destroying" ) - self:AddTransition( "*", "Return", "Returning" ) - - self.SetClient = SetClient - self.SetClient:FilterOnce() - self.SpawnAI = SpawnAI - - self.SpawnQueue = {} - - self.ToNearestAirbase = false - self.ToHomeAirbase = false - - self:__Monitor( 1 ) - - return self -end - ---- Sets the earliest to the latest interval in seconds how long AI_BALANCER will wait to spawn a new AI. --- Provide 2 identical seconds if the interval should be a fixed amount of seconds. --- @param #AI_BALANCER self --- @param #number Earliest The earliest a new AI can be spawned in seconds. --- @param #number Latest The latest a new AI can be spawned in seconds. --- @return self -function AI_BALANCER:InitSpawnInterval( Earliest, Latest ) - - self.Earliest = Earliest - self.Latest = Latest - - return self -end - ---- Returns the AI to the nearest friendly @{Airbase#AIRBASE}. --- @param #AI_BALANCER self --- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}. --- @param Core.Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Set#SET_AIRBASE}s to evaluate where to return to. -function AI_BALANCER:ReturnToNearestAirbases( ReturnTresholdRange, ReturnAirbaseSet ) - - self.ToNearestAirbase = true - self.ReturnTresholdRange = ReturnTresholdRange - self.ReturnAirbaseSet = ReturnAirbaseSet -end - ---- Returns the AI to the home @{Airbase#AIRBASE}. --- @param #AI_BALANCER self --- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}. -function AI_BALANCER:ReturnToHomeAirbase( ReturnTresholdRange ) - - self.ToHomeAirbase = true - self.ReturnTresholdRange = ReturnTresholdRange -end - ---- @param #AI_BALANCER self --- @param Core.Set#SET_GROUP SetGroup --- @param #string ClientName --- @param Wrapper.Group#GROUP AIGroup -function AI_BALANCER:onenterSpawning( SetGroup, From, Event, To, ClientName ) - - -- OK, Spawn a new group from the default SpawnAI object provided. - local AIGroup = self.SpawnAI:Spawn() -- Wrapper.Group#GROUP - if AIGroup then - AIGroup:E( "Spawning new AIGroup" ) - --TODO: need to rework UnitName thing ... - - SetGroup:Add( ClientName, AIGroup ) - self.SpawnQueue[ClientName] = nil - - -- Fire the Spawned event. The first parameter is the AIGroup just Spawned. - -- Mission designers can catch this event to bind further actions to the AIGroup. - self:Spawned( AIGroup ) - end -end - ---- @param #AI_BALANCER self --- @param Core.Set#SET_GROUP SetGroup --- @param Wrapper.Group#GROUP AIGroup -function AI_BALANCER:onenterDestroying( SetGroup, From, Event, To, ClientName, AIGroup ) - - AIGroup:Destroy() - SetGroup:Flush() - SetGroup:Remove( ClientName ) - SetGroup:Flush() -end - ---- @param #AI_BALANCER self --- @param Core.Set#SET_GROUP SetGroup --- @param Wrapper.Group#GROUP AIGroup -function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup ) - - local AIGroupTemplate = AIGroup:GetTemplate() - if self.ToHomeAirbase == true then - local WayPointCount = #AIGroupTemplate.route.points - local SwitchWayPointCommand = AIGroup:CommandSwitchWayPoint( 1, WayPointCount, 1 ) - AIGroup:SetCommand( SwitchWayPointCommand ) - AIGroup:MessageToRed( "Returning to home base ...", 30 ) - else - -- Okay, we need to send this Group back to the nearest base of the Coalition of the AI. - --TODO: i need to rework the POINT_VEC2 thing. - local PointVec2 = POINT_VEC2:New( AIGroup:GetVec2().x, AIGroup:GetVec2().y ) - local ClosestAirbase = self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2( PointVec2 ) - self:T( ClosestAirbase.AirbaseName ) - AIGroup:MessageToRed( "Returning to " .. ClosestAirbase:GetName().. " ...", 30 ) - local RTBRoute = AIGroup:RouteReturnToAirbase( ClosestAirbase ) - AIGroupTemplate.route = RTBRoute - AIGroup:Respawn( AIGroupTemplate ) - end - -end - - ---- @param #AI_BALANCER self -function AI_BALANCER:onenterMonitoring( SetGroup ) - - self:T2( { self.SetClient:Count() } ) - --self.SetClient:Flush() - - self.SetClient:ForEachClient( - --- @param Wrapper.Client#CLIENT Client - function( Client ) - self:T3(Client.ClientName) - - local AIGroup = self.Set:Get( Client.UnitName ) -- Wrapper.Group#GROUP - if Client:IsAlive() then - - if AIGroup and AIGroup:IsAlive() == true then - - if self.ToNearestAirbase == false and self.ToHomeAirbase == false then - self:Destroy( Client.UnitName, AIGroup ) - else - -- We test if there is no other CLIENT within the self.ReturnTresholdRange of the first unit of the AI group. - -- If there is a CLIENT, the AI stays engaged and will not return. - -- If there is no CLIENT within the self.ReturnTresholdRange, then the unit will return to the Airbase return method selected. - - local PlayerInRange = { Value = false } - local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnTresholdRange ) - - self:T2( RangeZone ) - - _DATABASE:ForEachPlayer( - --- @param Wrapper.Unit#UNIT RangeTestUnit - function( RangeTestUnit, RangeZone, AIGroup, PlayerInRange ) - self:T2( { PlayerInRange, RangeTestUnit.UnitName, RangeZone.ZoneName } ) - if RangeTestUnit:IsInZone( RangeZone ) == true then - self:T2( "in zone" ) - if RangeTestUnit:GetCoalition() ~= AIGroup:GetCoalition() then - self:T2( "in range" ) - PlayerInRange.Value = true - end - end - end, - - --- @param Core.Zone#ZONE_RADIUS RangeZone - -- @param Wrapper.Group#GROUP AIGroup - function( RangeZone, AIGroup, PlayerInRange ) - if PlayerInRange.Value == false then - self:Return( AIGroup ) - end - end - , RangeZone, AIGroup, PlayerInRange - ) - - end - self.Set:Remove( Client.UnitName ) - end - else - if not AIGroup or not AIGroup:IsAlive() == true then - self:T( "Client " .. Client.UnitName .. " not alive." ) - if not self.SpawnQueue[Client.UnitName] then - -- Spawn a new AI taking into account the spawn interval Earliest, Latest - self:__Spawn( math.random( self.Earliest, self.Latest ), Client.UnitName ) - self.SpawnQueue[Client.UnitName] = true - self:E( "New AI Spawned for Client " .. Client.UnitName ) - end - end - end - return true - end - ) - - self:__Monitor( 10 ) -end - - - ---- **AI** -- **Air Patrolling or Staging.** --- --- ![Banner Image](..\Presentations\AI_PATROL\Dia1.JPG) --- --- === --- --- AI PATROL classes makes AI Controllables execute an Patrol. --- --- There are the following types of PATROL classes defined: --- --- * @{#AI_PATROL_ZONE}: Perform a PATROL in a zone. --- --- ==== --- --- # **OPEN ISSUES** --- --- 2017-01-17: When Spawned AI is located at an airbase, it will be routed first back to the airbase after take-off. --- --- 2016-01-17: --- -- Fixed problem with AI returning to base too early and unexpected. --- -- ReSpawning of AI will reset the AI_PATROL and derived classes. --- -- Checked the correct workings of SCHEDULER, and it DOES work correctly. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-17: Rename of class: **AI\_PATROL\_ZONE** is the new name for the old _AI\_PATROLZONE_. --- --- 2017-01-15: Complete revision. AI_PATROL_ZONE is the base class for other AI_PATROL like classes. --- --- 2016-09-01: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review. --- --- ### Authors: --- --- * **FlightControl**: Design & Programming. --- --- @module AI_Patrol - ---- AI_PATROL_ZONE class --- @type AI_PATROL_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @field Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @field Functional.Spawn#SPAWN CoordTest --- @extends Core.Fsm#FSM_CONTROLLABLE - ---- # 1) @{#AI_PATROL_ZONE} class, extends @{Fsm#FSM_CONTROLLABLE} --- --- The @{#AI_PATROL_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}. --- --- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) --- --- The AI_PATROL_ZONE is assigned a @{Group} and this must be done before the AI_PATROL_ZONE process can be started using the **Start** event. --- --- ![Process](..\Presentations\AI_PATROL\Dia4.JPG) --- --- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. --- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. --- --- ![Process](..\Presentations\AI_PATROL\Dia5.JPG) --- --- This cycle will continue. --- --- ![Process](..\Presentations\AI_PATROL\Dia6.JPG) --- --- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. --- --- ![Process](..\Presentations\AI_PATROL\Dia9.JPG) --- ----- Note that the enemy is not engaged! To model enemy engagement, either tailor the **Detected** event, or --- use derived AI_ classes to model AI offensive or defensive behaviour. --- --- ![Process](..\Presentations\AI_PATROL\Dia10.JPG) --- --- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. --- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. --- --- ![Process](..\Presentations\AI_PATROL\Dia11.JPG) --- --- ## 1.1) AI_PATROL_ZONE constructor --- --- * @{#AI_PATROL_ZONE.New}(): Creates a new AI_PATROL_ZONE object. --- --- ## 1.2) AI_PATROL_ZONE is a FSM --- --- ![Process](..\Presentations\AI_PATROL\Dia2.JPG) --- --- ### 1.2.1) AI_PATROL_ZONE States --- --- * **None** ( Group ): The process is not started yet. --- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. --- * **Returning** ( Group ): The AI is returning to Base. --- * **Stopped** ( Group ): The process is stopped. --- * **Crashed** ( Group ): The AI has crashed or is dead. --- --- ### 1.2.2) AI_PATROL_ZONE Events --- --- * **Start** ( Group ): Start the process. --- * **Stop** ( Group ): Stop the process. --- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone. --- * **RTB** ( Group ): Route the AI to the home base. --- * **Detect** ( Group ): The AI is detecting targets. --- * **Detected** ( Group ): The AI has detected new targets. --- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. --- --- ## 1.3) Set or Get the AI controllable --- --- * @{#AI_PATROL_ZONE.SetControllable}(): Set the AIControllable. --- * @{#AI_PATROL_ZONE.GetControllable}(): Get the AIControllable. --- --- ## 1.4) Set the Speed and Altitude boundaries of the AI controllable --- --- * @{#AI_PATROL_ZONE.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol. --- * @{#AI_PATROL_ZONE.SetAltitude}(): Set altitude boundaries of the AI, for the next patrol. --- --- ## 1.5) Manage the detection process of the AI controllable --- --- The detection process of the AI controllable can be manipulated. --- Detection requires an amount of CPU power, which has an impact on your mission performance. --- Only put detection on when absolutely necessary, and the frequency of the detection can also be set. --- --- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets. --- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased. --- --- The detection frequency can be set with @{#AI_PATROL_ZONE.SetDetectionInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. --- Use the method @{#AI_PATROL_ZONE.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI. --- --- The detection can be filtered to potential targets in a specific zone. --- Use the method @{#AI_PATROL_ZONE.SetDetectionZone}() to set the zone where targets need to be detected. --- Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected --- according the weather conditions. --- --- ## 1.6) Manage the "out of fuel" in the AI_PATROL_ZONE --- --- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. --- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. --- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, --- while a new AI is targetted to the AI_PATROL_ZONE. --- Once the time is finished, the old AI will return to the base. --- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this proces in place. --- --- ## 1.7) Manage "damage" behaviour of the AI in the AI_PATROL_ZONE --- --- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on. --- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). --- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this proces in place. --- --- === --- --- @field #AI_PATROL_ZONE AI_PATROL_ZONE --- -AI_PATROL_ZONE = { - ClassName = "AI_PATROL_ZONE", -} - ---- Creates a new AI_PATROL_ZONE object --- @param #AI_PATROL_ZONE self --- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO --- @return #AI_PATROL_ZONE self --- @usage --- -- Define a new AI_PATROL_ZONE Object. This PatrolArea will patrol an AIControllable within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h. --- PatrolZone = ZONE:New( 'PatrolZone' ) --- PatrolSpawn = SPAWN:New( 'Patrol Group' ) --- PatrolArea = AI_PATROL_ZONE:New( PatrolZone, 3000, 6000, 600, 900 ) -function AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_PATROL_ZONE - - - self.PatrolZone = PatrolZone - self.PatrolFloorAltitude = PatrolFloorAltitude - self.PatrolCeilingAltitude = PatrolCeilingAltitude - self.PatrolMinSpeed = PatrolMinSpeed - self.PatrolMaxSpeed = PatrolMaxSpeed - - -- defafult PatrolAltType to "RADIO" if not specified - self.PatrolAltType = PatrolAltType or "RADIO" - - self:SetDetectionInterval( 30 ) - - self.CheckStatus = true - - self:ManageFuel( .2, 60 ) - self:ManageDamage( 1 ) - - - self.DetectedUnits = {} -- This table contains the targets detected during patrol. - - self:SetStartState( "None" ) - - self:AddTransition( "*", "Stop", "Stopped" ) - ---- OnLeave Transition Handler for State Stopped. --- @function [parent=#AI_PATROL_ZONE] OnLeaveStopped --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnEnter Transition Handler for State Stopped. --- @function [parent=#AI_PATROL_ZONE] OnEnterStopped --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- OnBefore Transition Handler for Event Stop. --- @function [parent=#AI_PATROL_ZONE] OnBeforeStop --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event Stop. --- @function [parent=#AI_PATROL_ZONE] OnAfterStop --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event Stop. --- @function [parent=#AI_PATROL_ZONE] Stop --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event Stop. --- @function [parent=#AI_PATROL_ZONE] __Stop --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - - self:AddTransition( "None", "Start", "Patrolling" ) - ---- OnBefore Transition Handler for Event Start. --- @function [parent=#AI_PATROL_ZONE] OnBeforeStart --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event Start. --- @function [parent=#AI_PATROL_ZONE] OnAfterStart --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event Start. --- @function [parent=#AI_PATROL_ZONE] Start --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event Start. --- @function [parent=#AI_PATROL_ZONE] __Start --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - ---- OnLeave Transition Handler for State Patrolling. --- @function [parent=#AI_PATROL_ZONE] OnLeavePatrolling --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnEnter Transition Handler for State Patrolling. --- @function [parent=#AI_PATROL_ZONE] OnEnterPatrolling --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - - self:AddTransition( "Patrolling", "Route", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE. - ---- OnBefore Transition Handler for Event Route. --- @function [parent=#AI_PATROL_ZONE] OnBeforeRoute --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event Route. --- @function [parent=#AI_PATROL_ZONE] OnAfterRoute --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event Route. --- @function [parent=#AI_PATROL_ZONE] Route --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event Route. --- @function [parent=#AI_PATROL_ZONE] __Route --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE. - ---- OnBefore Transition Handler for Event Status. --- @function [parent=#AI_PATROL_ZONE] OnBeforeStatus --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event Status. --- @function [parent=#AI_PATROL_ZONE] OnAfterStatus --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event Status. --- @function [parent=#AI_PATROL_ZONE] Status --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event Status. --- @function [parent=#AI_PATROL_ZONE] __Status --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Detect", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE. - ---- OnBefore Transition Handler for Event Detect. --- @function [parent=#AI_PATROL_ZONE] OnBeforeDetect --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event Detect. --- @function [parent=#AI_PATROL_ZONE] OnAfterDetect --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event Detect. --- @function [parent=#AI_PATROL_ZONE] Detect --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event Detect. --- @function [parent=#AI_PATROL_ZONE] __Detect --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Detected", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE. - ---- OnBefore Transition Handler for Event Detected. --- @function [parent=#AI_PATROL_ZONE] OnBeforeDetected --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event Detected. --- @function [parent=#AI_PATROL_ZONE] OnAfterDetected --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event Detected. --- @function [parent=#AI_PATROL_ZONE] Detected --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event Detected. --- @function [parent=#AI_PATROL_ZONE] __Detected --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "RTB", "Returning" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE. - ---- OnBefore Transition Handler for Event RTB. --- @function [parent=#AI_PATROL_ZONE] OnBeforeRTB --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnAfter Transition Handler for Event RTB. --- @function [parent=#AI_PATROL_ZONE] OnAfterRTB --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - ---- Synchronous Event Trigger for Event RTB. --- @function [parent=#AI_PATROL_ZONE] RTB --- @param #AI_PATROL_ZONE self - ---- Asynchronous Event Trigger for Event RTB. --- @function [parent=#AI_PATROL_ZONE] __RTB --- @param #AI_PATROL_ZONE self --- @param #number Delay The delay in seconds. - ---- OnLeave Transition Handler for State Returning. --- @function [parent=#AI_PATROL_ZONE] OnLeaveReturning --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnEnter Transition Handler for State Returning. --- @function [parent=#AI_PATROL_ZONE] OnEnterReturning --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - - self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_PATROL_ZONE. - - self:AddTransition( "*", "Eject", "*" ) - self:AddTransition( "*", "Crash", "Crashed" ) - self:AddTransition( "*", "PilotDead", "*" ) - - return self -end - - - - ---- Sets (modifies) the minimum and maximum speed of the patrol. --- @param #AI_PATROL_ZONE self --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed ) - self:F2( { PatrolMinSpeed, PatrolMaxSpeed } ) - - self.PatrolMinSpeed = PatrolMinSpeed - self.PatrolMaxSpeed = PatrolMaxSpeed -end - - - ---- Sets the floor and ceiling altitude of the patrol. --- @param #AI_PATROL_ZONE self --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude ) - self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } ) - - self.PatrolFloorAltitude = PatrolFloorAltitude - self.PatrolCeilingAltitude = PatrolCeilingAltitude -end - --- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets. --- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased. - ---- Set the detection on. The AI will detect for targets. --- @param #AI_PATROL_ZONE self --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionOn() - self:F2() - - self.DetectOn = true -end - ---- Set the detection off. The AI will NOT detect for targets. --- However, the list of already detected targets will be kept and can be enquired! --- @param #AI_PATROL_ZONE self --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionOff() - self:F2() - - self.DetectOn = false -end - ---- Set the status checking off. --- @param #AI_PATROL_ZONE self --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetStatusOff() - self:F2() - - self.CheckStatus = false -end - ---- Activate the detection. The AI will detect for targets if the Detection is switched On. --- @param #AI_PATROL_ZONE self --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionActivated() - self:F2() - - self:ClearDetectedUnits() - self.DetectActivated = true - self:__Detect( -self.DetectInterval ) -end - ---- Deactivate the detection. The AI will NOT detect for targets. --- @param #AI_PATROL_ZONE self --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionDeactivated() - self:F2() - - self:ClearDetectedUnits() - self.DetectActivated = false -end - ---- Set the interval in seconds between each detection executed by the AI. --- The list of already detected targets will be kept and updated. --- Newly detected targets will be added, but already detected targets that were --- not detected in this cycle, will NOT be removed! --- The default interval is 30 seconds. --- @param #AI_PATROL_ZONE self --- @param #number Seconds The interval in seconds. --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionInterval( Seconds ) - self:F2() - - if Seconds then - self.DetectInterval = Seconds - else - self.DetectInterval = 30 - end -end - ---- Set the detection zone where the AI is detecting targets. --- @param #AI_PATROL_ZONE self --- @param Core.Zone#ZONE DetectionZone The zone where to detect targets. --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:SetDetectionZone( DetectionZone ) - self:F2() - - if DetectionZone then - self.DetectZone = DetectionZone - else - self.DetectZone = nil - end -end - ---- Gets a list of @{Unit#UNIT}s that were detected by the AI. --- No filtering is applied, so, ANY detected UNIT can be in this list. --- It is up to the mission designer to use the @{Unit} class and methods to filter the targets. --- @param #AI_PATROL_ZONE self --- @return #table The list of @{Unit#UNIT}s -function AI_PATROL_ZONE:GetDetectedUnits() - self:F2() - - return self.DetectedUnits -end - ---- Clears the list of @{Unit#UNIT}s that were detected by the AI. --- @param #AI_PATROL_ZONE self -function AI_PATROL_ZONE:ClearDetectedUnits() - self:F2() - self.DetectedUnits = {} -end - ---- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. --- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. --- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE. --- Once the time is finished, the old AI will return to the base. --- @param #AI_PATROL_ZONE self --- @param #number PatrolFuelTresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel. --- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base. --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:ManageFuel( PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime ) - - self.PatrolManageFuel = true - self.PatrolFuelTresholdPercentage = PatrolFuelTresholdPercentage - self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime - - return self -end - ---- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base. --- However, damage cannot be foreseen early on. --- Therefore, when the damage treshold is reached, --- the AI will return immediately to the home base (RTB). --- Note that for groups, the average damage of the complete group will be calculated. --- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25. --- @param #AI_PATROL_ZONE self --- @param #number PatrolDamageTreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged. --- @return #AI_PATROL_ZONE self -function AI_PATROL_ZONE:ManageDamage( PatrolDamageTreshold ) - - self.PatrolManageDamage = true - self.PatrolDamageTreshold = PatrolDamageTreshold - - return self -end - ---- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. --- @param #AI_PATROL_ZONE self --- @return #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_PATROL_ZONE:onafterStart( Controllable, From, Event, To ) - self:F2() - - self:__Route( 1 ) -- Route to the patrol point. The asynchronous trigger is important, because a spawned group and units takes at least one second to come live. - self:__Status( 60 ) -- Check status status every 30 seconds. - self:SetDetectionActivated() - - self:HandleEvent( EVENTS.PilotDead, self.OnPilotDead ) - self:HandleEvent( EVENTS.Crash, self.OnCrash ) - self:HandleEvent( EVENTS.Ejection, self.OnEjection ) - - Controllable:OptionROEHoldFire() - Controllable:OptionROTVertical() - - self.Controllable:OnReSpawn( - function( PatrolGroup ) - self:E( "ReSpawn" ) - self:__Reset( 1 ) - self:__Route( 5 ) - end - ) - - self:SetDetectionOn() - -end - - ---- @param #AI_PATROL_ZONE self ---- @param Wrapper.Controllable#CONTROLLABLE Controllable -function AI_PATROL_ZONE:onbeforeDetect( Controllable, From, Event, To ) - - return self.DetectOn and self.DetectActivated -end - ---- @param #AI_PATROL_ZONE self ---- @param Wrapper.Controllable#CONTROLLABLE Controllable -function AI_PATROL_ZONE:onafterDetect( Controllable, From, Event, To ) - - local Detected = false - - local DetectedTargets = Controllable:GetDetectedTargets() - for TargetID, Target in pairs( DetectedTargets or {} ) do - local TargetObject = Target.object - - if TargetObject and TargetObject:isExist() and TargetObject.id_ < 50000000 then - - local TargetUnit = UNIT:Find( TargetObject ) - local TargetUnitName = TargetUnit:GetName() - - if self.DetectionZone then - if TargetUnit:IsInZone( self.DetectionZone ) then - self:T( {"Detected ", TargetUnit } ) - if self.DetectedUnits[TargetUnit] == nil then - self.DetectedUnits[TargetUnit] = true - end - Detected = true - end - else - if self.DetectedUnits[TargetUnit] == nil then - self.DetectedUnits[TargetUnit] = true - end - Detected = true - end - end - end - - self:__Detect( -self.DetectInterval ) - - if Detected == true then - self:__Detected( 1.5 ) - end - -end - ---- @param Wrapper.Controllable#CONTROLLABLE AIControllable --- This statis method is called from the route path within the last task at the last waaypoint of the Controllable. --- Note that this method is required, as triggers the next route when patrolling for the Controllable. -function AI_PATROL_ZONE:_NewPatrolRoute( AIControllable ) - - local PatrolZone = AIControllable:GetState( AIControllable, "PatrolZone" ) -- PatrolCore.Zone#AI_PATROL_ZONE - PatrolZone:__Route( 1 ) -end - - ---- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. --- @param #AI_PATROL_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To ) - - self:F2() - - -- When RTB, don't allow anymore the routing. - if From == "RTB" then - return - end - - - if self.Controllable:IsAlive() then - -- Determine if the AIControllable is within the PatrolZone. - -- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point. - - local PatrolRoute = {} - - -- Calculate the current route point of the controllable as the start point of the route. - -- However, when the controllable is not in the air, - -- the controllable current waypoint is probably the airbase... - -- Thus, if we would take the current waypoint as the startpoint, upon take-off, the controllable flies - -- immediately back to the airbase, and this is not correct. - -- Therefore, when on a runway, get as the current route point a random point within the PatrolZone. - -- This will make the plane fly immediately to the patrol zone. - - if self.Controllable:InAir() == false then - self:E( "Not in the air, finding route path within PatrolZone" ) - local CurrentVec2 = self.Controllable:GetVec2() - --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). - local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() - local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) - local ToPatrolZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TakeOffParking, - POINT_VEC3.RoutePointAction.FromParkingArea, - ToPatrolZoneSpeed, - true - ) - PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint - else - self:E( "In the air, finding route path within PatrolZone" ) - local CurrentVec2 = self.Controllable:GetVec2() - --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). - local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() - local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) - local ToPatrolZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - ToPatrolZoneSpeed, - true - ) - PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint - end - - - --- Define a random point in the @{Zone}. The AI will fly to that point within the zone. - - --- Find a random 2D point in PatrolZone. - local ToTargetVec2 = self.PatrolZone:GetRandomVec2() - self:T2( ToTargetVec2 ) - - --- Define Speed and Altitude. - local ToTargetAltitude = math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ) - local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) - self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } ) - - --- Obtain a 3D @{Point} from the 2D point + altitude. - local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y ) - - --- Create a route point of type air. - local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - ToTargetSpeed, - true - ) - - --self.CoordTest:SpawnFromVec3( ToTargetPointVec3:GetVec3() ) - - --ToTargetPointVec3:SmokeRed() - - PatrolRoute[#PatrolRoute+1] = ToTargetRoutePoint - - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - self.Controllable:WayPointInitialize( PatrolRoute ) - - --- Do a trick, link the NewPatrolRoute function of the PATROLGROUP object to the AIControllable in a temporary variable ... - self.Controllable:SetState( self.Controllable, "PatrolZone", self ) - self.Controllable:WayPointFunction( #PatrolRoute, 1, "AI_PATROL_ZONE:_NewPatrolRoute" ) - - --- NOW ROUTE THE GROUP! - self.Controllable:WayPointExecute( 1, 2 ) - end - -end - ---- @param #AI_PATROL_ZONE self -function AI_PATROL_ZONE:onbeforeStatus() - - return self.CheckStatus -end - ---- @param #AI_PATROL_ZONE self -function AI_PATROL_ZONE:onafterStatus() - self:F2() - - if self.Controllable and self.Controllable:IsAlive() then - - local RTB = false - - local Fuel = self.Controllable:GetUnit(1):GetFuel() - if Fuel < self.PatrolFuelTresholdPercentage then - self:E( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" ) - local OldAIControllable = self.Controllable - local AIControllableTemplate = self.Controllable:GetTemplate() - - local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed ) - local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) ) - OldAIControllable:SetTask( TimedOrbitTask, 10 ) - - RTB = true - else - end - - -- TODO: Check GROUP damage function. - local Damage = self.Controllable:GetLife() - if Damage <= self.PatrolDamageTreshold then - self:E( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" ) - RTB = true - end - - if RTB == true then - self:RTB() - else - self:__Status( 60 ) -- Execute the Patrol event after 30 seconds. - end - end -end - ---- @param #AI_PATROL_ZONE self -function AI_PATROL_ZONE:onafterRTB() - self:F2() - - if self.Controllable and self.Controllable:IsAlive() then - - self:SetDetectionOff() - self.CheckStatus = false - - local PatrolRoute = {} - - --- Calculate the current route point. - local CurrentVec2 = self.Controllable:GetVec2() - - --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). - local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() - local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) - local ToPatrolZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - ToPatrolZoneSpeed, - true - ) - - PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint - - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - self.Controllable:WayPointInitialize( PatrolRoute ) - - --- NOW ROUTE THE GROUP! - self.Controllable:WayPointExecute( 1, 1 ) - - end - -end - ---- @param #AI_PATROL_ZONE self -function AI_PATROL_ZONE:onafterDead() - self:SetDetectionOff() - self:SetStatusOff() -end - ---- @param #AI_PATROL_ZONE self --- @param Core.Event#EVENTDATA EventData -function AI_PATROL_ZONE:OnCrash( EventData ) - - if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then - self:E( self.Controllable:GetUnits() ) - if #self.Controllable:GetUnits() == 1 then - self:__Crash( 1, EventData ) - end - end -end - ---- @param #AI_PATROL_ZONE self --- @param Core.Event#EVENTDATA EventData -function AI_PATROL_ZONE:OnEjection( EventData ) - - if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then - self:__Eject( 1, EventData ) - end -end - ---- @param #AI_PATROL_ZONE self --- @param Core.Event#EVENTDATA EventData -function AI_PATROL_ZONE:OnPilotDead( EventData ) - - if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then - self:__PilotDead( 1, EventData ) - end -end ---- **AI** -- **Provide Close Air Support to friendly ground troops.** --- --- ![Banner Image](..\Presentations\AI_CAS\Dia1.JPG) --- --- === --- --- AI CAS classes makes AI Controllables execute a Close Air Support. --- --- There are the following types of CAS classes defined: --- --- * @{#AI_CAS_ZONE}: Perform a CAS in a zone. --- --- === --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-15: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. --- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module AI_Cas - - ---- AI_CAS_ZONE class --- @type AI_CAS_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. --- @extends AI.AI_Patrol#AI_PATROL_ZONE - ---- # 1) @{#AI_CAS_ZONE} class, extends @{AI_Patrol#AI_PATROL_ZONE} --- --- @{#AI_CAS_ZONE} derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. --- --- The @{#AI_CAS_ZONE} class implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Controllable} or @{Group}. --- The AI_CAS_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. --- --- ![HoldAndEngage](..\Presentations\AI_CAS\Dia3.JPG) --- --- The AI_CAS_ZONE is assigned a @{Group} and this must be done before the AI_CAS_ZONE process can be started through the **Start** event. --- --- ![Start Event](..\Presentations\AI_CAS\Dia4.JPG) --- --- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone, --- using a random speed within the given altitude and speed limits. --- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. --- This cycle will continue until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. --- --- ![Route Event](..\Presentations\AI_CAS\Dia5.JPG) --- --- When the AI is commanded to provide Close Air Support (through the event **Engage**), the AI will fly towards the Engage Zone. --- Any target that is detected in the Engage Zone will be reported and will be destroyed by the AI. --- --- ![Engage Event](..\Presentations\AI_CAS\Dia6.JPG) --- --- The AI will detect the targets and will only destroy the targets within the Engage Zone. --- --- ![Engage Event](..\Presentations\AI_CAS\Dia7.JPG) --- --- Every target that is destroyed, is reported< by the AI. --- --- ![Engage Event](..\Presentations\AI_CAS\Dia8.JPG) --- --- Note that the AI does not know when the Engage Zone is cleared, and therefore will keep circling in the zone. --- --- ![Engage Event](..\Presentations\AI_CAS\Dia9.JPG) --- --- Until it is notified through the event **Accomplish**, which is to be triggered by an observing party: --- --- * a FAC --- * a timed event --- * a menu option selected by a human --- * a condition --- * others ... --- --- ![Engage Event](..\Presentations\AI_CAS\Dia10.JPG) --- --- When the AI has accomplished the CAS, it will fly back to the Patrol Zone. --- --- ![Engage Event](..\Presentations\AI_CAS\Dia11.JPG) --- --- It will keep patrolling there, until it is notified to RTB or move to another CAS Zone. --- It can be notified to go RTB through the **RTB** event. --- --- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. --- --- ![Engage Event](..\Presentations\AI_CAS\Dia12.JPG) --- --- # 1.1) AI_CAS_ZONE constructor --- --- * @{#AI_CAS_ZONE.New}(): Creates a new AI_CAS_ZONE object. --- --- ## 1.2) AI_CAS_ZONE is a FSM --- --- ![Process](..\Presentations\AI_CAS\Dia2.JPG) --- --- ### 1.2.1) AI_CAS_ZONE States --- --- * **None** ( Group ): The process is not started yet. --- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. --- * **Engaging** ( Group ): The AI is engaging the targets in the Engage Zone, executing CAS. --- * **Returning** ( Group ): The AI is returning to Base.. --- --- ### 1.2.2) AI_CAS_ZONE Events --- --- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. --- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. --- * **@{#AI_CAS_ZONE.Engage}**: Engage the AI to provide CAS in the Engage Zone, destroying any target it finds. --- * **@{#AI_CAS_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone. --- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. --- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Unit}. --- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Unit}s assigned in the CAS task. --- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. --- --- === --- --- @field #AI_CAS_ZONE AI_CAS_ZONE --- -AI_CAS_ZONE = { - ClassName = "AI_CAS_ZONE", -} - - - ---- Creates a new AI_CAS_ZONE object --- @param #AI_CAS_ZONE self --- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @param Core.Zone#ZONE_BASE EngageZone The zone where the engage will happen. --- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO --- @return #AI_CAS_ZONE self -function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType ) - - -- Inherits from BASE - local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_CAS_ZONE - - self.EngageZone = EngageZone - self.Accomplished = false - - self:SetDetectionZone( self.EngageZone ) - - self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE. - - --- OnBefore Transition Handler for Event Engage. - -- @function [parent=#AI_CAS_ZONE] OnBeforeEngage - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Engage. - -- @function [parent=#AI_CAS_ZONE] OnAfterEngage - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Engage. - -- @function [parent=#AI_CAS_ZONE] Engage - -- @param #AI_CAS_ZONE self - -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. - -- If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. - -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. - - --- Asynchronous Event Trigger for Event Engage. - -- @function [parent=#AI_CAS_ZONE] __Engage - -- @param #AI_CAS_ZONE self - -- @param #number Delay The delay in seconds. - -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. - -- If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. - -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. - ---- OnLeave Transition Handler for State Engaging. --- @function [parent=#AI_CAS_ZONE] OnLeaveEngaging --- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnEnter Transition Handler for State Engaging. --- @function [parent=#AI_CAS_ZONE] OnEnterEngaging --- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - - self:AddTransition( "Engaging", "Target", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE. - - self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE. - - --- OnBefore Transition Handler for Event Fired. - -- @function [parent=#AI_CAS_ZONE] OnBeforeFired - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Fired. - -- @function [parent=#AI_CAS_ZONE] OnAfterFired - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Fired. - -- @function [parent=#AI_CAS_ZONE] Fired - -- @param #AI_CAS_ZONE self - - --- Asynchronous Event Trigger for Event Fired. - -- @function [parent=#AI_CAS_ZONE] __Fired - -- @param #AI_CAS_ZONE self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE. - - --- OnBefore Transition Handler for Event Destroy. - -- @function [parent=#AI_CAS_ZONE] OnBeforeDestroy - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Destroy. - -- @function [parent=#AI_CAS_ZONE] OnAfterDestroy - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Destroy. - -- @function [parent=#AI_CAS_ZONE] Destroy - -- @param #AI_CAS_ZONE self - - --- Asynchronous Event Trigger for Event Destroy. - -- @function [parent=#AI_CAS_ZONE] __Destroy - -- @param #AI_CAS_ZONE self - -- @param #number Delay The delay in seconds. - - - self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE. - - --- OnBefore Transition Handler for Event Abort. - -- @function [parent=#AI_CAS_ZONE] OnBeforeAbort - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Abort. - -- @function [parent=#AI_CAS_ZONE] OnAfterAbort - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Abort. - -- @function [parent=#AI_CAS_ZONE] Abort - -- @param #AI_CAS_ZONE self - - --- Asynchronous Event Trigger for Event Abort. - -- @function [parent=#AI_CAS_ZONE] __Abort - -- @param #AI_CAS_ZONE self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAS_ZONE. - - --- OnBefore Transition Handler for Event Accomplish. - -- @function [parent=#AI_CAS_ZONE] OnBeforeAccomplish - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Accomplish. - -- @function [parent=#AI_CAS_ZONE] OnAfterAccomplish - -- @param #AI_CAS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Accomplish. - -- @function [parent=#AI_CAS_ZONE] Accomplish - -- @param #AI_CAS_ZONE self - - --- Asynchronous Event Trigger for Event Accomplish. - -- @function [parent=#AI_CAS_ZONE] __Accomplish - -- @param #AI_CAS_ZONE self - -- @param #number Delay The delay in seconds. - - return self -end - - ---- Set the Engage Zone where the AI is performing CAS. Note that if the EngageZone is changed, the AI needs to re-detect targets. --- @param #AI_CAS_ZONE self --- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAS. --- @return #AI_CAS_ZONE self -function AI_CAS_ZONE:SetEngageZone( EngageZone ) - self:F2() - - if EngageZone then - self.EngageZone = EngageZone - else - self.EngageZone = nil - end -end - - - ---- onafter State Transition for Event Start. --- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAS_ZONE:onafterStart( Controllable, From, Event, To ) - - -- Call the parent Start event handler - self:GetParent(self).onafterStart( self, Controllable, From, Event, To ) - self:HandleEvent( EVENTS.Dead ) - - self:SetDetectionDeactivated() -- When not engaging, set the detection off. -end - ---- @param Wrapper.Controllable#CONTROLLABLE AIControllable -function _NewEngageRoute( AIControllable ) - - AIControllable:T( "NewEngageRoute" ) - local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cas#AI_CAS_ZONE - EngageZone:__Engage( 1, EngageZone.EngageSpeed, EngageZone.EngageAltitude, EngageZone.EngageWeaponExpend, EngageZone.EngageAttackQty, EngageZone.EngageDirection ) -end - - ---- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAS_ZONE:onbeforeEngage( Controllable, From, Event, To ) - - if self.Accomplished == true then - return false - end -end - ---- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To ) - self:E("onafterTarget") - - if Controllable:IsAlive() then - - local AttackTasks = {} - - for DetectedUnit, Detected in pairs( self.DetectedUnits ) do - local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT - if DetectedUnit:IsAlive() then - if DetectedUnit:IsInZone( self.EngageZone ) then - if Detected == true then - self:E( {"Target: ", DetectedUnit } ) - self.DetectedUnits[DetectedUnit] = false - local AttackTask = Controllable:TaskAttackUnit( DetectedUnit, false, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil ) - self.Controllable:PushTask( AttackTask, 1 ) - end - end - else - self.DetectedUnits[DetectedUnit] = nil - end - end - - self:__Target( -10 ) - - end -end - - ---- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAS_ZONE:onafterAbort( Controllable, From, Event, To ) - Controllable:ClearTasks() - self:__Route( 1 ) -end - ---- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. --- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, - EngageSpeed, - EngageAltitude, - EngageWeaponExpend, - EngageAttackQty, - EngageDirection ) - self:F("onafterEngage") - - self.EngageSpeed = EngageSpeed or 400 - self.EngageAltitude = EngageAltitude or 2000 - self.EngageWeaponExpend = EngageWeaponExpend - self.EngageAttackQty = EngageAttackQty - self.EngageDirection = EngageDirection - - if Controllable:IsAlive() then - - local EngageRoute = {} - - --- Calculate the current route point. - local CurrentVec2 = self.Controllable:GetVec2() - - --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). - local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() - local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) - local ToEngageZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - self.EngageSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = CurrentRoutePoint - - local AttackTasks = {} - - for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do - local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT - self:T( DetectedUnit ) - if DetectedUnit:IsAlive() then - if DetectedUnit:IsInZone( self.EngageZone ) then - self:E( {"Engaging ", DetectedUnit } ) - AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit, - true, - EngageWeaponExpend, - EngageAttackQty, - EngageDirection - ) - end - else - self.DetectedUnits[DetectedUnit] = nil - end - end - - EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) - - --- Define a random point in the @{Zone}. The AI will fly to that point within the zone. - - --- Find a random 2D point in EngageZone. - local ToTargetVec2 = self.EngageZone:GetRandomVec2() - self:T2( ToTargetVec2 ) - - --- Obtain a 3D @{Point} from the 2D point + altitude. - local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y ) - - --- Create a route point of type air. - local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - self.EngageSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = ToTargetRoutePoint - - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - Controllable:WayPointInitialize( EngageRoute ) - - --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... - Controllable:SetState( Controllable, "EngageZone", self ) - - Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" ) - - --- NOW ROUTE THE GROUP! - Controllable:WayPointExecute( 1 ) - - Controllable:OptionROEOpenFire() - Controllable:OptionROTVertical() - - self:SetDetectionInterval( 2 ) - self:SetDetectionActivated() - self:__Target( -2 ) -- Start Targetting - end -end - - ---- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAS_ZONE:onafterAccomplish( Controllable, From, Event, To ) - self.Accomplished = true - self:SetDetectionDeactivated() -end - - ---- @param #AI_CAS_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @param Core.Event#EVENTDATA EventData -function AI_CAS_ZONE:onafterDestroy( Controllable, From, Event, To, EventData ) - - if EventData.IniUnit then - self.DetectedUnits[EventData.IniUnit] = nil - end -end - - ---- @param #AI_CAS_ZONE self --- @param Core.Event#EVENTDATA EventData -function AI_CAS_ZONE:OnEventDead( EventData ) - self:F( { "EventDead", EventData } ) - - if EventData.IniDCSUnit then - if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then - self:__Destroy( 1, EventData ) - end - end -end - - ---- **AI** - **Execute Combat Air Patrol (CAP).** --- --- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG) --- --- === --- --- AI CAP classes makes AI Controllables execute a Combat Air Patrol. --- --- There are the following types of CAP classes defined: --- --- * @{#AI_CAP_ZONE}: Perform a CAP in a zone. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-01-15: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing. --- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing. --- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision. --- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing. --- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module AI_Cap - - ---- @type AI_CAP_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. --- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. --- @extends AI.AI_Patrol#AI_PATROL_ZONE - - ---- # 1) @{#AI_CAP_ZONE} class, extends @{AI_CAP#AI_PATROL_ZONE} --- --- The @{#AI_CAP_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group} --- and automatically engage any airborne enemies that are within a certain range or within a certain zone. --- --- ![Process](..\Presentations\AI_CAP\Dia3.JPG) --- --- The AI_CAP_ZONE is assigned a @{Group} and this must be done before the AI_CAP_ZONE process can be started using the **Start** event. --- --- ![Process](..\Presentations\AI_CAP\Dia4.JPG) --- --- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. --- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. --- --- ![Process](..\Presentations\AI_CAP\Dia5.JPG) --- --- This cycle will continue. --- --- ![Process](..\Presentations\AI_CAP\Dia6.JPG) --- --- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. --- --- ![Process](..\Presentations\AI_CAP\Dia9.JPG) --- --- When enemies are detected, the AI will automatically engage the enemy. --- --- ![Process](..\Presentations\AI_CAP\Dia10.JPG) --- --- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. --- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. --- --- ![Process](..\Presentations\AI_CAP\Dia13.JPG) --- --- ## 1.1) AI_CAP_ZONE constructor --- --- * @{#AI_CAP_ZONE.New}(): Creates a new AI_CAP_ZONE object. --- --- ## 1.2) AI_CAP_ZONE is a FSM --- --- ![Process](..\Presentations\AI_CAP\Dia2.JPG) --- --- ### 1.2.1) AI_CAP_ZONE States --- --- * **None** ( Group ): The process is not started yet. --- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. --- * **Engaging** ( Group ): The AI is engaging the bogeys. --- * **Returning** ( Group ): The AI is returning to Base.. --- --- ### 1.2.2) AI_CAP_ZONE Events --- --- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. --- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. --- * **@{#AI_CAP_ZONE.Engage}**: Let the AI engage the bogeys. --- * **@{#AI_CAP_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone. --- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. --- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Unit}. --- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task. --- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. --- --- ## 1.3) Set the Range of Engagement --- --- ![Range](..\Presentations\AI_CAP\Dia11.JPG) --- --- An optional range can be set in meters, --- that will define when the AI will engage with the detected airborne enemy targets. --- The range can be beyond or smaller than the range of the Patrol Zone. --- The range is applied at the position of the AI. --- Use the method @{AI_CAP#AI_CAP_ZONE.SetEngageRange}() to define that range. --- --- ## 1.4) Set the Zone of Engagement --- --- ![Zone](..\Presentations\AI_CAP\Dia12.JPG) --- --- An optional @{Zone} can be set, --- that will define when the AI will engage with the detected airborne enemy targets. --- Use the method @{AI_Cap#AI_CAP_ZONE.SetEngageZone}() to define that Zone. --- --- === --- --- @field #AI_CAP_ZONE AI_CAP_ZONE --- -AI_CAP_ZONE = { - ClassName = "AI_CAP_ZONE", -} - - - ---- Creates a new AI_CAP_ZONE object --- @param #AI_CAP_ZONE self --- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO --- @return #AI_CAP_ZONE self -function AI_CAP_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) - - -- Inherits from BASE - local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_CAP_ZONE - - self.Accomplished = false - self.Engaging = false - - self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. - - --- OnBefore Transition Handler for Event Engage. - -- @function [parent=#AI_CAP_ZONE] OnBeforeEngage - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Engage. - -- @function [parent=#AI_CAP_ZONE] OnAfterEngage - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Engage. - -- @function [parent=#AI_CAP_ZONE] Engage - -- @param #AI_CAP_ZONE self - - --- Asynchronous Event Trigger for Event Engage. - -- @function [parent=#AI_CAP_ZONE] __Engage - -- @param #AI_CAP_ZONE self - -- @param #number Delay The delay in seconds. - ---- OnLeave Transition Handler for State Engaging. --- @function [parent=#AI_CAP_ZONE] OnLeaveEngaging --- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @return #boolean Return false to cancel Transition. - ---- OnEnter Transition Handler for State Engaging. --- @function [parent=#AI_CAP_ZONE] OnEnterEngaging --- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. - - self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. - - --- OnBefore Transition Handler for Event Fired. - -- @function [parent=#AI_CAP_ZONE] OnBeforeFired - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Fired. - -- @function [parent=#AI_CAP_ZONE] OnAfterFired - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Fired. - -- @function [parent=#AI_CAP_ZONE] Fired - -- @param #AI_CAP_ZONE self - - --- Asynchronous Event Trigger for Event Fired. - -- @function [parent=#AI_CAP_ZONE] __Fired - -- @param #AI_CAP_ZONE self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. - - --- OnBefore Transition Handler for Event Destroy. - -- @function [parent=#AI_CAP_ZONE] OnBeforeDestroy - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Destroy. - -- @function [parent=#AI_CAP_ZONE] OnAfterDestroy - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Destroy. - -- @function [parent=#AI_CAP_ZONE] Destroy - -- @param #AI_CAP_ZONE self - - --- Asynchronous Event Trigger for Event Destroy. - -- @function [parent=#AI_CAP_ZONE] __Destroy - -- @param #AI_CAP_ZONE self - -- @param #number Delay The delay in seconds. - - - self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. - - --- OnBefore Transition Handler for Event Abort. - -- @function [parent=#AI_CAP_ZONE] OnBeforeAbort - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Abort. - -- @function [parent=#AI_CAP_ZONE] OnAfterAbort - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Abort. - -- @function [parent=#AI_CAP_ZONE] Abort - -- @param #AI_CAP_ZONE self - - --- Asynchronous Event Trigger for Event Abort. - -- @function [parent=#AI_CAP_ZONE] __Abort - -- @param #AI_CAP_ZONE self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_CAP_ZONE. - - --- OnBefore Transition Handler for Event Accomplish. - -- @function [parent=#AI_CAP_ZONE] OnBeforeAccomplish - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Accomplish. - -- @function [parent=#AI_CAP_ZONE] OnAfterAccomplish - -- @param #AI_CAP_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Accomplish. - -- @function [parent=#AI_CAP_ZONE] Accomplish - -- @param #AI_CAP_ZONE self - - --- Asynchronous Event Trigger for Event Accomplish. - -- @function [parent=#AI_CAP_ZONE] __Accomplish - -- @param #AI_CAP_ZONE self - -- @param #number Delay The delay in seconds. - - return self -end - - ---- Set the Engage Zone which defines where the AI will engage bogies. --- @param #AI_CAP_ZONE self --- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP. --- @return #AI_CAP_ZONE self -function AI_CAP_ZONE:SetEngageZone( EngageZone ) - self:F2() - - if EngageZone then - self.EngageZone = EngageZone - else - self.EngageZone = nil - end -end - ---- Set the Engage Range when the AI will engage with airborne enemies. --- @param #AI_CAP_ZONE self --- @param #number EngageRange The Engage Range. --- @return #AI_CAP_ZONE self -function AI_CAP_ZONE:SetEngageRange( EngageRange ) - self:F2() - - if EngageRange then - self.EngageRange = EngageRange - else - self.EngageRange = nil - end -end - ---- onafter State Transition for Event Start. --- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAP_ZONE:onafterStart( Controllable, From, Event, To ) - - -- Call the parent Start event handler - self:GetParent(self).onafterStart( self, Controllable, From, Event, To ) - self:HandleEvent( EVENTS.Dead ) - -end - --- todo: need to fix this global function - ---- @param Wrapper.Controllable#CONTROLLABLE AIControllable -function _NewEngageCapRoute( AIControllable ) - - AIControllable:T( "NewEngageRoute" ) - local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_CAP_ZONE - EngageZone:__Engage( 1 ) -end - ---- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAP_ZONE:onbeforeEngage( Controllable, From, Event, To ) - - if self.Accomplished == true then - return false - end -end - ---- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To ) - - if From ~= "Engaging" then - - local Engage = false - - for DetectedUnit, Detected in pairs( self.DetectedUnits ) do - - local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT - self:T( DetectedUnit ) - if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then - Engage = true - break - end - end - - if Engage == true then - self:E( 'Detected -> Engaging' ) - self:__Engage( 1 ) - end - end -end - - ---- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAP_ZONE:onafterAbort( Controllable, From, Event, To ) - Controllable:ClearTasks() - self:__Route( 1 ) -end - - - - ---- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To ) - - if Controllable:IsAlive() then - - local EngageRoute = {} - - --- Calculate the current route point. - local CurrentVec2 = self.Controllable:GetVec2() - - --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). - local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() - local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) - local ToEngageZoneSpeed = self.PatrolMaxSpeed - local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - ToEngageZoneSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = CurrentRoutePoint - - - --- Find a random 2D point in PatrolZone. - local ToTargetVec2 = self.PatrolZone:GetRandomVec2() - self:T2( ToTargetVec2 ) - - --- Define Speed and Altitude. - local ToTargetAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude ) - local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) - self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } ) - - --- Obtain a 3D @{Point} from the 2D point + altitude. - local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y ) - - --- Create a route point of type air. - local ToPatrolRoutePoint = ToTargetPointVec3:RoutePointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - ToTargetSpeed, - true - ) - - EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint - - Controllable:OptionROEOpenFire() - Controllable:OptionROTPassiveDefense() - - local AttackTasks = {} - - for DetectedUnit, Detected in pairs( self.DetectedUnits ) do - local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT - self:T( { DetectedUnit, DetectedUnit:IsAlive(), DetectedUnit:IsAir() } ) - if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then - if self.EngageZone then - if DetectedUnit:IsInZone( self.EngageZone ) then - self:E( {"Within Zone and Engaging ", DetectedUnit } ) - AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) - end - else - if self.EngageRange then - if DetectedUnit:GetPointVec3():Get2DDistance(Controllable:GetPointVec3() ) <= self.EngageRange then - self:E( {"Within Range and Engaging", DetectedUnit } ) - AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) - end - else - AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit ) - end - end - else - self.DetectedUnits[DetectedUnit] = nil - end - end - - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - self.Controllable:WayPointInitialize( EngageRoute ) - - - if #AttackTasks == 0 then - self:E("No targets found -> Going back to Patrolling") - self:__Abort( 1 ) - self:__Route( 1 ) - self:SetDetectionActivated() - else - EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) - - --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... - self.Controllable:SetState( self.Controllable, "EngageZone", self ) - - self.Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageCapRoute" ) - - self:SetDetectionDeactivated() - end - - --- NOW ROUTE THE GROUP! - self.Controllable:WayPointExecute( 1, 2 ) - - end -end - ---- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function AI_CAP_ZONE:onafterAccomplish( Controllable, From, Event, To ) - self.Accomplished = true - self:SetDetectionOff() -end - ---- @param #AI_CAP_ZONE self --- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @param Core.Event#EVENTDATA EventData -function AI_CAP_ZONE:onafterDestroy( Controllable, From, Event, To, EventData ) - - if EventData.IniUnit then - self.DetectedUnits[EventData.IniUnit] = nil - end -end - ---- @param #AI_CAP_ZONE self --- @param Core.Event#EVENTDATA EventData -function AI_CAP_ZONE:OnEventDead( EventData ) - self:F( { "EventDead", EventData } ) - - if EventData.IniDCSUnit then - if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then - self:__Destroy( 1, EventData ) - end - end -end ----Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Ground** -- --- **Management of logical cargo objects, that can be transported from and to transportation carriers.** --- --- ![Banner Image](..\Presentations\AI_CARGO\CARGO.JPG) --- --- === --- --- Cargo can be of various forms, always are composed out of ONE object ( one unit or one static or one slingload crate ): --- --- * AI_CARGO_UNIT, represented by a @{Unit} in a @{Group}: Cargo can be represented by a Unit in a Group. Destruction of the Unit will mean that the cargo is lost. --- * CARGO_STATIC, represented by a @{Static}: Cargo can be represented by a Static. Destruction of the Static will mean that the cargo is lost. --- * AI_CARGO_PACKAGE, contained in a @{Unit} of a @{Group}: Cargo can be contained within a Unit of a Group. The cargo can be **delivered** by the @{Unit}. If the Unit is destroyed, the cargo will be destroyed also. --- * AI_CARGO_PACKAGE, Contained in a @{Static}: Cargo can be contained within a Static. The cargo can be **collected** from the @Static. If the @{Static} is destroyed, the cargo will be destroyed. --- * CARGO_SLINGLOAD, represented by a @{Cargo} that is transportable: Cargo can be represented by a Cargo object that is transportable. Destruction of the Cargo will mean that the cargo is lost. --- --- * AI_CARGO_GROUPED, represented by a Group of CARGO_UNITs. --- --- # 1) @{#AI_CARGO} class, extends @{Fsm#FSM_PROCESS} --- --- The @{#AI_CARGO} class defines the core functions that defines a cargo object within MOOSE. --- A cargo is a logical object defined that is available for transport, and has a life status within a simulation. --- --- The AI_CARGO is a state machine: it manages the different events and states of the cargo. --- All derived classes from AI_CARGO follow the same state machine, expose the same cargo event functions, and provide the same cargo states. --- --- ## 1.2.1) AI_CARGO Events: --- --- * @{#AI_CARGO.Board}( ToCarrier ): Boards the cargo to a carrier. --- * @{#AI_CARGO.Load}( ToCarrier ): Loads the cargo into a carrier, regardless of its position. --- * @{#AI_CARGO.UnBoard}( ToPointVec2 ): UnBoard the cargo from a carrier. This will trigger a movement of the cargo to the option ToPointVec2. --- * @{#AI_CARGO.UnLoad}( ToPointVec2 ): UnLoads the cargo from a carrier. --- * @{#AI_CARGO.Dead}( Controllable ): The cargo is dead. The cargo process will be ended. --- --- ## 1.2.2) AI_CARGO States: --- --- * **UnLoaded**: The cargo is unloaded from a carrier. --- * **Boarding**: The cargo is currently boarding (= running) into a carrier. --- * **Loaded**: The cargo is loaded into a carrier. --- * **UnBoarding**: The cargo is currently unboarding (=running) from a carrier. --- * **Dead**: The cargo is dead ... --- * **End**: The process has come to an end. --- --- ## 1.2.3) AI_CARGO state transition methods: --- --- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. --- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Leaving** the state. --- The state transition method needs to start with the name **OnLeave + the name of the state**. --- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, --- but then you'll need to specify your own logic using the AIControllable! --- --- * **Entering** the state. --- The state transition method needs to start with the name **OnEnter + the name of the state**. --- These state transition methods need to provide a return value, which is specified at the function description. --- --- # 2) #AI_CARGO_UNIT class --- --- The AI_CARGO_UNIT class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. --- Use the event functions as described above to Load, UnLoad, Board, UnBoard the AI_CARGO_UNIT objects to and from carriers. --- --- # 5) #AI_CARGO_GROUPED class --- --- The AI_CARGO_GROUPED class defines a cargo that is represented by a group of UNIT objects within the simulator, and can be transported by a carrier. --- Use the event functions as described above to Load, UnLoad, Board, UnBoard the AI_CARGO_UNIT objects to and from carriers. --- --- This module is still under construction, but is described above works already, and will keep working ... --- --- @module Cargo - --- Events - --- Board - ---- Boards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo to the Carrier. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#AI_CARGO] Board --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - ---- Boards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo to the Carrier. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#AI_CARGO] __Board --- @param #AI_CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - - --- UnBoard - ---- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. --- The cargo must be in the **Loaded** state. --- @function [parent=#AI_CARGO] UnBoard --- @param #AI_CARGO self --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. - ---- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. --- The cargo must be in the **Loaded** state. --- @function [parent=#AI_CARGO] __UnBoard --- @param #AI_CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. - - --- Load - ---- Loads the cargo to a Carrier. The event will load the cargo into the Carrier regardless of its position. There will be no movement simulated of the cargo loading. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#AI_CARGO] Load --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - ---- Loads the cargo to a Carrier. The event will load the cargo into the Carrier regardless of its position. There will be no movement simulated of the cargo loading. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#AI_CARGO] __Load --- @param #AI_CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - - --- UnLoad - ---- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. --- The cargo must be in the **Loaded** state. --- @function [parent=#AI_CARGO] UnLoad --- @param #AI_CARGO self --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. - ---- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. --- The cargo must be in the **Loaded** state. --- @function [parent=#AI_CARGO] __UnLoad --- @param #AI_CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. - --- State Transition Functions - --- UnLoaded - ---- @function [parent=#AI_CARGO] OnLeaveUnLoaded --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#AI_CARGO] OnEnterUnLoaded --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - --- Loaded - ---- @function [parent=#AI_CARGO] OnLeaveLoaded --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#AI_CARGO] OnEnterLoaded --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - --- Boarding - ---- @function [parent=#AI_CARGO] OnLeaveBoarding --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#AI_CARGO] OnEnterBoarding --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - --- UnBoarding - ---- @function [parent=#AI_CARGO] OnLeaveUnBoarding --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#AI_CARGO] OnEnterUnBoarding --- @param #AI_CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - - --- TODO: Find all Carrier objects and make the type of the Carriers Wrapper.Unit#UNIT in the documentation. - -CARGOS = {} - -do -- AI_CARGO - - --- @type AI_CARGO - -- @extends Core.Fsm#FSM_PROCESS - -- @field #string Type A string defining the type of the cargo. eg. Engineers, Equipment, Screwdrivers. - -- @field #string Name A string defining the name of the cargo. The name is the unique identifier of the cargo. - -- @field #number Weight A number defining the weight of the cargo. The weight is expressed in kg. - -- @field #number ReportRadius (optional) A number defining the radius in meters when the cargo is signalling or reporting to a Carrier. - -- @field #number NearRadius (optional) A number defining the radius in meters when the cargo is near to a Carrier, so that it can be loaded. - -- @field Wrapper.Controllable#CONTROLLABLE CargoObject The alive DCS object representing the cargo. This value can be nil, meaning, that the cargo is not represented anywhere... - -- @field Wrapper.Controllable#CONTROLLABLE CargoCarrier The alive DCS object carrying the cargo. This value can be nil, meaning, that the cargo is not contained anywhere... - -- @field #boolean Slingloadable This flag defines if the cargo can be slingloaded. - -- @field #boolean Moveable This flag defines if the cargo is moveable. - -- @field #boolean Representable This flag defines if the cargo can be represented by a DCS Unit. - -- @field #boolean Containable This flag defines if the cargo can be contained within a DCS Unit. - AI_CARGO = { - ClassName = "AI_CARGO", - Type = nil, - Name = nil, - Weight = nil, - CargoObject = nil, - CargoCarrier = nil, - Representable = false, - Slingloadable = false, - Moveable = false, - Containable = false, - } - ---- @type AI_CARGO.CargoObjects --- @map < #string, Wrapper.Positionable#POSITIONABLE > The alive POSITIONABLE objects representing the the cargo. - - ---- AI_CARGO Constructor. This class is an abstract class and should not be instantiated. --- @param #AI_CARGO self --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO -function AI_CARGO:New( Type, Name, Weight, ReportRadius, NearRadius ) - - local self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM_CONTROLLABLE - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - self:SetStartState( "UnLoaded" ) - self:AddTransition( "UnLoaded", "Board", "Boarding" ) - self:AddTransition( "Boarding", "Boarding", "Boarding" ) - self:AddTransition( "Boarding", "Load", "Loaded" ) - self:AddTransition( "UnLoaded", "Load", "Loaded" ) - self:AddTransition( "Loaded", "UnBoard", "UnBoarding" ) - self:AddTransition( "UnBoarding", "UnBoarding", "UnBoarding" ) - self:AddTransition( "UnBoarding", "UnLoad", "UnLoaded" ) - self:AddTransition( "Loaded", "UnLoad", "UnLoaded" ) - - - self.Type = Type - self.Name = Name - self.Weight = Weight - self.ReportRadius = ReportRadius - self.NearRadius = NearRadius - self.CargoObject = nil - self.CargoCarrier = nil - self.Representable = false - self.Slingloadable = false - self.Moveable = false - self.Containable = false - - - self.CargoScheduler = SCHEDULER:New() - - CARGOS[self.Name] = self - - return self -end - - ---- Template method to spawn a new representation of the AI_CARGO in the simulator. --- @param #AI_CARGO self --- @return #AI_CARGO -function AI_CARGO:Spawn( PointVec2 ) - self:F() - -end - - ---- Check if CargoCarrier is near the Cargo to be Loaded. --- @param #AI_CARGO self --- @param Core.Point#POINT_VEC2 PointVec2 --- @return #boolean -function AI_CARGO:IsNear( PointVec2 ) - self:F( { PointVec2 } ) - - local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - self:T( Distance ) - - if Distance <= self.NearRadius then - return true - else - return false - end -end - -end - -do -- AI_CARGO_REPRESENTABLE - - --- @type AI_CARGO_REPRESENTABLE - -- @extends #AI_CARGO - AI_CARGO_REPRESENTABLE = { - ClassName = "AI_CARGO_REPRESENTABLE" - } - ---- AI_CARGO_REPRESENTABLE Constructor. --- @param #AI_CARGO_REPRESENTABLE self --- @param Wrapper.Controllable#Controllable CargoObject --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_REPRESENTABLE -function AI_CARGO_REPRESENTABLE:New( CargoObject, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO:New( Type, Name, Weight, ReportRadius, NearRadius ) ) -- #AI_CARGO - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - return self -end - ---- Route a cargo unit to a PointVec2. --- @param #AI_CARGO_REPRESENTABLE self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #number Speed --- @return #AI_CARGO_REPRESENTABLE -function AI_CARGO_REPRESENTABLE:RouteTo( ToPointVec2, Speed ) - self:F2( ToPointVec2 ) - - local Points = {} - - local PointStartVec2 = self.CargoObject:GetPointVec2() - - Points[#Points+1] = PointStartVec2:RoutePointGround( Speed ) - Points[#Points+1] = ToPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 2 ) - return self -end - -end -- AI_CARGO - -do -- AI_CARGO_UNIT - - --- @type AI_CARGO_UNIT - -- @extends #AI_CARGO_REPRESENTABLE - AI_CARGO_UNIT = { - ClassName = "AI_CARGO_UNIT" - } - ---- AI_CARGO_UNIT Constructor. --- @param #AI_CARGO_UNIT self --- @param Wrapper.Unit#UNIT CargoUnit --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_UNIT -function AI_CARGO_UNIT:New( CargoUnit, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, Weight, ReportRadius, NearRadius ) ) -- #AI_CARGO_UNIT - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - self:T( CargoUnit ) - self.CargoObject = CargoUnit - - self:T( self.ClassName ) - - return self -end - ---- Enter UnBoarding State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Point#POINT_VEC2 ToPointVec2 -function AI_CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2 ) - self:F() - - local Angle = 180 - local Speed = 10 - local DeployDistance = 5 - local RouteDistance = 60 - - if From == "Loaded" then - - local CargoCarrierPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, CargoDeployHeading ) - local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading ) - - -- if there is no ToPointVec2 given, then use the CargoRoutePointVec2 - ToPointVec2 = ToPointVec2 or CargoRoutePointVec2 - - local FromPointVec2 = CargoCarrierPointVec2 - - -- Respawn the group... - if self.CargoObject then - self.CargoObject:ReSpawn( CargoDeployPointVec2:GetVec3(), CargoDeployHeading ) - self.CargoCarrier = nil - - local Points = {} - Points[#Points+1] = FromPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = ToPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 1 ) - - self:__UnBoarding( 1, ToPointVec2 ) - end - end - -end - ---- Leave UnBoarding State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Point#POINT_VEC2 ToPointVec2 -function AI_CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "UnBoarding" then - if self:IsNear( ToPointVec2 ) then - return true - else - self:__UnBoarding( 1, ToPointVec2 ) - end - return false - end - -end - ---- UnBoard Event. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Point#POINT_VEC2 ToPointVec2 -function AI_CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - self.CargoInAir = self.CargoObject:InAir() - - self:T( self.CargoInAir ) - - -- Only unboard the cargo when the carrier is not in the air. - -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). - if not self.CargoInAir then - - end - - self:__UnLoad( 1, ToPointVec2 ) - -end - - - ---- Enter UnLoaded State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Point#POINT_VEC2 -function AI_CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "Loaded" then - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = StartPointVec2:Translate( Distance, CargoDeployHeading ) - - ToPointVec2 = ToPointVec2 or POINT_VEC2:New( CargoDeployPointVec2:GetX(), CargoDeployPointVec2:GetY() ) - - -- Respawn the group... - if self.CargoObject then - self.CargoObject:ReSpawn( ToPointVec2:GetVec3(), 0 ) - self.CargoCarrier = nil - end - - end - - if self.OnUnLoadedCallBack then - self.OnUnLoadedCallBack( self, unpack( self.OnUnLoadedParameters ) ) - self.OnUnLoadedCallBack = nil - end - -end - - - ---- Enter Boarding State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - local Speed = 10 - local Angle = 180 - local Distance = 5 - - if From == "UnLoaded" then - local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) - - local Points = {} - - local PointStartVec2 = self.CargoObject:GetPointVec2() - - Points[#Points+1] = PointStartVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 2 ) - end - -end - ---- Leave Boarding State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_UNIT:onleaveBoarding( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - if self:IsNear( CargoCarrier:GetPointVec2() ) then - self:__Load( 1, CargoCarrier ) - return true - else - self:__Boarding( 1, CargoCarrier ) - end - return false -end - ---- Loaded State. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier ) - self:F() - - self.CargoCarrier = CargoCarrier - - -- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects). - if self.CargoObject then - self:T("Destroying") - self.CargoObject:Destroy() - end -end - - ---- Board Event. --- @param #AI_CARGO_UNIT self --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier ) - self:F() - - self.CargoInAir = self.CargoObject:InAir() - - self:T( self.CargoInAir ) - - -- Only move the group to the carrier when the cargo is not in the air - -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). - if not self.CargoInAir then - self:Load( CargoCarrier ) - end - -end - -end - -do -- AI_CARGO_PACKAGE - - --- @type AI_CARGO_PACKAGE - -- @extends #AI_CARGO_REPRESENTABLE - AI_CARGO_PACKAGE = { - ClassName = "AI_CARGO_PACKAGE" - } - ---- AI_CARGO_PACKAGE Constructor. --- @param #AI_CARGO_PACKAGE self --- @param Wrapper.Unit#UNIT CargoCarrier The UNIT carrying the package. --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_PACKAGE -function AI_CARGO_PACKAGE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO_REPRESENTABLE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) ) -- #AI_CARGO_PACKAGE - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - self:T( CargoCarrier ) - self.CargoCarrier = CargoCarrier - - return self -end - ---- Board Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #number Speed --- @param #number BoardDistance --- @param #number Angle -function AI_CARGO_PACKAGE:onafterOnBoard( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - self:F() - - self.CargoInAir = self.CargoCarrier:InAir() - - self:T( self.CargoInAir ) - - -- Only move the CargoCarrier to the New CargoCarrier when the New CargoCarrier is not in the air. - if not self.CargoInAir then - - local Points = {} - - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - self:T( { CargoCarrierHeading, CargoDeployHeading } ) - local CargoDeployPointVec2 = CargoCarrier:GetPointVec2():Translate( BoardDistance, CargoDeployHeading ) - - Points[#Points+1] = StartPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoCarrier:TaskRoute( Points ) - self.CargoCarrier:SetTask( TaskRoute, 1 ) - end - - self:Boarded( CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - -end - ---- Check if CargoCarrier is near the Cargo to be Loaded. --- @param #AI_CARGO_PACKAGE self --- @param Wrapper.Unit#UNIT CargoCarrier --- @return #boolean -function AI_CARGO_PACKAGE:IsNear( CargoCarrier ) - self:F() - - local CargoCarrierPoint = CargoCarrier:GetPointVec2() - - local Distance = CargoCarrierPoint:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) - self:T( Distance ) - - if Distance <= self.NearRadius then - return true - else - return false - end -end - ---- Boarded Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_PACKAGE:onafterOnBoarded( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - self:F() - - if self:IsNear( CargoCarrier ) then - self:__Load( 1, CargoCarrier, Speed, LoadDistance, Angle ) - else - self:__Boarded( 1, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - end -end - ---- UnBoard Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param #number Speed --- @param #number UnLoadDistance --- @param #number UnBoardDistance --- @param #number Radius --- @param #number Angle -function AI_CARGO_PACKAGE:onafterUnBoard( From, Event, To, CargoCarrier, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle ) - self:F() - - self.CargoInAir = self.CargoCarrier:InAir() - - self:T( self.CargoInAir ) - - -- Only unboard the cargo when the carrier is not in the air. - -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). - if not self.CargoInAir then - - self:_Next( self.FsmP.UnLoad, UnLoadDistance, Angle ) - - local Points = {} - - local StartPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - self:T( { CargoCarrierHeading, CargoDeployHeading } ) - local CargoDeployPointVec2 = StartPointVec2:Translate( UnBoardDistance, CargoDeployHeading ) - - Points[#Points+1] = StartPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = CargoCarrier:TaskRoute( Points ) - CargoCarrier:SetTask( TaskRoute, 1 ) - end - - self:__UnBoarded( 1 , CargoCarrier, Speed ) - -end - ---- UnBoarded Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_PACKAGE:onafterUnBoarded( From, Event, To, CargoCarrier, Speed ) - self:F() - - if self:IsNear( CargoCarrier ) then - self:__UnLoad( 1, CargoCarrier, Speed ) - else - self:__UnBoarded( 1, CargoCarrier, Speed ) - end -end - ---- Load Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #number Speed --- @param #number LoadDistance --- @param #number Angle -function AI_CARGO_PACKAGE:onafterLoad( From, Event, To, CargoCarrier, Speed, LoadDistance, Angle ) - self:F() - - self.CargoCarrier = CargoCarrier - - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = StartPointVec2:Translate( LoadDistance, CargoDeployHeading ) - - local Points = {} - Points[#Points+1] = StartPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoCarrier:TaskRoute( Points ) - self.CargoCarrier:SetTask( TaskRoute, 1 ) - -end - ---- UnLoad Event. --- @param #AI_CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param #number Distance --- @param #number Angle -function AI_CARGO_PACKAGE:onafterUnLoad( From, Event, To, CargoCarrier, Speed, Distance, Angle ) - self:F() - - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = StartPointVec2:Translate( Distance, CargoDeployHeading ) - - self.CargoCarrier = CargoCarrier - - local Points = {} - Points[#Points+1] = StartPointVec2:RoutePointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:RoutePointGround( Speed ) - - local TaskRoute = self.CargoCarrier:TaskRoute( Points ) - self.CargoCarrier:SetTask( TaskRoute, 1 ) - -end - - -end - -do -- AI_CARGO_GROUP - - --- @type AI_CARGO_GROUP - -- @extends AI.AI_Cargo#AI_CARGO - -- @field Set#SET_BASE CargoSet A set of cargo objects. - -- @field #string Name A string defining the name of the cargo group. The name is the unique identifier of the cargo. - AI_CARGO_GROUP = { - ClassName = "AI_CARGO_GROUP", - } - ---- AI_CARGO_GROUP constructor. --- @param #AI_CARGO_GROUP self --- @param Core.Set#Set_BASE CargoSet --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_GROUP -function AI_CARGO_GROUP:New( CargoSet, Type, Name, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO:New( Type, Name, 0, ReportRadius, NearRadius ) ) -- #AI_CARGO_GROUP - self:F( { Type, Name, ReportRadius, NearRadius } ) - - self.CargoSet = CargoSet - - - return self -end - -end -- AI_CARGO_GROUP - -do -- AI_CARGO_GROUPED - - --- @type AI_CARGO_GROUPED - -- @extends AI.AI_Cargo#AI_CARGO_GROUP - AI_CARGO_GROUPED = { - ClassName = "AI_CARGO_GROUPED", - } - ---- AI_CARGO_GROUPED constructor. --- @param #AI_CARGO_GROUPED self --- @param Core.Set#Set_BASE CargoSet --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #AI_CARGO_GROUPED -function AI_CARGO_GROUPED:New( CargoSet, Type, Name, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, AI_CARGO_GROUP:New( CargoSet, Type, Name, ReportRadius, NearRadius ) ) -- #AI_CARGO_GROUPED - self:F( { Type, Name, ReportRadius, NearRadius } ) - - return self -end - ---- Enter Boarding State. --- @param #AI_CARGO_GROUPED self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onenterBoarding( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - if From == "UnLoaded" then - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - Cargo:__Board( 1, CargoCarrier ) - end - ) - - self:__Boarding( 1, CargoCarrier ) - end - -end - ---- Enter Loaded State. --- @param #AI_CARGO_GROUPED self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onenterLoaded( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - if From == "UnLoaded" then - -- For each Cargo object within the AI_CARGO_GROUPED, load each cargo to the CargoCarrier. - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - Cargo:Load( CargoCarrier ) - end - end -end - ---- Leave Boarding State. --- @param #AI_CARGO_GROUPED self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onleaveBoarding( From, Event, To, CargoCarrier ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - local Boarded = true - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( Cargo.current ) - if not Cargo:is( "Loaded" ) then - Boarded = false - end - end - - if not Boarded then - self:__Boarding( 1, CargoCarrier ) - else - self:__Load( 1, CargoCarrier ) - end - return Boarded -end - ---- Enter UnBoarding State. --- @param #AI_CARGO_GROUPED self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onenterUnBoarding( From, Event, To, ToPointVec2 ) - self:F() - - local Timer = 1 - - if From == "Loaded" then - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - Cargo:__UnBoard( Timer, ToPointVec2 ) - Timer = Timer + 10 - end - ) - - self:__UnBoarding( 1, ToPointVec2 ) - end - -end - ---- Leave UnBoarding State. --- @param #AI_CARGO_GROUPED self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onleaveUnBoarding( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "UnBoarding" then - local UnBoarded = true - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( Cargo.current ) - if not Cargo:is( "UnLoaded" ) then - UnBoarded = false - end - end - - if UnBoarded then - return true - else - self:__UnBoarding( 1, ToPointVec2 ) - end - - return false - end - -end - ---- UnBoard Event. --- @param #AI_CARGO_GROUPED self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onafterUnBoarding( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - self:__UnLoad( 1, ToPointVec2 ) -end - - - ---- Enter UnLoaded State. --- @param #AI_CARGO_GROUPED self --- @param Core.Point#POINT_VEC2 --- @param #string Event --- @param #string From --- @param #string To -function AI_CARGO_GROUPED:onenterUnLoaded( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - if From == "Loaded" then - - -- For each Cargo object within the AI_CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - Cargo:UnLoad( ToPointVec2 ) - end - ) - - end - -end - -end -- AI_CARGO_GROUPED - - - ---- (SP) (MP) (FSM) Accept or reject process for player (task) assignments. --- --- === --- --- # @{#ACT_ASSIGN} FSM template class, extends @{Fsm#FSM_PROCESS} --- --- ## ACT_ASSIGN state machine: --- --- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. --- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. --- Each derived class follows exactly the same process, using the same events and following the same state transitions, --- but will have **different implementation behaviour** upon each event or state transition. --- --- ### ACT_ASSIGN **Events**: --- --- These are the events defined in this class: --- --- * **Start**: Start the tasking acceptance process. --- * **Assign**: Assign the task. --- * **Reject**: Reject the task.. --- --- ### ACT_ASSIGN **Event methods**: --- --- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. --- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine: --- --- * **Immediate**: The event method has exactly the name of the event. --- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. --- --- ### ACT_ASSIGN **States**: --- --- * **UnAssigned**: The player has not accepted the task. --- * **Assigned (*)**: The player has accepted the task. --- * **Rejected (*)**: The player has not accepted the task. --- * **Waiting**: The process is awaiting player feedback. --- * **Failed (*)**: The process has failed. --- --- (*) End states of the process. --- --- ### ACT_ASSIGN state transition methods: --- --- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. --- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Before** the state transition. --- The state transition method needs to start with the name **OnBefore + the name of the state**. --- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, --- but then you'll need to specify your own logic using the AIControllable! --- --- * **After** the state transition. --- The state transition method needs to start with the name **OnAfter + the name of the state**. --- These state transition methods need to provide a return value, which is specified at the function description. --- --- === --- --- # 1) @{#ACT_ASSIGN_ACCEPT} class, extends @{Fsm.Assign#ACT_ASSIGN} --- --- The ACT_ASSIGN_ACCEPT class accepts by default a task for a player. No player intervention is allowed to reject the task. --- --- ## 1.1) ACT_ASSIGN_ACCEPT constructor: --- --- * @{#ACT_ASSIGN_ACCEPT.New}(): Creates a new ACT_ASSIGN_ACCEPT object. --- --- === --- --- # 2) @{#ACT_ASSIGN_MENU_ACCEPT} class, extends @{Fsm.Assign#ACT_ASSIGN} --- --- The ACT_ASSIGN_MENU_ACCEPT class accepts a task when the player accepts the task through an added menu option. --- This assignment type is useful to conditionally allow the player to choose whether or not he would accept the task. --- The assignment type also allows to reject the task. --- --- ## 2.1) ACT_ASSIGN_MENU_ACCEPT constructor: --- ----------------------------------------- --- --- * @{#ACT_ASSIGN_MENU_ACCEPT.New}(): Creates a new ACT_ASSIGN_MENU_ACCEPT object. --- --- === --- --- @module Assign - - -do -- ACT_ASSIGN - - --- ACT_ASSIGN class - -- @type ACT_ASSIGN - -- @field Tasking.Task#TASK Task - -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone - -- @extends Core.Fsm#FSM_PROCESS - ACT_ASSIGN = { - ClassName = "ACT_ASSIGN", - } - - - --- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted. - -- @param #ACT_ASSIGN self - -- @return #ACT_ASSIGN The task acceptance process. - function ACT_ASSIGN:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ASSIGN" ) ) -- Core.Fsm#FSM_PROCESS - - self:AddTransition( "UnAssigned", "Start", "Waiting" ) - self:AddTransition( "Waiting", "Assign", "Assigned" ) - self:AddTransition( "Waiting", "Reject", "Rejected" ) - self:AddTransition( "*", "Fail", "Failed" ) - - self:AddEndState( "Assigned" ) - self:AddEndState( "Rejected" ) - self:AddEndState( "Failed" ) - - self:SetStartState( "UnAssigned" ) - - return self - end - -end -- ACT_ASSIGN - - - -do -- ACT_ASSIGN_ACCEPT - - --- ACT_ASSIGN_ACCEPT class - -- @type ACT_ASSIGN_ACCEPT - -- @field Tasking.Task#TASK Task - -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone - -- @extends #ACT_ASSIGN - ACT_ASSIGN_ACCEPT = { - ClassName = "ACT_ASSIGN_ACCEPT", - } - - - --- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted. - -- @param #ACT_ASSIGN_ACCEPT self - -- @param #string TaskBriefing - function ACT_ASSIGN_ACCEPT:New( TaskBriefing ) - - local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_ACCEPT - - self.TaskBriefing = TaskBriefing - - return self - end - - function ACT_ASSIGN_ACCEPT:Init( FsmAssign ) - - self.TaskBriefing = FsmAssign.TaskBriefing - end - - --- StateMachine callback function - -- @param #ACT_ASSIGN_ACCEPT self - -- @param Wrapper.Unit#UNIT ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, From, Event, To ) - self:E( { ProcessUnit, From, Event, To } ) - - self:__Assign( 1 ) - end - - --- StateMachine callback function - -- @param #ACT_ASSIGN_ACCEPT self - -- @param Wrapper.Unit#UNIT ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, From, Event, To ) - env.info( "in here" ) - self:E( { ProcessUnit, From, Event, To } ) - - local ProcessGroup = ProcessUnit:GetGroup() - - self:Message( "You are assigned to the task " .. self.Task:GetName() ) - - self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) - end - -end -- ACT_ASSIGN_ACCEPT - - -do -- ACT_ASSIGN_MENU_ACCEPT - - --- ACT_ASSIGN_MENU_ACCEPT class - -- @type ACT_ASSIGN_MENU_ACCEPT - -- @field Tasking.Task#TASK Task - -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE TargetZone - -- @extends #ACT_ASSIGN - ACT_ASSIGN_MENU_ACCEPT = { - ClassName = "ACT_ASSIGN_MENU_ACCEPT", - } - - --- Init. - -- @param #ACT_ASSIGN_MENU_ACCEPT self - -- @param #string TaskName - -- @param #string TaskBriefing - -- @return #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:New( TaskName, TaskBriefing ) - - -- Inherits from BASE - local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_MENU_ACCEPT - - self.TaskName = TaskName - self.TaskBriefing = TaskBriefing - - return self - end - - function ACT_ASSIGN_MENU_ACCEPT:Init( FsmAssign ) - - self.TaskName = FsmAssign.TaskName - self.TaskBriefing = FsmAssign.TaskBriefing - end - - - --- Creates a new task assignment state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. - -- @param #ACT_ASSIGN_MENU_ACCEPT self - -- @param #string TaskName - -- @param #string TaskBriefing - -- @return #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:Init( TaskName, TaskBriefing ) - - self.TaskBriefing = TaskBriefing - self.TaskName = TaskName - - return self - end - - --- StateMachine callback function - -- @param #ACT_ASSIGN_MENU_ACCEPT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, From, Event, To ) - self:E( { ProcessUnit, From, Event, To } ) - - self:Message( "Access the radio menu to accept the task. You have 30 seconds or the assignment will be cancelled." ) - - local ProcessGroup = ProcessUnit:GetGroup() - - self.Menu = MENU_GROUP:New( ProcessGroup, "Task " .. self.TaskName .. " acceptance" ) - self.MenuAcceptTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Accept task " .. self.TaskName, self.Menu, self.MenuAssign, self ) - self.MenuRejectTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Reject task " .. self.TaskName, self.Menu, self.MenuReject, self ) - end - - --- Menu function. - -- @param #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:MenuAssign() - self:E( ) - - self:__Assign( 1 ) - end - - --- Menu function. - -- @param #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:MenuReject() - self:E( ) - - self:__Reject( 1 ) - end - - --- StateMachine callback function - -- @param #ACT_ASSIGN_MENU_ACCEPT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, From, Event, To ) - self:E( { ProcessUnit.UnitNameFrom, Event, To } ) - - self.Menu:Remove() - end - - --- StateMachine callback function - -- @param #ACT_ASSIGN_MENU_ACCEPT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, From, Event, To ) - self:E( { ProcessUnit.UnitName, From, Event, To } ) - - self.Menu:Remove() - --TODO: need to resolve this problem ... it has to do with the events ... - --self.Task:UnAssignFromUnit( ProcessUnit )needs to become a callback funtion call upon the event - ProcessUnit:Destroy() - end - -end -- ACT_ASSIGN_MENU_ACCEPT ---- (SP) (MP) (FSM) Route AI or players through waypoints or to zones. --- --- === --- --- # @{#ACT_ROUTE} FSM class, extends @{Fsm#FSM_PROCESS} --- --- ## ACT_ROUTE state machine: --- --- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. --- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. --- Each derived class follows exactly the same process, using the same events and following the same state transitions, --- but will have **different implementation behaviour** upon each event or state transition. --- --- ### ACT_ROUTE **Events**: --- --- These are the events defined in this class: --- --- * **Start**: The process is started. The process will go into the Report state. --- * **Report**: The process is reporting to the player the route to be followed. --- * **Route**: The process is routing the controllable. --- * **Pause**: The process is pausing the route of the controllable. --- * **Arrive**: The controllable has arrived at a route point. --- * **More**: There are more route points that need to be followed. The process will go back into the Report state. --- * **NoMore**: There are no more route points that need to be followed. The process will go into the Success state. --- --- ### ACT_ROUTE **Event methods**: --- --- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. --- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine: --- --- * **Immediate**: The event method has exactly the name of the event. --- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. --- --- ### ACT_ROUTE **States**: --- --- * **None**: The controllable did not receive route commands. --- * **Arrived (*)**: The controllable has arrived at a route point. --- * **Aborted (*)**: The controllable has aborted the route path. --- * **Routing**: The controllable is understay to the route point. --- * **Pausing**: The process is pausing the routing. AI air will go into hover, AI ground will stop moving. Players can fly around. --- * **Success (*)**: All route points were reached. --- * **Failed (*)**: The process has failed. --- --- (*) End states of the process. --- --- ### ACT_ROUTE state transition methods: --- --- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. --- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Before** the state transition. --- The state transition method needs to start with the name **OnBefore + the name of the state**. --- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, --- but then you'll need to specify your own logic using the AIControllable! --- --- * **After** the state transition. --- The state transition method needs to start with the name **OnAfter + the name of the state**. --- These state transition methods need to provide a return value, which is specified at the function description. --- --- === --- --- # 1) @{#ACT_ROUTE_ZONE} class, extends @{Fsm.Route#ACT_ROUTE} --- --- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Controllable} player @{Unit} to a @{Zone}. --- The player receives on perioding times messages with the coordinates of the route to follow. --- Upon arrival at the zone, a confirmation of arrival is sent, and the process will be ended. --- --- # 1.1) ACT_ROUTE_ZONE constructor: --- --- * @{#ACT_ROUTE_ZONE.New}(): Creates a new ACT_ROUTE_ZONE object. --- --- === --- --- @module Route - - -do -- ACT_ROUTE - - --- ACT_ROUTE class - -- @type ACT_ROUTE - -- @field Tasking.Task#TASK TASK - -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE Zone - -- @extends Core.Fsm#FSM_PROCESS - ACT_ROUTE = { - ClassName = "ACT_ROUTE", - } - - - --- Creates a new routing state machine. The process will route a CLIENT to a ZONE until the CLIENT is within that ZONE. - -- @param #ACT_ROUTE self - -- @return #ACT_ROUTE self - function ACT_ROUTE:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ROUTE" ) ) -- Core.Fsm#FSM_PROCESS - - self:AddTransition( "None", "Start", "Routing" ) - self:AddTransition( "*", "Report", "Reporting" ) - self:AddTransition( "*", "Route", "Routing" ) - self:AddTransition( "Routing", "Pause", "Pausing" ) - self:AddTransition( "*", "Abort", "Aborted" ) - self:AddTransition( "Routing", "Arrive", "Arrived" ) - self:AddTransition( "Arrived", "Success", "Success" ) - self:AddTransition( "*", "Fail", "Failed" ) - self:AddTransition( "", "", "" ) - self:AddTransition( "", "", "" ) - - self:AddEndState( "Arrived" ) - self:AddEndState( "Failed" ) - - self:SetStartState( "None" ) - - return self - end - - --- Task Events - - --- StateMachine callback function - -- @param #ACT_ROUTE self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ROUTE:onafterStart( ProcessUnit, From, Event, To ) - - - self:__Route( 1 ) - end - - --- Check if the controllable has arrived. - -- @param #ACT_ROUTE self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @return #boolean - function ACT_ROUTE:onfuncHasArrived( ProcessUnit ) - return false - end - - --- StateMachine callback function - -- @param #ACT_ROUTE self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ROUTE:onbeforeRoute( ProcessUnit, From, Event, To ) - self:F( { "BeforeRoute 1", self.DisplayCount, self.DisplayInterval } ) - - if ProcessUnit:IsAlive() then - self:F( "BeforeRoute 2" ) - local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic - if self.DisplayCount >= self.DisplayInterval then - self:T( { HasArrived = HasArrived } ) - if not HasArrived then - self:Report() - end - self.DisplayCount = 1 - else - self.DisplayCount = self.DisplayCount + 1 - end - - self:T( { DisplayCount = self.DisplayCount } ) - - if HasArrived then - self:__Arrive( 1 ) - else - self:__Route( 1 ) - end - - return HasArrived -- if false, then the event will not be executed... - end - - return false - - end - -end -- ACT_ROUTE - - -do -- ACT_ROUTE_POINT - - --- ACT_ROUTE_POINT class - -- @type ACT_ROUTE_POINT - -- @field Tasking.Task#TASK TASK - -- @extends #ACT_ROUTE - ACT_ROUTE_POINT = { - ClassName = "ACT_ROUTE_POINT", - } - - - --- Creates a new routing state machine. - -- The task will route a controllable to a PointVec2 until the controllable is within the Range. - -- @param #ACT_ROUTE_POINT self - -- @param Core.Point#POINT_VEC2 The PointVec2 to Target. - -- @param #number Range The Distance to Target. - -- @param Core.Zone#ZONE_BASE Zone - function ACT_ROUTE_POINT:New( PointVec2, Range ) - local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_POINT - - self.PointVec2 = PointVec2 - self.Range = Range or 0 - - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - - return self - end - - function ACT_ROUTE_POINT:Init( FsmRoute ) - - self.PointVec2 = FsmRoute.PointVec2 - self.Range = FsmRoute.Range or 0 - - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - end - - --- Set PointVec2 - -- @param #ACT_ROUTE_POINT self - -- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to. - function ACT_ROUTE_POINT:SetPointVec2( PointVec2 ) - self:F2( { PointVec2 } ) - self.PointVec2 = PointVec2 - end - - --- Get PointVec2 - -- @param #ACT_ROUTE_POINT self - -- @return Core.Point#POINT_VEC2 PointVec2 The PointVec2 to route to. - function ACT_ROUTE_POINT:GetPointVec2() - self:F2( { self.PointVec2 } ) - return self.PointVec2 - end - - --- Set Range around PointVec2 - -- @param #ACT_ROUTE_POINT self - -- @param #number Range The Range to consider the arrival. Default is 10000 meters. - function ACT_ROUTE_POINT:SetRange( Range ) - self:F2( { self.Range } ) - self.Range = Range or 10000 - end - - --- Get Range around PointVec2 - -- @param #ACT_ROUTE_POINT self - -- @return #number The Range to consider the arrival. Default is 10000 meters. - function ACT_ROUTE_POINT:GetRange() - return self.Range - end - - --- Method override to check if the controllable has arrived. - -- @param #ACT_ROUTE_POINT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @return #boolean - function ACT_ROUTE_POINT:onfuncHasArrived( ProcessUnit ) - - if ProcessUnit:IsAlive() then - local Distance = self.PointVec2:Get2DDistance( ProcessUnit:GetPointVec2() ) - - if Distance <= self.Range then - local RouteText = "You have arrived." - self:Message( RouteText ) - return true - end - end - - return false - end - - --- Task Events - - --- StateMachine callback function - -- @param #ACT_ROUTE_POINT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ROUTE_POINT:onenterReporting( ProcessUnit, From, Event, To ) - - local TaskUnitPointVec2 = ProcessUnit:GetPointVec2() - local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( self.PointVec2 ) .. " km." - self:Message( RouteText ) - end - -end -- ACT_ROUTE_POINT - - -do -- ACT_ROUTE_ZONE - - --- ACT_ROUTE_ZONE class - -- @type ACT_ROUTE_ZONE - -- @field Tasking.Task#TASK TASK - -- @field Wrapper.Unit#UNIT ProcessUnit - -- @field Core.Zone#ZONE_BASE Zone - -- @extends #ACT_ROUTE - ACT_ROUTE_ZONE = { - ClassName = "ACT_ROUTE_ZONE", - } - - - --- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE. - -- @param #ACT_ROUTE_ZONE self - -- @param Core.Zone#ZONE_BASE Zone - function ACT_ROUTE_ZONE:New( Zone ) - local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE - - self.Zone = Zone - - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - - return self - end - - function ACT_ROUTE_ZONE:Init( FsmRoute ) - - self.Zone = FsmRoute.Zone - - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - end - - --- Set Zone - -- @param #ACT_ROUTE_ZONE self - -- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to. - function ACT_ROUTE_ZONE:SetZone( Zone ) - self.Zone = Zone - end - - --- Get Zone - -- @param #ACT_ROUTE_ZONE self - -- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to. - function ACT_ROUTE_ZONE:GetZone() - return self.Zone - end - - --- Method override to check if the controllable has arrived. - -- @param #ACT_ROUTE self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @return #boolean - function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit ) - - if ProcessUnit:IsInZone( self.Zone ) then - local RouteText = "You have arrived within the zone." - self:Message( RouteText ) - end - - return ProcessUnit:IsInZone( self.Zone ) - end - - --- Task Events - - --- StateMachine callback function - -- @param #ACT_ROUTE_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ROUTE_ZONE:onenterReporting( ProcessUnit, From, Event, To ) - - local ZoneVec2 = self.Zone:GetVec2() - local ZonePointVec2 = POINT_VEC2:New( ZoneVec2.x, ZoneVec2.y ) - local TaskUnitVec2 = ProcessUnit:GetVec2() - local TaskUnitPointVec2 = POINT_VEC2:New( TaskUnitVec2.x, TaskUnitVec2.y ) - local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( ZonePointVec2 ) .. " km." - self:Message( RouteText ) - end - -end -- ACT_ROUTE_ZONE ---- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Unit}s. --- --- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG) --- --- === --- --- @module Account - - -do -- ACT_ACCOUNT - - --- # @{#ACT_ACCOUNT} FSM class, extends @{Fsm#FSM_PROCESS} - -- - -- ## ACT_ACCOUNT state machine: - -- - -- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. - -- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. - -- Each derived class follows exactly the same process, using the same events and following the same state transitions, - -- but will have **different implementation behaviour** upon each event or state transition. - -- - -- ### ACT_ACCOUNT States - -- - -- * **Asigned**: The player is assigned. - -- * **Waiting**: Waiting for an event. - -- * **Report**: Reporting. - -- * **Account**: Account for an event. - -- * **Accounted**: All events have been accounted for, end of the process. - -- * **Failed**: Failed the process. - -- - -- ### ACT_ACCOUNT Events - -- - -- * **Start**: Start the process. - -- * **Wait**: Wait for an event. - -- * **Report**: Report the status of the accounting. - -- * **Event**: An event happened, process the event. - -- * **More**: More targets. - -- * **NoMore (*)**: No more targets. - -- * **Fail (*)**: The action process has failed. - -- - -- (*) End states of the process. - -- - -- ### ACT_ACCOUNT state transition methods: - -- - -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. - -- There are 2 moments when state transition methods will be called by the state machine: - -- - -- * **Before** the state transition. - -- The state transition method needs to start with the name **OnBefore + the name of the state**. - -- If the state transition method returns false, then the processing of the state transition will not be done! - -- If you want to change the behaviour of the AIControllable at this event, return false, - -- but then you'll need to specify your own logic using the AIControllable! - -- - -- * **After** the state transition. - -- The state transition method needs to start with the name **OnAfter + the name of the state**. - -- These state transition methods need to provide a return value, which is specified at the function description. - -- - -- @type ACT_ACCOUNT - -- @field Set#SET_UNIT TargetSetUnit - -- @extends Core.Fsm#FSM_PROCESS - ACT_ACCOUNT = { - ClassName = "ACT_ACCOUNT", - TargetSetUnit = nil, - } - - --- Creates a new DESTROY process. - -- @param #ACT_ACCOUNT self - -- @return #ACT_ACCOUNT - function ACT_ACCOUNT:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM_PROCESS:New() ) -- Core.Fsm#FSM_PROCESS - - self:AddTransition( "Assigned", "Start", "Waiting") - self:AddTransition( "*", "Wait", "Waiting") - self:AddTransition( "*", "Report", "Report") - self:AddTransition( "*", "Event", "Account") - self:AddTransition( "Account", "More", "Wait") - self:AddTransition( "Account", "NoMore", "Accounted") - self:AddTransition( "*", "Fail", "Failed") - - self:AddEndState( "Accounted" ) - self:AddEndState( "Failed" ) - - self:SetStartState( "Assigned" ) - - return self - end - - --- Process Events - - --- StateMachine callback function - -- @param #ACT_ACCOUNT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ACCOUNT:onafterStart( ProcessUnit, From, Event, To ) - - self:HandleEvent( EVENTS.Dead, self.onfuncEventDead ) - - self:__Wait( 1 ) - end - - - --- StateMachine callback function - -- @param #ACT_ACCOUNT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ACCOUNT:onenterWaiting( ProcessUnit, From, Event, To ) - - if self.DisplayCount >= self.DisplayInterval then - self:Report() - self.DisplayCount = 1 - else - self.DisplayCount = self.DisplayCount + 1 - end - - return true -- Process always the event. - end - - --- StateMachine callback function - -- @param #ACT_ACCOUNT self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ACCOUNT:onafterEvent( ProcessUnit, From, Event, To, Event ) - - self:__NoMore( 1 ) - end - -end -- ACT_ACCOUNT - -do -- ACT_ACCOUNT_DEADS - - --- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Fsm.Account#ACT_ACCOUNT} - -- - -- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units. - -- The process is given a @{Set} of units that will be tracked upon successful destruction. - -- The process will end after each target has been successfully destroyed. - -- Each successful dead will trigger an Account state transition that can be scored, modified or administered. - -- - -- - -- ## ACT_ACCOUNT_DEADS constructor: - -- - -- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object. - -- - -- @type ACT_ACCOUNT_DEADS - -- @field Set#SET_UNIT TargetSetUnit - -- @extends #ACT_ACCOUNT - ACT_ACCOUNT_DEADS = { - ClassName = "ACT_ACCOUNT_DEADS", - TargetSetUnit = nil, - } - - - --- Creates a new DESTROY process. - -- @param #ACT_ACCOUNT_DEADS self - -- @param Set#SET_UNIT TargetSetUnit - -- @param #string TaskName - function ACT_ACCOUNT_DEADS:New( TargetSetUnit, TaskName ) - -- Inherits from BASE - local self = BASE:Inherit( self, ACT_ACCOUNT:New() ) -- #ACT_ACCOUNT_DEADS - - self.TargetSetUnit = TargetSetUnit - self.TaskName = TaskName - - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - self.DisplayCategory = "HQ" -- Targets is the default display category - - return self - end - - function ACT_ACCOUNT_DEADS:Init( FsmAccount ) - - self.TargetSetUnit = FsmAccount.TargetSetUnit - self.TaskName = FsmAccount.TaskName - end - - --- Process Events - - --- StateMachine callback function - -- @param #ACT_ACCOUNT_DEADS self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To ) - self:E( { ProcessUnit, From, Event, To } ) - - self:Message( "Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed." ) - end - - - --- StateMachine callback function - -- @param #ACT_ACCOUNT_DEADS self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, Task, From, Event, To, EventData ) - self:T( { ProcessUnit, EventData, From, Event, To } ) - - self:T({self.Controllable}) - - self.TargetSetUnit:Flush() - - self:T( { "Before sending Message", EventData.IniUnitName, self.TargetSetUnit:FindUnit( EventData.IniUnitName ) } ) - if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then - self:T( "Sending Message" ) - local TaskGroup = ProcessUnit:GetGroup() - self.TargetSetUnit:Remove( EventData.IniUnitName ) - self:Message( "You hit a target. Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." ) - end - self:T( { "After sending Message" } ) - end - - --- StateMachine callback function - -- @param #ACT_ACCOUNT_DEADS self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To ) - - if self.TargetSetUnit:Count() > 0 then - self:__More( 1 ) - else - self:__NoMore( 1 ) - end - end - - --- DCS Events - - --- @param #ACT_ACCOUNT_DEADS self - -- @param Event#EVENTDATA EventData - function ACT_ACCOUNT_DEADS:onfuncEventDead( EventData ) - self:T( { "EventDead", EventData } ) - - if EventData.IniDCSUnit then - self:Event( EventData ) - end - end - -end -- ACT_ACCOUNT DEADS ---- (SP) (MP) (FSM) Route AI or players through waypoints or to zones. --- --- === --- --- # @{#ACT_ASSIST} FSM class, extends @{Fsm#FSM_PROCESS} --- --- ## ACT_ASSIST state machine: --- --- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. --- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. --- Each derived class follows exactly the same process, using the same events and following the same state transitions, --- but will have **different implementation behaviour** upon each event or state transition. --- --- ### ACT_ASSIST **Events**: --- --- These are the events defined in this class: --- --- * **Start**: The process is started. --- * **Next**: The process is smoking the targets in the given zone. --- --- ### ACT_ASSIST **Event methods**: --- --- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. --- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine: --- --- * **Immediate**: The event method has exactly the name of the event. --- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. --- --- ### ACT_ASSIST **States**: --- --- * **None**: The controllable did not receive route commands. --- * **AwaitSmoke (*)**: The process is awaiting to smoke the targets in the zone. --- * **Smoking (*)**: The process is smoking the targets in the zone. --- * **Failed (*)**: The process has failed. --- --- (*) End states of the process. --- --- ### ACT_ASSIST state transition methods: --- --- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. --- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Before** the state transition. --- The state transition method needs to start with the name **OnBefore + the name of the state**. --- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, --- but then you'll need to specify your own logic using the AIControllable! --- --- * **After** the state transition. --- The state transition method needs to start with the name **OnAfter + the name of the state**. --- These state transition methods need to provide a return value, which is specified at the function description. --- --- === --- --- # 1) @{#ACT_ASSIST_SMOKE_TARGETS_ZONE} class, extends @{Fsm.Route#ACT_ASSIST} --- --- The ACT_ASSIST_SMOKE_TARGETS_ZONE class implements the core functions to smoke targets in a @{Zone}. --- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour. --- At random intervals, a new target is smoked. --- --- # 1.1) ACT_ASSIST_SMOKE_TARGETS_ZONE constructor: --- --- * @{#ACT_ASSIST_SMOKE_TARGETS_ZONE.New}(): Creates a new ACT_ASSIST_SMOKE_TARGETS_ZONE object. --- --- === --- --- @module Smoke - -do -- ACT_ASSIST - - --- ACT_ASSIST class - -- @type ACT_ASSIST - -- @extends Core.Fsm#FSM_PROCESS - ACT_ASSIST = { - ClassName = "ACT_ASSIST", - } - - --- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. - -- @param #ACT_ASSIST self - -- @return #ACT_ASSIST - function ACT_ASSIST:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ASSIST" ) ) -- Core.Fsm#FSM_PROCESS - - self:AddTransition( "None", "Start", "AwaitSmoke" ) - self:AddTransition( "AwaitSmoke", "Next", "Smoking" ) - self:AddTransition( "Smoking", "Next", "AwaitSmoke" ) - self:AddTransition( "*", "Stop", "Success" ) - self:AddTransition( "*", "Fail", "Failed" ) - - self:AddEndState( "Failed" ) - self:AddEndState( "Success" ) - - self:SetStartState( "None" ) - - return self - end - - --- Task Events - - --- StateMachine callback function - -- @param #ACT_ASSIST self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To ) - - local ProcessGroup = ProcessUnit:GetGroup() - local MissionMenu = self:GetMission():GetMenu( ProcessGroup ) - - local function MenuSmoke( MenuParam ) - self:E( MenuParam ) - local self = MenuParam.self - local SmokeColor = MenuParam.SmokeColor - self.SmokeColor = SmokeColor - self:__Next( 1 ) - end - - self.Menu = MENU_GROUP:New( ProcessGroup, "Target acquisition", MissionMenu ) - self.MenuSmokeBlue = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop blue smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Blue } ) - self.MenuSmokeGreen = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop green smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Green } ) - self.MenuSmokeOrange = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop Orange smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Orange } ) - self.MenuSmokeRed = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop Red smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Red } ) - self.MenuSmokeWhite = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop White smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.White } ) - end - - --- StateMachine callback function - -- @param #ACT_ASSIST self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To ) - - self.Menu:Remove() -- When stopped, remove the menus - end - -end - -do -- ACT_ASSIST_SMOKE_TARGETS_ZONE - - --- ACT_ASSIST_SMOKE_TARGETS_ZONE class - -- @type ACT_ASSIST_SMOKE_TARGETS_ZONE - -- @field Set#SET_UNIT TargetSetUnit - -- @field Core.Zone#ZONE_BASE TargetZone - -- @extends #ACT_ASSIST - ACT_ASSIST_SMOKE_TARGETS_ZONE = { - ClassName = "ACT_ASSIST_SMOKE_TARGETS_ZONE", - } - --- function ACT_ASSIST_SMOKE_TARGETS_ZONE:_Destructor() --- self:E("_Destructor") --- --- self.Menu:Remove() --- self:EventRemoveAll() --- end - - --- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. - -- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self - -- @param Set#SET_UNIT TargetSetUnit - -- @param Core.Zone#ZONE_BASE TargetZone - function ACT_ASSIST_SMOKE_TARGETS_ZONE:New( TargetSetUnit, TargetZone ) - local self = BASE:Inherit( self, ACT_ASSIST:New() ) -- #ACT_ASSIST - - self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - - return self - end - - function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( FsmSmoke ) - - self.TargetSetUnit = FsmSmoke.TargetSetUnit - self.TargetZone = FsmSmoke.TargetZone - end - - --- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. - -- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self - -- @param Set#SET_UNIT TargetSetUnit - -- @param Core.Zone#ZONE_BASE TargetZone - -- @return #ACT_ASSIST_SMOKE_TARGETS_ZONE self - function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( TargetSetUnit, TargetZone ) - - self.TargetSetUnit = TargetSetUnit - self.TargetZone = TargetZone - - return self - end - - --- StateMachine callback function - -- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self - -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit - -- @param #string Event - -- @param #string From - -- @param #string To - function ACT_ASSIST_SMOKE_TARGETS_ZONE:onenterSmoking( ProcessUnit, From, Event, To ) - - self.TargetSetUnit:ForEachUnit( - --- @param Wrapper.Unit#UNIT SmokeUnit - function( SmokeUnit ) - if math.random( 1, ( 100 * self.TargetSetUnit:Count() ) / 4 ) <= 100 then - SCHEDULER:New( self, - function() - if SmokeUnit:IsAlive() then - SmokeUnit:Smoke( self.SmokeColor, 150 ) - end - end, {}, math.random( 10, 60 ) - ) - end - end - ) - - end - -end--- A COMMANDCENTER is the owner of multiple missions within MOOSE. --- A COMMANDCENTER governs multiple missions, the tasking and the reporting. --- @module CommandCenter - - - ---- The REPORT class --- @type REPORT --- @extends Core.Base#BASE -REPORT = { - ClassName = "REPORT", -} - ---- Create a new REPORT. --- @param #REPORT self --- @param #string Title --- @return #REPORT -function REPORT:New( Title ) - - local self = BASE:Inherit( self, BASE:New() ) - - self.Report = {} - if Title then - self.Report[#self.Report+1] = Title - end - - return self -end - ---- Add a new line to a REPORT. --- @param #REPORT self --- @param #string Text --- @return #REPORT -function REPORT:Add( Text ) - self.Report[#self.Report+1] = Text - return self.Report[#self.Report] -end - ---- Produces the text of the report, taking into account an optional delimeter, which is \n by default. --- @param #REPORT self --- @param #string Delimiter (optional) A delimiter text. --- @return #string The report text. -function REPORT:Text( Delimiter ) - Delimiter = Delimiter or "\n" - local ReportText = table.concat( self.Report, Delimiter ) or "" - return ReportText -end - ---- The COMMANDCENTER class --- @type COMMANDCENTER --- @field Wrapper.Group#GROUP HQ --- @field Dcs.DCSCoalitionWrapper.Object#coalition CommandCenterCoalition --- @list Missions --- @extends Core.Base#BASE -COMMANDCENTER = { - ClassName = "COMMANDCENTER", - CommandCenterName = "", - CommandCenterCoalition = nil, - CommandCenterPositionable = nil, - Name = "", -} ---- The constructor takes an IDENTIFIABLE as the HQ command center. --- @param #COMMANDCENTER self --- @param Wrapper.Positionable#POSITIONABLE CommandCenterPositionable --- @param #string CommandCenterName --- @return #COMMANDCENTER -function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) - - local self = BASE:Inherit( self, BASE:New() ) - - self.CommandCenterPositionable = CommandCenterPositionable - self.CommandCenterName = CommandCenterName or CommandCenterPositionable:GetName() - self.CommandCenterCoalition = CommandCenterPositionable:GetCoalition() - - self.Missions = {} - - self:HandleEvent( EVENTS.Birth, - --- @param #COMMANDCENTER self - -- @param Core.Event#EVENTDATA EventData - function( self, EventData ) - if EventData.IniObjectCategory == 1 then - local EventGroup = GROUP:Find( EventData.IniDCSGroup ) - if EventGroup and self:HasGroup( EventGroup ) then - local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu ) - local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup ) - local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup ) - self:ReportSummary( EventGroup ) - end - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! - Mission:JoinUnit( PlayerUnit, PlayerGroup ) - Mission:ReportDetails() - end - end - - end - ) - - -- When a player enters a client or a unit, the CommandCenter will check for each Mission and each Task in the Mission if the player has things to do. - -- For these elements, it will= - -- - Set the correct menu. - -- - Assign the PlayerUnit to the Task if required. - -- - Send a message to the other players in the group that this player has joined. - self:HandleEvent( EVENTS.PlayerEnterUnit, - --- @param #COMMANDCENTER self - -- @param Core.Event#EVENTDATA EventData - function( self, EventData ) - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! - Mission:JoinUnit( PlayerUnit, PlayerGroup ) - Mission:ReportDetails() - end - end - ) - - -- Handle when a player leaves a slot and goes back to spectators ... - -- The PlayerUnit will be UnAssigned from the Task. - -- When there is no Unit left running the Task, the Task goes into Abort... - self:HandleEvent( EVENTS.PlayerLeaveUnit, - --- @param #TASK self - -- @param Core.Event#EVENTDATA EventData - function( self, EventData ) - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:AbortUnit( PlayerUnit ) - end - end - ) - - -- Handle when a player leaves a slot and goes back to spectators ... - -- The PlayerUnit will be UnAssigned from the Task. - -- When there is no Unit left running the Task, the Task goes into Abort... - self:HandleEvent( EVENTS.Crash, - --- @param #TASK self - -- @param Core.Event#EVENTDATA EventData - function( self, EventData ) - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - Mission:CrashUnit( PlayerUnit ) - end - end - ) - - return self -end - ---- Gets the name of the HQ command center. --- @param #COMMANDCENTER self --- @return #string -function COMMANDCENTER:GetName() - - return self.CommandCenterName -end - ---- Gets the POSITIONABLE of the HQ command center. --- @param #COMMANDCENTER self --- @return Wrapper.Positionable#POSITIONABLE -function COMMANDCENTER:GetPositionable() - return self.CommandCenterPositionable -end - ---- Get the Missions governed by the HQ command center. --- @param #COMMANDCENTER self --- @return #list -function COMMANDCENTER:GetMissions() - - return self.Missions -end - ---- Add a MISSION to be governed by the HQ command center. --- @param #COMMANDCENTER self --- @param Tasking.Mission#MISSION Mission --- @return Tasking.Mission#MISSION -function COMMANDCENTER:AddMission( Mission ) - - self.Missions[Mission] = Mission - - return Mission -end - ---- Removes a MISSION to be governed by the HQ command center. --- The given Mission is not nilified. --- @param #COMMANDCENTER self --- @param Tasking.Mission#MISSION Mission --- @return Tasking.Mission#MISSION -function COMMANDCENTER:RemoveMission( Mission ) - - self.Missions[Mission] = nil - - return Mission -end - ---- Sets the menu structure of the Missions governed by the HQ command center. --- @param #COMMANDCENTER self -function COMMANDCENTER:SetMenu() - self:F() - - self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "Command Center (" .. self:GetName() .. ")" ) - - local MenuTime = timer.getTime() - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:SetMenu( MenuTime ) - end - - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:RemoveMenu( MenuTime ) - end - -end - ---- Gets the commandcenter menu structure governed by the HQ command center. --- @param #COMMANDCENTER self --- @return Core.Menu#MENU_COALITION -function COMMANDCENTER:GetMenu() - self:F() - return self.CommandCenterMenu -end - ---- Checks of the COMMANDCENTER has a GROUP. --- @param #COMMANDCENTER self --- @param Wrapper.Group#GROUP --- @return #boolean -function COMMANDCENTER:HasGroup( MissionGroup ) - - local Has = false - - for MissionID, Mission in pairs( self.Missions ) do - local Mission = Mission -- Tasking.Mission#MISSION - if Mission:HasGroup( MissionGroup ) then - Has = true - break - end - end - - return Has -end - ---- Send a CC message to the coalition of the CC. --- @param #COMMANDCENTER self -function COMMANDCENTER:MessageToAll( Message ) - - self:GetPositionable():MessageToAll( Message, 20, self:GetName() ) - -end - ---- Send a CC message to a GROUP. --- @param #COMMANDCENTER self --- @param #string Message --- @param Wrapper.Group#GROUP TaskGroup --- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. -function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name ) - - local Prefix = "@ Group" - Prefix = Prefix .. ( Name and " (" .. Name .. "): " or '' ) - Message = Prefix .. Message - self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() ) - -end - ---- Send a CC message to the coalition of the CC. --- @param #COMMANDCENTER self -function COMMANDCENTER:MessageToCoalition( Message ) - - local CCCoalition = self:GetPositionable():GetCoalition() - --TODO: Fix coalition bug! - self:GetPositionable():MessageToCoalition( Message, 20, CCCoalition, self:GetName() ) - -end - - ---- Report the status of all MISSIONs to a GROUP. --- Each Mission is listed, with an indication how many Tasks are still to be completed. --- @param #COMMANDCENTER self -function COMMANDCENTER:ReportSummary( ReportGroup ) - self:E( ReportGroup ) - - local Report = REPORT:New() - - for MissionID, Mission in pairs( self.Missions ) do - local Mission = Mission -- Tasking.Mission#MISSION - Report:Add( " - " .. Mission:ReportOverview() ) - end - - self:GetPositionable():MessageToGroup( Report:Text(), 30, ReportGroup ) - -end - ---- Report the status of a Task to a Group. --- Report the details of a Mission, listing the Mission, and all the Task details. --- @param #COMMANDCENTER self -function COMMANDCENTER:ReportDetails( ReportGroup, Task ) - self:E( ReportGroup ) - - local Report = REPORT:New() - - for MissionID, Mission in pairs( self.Missions ) do - local Mission = Mission -- Tasking.Mission#MISSION - Report:Add( " - " .. Mission:ReportDetails() ) - end - - self:GetPositionable():MessageToGroup( Report:Text(), 30, ReportGroup ) -end - ---- A MISSION is the main owner of a Mission orchestration within MOOSE . The Mission framework orchestrates @{CLIENT}s, @{TASK}s, @{STAGE}s etc. --- A @{CLIENT} needs to be registered within the @{MISSION} through the function @{AddClient}. A @{TASK} needs to be registered within the @{MISSION} through the function @{AddTask}. --- @module Mission - ---- The MISSION class --- @type MISSION --- @field #MISSION.Clients _Clients --- @field Core.Menu#MENU_COALITION MissionMenu --- @field #string MissionBriefing --- @extends Core.Fsm#FSM -MISSION = { - ClassName = "MISSION", - Name = "", - MissionStatus = "PENDING", -} - ---- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. --- @param #MISSION self --- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter --- @param #string MissionName is the name of the mission. This name will be used to reference the status of each mission by the players. --- @param #string MissionPriority is a string indicating the "priority" of the Mission. f.e. "Primary", "Secondary" or "First", "Second". It is free format and up to the Mission designer to choose. There are no rules behind this field. --- @param #string MissionBriefing is a string indicating the mission briefing to be shown when a player joins a @{CLIENT}. --- @param Dcs.DCSCoalitionWrapper.Object#coalition MissionCoalition is a string indicating the coalition or party to which this mission belongs to. It is free format and can be chosen freely by the mission designer. Note that this field is not to be confused with the coalition concept of the ME. Examples of a Mission Coalition could be "NATO", "CCCP", "Intruders", "Terrorists"... --- @return #MISSION self -function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition ) - - local self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM - - self:SetStartState( "Idle" ) - - self:AddTransition( "Idle", "Start", "Ongoing" ) - - --- OnLeave Transition Handler for State Idle. - -- @function [parent=#MISSION] OnLeaveIdle - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Idle. - -- @function [parent=#MISSION] OnEnterIdle - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnLeave Transition Handler for State Ongoing. - -- @function [parent=#MISSION] OnLeaveOngoing - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Ongoing. - -- @function [parent=#MISSION] OnEnterOngoing - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Start. - -- @function [parent=#MISSION] OnBeforeStart - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Start. - -- @function [parent=#MISSION] OnAfterStart - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Start. - -- @function [parent=#MISSION] Start - -- @param #MISSION self - - --- Asynchronous Event Trigger for Event Start. - -- @function [parent=#MISSION] __Start - -- @param #MISSION self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "Ongoing", "Stop", "Idle" ) - - --- OnLeave Transition Handler for State Idle. - -- @function [parent=#MISSION] OnLeaveIdle - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Idle. - -- @function [parent=#MISSION] OnEnterIdle - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Stop. - -- @function [parent=#MISSION] OnBeforeStop - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Stop. - -- @function [parent=#MISSION] OnAfterStop - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Stop. - -- @function [parent=#MISSION] Stop - -- @param #MISSION self - - --- Asynchronous Event Trigger for Event Stop. - -- @function [parent=#MISSION] __Stop - -- @param #MISSION self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "Ongoing", "Complete", "Completed" ) - - --- OnLeave Transition Handler for State Completed. - -- @function [parent=#MISSION] OnLeaveCompleted - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Completed. - -- @function [parent=#MISSION] OnEnterCompleted - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Complete. - -- @function [parent=#MISSION] OnBeforeComplete - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Complete. - -- @function [parent=#MISSION] OnAfterComplete - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Complete. - -- @function [parent=#MISSION] Complete - -- @param #MISSION self - - --- Asynchronous Event Trigger for Event Complete. - -- @function [parent=#MISSION] __Complete - -- @param #MISSION self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Fail", "Failed" ) - - --- OnLeave Transition Handler for State Failed. - -- @function [parent=#MISSION] OnLeaveFailed - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State Failed. - -- @function [parent=#MISSION] OnEnterFailed - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Fail. - -- @function [parent=#MISSION] OnBeforeFail - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Fail. - -- @function [parent=#MISSION] OnAfterFail - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Fail. - -- @function [parent=#MISSION] Fail - -- @param #MISSION self - - --- Asynchronous Event Trigger for Event Fail. - -- @function [parent=#MISSION] __Fail - -- @param #MISSION self - -- @param #number Delay The delay in seconds. - - self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) - - self.CommandCenter = CommandCenter - CommandCenter:AddMission( self ) - - self.Name = MissionName - self.MissionPriority = MissionPriority - self.MissionBriefing = MissionBriefing - self.MissionCoalition = MissionCoalition - - self.Tasks = {} - - -- Private implementations - - - - return self -end - --- FSM function for a MISSION --- @param #MISSION self --- @param #string From --- @param #string Event --- @param #string To -function MISSION:onbeforeComplete( From, Event, To ) - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if not Task:IsStateSuccess() and not Task:IsStateFailed() and not Task:IsStateAborted() and not Task:IsStateCancelled() then - return false -- Mission cannot be completed. Other Tasks are still active. - end - end - return true -- Allow Mission completion. -end - --- FSM function for a MISSION --- @param #MISSION self --- @param #string From --- @param #string Event --- @param #string To -function MISSION:onenterCompleted( From, Event, To ) - - self:GetCommandCenter():MessageToCoalition( "Mission " .. self:GetName() .. " has been completed! Good job guys!" ) -end - ---- Gets the mission name. --- @param #MISSION self --- @return #MISSION self -function MISSION:GetName() - return self.Name -end - ---- Add a Unit to join the Mission. --- For each Task within the Mission, the Unit is joined with the Task. --- If the Unit was not part of a Task in the Mission, false is returned. --- If the Unit is part of a Task in the Mission, true is returned. --- @param #MISSION self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player joining the Mission. --- @param Wrapper.Group#GROUP PlayerGroup The GROUP of the player joining the Mission. --- @return #boolean true if Unit is part of a Task in the Mission. -function MISSION:JoinUnit( PlayerUnit, PlayerGroup ) - self:F( { PlayerUnit = PlayerUnit, PlayerGroup = PlayerGroup } ) - - local PlayerUnitAdded = false - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if Task:JoinUnit( PlayerUnit, PlayerGroup ) then - PlayerUnitAdded = true - end - end - - return PlayerUnitAdded -end - ---- Aborts a PlayerUnit from the Mission. --- For each Task within the Mission, the PlayerUnit is removed from Task where it is assigned. --- If the Unit was not part of a Task in the Mission, false is returned. --- If the Unit is part of a Task in the Mission, true is returned. --- @param #MISSION self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player joining the Mission. --- @return #boolean true if Unit is part of a Task in the Mission. -function MISSION:AbortUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - local PlayerUnitRemoved = false - - for TaskID, Task in pairs( self:GetTasks() ) do - if Task:AbortUnit( PlayerUnit ) then - PlayerUnitRemoved = true - end - end - - return PlayerUnitRemoved -end - ---- Handles a crash of a PlayerUnit from the Mission. --- For each Task within the Mission, the PlayerUnit is removed from Task where it is assigned. --- If the Unit was not part of a Task in the Mission, false is returned. --- If the Unit is part of a Task in the Mission, true is returned. --- @param #MISSION self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player crashing. --- @return #boolean true if Unit is part of a Task in the Mission. -function MISSION:CrashUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - local PlayerUnitRemoved = false - - for TaskID, Task in pairs( self:GetTasks() ) do - if Task:CrashUnit( PlayerUnit ) then - PlayerUnitRemoved = true - end - end - - return PlayerUnitRemoved -end - ---- Add a scoring to the mission. --- @param #MISSION self --- @return #MISSION self -function MISSION:AddScoring( Scoring ) - self.Scoring = Scoring - return self -end - ---- Get the scoring object of a mission. --- @param #MISSION self --- @return #SCORING Scoring -function MISSION:GetScoring() - return self.Scoring -end - ---- Get the groups for which TASKS are given in the mission --- @param #MISSION self --- @return Core.Set#SET_GROUP -function MISSION:GetGroups() - - local SetGroup = SET_GROUP:New() - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - local GroupSet = Task:GetGroups() - GroupSet:ForEachGroup( - function( TaskGroup ) - SetGroup:Add( TaskGroup, TaskGroup ) - end - ) - end - - return SetGroup - -end - - ---- Sets the Planned Task menu. --- @param #MISSION self --- @param #number MenuTime -function MISSION:SetMenu( MenuTime ) - self:F() - - for _, TaskData in pairs( self:GetTasks() ) do - local Task = TaskData -- Tasking.Task#TASK - Task:SetMenu( MenuTime ) - end -end - ---- Removes the Planned Task menu. --- @param #MISSION self --- @param #number MenuTime -function MISSION:RemoveMenu( MenuTime ) - self:F() - - for _, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Task:RemoveMenu( MenuTime ) - end -end - - ---- Gets the COMMANDCENTER. --- @param #MISSION self --- @return Tasking.CommandCenter#COMMANDCENTER -function MISSION:GetCommandCenter() - return self.CommandCenter -end - - ---- Removes a Task menu. --- @param #MISSION self --- @param Tasking.Task#TASK Task --- @return #MISSION self -function MISSION:RemoveTaskMenu( Task ) - - Task:RemoveMenu() -end - - ---- Gets the mission menu for the coalition. --- @param #MISSION self --- @param Wrapper.Group#GROUP TaskGroup --- @return Core.Menu#MENU_COALITION self -function MISSION:GetMenu( TaskGroup ) - - local CommandCenter = self:GetCommandCenter() - local CommandCenterMenu = CommandCenter:GetMenu() - - local MissionName = self:GetName() - local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) - - return MissionMenu -end - - ---- Get the TASK identified by the TaskNumber from the Mission. This function is useful in GoalFunctions. --- @param #string TaskName The Name of the @{Task} within the @{Mission}. --- @return Tasking.Task#TASK The Task --- @return #nil Returns nil if no task was found. -function MISSION:GetTask( TaskName ) - self:F( { TaskName } ) - - return self.Tasks[TaskName] -end - - ---- Register a @{Task} to be completed within the @{Mission}. --- Note that there can be multiple @{Task}s registered to be completed. --- Each Task can be set a certain Goals. The Mission will not be completed until all Goals are reached. --- @param #MISSION self --- @param Tasking.Task#TASK Task is the @{Task} object. --- @return Tasking.Task#TASK The task added. -function MISSION:AddTask( Task ) - - local TaskName = Task:GetTaskName() - self:F( TaskName ) - - self.Tasks[TaskName] = self.Tasks[TaskName] or { n = 0 } - - self.Tasks[TaskName] = Task - - self:GetCommandCenter():SetMenu() - - return Task -end - ---- Removes a @{Task} to be completed within the @{Mission}. --- Note that there can be multiple @{Task}s registered to be completed. --- Each Task can be set a certain Goals. The Mission will not be completed until all Goals are reached. --- @param #MISSION self --- @param Tasking.Task#TASK Task is the @{Task} object. --- @return #nil The cleaned Task reference. -function MISSION:RemoveTask( Task ) - - local TaskName = Task:GetTaskName() - - self:F( TaskName ) - self.Tasks[TaskName] = self.Tasks[TaskName] or { n = 0 } - - -- Ensure everything gets garbarge collected. - self.Tasks[TaskName] = nil - Task = nil - - collectgarbage() - - self:GetCommandCenter():SetMenu() - - return nil -end - ---- Return the next @{Task} ID to be completed within the @{Mission}. --- @param #MISSION self --- @param Tasking.Task#TASK Task is the @{Task} object. --- @return Tasking.Task#TASK The task added. -function MISSION:GetNextTaskID( Task ) - - local TaskName = Task:GetTaskName() - self:F( TaskName ) - self.Tasks[TaskName] = self.Tasks[TaskName] or { n = 0 } - - self.Tasks[TaskName].n = self.Tasks[TaskName].n + 1 - - return self.Tasks[TaskName].n -end - ---- Is the @{Mission} **Completed**. --- @param #MISSION self --- @return #boolean -function MISSION:IsCompleted() - return self:Is( "Completed" ) -end - ---- Is the @{Mission} **Idle**. --- @param #MISSION self --- @return #boolean -function MISSION:IsIdle() - return self:Is( "Idle" ) -end - ---- Is the @{Mission} **Ongoing**. --- @param #MISSION self --- @return #boolean -function MISSION:IsOngoing() - return self:Is( "Ongoing" ) -end - ---- Is the @{Mission} **Failed**. --- @param #MISSION self --- @return #boolean -function MISSION:IsFailed() - return self:Is( "Failed" ) -end - ---- Is the @{Mission} **Hold**. --- @param #MISSION self --- @return #boolean -function MISSION:IsHold() - return self:Is( "Hold" ) -end - ---- Validates if the Mission has a Group --- @param #MISSION --- @return #boolean true if the Mission has a Group. -function MISSION:HasGroup( TaskGroup ) - local Has = false - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if Task:HasGroup( TaskGroup ) then - Has = true - break - end - end - - return Has -end - ---- Create a summary report of the Mission (one line). --- @param #MISSION self --- @return #string -function MISSION:ReportSummary() - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetName() - - -- Determine the status of the mission. - local Status = self:GetState() - - -- Determine how many tasks are remaining. - local TasksRemaining = 0 - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if Task:IsStateSuccess() or Task:IsStateFailed() then - else - TasksRemaining = TasksRemaining + 1 - end - end - - Report:Add( "Mission " .. Name .. " - " .. Status .. " - " .. TasksRemaining .. " tasks remaining." ) - - return Report:Text() -end - ---- Create a overview report of the Mission (multiple lines). --- @param #MISSION self --- @return #string -function MISSION:ReportOverview() - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetName() - - -- Determine the status of the mission. - local Status = self:GetState() - - Report:Add( "Mission " .. Name .. " - State '" .. Status .. "'" ) - - -- Determine how many tasks are remaining. - local TasksRemaining = 0 - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Report:Add( "- " .. Task:ReportSummary() ) - end - - return Report:Text() -end - ---- Create a detailed report of the Mission, listing all the details of the Task. --- @param #MISSION self --- @return #string -function MISSION:ReportDetails() - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetName() - - -- Determine the status of the mission. - local Status = self:GetState() - - Report:Add( "Mission " .. Name .. " - State '" .. Status .. "'" ) - - -- Determine how many tasks are remaining. - local TasksRemaining = 0 - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Report:Add( Task:ReportDetails() ) - end - - return Report:Text() -end - ---- Get all the TASKs from the Mission. This function is useful in GoalFunctions. --- @return {TASK,...} Structure of TASKS with the @{TASK} number as the key. --- @usage --- -- Get Tasks from the Mission. --- Tasks = Mission:GetTasks() --- env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" ) -function MISSION:GetTasks() - self:F() - - return self.Tasks -end - - ---- This module contains the TASK class. --- --- 1) @{#TASK} class, extends @{Base#BASE} --- ============================================ --- 1.1) The @{#TASK} class implements the methods for task orchestration within MOOSE. --- ---------------------------------------------------------------------------------------- --- The class provides a couple of methods to: --- --- * @{#TASK.AssignToGroup}():Assign a task to a group (of players). --- * @{#TASK.AddProcess}():Add a @{Process} to a task. --- * @{#TASK.RemoveProcesses}():Remove a running @{Process} from a running task. --- * @{#TASK.SetStateMachine}():Set a @{Fsm} to a task. --- * @{#TASK.RemoveStateMachine}():Remove @{Fsm} from a task. --- * @{#TASK.HasStateMachine}():Enquire if the task has a @{Fsm} --- * @{#TASK.AssignToUnit}(): Assign a task to a unit. (Needs to be implemented in the derived classes from @{#TASK}. --- * @{#TASK.UnAssignFromUnit}(): Unassign the task from a unit. --- * @{#TASK.SetTimeOut}(): Set timer in seconds before task gets cancelled if not assigned. --- --- 1.2) Set and enquire task status (beyond the task state machine processing). --- ---------------------------------------------------------------------------- --- A task needs to implement as a minimum the following task states: --- --- * **Success**: Expresses the successful execution and finalization of the task. --- * **Failed**: Expresses the failure of a task. --- * **Planned**: Expresses that the task is created, but not yet in execution and is not assigned yet. --- * **Assigned**: Expresses that the task is assigned to a Group of players, and that the task is in execution mode. --- --- A task may also implement the following task states: --- --- * **Rejected**: Expresses that the task is rejected by a player, who was requested to accept the task. --- * **Cancelled**: Expresses that the task is cancelled by HQ or through a logical situation where a cancellation of the task is required. --- --- A task can implement more statusses than the ones outlined above. Please consult the documentation of the specific tasks to understand the different status modelled. --- --- The status of tasks can be set by the methods **State** followed by the task status. An example is `StateAssigned()`. --- The status of tasks can be enquired by the methods **IsState** followed by the task status name. An example is `if IsStateAssigned() then`. --- --- 1.3) Add scoring when reaching a certain task status: --- ----------------------------------------------------- --- Upon reaching a certain task status in a task, additional scoring can be given. If the Mission has a scoring system attached, the scores will be added to the mission scoring. --- Use the method @{#TASK.AddScore}() to add scores when a status is reached. --- --- 1.4) Task briefing: --- ------------------- --- A task briefing can be given that is shown to the player when he is assigned to the task. --- --- === --- --- ### Authors: FlightControl - Design and Programming --- --- @module Task - ---- The TASK class --- @type TASK --- @field Core.Scheduler#SCHEDULER TaskScheduler --- @field Tasking.Mission#MISSION Mission --- @field Core.Set#SET_GROUP SetGroup The Set of Groups assigned to the Task --- @field Core.Fsm#FSM_PROCESS FsmTemplate --- @field Tasking.Mission#MISSION Mission --- @field Tasking.CommandCenter#COMMANDCENTER CommandCenter --- @extends Core.Fsm#FSM_TASK -TASK = { - ClassName = "TASK", - TaskScheduler = nil, - ProcessClasses = {}, -- The container of the Process classes that will be used to create and assign new processes for the task to ProcessUnits. - Processes = {}, -- The container of actual process objects instantiated and assigned to ProcessUnits. - Players = nil, - Scores = {}, - Menu = {}, - SetGroup = nil, - FsmTemplate = nil, - Mission = nil, - CommandCenter = nil, - TimeOut = 0, -} - ---- FSM PlayerAborted event handler prototype for TASK. --- @function [parent=#TASK] OnAfterPlayerAborted --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The Unit of the Player when he went back to spectators or left the mission. --- @param #string PlayerName The name of the Player. - ---- FSM PlayerCrashed event handler prototype for TASK. --- @function [parent=#TASK] OnAfterPlayerCrashed --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The Unit of the Player when he crashed in the mission. --- @param #string PlayerName The name of the Player. - ---- FSM PlayerDead event handler prototype for TASK. --- @function [parent=#TASK] OnAfterPlayerDead --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The Unit of the Player when he died in the mission. --- @param #string PlayerName The name of the Player. - ---- FSM Fail synchronous event function for TASK. --- Use this event to Fail the Task. --- @function [parent=#TASK] Fail --- @param #TASK self - ---- FSM Fail asynchronous event function for TASK. --- Use this event to Fail the Task. --- @function [parent=#TASK] __Fail --- @param #TASK self - ---- FSM Abort synchronous event function for TASK. --- Use this event to Abort the Task. --- @function [parent=#TASK] Abort --- @param #TASK self - ---- FSM Abort asynchronous event function for TASK. --- Use this event to Abort the Task. --- @function [parent=#TASK] __Abort --- @param #TASK self - ---- FSM Success synchronous event function for TASK. --- Use this event to make the Task a Success. --- @function [parent=#TASK] Success --- @param #TASK self - ---- FSM Success asynchronous event function for TASK. --- Use this event to make the Task a Success. --- @function [parent=#TASK] __Success --- @param #TASK self - ---- FSM Cancel synchronous event function for TASK. --- Use this event to Cancel the Task. --- @function [parent=#TASK] Cancel --- @param #TASK self - ---- FSM Cancel asynchronous event function for TASK. --- Use this event to Cancel the Task. --- @function [parent=#TASK] __Cancel --- @param #TASK self - ---- FSM Replan synchronous event function for TASK. --- Use this event to Replan the Task. --- @function [parent=#TASK] Replan --- @param #TASK self - ---- FSM Replan asynchronous event function for TASK. --- Use this event to Replan the Task. --- @function [parent=#TASK] __Replan --- @param #TASK self - - ---- Instantiates a new TASK. Should never be used. Interface Class. --- @param #TASK self --- @param Tasking.Mission#MISSION Mission The mission wherein the Task is registered. --- @param Core.Set#SET_GROUP SetGroupAssign The set of groups for which the Task can be assigned. --- @param #string TaskName The name of the Task --- @param #string TaskType The type of the Task --- @return #TASK self -function TASK:New( Mission, SetGroupAssign, TaskName, TaskType ) - - local self = BASE:Inherit( self, FSM_TASK:New() ) -- Core.Fsm#FSM_TASK - - self:SetStartState( "Planned" ) - self:AddTransition( "Planned", "Assign", "Assigned" ) - self:AddTransition( "Assigned", "AssignUnit", "Assigned" ) - self:AddTransition( "Assigned", "Success", "Success" ) - self:AddTransition( "Assigned", "Fail", "Failed" ) - self:AddTransition( "Assigned", "Abort", "Aborted" ) - self:AddTransition( "Assigned", "Cancel", "Cancelled" ) - self:AddTransition( "*", "PlayerCrashed", "*" ) - self:AddTransition( "*", "PlayerAborted", "*" ) - self:AddTransition( "*", "PlayerDead", "*" ) - self:AddTransition( { "Failed", "Aborted", "Cancelled" }, "Replan", "Planned" ) - self:AddTransition( "*", "TimeOut", "Cancelled" ) - - self:E( "New TASK " .. TaskName ) - - self.Processes = {} - self.Fsm = {} - - self.Mission = Mission - self.CommandCenter = Mission:GetCommandCenter() - - self.SetGroup = SetGroupAssign - - self:SetType( TaskType ) - self:SetName( TaskName ) - self:SetID( Mission:GetNextTaskID( self ) ) -- The Mission orchestrates the task sequences .. - - self.TaskBriefing = "You are invited for the task: " .. self.TaskName .. "." - - self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() - - - return self -end - ---- Get the Task FSM Process Template --- @param #TASK self --- @return Core.Fsm#FSM_PROCESS -function TASK:GetUnitProcess( TaskUnit ) - - if TaskUnit then - return self:GetStateMachine( TaskUnit ) - else - return self.FsmTemplate - end -end - ---- Sets the Task FSM Process Template --- @param #TASK self --- @param Core.Fsm#FSM_PROCESS -function TASK:SetUnitProcess( FsmTemplate ) - - self.FsmTemplate = FsmTemplate -end - ---- Add a PlayerUnit to join the Task. --- For each Group within the Task, the Unit is check if it can join the Task. --- If the Unit was not part of the Task, false is returned. --- If the Unit is part of the Task, true is returned. --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player joining the Mission. --- @param Wrapper.Group#GROUP PlayerGroup The GROUP of the player joining the Mission. --- @return #boolean true if Unit is part of the Task. -function TASK:JoinUnit( PlayerUnit, PlayerGroup ) - self:F( { PlayerUnit = PlayerUnit, PlayerGroup = PlayerGroup } ) - - local PlayerUnitAdded = false - - local PlayerGroups = self:GetGroups() - - -- Is the PlayerGroup part of the PlayerGroups? - if PlayerGroups:IsIncludeObject( PlayerGroup ) then - - -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is added to the Task. - -- If the PlayerGroup is not assigned to the Task, the menu needs to be set. In that case, the PlayerUnit will become the GroupPlayer leader. - if self:IsStatePlanned() or self:IsStateReplanned() then - self:SetMenuForGroup( PlayerGroup ) - --self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) - end - if self:IsStateAssigned() then - local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) - self:E( { IsAssignedToGroup = IsAssignedToGroup } ) - if IsAssignedToGroup then - self:AssignToUnit( PlayerUnit ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " joined Task " .. self:GetName() ) - end - end - end - - return PlayerUnitAdded -end - ---- Abort a PlayerUnit from a Task. --- If the Unit was not part of the Task, false is returned. --- If the Unit is part of the Task, true is returned. --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. --- @return #boolean true if Unit is part of the Task. -function TASK:AbortUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - local PlayerUnitAborted = false - - local PlayerGroups = self:GetGroups() - local PlayerGroup = PlayerUnit:GetGroup() - - -- Is the PlayerGroup part of the PlayerGroups? - if PlayerGroups:IsIncludeObject( PlayerGroup ) then - - -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. - -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. - if self:IsStateAssigned() then - local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) - self:E( { IsAssignedToGroup = IsAssignedToGroup } ) - if IsAssignedToGroup then - self:UnAssignFromUnit( PlayerUnit ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " aborted Task " .. self:GetName() ) - self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) - if #PlayerGroup:GetUnits() == 1 then - self:UnAssignFromGroup( PlayerGroup ) - PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) - self:RemoveMenuForGroup( PlayerGroup ) - end - self:Abort() - end - end - end - - return PlayerUnitAborted -end - ---- A PlayerUnit crashed in a Task. Abort the Player. --- If the Unit was not part of the Task, false is returned. --- If the Unit is part of the Task, true is returned. --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. --- @return #boolean true if Unit is part of the Task. -function TASK:CrashUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - local PlayerUnitCrashed = false - - local PlayerGroups = self:GetGroups() - local PlayerGroup = PlayerUnit:GetGroup() - - -- Is the PlayerGroup part of the PlayerGroups? - if PlayerGroups:IsIncludeObject( PlayerGroup ) then - - -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. - -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. - if self:IsStateAssigned() then - local IsAssignedToGroup = self:IsAssignedToGroup( PlayerGroup ) - self:E( { IsAssignedToGroup = IsAssignedToGroup } ) - if IsAssignedToGroup then - self:UnAssignFromUnit( PlayerUnit ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " crashed in Task " .. self:GetName() ) - self:E( { TaskGroup = PlayerGroup:GetName(), GetUnits = PlayerGroup:GetUnits() } ) - if #PlayerGroup:GetUnits() == 1 then - PlayerGroup:SetState( PlayerGroup, "Assigned", nil ) - self:RemoveMenuForGroup( PlayerGroup ) - end - self:PlayerCrashed( PlayerUnit ) - end - end - end - - return PlayerUnitCrashed -end - - - ---- Gets the Mission to where the TASK belongs. --- @param #TASK self --- @return Tasking.Mission#MISSION -function TASK:GetMission() - - return self.Mission -end - - ---- Gets the SET_GROUP assigned to the TASK. --- @param #TASK self --- @return Core.Set#SET_GROUP -function TASK:GetGroups() - return self.SetGroup -end - - - ---- Assign the @{Task} to a @{Group}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @return #TASK -function TASK:AssignToGroup( TaskGroup ) - self:F2( TaskGroup:GetName() ) - - local TaskGroupName = TaskGroup:GetName() - - TaskGroup:SetState( TaskGroup, "Assigned", self ) - - local Mission = self:GetMission() - local MissionMenu = Mission:GetMenu( TaskGroup ) - MissionMenu:RemoveSubMenus() - - --self:RemoveMenuForGroup( TaskGroup ) - self:SetAssignedMenuForGroup( TaskGroup ) - - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - self:E(PlayerName) - if PlayerName ~= nil or PlayerName ~= "" then - self:AssignToUnit( TaskUnit ) - end - end - - return self -end - ---- --- @param #TASK self --- @param Wrapper.Group#GROUP FindGroup --- @return #boolean -function TASK:HasGroup( FindGroup ) - - return self:GetGroups():IsIncludeObject( FindGroup ) - -end - ---- Assign the @{Task} to an alive @{Unit}. --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return #TASK self -function TASK:AssignToUnit( TaskUnit ) - self:F( TaskUnit:GetName() ) - - local FsmTemplate = self:GetUnitProcess() - - -- Assign a new FsmUnit to TaskUnit. - local FsmUnit = self:SetStateMachine( TaskUnit, FsmTemplate:Copy( TaskUnit, self ) ) -- Core.Fsm#FSM_PROCESS - self:E({"Address FsmUnit", tostring( FsmUnit ) } ) - - FsmUnit:SetStartState( "Planned" ) - - FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. - - return self -end - ---- UnAssign the @{Task} from an alive @{Unit}. --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return #TASK self -function TASK:UnAssignFromUnit( TaskUnit ) - self:F( TaskUnit:GetName() ) - - self:RemoveStateMachine( TaskUnit ) - - return self -end - ---- Sets the TimeOut for the @{Task}. If @{Task} stayed planned for longer than TimeOut, it gets into Cancelled status. --- @param #TASK self --- @param #integer Timer in seconds --- @return #TASK self -function TASK:SetTimeOut ( Timer ) - self:F( Timer ) - self.TimeOut = Timer - self:__TimeOut( self.TimeOut ) - return self -end - ---- Send a message of the @{Task} to the assigned @{Group}s. --- @param #TASK self -function TASK:MessageToGroups( Message ) - self:F( { Message = Message } ) - - local Mission = self:GetMission() - local CC = Mission:GetCommandCenter() - - for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - local TaskGroup = TaskGroup -- Wrapper.Group#GROUP - CC:MessageToGroup( Message, TaskGroup, TaskGroup:GetName() ) - end -end - - ---- Send the briefng message of the @{Task} to the assigned @{Group}s. --- @param #TASK self -function TASK:SendBriefingToAssignedGroups() - self:F2() - - for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - - if self:IsAssignedToGroup( TaskGroup ) then - TaskGroup:Message( self.TaskBriefing, 60 ) - end - end -end - - ---- UnAssign the @{Task} from the @{Group}s. --- @param #TASK self -function TASK:UnAssignFromGroups() - self:F2() - - for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - self:UnAssignFromGroup( TaskGroup ) - end -end - ---- UnAssign the @{Task} from a @{Group}. --- @param #TASK self -function TASK:UnAssignFromGroup( TaskGroup ) - self:F2( { TaskGroup } ) - - TaskGroup:SetState( TaskGroup, "Assigned", nil ) - - self:RemoveAssignedMenuForGroup( TaskGroup ) - - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - if PlayerName ~= nil or PlayerName ~= "" then - self:UnAssignFromUnit( TaskUnit ) - end - end -end - - - ---- Returns if the @{Task} is assigned to the Group. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @return #boolean -function TASK:IsAssignedToGroup( TaskGroup ) - - local TaskGroupName = TaskGroup:GetName() - - if self:IsStateAssigned() then - if TaskGroup:GetState( TaskGroup, "Assigned" ) == self then - self:T( { "Task is assigned to:", TaskGroup:GetName() } ) - return true - end - end - - self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) - return false -end - ---- Returns if the @{Task} has still alive and assigned Units. --- @param #TASK self --- @return #boolean -function TASK:HasAliveUnits() - self:F() - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if self:IsStateAssigned() then - if self:IsAssignedToGroup( TaskGroup ) then - for TaskUnitID, TaskUnit in pairs( TaskGroup:GetUnits() ) do - if TaskUnit:IsAlive() then - self:T( { HasAliveUnits = true } ) - return true - end - end - end - end - end - - self:T( { HasAliveUnits = false } ) - return false -end - ---- Set the menu options of the @{Task} to all the groups in the SetGroup. --- @param #TASK self --- @param #number MenuTime --- @return #TASK -function TASK:SetMenu( MenuTime ) - self:F() - - self.SetGroup:Flush() - for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do - local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP - if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then - if self:IsStatePlanned() or self:IsStateReplanned() then - self:SetMenuForGroup( TaskGroup, MenuTime ) - end - end - end -end - - - ---- Set the Menu for a Group --- @param #TASK self --- @param #number MenuTime --- @return #TASK -function TASK:SetMenuForGroup( TaskGroup, MenuTime ) - - if not TaskGroup:GetState( TaskGroup, "Assigned" ) then - self:SetPlannedMenuForGroup( TaskGroup, self:GetTaskName(), MenuTime ) - else - if not self:IsAssignedToGroup( TaskGroup ) then - self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) - end - end -end - - ---- Set the planned menu option of the @{Task}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @param #string MenuText The menu text. --- @param #number MenuTime --- @return #TASK self -function TASK:SetPlannedMenuForGroup( TaskGroup, MenuText, MenuTime ) - self:E( TaskGroup:GetName() ) - - local Mission = self:GetMission() - local MissionName = Mission:GetName() - local CommandCenter = Mission:GetCommandCenter() - local CommandCenterMenu = CommandCenter:GetMenu() - - local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) - - - local MissionMenu = Mission:GetMenu( TaskGroup ) - - local TaskType = self:GetType() - local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, MissionMenu ):SetTime( MenuTime ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, MenuText, TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ):SetTime( MenuTime ):SetRemoveParent( true ) - - return self -end - ---- Set the assigned menu options of the @{Task}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @param #number MenuTime --- @return #TASK self -function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) - self:E( TaskGroup:GetName() ) - - local Mission = self:GetMission() - local MissionMenu = Mission:GetMenu( TaskGroup ) - - self:E( { MissionMenu = MissionMenu } ) - - local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Task Status", MissionMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Abort Task", MissionMenu, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ) - - return self -end - ---- Remove the menu options of the @{Task} to all the groups in the SetGroup. --- @param #TASK self --- @param #number MenuTime --- @return #TASK -function TASK:RemoveMenu( MenuTime ) - self:F() - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - local TaskGroup = TaskGroup -- Wrapper.Group#GROUP - if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then - if not self:IsAssignedToGroup( TaskGroup ) then - self:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) - end - end - end -end - - ---- Remove the menu option of the @{Task} for a @{Group}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @param #number MenuTime --- @return #TASK self -function TASK:RemovePlannedMenuForGroup( TaskGroup, MenuTime ) - self:F() - - local Mission = self:GetMission() - local MissionName = Mission:GetName() - - local MissionMenu = Mission:GetMenu( TaskGroup ) - - if MissionMenu then - local TaskType = self:GetType() - local TypeMenu = MissionMenu:GetMenu( TaskType ) - - if TypeMenu then - local TaskMenu = TypeMenu:GetMenu( self:GetTaskName() ) - if TaskMenu then - TaskMenu:Remove( MenuTime ) - end - end - end - -end - ---- Remove the assigned menu option of the @{Task} for a @{Group}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @param #number MenuTime --- @return #TASK self -function TASK:RemoveAssignedMenuForGroup( TaskGroup ) - self:F() - - local Mission = self:GetMission() - local MissionName = Mission:GetName() - - local MissionMenu = Mission:GetMenu( TaskGroup ) - - if MissionMenu then - MissionMenu:RemoveSubMenus() - end - -end - -function TASK.MenuAssignToGroup( MenuParam ) - - local self = MenuParam.self - local TaskGroup = MenuParam.TaskGroup - - self:E( "Assigned menu selected") - - self:AssignToGroup( TaskGroup ) -end - ---- Report the task status. --- @param #TASK self -function TASK:MenuTaskStatus( TaskGroup ) - - local ReportText = self:ReportDetails() - - self:T( ReportText ) - self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup ) - -end - ---- Report the task status. --- @param #TASK self -function TASK:MenuTaskAbort( TaskGroup ) - - self:Abort() -end - - - ---- Returns the @{Task} name. --- @param #TASK self --- @return #string TaskName -function TASK:GetTaskName() - return self.TaskName -end - - - - ---- Get the default or currently assigned @{Process} template with key ProcessName. --- @param #TASK self --- @param #string ProcessName --- @return Core.Fsm#FSM_PROCESS -function TASK:GetProcessTemplate( ProcessName ) - - local ProcessTemplate = self.ProcessClasses[ProcessName] - - return ProcessTemplate -end - - - --- TODO: Obscolete? ---- Fail processes from @{Task} with key @{Unit} --- @param #TASK self --- @param #string TaskUnitName --- @return #TASK self -function TASK:FailProcesses( TaskUnitName ) - - for ProcessID, ProcessData in pairs( self.Processes[TaskUnitName] ) do - local Process = ProcessData - Process.Fsm:Fail() - end -end - ---- Add a FiniteStateMachine to @{Task} with key Task@{Unit} --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @param Core.Fsm#FSM_PROCESS Fsm --- @return #TASK self -function TASK:SetStateMachine( TaskUnit, Fsm ) - self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil, Fsm:GetClassNameAndID() } ) - - self.Fsm[TaskUnit] = Fsm - - return Fsm -end - ---- Gets the FiniteStateMachine of @{Task} with key Task@{Unit} --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return Core.Fsm#FSM_PROCESS -function TASK:GetStateMachine( TaskUnit ) - self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) - - return self.Fsm[TaskUnit] -end - ---- Remove FiniteStateMachines from @{Task} with key Task@{Unit} --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return #TASK self -function TASK:RemoveStateMachine( TaskUnit ) - self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) - - self:E( self.Fsm ) - for TaskUnitT, Fsm in pairs( self.Fsm ) do - self:E( TaskUnitT ) - end - - self.Fsm[TaskUnit] = nil - - collectgarbage() - self:E( "Garbage Collected, Processes should be finalized now ...") -end - ---- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return #TASK self -function TASK:HasStateMachine( TaskUnit ) - self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) - - return ( self.Fsm[TaskUnit] ~= nil ) -end - - ---- Gets the Scoring of the task --- @param #TASK self --- @return Functional.Scoring#SCORING Scoring -function TASK:GetScoring() - return self.Mission:GetScoring() -end - - ---- Gets the Task Index, which is a combination of the Task type, the Task name. --- @param #TASK self --- @return #string The Task ID -function TASK:GetTaskIndex() - - local TaskType = self:GetType() - local TaskName = self:GetName() - - return TaskType .. "." .. TaskName -end - ---- Sets the Name of the Task --- @param #TASK self --- @param #string TaskName -function TASK:SetName( TaskName ) - self.TaskName = TaskName -end - ---- Gets the Name of the Task --- @param #TASK self --- @return #string The Task Name -function TASK:GetName() - return self.TaskName -end - ---- Sets the Type of the Task --- @param #TASK self --- @param #string TaskType -function TASK:SetType( TaskType ) - self.TaskType = TaskType -end - ---- Gets the Type of the Task --- @param #TASK self --- @return #string TaskType -function TASK:GetType() - return self.TaskType -end - ---- Sets the ID of the Task --- @param #TASK self --- @param #string TaskID -function TASK:SetID( TaskID ) - self.TaskID = TaskID -end - ---- Gets the ID of the Task --- @param #TASK self --- @return #string TaskID -function TASK:GetID() - return self.TaskID -end - - ---- Sets a @{Task} to status **Success**. --- @param #TASK self -function TASK:StateSuccess() - self:SetState( self, "State", "Success" ) - return self -end - ---- Is the @{Task} status **Success**. --- @param #TASK self -function TASK:IsStateSuccess() - return self:Is( "Success" ) -end - ---- Sets a @{Task} to status **Failed**. --- @param #TASK self -function TASK:StateFailed() - self:SetState( self, "State", "Failed" ) - return self -end - ---- Is the @{Task} status **Failed**. --- @param #TASK self -function TASK:IsStateFailed() - return self:Is( "Failed" ) -end - ---- Sets a @{Task} to status **Planned**. --- @param #TASK self -function TASK:StatePlanned() - self:SetState( self, "State", "Planned" ) - return self -end - ---- Is the @{Task} status **Planned**. --- @param #TASK self -function TASK:IsStatePlanned() - return self:Is( "Planned" ) -end - ---- Sets a @{Task} to status **Aborted**. --- @param #TASK self -function TASK:StateAborted() - self:SetState( self, "State", "Aborted" ) - return self -end - ---- Is the @{Task} status **Aborted**. --- @param #TASK self -function TASK:IsStateAborted() - return self:Is( "Aborted" ) -end - ---- Sets a @{Task} to status **Cancelled**. --- @param #TASK self -function TASK:StateCancelled() - self:SetState( self, "State", "Cancelled" ) - return self -end - ---- Is the @{Task} status **Cancelled**. --- @param #TASK self -function TASK:IsStateCancelled() - return self:Is( "Cancelled" ) -end - ---- Sets a @{Task} to status **Assigned**. --- @param #TASK self -function TASK:StateAssigned() - self:SetState( self, "State", "Assigned" ) - return self -end - ---- Is the @{Task} status **Assigned**. --- @param #TASK self -function TASK:IsStateAssigned() - return self:Is( "Assigned" ) -end - ---- Sets a @{Task} to status **Hold**. --- @param #TASK self -function TASK:StateHold() - self:SetState( self, "State", "Hold" ) - return self -end - ---- Is the @{Task} status **Hold**. --- @param #TASK self -function TASK:IsStateHold() - return self:Is( "Hold" ) -end - ---- Sets a @{Task} to status **Replanned**. --- @param #TASK self -function TASK:StateReplanned() - self:SetState( self, "State", "Replanned" ) - return self -end - ---- Is the @{Task} status **Replanned**. --- @param #TASK self -function TASK:IsStateReplanned() - return self:Is( "Replanned" ) -end - ---- Gets the @{Task} status. --- @param #TASK self -function TASK:GetStateString() - return self:GetState( self, "State" ) -end - ---- Sets a @{Task} briefing. --- @param #TASK self --- @param #string TaskBriefing --- @return #TASK self -function TASK:SetBriefing( TaskBriefing ) - self.TaskBriefing = TaskBriefing - return self -end - - - - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName ) - - self:E( { "Task Assigned", self.Dispatcher } ) - - self:MessageToGroups( "Task " .. self:GetName() .. " has been assigned to your group." ) - - if self.Dispatcher then - self:E( "Firing Assign event " ) - self.Dispatcher:Assign( self, PlayerUnit, PlayerName ) - end - - self:GetMission():__Start( 1 ) -end - - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onenterSuccess( From, Event, To ) - - self:E( "Task Success" ) - - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is successful! Good job!" ) - self:UnAssignFromGroups() - - self:GetMission():__Complete( 1 ) - -end - - ---- FSM function for a TASK --- @param #TASK self --- @param #string From --- @param #string Event --- @param #string To -function TASK:onenterAborted( From, Event, To ) - - self:E( "Task Aborted" ) - - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has been aborted! Task may be replanned." ) - - self:UnAssignFromGroups() - - self:__Replan( 5 ) -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string From --- @param #string Event --- @param #string To -function TASK:onafterReplan( From, Event, To ) - - self:E( "Task Replanned" ) - - self:GetMission():GetCommandCenter():MessageToCoalition( "Replanning Task " .. self:GetName() .. "." ) - - self:SetMenu() - -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string From --- @param #string Event --- @param #string To -function TASK:onenterFailed( From, Event, To ) - - self:E( "Task Failed" ) - - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has failed!" ) - - self:UnAssignFromGroups() -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onstatechange( From, Event, To ) - - if self:IsTrace() then - MESSAGE:New( "@ Task " .. self.TaskName .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() - end - - if self.Scores[To] then - local Scoring = self:GetScoring() - if Scoring then - self:E( { self.Scores[To].ScoreText, self.Scores[To].Score } ) - Scoring:_AddMissionScore( self.Mission, self.Scores[To].ScoreText, self.Scores[To].Score ) - end - end - -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onenterPlanned( From, Event, To) - if not self.TimeOut == 0 then - self.__TimeOut( self.TimeOut ) - end -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onbeforeTimeOut( From, Event, To ) - if From == "Planned" then - self:RemoveMenu() - return true - end - return false -end - -do -- Dispatcher - - --- Set dispatcher of a task - -- @param #TASK self - -- @param Tasking.DetectionManager#DETECTION_MANAGER Dispatcher - -- @return #TASK - function TASK:SetDispatcher( Dispatcher ) - self.Dispatcher = Dispatcher - end - -end - -do -- Reporting - ---- Create a summary report of the Task. --- List the Task Name and Status --- @param #TASK self --- @return #string -function TASK:ReportSummary() - - local Report = REPORT:New() - - -- List the name of the Task. - local Name = self:GetName() - - -- Determine the status of the Task. - local State = self:GetState() - - Report:Add( "Task " .. Name .. " - State '" .. State ) - - return Report:Text() -end - - ---- Create a detailed report of the Task. --- List the Task Status, and the Players assigned to the Task. --- @param #TASK self --- @return #string -function TASK:ReportDetails() - - local Report = REPORT:New() - - -- List the name of the Task. - local Name = self:GetName() - - -- Determine the status of the Task. - local State = self:GetState() - - -- Loop each Unit active in the Task, and find Player Names. - local PlayerNames = {} - local PlayerReport = REPORT:New( " - Players:" ) - for PlayerGroupID, PlayerGroupData in pairs( self:GetGroups():GetSet() ) do - local PlayerGroup = PlayerGroupData -- Wrapper.Group#GROUP - PlayerNames = PlayerGroup:GetPlayerNames() - if PlayerNames then - PlayerReport:Add( " -- Group " .. PlayerGroup:GetCallsign() .. ": " .. table.concat( PlayerNames, ", " ) ) - end - end - - -- Loop each Process in the Task, and find Reporting Details. - Report:Add( string.format( " - Task %s\n -- State '%s'\n%s", Name, State, PlayerReport:Text() ) ) - return Report:Text() -end - - -end -- Reporting ---- This module contains the DETECTION_MANAGER class and derived classes. --- --- === --- --- 1) @{DetectionManager#DETECTION_MANAGER} class, extends @{Fsm#FSM} --- ==================================================================== --- The @{DetectionManager#DETECTION_MANAGER} class defines the core functions to report detected objects to groups. --- Reportings can be done in several manners, and it is up to the derived classes if DETECTION_MANAGER to model the reporting behaviour. --- --- 1.1) DETECTION_MANAGER constructor: --- ----------------------------------- --- * @{DetectionManager#DETECTION_MANAGER.New}(): Create a new DETECTION_MANAGER instance. --- --- 1.2) DETECTION_MANAGER reporting: --- --------------------------------- --- Derived DETECTION_MANAGER classes will reports detected units using the method @{DetectionManager#DETECTION_MANAGER.ReportDetected}(). This method implements polymorphic behaviour. --- --- The time interval in seconds of the reporting can be changed using the methods @{DetectionManager#DETECTION_MANAGER.SetReportInterval}(). --- To control how long a reporting message is displayed, use @{DetectionManager#DETECTION_MANAGER.SetReportDisplayTime}(). --- Derived classes need to implement the method @{DetectionManager#DETECTION_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report. --- --- Reporting can be started and stopped using the methods @{DetectionManager#DETECTION_MANAGER.StartReporting}() and @{DetectionManager#DETECTION_MANAGER.StopReporting}() respectively. --- If an ad-hoc report is requested, use the method @{DetectionManager#DETECTION_MANAGER#ReportNow}(). --- --- The default reporting interval is every 60 seconds. The reporting messages are displayed 15 seconds. --- --- === --- --- 2) @{DetectionManager#DETECTION_REPORTING} class, extends @{DetectionManager#DETECTION_MANAGER} --- ========================================================================================= --- The @{DetectionManager#DETECTION_REPORTING} class implements detected units reporting. Reporting can be controlled using the reporting methods available in the @{DetectionManager#DETECTION_MANAGER} class. --- --- 2.1) DETECTION_REPORTING constructor: --- ------------------------------- --- The @{DetectionManager#DETECTION_REPORTING.New}() method creates a new DETECTION_REPORTING instance. --- --- --- === --- --- ### Contributions: Mechanist, Prof_Hilactic, FlightControl - Concept & Testing --- ### Author: FlightControl - Framework Design & Programming --- --- @module DetectionManager - -do -- DETECTION MANAGER - - --- DETECTION_MANAGER class. - -- @type DETECTION_MANAGER - -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. - -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. - -- @extends Core.Fsm#FSM - DETECTION_MANAGER = { - ClassName = "DETECTION_MANAGER", - SetGroup = nil, - Detection = nil, - } - - --- FAC constructor. - -- @param #DETECTION_MANAGER self - -- @param Set#SET_GROUP SetGroup - -- @param Functional.Detection#DETECTION_BASE Detection - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:New( SetGroup, Detection ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_MANAGER - - self.SetGroup = SetGroup - self.Detection = Detection - - self:SetStartState( "Stopped" ) - self:AddTransition( "Stopped", "Start", "Started" ) - self:AddTransition( "Started", "Stop", "Stopped" ) - self:AddTransition( "Started", "Report", "Started" ) - - self:SetReportInterval( 30 ) - self:SetReportDisplayTime( 25 ) - - Detection:__Start( 1 ) - - return self - end - - function DETECTION_MANAGER:onafterStart( From, Event, To ) - self:Report() - end - - function DETECTION_MANAGER:onafterReport( From, Event, To ) - - self:E( "onafterReport" ) - - self:__Report( -self._ReportInterval ) - - self:ProcessDetected( self.Detection ) - end - - --- Set the reporting time interval. - -- @param #DETECTION_MANAGER self - -- @param #number ReportInterval The interval in seconds when a report needs to be done. - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:SetReportInterval( ReportInterval ) - self:F2() - - self._ReportInterval = ReportInterval - end - - - --- Set the reporting message display time. - -- @param #DETECTION_MANAGER self - -- @param #number ReportDisplayTime The display time in seconds when a report needs to be done. - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:SetReportDisplayTime( ReportDisplayTime ) - self:F2() - - self._ReportDisplayTime = ReportDisplayTime - end - - --- Get the reporting message display time. - -- @param #DETECTION_MANAGER self - -- @return #number ReportDisplayTime The display time in seconds when a report needs to be done. - function DETECTION_MANAGER:GetReportDisplayTime() - self:F2() - - return self._ReportDisplayTime - end - - --- Reports the detected items to the @{Set#SET_GROUP}. - -- @param #DETECTION_MANAGER self - -- @param Functional.Detection#DETECTION_BASE Detection - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:ProcessDetected( Detection ) - self:E() - - end - -end - - -do -- DETECTION_REPORTING - - --- DETECTION_REPORTING class. - -- @type DETECTION_REPORTING - -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. - -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. - -- @extends #DETECTION_MANAGER - DETECTION_REPORTING = { - ClassName = "DETECTION_REPORTING", - } - - - --- DETECTION_REPORTING constructor. - -- @param #DETECTION_REPORTING self - -- @param Set#SET_GROUP SetGroup - -- @param Functional.Detection#DETECTION_AREAS Detection - -- @return #DETECTION_REPORTING self - function DETECTION_REPORTING:New( SetGroup, Detection ) - - -- Inherits from DETECTION_MANAGER - local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #DETECTION_REPORTING - - self:Schedule( 1, 30 ) - return self - end - - --- Creates a string of the detected items in a @{Detection}. - -- @param #DETECTION_MANAGER self - -- @param Set#SET_UNIT DetectedSet The detected Set created by the @{Detection#DETECTION_BASE} object. - -- @return #DETECTION_MANAGER self - function DETECTION_REPORTING:GetDetectedItemsText( DetectedSet ) - self:F2() - - local MT = {} -- Message Text - local UnitTypes = {} - - for DetectedUnitID, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - if DetectedUnit:IsAlive() then - local UnitType = DetectedUnit:GetTypeName() - - if not UnitTypes[UnitType] then - UnitTypes[UnitType] = 1 - else - UnitTypes[UnitType] = UnitTypes[UnitType] + 1 - end - end - end - - for UnitTypeID, UnitType in pairs( UnitTypes ) do - MT[#MT+1] = UnitType .. " of " .. UnitTypeID - end - - return table.concat( MT, ", " ) - end - - - - --- Reports the detected items to the @{Set#SET_GROUP}. - -- @param #DETECTION_REPORTING self - -- @param Wrapper.Group#GROUP Group The @{Group} object to where the report needs to go. - -- @param Functional.Detection#DETECTION_AREAS Detection The detection created by the @{Detection#DETECTION_BASE} object. - -- @return #boolean Return true if you want the reporting to continue... false will cancel the reporting loop. - function DETECTION_REPORTING:ProcessDetected( Group, Detection ) - self:F2( Group ) - - self:E( Group ) - local DetectedMsg = {} - for DetectedAreaID, DetectedAreaData in pairs( Detection:GetDetectedAreas() ) do - local DetectedArea = DetectedAreaData -- Functional.Detection#DETECTION_AREAS.DetectedArea - DetectedMsg[#DetectedMsg+1] = " - Group #" .. DetectedAreaID .. ": " .. self:GetDetectedItemsText( DetectedArea.Set ) - end - local FACGroup = Detection:GetDetectionGroups() - FACGroup:MessageToGroup( "Reporting detected target groups:\n" .. table.concat( DetectedMsg, "\n" ), self:GetReportDisplayTime(), Group ) - - return true - end - -end - ---- **Tasking** - The TASK_A2G_DISPATCHER creates and manages player TASK_A2G tasks based on detected targets. --- --- === --- --- # 1) @{#TASK_A2G_DISPATCHER} class, extends @{#DETECTION_MANAGER} --- --- The @{#TASK_A2G_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups). --- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. --- Find a summary below describing for which situation a task type is created: --- --- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter. --- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter. --- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars. --- --- Other task types will follow... --- --- 3.1) TASK_A2G_DISPATCHER constructor: --- -------------------------------------- --- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK_A2G_DISPATCHER instance. --- --- === --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-09: Initial class and API. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module Task_A2G_Dispatcher - -do -- TASK_A2G_DISPATCHER - - --- TASK_A2G_DISPATCHER class. - -- @type TASK_A2G_DISPATCHER - -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. - -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. - -- @field Tasking.Mission#MISSION Mission - -- @extends Tasking.DetectionManager#DETECTION_MANAGER - TASK_A2G_DISPATCHER = { - ClassName = "TASK_A2G_DISPATCHER", - Mission = nil, - Detection = nil, - } - - - --- TASK_A2G_DISPATCHER constructor. - -- @param #TASK_A2G_DISPATCHER self - -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. - -- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. - -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. - -- @return #TASK_A2G_DISPATCHER self - function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection ) - - -- Inherits from DETECTION_MANAGER - local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER - - self.Detection = Detection - self.Mission = Mission - - self:AddTransition( "Started", "Assign", "Started" ) - - --- OnAfter Transition Handler for Event Assign. - -- @function [parent=#TASK_A2G_DISPATCHER] OnAfterAssign - -- @param #TASK_A2G_DISPATCHER self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Tasking.Task_A2G#TASK_A2G Task - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param #string PlayerName - - self:__Start( 5 ) - - return self - end - - - --- Creates a SEAD task when there are targets for it. - -- @param #TASK_A2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem - -- @return Set#SET_UNIT TargetSetUnit: The target set of units. - -- @return #nil If there are no targets to be set. - function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem ) - self:F( { DetectedItem.ItemID } ) - - local DetectedSet = DetectedItem.Set - local DetectedZone = DetectedItem.Zone - - -- Determine if the set has radar targets. If it does, construct a SEAD task. - local RadarCount = DetectedSet:HasSEAD() - - if RadarCount > 0 then - - -- Here we're doing something advanced... We're copying the DetectedSet, but making a new Set only with SEADable Radar units in it. - local TargetSetUnit = SET_UNIT:New() - TargetSetUnit:SetDatabase( DetectedSet ) - TargetSetUnit:FilterHasSEAD() - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Creates a CAS task when there are targets for it. - -- @param #TASK_A2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem - -- @return Tasking.Task#TASK - function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem ) - self:F( { DetectedItem.ItemID } ) - - local DetectedSet = DetectedItem.Set - local DetectedZone = DetectedItem.Zone - - - -- Determine if the set has radar targets. If it does, construct a SEAD task. - local GroundUnitCount = DetectedSet:HasGroundUnits() - local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) - - if GroundUnitCount > 0 and FriendliesNearBy == true then - - -- Copy the Set - local TargetSetUnit = SET_UNIT:New() - TargetSetUnit:SetDatabase( DetectedSet ) - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Creates a BAI task when there are targets for it. - -- @param #TASK_A2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem - -- @return Tasking.Task#TASK - function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition ) - self:F( { DetectedItem.ItemID } ) - - local DetectedSet = DetectedItem.Set - local DetectedZone = DetectedItem.Zone - - - -- Determine if the set has radar targets. If it does, construct a SEAD task. - local GroundUnitCount = DetectedSet:HasGroundUnits() - local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem ) - - if GroundUnitCount > 0 and FriendliesNearBy == false then - - -- Copy the Set - local TargetSetUnit = SET_UNIT:New() - TargetSetUnit:SetDatabase( DetectedSet ) - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Evaluates the removal of the Task from the Mission. - -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". - -- @param #TASK_A2G_DISPATCHER self - -- @param Tasking.Mission#MISSION Mission - -- @param Tasking.Task#TASK Task - -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem - -- @return Tasking.Task#TASK - function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItem ) - - if Task then - if Task:IsStatePlanned() and DetectedItem.Changed == true then - self:E( "Removing Tasking: " .. Task:GetTaskName() ) - Task = Mission:RemoveTask( Task ) - end - end - - return Task - end - - - --- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}. - -- @param #TASK_A2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. - -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. - function TASK_A2G_DISPATCHER:ProcessDetected( Detection ) - self:E() - - local AreaMsg = {} - local TaskMsg = {} - local ChangeMsg = {} - - local Mission = self.Mission - local ReportSEAD = REPORT:New( "- SEAD Tasks:") - local ReportCAS = REPORT:New( "- CAS Tasks:") - local ReportBAI = REPORT:New( "- BAI Tasks:") - local ReportChanges = REPORT:New( " - Changes:" ) - - --- First we need to the detected targets. - for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do - - local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem - local DetectedSet = DetectedItem.Set -- Functional.Detection#DETECTION_BASE.DetectedSet - local DetectedZone = DetectedItem.Zone - self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) - DetectedSet:Flush() - - local ItemID = DetectedItem.ItemID - - -- Evaluate SEAD Tasking - local SEADTask = Mission:GetTask( string.format( "SEAD.%03d", ItemID ) ) - SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedItem ) - if not SEADTask then - local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - local Task = TASK_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", ItemID ), TargetSetUnit ) - Task:SetTargetZone( DetectedZone ) - Task:SetDispatcher( self ) - SEADTask = Mission:AddTask( Task ) - end - end - if SEADTask and SEADTask:IsStatePlanned() then - ReportSEAD:Add( string.format( " - %s.%02d - %s", "SEAD", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) - end - - -- Evaluate CAS Tasking - local CASTask = Mission:GetTask( string.format( "CAS.%03d", ItemID ) ) - CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedItem ) - if not CASTask then - local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - local Task = TASK_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", ItemID ), TargetSetUnit ) - Task:SetTargetZone( DetectedZone ) - Task:SetDispatcher( self ) - CASTask = Mission:AddTask( Task ) - end - end - if CASTask and CASTask:IsStatePlanned() then - ReportCAS:Add( string.format( " - %s.%02d - %s", "CAS", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) - end - - -- Evaluate BAI Tasking - local BAITask = Mission:GetTask( string.format( "BAI.%03d", ItemID ) ) - BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedItem ) - if not BAITask then - local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - local Task = TASK_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", ItemID ), TargetSetUnit ) - Task:SetTargetZone( DetectedZone ) - Task:SetDispatcher( self ) - BAITask = Mission:AddTask( Task ) - end - end - if BAITask and BAITask:IsStatePlanned() then - ReportBAI:Add( string.format( " - %s.%02d - %s", "BAI", ItemID, Detection:DetectedItemReportSummary(DetectedItemID) ) ) - end - - - -- Loop through the changes ... - local ChangeText = Detection:GetChangeText( DetectedItem ) - ReportChanges:Add( ChangeText ) - - - -- OK, so the tasking has been done, now delete the changes reported for the area. - Detection:AcceptChanges( DetectedItem ) - - end - - -- TODO set menus using the HQ coordinator - Mission:GetCommandCenter():SetMenu() - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if not TaskGroup:GetState( TaskGroup, "Assigned" ) then - Mission:GetCommandCenter():MessageToGroup( - string.format( "HQ Reporting - Planned tasks for mission '%s':\n%s\n", - self.Mission:GetName(), - string.format( "%s\n\n%s\n\n%s\n\n%s", ReportSEAD:Text(), ReportCAS:Text(), ReportBAI:Text(), ReportChanges:Text() - ) - ), TaskGroup - ) - end - end - - return true - end - -end--- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements. --- --- ![Banner Image](..\Presentations\TASK_A2G\Dia1.JPG) --- --- --- # 1) @{Task_A2G#TASK_A2G} class, extends @{Task#TASK} --- --- The @{#TASK_A2G} class defines Air To Ground tasks for a @{Set} of Target Units, --- based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_A2G is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: --- --- * **None**: Start of the process --- * **Planned**: The A2G task is planned. --- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}. --- * **Success**: The A2G task is successfully completed. --- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. --- --- # 1.1) Set the scoring of achievements in an A2G attack. --- --- Scoring or penalties can be given in the following circumstances: --- --- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed. --- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed. --- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed. --- --- # 2) @{Task_A2G#TASK_SEAD} class, extends @{Task_A2G#TASK_A2G} --- --- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units. --- --- === --- --- # 3) @{Task_A2G#TASK_CAS} class, extends @{Task_A2G#TASK_A2G} --- --- The @{#TASK_CAS} class defines a CAS task for a @{Set} of Target Units. --- --- === --- --- # 4) @{Task_A2G#TASK_BAI} class, extends @{Task_A2G#TASK_A2G} --- --- The @{#TASK_BAI} class defines a BAI task for a @{Set} of Target Units. --- --- ==== --- --- # **API CHANGE HISTORY** --- --- The underlying change log documents the API changes. Please read this carefully. The following notation is used: --- --- * **Added** parts are expressed in bold type face. --- * _Removed_ parts are expressed in italic type face. --- --- Hereby the change log: --- --- 2017-03-09: Revised version. --- --- === --- --- # **AUTHORS and CONTRIBUTIONS** --- --- ### Contributions: --- --- * **[WingThor]**: Concept, Advice & Testing. --- --- ### Authors: --- --- * **FlightControl**: Concept, Design & Programming. --- --- @module Task_A2G - -do -- TASK_A2G - - --- The TASK_A2G class - -- @type TASK_A2G - -- @field Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - TASK_A2G = { - ClassName = "TASK_A2G", - } - - --- Instantiates a new TASK_A2G. - -- @param #TASK_A2G self - -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. - -- @return #TASK_A2G self - function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) ) -- Tasking.Task#TASK_A2G - self:F() - - self.TargetSetUnit = TargetSetUnit - self.TaskType = TaskType - - Mission:AddTask( self ) - - local Fsm = self:GetUnitProcess() - - - Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } ) - - Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) - Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) - Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) - - Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" ) - - Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" ) - Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" ) - - Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, self.TaskType ), { Accounted = "Success" } ) - Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" ) - Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} ) - Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} ) - Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" ) - - Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) - Fsm:AddTransition( "Accounted", "Success", "Success" ) - Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) - Fsm:AddTransition( "Failed", "Fail", "Failed" ) - - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_A2G#TASK_A2G Task - function Fsm:onafterRouteToRendezVous( TaskUnit, Task ) - self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - -- Determine the first Unit from the self.RendezVousSetUnit - - if Task:GetRendezVousZone( TaskUnit ) then - self:__RouteToRendezVousZone( 0.1 ) - else - if Task:GetRendezVousPointVec2( TaskUnit ) then - self:__RouteToRendezVousPoint( 0.1 ) - else - self:__ArriveAtRendezVous( 0.1 ) - end - end - end - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task#TASK_A2G Task - function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task ) - self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - -- Determine the first Unit from the self.TargetSetUnit - - self:__Engage( 0.1 ) - end - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task#TASK_A2G Task - function Fsm:onafterEngage( TaskUnit, Task ) - self:E( { self } ) - self:__Account( 0.1 ) - self:__RouteToTarget(0.1 ) - self:__RouteToTargets( -10 ) - end - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_A2G#TASK_A2G Task - function Fsm:onafterRouteToTarget( TaskUnit, Task ) - self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - -- Determine the first Unit from the self.TargetSetUnit - - if Task:GetTargetZone( TaskUnit ) then - self:__RouteToTargetZone( 0.1 ) - else - local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT - if TargetUnit then - local PointVec2 = TargetUnit:GetPointVec2() - self:T( { TargetPointVec2 = PointVec2, PointVec2:GetX(), PointVec2:GetAlt(), PointVec2:GetZ() } ) - Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit ) - end - self:__RouteToTargetPoint( 0.1 ) - end - end - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_A2G#TASK_A2G Task - function Fsm:onafterRouteToTargets( TaskUnit, Task ) - self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT - if TargetUnit then - Task:SetTargetPointVec2( TargetUnit:GetPointVec2(), TaskUnit ) - end - self:__RouteToTargets( -10 ) - end - - return self - - end - - --- @param #TASK_A2G self - function TASK_A2G:GetPlannedMenuText() - return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" - end - - --- @param #TASK_A2G self - -- @param Core.Point#POINT_VEC2 RendezVousPointVec2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map. - -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. - -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_A2G:SetRendezVousPointVec2( RendezVousPointVec2, RendezVousRange, TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - ActRouteRendezVous:SetPointVec2( RendezVousPointVec2 ) - ActRouteRendezVous:SetRange( RendezVousRange ) - end - - --- @param #TASK_A2G self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Point#POINT_VEC2 The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map. - -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. - function TASK_A2G:GetRendezVousPointVec2( TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - return ActRouteRendezVous:GetPointVec2(), ActRouteRendezVous:GetRange() - end - - - - --- @param #TASK_A2G self - -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. - -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE - ActRouteRendezVous:SetZone( RendezVousZone ) - end - - --- @param #TASK_A2G self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map. - function TASK_A2G:GetRendezVousZone( TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE - return ActRouteRendezVous:GetZone() - end - - --- @param #TASK_A2G self - -- @param Core.Point#POINT_VEC2 TargetPointVec2 The PointVec2 object where the Target is located on the map. - -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_A2G:SetTargetPointVec2( TargetPointVec2, TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - ActRouteTarget:SetPointVec2( TargetPointVec2 ) - end - - - --- @param #TASK_A2G self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Point#POINT_VEC2 The PointVec2 object where the Target is located on the map. - function TASK_A2G:GetTargetPointVec2( TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - return ActRouteTarget:GetPointVec2() - end - - - --- @param #TASK_A2G self - -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. - -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_A2G:SetTargetZone( TargetZone, TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE - ActRouteTarget:SetZone( TargetZone ) - end - - - --- @param #TASK_A2G self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. - function TASK_A2G:GetTargetZone( TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE - return ActRouteTarget:GetZone() - end - - --- Set a score when a target in scope of the A2G attack, has been destroyed . - -- @param #TASK_A2G self - -- @param #string Text The text to display to the player, when the target has been destroyed. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G - function TASK_A2G:SetScoreOnDestroy( Text, Score, TaskUnit ) - self:F( { Text, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScoreProcess( "Engaging", "Account", "Account", Text, Score ) - - return self - end - - --- Set a score when all the targets in scope of the A2G attack, have been destroyed. - -- @param #TASK_A2G self - -- @param #string Text The text to display to the player, when all targets hav been destroyed. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G - function TASK_A2G:SetScoreOnSuccess( Text, Score, TaskUnit ) - self:F( { Text, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Success", Text, Score ) - - return self - end - - --- Set a penalty when the A2G attack has failed. - -- @param #TASK_A2G self - -- @param #string Text The text to display to the player, when the A2G attack has failed. - -- @param #number Penalty The penalty in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G - function TASK_A2G:SetPenaltyOnFailed( Text, Penalty, TaskUnit ) - self:F( { Text, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Failed", Text, Penalty ) - - return self - end - - -end - - -do -- TASK_SEAD - - --- The TASK_SEAD class - -- @type TASK_SEAD - -- @field Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - TASK_SEAD = { - ClassName = "TASK_SEAD", - } - - --- Instantiates a new TASK_SEAD. - -- @param #TASK_SEAD self - -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. - -- @return #TASK_SEAD self - function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit ) - local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD" ) ) -- #TASK_SEAD - self:F() - - return self - end - -end - -do -- TASK_BAI - - --- The TASK_BAI class - -- @type TASK_BAI - -- @field Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - TASK_BAI = { - ClassName = "TASK_BAI", - } - - --- Instantiates a new TASK_BAI. - -- @param #TASK_BAI self - -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. - -- @return #TASK_BAI self - function TASK_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit ) - local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI" ) ) -- #TASK_BAI - self:F() - - return self - end - -end - -do -- TASK_CAS - - --- The TASK_CAS class - -- @type TASK_CAS - -- @field Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - TASK_CAS = { - ClassName = "TASK_CAS", - } - - --- Instantiates a new TASK_CAS. - -- @param #TASK_CAS self - -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets - -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. - -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. - -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. - -- @return #TASK_CAS self - function TASK_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit ) - local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS" ) ) -- #TASK_CAS - self:F() - - return self - end - -end ---- The main include file for the MOOSE system. --- Test of permissions - ---- Core Routines -Include.File( "Utilities/Routines" ) -Include.File( "Utilities/Utils" ) - ---- Core Classes -Include.File( "Core/Base" ) -Include.File( "Core/Scheduler" ) -Include.File( "Core/ScheduleDispatcher") -Include.File( "Core/Event" ) -Include.File( "Core/Menu" ) -Include.File( "Core/Zone" ) -Include.File( "Core/Database" ) -Include.File( "Core/Set" ) -Include.File( "Core/Point" ) -Include.File( "Core/Message" ) -Include.File( "Core/Fsm" ) -Include.File( "Core/Radio" ) - ---- Wrapper Classes -Include.File( "Wrapper/Object" ) -Include.File( "Wrapper/Identifiable" ) -Include.File( "Wrapper/Positionable" ) -Include.File( "Wrapper/Controllable" ) -Include.File( "Wrapper/Group" ) -Include.File( "Wrapper/Unit" ) -Include.File( "Wrapper/Client" ) -Include.File( "Wrapper/Static" ) -Include.File( "Wrapper/Airbase" ) -Include.File( "Wrapper/Scenery" ) - ---- Functional Classes -Include.File( "Functional/Scoring" ) -Include.File( "Functional/CleanUp" ) -Include.File( "Functional/Spawn" ) -Include.File( "Functional/Movement" ) -Include.File( "Functional/Sead" ) -Include.File( "Functional/Escort" ) -Include.File( "Functional/MissileTrainer" ) -Include.File( "Functional/AirbasePolice" ) -Include.File( "Functional/Detection" ) - ---- AI Classes -Include.File( "AI/AI_Balancer" ) -Include.File( "AI/AI_Patrol" ) -Include.File( "AI/AI_Cap" ) -Include.File( "AI/AI_Cas" ) -Include.File( "AI/AI_Cargo" ) - ---- Actions -Include.File( "Actions/Act_Assign" ) -Include.File( "Actions/Act_Route" ) -Include.File( "Actions/Act_Account" ) -Include.File( "Actions/Act_Assist" ) - ---- Task Handling Classes -Include.File( "Tasking/CommandCenter" ) -Include.File( "Tasking/Mission" ) -Include.File( "Tasking/Task" ) -Include.File( "Tasking/DetectionManager" ) -Include.File( "Tasking/Task_A2G_Dispatcher") -Include.File( "Tasking/Task_A2G" ) - - --- The order of the declarations is important here. Don't touch it. - ---- Declare the event dispatcher based on the EVENT class -_EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT - ---- Declare the timer dispatcher based on the SCHEDULEDISPATCHER class -_SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.Timer#SCHEDULEDISPATCHER - ---- Declare the main database object, which is used internally by the MOOSE classes. -_DATABASE = DATABASE:New() -- Database#DATABASE - - - - -BASE:TraceOnOff( false ) -env.info( '*** MOOSE INCLUDE END *** ' ) + env.info( "Moose: " .. IncludeFile .. " dynamically loaded from " .. __Moose.ProgramPath ) + return f() + end + end +end + +__Moose.ProgramPath = "Scripts/Moose/" + +__Moose.Includes = {} +__Moose.Include( 'Utilities/Routines.lua' ) +__Moose.Include( 'Utilities/Utils.lua' ) +__Moose.Include( 'Core/Base.lua' ) +__Moose.Include( 'Core/Report.lua' ) +__Moose.Include( 'Core/Scheduler.lua' ) +__Moose.Include( 'Core/ScheduleDispatcher.lua' ) +__Moose.Include( 'Core/Event.lua' ) +__Moose.Include( 'Core/Settings.lua' ) +__Moose.Include( 'Core/Menu.lua' ) +__Moose.Include( 'Core/Zone.lua' ) +__Moose.Include( 'Core/Database.lua' ) +__Moose.Include( 'Core/Set.lua' ) +__Moose.Include( 'Core/Point.lua' ) +__Moose.Include( 'Core/Message.lua' ) +__Moose.Include( 'Core/Fsm.lua' ) +__Moose.Include( 'Core/Radio.lua' ) +__Moose.Include( 'Core/SpawnStatic.lua' ) +__Moose.Include( 'Core/Cargo.lua' ) +__Moose.Include( 'Core/Spot.lua' ) +__Moose.Include( 'Wrapper/Object.lua' ) +__Moose.Include( 'Wrapper/Identifiable.lua' ) +__Moose.Include( 'Wrapper/Positionable.lua' ) +__Moose.Include( 'Wrapper/Controllable.lua' ) +__Moose.Include( 'Wrapper/Group.lua' ) +__Moose.Include( 'Wrapper/Unit.lua' ) +__Moose.Include( 'Wrapper/Client.lua' ) +__Moose.Include( 'Wrapper/Static.lua' ) +__Moose.Include( 'Wrapper/Airbase.lua' ) +__Moose.Include( 'Wrapper/Scenery.lua' ) +__Moose.Include( 'Functional/Scoring.lua' ) +__Moose.Include( 'Functional/CleanUp.lua' ) +__Moose.Include( 'Functional/Spawn.lua' ) +__Moose.Include( 'Functional/Movement.lua' ) +__Moose.Include( 'Functional/Sead.lua' ) +__Moose.Include( 'Functional/Escort.lua' ) +__Moose.Include( 'Functional/MissileTrainer.lua' ) +__Moose.Include( 'Functional/AirbasePolice.lua' ) +__Moose.Include( 'Functional/Detection.lua' ) +__Moose.Include( 'Functional/Designate.lua' ) +__Moose.Include( 'AI/AI_Balancer.lua' ) +__Moose.Include( 'AI/AI_A2A.lua' ) +__Moose.Include( 'AI/AI_A2A_Patrol.lua' ) +__Moose.Include( 'AI/AI_A2A_Cap.lua' ) +__Moose.Include( 'AI/AI_A2A_Gci.lua' ) +__Moose.Include( 'AI/AI_A2A_Dispatcher.lua' ) +__Moose.Include( 'AI/AI_Patrol.lua' ) +__Moose.Include( 'AI/AI_Cap.lua' ) +__Moose.Include( 'AI/AI_Cas.lua' ) +__Moose.Include( 'AI/AI_Bai.lua' ) +__Moose.Include( 'AI/AI_Formation.lua' ) +__Moose.Include( 'Actions/Act_Assign.lua' ) +__Moose.Include( 'Actions/Act_Route.lua' ) +__Moose.Include( 'Actions/Act_Account.lua' ) +__Moose.Include( 'Actions/Act_Assist.lua' ) +__Moose.Include( 'Tasking/CommandCenter.lua' ) +__Moose.Include( 'Tasking/Mission.lua' ) +__Moose.Include( 'Tasking/Task.lua' ) +__Moose.Include( 'Tasking/DetectionManager.lua' ) +__Moose.Include( 'Tasking/Task_A2G_Dispatcher.lua' ) +__Moose.Include( 'Tasking/Task_A2G.lua' ) +__Moose.Include( 'Tasking/Task_A2A_Dispatcher.lua' ) +__Moose.Include( 'Tasking/Task_A2A.lua' ) +__Moose.Include( 'Tasking/Task_Cargo.lua' ) +__Moose.Include( 'Moose.lua' ) +BASE:TraceOnOff( true ) +env.info( '*** MOOSE INCLUDE END *** ' ) diff --git a/Moose Mission Setup/Moose_Create.bat b/Moose Mission Setup/Moose_Create.bat deleted file mode 100644 index 5f2979007..000000000 --- a/Moose Mission Setup/Moose_Create.bat +++ /dev/null @@ -1,113 +0,0 @@ -ECHO OFF - -REM Create Moose.lua File - -ECHO Path to Moose *.lua files: %1 -ECHO Current Date: %2 -ECHO Path to Update Missions: %3 -ECHO Dynamic or Static: %4 - -DEL Moose.lua - -IF %4 == D GOTO Dynamic -IF %4 == S GOTO Static - -GOTO End - -:Dynamic - -ECHO Dynamic Moose.lua - -REM Create a timestamp with is logged in the DCS.log file. -ECHO env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' ) > Moose.lua -ECHO env.info( 'Moose Generation Timestamp: %2' ) >> Moose.lua - -COPY /b Moose.lua + "Moose Create Dynamic\Moose_Dynamic_Loader.lua" Moose.lua -COPY /b Moose.lua + "Moose Create Dynamic\Moose_Trace_On.lua" Moose.lua - -GOTO End - -:Static - -ECHO Static Moose.lua - -REM Create a timestamp with is logged in the DCS.log file. -ECHO env.info( '*** MOOSE STATIC INCLUDE START *** ' ) > Moose.lua -ECHO env.info( 'Moose Generation Timestamp: %2' ) >> Moose.lua - -COPY /b Moose.lua + "Moose Create Static\Moose_Static_Loader.lua" Moose.lua - - -rem Core Routines -COPY /b Moose.lua + %1\Utilities\Routines.lua Moose.lua -COPY /b Moose.lua + %1\Utilities\Utils.lua Moose.lua - -rem Core Classes -COPY /b Moose.lua + %1\Core\Base.lua Moose.lua -COPY /b Moose.lua + %1\Core\Scheduler.lua Moose.lua -COPY /b Moose.lua + %1\Core\ScheduleDispatcher.lua Moose.lua -COPY /b Moose.lua + %1\Core\Event.lua Moose.lua -COPY /b Moose.lua + %1\Core\Menu.lua Moose.lua -COPY /b Moose.lua + %1\Core\Zone.lua Moose.lua -COPY /b Moose.lua + %1\Core\Database.lua Moose.lua -COPY /b Moose.lua + %1\Core\Set.lua Moose.lua -COPY /b Moose.lua + %1\Core\Point.lua Moose.lua -COPY /b Moose.lua + %1\Core\Message.lua Moose.lua -COPY /b Moose.lua + %1\Core\Fsm.lua Moose.lua -COPY /b Moose.lua + %1\Core\Radio.lua Moose.lua - -rem Wrapper Classes -COPY /b Moose.lua + %1\Wrapper\Object.lua Moose.lua -COPY /b Moose.lua + %1\Wrapper\Identifiable.lua Moose.lua -COPY /b Moose.lua + %1\Wrapper\Positionable.lua Moose.lua -COPY /b Moose.lua + %1\Wrapper\Controllable.lua Moose.lua -COPY /b Moose.lua + %1\Wrapper\Group.lua Moose.lua -COPY /b Moose.lua + %1\Wrapper\Unit.lua Moose.lua -COPY /b Moose.lua + %1\Wrapper\Client.lua Moose.lua -COPY /b Moose.lua + %1\Wrapper\Static.lua Moose.lua -COPY /b Moose.lua + %1\Wrapper\Airbase.lua Moose.lua -COPY /b Moose.lua + %1\Wrapper\Scenery.lua Moose.lua - -rem Functional Classes -COPY /b Moose.lua + %1\Functional\Scoring.lua Moose.lua -COPY /b Moose.lua + %1\Functional\CleanUp.lua Moose.lua -COPY /b Moose.lua + %1\Functional\Spawn.lua Moose.lua -COPY /b Moose.lua + %1\Functional\Movement.lua Moose.lua -COPY /b Moose.lua + %1\Functional\Sead.lua Moose.lua -COPY /b Moose.lua + %1\Functional\Escort.lua Moose.lua -COPY /b Moose.lua + %1\Functional\MissileTrainer.lua Moose.lua -COPY /b Moose.lua + %1\Functional\AirbasePolice.lua Moose.lua -COPY /b Moose.lua + %1\Functional\Detection.lua Moose.lua - -rem AI Classes -COPY /b Moose.lua + %1\AI\AI_Balancer.lua Moose.lua -COPY /b Moose.lua + %1\AI\AI_Patrol.lua Moose.lua -COPY /b Moose.lua + %1\AI\AI_Cas.lua Moose.lua -COPY /b Moose.lua + %1\AI\AI_Cap.lua Moose.lua -COPY /b Moose.lua + %1\AI\AI_Cargo.lua Moose.lua - - -rem Actions -COPY /b Moose.lua + %1\Actions\Act_Assign.lua Moose.lua -COPY /b Moose.lua + %1\Actions\Act_Route.lua Moose.lua -COPY /b Moose.lua + %1\Actions\Act_Account.lua Moose.lua -COPY /b Moose.lua + %1\Actions\Act_Assist.lua Moose.lua - -rem Task Handling Classes -COPY /b Moose.lua + %1\Tasking\CommandCenter.lua Moose.lua -COPY /b Moose.lua + %1\Tasking\Mission.lua Moose.lua -COPY /b Moose.lua + %1\Tasking\Task.lua Moose.lua -COPY /b Moose.lua + %1\Tasking\DetectionManager.lua Moose.lua -COPY /b Moose.lua + %1\Tasking\Task_A2G_Dispatcher.lua Moose.lua -COPY /b Moose.lua + %1\Tasking\Task_A2G.lua Moose.lua - -COPY /b Moose.lua + %1\Moose.lua Moose.lua - -COPY /b Moose.lua + "Moose Create Static\Moose_Trace_Off.lua" Moose.lua - -GOTO End - -:End - -ECHO env.info( '*** MOOSE INCLUDE END *** ' ) >> Moose.lua -COPY Moose.lua %3 diff --git a/Moose Mission Setup/Moose_Create.lua b/Moose Mission Setup/Moose_Create.lua new file mode 100644 index 000000000..be10d3aac --- /dev/null +++ b/Moose Mission Setup/Moose_Create.lua @@ -0,0 +1,78 @@ +-- This routine is called from the LDT environment to create the Moose.lua file stub for use in .miz files. + +local MooseDynamicStatic = arg[1] +local MooseDate = arg[2] +local MooseDevelopmentPath = arg[3] +local MooseSetupPath = arg[4] + +print( "Moose (D)ynamic (S)tatic : " .. MooseDynamicStatic ) +print( "Current Date : " .. MooseDate ) +print( "Moose development path : " .. MooseDevelopmentPath ) +print( "Moose setup path : " .. MooseSetupPath ) + +local MooseSourcesFilePath = MooseSetupPath .. "/Moose.files" +local MooseFilePath = MooseSetupPath .. "/Moose.lua" + +print( "Reading Moose source list : " .. MooseSourcesFilePath ) + +local MooseFile = io.open( MooseFilePath, "w" ) + +if MooseDynamicStatic == "D" then + MooseFile:write( "env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' )\n" ) +end +if MooseDynamicStatic == "S" then + MooseFile:write( "env.info( '*** MOOSE STATIC INCLUDE START *** ' )\n" ) +end + +MooseFile:write( "env.info( 'Moose Generation Timestamp: " .. MooseDate .. "' )\n" ) + +local MooseLoaderPath +if MooseDynamicStatic == "D" then + MooseLoaderPath = MooseSetupPath .. "/Moose Create Dynamic/Moose_Dynamic_Loader.lua" +end +if MooseDynamicStatic == "S" then + MooseLoaderPath = MooseSetupPath .. "/Moose Create Static/Moose_Static_Loader.lua" +end + +local MooseLoader = io.open( MooseLoaderPath, "r" ) +local MooseLoaderText = MooseLoader:read( "*a" ) +MooseLoader:close() + +MooseFile:write( MooseLoaderText ) + + +local MooseSourcesFile = io.open( MooseSourcesFilePath, "r" ) +local MooseSource = MooseSourcesFile:read("*l") + +while( MooseSource ) do + + if MooseSource ~= "" then + local MooseFilePath = MooseDevelopmentPath .. "/" .. MooseSource + if MooseDynamicStatic == "D" then + print( "Load dynamic: " .. MooseSource ) + MooseFile:write( "__Moose.Include( '" .. MooseSource .. "' )\n" ) + end + if MooseDynamicStatic == "S" then + print( "Load static: " .. MooseSource ) + local MooseSourceFile = io.open( MooseFilePath, "r" ) + local MooseSourceFileText = MooseSourceFile:read( "*a" ) + MooseSourceFile:close() + + MooseFile:write( MooseSourceFileText ) + end + end + + MooseSource = MooseSourcesFile:read("*l") +end + +if MooseDynamicStatic == "D" then + MooseFile:write( "BASE:TraceOnOff( true )\n" ) +end +if MooseDynamicStatic == "S" then + MooseFile:write( "BASE:TraceOnOff( false )\n" ) +end + +MooseFile:write( "env.info( '*** MOOSE INCLUDE END *** ' )\n" ) + +MooseSourcesFile:close() +MooseFile:close() diff --git a/Moose Presentations/ACT_ACCOUNT.pptx b/Moose Presentations/ACT_ACCOUNT.pptx deleted file mode 100644 index 019ae69b7..000000000 Binary files a/Moose Presentations/ACT_ACCOUNT.pptx and /dev/null differ diff --git a/Moose Presentations/AI_BALANCER.pptx b/Moose Presentations/AI_BALANCER.pptx deleted file mode 100644 index 39ba7e6a5..000000000 Binary files a/Moose Presentations/AI_BALANCER.pptx and /dev/null differ diff --git a/Moose Presentations/AI_CAP.pptx b/Moose Presentations/AI_CAP.pptx deleted file mode 100644 index 17395cd37..000000000 Binary files a/Moose Presentations/AI_CAP.pptx and /dev/null differ diff --git a/Moose Presentations/AI_CAS.pptx b/Moose Presentations/AI_CAS.pptx deleted file mode 100644 index 07e718c00..000000000 Binary files a/Moose Presentations/AI_CAS.pptx and /dev/null differ diff --git a/Moose Presentations/AI_PATROL.pptx b/Moose Presentations/AI_PATROL.pptx deleted file mode 100644 index a7a9ff112..000000000 Binary files a/Moose Presentations/AI_PATROL.pptx and /dev/null differ diff --git a/Moose Presentations/BASE.pptx b/Moose Presentations/BASE.pptx deleted file mode 100644 index c2e953d1f..000000000 Binary files a/Moose Presentations/BASE.pptx and /dev/null differ diff --git a/Moose Presentations/CARGO.pptx b/Moose Presentations/CARGO.pptx deleted file mode 100644 index 617704b83..000000000 Binary files a/Moose Presentations/CARGO.pptx and /dev/null differ diff --git a/Moose Presentations/DETECTION.pptx b/Moose Presentations/DETECTION.pptx deleted file mode 100644 index 0f8e1f2b2..000000000 Binary files a/Moose Presentations/DETECTION.pptx and /dev/null differ diff --git a/Moose Presentations/EVENT.pptx b/Moose Presentations/EVENT.pptx deleted file mode 100644 index 940cff234..000000000 Binary files a/Moose Presentations/EVENT.pptx and /dev/null differ diff --git a/Moose Presentations/FSM.pptx b/Moose Presentations/FSM.pptx deleted file mode 100644 index 65f5eaded..000000000 Binary files a/Moose Presentations/FSM.pptx and /dev/null differ diff --git a/Moose Presentations/LED Score Board.png b/Moose Presentations/LED Score Board.png deleted file mode 100644 index 673d1c74b..000000000 Binary files a/Moose Presentations/LED Score Board.png and /dev/null differ diff --git a/Moose Presentations/MESSAGE.pptx b/Moose Presentations/MESSAGE.pptx deleted file mode 100644 index 164c27510..000000000 Binary files a/Moose Presentations/MESSAGE.pptx and /dev/null differ diff --git a/Moose Presentations/MOOSE.pptx b/Moose Presentations/MOOSE.pptx deleted file mode 100644 index b9c40c706..000000000 Binary files a/Moose Presentations/MOOSE.pptx and /dev/null differ diff --git a/Moose Presentations/SCHEDULER.pptx b/Moose Presentations/SCHEDULER.pptx deleted file mode 100644 index 6dab2d58c..000000000 Binary files a/Moose Presentations/SCHEDULER.pptx and /dev/null differ diff --git a/Moose Presentations/SCORING.pptx b/Moose Presentations/SCORING.pptx deleted file mode 100644 index 576daef1f..000000000 Binary files a/Moose Presentations/SCORING.pptx and /dev/null differ diff --git a/Moose Presentations/SET.pptx b/Moose Presentations/SET.pptx deleted file mode 100644 index a032db2f3..000000000 Binary files a/Moose Presentations/SET.pptx and /dev/null differ diff --git a/Moose Presentations/SETUP.pptx b/Moose Presentations/SETUP.pptx deleted file mode 100644 index 226ce6869..000000000 Binary files a/Moose Presentations/SETUP.pptx and /dev/null differ diff --git a/Moose Presentations/SPAWN.pptx b/Moose Presentations/SPAWN.pptx deleted file mode 100644 index afd38a8b5..000000000 Binary files a/Moose Presentations/SPAWN.pptx and /dev/null differ diff --git a/Moose Presentations/TASK.pptx b/Moose Presentations/TASK.pptx deleted file mode 100644 index 70da479bc..000000000 Binary files a/Moose Presentations/TASK.pptx and /dev/null differ diff --git a/Moose Presentations/TASK_A2G.pptx b/Moose Presentations/TASK_A2G.pptx deleted file mode 100644 index 9254132a1..000000000 Binary files a/Moose Presentations/TASK_A2G.pptx and /dev/null differ diff --git a/Moose Presentations/TASK_DISPATCHER.pptx b/Moose Presentations/TASK_DISPATCHER.pptx deleted file mode 100644 index 0d9ebd4ab..000000000 Binary files a/Moose Presentations/TASK_DISPATCHER.pptx and /dev/null differ diff --git a/Moose Presentations/USER MISSIONS.pptx b/Moose Presentations/USER MISSIONS.pptx deleted file mode 100644 index 9fa93dbdd..000000000 Binary files a/Moose Presentations/USER MISSIONS.pptx and /dev/null differ diff --git a/Moose Presentations/USER MISSIONS/Dia1.JPG b/Moose Presentations/USER MISSIONS/Dia1.JPG deleted file mode 100644 index 1b1131f73..000000000 Binary files a/Moose Presentations/USER MISSIONS/Dia1.JPG and /dev/null differ diff --git a/Moose Presentations/ZONE.pptx b/Moose Presentations/ZONE.pptx deleted file mode 100644 index 7bf099ec4..000000000 Binary files a/Moose Presentations/ZONE.pptx and /dev/null differ diff --git a/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.lua b/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.lua deleted file mode 100644 index 8abd67b36..000000000 --- a/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.lua +++ /dev/null @@ -1,3 +0,0 @@ - -local PlanesClientSet = SET_CLIENT:New():FilterCategories( "plane" ):FilterStart() -local AirbasePolice = AIRBASEPOLICE_CAUCASUS:New( PlanesClientSet ) diff --git a/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz b/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz deleted file mode 100644 index 40da60e12..000000000 Binary files a/Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 - Caucasus.miz and /dev/null differ diff --git a/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.lua b/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.lua deleted file mode 100644 index f908630f9..000000000 --- a/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.lua +++ /dev/null @@ -1,3 +0,0 @@ - -local PlanesClientSet = SET_CLIENT:New():FilterCategories( "plane" ):FilterStart() -local AirbasePolice = AIRBASEPOLICE_NEVADA:New( PlanesClientSet ) diff --git a/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz b/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz deleted file mode 100644 index 4893f8abf..000000000 Binary files a/Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 - Nevada.miz and /dev/null differ diff --git a/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.lua b/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.lua deleted file mode 100644 index da3f23412..000000000 --- a/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.lua +++ /dev/null @@ -1,10 +0,0 @@ - - - - -Clean = CLEANUP:New( 'CLEAN_BATUMI', 180 ) - -SpawnRU = SPAWN:New( 'RU Attack Heli Batumi'):InitLimit( 2, 20 ):SpawnScheduled( 2, 0.2 ) - -SpawnUS = SPAWN:New( 'US Attack Heli Batumi'):InitLimit( 2, 20 ):SpawnScheduled( 2, 0.2 ) - diff --git a/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz b/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz deleted file mode 100644 index 6f70aac91..000000000 Binary files a/Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase CleanUp/ACL-001 - Airbase CleanUp.miz and /dev/null differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.lua b/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.lua deleted file mode 100644 index 44b68cdb4..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.lua +++ /dev/null @@ -1,28 +0,0 @@ ---- --- Name: AIB-001 - Spawned AI --- Author: FlightControl --- Date Created: 07 Dec 2016 --- --- # Situation: --- --- For the red coalition, 2 client slots are foreseen. --- We test the AI spawning frequency, validating the number of spawned AI, --- matching the amount of players that not have joined the mission. --- When players join, AI should fly to the nearest home base. --- --- # Test cases: --- --- 1. If no player is logging into the red slots, 2 red AI planes should be alive. --- 2. If a player joins one red slot, one red AI plane should return to the nearest home base. --- 3. If two players join the red slots, no AI plane should be spawned, and all airborne AI planes should return to the nearest home base. - --- Define the SET of CLIENTs from the red coalition. This SET is filled during startup. -RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) - --- Define the SPAWN object for the red AI plane template. --- We use InitCleanUp to check every 20 seconds, if there are no planes blocked at the airbase, waithing for take-off. --- If a blocked plane exists, this red plane will be ReSpawned. -RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) - --- Start the AI_BALANCER, using the SET of red CLIENTs, and the SPAWN object as a parameter. -RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz b/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz deleted file mode 100644 index f5619d3e0..000000000 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz and /dev/null differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.lua b/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.lua deleted file mode 100644 index c0b085a92..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.lua +++ /dev/null @@ -1,43 +0,0 @@ --- Name: AIB-002 - Patrol AI.lua --- Author: FlightControl --- Date Created: 7 December 2016 --- --- # Situation: --- --- For the red coalition, 2 client slots are foreseen. --- For those players that have not joined the mission, red AI is spawned. --- The red AI should start patrolling an area until fuel is empty and return to the home base. --- --- # Test cases: --- --- 1. If no player is logging into the red slots, 2 red AI planes should be alive. --- 2. If a player joins one red slot, one red AI plane should return to the nearest home base. --- 3. If two players join the red slots, no AI plane should be spawned, and all airborne AI planes should return to the nearest home base. --- 4. Spawned AI should take-off from the airbase, and start patrolling the area around Anapa. --- 5. When the AI is out-of-fuel, it should report it is returning to the home base, and land at Anapa. - --- Define the SET of CLIENTs from the red coalition. This SET is filled during startup. -RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) - --- Define the SPAWN object for the red AI plane template. --- We use InitCleanUp to check every 20 seconds, if there are no planes blocked at the airbase, waithing for take-off. --- If a blocked plane exists, this red plane will be ReSpawned. -RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) - --- Start the AI_BALANCER, using the SET of red CLIENTs, and the SPAWN object as a parameter. -RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) - -local PatrolZones = {} - -function RU_AI_Balancer:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) - - local PatrolZoneGroup = GROUP:FindByName( "PatrolZone" ) - local PatrolZone = ZONE_POLYGON:New( "PatrolZone", PatrolZoneGroup ) - - - PatrolZones[AIGroup] = AI_PATROL_ZONE:New( PatrolZone, 3000, 6000, 400, 600 ) - PatrolZones[AIGroup]:ManageFuel( 0.2, 60 ) - PatrolZones[AIGroup]:SetControllable( AIGroup ) - PatrolZones[AIGroup]:__Start( 5 ) - -end diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz b/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz deleted file mode 100644 index 4321844f6..000000000 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 - Patrol AI.miz and /dev/null differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.lua b/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.lua deleted file mode 100644 index 2f4a137ab..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.lua +++ /dev/null @@ -1,24 +0,0 @@ -RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) -RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) -RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) - -RU_AirbasesSet = SET_AIRBASE:New():FilterCoalitions("red"):FilterStart() -RU_AirbasesSet:Flush() -RU_AI_Balancer:ReturnToNearestAirbases( 10000, RU_AirbasesSet ) - - -US_PlanesClientSet = SET_CLIENT:New():FilterCountries( "USA" ):FilterCategories( "plane" ) -US_PlanesSpawn = SPAWN:New( "AI US" ):InitCleanUp( 20 ) -US_AI_Balancer = AI_BALANCER:New( US_PlanesClientSet, US_PlanesSpawn ) - ---RU_AI_Balancer:ReturnToHomeAirbase( 10000 ) - ---local PatrolZoneGroup = GROUP:FindByName( "Patrol Zone Blue" ) ---local PatrolZoneBlue = ZONE_POLYGON:New( "PatrolZone", PatrolZoneGroup ) ---local PatrolZoneB = AI_PATROL_ZONE:New( PatrolZoneBlue, 3000, 6000, 900, 1100 ):ManageFuel( 0.2, 180 ) ---US_AI_Balancer:SetPatrolZone( PatrolZoneB ) --- ---local PatrolZoneGroup = GROUP:FindByName( "Patrol Zone Red" ) ---local PatrolZoneRed = ZONE_POLYGON:New( "PatrolZone", PatrolZoneGroup ) ---local PatrolZoneR = AI_PATROL_ZONE:New( PatrolZoneRed, 3000, 6000, 900, 1100 ):ManageFuel( 0.2, 180 ) ---RU_AI_Balancer:SetPatrolZone( PatrolZoneR ) diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz b/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz deleted file mode 100644 index 44597cba3..000000000 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz and /dev/null differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.lua b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.lua deleted file mode 100644 index 07efab0d0..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.lua +++ /dev/null @@ -1,47 +0,0 @@ --- Name: AIB-004 - Respawn Test when Destroyed.lua --- Author: FlightControl --- Date Created: 7 January 2017 --- --- # Situation: --- --- For the red coalition, 2 client slots are foreseen. --- For those players that have not joined the mission, red AI is spawned. --- The red AI should start patrolling an area. --- --- The blue side has SAMs nearby. --- Once the red AI takes off, the red AI is attacked by the blue SAMs. --- Red AI should be killed and once that happens, a Respawn of the group should happen! --- The Respawn happens through the InitCleanUp() API of SPAWN. --- --- # Test cases: --- --- 1. If no player is logging into the red slots, 2 red AI planes should be alive. --- 2. If a player joins one red slot, one red AI plane should return to the nearest home base. --- 3. If two players join the red slots, no AI plane should be spawned, and all airborne AI planes should return to the nearest home base. --- 4. Monitor that once a red AI is destroyed, that it ReSpawns... - - --- Define the SET of CLIENTs from the red coalition. This SET is filled during startup. -RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) - --- Define the SPAWN object for the red AI plane template. --- We use InitCleanUp to check every 20 seconds, if there are no planes blocked at the airbase, waithing for take-off. --- If a blocked plane exists, this red plane will be ReSpawned. -RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) - --- Start the AI_BALANCER, using the SET of red CLIENTs, and the SPAWN object as a parameter. -RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) - -function RU_AI_Balancer:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) - - local PatrolZoneGroup = GROUP:FindByName( "PatrolZone" ) - local PatrolZone = ZONE_POLYGON:New( "PatrolZone", PatrolZoneGroup ) - - - local Patrol = AI_PATROL_ZONE:New( PatrolZone, 3000, 6000, 400, 600 ) - Patrol:ManageFuel( 0.2, 60 ) - Patrol:SetControllable( AIGroup ) - Patrol:__Start( 5 ) - -end - diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz deleted file mode 100644 index bdc946b15..000000000 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz and /dev/null differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.lua b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.lua deleted file mode 100644 index f0253d5e9..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.lua +++ /dev/null @@ -1,51 +0,0 @@ --- Name: AIB-005 - Patrol AI and Randomize Zones --- Author: FlightControl --- Date Created: 10 Jan 2016 --- --- # Situation: --- --- For the red coalition, 2 client slots are foreseen. --- For those players that have not joined the mission, red AI is spawned. --- The red AI should start patrolling an area until fuel is empty and return to the home base. --- For each AI being spawned, ensure that they fly to a random zone defined within the mission editor. --- Right now there are two patrol zones defined, so the AI should start patrolliing in one of these zones. --- --- # Test cases: --- --- 1. If no player is logging into the red slots, 2 red AI planes should be alive. --- 2. If a player joins one red slot, one red AI plane should return to the nearest home base. --- 3. If two players join the red slots, no AI plane should be spawned, and all airborne AI planes should return to the nearest home base. --- 4. Spawned AI should take-off from the airbase, and start patrolling the area around Anapa. --- 5. When the AI is out-of-fuel, it should report it is returning to the home base, and land at Anapa. --- 6. Ensure that you see the AI patrol in one of the two zones ... - --- Define the SET of CLIENTs from the red coalition. This SET is filled during startup. -RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) - --- Define the SPAWN object for the red AI plane template. --- We use InitCleanUp to check every 20 seconds, if there are no planes blocked at the airbase, waithing for take-off. --- If a blocked plane exists, this red plane will be ReSpawned. -RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) - --- Start the AI_BALANCER, using the SET of red CLIENTs, and the SPAWN object as a parameter. -RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) - --- Create the first polygon zone ... -PatrolZoneGroup1 = GROUP:FindByName( "PatrolZone1" ) -PatrolZone1 = ZONE_POLYGON:New( "PatrolZone1", PatrolZoneGroup1 ) - --- Create the second polygon zone ... -PatrolZoneGroup2 = GROUP:FindByName( "PatrolZone2" ) -PatrolZone2 = ZONE_POLYGON:New( "PatrolZone2", PatrolZoneGroup2 ) - --- Now, create an array of these zones ... -PatrolZoneArray = { PatrolZone1, PatrolZone2 } - -function RU_AI_Balancer:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) - - local Patrol = AI_PATROL_ZONE:New( PatrolZoneArray[math.random( 1, 2 )], 3000, 6000, 400, 600 ) - Patrol:ManageFuel( 0.2, 60 ) - Patrol:SetControllable( AIGroup ) - Patrol:Start() - -end diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz b/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz deleted file mode 100644 index 348d0ae84..000000000 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz and /dev/null differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.lua b/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.lua deleted file mode 100644 index 31574814d..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.lua +++ /dev/null @@ -1,43 +0,0 @@ --- Name: AIB-005 - Patrol AI and Randomize Zones --- Author: FlightControl --- Date Created: 10 Jan 2016 --- --- # Situation: --- --- For the red coalition, 2 client slots are foreseen. --- For those players that have not joined the mission, red AI is spawned. --- You'll notice a lot of AI is being spawned, as there are a lot of slots... --- If the SPAWN API :InitCleanUp( secs ) is NOT used, you'll notice that the planes block each other on the runway. --- After a short period of time, nothing will move anymore... --- The :InitCleanUp( seconds ) API of the SPAWN class ensure that any AI that is parked longer than the --- specified amount of seconds, is respawned back at the parking position. --- This frees up the other planes departing, and the airbase is in this way decluttered... --- --- # Test cases: --- --- 1. Observe the de-cluttering of planes at Krymsk. --- 2. Play with the InitCleanUp API of the SPAWN class, extende the amount of seconds to find the optimal setting. - --- Define the SET of CLIENTs from the red coalition. This SET is filled during startup. -RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ) - --- Define the SPAWN object for the red AI plane template. --- We use InitCleanUp to check every 20 seconds, if there are no planes blocked at the airbase, waithing for take-off. --- If a blocked plane exists, this red plane will be ReSpawned. -RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) - --- Start the AI_BALANCER, using the SET of red CLIENTs, and the SPAWN object as a parameter. -RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) - --- Create the first polygon zone ... -PatrolZoneGroup1 = GROUP:FindByName( "PatrolZone1" ) -PatrolZone1 = ZONE_POLYGON:New( "PatrolZone1", PatrolZoneGroup1 ) - -function RU_AI_Balancer:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) - - local Patrol = AI_PATROL_ZONE:New( PatrolZone1, 3000, 6000, 400, 600 ) - Patrol:ManageFuel( 0.2, 60 ) - Patrol:SetControllable( AIGroup ) - Patrol:__Start( 5 ) - -end diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz b/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz deleted file mode 100644 index fc05c592b..000000000 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at Airbases/AIB-006 - Declutter AI at Airbases.miz and /dev/null differ diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.lua b/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.lua deleted file mode 100644 index d6cc8a8f3..000000000 --- a/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.lua +++ /dev/null @@ -1,163 +0,0 @@ --- Name: AIB-007 - AI Balancers For all airports and both coalitions --- Author: Delta99 --- Date Created: 11 Feb 2017 --- --- Originally created to solve issues jg7xman (from Moose Slack group) was having in creating --- AI_BALANCER across multiple airbases. - --- # Situation: --- --- AI_BALANCERS created per airbase for both coalitions. Mutiple patrol zones are created --- for each side. Each flight that is created by AI_BALANCER will pick a random patrol zone --- to patrol. - --- # Test Cases --- --- 1. Observe at least 1 flight spawning and taking off from each airbase. --- 2. Each flight patrols randomly in one of its sides zones. --- 3. AI will respawn after killed. --- 4. Additional client slots are available at Sochi. If players don't take a slot there --- will be more than one AI taking off from Sochi. --- 5. Batumi contains a flight of 3 units rather than just 1 like most of the rest of the airbases. --- 6. Watch the coalition AI clash and kill each other. - --- Create the Red Patrol Zone Array - --- This zone array will be used in the AI_BALANCER to randomize the patrol --- zone that each spawned group will patrol - -RedPatrolZone = {} -RedPatrolZone[1] = ZONE:New( "RedPatrolZone1" ) -RedPatrolZone[2] = ZONE:New( "RedPatrolZone2" ) -RedPatrolZone[3] = ZONE:New( "RedPatrolZone3" ) -RedPatrolZone[4] = ZONE:New( "RedPatrolZone4" ) -RedPatrolZone[5] = ZONE:New( "RedPatrolZone5" ) -RedPatrolZone[6] = ZONE:New( "RedPatrolZone6" ) - --- Russian CAP Aircraft - --- These are the aircraft created in the mission editor that the AI will spawn --- with replacing any CLIENT created aircraft in the mission that a human --- player does not take. - -RU_PlanesSpawn = {} -RU_PlanesSpawn[1] = SPAWN:New( "RU CAP Anapa AB" ):InitCleanUp( 45 ) -RU_PlanesSpawn[2] = SPAWN:New( "RU CAP Beslan AB" ):InitCleanUp( 45 ) -RU_PlanesSpawn[3] = SPAWN:New( "RU CAP Gelendzhik AB" ):InitCleanUp( 45 ) -RU_PlanesSpawn[4] = SPAWN:New( "RU CAP Krasnodar Center AB" ):InitCleanUp( 45 ) -RU_PlanesSpawn[5] = SPAWN:New( "RU CAP Krasnodar Pashkovsky AB" ):InitCleanUp( 45 ) -RU_PlanesSpawn[6] = SPAWN:New( "RU CAP Krymsk AB" ):InitCleanUp( 45 ) -RU_PlanesSpawn[7] = SPAWN:New( "RU CAP Maykop AB" ):InitCleanUp( 45 ) -RU_PlanesSpawn[8] = SPAWN:New( "RU CAP Mineralnye Vody AB" ):InitCleanUp( 45 ) -RU_PlanesSpawn[9] = SPAWN:New( "RU CAP Mozdok AB" ):InitCleanUp( 45 ) -RU_PlanesSpawn[10] = SPAWN:New( "RU CAP Nalchik AB" ):InitCleanUp( 45 ) -RU_PlanesSpawn[11] = SPAWN:New( "RU CAP Novorossiysk AB" ):InitCleanUp( 45 ) - --- Russian Client Aircraft (via AI_BALANCER, AI will replace these if no human players are in the slot) - --- If you want more client slots per airbase that you want AI to be able to take control of then --- name them with the prefixes below and they will be picked up automatically by FilterPrevixes. --- --- For example, if you want another Client slot available at Anapa name it "RU CLIENT Anapa AB 2". --- The code here does not need to be changed. Only an addition in the mission editor. An example --- of this can be found on the USA side at Sochi AB. - -RU_PlanesClientSet = {} -RU_PlanesClientSet[1] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Anapa AB") -RU_PlanesClientSet[2] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Beslan AB") -RU_PlanesClientSet[3] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Gelendzhik AB") -RU_PlanesClientSet[4] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Krasnodar Center AB") -RU_PlanesClientSet[5] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Krasnodar Pashkovsky AB") -RU_PlanesClientSet[6] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Krymsk AB") -RU_PlanesClientSet[7] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Maykop AB") -RU_PlanesClientSet[8] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Mineralnye Vody AB") -RU_PlanesClientSet[9] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Mozdok AB") -RU_PlanesClientSet[10] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Nalchik AB") -RU_PlanesClientSet[11] = SET_CLIENT:New():FilterPrefixes("RU CLIENT Novorossiysk AB") - --- We setup an array to store all the AI_BALANCERS that are going to be created. Basically one --- per airbase. We loop through and create an AI_BALANCER as well as a separate OnAfterSpawned --- function for each. The Patrol Zone is randomized in the first parameter to AI_PATROL_ZONE:New() --- call. This is done for each of the AI_BALANCERS. To add more patrol zones, just define them in --- the mission editor and add into the array above. Code here does not need to be changed. The --- table.getn(RedPatrolZone) gets the number of elements in the RedPatrolZone array so that all --- of them are included to pick randomly. - - -RU_AI_Balancer = {} -for i=1, 11 do - RU_AI_Balancer[i] = AI_BALANCER:New(RU_PlanesClientSet[i], RU_PlanesSpawn[i]) - - -- We set a local variable within the for loop to the AI_BALANCER that was just created. - -- I couldn't get RU_AI_BALANCER[i]:OnAfterSpawn to be recognized so this is just pointing - -- curAIBalancer to the relevant RU_AI_BALANCER array item for each loop. - - -- So in this case there are essentially 11 OnAfterSpawned functions defined and handled. - - local curAIBalancer = RU_AI_Balancer[i] - function curAIBalancer:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) - local Patrol = AI_PATROL_ZONE:New( RedPatrolZone[math.random( 1, table.getn(RedPatrolZone))], 1500, 5500, 700, 1400 ) - Patrol:ManageFuel( 0.2, 60 ) - Patrol:SetControllable( AIGroup ) - Patrol:Start() - end -end - --- US / Blue side is setup pretty much identically to the RU side above. Same detailed comments --- above apply here. The main difference here is 10 airbases instead of 11. - --- Another difference is additional client slots at Sochi and a group defined at Batumi with --- more than 1 unit per group (flight of 3 units). This is just to show that you can have more --- client slots per airbase and more units in a single group that the AI will control. I think --- this will also allow you to fly lead with AI on your wing or you can fly wing with an AI --- leader. - --- Create the Blue Patrol Zone Array -BluePatrolZone = {} -BluePatrolZone[1] = ZONE:New( "BluePatrolZone1") -BluePatrolZone[2] = ZONE:New( "BluePatrolZone2") -BluePatrolZone[3] = ZONE:New( "BluePatrolZone3") -BluePatrolZone[4] = ZONE:New( "BluePatrolZone4") -BluePatrolZone[5] = ZONE:New( "BluePatrolZone5") -BluePatrolZone[6] = ZONE:New( "BluePatrolZone6") - ---United States CAP Aircraft (these are used as templates for AI) - -US_PlanesSpawn = {} -US_PlanesSpawn[1] = SPAWN:New( "US CAP Batumi AB" ):InitCleanUp( 45 ) -US_PlanesSpawn[2] = SPAWN:New( "US CAP Gudauta AB" ):InitCleanUp( 45 ) -US_PlanesSpawn[3] = SPAWN:New( "US CAP Kobuleti AB" ):InitCleanUp( 45 ) -US_PlanesSpawn[4] = SPAWN:New( "US CAP Kutaisi AB" ):InitCleanUp( 45 ) -US_PlanesSpawn[5] = SPAWN:New( "US CAP Senaki AB" ):InitCleanUp( 45 ) -US_PlanesSpawn[6] = SPAWN:New( "US CAP Sochi AB" ):InitCleanUp( 45 ) -US_PlanesSpawn[7] = SPAWN:New( "US CAP Soganlug AB" ):InitCleanUp( 45 ) -US_PlanesSpawn[8] = SPAWN:New( "US CAP Sukhumi AB" ):InitCleanUp( 45 ) -US_PlanesSpawn[9] = SPAWN:New( "US CAP Vaziani AB" ):InitCleanUp( 45 ) -US_PlanesSpawn[10] = SPAWN:New( "US CAP Tbilisi AB" ):InitCleanUp( 45 ) - ---United States Client Aircraft (via AI_BALANCER, AI will replace these if no human players are in the slot) - -US_PlanesClientSet = {} -US_PlanesClientSet[1] = SET_CLIENT:New():FilterPrefixes("US CLIENT Batumi AB") -US_PlanesClientSet[2] = SET_CLIENT:New():FilterPrefixes("US CLIENT Gudauta AB") -US_PlanesClientSet[3] = SET_CLIENT:New():FilterPrefixes("US CLIENT Kobuleti AB") -US_PlanesClientSet[4] = SET_CLIENT:New():FilterPrefixes("US CLIENT Kutaisi AB") -US_PlanesClientSet[5] = SET_CLIENT:New():FilterPrefixes("US CLIENT Senaki AB") -US_PlanesClientSet[6] = SET_CLIENT:New():FilterPrefixes("US CLIENT Sochi AB") -US_PlanesClientSet[7] = SET_CLIENT:New():FilterPrefixes("US CLIENT Soganlug AB") -US_PlanesClientSet[8] = SET_CLIENT:New():FilterPrefixes("US CLIENT Sukhumi AB") -US_PlanesClientSet[9] = SET_CLIENT:New():FilterPrefixes("US CLIENT Vaziani AB") -US_PlanesClientSet[10] = SET_CLIENT:New():FilterPrefixes("US CLIENT Tbilisi AB") - -US_AI_Balancer = {} -for i=1, 10 do - US_AI_Balancer[i] = AI_BALANCER:New( US_PlanesClientSet[i], US_PlanesSpawn[i] ) - - local curAIBalancer = US_AI_Balancer[i] - function curAIBalancer:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) - local Patrol = AI_PATROL_ZONE:New( BluePatrolZone[math.random( 1, table.getn(BluePatrolZone))], 1500, 5500, 700, 1400 ) - Patrol:ManageFuel( 0.2, 60 ) - Patrol:SetControllable( AIGroup ) - Patrol:Start() - end -end diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz b/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz deleted file mode 100644 index 26dfe66bc..000000000 Binary files a/Moose Test Missions/AIB - AI Balancing/AIB-007 - AI Balancers For all airports and both coalitions/AIB-007 - AI Balancers For all airports and both coalitions.miz and /dev/null differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.lua b/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.lua deleted file mode 100644 index 12abd4eb4..000000000 --- a/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.lua +++ /dev/null @@ -1,23 +0,0 @@ ---- --- Name: CAP-001 - Combat Air Patrol --- Author: FlightControl --- Date Created: 16 January 2017 --- --- # Situation: --- The Su-27 airplane will patrol in PatrolZone. --- It will not engage any enemy automatically. --- --- # Test cases: --- --- 1. Observe the Su-27 patrolling. --- - -local CapPlane = GROUP:FindByName( "Plane" ) - -local PatrolZone = ZONE:New( "Patrol Zone" ) - -AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) - -AICapZone:SetControllable( CapPlane ) - -AICapZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz deleted file mode 100644 index 2e7babc53..000000000 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air Patrol/CAP-001 - Combat Air Patrol.miz and /dev/null differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.lua b/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.lua deleted file mode 100644 index 87189c8c5..000000000 --- a/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.lua +++ /dev/null @@ -1,27 +0,0 @@ ---- --- Name: CAP-010 - CAP and Engage within Range --- Author: FlightControl --- Date Created: 16 January 2017 --- --- # Situation: --- --- The Su-27 airplane will patrol in PatrolZone. --- It will engage when it detects the airplane and when the A-10C is within the engage range. --- --- # Test cases: --- --- 1. Observe the Su-27 patrolling. --- 2. Observe that, when the A-10C is within the engage range, it will engage. --- 3. After engage, observe that the Su-27 returns to the PatrolZone. --- 4. If you want, you can wait until the Su-27 is out of fuel and will land. - -CapPlane = GROUP:FindByName( "Plane" ) - -PatrolZone = ZONE:New( "Patrol Zone" ) - -AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) - -AICapZone:SetControllable( CapPlane ) -AICapZone:SetEngageRange( 20000 ) -- Set the Engage Range to 20.000 meters. The AI won't engage when the enemy is beyond 20.000 meters. - -AICapZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz deleted file mode 100644 index 529550dde..000000000 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage within Range/CAP-010 - CAP and Engage within Range.miz and /dev/null differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.lua b/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.lua deleted file mode 100644 index 9c6d37415..000000000 --- a/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.lua +++ /dev/null @@ -1,32 +0,0 @@ ---- --- Name: CAP-011 - CAP and Engage within Zone --- Author: FlightControl --- Date Created: 16 January 2017 --- --- # Situation: --- --- The Su-27 airplane will patrol in PatrolZone. --- It will engage when it detects the airplane and when the A-10C is within the CapEngageZone. --- --- # Test cases: --- --- 1. Observe the Su-27 patrolling. --- 2. Observe that, when the A-10C is within the engage zone, it will engage. --- 3. After engage, observe that the Su-27 returns to the PatrolZone. --- 4. If you want, you can wait until the Su-27 is out of fuel and will land. - - -CapPlane = GROUP:FindByName( "Plane" ) - -PatrolZone = ZONE:New( "Patrol Zone" ) - -AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) - -EngageZoneGroup = GROUP:FindByName( "Engage Zone" ) - -CapEngageZone = ZONE_POLYGON:New( "Engage Zone", EngageZoneGroup ) - -AICapZone:SetControllable( CapPlane ) -AICapZone:SetEngageZone( CapEngageZone ) -- Set the Engage Zone. The AI will only engage when the bogeys are within the CapEngageZone. - -AICapZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz deleted file mode 100644 index c70916318..000000000 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage within Zone/CAP-011 - CAP and Engage within Zone.miz and /dev/null differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.lua b/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.lua deleted file mode 100644 index 3b0ad84bc..000000000 --- a/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.lua +++ /dev/null @@ -1,43 +0,0 @@ ---- --- Name: CAP-012 - CAP - Test Abort --- Author: FlightControl --- Date Created: 14 Mar 2017 --- --- # Situation: --- --- The Su-27 airplane will patrol in PatrolZone. --- It will engage when it detects the airplane and when the A-10C is within the CapEngageZone. --- It will abort the engagement after 1 minute and return to the patrol zone. --- --- # Test cases: --- --- 1. Observe the Su-27 patrolling. --- 2. Observe that, when the A-10C is within the engage zone, it will engage. --- 3. After engage, observe that the Su-27 returns to the PatrolZone. --- 4. When it engages, it will abort the engagement after 1 minute. - - -CapPlane = GROUP:FindByName( "Plane" ) - -PatrolZone = ZONE:New( "Patrol Zone" ) - -AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) - -EngageZoneGroup = GROUP:FindByName( "Engage Zone" ) - -CapEngageZone = ZONE_POLYGON:New( "Engage Zone", EngageZoneGroup ) - -AICapZone:SetControllable( CapPlane ) -AICapZone:SetEngageZone( CapEngageZone ) -- Set the Engage Zone. The AI will only engage when the bogeys are within the CapEngageZone. - -AICapZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. - -function AICapZone:OnAfterEngage(Controllable,From,Event,To) - AICapZone:__Abort( 60 ) -end - -function AICapZone:OnAfterAbort(Controllable,From,Event,To) - BASE:E("MISSION ABORTED! Returning to Patrol Zone!") - MESSAGE:New("MISSION ABORTED! Returning to Patrol Zone!",30,"ALERT!") -end - diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.miz deleted file mode 100644 index e0888120c..000000000 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-012 - CAP - Test Abort/CAP-012 - CAP - Test Abort.miz and /dev/null differ diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.lua b/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.lua deleted file mode 100644 index 16f456f04..000000000 --- a/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.lua +++ /dev/null @@ -1,27 +0,0 @@ ---- --- Name: CAP-020 - Combat Air Patrol RTB Test --- Author: FlightControl --- Date Created: 14 February 2017 --- --- # Situation: --- The Su-27 airplane will patrol in PatrolZone. --- It will return to base when out of fuel. --- --- # Test cases: --- --- 1. Observe the Su-27 patrolling. --- 2. It should return to base when out of fuel. --- - -CapSpawn = SPAWN:New( "Plane" ):InitLimit(1,2):InitRepeatOnLanding() - -CapGroup = CapSpawn:Spawn() - -PatrolZone = ZONE:New( "Patrol Zone" ) - -AICapZone = AI_CAP_ZONE:New( PatrolZone, 500, 1000, 500, 600 ) - -AICapZone:SetControllable( CapGroup ) - -AICapZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. - diff --git a/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz b/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz deleted file mode 100644 index af3d81f30..000000000 Binary files a/Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz and /dev/null differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz deleted file mode 100644 index c389dfc61..000000000 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz and /dev/null differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.lua b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.lua deleted file mode 100644 index a9a0ad737..000000000 --- a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.lua +++ /dev/null @@ -1,74 +0,0 @@ ---- --- Name: CAS-001 - CAS in a Zone by Airplane Group --- Author: FlightControl --- Date Created: 13 January 2017 --- --- # Situation: --- --- A group of 4 Su-25T at patrolling north of an engage zone for 10 minutes. --- After 10 minutes, the command center orders the Su-25T to engage the zone and execute a CAS. --- --- # Test cases: --- --- 1. Observe that the Su-25T is patrolling in the patrol zone, until the engage command is given. --- 2. The Su-25T are not detecting any target during the patrol. --- 3. When the Su-25T is commanded to engage, the group will fly to the engage zone. --- 4. Detection is activated and detected targets within the engage zone are assigned for CAS. --- 5. Observe the Su-25T eliminating the targets. --- 6. Observe the Su-25T defenses. --- 7. When all targets within the engage zone are destroyed, the Su-25T CAS task is set to Accomplished. --- 8. The Su-25T will return to base. - - - --- Create a local variable (in this case called CASEngagementZone) and --- using the ZONE function find the pre-defined zone called "Engagement Zone" --- currently on the map and assign it to this variable -CASEngagementZone = ZONE:New( "Engagement Zone" ) - --- Create a local variable (in this case called CASPlane) and --- using the GROUP function find the aircraft group called "Plane" and assign to this variable -CASPlane = GROUP:FindByName( "Plane" ) - --- Create a local Variable (in this cased called PatrolZone and --- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -PatrolZone = ZONE:New( "Patrol Zone" ) - --- Create and object (in this case called AICasZone) and --- using the functions AI_CAS_ZONE assign the parameters that define this object --- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) - --- Create an object (in this case called Targets) and --- using the GROUP function find the group labeled "Targets" and assign it to this object -Targets = GROUP:FindByName("Targets") - - --- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function -AICasZone:SetControllable( CASPlane ) - --- Tell the group CASPlane to start the mission in 1 second. -AICasZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. - --- After 10 minutes, tell the group CASPlane to engage the targets located in the engagement zone called CASEngagement Zone. (600 is 600 seconds) -AICasZone:__Engage( 600 ) - --- Check every 60 seconds whether the Targets have been eliminated. --- When the trigger completed has been fired, the Plane will go back to the Patrol Zone. -Check, CheckScheduleID = SCHEDULER:New(nil, - function() - if Targets:IsAlive() and Targets:GetSize() > 5 then - BASE:E( "Test Mission: " .. Targets:GetSize() .. " targets left to be destroyed.") - else - BASE:E( "Test Mission: The required targets are destroyed." ) - AICasZone:__Accomplish( 1 ) -- Now they should fly back to teh patrolzone and patrol. - end - end, {}, 20, 60, 0.2 ) - - --- When the targets in the zone are destroyed, (see scheduled function), the planes will return home ... -function AICasZone:OnAfterAccomplish( Controllable, From, Event, To ) - BASE:E( "Test Mission: Sending the Su-25T back to base." ) - Check:Stop( CheckScheduleID ) - AICasZone:__RTB( 1 ) -end diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz b/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz deleted file mode 100644 index 6b4b491e2..000000000 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz and /dev/null differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.lua b/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.lua deleted file mode 100644 index fad0bea38..000000000 --- a/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.lua +++ /dev/null @@ -1,75 +0,0 @@ ---- --- Name: CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed --- Author: FlightControl --- Date Created: 06 February 2017 --- --- # Situation: --- --- A group of 4 Su-25T at patrolling north of an engage zone for 1 minute. --- After 1 minute, the command center orders the Su-25T to engage the zone and execute a CAS. --- --- # Test cases: --- --- 1. Observe that the Su-25T is patrolling in the patrol zone, until the engage command is given. --- 2. The Su-25T are not detecting any target during the patrol. --- 3. When the Su-25T is commanded to engage, the group will fly to the engage zone. --- 3.1. The approach speed to the engage zone is set to 400 km/h. --- 4. Detection is activated and detected targets within the engage zone are assigned for CAS. --- 5. Observe the Su-25T eliminating the targets. --- 6. Observe the Su-25T defenses. --- 7. When all targets within the engage zone are destroyed, the Su-25T CAS task is set to Accomplished. --- 8. The Su-25T will return to base. - - - --- Create a local variable (in this case called CASEngagementZone) and --- using the ZONE function find the pre-defined zone called "Engagement Zone" --- currently on the map and assign it to this variable -CASEngagementZone = ZONE:New( "Engagement Zone" ) - --- Create a local variable (in this case called CASPlane) and --- using the GROUP function find the aircraft group called "Plane" and assign to this variable -CASPlane = GROUP:FindByName( "Plane" ) - --- Create a local Variable (in this cased called PatrolZone and --- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -PatrolZone = ZONE:New( "Patrol Zone" ) - --- Create and object (in this case called AICasZone) and --- using the functions AI_CAS_ZONE assign the parameters that define this object --- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) - --- Create an object (in this case called Targets) and --- using the GROUP function find the group labeled "Targets" and assign it to this object -Targets = GROUP:FindByName("Targets") - - --- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function -AICasZone:SetControllable( CASPlane ) - --- Tell the group CASPlane to start the mission in 1 second. -AICasZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. - --- After 10 minutes, tell the group CASPlane to engage the targets located in the engagement zone called CASEngagement Zone. (600 is 600 seconds) -AICasZone:__Engage( 60, 400 ) -- Engage after one minute with a speed of 400 km/h. - --- Check every 60 seconds whether the Targets have been eliminated. --- When the trigger completed has been fired, the Plane will go back to the Patrol Zone. -Check, CheckScheduleID = SCHEDULER:New(nil, - function() - if Targets:IsAlive() and Targets:GetSize() > 5 then - BASE:E( "Test Mission: " .. Targets:GetSize() .. " targets left to be destroyed.") - else - BASE:E( "Test Mission: The required targets are destroyed." ) - AICasZone:__Accomplish( 1 ) -- Now they should fly back to teh patrolzone and patrol. - end - end, {}, 20, 60, 0.2 ) - - --- When the targets in the zone are destroyed, (see scheduled function), the planes will return home ... -function AICasZone:OnAfterAccomplish( Controllable, From, Event, To ) - BASE:E( "Test Mission: Sending the Su-25T back to base." ) - Check:Stop( CheckScheduleID ) - AICasZone:__RTB( 1 ) -end diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz b/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz deleted file mode 100644 index d3401bf74..000000000 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane Group - Engage with Speed.miz and /dev/null differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.lua b/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.lua deleted file mode 100644 index 8af2ca77e..000000000 --- a/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.lua +++ /dev/null @@ -1,76 +0,0 @@ ---- --- Name: CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude --- Author: FlightControl --- Date Created: 6 February 2017 --- --- # Situation: --- --- A group of 4 Su-25T at patrolling north of an engage zone for 1 minute. --- After 1 minute, the command center orders the Su-25T to engage the zone and execute a CAS. --- --- # Test cases: --- --- 1. Observe that the Su-25T is patrolling in the patrol zone, until the engage command is given. --- 2. The Su-25T are not detecting any target during the patrol. --- 3. When the Su-25T is commanded to engage, the group will fly to the engage zone --- 3.1. The approach speed to the engage zone is set to 400 km/h. --- 3.2. The altitude to the engage zone and CAS execution is set to 500 meters. --- 4. Detection is activated and detected targets within the engage zone are assigned for CAS. --- 5. Observe the Su-25T eliminating the targets. --- 6. Observe the Su-25T defenses. --- 7. When all targets within the engage zone are destroyed, the Su-25T CAS task is set to Accomplished. --- 8. The Su-25T will return to base. - - - --- Create a local variable (in this case called CASEngagementZone) and --- using the ZONE function find the pre-defined zone called "Engagement Zone" --- currently on the map and assign it to this variable -CASEngagementZone = ZONE:New( "Engagement Zone" ) - --- Create a local variable (in this case called CASPlane) and --- using the GROUP function find the aircraft group called "Plane" and assign to this variable -CASPlane = GROUP:FindByName( "Plane" ) - --- Create a local Variable (in this cased called PatrolZone and --- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -PatrolZone = ZONE:New( "Patrol Zone" ) - --- Create and object (in this case called AICasZone) and --- using the functions AI_CAS_ZONE assign the parameters that define this object --- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) - --- Create an object (in this case called Targets) and --- using the GROUP function find the group labeled "Targets" and assign it to this object -Targets = GROUP:FindByName("Targets") - - --- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function -AICasZone:SetControllable( CASPlane ) - --- Tell the group CASPlane to start the mission in 1 second. -AICasZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. - --- After 10 minutes, tell the group CASPlane to engage the targets located in the engagement zone called CASEngagement Zone. (600 is 600 seconds) -AICasZone:__Engage( 600, 350, 4000 ) -- Engage after 10 minutes with a speed of 350 km/h and an altitude of 4000 meters. - --- Check every 60 seconds whether the Targets have been eliminated. --- When the trigger completed has been fired, the Plane will go back to the Patrol Zone. -Check, CheckScheduleID = SCHEDULER:New(nil, - function() - if Targets:IsAlive() and Targets:GetSize() > 5 then - BASE:E( "Test Mission: " .. Targets:GetSize() .. " targets left to be destroyed.") - else - BASE:E( "Test Mission: The required targets are destroyed." ) - AICasZone:__Accomplish( 1 ) -- Now they should fly back to teh patrolzone and patrol. - end - end, {}, 20, 60, 0.2 ) - - --- When the targets in the zone are destroyed, (see scheduled function), the planes will return home ... -function AICasZone:OnAfterAccomplish( Controllable, From, Event, To ) - BASE:E( "Test Mission: Sending the Su-25T back to base." ) - Check:Stop( CheckScheduleID ) - AICasZone:__RTB( 1 ) -end diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz b/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz deleted file mode 100644 index 0f412ea66..000000000 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone by Airplane Group - Engage with Speed and Altitude.miz and /dev/null differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.lua b/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.lua deleted file mode 100644 index 295b7d11d..000000000 --- a/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.lua +++ /dev/null @@ -1,81 +0,0 @@ ---- --- Name: CAS-004 - CAS in a Zone by Airplane Group - Test Abort --- Author: FlightControl --- Date Created: 14 Mar 2017 --- --- # Situation: --- --- A group of 4 Su-25T at patrolling north of an engage zone for 1 minute. --- After 10 minutes, the command center orders the Su-25T to engage the zone and execute a CAS. --- After 12 minutes, the mission is aborted. --- --- # Test cases: --- --- 1. Observe that the Su-25T is patrolling in the patrol zone, until the engage command is given. --- 2. The Su-25T are not detecting any target during the patrol. --- 3. When the Su-25T is commanded to engage, the group will fly to the engage zone --- 3.1. The approach speed to the engage zone is set to 350 km/h. --- 3.2. The altitude to the engage zone and CAS execution is set to 4000 meters. --- 4. Observe the mission being aborted. A message will be sent. --- 5. The Su-25T will go back patrolling. - - - --- Create a local variable (in this case called CASEngagementZone) and --- using the ZONE function find the pre-defined zone called "Engagement Zone" --- currently on the map and assign it to this variable -CASEngagementZone = ZONE:New( "Engagement Zone" ) - --- Create a local variable (in this case called CASPlane) and --- using the GROUP function find the aircraft group called "Plane" and assign to this variable -CASPlane = GROUP:FindByName( "Plane" ) - --- Create a local Variable (in this cased called PatrolZone and --- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -PatrolZone = ZONE:New( "Patrol Zone" ) - --- Create and object (in this case called AICasZone) and --- using the functions AI_CAS_ZONE assign the parameters that define this object --- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) - --- Create an object (in this case called Targets) and --- using the GROUP function find the group labeled "Targets" and assign it to this object -Targets = GROUP:FindByName("Targets") - - --- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function -AICasZone:SetControllable( CASPlane ) - --- Tell the group CASPlane to start the mission in 1 second. -AICasZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. - --- After 10 minutes, tell the group CASPlane to engage the targets located in the engagement zone called CASEngagement Zone. (600 is 600 seconds) -AICasZone:__Engage( 600, 350, 4000 ) -- Engage after 10 minutes with a speed of 350 km/h and an altitude of 4000 meters. - --- After 12 minutes, tell the group CASPlane to abort the engagement. -AICasZone:__Abort( 720 ) -- Abort the engagement. - --- Check every 60 seconds whether the Targets have been eliminated. --- When the trigger completed has been fired, the Plane will go back to the Patrol Zone. -Check, CheckScheduleID = SCHEDULER:New(nil, - function() - if Targets:IsAlive() and Targets:GetSize() > 5 then - BASE:E( "Test Mission: " .. Targets:GetSize() .. " targets left to be destroyed.") - else - BASE:E( "Test Mission: The required targets are destroyed." ) - AICasZone:__Accomplish( 1 ) -- Now they should fly back to teh patrolzone and patrol. - end - end, {}, 20, 60, 0.2 ) - -function AICasZone:OnAfterAbort(Controllable,From,Event,To) - BASE:E( "MISSION ABORT! Back to patrol zone." ) - MESSAGE:New("Mission ABORTED! Back to the Patrol Zone!",30,"ALERT!"):ToAll() -end - --- When the targets in the zone are destroyed, (see scheduled function), the planes will return home ... -function AICasZone:OnAfterAccomplish( Controllable, From, Event, To ) - BASE:E( "Test Mission: Sending the Su-25T back to base." ) - Check:Stop( CheckScheduleID ) - AICasZone:__RTB( 1 ) -end diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.miz b/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.miz deleted file mode 100644 index 560fb7e27..000000000 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-004 - CAS in a Zone by Airplane Group - Test Abort/CAS-004 - CAS in a Zone by Airplane Group - Test Abort.miz and /dev/null differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend.lua b/Moose Test Missions/CAS - Close Air Support/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend.lua deleted file mode 100644 index 203eed00a..000000000 --- a/Moose Test Missions/CAS - Close Air Support/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend.lua +++ /dev/null @@ -1,78 +0,0 @@ ---- --- Name: CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend --- Author: FlightControl --- Date Created: 18 Mar 2017 --- --- # Situation: --- --- A group of 4 Su-25T at patrolling north of an engage zone for 1 minute. --- After 1 minute, the command center orders the Su-25T to engage the zone and execute a CAS. --- The planes should expend 4 weapons per run. --- --- # Test cases: --- --- 1. Observe that the Su-25T is patrolling in the patrol zone, until the engage command is given. --- 2. The Su-25T are not detecting any target during the patrol. --- 3. When the Su-25T is commanded to engage, the group will fly to the engage zone --- 3.1. The approach speed to the engage zone is set to 400 km/h. --- 3.2. The altitude to the engage zone and CAS execution is set to 500 meters. --- 3.3. The planes should expend 4 weapons per run. --- 4. Detection is activated and detected targets within the engage zone are assigned for CAS. --- 5. Observe the Su-25T eliminating the targets. --- 6. Observe the Su-25T defenses. --- 7. When all targets within the engage zone are destroyed, the Su-25T CAS task is set to Accomplished. --- 8. The Su-25T will return to base. - - - --- Create a local variable (in this case called CASEngagementZone) and --- using the ZONE function find the pre-defined zone called "Engagement Zone" --- currently on the map and assign it to this variable -CASEngagementZone = ZONE:New( "Engagement Zone" ) - --- Create a local variable (in this case called CASPlane) and --- using the GROUP function find the aircraft group called "Plane" and assign to this variable -CASPlane = GROUP:FindByName( "Plane" ) - --- Create a local Variable (in this cased called PatrolZone and --- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -PatrolZone = ZONE:New( "Patrol Zone" ) - --- Create and object (in this case called AICasZone) and --- using the functions AI_CAS_ZONE assign the parameters that define this object --- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) - --- Create an object (in this case called Targets) and --- using the GROUP function find the group labeled "Targets" and assign it to this object -Targets = GROUP:FindByName("Targets") - - --- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function -AICasZone:SetControllable( CASPlane ) - --- Tell the group CASPlane to start the mission in 1 second. -AICasZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. - --- After 1 minute, tell the group CASPlane to engage the targets located in the engagement zone called CASEngagement Zone. (600 is 600 seconds) -AICasZone:__Engage( 60, 600, 8000, AI.Task.WeaponExpend.FOUR ) -- Engage with a speed of 600 km/h and an altitude of 8000 meters, weapn expend 4. - --- Check every 60 seconds whether the Targets have been eliminated. --- When the trigger completed has been fired, the Plane will go back to the Patrol Zone. -Check, CheckScheduleID = SCHEDULER:New(nil, - function() - if Targets:IsAlive() and Targets:GetSize() > 5 then - BASE:E( "Test Mission: " .. Targets:GetSize() .. " targets left to be destroyed.") - else - BASE:E( "Test Mission: The required targets are destroyed." ) - AICasZone:__Accomplish( 1 ) -- Now they should fly back to teh patrolzone and patrol. - end - end, {}, 20, 60, 0.2 ) - - --- When the targets in the zone are destroyed, (see scheduled function), the planes will return home ... -function AICasZone:OnAfterAccomplish( Controllable, From, Event, To ) - BASE:E( "Test Mission: Sending the Su-25T back to base." ) - Check:Stop( CheckScheduleID ) - AICasZone:__RTB( 1 ) -end diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend.miz b/Moose Test Missions/CAS - Close Air Support/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend.miz deleted file mode 100644 index acf7bbe90..000000000 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend/CAS-005 - CAS in a Zone by Airplane Group - Engage with WeaponExpend.miz and /dev/null differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.lua b/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.lua deleted file mode 100644 index 42f76f38f..000000000 --- a/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.lua +++ /dev/null @@ -1,76 +0,0 @@ ---- --- Name: CAS-010 - CAS in a Zone by Helicopter --- Author: FlightControl --- Date Created: 6 February 2017 --- --- # Situation: --- --- A group of 1 Ka-50 patrolling north of an engage zone for 1 minute. --- After 1 minute, the command center orders the Ka-50 to engage the zone and execute a CAS. --- --- # Test cases: --- --- 1. Observe that the Ka-50 is patrolling in the patrol zone, until the engage command is given. --- 2. The Ka-50 are not detecting any target during the patrol. --- 3. When the Ka-50 is commanded to engage, the group will fly to the engage zone. --- 3.1. Engage Speed is set to 100 km/h. --- 3.2. Engage Altitude is set to 150 meters. --- 4. Detection is activated and detected targets within the engage zone are assigned for CAS. --- 5. Observe the Ka-50 eliminating the targets. --- 6. Observe the Ka-50 defenses. --- 7. When all targets within the engage zone are destroyed, the Ka-50 CAS task is set to Accomplished. --- 8. The Ka-50 will return to base. - - - --- Create a local variable (in this case called CASEngagementZone) and --- using the ZONE function find the pre-defined zone called "Engagement Zone" --- currently on the map and assign it to this variable -CASEngagementZone = ZONE:New( "Engagement Zone" ) - --- Create a local variable (in this case called CASPlane) and --- using the GROUP function find the aircraft group called "Plane" and assign to this variable -CASPlane = GROUP:FindByName( "Helicopter" ) - --- Create a local Variable (in this cased called PatrolZone and --- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -PatrolZone = ZONE:New( "Patrol Zone" ) - --- Create and object (in this case called AICasZone) and --- using the functions AI_CAS_ZONE assign the parameters that define this object --- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) - --- Create an object (in this case called Targets) and --- using the GROUP function find the group labeled "Targets" and assign it to this object -Targets = GROUP:FindByName("Targets") - - --- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function -AICasZone:SetControllable( CASPlane ) - --- Tell the group CASPlane to start the mission in 1 second. -AICasZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. - --- After 10 minutes, tell the group CASPlane to engage the targets located in the engagement zone called CASEngagement Zone. (600 is 600 seconds) -AICasZone:__Engage( 60, 100, 150 ) - --- Check every 60 seconds whether the Targets have been eliminated. --- When the trigger completed has been fired, the Plane will go back to the Patrol Zone. -Check, CheckScheduleID = SCHEDULER:New(nil, - function() - if Targets:IsAlive() and Targets:GetSize() > 5 then - BASE:E( "Test Mission: " .. Targets:GetSize() .. " targets left to be destroyed.") - else - BASE:E( "Test Mission: The required targets are destroyed." ) - AICasZone:__Accomplish( 1 ) -- Now they should fly back to teh patrolzone and patrol. - end - end, {}, 20, 60, 0.2 ) - - --- When the targets in the zone are destroyed, (see scheduled function), the planes will return home ... -function AICasZone:OnAfterAccomplish( Controllable, From, Event, To ) - BASE:E( "Test Mission: Sending the Su-25T back to base." ) - Check:Stop( CheckScheduleID ) - AICasZone:__RTB( 1 ) -end diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz b/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz deleted file mode 100644 index 18f37c504..000000000 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz and /dev/null differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.lua b/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.lua deleted file mode 100644 index dbf2bcb05..000000000 --- a/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.lua +++ /dev/null @@ -1,75 +0,0 @@ ---- --- Name: CAS-011 - CAS in a Zone by Helicopter Group --- Author: FlightControl --- Date Created: 6 February 2017 --- --- # Situation: --- --- A group of 4 Ka-50 patrolling north of an engage zone for 1 minute. --- After 1 minute, the command center orders the Ka-50 to engage the zone and execute a CAS. --- --- # Test cases: --- --- 1. Observe that the Ka-50 is patrolling in the patrol zone, until the engage command is given. --- 2. The Ka-50 are not detecting any target during the patrol. --- 3. When the Ka-50 is commanded to engage, the group will fly to the engage zone. --- 3.1. Engage Speed is set to 100 km/h. --- 3.2. Engage Altitude is set to 150 meters. --- 4. Detection is activated and detected targets within the engage zone are assigned for CAS. --- 5. Observe the Ka-50 eliminating the targets. --- 6. Observe the Ka-50 defenses. --- 7. When all targets within the engage zone are destroyed, the Ka-50 CAS task is set to Accomplished. --- 8. The Ka-50 will return to base. - - --- Create a local variable (in this case called CASEngagementZone) and --- using the ZONE function find the pre-defined zone called "Engagement Zone" --- currently on the map and assign it to this variable -CASEngagementZone = ZONE:New( "Engagement Zone" ) - --- Create a local variable (in this case called CASPlane) and --- using the GROUP function find the aircraft group called "Plane" and assign to this variable -CASPlane = GROUP:FindByName( "Helicopter" ) - --- Create a local Variable (in this cased called PatrolZone and --- using the ZONE function find the pre-defined zone called "Patrol Zone" and assign it to this variable -PatrolZone = ZONE:New( "Patrol Zone" ) - --- Create and object (in this case called AICasZone) and --- using the functions AI_CAS_ZONE assign the parameters that define this object --- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -AICasZone = AI_CAS_ZONE:New( PatrolZone, 500, 1000, 500, 600, CASEngagementZone ) - --- Create an object (in this case called Targets) and --- using the GROUP function find the group labeled "Targets" and assign it to this object -Targets = GROUP:FindByName("Targets") - - --- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function -AICasZone:SetControllable( CASPlane ) - --- Tell the group CASPlane to start the mission in 1 second. -AICasZone:__Start( 1 ) -- They should statup, and start patrolling in the PatrolZone. - --- After 10 minutes, tell the group CASPlane to engage the targets located in the engagement zone called CASEngagement Zone. (600 is 600 seconds) -AICasZone:__Engage( 60, 100, 150 ) - --- Check every 60 seconds whether the Targets have been eliminated. --- When the trigger completed has been fired, the Plane will go back to the Patrol Zone. -Check, CheckScheduleID = SCHEDULER:New(nil, - function() - if Targets:IsAlive() and Targets:GetSize() > 5 then - BASE:E( "Test Mission: " .. Targets:GetSize() .. " targets left to be destroyed.") - else - BASE:E( "Test Mission: The required targets are destroyed." ) - AICasZone:__Accomplish( 1 ) -- Now they should fly back to teh patrolzone and patrol. - end - end, {}, 20, 60, 0.2 ) - - --- When the targets in the zone are destroyed, (see scheduled function), the planes will return home ... -function AICasZone:OnAfterAccomplish( Controllable, From, Event, To ) - BASE:E( "Test Mission: Sending the Su-25T back to base." ) - Check:Stop( CheckScheduleID ) - AICasZone:__RTB( 1 ) -end diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz b/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz deleted file mode 100644 index d0bda8a32..000000000 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz and /dev/null differ diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.lua b/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.lua deleted file mode 100644 index 05d2beba9..000000000 --- a/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.lua +++ /dev/null @@ -1,91 +0,0 @@ ---- --- Name: CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups --- Author: FlightControl --- Date Created: 6 February 2017 --- --- # Situation: --- --- A group of 4 Ka-50 and 5 Su-25T are patrolling north in two engage zone for 5 minutes. --- After 5 minutes, the command center orders the groups to engage the zone and execute a CAS. --- --- # Test cases: --- --- 1. Observe that the groups is patrolling in the patrol zone, until the engage command is given. --- 2. The groups are not detecting any target during the patrol. --- 3. When the groups is commanded to engage, the group will fly to the engage zone. --- 3.1. Engage Speed for the Su-25T is set to 350 km/h. --- 3.2. Engage Altitude for the Su-25T is set to 1500 meters. --- 3.3. Engage Speed for the Ka-50 is set to 100 km/h. --- 3.4. Engage Altitude for the Ka-50 is set to 150 meters. --- 4. Detection is activated and detected targets within the engage zone are assigned for CAS. --- 5. Observe the groups eliminating the targets. --- 6. Observe the groups defenses. --- 7. When all targets within the engage zone are destroyed, the groups CAS task is set to Accomplished. --- 8. The groups will return to base. - - - --- Create a local variable (in this case called CASEngagementZone) and --- using the ZONE function find the pre-defined zone called "Engagement Zone" --- currently on the map and assign it to this variable -CASEngagementZone = ZONE:New( "Engagement Zone" ) - --- Create a local variables (in this case called CASPlane and CASHelicopters) and --- using the GROUP function find the aircraft group called "Plane" and "Helicopter" and assign to these variables -CASPlane = GROUP:FindByName( "Plane" ) -CASHelicopter = GROUP:FindByName( "Helicopter" ) - --- Create two patrol zones, one for the Planes and one for the Helicopters. -PatrolZonePlanes = ZONE:New( "Patrol Zone Planes" ) -PatrolZoneHelicopters = ZONE:New( "Patrol Zone Helicopters" ) - --- Create and object (in this case called AICasZone) and --- using the functions AI_CAS_ZONE assign the parameters that define this object --- (in this case PatrolZone, 500, 1000, 500, 600, CASEngagementZone) -AICasZonePlanes = AI_CAS_ZONE:New( PatrolZonePlanes, 400, 500, 500, 2500, CASEngagementZone ) -AICasZoneHelicopters = AI_CAS_ZONE:New( PatrolZoneHelicopters, 100, 250, 300, 1000, CASEngagementZone ) - --- Create an object (in this case called Targets) and --- using the GROUP function find the group labeled "Targets" and assign it to this object -Targets = GROUP:FindByName("Targets") - - --- Tell the program to use the object (in this case called CASPlane) as the group to use in the CAS function -AICasZonePlanes:SetControllable( CASPlane ) -AICasZoneHelicopters:SetControllable( CASHelicopter ) - --- Tell the group CASPlane to start the mission in 1 second. -AICasZonePlanes:__Start( 1 ) -- They should startup, and start patrolling in the PatrolZone. -AICasZoneHelicopters:__Start( 1 ) -- They should startup, and start patrolling in the PatrolZone. - --- After 10 minutes, tell the group CASPlanes and CASHelicopters to engage the targets located in the engagement zone called CASEngagement Zone. -AICasZonePlanes:__Engage( 300, 350, 1500 ) -- Engage with a speed of 350 km/h and 1500 meter altitude. -AICasZoneHelicopters:__Engage( 300, 100, 150 ) -- Engage with a speed of 100 km/h and 150 meter altitude. - - --- Check every 60 seconds whether the Targets have been eliminated. --- When the trigger completed has been fired, the Planes and Helicopters will go back to the Patrol Zone. -Check, CheckScheduleID = SCHEDULER:New(nil, - function() - if Targets:IsAlive() and Targets:GetSize() > 5 then - BASE:E( "Test Mission: " .. Targets:GetSize() .. " targets left to be destroyed.") - else - BASE:E( "Test Mission: The required targets are destroyed." ) - Check:Stop( CheckScheduleID ) - AICasZonePlanes:__Accomplish( 1 ) -- Now they should fly back to teh patrolzone and patrol. - AICasZoneHelicopters:__Accomplish( 1 ) -- Now they should fly back to teh patrolzone and patrol. - end - end, {}, 20, 60, 0.2 ) - - --- When the targets in the zone are destroyed, (see scheduled function), the planes will return home ... -function AICasZonePlanes:OnAfterAccomplish( Controllable, From, Event, To ) - BASE:E( "Test Mission: Sending the Su-25T back to base." ) - AICasZonePlanes:__RTB( 1 ) -end - --- When the targets in the zone are destroyed, (see scheduled function), the helicpters will return home ... -function AICasZoneHelicopters:OnAfterAccomplish( Controllable, From, Event, To ) - BASE:E( "Test Mission: Sending the Ka-50 back to base." ) - AICasZoneHelicopters:__RTB( 1 ) -end diff --git a/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz b/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz deleted file mode 100644 index 490b66e68..000000000 Binary files a/Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS in 1 Radius Zone by Helicopter and AirPlane Groups.miz and /dev/null differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.lua b/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.lua deleted file mode 100644 index 4df5cc8d0..000000000 --- a/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.lua +++ /dev/null @@ -1,11 +0,0 @@ - -CargoEngineer = UNIT:FindByName( "Engineer" ) -InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 2000, 25 ) - -CargoCarrier = UNIT:FindByName( "Carrier" ) - --- This call will make the Cargo run to the CargoCarrier. --- Upon arrival at the CargoCarrier, the Cargo will be Loaded into the Carrier. --- This process is now fully automated. -InfantryCargo:Board( CargoCarrier ) - diff --git a/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz b/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz deleted file mode 100644 index 6d8d448e4..000000000 Binary files a/Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit Boarding.miz and /dev/null differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.lua b/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.lua deleted file mode 100644 index bbc6edc58..000000000 --- a/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.lua +++ /dev/null @@ -1,11 +0,0 @@ - -CargoEngineer = UNIT:FindByName( "Engineer" ) -InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 2000, 25 ) - -CargoCarrier = UNIT:FindByName( "Carrier" ) - --- This will Load immediately the Cargo into the Carrier, regardless where the Cargo is. -InfantryCargo:Load( CargoCarrier ) - --- This will Unboard the Cargo from the Carrier. -InfantryCargo:UnBoard() \ No newline at end of file diff --git a/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz b/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz deleted file mode 100644 index 11838ec94..000000000 Binary files a/Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 - Unit Unboarding.miz and /dev/null differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.lua b/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.lua deleted file mode 100644 index 6cf1b1f69..000000000 --- a/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.lua +++ /dev/null @@ -1,25 +0,0 @@ - -CargoEngineer = UNIT:FindByName( "Engineer" ) -InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 2000, 25 ) - -CargoCarrierFrom = UNIT:FindByName( "CarrierFrom" ) - -CargoCarrierTo = UNIT:FindByName( "CarrierTo" ) - --- This call will make the Cargo run to the CargoCarrier. --- Upon arrival at the CargoCarrier, the Cargo will be Loaded into the Carrier. --- This process is now fully automated. -InfantryCargo:Board( CargoCarrierFrom ) - --- Once the Cargo has been loaded into the Carrier, drive to a point and unload the Cargo. -function InfantryCargo:OnEnterLoaded() - self:__UnBoard( 1 ) - self.OnEnterLoaded = nil -end - --- Once the Cargo has been unloaded from the Carrier (the Cargo has arrived to the unload gathering point), OnBoard the Cargo in the other Carrier. -function InfantryCargo:OnEnterUnLoaded() - self:__Board( 1, CargoCarrierTo ) - self.OnEnterUnLoaded = nil -end - diff --git a/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz b/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz deleted file mode 100644 index ad54736e9..000000000 Binary files a/Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 - Unit Transferring.miz and /dev/null differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.lua b/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.lua deleted file mode 100644 index ddc9b940a..000000000 --- a/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.lua +++ /dev/null @@ -1,16 +0,0 @@ - -CargoSet = SET_BASE:New() -CargoSet:Add( "Engineer1", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer1" ), "Engineers", "Engineer", 81, 2000, 25 ) ) -CargoSet:Add( "Engineer2", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer2" ), "Engineers", "Engineer", 64, 2000, 25 ) ) -CargoSet:Add( "Engineer3", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer3" ), "Engineers", "Engineer", 72, 2000, 25 ) ) -CargoSet:Add( "Engineer4", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer4" ), "Engineers", "Engineer", 69, 2000, 25 ) ) - -InfantryCargo = AI_CARGO_GROUPED:New( CargoSet, "Engineers", "Engineers", 2000, 25 ) - -CargoCarrier = UNIT:FindByName( "Carrier" ) - --- This call will make the Cargo run to the CargoCarrier. --- Upon arrival at the CargoCarrier, the Cargo will be Loaded into the Carrier. --- This process is now fully automated. -InfantryCargo:Board( CargoCarrier ) - diff --git a/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz b/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz deleted file mode 100644 index 8733e3718..000000000 Binary files a/Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 - Group Boarding.miz and /dev/null differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.lua b/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.lua deleted file mode 100644 index d8f345422..000000000 --- a/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.lua +++ /dev/null @@ -1,16 +0,0 @@ - -CargoSet = SET_BASE:New() -CargoSet:Add( "Engineer1", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer1" ), "Engineers", "Engineer", 81, 2000, 25 ) ) -CargoSet:Add( "Engineer2", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer2" ), "Engineers", "Engineer", 64, 2000, 25 ) ) -CargoSet:Add( "Engineer3", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer3" ), "Engineers", "Engineer", 72, 2000, 25 ) ) -CargoSet:Add( "Engineer4", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer4" ), "Engineers", "Engineer", 69, 2000, 25 ) ) - -InfantryCargo = AI_CARGO_GROUPED:New( CargoSet, "Engineers", "Engineers", 2000, 25 ) - -CargoCarrier = UNIT:FindByName( "Carrier" ) - --- This will Load immediately the Cargo into the Carrier, regardless where the Cargo is. -InfantryCargo:Load( CargoCarrier ) - --- This will Unboard the Cargo from the Carrier. -InfantryCargo:UnBoard() \ No newline at end of file diff --git a/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz b/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz deleted file mode 100644 index 4a238d033..000000000 Binary files a/Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 - Group Unboarding.miz and /dev/null differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.lua b/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.lua deleted file mode 100644 index 49411831f..000000000 --- a/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.lua +++ /dev/null @@ -1,29 +0,0 @@ - -CargoSet = SET_BASE:New() -CargoSet:Add( "Engineer1", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer1" ), "Engineers", "Engineer", 81, 2000, 25 ) ) -CargoSet:Add( "Engineer2", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer2" ), "Engineers", "Engineer", 64, 2000, 25 ) ) -CargoSet:Add( "Engineer3", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer3" ), "Engineers", "Engineer", 72, 2000, 25 ) ) -CargoSet:Add( "Engineer4", AI_CARGO_UNIT:New( UNIT:FindByName( "Engineer4" ), "Engineers", "Engineer", 69, 2000, 25 ) ) - -InfantryCargo = AI_CARGO_GROUPED:New( CargoSet, "Engineers", "Engineers", 2000, 25 ) - -CargoCarrierFrom = UNIT:FindByName( "CarrierFrom" ) - -CargoCarrierTo = UNIT:FindByName( "CarrierTo" ) - --- This call will make the Cargo run to the CargoCarrier. --- Upon arrival at the CargoCarrier, the Cargo will be Loaded into the Carrier. --- This process is now fully automated. -InfantryCargo:Board( CargoCarrierFrom ) - --- Once the Cargo has been loaded into the Carrier, drive to a point and unload the Cargo. -function InfantryCargo:OnEnterLoaded() - self:__UnBoard( 1 ) - self.OnEnterLoaded = nil -end - --- Once the Cargo has been unloaded from the Carrier (the Cargo has arrived to the unload gathering point), OnBoard the Cargo in the other Carrier. -function InfantryCargo:OnEnterUnLoaded() - self:__Board( 1, CargoCarrierTo ) - self.OnEnterUnLoaded = nil -end diff --git a/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz b/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz deleted file mode 100644 index 748ead674..000000000 Binary files a/Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 - Group Transferring.miz and /dev/null differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.lua b/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.lua deleted file mode 100644 index c26fa24fa..000000000 --- a/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.lua +++ /dev/null @@ -1,11 +0,0 @@ - -DeliveryUnit = UNIT:FindByName( "Delivery" ) -Letter = AI_CARGO_PACKAGE:New( DeliveryUnit, "Letter", "Secret Orders", "0.3", 2000, 25 ) - -CargoCarrier = UNIT:FindByName( "Carrier" ) - --- This call will make the Cargo run to the CargoCarrier. --- Upon arrival at the CargoCarrier, the Cargo will be Loaded into the Carrier. --- This process is now fully automated. -Letter:Board( CargoCarrier, 40, 3, 25, 90 ) - diff --git a/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz b/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz deleted file mode 100644 index 764062734..000000000 Binary files a/Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 - Package Boarding.miz and /dev/null differ diff --git a/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.lua b/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.lua deleted file mode 100644 index 716b6186c..000000000 --- a/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.lua +++ /dev/null @@ -1,13 +0,0 @@ - -CargoEngineer = UNIT:FindByName( "Engineer" ) -InfantryCargo = AI_CARGO_UNIT:New( CargoEngineer, "Engineer", "Engineer Sven", "81", 2000, 25 ) - -CargoCarrier = UNIT:FindByName( "Carrier" ) - --- This will Load the Cargo into the Carrier, regardless where the Cargo is. -InfantryCargo:Load( CargoCarrier ) - --- This will Unboard the Cargo from the Carrier. --- The Cargo will run from the Carrier to a point in the NearRadius around the Carrier. --- Unboard the Cargo with a speed of 10 km/h, go to 200 meters 180 degrees from the Carrier, iin a zone of 25 meters (NearRadius). -InfantryCargo:UnBoard( 10, 2, 20, 10, 180 ) \ No newline at end of file diff --git a/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz b/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz deleted file mode 100644 index 478ad554d..000000000 Binary files a/Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 - Package Unboarding.miz and /dev/null differ diff --git a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.lua b/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.lua deleted file mode 100644 index 7afca5c49..000000000 --- a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.lua +++ /dev/null @@ -1,24 +0,0 @@ ---- --- Name: DET-001 - Detection Areas --- Author: FlightControl --- Date Created: 04 Feb 2017 --- --- # Situation: --- --- A small blue vehicle with laser detection methods is detecting targets. --- Targets are grouped within areas. A detection range and zone range is given to group the detected units. --- This demo will group 5 red vehicles in areas. One vehicle is diving from one group to the other. --- --- # Test cases: --- --- 1. Observe the flaring of the areas formed --- 2. Observe the smoking of the units detected --- 3. Observe the areas being flexibly changed very detection run. --- 4. The truck driving from the one group to the other, will leave the first area, and will join the second. --- 5. While driving in between the areas, it will have a separate area. - -FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC Group" ):FilterStart() - -FACDetection = DETECTION_AREAS:New( FACSetGroup, 150, 250 ):BoundDetectedZones():SmokeDetectedUnits() - -FACDetection:__Start( 5 ) \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz b/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz deleted file mode 100644 index c05ea2ea9..000000000 Binary files a/Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001 - Detection Areas.miz and /dev/null differ diff --git a/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.lua b/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.lua deleted file mode 100644 index 7fcc33cc6..000000000 --- a/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.lua +++ /dev/null @@ -1,56 +0,0 @@ ---- --- Name: DET-100 - Detection Probability Distance --- Author: FlightControl --- Date Created: 04 Feb 2017 --- --- # Situation: --- --- Demonstrates the DistanceProbability factor during the detection of units. --- --- Two JTAC are detecting 4 units, which are 10 km away. --- The first JTAC has no DistanceProbability set. --- The second JTAC has a DistanceProbability set. --- --- # Test cases: --- --- 1. Observe the reporting of both the first and second JTAC. The second should report slower the detection than the first. --- 2. Eventually all units should be detected by both JTAC. - -RecceSetGroup1 = SET_GROUP:New():FilterPrefixes( "Recce 1" ):FilterStart() -RecceSetGroup2 = SET_GROUP:New():FilterPrefixes( "Recce 2" ):FilterStart() - -HQ = GROUP:FindByName( "HQ" ) - -CC = COMMANDCENTER:New( HQ, "HQ" ) - -RecceDetection1 = DETECTION_UNITS:New( RecceSetGroup1 ) - -RecceDetection2 = DETECTION_UNITS:New( RecceSetGroup2 ) -RecceDetection2:SetDistanceProbability( 0.2 ) -- Set a 20% probability that a vehicle can be detected at 4km distance. - -RecceDetection1:Start() -RecceDetection2:Start() - ---- OnAfter Transition Handler for Event Detect. --- @param Functional.Detection#DETECTION_UNITS self --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function RecceDetection1:OnAfterDetect(From,Event,To) - - local DetectionReport = RecceDetection1:DetectedReportDetailed() - - HQ:MessageToAll( DetectionReport, 15, "Detection 1 - No distance Probability" ) -end - ---- OnAfter Transition Handler for Event Detect. --- @param Functional.Detection#DETECTION_UNITS self --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function RecceDetection2:OnAfterDetect(From,Event,To) - - local DetectionReport = RecceDetection2:DetectedReportDetailed() - - HQ:MessageToAll( DetectionReport, 15, "Detection 2 - Distance Probability" ) -end diff --git a/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.miz b/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.miz deleted file mode 100644 index 067efcd42..000000000 Binary files a/Moose Test Missions/DET - Detection/DET-100 - Detection Probability Distance/DET-100 - Detection Probability Distance.miz and /dev/null differ diff --git a/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.lua b/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.lua deleted file mode 100644 index a09bef36d..000000000 --- a/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.lua +++ /dev/null @@ -1,9 +0,0 @@ - - -FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC Group" ):FilterStart() - -FACDetection = DETECTION_AREAS:New( FACSetGroup, 1000, 250 ) -SeadClientSet = SET_CLIENT:New():FilterCoalitions( "blue" ):FilterStart() -DestroyClientSet = SET_CLIENT:New():FilterCoalitions( "blue" ):FilterStart() - -FACReporting = FAC_REPORTING:New( FACClientSet, FACDetection ) diff --git a/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz b/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz deleted file mode 100644 index 58d3f0069..000000000 Binary files a/Moose Test Missions/DET - Detection/DET-101 - Detection Reporting/DET-101 - Detection Reporting.miz and /dev/null differ diff --git a/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-100 - Detection Probability Distance.miz b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-100 - Detection Probability Distance.miz deleted file mode 100644 index 262bc3681..000000000 Binary files a/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-100 - Detection Probability Distance.miz and /dev/null differ diff --git a/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.lua b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.lua deleted file mode 100644 index 6659d2c08..000000000 --- a/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.lua +++ /dev/null @@ -1,62 +0,0 @@ ---- --- Name: DET-120 - Detection Probability Zones --- Author: FlightControl --- Date Created: 04 Feb 2017 --- --- # Situation: --- --- Demonstrates the DistanceProbability factor during the detection of units. --- --- Two JTAC are detecting 4 units, which are 10 km away. --- The first JTAC has no DistanceProbability set. --- The second JTAC has a DistanceProbability set. --- --- # Test cases: --- --- 1. Observe the reporting of both the first and second JTAC. The second should report slower the detection than the first. --- 2. Eventually all units should be detected by both JTAC. - -RecceSetGroup1 = SET_GROUP:New():FilterPrefixes( "Recce 1" ):FilterStart() -RecceSetGroup2 = SET_GROUP:New():FilterPrefixes( "Recce 2" ):FilterStart() - -HQ = GROUP:FindByName( "HQ" ) - -CC = COMMANDCENTER:New( HQ, "HQ" ) - -RecceDetection1 = DETECTION_UNITS:New( RecceSetGroup1 ) - -RecceDetection2 = DETECTION_UNITS:New( RecceSetGroup2 ) - -ForestZone = ZONE_POLYGON:New( "ForestZone", GROUP:FindByName( "ForestZone" ) ) - -RecceDetection2:SetZoneProbability( { { ForestZone, 0.1 } } ) -- Set a 10% probability that a vehicle can be detected within the forest. - - -RecceDetection1:Start() -RecceDetection2:Start() - ---- OnAfter Transition Handler for Event Detect. --- @param Functional.Detection#DETECTION_UNITS self --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function RecceDetection1:OnAfterDetect(From,Event,To) - - local DetectionReport = self:DetectedReportDetailed() - - HQ:MessageToAll( DetectionReport, 15, "Detection 1 - No Zone Probability" ) -end - ---- OnAfter Transition Handler for Event Detect. --- @param Functional.Detection#DETECTION_UNITS self --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function RecceDetection2:OnAfterDetect(From,Event,To) - - local DetectionReport = self:DetectedReportDetailed() - - HQ:MessageToAll( DetectionReport, 15, "Detection 2 - Forest Zone Probability" ) -end - -garbagecollect() diff --git a/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.miz b/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.miz deleted file mode 100644 index 302fa29fc..000000000 Binary files a/Moose Test Missions/DET - Detection/DET-120 - Detection Probability Zones/DET-120 - Detection Probability Zones.miz and /dev/null differ diff --git a/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.lua b/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.lua deleted file mode 100644 index 00fd71b74..000000000 --- a/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.lua +++ /dev/null @@ -1,42 +0,0 @@ ---- --- Name: DET-200 - Detection UNITS --- Author: FlightControl --- Date Created: 13 Feb 2017 --- --- # Situation: --- --- Demonstrates the detection of units. --- --- A Set of Recce are detecting a large group of units, which are 5 km away. --- Select one of the blue Recce, and press F7. Watch the reporting of the detection evolve. --- The enemy is approaching. --- --- # Test cases: --- --- 1. Observe the detection reporting of both the Recce. --- 2. Eventually all units should be detected by both Recce. - -RecceSetGroup = SET_GROUP:New():FilterPrefixes( "Recce" ):FilterStart() - -HQ = GROUP:FindByName( "HQ" ) - -CC = COMMANDCENTER:New( HQ, "HQ" ) - -RecceDetection = DETECTION_UNITS:New( RecceSetGroup ) - -RecceDetection:Start() - ---- OnAfter Transition Handler for Event Detect. --- @param Functional.Detection#DETECTION_UNITS self --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function RecceDetection:OnAfterDetect(From,Event,To) - - self:E("Detect") - - local DetectionReport = RecceDetection:DetectedReportDetailed() - - CC:MessageToAll( DetectionReport, 15, "" ) -end - diff --git a/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.miz b/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.miz deleted file mode 100644 index 593b2bc82..000000000 Binary files a/Moose Test Missions/DET - Detection/DET-200 - Detection UNITS/DET-200 - Detection UNITS.miz and /dev/null differ diff --git a/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.lua b/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.lua deleted file mode 100644 index e39217d20..000000000 --- a/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.lua +++ /dev/null @@ -1,42 +0,0 @@ ---- --- Name: DET-210 - Detection TYPES --- Author: FlightControl --- Date Created: 13 Feb 2017 --- --- # Situation: --- --- Demonstrates the detection of units. --- --- A Set of Recce are detecting a large group of units, which are 5 km away. --- Select one of the blue Recce, and press F7. Watch the reporting of the detection evolve. --- The enemy is approaching. --- --- The blue Recce will report the detected units grouped per vehicle type! --- --- # Test cases: --- --- 1. Observe the detection reporting of both the Recce. --- 2. Eventually all units should be detected by both Recce. - -RecceSetGroup = SET_GROUP:New():FilterPrefixes( "Recce" ):FilterStart() - -HQ = GROUP:FindByName( "HQ" ) - -CC = COMMANDCENTER:New( HQ, "HQ" ) - -RecceDetection = DETECTION_TYPES:New( RecceSetGroup ) - -RecceDetection:Start() - ---- OnAfter Transition Handler for Event Detect. --- @param Functional.Detection#DETECTION_UNITS self --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function RecceDetection:OnAfterDetect(From,Event,To) - - local DetectionReport = RecceDetection:DetectedReportDetailed() - - CC:MessageToAll( DetectionReport, 15, "" ) -end - diff --git a/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.miz b/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.miz deleted file mode 100644 index 01f7dfdc9..000000000 Binary files a/Moose Test Missions/DET - Detection/DET-210 - Detection TYPES/DET-210 - Detection TYPES.miz and /dev/null differ diff --git a/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.lua b/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.lua deleted file mode 100644 index 34cf755e6..000000000 --- a/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.lua +++ /dev/null @@ -1,58 +0,0 @@ ---- --- Name: DET-100 - Detection Probability Distance --- Author: FlightControl --- Date Created: 04 Feb 2017 --- --- # Situation: --- --- Demonstrates the DistanceProbability factor during the detection of units. --- --- Two JTAC are detecting 4 units, which are 10 km away. --- The first JTAC has no DistanceProbability set. --- The second JTAC has a DistanceProbability set. --- --- # Test cases: --- --- 1. Observe the reporting of both the first and second JTAC. The second should report slower the detection than the first. --- 2. Eventually all units should be detected by both JTAC. - -RecceSetGroup1 = SET_GROUP:New():FilterPrefixes( "Recce 1" ):FilterStart() -RecceSetGroup2 = SET_GROUP:New():FilterPrefixes( "Recce 2" ):FilterStart() - -HQ = GROUP:FindByName( "HQ" ) - -CC = COMMANDCENTER:New( HQ, "HQ" ) - -RecceDetection1 = DETECTION_AREAS:New( RecceSetGroup1, 1000 ) -RecceDetection1:BoundDetectedZones() - -RecceDetection2 = DETECTION_AREAS:New( RecceSetGroup2, 1000 ) -RecceDetection2:SetDistanceProbability( 0.2 ) -- Set a 20% probability that a vehicle can be detected at 4km distance. -RecceDetection1:BoundDetectedZones() - -RecceDetection1:Start() -RecceDetection2:Start() - ---- OnAfter Transition Handler for Event Detect. --- @param Functional.Detection#DETECTION_UNITS self --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function RecceDetection1:OnAfterDetect(From,Event,To) - - local DetectionReport = RecceDetection1:DetectedReportDetailed() - - HQ:MessageToAll( DetectionReport, 15, "Detection 1 - No distance Probability" ) -end - ---- OnAfter Transition Handler for Event Detect. --- @param Functional.Detection#DETECTION_UNITS self --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function RecceDetection2:OnAfterDetect(From,Event,To) - - local DetectionReport = RecceDetection2:DetectedReportDetailed() - - HQ:MessageToAll( DetectionReport, 15, "Detection 2 - Distance Probability" ) -end \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.miz b/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.miz deleted file mode 100644 index e31a2d417..000000000 Binary files a/Moose Test Missions/DET - Detection/DET-250 - Detection AREAS/DET-250 - Detection AREAS.miz and /dev/null differ diff --git a/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.lua b/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.lua deleted file mode 100644 index cbba0418f..000000000 --- a/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.lua +++ /dev/null @@ -1,46 +0,0 @@ ---- --- Name: DET-255 - Detection AEAS with Destroys --- Author: FlightControl --- Date Created: 06 Mar 2017 --- --- # Situation: --- --- A small blue vehicle with laser detection methods is detecting targets. --- Targets are grouped within areas. A detection range and zone range is given to group the detected units. --- This demo will group red vehicles in areas. One vehicle is diving from one group to the other. --- After 30 seconds, one vehicle is destroyed in a zone. --- After 60 seconds, a vehicle is destroyed that is a leader of a zone. --- After 90 seconds, all vehicles are destroyed in a zone. --- --- # Test cases: --- --- 1. Observe the flaring of the areas formed --- 2. Observe the smoking of the units detected --- 3. Observe the areas being flexibly changed very detection run. --- 4. The truck driving from the one group to the other, will leave the first area, and will join the second. --- 5. While driving in between the areas, it will have a separate area. --- 6. Observe the correct removal or relocation of the ZONEs. - -FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC Group" ):FilterStart() - -FACDetection = DETECTION_AREAS:New( FACSetGroup, 150, 250 ):BoundDetectedZones():SmokeDetectedUnits() - -FACDetection:__Start( 5 ) - -SCHEDULER:New( nil,function() - local Target = UNIT:FindByName( "Target #004") - Target:Destroy() - end, {}, 30 - ) - -SCHEDULER:New( nil,function() - local Target = UNIT:FindByName( "Target #006") - Target:Destroy() - end, {}, 60 - ) - -SCHEDULER:New( nil,function() - local Target = UNIT:FindByName( "Target #007") - Target:Destroy() - end, {}, 90 - ) \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.miz b/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.miz deleted file mode 100644 index 8892f6c23..000000000 Binary files a/Moose Test Missions/DET - Detection/DET-255 - Detection AEAS with Destroys/DET-255 - Detection AEAS with Destroys.miz and /dev/null differ diff --git a/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.lua b/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.lua deleted file mode 100644 index 41277f443..000000000 --- a/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.lua +++ /dev/null @@ -1,70 +0,0 @@ ---- --- Name: DET-500 - Handle Detected Event - Govern Artillery Demo --- Author: FlightControl --- Date Created: 13 Feb 2017 --- --- # Situation: --- --- Demonstrates the detection of units. --- --- A Set of Recces are detecting a large group of units, which are 5 km away. --- Once the Recces detect the enemy, the artilley units are controlled and will fire a missile to the target. --- --- # Test cases: --- --- 1. Observe the detected reporting of the recces. --- 2. When one Recce group detects a target, it will select an artillery unit and fire a missile. --- 3. This will run until all Recces have eliminated the targets. - -RecceSetGroup = SET_GROUP:New():FilterCoalitions( "blue" ):FilterPrefixes( "Recce" ):FilterStart() -ArtillerySetGroup = SET_GROUP:New():FilterCoalitions( "blue" ):FilterPrefixes( "Artillery" ):FilterStart() - -HQ = GROUP:FindByName( "HQ" ) - -CC = COMMANDCENTER:New( HQ, "HQ" ) - -RecceDetection = DETECTION_UNITS:New( RecceSetGroup ) -RecceDetection:SetDetectionInterval( 5 ) - -RecceDetection:Start() - ---- OnAfter Transition Handler for Event Detect. --- @param Functional.Detection#DETECTION_UNITS self --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. -function RecceDetection:OnAfterDetect(From,Event,To) - - local DetectionReport = RecceDetection:DetectedReportDetailed() - - CC:GetPositionable():MessageToAll( DetectionReport, 15, "" ) -end - -local ArtilleryTime = {} -local ArtilleryAim = 180 - ---- OnAfter Transition Handler for Event Detect. --- @param Functional.Detection#DETECTION_UNITS self --- @param #string From The From State string. --- @param #string Event The Event string. --- @param #string To The To State string. --- @param Wrapper.Unit#UNIT DetectedUnits -function RecceDetection:OnAfterDetected( From, Event, To, DetectedUnits ) - self:E( { From, Event, To, DetectedUnits } ) - - for DetectedUnitID, DetectedUnit in pairs( DetectedUnits ) do - local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT - local Artillery = ArtillerySetGroup:GetRandom() -- Wrapper.Group#GROUP - - if ArtilleryTime[Artillery] and ArtilleryTime[Artillery] <= timer.getTime() - ArtilleryAim then - ArtilleryTime[Artillery] = nil - end - - if not ArtilleryTime[Artillery] then - local Task = Artillery:TaskFireAtPoint( DetectedUnit:GetVec2(), 500, 4 ) -- Fire 2 rockets to the target point. - Artillery:SetTask( Task, 0.5 ) - ArtilleryTime[Artillery] = timer.getTime() - end - - end -end \ No newline at end of file diff --git a/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.miz b/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.miz deleted file mode 100644 index 678497906..000000000 Binary files a/Moose Test Missions/DET - Detection/DET-500 - Handle Detected Event - Govern Artillery Demo/DET-500 - Handle Detected Event - Govern Artillery Demo.miz and /dev/null differ diff --git a/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.lua b/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.lua deleted file mode 100644 index cc2969de4..000000000 --- a/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.lua +++ /dev/null @@ -1,25 +0,0 @@ ---- --- Name: DET-900 - Detection Test with RED FACA --- Author: FlightControl --- Date Created: 06 Mar 2017 --- --- # Situation: --- --- A red FACA is detecting targets while airborne. --- Targets are grouped within areas. A detection range and zone range is given to group the detected units. --- This demo will group blue vehicles in areas. --- Upon the detection capabilities of the red FACA, the blue vehicles will be grouped when detected. --- All blue vehicles have ROE on hold. --- --- # Test cases: --- --- 1. Observe the tyres put around the detected areas formed --- 2. Observe the smoking of the units detected --- 3. Observe the areas being flexibly changed very detection run. - -FACSetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() - -FACDetection = DETECTION_AREAS:New( FACSetGroup, 2000, 250 ):BoundDetectedZones():SmokeDetectedUnits() - - -FACDetection:__Start( 5 ) diff --git a/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.miz b/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.miz deleted file mode 100644 index 3817f41f7..000000000 Binary files a/Moose Test Missions/DET - Detection/DET-900 - Detection Test with RED FACA/DET-900 - Detection Test with RED FACA.miz and /dev/null differ diff --git a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.lua b/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.lua deleted file mode 100644 index 054b2325c..000000000 --- a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.lua +++ /dev/null @@ -1,89 +0,0 @@ ---- --- Name: ESC-001 - Escorting Helicopters --- Author: FlightControl --- Date Created: 10 Mar 2017 --- --- # Situation: --- --- Your client helicopter is flying in the battle field. --- It is escorted by an MI-28N, which you can command... --- Use the menu options to: --- - Make the escort follow you. --- - Report detected targets. --- - Attack targets --- - Flare --- --- # Test cases: --- --- 1. When executing the commands, observe the MI-28N reactions. - -do - local function EventAliveHelicopter( Client ) - local EscortGroupHeli1 = SpawnEscortHeli:ReSpawn(1) - local EscortHeli1 = ESCORT - :New( Client, EscortGroupHeli1, "Escort Helicopter" ) - :MenuFollowAt( 100 ) - :MenuFollowAt( 200 ) - :MenuHoldAtEscortPosition( 20, 10, "Hold at %d meters for %d seconds" ) - :MenuHoldAtLeaderPosition( 120 ) - :MenuFlare( "Disperse Flares" ) - :MenuSmoke() - :MenuReportTargets( 60, 20 ) - :MenuResumeMission() - :MenuROE() - :MenuAssistedAttack() - - EscortHeli1:SetDetection( EscortHeliDetection ) - - local EscortGroupArtillery = SpawnEscortArtillery:ReSpawn(1) - local EscortArtillery = ESCORT - :New( Client, EscortGroupArtillery, "Escort Artillery" ) - :Menus() - end - - local function EventAlivePlane( Client ) - local EscortGroupPlane2 = SpawnEscortPlane:ReSpawn(1) - local EscortPlane2 = ESCORT - :New( Client, EscortGroupPlane2, "Escort Test Plane" ) - :MenuFollowAt( 100 ) - :MenuFollowAt( 200 ) - :MenuHoldAtEscortPosition( 20, 10, "Hold at %d meters for %d seconds" ) - :MenuHoldAtLeaderPosition( 120 ) - :MenuFlare( "Disperse Flares" ) - :MenuSmoke() - :MenuReportTargets( 60, 20 ) - :MenuResumeMission() - :MenuAssistedAttack() - :MenuROE() - :MenuEvasion() - - local EscortGroupGround2 = SpawnEscortGround:ReSpawn(1) - local EscortGround2 = ESCORT - :New( Client, EscortGroupGround2, "Test Ground" ) - :Menus() - - local EscortGroupShip2 = SpawnEscortShip:ReSpawn(1) - local EscortShip2 = ESCORT - :New( Client, EscortGroupShip2, "Test Ship" ) - :Menus() - end - - SpawnEscortHeli = SPAWN:New( "Escort Helicopter" ) - SpawnEscortPlane = SPAWN:New( "Escort Plane" ) - SpawnEscortGround = SPAWN:New( "Escort Ground" ) - SpawnEscortShip = SPAWN:New( "Escort Ship" ) - SpawnEscortArtillery = SPAWN:New( "Ground Attack Assistance" ) - - EscortHeliSetGroup = SET_GROUP:New():FilterPrefixes("Escort Helicopter"):FilterStart() - EscortHeliDetection = DETECTION_AREAS:New( EscortHeliSetGroup, 1000, 500 ) - - EscortHeliDetection:BoundDetectedZones() - EscortHeliDetection:SetDetectionInterval( 15 ) - - EscortClientHeli = CLIENT:FindByName( "Lead Helicopter", "Fly around and observe the behaviour of the escort helicopter" ):Alive( EventAliveHelicopter ) - EscortClientPlane = CLIENT:FindByName( "Lead Plane", "Fly around and observe the behaviour of the escort airplane. Select Navigate->Joun-Up and airplane should follow you. Change speed and directions." ) - :Alive( EventAlivePlane ) - -end - -env.info( "Test Mission loaded" ) diff --git a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz b/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz deleted file mode 100644 index c097c3281..000000000 Binary files a/Moose Test Missions/ESC - Escorting/ESC-001 - Escorting Helicopters/ESC-001 - Escorting Helicopters.miz and /dev/null differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.lua b/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.lua deleted file mode 100644 index b2257c66a..000000000 --- a/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.lua +++ /dev/null @@ -1,40 +0,0 @@ ---- --- Name: EVT-001 - API Demo 1 --- Author: FlightControl --- Date Created: 7 February 2017 --- --- # Situation: --- --- A task shoots another tank. If one of the is dead, the event will be catched. --- --- # Test cases: --- --- 1. Observe the tanks shooting each other. --- 2. If one of the tanks die, an event will be catched. --- 3. Observe the surviving unit smoking. - - -local Tank1 = UNIT:FindByName( "Tank A" ) -local Tank2 = UNIT:FindByName( "Tank B" ) - -Tank1:HandleEvent( EVENTS.Dead ) - -Tank2:HandleEvent( EVENTS.Dead ) - ---- @param Wrapper.Unit#UNIT self -function Tank1:OnEventDead( EventData ) - - self:SmokeGreen() -end - ---- @param Wrapper.Unit#UNIT self -function Tank2:OnEventDead( EventData ) - - self:SmokeBlue() -end - -function Tank2:OnEventCrash(EventData) - -end - - diff --git a/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz b/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz deleted file mode 100644 index 87e19707b..000000000 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001 - API Demo 1.miz and /dev/null differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.lua deleted file mode 100644 index 2f84ad795..000000000 --- a/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.lua +++ /dev/null @@ -1,25 +0,0 @@ ---- --- Name: EVT-100 - UNIT OnEventShot Example --- Author: FlightControl --- Date Created: 7 Feb 2017 --- --- # Situation: --- --- A plane is flying in the air and shoots an missile to a ground target. --- --- # Test cases: --- --- 1. Observe the plane shooting the missile. --- 2. Observe when the plane shoots the missile, a dcs.log entry is written in the logging. --- 3. Check the contents of the fields of the S_EVENT_SHOT entry. - -Plane = UNIT:FindByName( "Plane" ) - -Plane:HandleEvent( EVENTS.Shot ) - -function Plane:OnEventShot( EventData ) - - Plane:MessageToAll( "I just fired a missile!", 15, "Alert!" ) -end - - diff --git a/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.miz deleted file mode 100644 index a8fefbf80..000000000 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-100 - UNIT OnEventShot Example/EVT-100 - UNIT OnEventShot Example.miz and /dev/null differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.lua deleted file mode 100644 index 03c8435ab..000000000 --- a/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.lua +++ /dev/null @@ -1,32 +0,0 @@ ---- --- Name: EVT-101 - UNIT OnEventHit Example --- Author: FlightControl --- Date Created: 7 Feb 2017 --- --- # Situation: --- --- A plane is flying in the air and shoots an missile to a ground target. --- --- # Test cases: --- --- 1. Observe the plane shooting the missile. --- 2. Observe when the missile hits the target, a dcs.log entry is written in the logging. --- 3. Check the contents of the fields of the S_EVENT_HIT entry. - -Plane = UNIT:FindByName( "Plane" ) - -Tank = UNIT:FindByName( "Tank" ) - -Plane:HandleEvent( EVENTS.Hit ) -Tank:HandleEvent( EVENTS.Hit ) - -function Plane:OnEventHit( EventData ) - - Plane:MessageToAll( "I just got hit!", 15, "Alert!" ) -end - -function Tank:OnEventHit( EventData ) - Tank:MessageToAll( "I just got hit!", 15, "Alert!" ) -end - - diff --git a/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.miz deleted file mode 100644 index f607c4eef..000000000 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-101 - UNIT OnEventHit Example/EVT-101 - UNIT OnEventHit Example.miz and /dev/null differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.lua deleted file mode 100644 index 203d946db..000000000 --- a/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.lua +++ /dev/null @@ -1,33 +0,0 @@ ---- --- Name: EVT-102 - UNIT OnEventTakeoff Example --- Author: FlightControl --- Date Created: 7 Feb 2017 --- --- # Situation: --- --- A human plane and an AI plane are taking off from an airfield. --- --- # Test cases: --- --- 1. Take-Off the planes from the runway. --- 2. When the planes take-off, observe the message being sent. --- 3. Check the contents of the fields of the S_EVENT_TAKEOFF entry in the dcs.log file. - -PlaneAI = UNIT:FindByName( "PlaneAI" ) - -PlaneHuman = UNIT:FindByName( "PlaneHuman" ) - -PlaneAI:HandleEvent( EVENTS.Takeoff ) -PlaneHuman:HandleEvent( EVENTS.Takeoff ) - -function PlaneAI:OnEventTakeoff( EventData ) - - PlaneHuman:MessageToAll( "AI Taking Off", 15, "Alert!" ) -end - -function PlaneHuman:OnEventTakeoff( EventData ) - - PlaneHuman:MessageToAll( "Player " .. PlaneHuman:GetPlayerName() .. " is Taking Off", 15, "Alert!" ) -end - - diff --git a/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.miz deleted file mode 100644 index 1fb313b93..000000000 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-102 - UNIT OnEventTakeoff Example/EVT-102 - UNIT OnEventTakeoff Example.miz and /dev/null differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.lua deleted file mode 100644 index ba4e7608c..000000000 --- a/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.lua +++ /dev/null @@ -1,38 +0,0 @@ ---- --- Name: EVT-103 - UNIT OnEventLand Example --- Author: FlightControl --- Date Created: 7 Feb 2017 --- --- # Situation: --- --- An AI plane is landing on an airfield. --- When the plane landed, a new plane is spawned. --- --- # Test cases: --- --- 1. Observe the plane landing. --- 2. When the AI plane lands, observe the new plane being spawned. --- 3. Check the contents of the fields of the S_EVENT_LAND entry in the dcs.log file. - --- Create a variable PlaneAI that holds a reference to UNIT object (created by moose at the beginning of the mission) with the name "PlaneAI". -PlaneAI = UNIT:FindByName( "PlaneAI" ) - --- Create a SPAWN object to spawn a new plane once the hold one lands. -SpawnPlane = SPAWN:New( "SpawnPlaneAI" ) - --- Declare a new variable that will hold the new spawned SpawnPlaneAI -local NewPlane - - --- Subscribe to the event Land. The Land event occurs when a plane lands at an airfield. -PlaneAI:HandleEvent( EVENTS.Land ) - --- Because the PlaneAI object is subscribed to the Land event, the following method will be automatically --- called when the land event is happening FOR THE PlaneAI UNIT only! -function PlaneAI:OnEventLand( EventData ) - - -- Okay, the PlaneAI has landed, now spawn the new plane ( a predator ) - NewPlane = SpawnPlane:Spawn() -end - - diff --git a/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.miz deleted file mode 100644 index 9b51a437e..000000000 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-103 - UNIT OnEventLand Example/EVT-103 - UNIT OnEventLand Example.miz and /dev/null differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.lua deleted file mode 100644 index 9a6675315..000000000 --- a/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.lua +++ /dev/null @@ -1,35 +0,0 @@ ---- --- Name: EVT-104 - UNIT OnEventCrash Example --- Author: FlightControl --- Date Created: 7 Feb 2017 --- --- # Situation: --- --- A human plane is fyling in the air. Crash it into the ground. --- Once you are crashed into the ground, at the place where you crashed, a smoke should start burning ... --- --- # Test cases: --- --- 1. Fly the plane into the ground. --- 2. When your plane crashes, observe a smoke starting to burn right were you crashed. --- 3. Check the contents of the fields of the S_EVENT_CRASH entry in the dcs.log file. - --- Create a variable PlaneHuman that holds a reference to UNIT object (created by moose at the beginning of the mission) with the name "PlaneHuman". -PlaneHuman = UNIT:FindByName( "PlaneHuman" ) - --- Subscribe to the event Crash. The Crash event occurs when a plane crashes into the ground (or into something else). -PlaneHuman:HandleEvent( EVENTS.Crash ) - --- Because the PlaneHuman object is subscribed to the Crash event, the following method will be automatically --- called when the Crash event is happening FOR THE PlaneHuman UNIT only! - ---- @param self --- @param Core.Event#EVENTDATA EventData -function PlaneHuman:OnEventCrash( EventData ) - - -- Okay, the PlaneHuman has crashed, now smoke at the x, z position. - self:E( "Smoking at the position" ) - EventData.IniUnit:SmokeOrange() -end - - diff --git a/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.miz deleted file mode 100644 index a3ac0a634..000000000 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-104 - UNIT OnEventCrash Example/EVT-104 - UNIT OnEventCrash Example.miz and /dev/null differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.lua deleted file mode 100644 index 7afed4b7d..000000000 --- a/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.lua +++ /dev/null @@ -1,28 +0,0 @@ ---- --- Name: EVT-200 - GROUP OnEventShot Example --- Author: FlightControl --- Date Created: 07 Mar 2017 --- --- # Situation: --- --- Two groups of planes are flying in the air and shoot an missile to a multitude of ground targets. --- --- # Test cases: --- --- 1. Observe the planes shooting the missile. --- 2. Observe when the planes shoots the missile, a dcs.log entry is written in the logging. --- 3. Check the contents of the fields of the S_EVENT_SHOT entry. --- 4. The planes of GROUP "Group Plane A", should only send a message when they shoot a missile. --- 5. The planes of GROUP "Group Plane B", should NOT send a message when they shoot a missile. - -PlaneGroup = GROUP:FindByName( "Group Plane A" ) - -PlaneGroup:HandleEvent( EVENTS.Shot ) - -function PlaneGroup:OnEventShot( EventData ) - - self:E( "I just fired a missile and I am part of " .. EventData.IniGroupName ) - EventData.IniUnit:MessageToAll( "I just fired a missile and I am part of " .. EventData.IniGroupName, 15, "Alert!" ) -end - - diff --git a/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.miz deleted file mode 100644 index 2fc35a4d9..000000000 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-200 - GROUP OnEventShot Example/EVT-200 - GROUP OnEventShot Example.miz and /dev/null differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.lua deleted file mode 100644 index 765419409..000000000 --- a/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.lua +++ /dev/null @@ -1,28 +0,0 @@ ---- --- Name: EVT-201 - GROUP OnEventHit Example --- Author: FlightControl --- Date Created: 08 Mar 2017 --- --- # Situation: --- --- Two groups of planes are flying in the air and shoot an missile to a multitude of ground targets. --- --- # Test cases: --- --- 1. Observe the planes shooting the missile. --- 2. Observe when the planes shoots the missile, and hit the group Tanks A, a dcs.log entry is written in the logging. --- 3. Check the contents of the fields of the S_EVENT_HIT entry. --- 4. The tanks of GROUP "Group Tanks A", should only send a message when they get hit. --- 5. The tanks of GROUP "Group Tanks B", should NOT send a message when they get hit. - -TanksGroup = GROUP:FindByName( "Group Tanks A" ) - -TanksGroup:HandleEvent( EVENTS.Hit ) - -function TanksGroup:OnEventHit( EventData ) - - self:E( "I just got hit and I am part of " .. EventData.TgtGroupName ) - EventData.TgtUnit:MessageToAll( "I just got hit and I am part of " .. EventData.TgtGroupName, 15, "Alert!" ) -end - - diff --git a/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.miz deleted file mode 100644 index ec9b1a296..000000000 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-201 - GROUP OnEventHit Example/EVT-201 - GROUP OnEventHit Example.miz and /dev/null differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.lua b/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.lua deleted file mode 100644 index 9847ce500..000000000 --- a/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.lua +++ /dev/null @@ -1,34 +0,0 @@ ---- --- Name: EVT-401 - Generic OnEventHit Example --- Author: FlightControl --- Date Created: 15 February 2017 --- --- # Situation: --- --- Ground targets are shooting each other. --- --- # Test cases: --- --- 1. Observe the ground forces shooting each other. --- 2. Observe when a tank receives a hit, a dcs.log entry is written in the logging. --- 3. The generic EventHandler objects should receive the hit events. - -CC = COMMANDCENTER:New( UNIT:FindByName( "HQ" ), "HQ" ) - -EventHandler1 = EVENTHANDLER:New() -EventHandler2 = EVENTHANDLER:New() - -EventHandler1:HandleEvent( EVENTS.Hit ) -EventHandler2:HandleEvent( EVENTS.Hit ) - -function EventHandler1:OnEventHit( EventData ) - self:E("hello 1") - CC:GetPositionable():MessageToAll( "I just got hit!", 15 , "Alert!" ) -end - -function EventHandler2:OnEventHit( EventData ) - self:E("hello 2") - CC:GetPositionable():MessageToAll( "I just got hit!", 15, "Alert!" ) -end - - diff --git a/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz b/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz deleted file mode 100644 index e7a7c6b19..000000000 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-401 - Generic OnEventHit Example/EVT-401 - Generic OnEventHit Example.miz and /dev/null differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.lua b/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.lua deleted file mode 100644 index 2d81929e2..000000000 --- a/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.lua +++ /dev/null @@ -1,63 +0,0 @@ ---- --- Name: EVT-103 - OnEventLand Example --- Author: CraigOwen --- Date Created: 12 February 2017 --- --- # Situation: --- --- A client plane is landing on an airfield, trying to pick a rope in the landing zones. --- When the plane landed in one of the zones, a vehicle flares and a message ist printed out to the client. --- --- # Test cases: --- --- 1. Land the plane. --- 2. When the plane landed, observe your message and the signal. --- 3. Check the contents of the fields of the S_EVENT_LAND entry in the dcs.log file. - --- Create a unit which signalizes if the client landed good. -signal = UNIT:FindByName("LandingZoneChallenge - Signal") - --- Create the zones used for the landing check --- Init Zone -InitZone = ZONE:New("LandingChallange - InitZone") - --- Ropes -zonegroup1 = GROUP:FindByName("LandingZoneChallenge - RopeGroup 1" ) -zonegroup2 = GROUP:FindByName("LandingZoneChallenge - RopeGroup 2" ) -zonegroup3 = GROUP:FindByName("LandingZoneChallenge - RopeGroup 3" ) -LandZoneRope1 = ZONE_POLYGON:New( "Rope1", zonegroup1) -LandZoneRope2 = ZONE_POLYGON:New( "Rope2", zonegroup2) -LandZoneRope3 = ZONE_POLYGON:New( "Rope3", zonegroup3) - --- Create a variable Plane that holds a reference to CLIENT object (created by moose at the beginning of the mission) with the name "Plane". -Plane = CLIENT:FindByName( "Plane" ) --- Subscribe to the event Land. The Land event occurs when a plane lands at an airfield. -Plane:HandleEvent( EVENTS.Land ) - --- This function will be called whenever the Plane-Object (client) lands! -function Plane:OnEventLand( EventData ) - - -- check wether the client landet at the right airport, where the challenge is located - if not Plane:IsInZone(InitZone) then - return - end - - -- check if the touchdown took place inside of one of the zones - if Plane:IsInZone(LandZoneRope1) then - MESSAGE:New("Great job! You picked the first rope.", 15, "Landing challenge" ):ToClient( Plane ) - signal:FlareGreen() - elseif Plane:IsInZone(LandZoneRope2) then - MESSAGE:New("Good job! You picked the second rope.", 15, "Landing challenge" ):ToClient( Plane ) - signal:FlareYellow() - elseif Plane:IsInZone(LandZoneRope3) then - MESSAGE:New("Close! You picked the last rope.", 15, "Landing challenge" ):ToClient( Plane ) - signal:FlareRed() - else - MESSAGE:New("Too bad, no rope picked! Thrust your engines and try again.", 15, "Landing challenge" ):ToClient( Plane ) - end - - -end - - -MESSAGE:New("Try to land on the runway in between the red trucks.", 15, "Landing challenge"):ToClient(Plane) \ No newline at end of file diff --git a/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz b/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz deleted file mode 100644 index 360be667c..000000000 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-500 - OnEventLand LandingChallenge/EVT-500 - OnEventLand LandingChallenge.miz and /dev/null differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.lua b/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.lua deleted file mode 100644 index 469122ac9..000000000 --- a/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.lua +++ /dev/null @@ -1,151 +0,0 @@ ---- --- Name: EVT-103 - OnEventLand LandingChallengeComplex --- Author: CraigOwen --- Date Created: 12 February 2017 --- --- # Situation: --- --- Approaching the airfield the client gets a message and can try to land inside the landing zones. --- Here we want all clients to participate in the challenge, not only one. --- When the plane landed in one of the zones, a vehicle flares and a message ist printed out to the client. --- --- # Test cases: --- --- 1. Land one of the planes. --- 2. While landing the plane, observe your message and the signal (flare). --- 3. Check the contents of the fields of the S_EVENT_LAND entry in the dcs.log file. - --- In this advanced challenge we want to make sure all the following. --- 1. The challenge takes place at a certain airfield. --- 2. All clients can repeat the challange at any time, try after try. --- 3. All clients approaching the airport get a message indicating the challenge. --- 4. There is no useraction needet to participate in this, providing full focus on the task. - --- So lets go then... in five steps --- 1. Create a unit to signalize (flare) whenever a client landed correctly --- 2. Create Zones --- 3. Create a set of clients --- 4. Handle the EVENT.Land for all clients in the set using the signal unit --- 5. Create a scheduler checking for clients in zones - --- 1. Create a unit which signalizes if the client landed good. -signal = UNIT:FindByName("LandingZoneChallenge - Signal") - --- 2. Create Zones --- Init Zone - This is the global Zone for the LandingChallenge -InitZone = ZONE:New("LandingChallange - InitZone") - ---Ingress Zone - This Zone tries to asure the client approaches the runway from the right side -IngressZoneTemplate = GROUP:FindByName( "LandingZoneChallenge - IngressZone" ) -IngressZone = ZONE_POLYGON:New( "IngressZone", IngressZoneTemplate ) - --- Ropes - theese zones will simulate the ropes on a carrier. -zonegroup1 = GROUP:FindByName("LandingZoneChallenge - Rope 1" ) -zonegroup2 = GROUP:FindByName("LandingZoneChallenge - Rope 2" ) -zonegroup3 = GROUP:FindByName("LandingZoneChallenge - Rope 3" ) -LandZoneRope1 = ZONE_POLYGON:New( "Rope1", zonegroup1) -LandZoneRope2 = ZONE_POLYGON:New( "Rope2", zonegroup2) -LandZoneRope3 = ZONE_POLYGON:New( "Rope3", zonegroup3) - - --- 3. Create a set of clients --- In this example we do not want to handle the event for one specific client, but rather for all red plane clients. --- To achieve this, we start with filtering the clients and saving those into the "BlueClients" variable -RedClients = SET_CLIENT:New():FilterCoalitions("red"):FilterStart() - - --- 4. We want to let every client subscribe to the event EVENT.Land. This event occurs when a plane lands. Be aware that this could be any airfield at this point. --- To do so, we run the ForEachClient method on our set of clients and call a function taking the client as parameter -RedClients:ForEachClient( - --- This function will be called for every single client in the set - -- @param MooseClient#CLIENT ClientInSet - function( ClientInSet ) - - -- Inside here we want to do two things. - -- 1. Write down the local function doing all the magic. - -- 2. Call this function for each ClientInSet - - -- 1. The magic - local function ResetClientForZone( MooseClient ) - --At first we set this client to a state, in wich she/he is not participating in this event - MooseClient:SetState( MooseClient, "ZoneStep", "0" ) - - --Now we subscribe to the event just like we did in the first example. - MooseClient:HandleEvent(EVENTS.Land) - - - --- Finally we set up the so called handler FOR the event. This is a function wich will determine what happens, whenever a client lands. - -- Note here, that the function has the MooseClient in front. So this function will literaly get a part of the client itself. - -- Therefore we can refere to "self" inside the function whenever meaning the MooseClient - -- The param EventData is a parameter given to all event handlers and providing several data about this particular event. - -- @param Core.Event#EVENTDATA EventData - function MooseClient:OnEventLand( EventData ) - - -- Ok now the client "MooseClient" definetly has landed. And beeing here means being the client. MooseClient <-> self - -- So now i want to know 2 things, to verify that i have done everything right. - -- 1. I want to know if my(self) landed in the challengeZone, so landing in other places will not react to this challenge - -- 2. Furthermore i want to know if my(self) came from the right side. - -- In all other cases nothing shell happen, so we reset the client state here and return doin nothing else. - if not self:IsInZone(InitZone) or self:GetState( self, "ZoneStep" ) ~= "2" then - self:SetState( self, "ZoneStep", "0" ) - return - end - - -- Here we check wich rope was picked and set the signal and message according to it. - if self:IsInZone(LandZoneRope1) then - MESSAGE:New("Great job! You picked the first rope.", 15, "Landing challenge" ):ToClient( self ) - signal:FlareGreen() - elseif self:IsInZone(LandZoneRope2) then - MESSAGE:New("Good job! You picked the second rope.", 15, "Landing challenge" ):ToClient( self ) - signal:FlareYellow() - elseif self:IsInZone(LandZoneRope3) then - MESSAGE:New("Close! You picked the last rope.", 15, "Landing challenge" ):ToClient( self ) - signal:FlareRed() - else - MESSAGE:New("Too bad, no rope picked! Thrust your engines and try again.", 15, "Landing challenge" ):ToClient( self ) - end - - -- Finally we set the client back to step 1, allowing a new message for landing - self:SetState( self, "ZoneStep", "1" ) - - end - end - - -- 2. As we're now all set, we can finally call our function for every ClientInSet - ClientInSet:Alive( ResetClientForZone ) - - end - ) - --- 5. Finally we use a scheduler checking wether clients are inside or outside these zones. -LandingChallangeActionsScheduler, LandingChallangeActionsSchedulerID = SCHEDULER:New( nil, - function () - - -- Flying by the airport there will be a message showing that the landing challange is currently in place. - -- This will make the ClientState shift from 0 -> 1 - RedClients:ForEachClientInZone( InitZone, - function( MooseClient ) - BASE:E( { Client = MooseClient, State = MooseClient:GetState( MooseClient, "ZoneStep" ) } ) - if MooseClient:IsAlive() and MooseClient:GetState( MooseClient, "ZoneStep" ) == "0" then - MooseClient:SetState( MooseClient, "ZoneStep", "1" ) - MESSAGE:New("Welcome to the Landing challenge. If you want to participate, get yourself a landing clearance by ATC and navigate to the landing corridor.", 20, "Landing challenge" ):ToClient( MooseClient ) - end - end - ) - - -- The client is approaching the runway from the correct side? - -- If yes, then shift state from 1 to 2 - RedClients:ForEachClientInZone( IngressZone, - function( MooseClient ) - BASE:E( { Client = MooseClient, State = MooseClient:GetState( MooseClient, "ZoneStep" ) } ) - if MooseClient:IsAlive() and MooseClient:GetState( MooseClient, "ZoneStep" ) == "1" then - MooseClient:SetState( MooseClient, "ZoneStep", "2" ) - MESSAGE:New("Ok, now its your turn. Land your airframe and try to get one of the ropes. Good luck!", 15, "Landing challenge" ):ToClient( MooseClient ) - end - end - ) - - end, {}, 5, 5 - ) - -MESSAGE:New("Try to land on the runway in between the red trucks located at the right side.", 15, "Landing challenge"):ToAll() \ No newline at end of file diff --git a/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz b/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz deleted file mode 100644 index bad078054..000000000 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-501 - OnEventLand LandingChallengeComplex/EVT-501 - OnEventLand LandingChallengeComplex.miz and /dev/null differ diff --git a/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.lua b/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.lua deleted file mode 100644 index e05d83081..000000000 --- a/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.lua +++ /dev/null @@ -1,29 +0,0 @@ ---- --- Name: EVT-600 - OnEventHit Example with a Set of Units --- Author: FlightControl --- Date Created: 6 Mar 2017 --- --- # Situation: --- --- A plane is flying in the air and shoots an missile to a ground target. --- It will shoot a couple of tanks units that are part of a Set. --- --- # Test cases: --- --- 1. Observe the plane shooting the missile. --- 2. Observe when the plane hits a tank, a dcs.log entry is written in the logging. --- 4. Observe the tanks hitting the targets and the messages appear. --- 3. Check the contents of the fields of the S_EVENT_HIT entries. - -Plane = UNIT:FindByName( "Plane" ) - -UnitSet = SET_UNIT:New():FilterPrefixes( "Tank" ):FilterStart() - -UnitSet:HandleEvent( EVENTS.Hit ) - -function UnitSet:OnEventHit( EventData ) - - Plane:MessageToAll( "I just hit a tank! " .. EventData.IniUnit:GetName(), 15, "Alert!" ) -end - - diff --git a/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.miz b/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.miz deleted file mode 100644 index abae38230..000000000 Binary files a/Moose Test Missions/EVT - Event Handling/EVT-600 - OnEventHit Example with a Set of Units/EVT-600 - OnEventHit Example with a Set of Units.miz and /dev/null differ diff --git a/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.lua b/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.lua deleted file mode 100644 index 1ad43849b..000000000 --- a/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.lua +++ /dev/null @@ -1,122 +0,0 @@ ---- Transition Explanation --- --- === --- --- Name: Transition Explanation --- Author: FlightControl --- Date Created: 05 Jan 2017 --- --- # Situation: --- --- Create a simple FSM. --- Add 2 transitions that will switch state from "Green" to "Red" upon event "Switch". --- --- # Test cases: --- --- # Status: TESTED 05 Jan 2017 - -local FsmDemo = FSM:New() -- #FsmDemo -local FsmUnit = UNIT:FindByName( "FlareUnit" ) - -FsmDemo:SetStartState( "Green" ) - -do FsmDemo:AddTransition( "Green", "Switch", "Red" ) -- FSM Transition for type #FsmDemo. - - --- OnLeave State Transition for Green. - -- @function [parent=#FsmDemo] OnLeaveGreen - -- @param #FsmDemo self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter State Transition for Red. - -- @function [parent=#FsmDemo] OnEnterRed - -- @param #FsmDemo self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore State Transition for Switch. - -- @function [parent=#FsmDemo] OnBeforeSwitch - -- @param #FsmDemo self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter State Transition for Switch. - -- @function [parent=#FsmDemo] OnAfterSwitch - -- @param #FsmDemo self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Embedded Event Trigger for Switch. - -- @function [parent=#FsmDemo] Switch - -- @param #FsmDemo self - - --- Delayed Event Trigger for Switch - -- @function [parent=#FsmDemo] __Switch - -- @param #FsmDemo self - -- @param #number Delay The delay in seconds. - -end -- FsmDemo - -do FsmDemo:AddTransition( "Red", "Switch", "Green" ) -- FSM Transition for type #FsmDemo. - - --- OnLeave State Transition for Red. - -- @function [parent=#FsmDemo] OnLeaveRed - -- @param #FsmDemo self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter State Transition for Green. - -- @function [parent=#FsmDemo] OnEnterGreen - -- @param #FsmDemo self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore State Transition for Switch. - -- @function [parent=#FsmDemo] OnBeforeSwitch - -- @param #FsmDemo self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter State Transition for Switch. - -- @function [parent=#FsmDemo] OnAfterSwitch - -- @param #FsmDemo self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Embedded Event Trigger for Switch. - -- @function [parent=#FsmDemo] Switch - -- @param #FsmDemo self - - --- Delayed Event Trigger for Switch - -- @function [parent=#FsmDemo] __Switch - -- @param #FsmDemo self - -- @param #number Delay The delay in seconds. - -end -- FsmDemo - -function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit ) - self:E( { From, Event, To, FsmUnit } ) - if From == "Green" then - FsmUnit:Flare(FLARECOLOR.Green) - else - if From == "Red" then - FsmUnit:Flare(FLARECOLOR.Red) - end - end - FsmDemo:__Switch( 5, FsmUnit ) -end - -FsmDemo:__Switch( 5, FsmUnit ) - diff --git a/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz b/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz deleted file mode 100644 index ad8e22fb6..000000000 Binary files a/Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition Explanation/FSM-100 - Transition Explanation.miz and /dev/null differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.lua b/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.lua deleted file mode 100644 index 7f33d42f2..000000000 --- a/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.lua +++ /dev/null @@ -1,32 +0,0 @@ ---- --- Name: GRP-100 - IsAlive --- Author: FlightControl --- Date Created: 23 Feb 2017 --- --- # Situation: --- --- This test is about checking if IsAlive on GROUP level is working correctly. --- Two ground forces GROUPS are shooting each other. --- Check the IsAlive status in the logging of the survivor and the defeat. --- --- # Test cases: --- --- 1. Observe the IsAlive statuses in the dcs.log file. - - - ---Create Spawn Groups -local GroupBlue = GROUP:FindByName( "Blue" ) -local GroupRed = GROUP:FindByName( "Red" ) - -local Schedule, ScheduleID = SCHEDULER:New( nil, - --- Variable Declarations - -- @param Wrapper.Group#GROUP GroupBlue - -- @param Wrapper.Group#GROUP GroupRed - function( GroupBlue, GroupRed ) - local IsAliveBlue = GroupBlue:IsAlive() - local IsAliveRed = GroupRed:IsAlive() - BASE:E( { IsAliveBlue = IsAliveBlue, IsAliveRed = IsAliveRed } ) - end, { GroupBlue, GroupRed }, 1, 1 -) - diff --git a/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz b/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz deleted file mode 100644 index 465eb6811..000000000 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-100 - IsAlive/GRP-100 - IsAlive.miz and /dev/null differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.lua b/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.lua deleted file mode 100644 index 101ffafee..000000000 --- a/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.lua +++ /dev/null @@ -1,30 +0,0 @@ ---- This test demonstrates the use(s) of the SwitchWayPoint method of the GROUP class. - -local HeliGroup = GROUP:FindByName( "Helicopter" ) - -local AttackGroup = GROUP:FindByName( "AttackGroup" ) - -local AttackUnits = AttackGroup:GetUnits() - -local Tasks = {} - -for i = 1, #AttackUnits do - - local AttackUnit = AttackGroup:GetUnit( i ) - Tasks[#Tasks+1] = HeliGroup:TaskAttackUnit( AttackUnit ) -end - -Tasks[#Tasks+1] = HeliGroup:TaskFunction( 1, 7, "_Resume", { "''" } ) - ---- @param Wrapper.Group#GROUP HeliGroup -function _Resume( HeliGroup ) - env.info( '_Resume' ) - - HeliGroup:MessageToAll( "Resuming",10,"Info") -end - -HeliGroup:PushTask( - HeliGroup:TaskCombo( - Tasks - ), 30 -) \ No newline at end of file diff --git a/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.miz b/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.miz deleted file mode 100644 index 7c3d455c6..000000000 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-100 - TaskAttackUnit/GRP-100 - TaskAttackUnit.miz and /dev/null differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.lua b/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.lua deleted file mode 100644 index 176f507c9..000000000 --- a/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.lua +++ /dev/null @@ -1,24 +0,0 @@ - ---Create Spawn Groups -local SpawnPlane1 = SPAWN:New("Plane 1") -local SpawnPlane2 = SPAWN:New("Plane 2") - ---Spawn Groups into world -local GroupPlane1 = SpawnPlane1:Spawn() ---local GroupPlane1 = GROUP:FindByName( "Plane 1" ) -local GroupPlane2 = SpawnPlane2:Spawn() ---local GroupPlane2 = GROUP:FindByName( "Plane 2" ) - ---Create Task for plane2 (follow groupPlane1 at Vec3 offset) (Note: I think I need to be using controllers here) ---i.e. cntrlPlane1 = groupPlane1.getController(groupPlane1) - -local PointVec3 = POINT_VEC3:New( 100, 0, -100 ) -- This is a Vec3 class. - -local FollowDCSTask = GroupPlane2:TaskFollow( GroupPlane1, PointVec3:GetVec3() ) - ---Activate Task (Either PushTask/SetTask?) --- PushTask will push a task on the execution queue of the group. --- SetTask will delete all tasks from the current group queue, and executes this task. - -GroupPlane2:SetTask( FollowDCSTask, 1 ) - diff --git a/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz b/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz deleted file mode 100644 index 68b995f09..000000000 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-200 - Follow Group/GRP-200 - Follow Group.miz and /dev/null differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.lua b/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.lua deleted file mode 100644 index 03258b2cf..000000000 --- a/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.lua +++ /dev/null @@ -1,12 +0,0 @@ ---- This test demonstrates the use(s) of the SwitchWayPoint method of the GROUP class. - -HeliGroup = GROUP:FindByName( "Helicopter" ) - ---- Route the helicopter back to the FARP after 60 seconds. --- We use the SCHEDULER class to do this. -SCHEDULER:New( nil, - function( HeliGroup ) - local CommandRTB = HeliGroup:CommandSwitchWayPoint( 2, 8 ) - HeliGroup:SetCommand( CommandRTB ) - end, { HeliGroup }, 90 -) diff --git a/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz b/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz deleted file mode 100644 index 7c61e2010..000000000 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-300 - Switch WayPoints/GRP-300 - Switch WayPoints.miz and /dev/null differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-310 - Command StopRoute/GRP-310 - Command StopRoute.lua b/Moose Test Missions/GRP - Group Commands/GRP-310 - Command StopRoute/GRP-310 - Command StopRoute.lua deleted file mode 100644 index 1f7710d18..000000000 --- a/Moose Test Missions/GRP - Group Commands/GRP-310 - Command StopRoute/GRP-310 - Command StopRoute.lua +++ /dev/null @@ -1,39 +0,0 @@ ---- --- Name: GRP-310 - Command StopRoute --- Author: FlightControl --- Date Created: 25 Mar 2017 --- --- # Situation: --- A ground unit is moving. --- Using the command CommandStopMove it will stop moving after 10 seconds. --- --- # Test cases: --- --- 1. Observe the ground group stopping to move. --- - ---- @param Wrapper.Group#GROUP GroundGroup -function StopMove( GroundGroup ) - - BASE:E("Stop") - local Command = GroundGroup:CommandStopRoute( true ) - GroundGroup:SetCommand(Command) - -end - ---- @param Wrapper.Group#GROUP GroundGroup -function StartMove( GroundGroup ) - - BASE:E("Start") - local Command = GroundGroup:CommandStopRoute( false ) - GroundGroup:SetCommand(Command) - -end - -GroundGroup = GROUP:FindByName( "Ground" ) - -Scheduler = SCHEDULER:New( nil ) -ScheduleIDStop = Scheduler:Schedule(nil, StopMove, { GroundGroup }, 10, 20 ) -ScheduleIDStart = Scheduler:Schedule(nil, StartMove, { GroundGroup }, 20, 20 ) - - diff --git a/Moose Test Missions/GRP - Group Commands/GRP-310 - Command StopRoute/GRP-310 - Command StopRoute.miz b/Moose Test Missions/GRP - Group Commands/GRP-310 - Command StopRoute/GRP-310 - Command StopRoute.miz deleted file mode 100644 index 9508ba428..000000000 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-310 - Command StopRoute/GRP-310 - Command StopRoute.miz and /dev/null differ diff --git a/Moose Test Missions/GRP - Group Commands/GRP-400 - RouteReturnToAirbase/GRP-400 - RouteReturnToAirbase.lua b/Moose Test Missions/GRP - Group Commands/GRP-400 - RouteReturnToAirbase/GRP-400 - RouteReturnToAirbase.lua deleted file mode 100644 index 47ff792f4..000000000 --- a/Moose Test Missions/GRP - Group Commands/GRP-400 - RouteReturnToAirbase/GRP-400 - RouteReturnToAirbase.lua +++ /dev/null @@ -1,45 +0,0 @@ ---- --- Name: GRP-400 - RouteReturnToAirbase --- Author: FlightControl --- Date Created: 25 Mar 2017 --- --- # Situation: --- Three air units are flying and are commanded to return a specific airbase. --- --- # Test cases: --- --- 1. Observe the Air1 group return to Batumi after 10 seconds. --- 2. Observe the Air2 group returning to Kobuleti after 300 seconds. (It was planned to land at Kutaisi). --- 3. Observe the Air3 group returning to the home (landing) airbase after 300 seconds. (It was planned to land at Kutaisi). --- - ---- @param Wrapper.Group#GROUP AirGroup -function ReturnToBatumi( AirGroup ) - BASE:E("ReturnToBatumi") - AirGroup:RouteRTB( AIRBASE:FindByName("Batumi") ) -end - ---- @param Wrapper.Group#GROUP AirGroup -function ReturnToKobuleti( AirGroup ) - BASE:E("ReturnToKobuleti") - AirGroup:RouteRTB( AIRBASE:FindByName("Kobuleti") ) -end - ---- @param Wrapper.Group#GROUP AirGroup -function ReturnToHome( AirGroup ) - BASE:E("ReturnToHome") - AirGroup:RouteRTB() -end - -Air1Group = GROUP:FindByName( "Air1" ) -Air2Group = GROUP:FindByName( "Air2" ) -Air3Group = GROUP:FindByName( "Air3" ) - -Scheduler = SCHEDULER:New( nil ) -ScheduleIDAir1 = Scheduler:Schedule(nil, ReturnToBatumi, { Air1Group }, 10 ) -ScheduleIDAir2 = Scheduler:Schedule(nil, ReturnToKobuleti, { Air2Group }, 300 ) -ScheduleIDAir3 = Scheduler:Schedule(nil, ReturnToHome, { Air3Group }, 300 ) - - - - diff --git a/Moose Test Missions/GRP - Group Commands/GRP-400 - RouteReturnToAirbase/GRP-400 - RouteReturnToAirbase.miz b/Moose Test Missions/GRP - Group Commands/GRP-400 - RouteReturnToAirbase/GRP-400 - RouteReturnToAirbase.miz deleted file mode 100644 index 5ced79316..000000000 Binary files a/Moose Test Missions/GRP - Group Commands/GRP-400 - RouteReturnToAirbase/GRP-400 - RouteReturnToAirbase.miz and /dev/null differ diff --git a/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.lua b/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.lua deleted file mode 100644 index d05eb4b02..000000000 --- a/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.lua +++ /dev/null @@ -1,39 +0,0 @@ - - - - - -BASE:TraceClass( "UNIT" ) -BASE:TraceClass( "GROUP" ) -BASE:TraceClass( "CLIENT" ) - -UnitTankAI1 = _DATABASE:FindUnit( "Smoke Test 1" ) -UnitTankAI2 = _DATABASE:FindUnit( "Smoke Test 2" ) -UnitTankAI3 = UNIT:FindByName( "Smoke Test 3" ) -UnitTankAI4 = _DATABASE:FindUnit( "Smoke Test 4" ) - -UnitTankAI1:SmokeBlue() - -UnitTankAI3:SmokeOrange() - -UnitTankAI2:T( UnitTankAI2:GetAmmo() ) - -GroupTanks = GROUP:FindByName( "Smoke Test" ) - -GroupTanks:T( GroupTanks:OptionROEOpenFirePossible() ) - -GroupTanks:OptionROEOpenFire() - - local function ClientAlive( Client, ClientNumber ) - GroupTanks:MessageToClient( "Hello Client " .. ClientNumber .. "! We are reporting to you on our way...", 5, Client ) - end - - -ClientHeli = CLIENT:FindByName( "Client Test 1", "Fly slowly to waypoint 3 of the Command Post!" ):Alive( ClientAlive, 1 ) -ClientHeli2 = CLIENT:FindByName( "Client Test 2", "Fly slowly to waypoint 3 of the Command Post!" ):Alive( ClientAlive, 2 ) - - - - - - diff --git a/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz b/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz deleted file mode 100644 index 2253de3c3..000000000 Binary files a/Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz and /dev/null differ diff --git a/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.lua b/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.lua deleted file mode 100644 index fa483f103..000000000 --- a/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.lua +++ /dev/null @@ -1,56 +0,0 @@ - -do - -- This demo creates a menu structure for the two clients of planes. - -- Each client will receive a different menu structure. - -- To test, join the planes, then look at the other radio menus (Option F10). - -- Then switch planes and check if the menu is still there. - -- And play with the Add and Remove menu options. - - -- Note that in multi player, this will only work after the DCS clients bug is solved. - - local function ShowStatus( PlaneClient, StatusText, Coalition ) - - MESSAGE:New( Coalition, 15 ):ToRed() - PlaneClient:Message( StatusText, 15 ) - end - - local MenuStatus = {} - - local function RemoveStatusMenu( MenuClient ) - local MenuClientName = MenuClient:GetName() - MenuStatus[MenuClientName]:Remove() - end - - --- @param Wrapper.Client#CLIENT MenuClient - local function AddStatusMenu( MenuClient ) - local MenuClientName = MenuClient:GetName() - -- This would create a menu for the red coalition under the MenuCoalitionRed menu object. - MenuStatus[MenuClientName] = MENU_CLIENT:New( MenuClient, "Status for Planes" ) - MENU_CLIENT_COMMAND:New( MenuClient, "Show Status", MenuStatus[MenuClientName], ShowStatus, MenuClient, "Status of planes is ok!", "Message to Red Coalition" ) - end - - SCHEDULER:New( nil, - function() - local PlaneClient = CLIENT:FindByName( "Plane 1" ) - if PlaneClient and PlaneClient:IsAlive() then - local MenuManage = MENU_CLIENT:New( PlaneClient, "Manage Menus" ) - MENU_CLIENT_COMMAND:New( PlaneClient, "Add Status Menu Plane 1", MenuManage, AddStatusMenu, PlaneClient ) - MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 1", MenuManage, RemoveStatusMenu, PlaneClient ) - end - end, {}, 10, 10 ) - - SCHEDULER:New( nil, - function() - local PlaneClient = CLIENT:FindByName( "Plane 2" ) - if PlaneClient and PlaneClient:IsAlive() then - local MenuManage = MENU_CLIENT:New( PlaneClient, "Manage Menus" ) - MENU_CLIENT_COMMAND:New( PlaneClient, "Add Status Menu Plane 2", MenuManage, AddStatusMenu, PlaneClient ) - MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneClient ) - end - end, {}, 10, 10 ) - -end - - - - diff --git a/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz b/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz deleted file mode 100644 index 092d14467..000000000 Binary files a/Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 - Menu Client.miz and /dev/null differ diff --git a/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.lua b/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.lua deleted file mode 100644 index f7ffaccef..000000000 --- a/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.lua +++ /dev/null @@ -1,43 +0,0 @@ - -do - -- This demo creates a menu structure for the planes within the red coalition. - -- To test, join the planes, then look at the other radio menus (Option F10). - -- Then switch planes and check if the menu is still there. - - local Plane1 = CLIENT:FindByName( "Plane 1" ) - local Plane2 = CLIENT:FindByName( "Plane 2" ) - - - -- This would create a menu for the red coalition under the main DCS "Others" menu. - local MenuCoalitionRed = MENU_COALITION:New( coalition.side.RED, "Manage Menus" ) - - - local function ShowStatus( StatusText, Coalition ) - - MESSAGE:New( Coalition, 15 ):ToRed() - Plane1:Message( StatusText, 15 ) - Plane2:Message( StatusText, 15 ) - end - - local MenuStatus -- Menu#MENU_COALITION - local MenuStatusShow -- Menu#MENU_COALITION_COMMAND - - local function RemoveStatusMenu() - MenuStatus:Remove() - end - - local function AddStatusMenu() - - -- This would create a menu for the red coalition under the MenuCoalitionRed menu object. - MenuStatus = MENU_COALITION:New( coalition.side.RED, "Status for Planes" ) - MenuStatusShow = MENU_COALITION_COMMAND:New( coalition.side.RED, "Show Status", MenuStatus, ShowStatus, "Status of planes is ok!", "Message to Red Coalition" ) - end - - local MenuAdd = MENU_COALITION_COMMAND:New( coalition.side.RED, "Add Status Menu", MenuCoalitionRed, AddStatusMenu ) - local MenuRemove = MENU_COALITION_COMMAND:New( coalition.side.RED, "Remove Status Menu", MenuCoalitionRed, RemoveStatusMenu ) - -end - - - - diff --git a/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz b/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz deleted file mode 100644 index d9f4c11bc..000000000 Binary files a/Moose Test Missions/MEN - Menu Options/MEN-002 - Menu Coalition/MEN-002 - Menu Coalition.miz and /dev/null differ diff --git a/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.lua b/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.lua deleted file mode 100644 index 94d602abf..000000000 --- a/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.lua +++ /dev/null @@ -1,56 +0,0 @@ - -do - -- This demo creates a menu structure for the two groups of planes. - -- Each group will receive a different menu structure. - -- To test, join the planes, then look at the other radio menus (Option F10). - -- Then switch planes and check if the menu is still there. - -- And play with the Add and Remove menu options. - - -- Note that in multi player, this will only work after the DCS groups bug is solved. - - local function ShowStatus( PlaneGroup, StatusText, Coalition ) - - MESSAGE:New( Coalition, 15 ):ToRed() - PlaneGroup:Message( StatusText, 15 ) - end - - local MenuStatus = {} - - local function RemoveStatusMenu( MenuGroup ) - local MenuGroupName = MenuGroup:GetName() - MenuStatus[MenuGroupName]:Remove() - end - - --- @param Wrapper.Group#GROUP MenuGroup - local function AddStatusMenu( MenuGroup ) - local MenuGroupName = MenuGroup:GetName() - -- This would create a menu for the red coalition under the MenuCoalitionRed menu object. - MenuStatus[MenuGroupName] = MENU_GROUP:New( MenuGroup, "Status for Planes" ) - MENU_GROUP_COMMAND:New( MenuGroup, "Show Status", MenuStatus[MenuGroupName], ShowStatus, MenuGroup, "Status of planes is ok!", "Message to Red Coalition" ) - end - - SCHEDULER:New( nil, - function() - local PlaneGroup = GROUP:FindByName( "Plane 1" ) - if PlaneGroup and PlaneGroup:IsAlive() then - local MenuManage = MENU_GROUP:New( PlaneGroup, "Manage Menus" ) - MENU_GROUP_COMMAND:New( PlaneGroup, "Add Status Menu Plane 1", MenuManage, AddStatusMenu, PlaneGroup ) - MENU_GROUP_COMMAND:New( PlaneGroup, "Remove Status Menu Plane 1", MenuManage, RemoveStatusMenu, PlaneGroup ) - end - end, {}, 10, 10 ) - - SCHEDULER:New( nil, - function() - local PlaneGroup = GROUP:FindByName( "Plane 2" ) - if PlaneGroup and PlaneGroup:IsAlive() then - local MenuManage = MENU_GROUP:New( PlaneGroup, "Manage Menus" ) - MENU_GROUP_COMMAND:New( PlaneGroup, "Add Status Menu Plane 2", MenuManage, AddStatusMenu, PlaneGroup ) - MENU_GROUP_COMMAND:New( PlaneGroup, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneGroup ) - end - end, {}, 10, 10 ) - -end - - - - diff --git a/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz b/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz deleted file mode 100644 index 8fa0d94ae..000000000 Binary files a/Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 - Menu Group.miz and /dev/null differ diff --git a/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.lua b/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.lua deleted file mode 100644 index b28f6644d..000000000 --- a/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.lua +++ /dev/null @@ -1,20 +0,0 @@ - --- Only use Include.File when developing new MOOSE classes. --- When using Moose.lua in the DO SCIPTS FILE initialization box, --- these Include.File statements are not needed, because all classes within Moose will be loaded. - - --- This is an example of a global -local Trainer = MISSILETRAINER - :New( 200, "Trainer: Welcome to the missile training, trainee! Missiles will be fired at you. Try to evade them. Good luck!" ) - :InitMessagesOnOff(true) - :InitAlertsToAll(true) - :InitAlertsHitsOnOff(true) - :InitAlertsLaunchesOnOff(false) -- I'll put it on below ... - :InitBearingOnOff(true) - :InitRangeOnOff(true) - :InitTrackingOnOff(true) - :InitTrackingToAll(true) - :InitMenusOnOff(false) - -Trainer:InitAlertsToAll(true) -- Now alerts are also on diff --git a/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz b/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz deleted file mode 100644 index 9c06d94d5..000000000 Binary files a/Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile Trainer/MIT-001 - Missile Trainer.miz and /dev/null differ diff --git a/Moose Test Missions/MOOSE_Header.lua b/Moose Test Missions/MOOSE_Header.lua deleted file mode 100644 index daaa33103..000000000 --- a/Moose Test Missions/MOOSE_Header.lua +++ /dev/null @@ -1,14 +0,0 @@ ---- --- Name: --- Author: --- Date Created: --- --- # Situation: --- --- . --- --- # Test cases: --- --- 1. --- 2. --- diff --git a/Moose Test Missions/MOOSE_Template.miz b/Moose Test Missions/MOOSE_Template.miz deleted file mode 100644 index a3e4643e4..000000000 Binary files a/Moose Test Missions/MOOSE_Template.miz and /dev/null differ diff --git a/Moose Test Missions/MOOSE_Test_Template.miz b/Moose Test Missions/MOOSE_Test_Template.miz deleted file mode 100644 index 24b76c5ba..000000000 Binary files a/Moose Test Missions/MOOSE_Test_Template.miz and /dev/null differ diff --git a/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.lua b/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.lua deleted file mode 100644 index cd25ae557..000000000 --- a/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.lua +++ /dev/null @@ -1,80 +0,0 @@ --- This test mission models the behaviour of the AI_PATROL_ZONE class. --- --- It creates a 2 AI_PATROL_ZONE objects with the name Patrol1 and Patrol2. --- Patrol1 will govern a GROUP object to patrol the zone defined by PatrolZone1, within 3000 meters and 6000 meters, within a speed of 400 and 600 km/h. --- When the GROUP object that is assigned to Patrol has fuel below 20%, the GROUP object will orbit for 60 secondes, before returning to base. --- --- Patrol2 will goven a GROUP object to patrol the zone defined by PatrolZone2, within 600 meters and 1000 meters, within a speed of 300 and 400 km/h. --- When the GROUP object that is assigned to Patrol has fuel below 20%, the GROUP object will orbit for 0 secondes, before returning to base. --- --- The Patrol1 and Patrol2 object have 2 state transition functions defined, which customize the default behaviour of the RTB state. --- When Patrol1 goes RTB, it will create a new GROUP object, that will be assigned to Patrol2. --- When Patrol2 goes RTB, it will create a new GROUP object, that will be assgined to Patrol1. --- --- In this way, the Patrol1 and Patrol2 objects are fluctuating the patrol pattern from PatrolZone1 and PatrolZone2 :-) - - -PatrolZoneGroup1 = GROUP:FindByName( "Patrol Zone 1" ) -PatrolZone1 = ZONE_POLYGON:New( "Patrol Zone 1", PatrolZoneGroup1 ) - -PatrolZoneGroup2 = GROUP:FindByName( "Patrol Zone 2" ) -PatrolZone2 = ZONE_POLYGON:New( "Patrol Zone 2", PatrolZoneGroup2 ) - -PatrolSpawn = SPAWN:New( "Patrol Group" ) -PatrolGroup = PatrolSpawn:Spawn() - -Patrol1 = AI_PATROL_ZONE:New( PatrolZone1, 3000, 6000, 400, 600 ) -Patrol1:ManageFuel( 0.2, 60 ) -Patrol1:SetControllable( PatrolGroup ) -Patrol1:__Start( 5 ) - -Patrol2 = AI_PATROL_ZONE:New( PatrolZone2, 600, 1000, 300, 400 ) -Patrol2:ManageFuel( 0.2, 0 ) - ---- State transition function for the PROCESS\_PATROLZONE **Patrol1** object --- @param #AI_PATROL_ZONE self --- @param Wrapper.Group#GROUP AIGroup --- @return #boolean If false is returned, then the OnAfter state transition function will not be called. -function Patrol1:OnLeaveRTB( AIGroup ) - AIGroup:MessageToRed( "Returning to base", 20 ) -end - ---- State transition function for the PROCESS\_PATROLZONE **Patrol1** object --- @param Process_PatrolCore.Zone#AI_PATROL_ZONE self --- @param Wrapper.Group#GROUP AIGroup -function Patrol1:OnAfterRTB( AIGroup ) - local NewGroup = PatrolSpawn:Spawn() - Patrol2:SetControllable( NewGroup ) - Patrol2:__Start( 1 ) -end - ---- State transition function for the PROCESS\_PATROLZONE **Patrol1** object --- @param Process_PatrolCore.Zone#AI_PATROL_ZONE self --- @param Wrapper.Group#GROUP AIGroup -function Patrol1:OnEnterPatrol( AIGroup ) - AIGroup:MessageToRed( "Patrolling in zone " .. PatrolZone1:GetName() , 20 ) -end - ---- State transition function for the PROCESS\_PATROLZONE **Patrol2** object --- @param #AI_PATROL_ZONE self --- @param Wrapper.Group#GROUP AIGroup --- @return #boolean If false is returned, then the OnEnter state transition function will not be called. -function Patrol2:OnBeforeRTB( AIGroup ) - AIGroup:MessageToRed( "Returning to base", 20 ) -end - ---- State transition function for the PROCESS\_PATROLZONE **Patrol2** object --- @param Process_PatrolCore.Zone#AI_PATROL_ZONE self --- @param Wrapper.Group#GROUP AIGroup -function Patrol2:OnEnterRTB( AIGroup ) - local NewGroup = PatrolSpawn:Spawn() - Patrol1:SetControllable( NewGroup ) - Patrol1:__Start( 1 ) -end - ---- State transition function for the PROCESS\_PATROLZONE **Patrol2** object --- @param Process_PatrolCore.Zone#AI_PATROL_ZONE self --- @param Wrapper.Group#GROUP AIGroup -function Patrol2:OnEnterPatrol( AIGroup ) - AIGroup:MessageToRed( "Patrolling in zone " .. PatrolZone2:GetName() , 20 ) -end diff --git a/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz b/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz deleted file mode 100644 index 27061e36e..000000000 Binary files a/Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol Zones/PAT-001 - Switching Patrol Zones.miz and /dev/null differ diff --git a/Moose Test Missions/RAD - Radio/RAD-000 - Transmission from Static/RAD-000 - Transmission from Static.lua b/Moose Test Missions/RAD - Radio/RAD-000 - Transmission from Static/RAD-000 - Transmission from Static.lua deleted file mode 100644 index 296604a1d..000000000 --- a/Moose Test Missions/RAD - Radio/RAD-000 - Transmission from Static/RAD-000 - Transmission from Static.lua +++ /dev/null @@ -1,19 +0,0 @@ --- This test mission demonstrates the RADIO class, particularily when the transmiter is anything but a UNIT or a GROUP (a STATIC in this case) --- The Player is in a Su25T parked on Batumi, and a Russian command center named "Russian Command Center" is placed 12km east of Batumi. - --- Note that if you are not using an ASM aircraft (a clickable cockpit aircraft), then the frequency and the modulation is not important. --- If you want to test the mission fully, replance the SU25T by an ASM aircraft you own and tune to the right frequency (108AM here) - -CommandCenter = STATIC:FindByName("Russian Command Center") - --- Let's get a reference to the Command Center's RADIO -CommandCenterRadio = CommandCenter:GetRadio() - --- Now, we'll set up the next transmission -CommandCenterRadio:SetFileName("Noise.ogg") -- We first need the file name of a sound, -CommandCenterRadio:SetFrequency(108) -- then a frequency in MHz, -CommandCenterRadio:SetModulation(radio.modulation.AM) -- a modulation (we use DCS' enumartion, this way we don't have to type numbers)... -CommandCenterRadio:SetPower(100) -- and finally a power in Watts. A "normal" ground TACAN station has a power of 120W. - --- We have finished tinkering with our transmission, now is the time to broadcast it ! -CommandCenterRadio:Broadcast() \ No newline at end of file diff --git a/Moose Test Missions/RAD - Radio/RAD-000 - Transmission from Static/RAD-000 - Transmission from Static.miz b/Moose Test Missions/RAD - Radio/RAD-000 - Transmission from Static/RAD-000 - Transmission from Static.miz deleted file mode 100644 index d9b05278b..000000000 Binary files a/Moose Test Missions/RAD - Radio/RAD-000 - Transmission from Static/RAD-000 - Transmission from Static.miz and /dev/null differ diff --git a/Moose Test Missions/RAD - Radio/RAD-001 - Transmission from UNIT or GROUP/RAD-001 - Transmission from UNIT or GROUP.lua b/Moose Test Missions/RAD - Radio/RAD-001 - Transmission from UNIT or GROUP/RAD-001 - Transmission from UNIT or GROUP.lua deleted file mode 100644 index 53b86eccb..000000000 --- a/Moose Test Missions/RAD - Radio/RAD-001 - Transmission from UNIT or GROUP/RAD-001 - Transmission from UNIT or GROUP.lua +++ /dev/null @@ -1,25 +0,0 @@ --- This test mission demonstrates the RADIO class, particularily when the transmiter is a UNIT or a GROUP --- The Player is in a Su25T parked on Batumi, and a Russian MiG-29 creatively named "Sergey" is placed above Kobuleti and is --- inbound for a landing on Batumi - --- Note that if you are not using an ASM aircraft (a clickable cockpit aircraft), then the frequency and the modulation is not important. --- If you want to test the mission fully, replance the SU25T by an ASM aircraft you own and tune to the right frequency (108AM here) - -Sergey = UNIT:FindByName("Sergey") - --- Let's get a reference to Sergey's RADIO -SergeyRadio = Sergey:GetRadio() - --- Now, we'll set up the next transmission -SergeyRadio:SetFileName("Noise.ogg") -- We first need the file name of a sound, -SergeyRadio:SetFrequency(108) -- then a frequency in MHz, -SergeyRadio:SetModulation(radio.modulation.AM) -- and a modulation (we use DCS' enumartion, this way we don't have to type numbers). - --- Since Sergey is a UNIT, we can add a subtitle (displayed on the top left) to the transmission, and loop the transmission -SergeyRadio:SetSubtitle("Hey, hear that noise ?", 5) -- The subtitle "Noise" will be displayed for 5 secs -SergeyRadio:SetLoop(false) - --- Notice that we didn't have to imput a power ? If the broadcater is a UNIT or a GROUP, DCS automatically guesses the power to use depending on the type of UNIT or GROUP - --- We have finished tinkering with our transmission, now is the time to broadcast it ! -SergeyRadio:Broadcast() \ No newline at end of file diff --git a/Moose Test Missions/RAD - Radio/RAD-001 - Transmission from UNIT or GROUP/RAD-001 - Transmission from UNIT or GROUP.miz b/Moose Test Missions/RAD - Radio/RAD-001 - Transmission from UNIT or GROUP/RAD-001 - Transmission from UNIT or GROUP.miz deleted file mode 100644 index c86971d28..000000000 Binary files a/Moose Test Missions/RAD - Radio/RAD-001 - Transmission from UNIT or GROUP/RAD-001 - Transmission from UNIT or GROUP.miz and /dev/null differ diff --git a/Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and Tricks/RAD-002 - Transmission Tips and Tricks.lua b/Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and Tricks/RAD-002 - Transmission Tips and Tricks.lua deleted file mode 100644 index 0d7a473f1..000000000 --- a/Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and Tricks/RAD-002 - Transmission Tips and Tricks.lua +++ /dev/null @@ -1,91 +0,0 @@ --- This test mission demonstrates the RADIO class in a practical scenario. --- It also focuses on how to create transmissions faster and more efficiently --- Please Read both RAD-000 and RAD-001, as well as SCH-000 code first. - --- Note that if you are not using an ASM aircraft (a clickable cockpit aircraft), then the frequency and the modulation is not important. --- If you want to test the mission fully, replance the SU25T by an ASM aircraft you own and tune to the right frequency (115AM here) - --- The Player is in a Su25T parked on Batumi, and a Russian command center named "Batumi Tower" placed near Batumi will act as Batumi's Radio Tower. --- This mission also features the "Viktor" flight, a Russian Su25, who is inbound for landing on Batumi. --- The goal of this script is to manage the dialog between Viktor and Batumi Tower. - --- The (short) conversation between Viktor and Batumi Tower will happen on 115 AM --- Time 0 : Batumi Tower "Viktor flight, this is Batumi Tower, enter left base runway one two five, report 5 kilometers final. Over." --- Time 10 : Viktor "Report 5 kilometers final, one two five, viktor" --- Time 145 : Viktor "Batumi Tower, Viktor is 5 kilomters final, request landing clearance. Over?" --- Time 154 : Batumi Tower "Viktor flight, you are claer to land, runway one two five. Check gear down." --- Time 160 : Viktor "Clear to land, One two five, Viktor" --- Time 210 : Viktor "Viktor, touchdown" --- Time 215 : Batumi Tower "Viktor, confirmed touchdown, taxi to parking area, Batumi Tower out." - - -BatumiRadio = STATIC:FindByName("Batumi Tower"):GetRadio() -ViktorRadio = UNIT:FindByName("Viktor"):GetRadio() - --- Let's first explore different shortcuts to setup a transmission before broadcastiong it ------------------------------------------------------------------------------------------------------------------------------------------------------- --- First, the long way. -BatumiRadio:SetFileName("Batumi Tower - Enter left base.ogg") -BatumiRadio:SetFrequency(115) -BatumiRadio:SetModulation(radio.modulation.AM) -BatumiRadio:SetPower(100) - --- Every RADIO.SetXXX() function returns the radio, so we can rewrite the code above this way : -BatumiRadio:SetFileName("Batumi Tower - Enter left base.ogg"):SetFrequency(115):SetModulation(radio.modulation.AM):SetPower(100) - --- We can also use the shortcut RADIO:NewGenericTransmission() to set multiple parameters in one function call --- If our broadcaster was a UNIT or a GROUP, the more appropriate shortcut to use would have been NewUnitTransmission() --- it works for both UNIT and GROUP, despite its name ! -BatumiRadio:NewGenericTransmission("Batumi Tower - Enter left base.ogg", 115, radio.modulation.AM, 100) - --- If you already set some parameters previously, you don't have to redo it ! --- NewGenericTransmission's paramter have to be set in order -BatumiRadio:NewGenericTransmission("Batumi Tower - Enter left base.ogg", 115) -- Modulation is still AM and power is still 100 (set previously) - ---If you want to change only the sound file, the frequency and the power for exemple, you can still use the appropriate Set function -BatumiRadio:NewGenericTransmission("Batumi Tower - Enter left base.ogg", 115):SetPower(100) - --- We have finished tinkering with our transmission, now is the time to broadcast it ! -BatumiRadio:Broadcast() - --- Now, if Viktor answered imedately, the two radio broadcasts would overlap. We need to delay Viktor's answer. ------------------------------------------------------------------------------------------------------------------------------------------------------- -CommunitcationScheduler = SCHEDULER:New( nil, - function() - ViktorRadio:SetFileName("Viktor - Enter left base ack.ogg"):SetFrequency(115):SetModulation(radio.modulation.AM):Broadcast() -- We don't specify a subtitle since we don't want one - end, {}, 10 -- 10s delay - ) - --- Viktor takes 145s to be 5km final, and need to contact Batumi Tower. ------------------------------------------------------------------------------------------------------------------------------------------------------- -CommunitcationScheduler:Schedule( nil, - function() - ViktorRadio:SetFileName("Viktor - Request landing clearance.ogg"):Broadcast() --We only specify the new file name, since frequency and modulation didn't change - end, {}, 145 - ) - --- Now that you understand everything about the RADIO class, the rest is pretty trivial -------------------------------------------------------------------------------------------------------------------------------------------------------- -CommunitcationScheduler:Schedule( nil, - function() - BatumiRadio:SetFileName("Batumi Tower - Clear to land.ogg"):Broadcast() - end, {}, 154 - ) - -CommunitcationScheduler:Schedule( nil, - function() - ViktorRadio:SetFileName("Viktor - Clear to land ack.ogg"):Broadcast() - end, {}, 160 - ) - -CommunitcationScheduler:Schedule( nil, - function() - ViktorRadio:SetFileName("Viktor - Touchdown.ogg"):Broadcast() - end, {}, 210 - ) - -CommunitcationScheduler:Schedule( nil, - function() - BatumiRadio:SetFileName("Batumi Tower - Taxi to parking.ogg"):Broadcast() - end, {}, 215 - ) \ No newline at end of file diff --git a/Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and Tricks/RAD-002 - Transmission Tips and Tricks.miz b/Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and Tricks/RAD-002 - Transmission Tips and Tricks.miz deleted file mode 100644 index 7beb833c0..000000000 Binary files a/Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and Tricks/RAD-002 - Transmission Tips and Tricks.miz and /dev/null differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.lua b/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.lua deleted file mode 100644 index be50637aa..000000000 --- a/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.lua +++ /dev/null @@ -1,33 +0,0 @@ ---- Simple function scheduling --- --- === --- --- Author: FlightControl --- Date Created: 12 Dec 2016 --- --- # Situation --- Uses the Tracing functions from BASE within the DCS.log file. Check the DCS.log file for the results. --- Create a new SCHEDULER object. --- Check the DCS.log. --- --- # Test cases: --- --- 1. The log should contain a "Hello World" line that is fired off 10 seconds after mission start. --- --- --- # Status: TESTED - 12 Dec 2016 - -local TestScheduler = SCHEDULER:New( nil, - function() - BASE:E( "Hello World 1") - end, {}, 1 - ) - -SCHEDULER:New( nil, - function() - BASE:E( "Hello World 2") - end, {}, 2 - ) - -collectgarbage() - diff --git a/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz deleted file mode 100644 index c44b83b7b..000000000 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-000 - Simple Scheduling/SCH-000 - Simple Scheduling.miz and /dev/null differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.lua b/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.lua deleted file mode 100644 index e759b7017..000000000 --- a/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.lua +++ /dev/null @@ -1,34 +0,0 @@ ---- Simple Object Scheduling --- --- === --- --- Author: FlightControl --- Date Created: 12 Dec 2016 --- --- # Situation --- Uses the Tracing functions from BASE within the DCS.log file. Check the DCS.log file for the results. --- Create a new SCHEDULER object. --- Check the DCS.log. --- --- # Test cases: --- --- 1. Tracing of a scheduler in an Object. --- The log should contain a "Hello World" line of the object, that is fired off 1 seconds after mission start. --- --- # Status: TESTED - 12 Dec 2016 - -local TEST_BASE = { - ClassName = "TEST_BASE", - } - -function TEST_BASE:New( Message ) - self = BASE:Inherit( self, BASE:New() ) - - local TestScheduler = SCHEDULER:New( self, - function( Object, Message ) - Object:E( Message ) - end, { Message }, 1 - ) -end - -local Test = TEST_BASE:New( "Hello World" ) \ No newline at end of file diff --git a/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz deleted file mode 100644 index 294d7e4bb..000000000 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object Scheduling/SCH-001 - Simple Object Scheduling.miz and /dev/null differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.lua b/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.lua deleted file mode 100644 index 4e1c84183..000000000 --- a/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.lua +++ /dev/null @@ -1,24 +0,0 @@ ---- Simple repeat scheduling of a function. --- --- === --- --- Author: FlightControl --- Date Created: 13 Dec 2016 --- --- # Situation --- Uses the Tracing functions from BASE within the DCS.log file. Check the DCS.log file for the results. --- Create a new SCHEDULER object. --- Check the DCS.log. --- --- # Test cases: --- --- 1. The log should contain "Hello World Repeat" lines that is fired off 1 second after mission start and is repeated every 1 seconds. --- --- --- # Status: TESTED - 13 Dec 2016 - -local TestScheduler = SCHEDULER:New( nil, - function() - BASE:E( "Hello World Repeat") - end, {}, 1, 1 - ) \ No newline at end of file diff --git a/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz deleted file mode 100644 index 31017e0aa..000000000 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat Scheduling/SCH-100 - Simple Repeat Scheduling.miz and /dev/null differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.lua b/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.lua deleted file mode 100644 index 67f721125..000000000 --- a/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.lua +++ /dev/null @@ -1,44 +0,0 @@ ---- Object Repeat Scheduling. --- --- === --- --- Author: FlightControl --- Date Created: 13 Dec 2016 --- --- # Situation --- Uses the Tracing functions from BASE within the DCS.log file. Check the DCS.log file for the results. --- Create a new SCHEDULER object. --- Check the DCS.log. --- --- Three Test objects are created. --- --- # Test cases: --- --- 1. Object Test1 should start after 1 seconds showing every second "Hello World Repeat 1". --- 2. Object Test2 should start after 2 seconds showing every 2 seconds "Hello World Repeat 2" and stop after one minute. --- 3. Object Test3 should start after 10 seconds showing with a 10 seconds randomized interval of 10 seconds "Hello World Repeat 3" and stop after one minute. --- --- # Status: TESTED - 13 Dec 2016 - -local TEST_BASE = { - ClassName = "TEST_BASE", - } - -function TEST_BASE:New( Message, Start, Repeat, Randomize, Stop ) - self = BASE:Inherit( self, BASE:New() ) - - self.TestScheduler = SCHEDULER:New( self, - function( Object, Message ) - Object:E( Message ) - end, { Message }, Start, Repeat, Randomize, Stop - ) - return self -end - -do -local Test1 = TEST_BASE:New( "Hello World Repeat 1", 1, 1 ) -end - -local Test2 = TEST_BASE:New( "Hello World Repeat 2", 2, 2, 0, 60 ) - -local Test3 = TEST_BASE:New( "Hello World Repeat 3", 10, 10, 1.0, 60 ) diff --git a/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz deleted file mode 100644 index 355e633b9..000000000 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat Scheduling/SCH-110 - Object Repeat Scheduling.miz and /dev/null differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.lua b/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.lua deleted file mode 100644 index 107ba46d1..000000000 --- a/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.lua +++ /dev/null @@ -1,41 +0,0 @@ ---- Simple repeat scheduling of a function. --- --- === --- --- Author: FlightControl --- Date Created: 14 Dec 2016 --- --- # Situation --- Uses the Tracing functions from BASE within the DCS.log file. Check the DCS.log file for the results. --- Create a new SCHEDULER object. --- Check the DCS.log. --- --- Start a schedule called TestScheduler. TestScheduler will repeat the words "Hello World Repeat" every second in the log. --- After 10 seconds, TestScheduler will stop the scheduler. --- After 20 seconds, TestScheduler will restart the scheduler. --- --- # Test cases: --- --- 1. Check that the "Hello World Repeat" lines are consistent with the scheduling timing. They should stop showing after 10 seconds, and restart after 20 seconds. --- --- --- # Status: TESTED - 14 Dec 2016 - -local TestScheduler = SCHEDULER:New( nil, - function() - BASE:E( timer.getTime() .. " - Hello World Repeat") - end, {}, 1, 1 - ) - -SCHEDULER:New( nil, - function() - TestScheduler:Stop() - end, {}, 10 - ) - -SCHEDULER:New( nil, - function() - TestScheduler:Start() - end, {}, 20 - ) - \ No newline at end of file diff --git a/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz b/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz deleted file mode 100644 index 97165d9c9..000000000 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz and /dev/null differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.lua b/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.lua deleted file mode 100644 index f5e5dab2d..000000000 --- a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.lua +++ /dev/null @@ -1,60 +0,0 @@ ---- No Object Scheduling because of garbage collect and Object nillification. --- --- === --- --- Author: FlightControl --- Date Created: 12 Dec 2016 --- --- # Situation --- Uses the Tracing functions from BASE within the DCS.log file. Check the DCS.log file for the results. --- Create a new SCHEDULER object. --- Check the DCS.log. --- --- A Test object is created. --- It is nillified directly after the Schedule has been planned. --- There should be no schedule fired. --- The Test object should be garbage collected! --- --- THIS IS A VERY IMPORTANT TEST! --- --- # Test cases: --- --- 1. No schedule should be fired! The destructors of the Test object should be shown. --- 2. Commend the nillification of the Test object in the source, and test again. --- The schedule should now be fired and Hello World should be logged through the Test object. --- --- # Status: STARTED - 12 Dec 2016 - -local TEST_BASE = { - ClassName = "TEST_BASE", - } - -function TEST_BASE:New( Message ) - self = BASE:Inherit( self, BASE:New() ) - - self.TestScheduler = SCHEDULER:New( self, - function( Object, Message ) - Object:E( Message ) - end, { Message }, 1 - ) - return self -end - -do -local Test1 = TEST_BASE:New( "Hello World Test 1" ) -Test1 = nil -BASE:E( Test1 ) -end - -local Test2 = TEST_BASE:New( "Hello World Test 2" ) -BASE:E( Test2 ) - -local Test3 = TEST_BASE:New( "Hello World Test 3" ) -Test3 = nil -BASE:E( Test3 ) - -collectgarbage() - -BASE:E( "Collect Garbage executed." ) -BASE:E( "You should only see a Hello Worlld message for Test 2!" ) -BASE:E( "Check if Test 1 and Test 3 are garbage collected!" ) \ No newline at end of file diff --git a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz deleted file mode 100644 index d3ae3dcc1..000000000 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object Scheduling/SCH-300 - GC Simple Object Scheduling.miz and /dev/null differ diff --git a/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.lua b/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.lua deleted file mode 100644 index 54faeb602..000000000 --- a/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.lua +++ /dev/null @@ -1,80 +0,0 @@ ---- Object Repeat Scheduling. --- --- === --- --- Author: FlightControl --- Date Created: 13 Dec 2016 --- --- # Situation --- Three objects Test1, Test2, Test 3 are created with schedule a function. --- After 15 seconds, Test1 is nillified and Garbage Collect is done. --- After 30 seconds, Test2 is nillified and Garbage Collect is done. --- After 45 seconds, Test3 is nillified and Garbage Collect is done. --- Uses the Tracing functions from BASE within the DCS.log file. Check the DCS.log file for the results. --- Create a new SCHEDULER object. --- Check the DCS.log. --- --- --- Three Test objects are created. --- --- # Test cases: --- --- 1. Object Test1 should start after 1 seconds showing every second "Hello World Repeat 1". --- 2. Object Test2 should start after 2 seconds showing every 2 seconds "Hello World Repeat 2" and stop after one minute. --- 3. Object Test3 should start after 10 seconds showing with a 10 seconds randomized interval of 10 seconds "Hello World Repeat 3" and stop after one minute. --- 4. After 15 seconds, Test1 should stop working. No "Hello World Repeat 1" may be shown after 15 seconds. --- 5. After 30 seconds, Test2 should stop working. No "Hello World Repeat 2" may be shown after 30 seconds. --- 6. After 45 seconds, Test3 should stop working. No "Hello World Repeat 3" may be shown after 45 seconds. --- --- # Status: TESTED - 13 Dec 2016 - -local TEST_BASE = { - ClassName = "TEST_BASE", - } - -function TEST_BASE:New( Message, Start, Repeat, Randomize, Stop ) - self = BASE:Inherit( self, BASE:New() ) - - self.TestScheduler = SCHEDULER:New( self, - function( Object, Message ) - Object:E( Message ) - end, { Message }, Start, Repeat, Randomize, Stop - ) - return self -end - -do -local Test1 = TEST_BASE:New( "Hello World Repeat 1", 1, 1 ) - --- Nillify Test1 after 15 seconds and garbage collect. -local Nil1 = SCHEDULER:New( nil, - function() - BASE:E( "Nillify Test1 and Garbage Collect" ) - Test1 = nil - collectgarbage() - end, {}, 15 ) - -end - -local Test2 = TEST_BASE:New( "Hello World Repeat 2", 2, 2, 0, 60 ) - - -local Test3 = TEST_BASE:New( "Hello World Repeat 3", 10, 10, 1.0, 60 ) - --- Nillify Test2 after 30 seconds and garbage collect. -local Nil2 = SCHEDULER:New( nil, - function() - BASE:E( "Nillify Test2 and Garbage Collect" ) - Test2 = nil - collectgarbage() - end, {}, 30 ) - --- Nillify Test3 after 45 seconds and garbage collect. -local Nil3 = SCHEDULER:New( nil, - function() - BASE:E( "Nillify Test3 and Garbage Collect" ) - Test3 = nil - collectgarbage() - end, {}, 45 ) - -collectgarbage() diff --git a/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz b/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz deleted file mode 100644 index b1a844852..000000000 Binary files a/Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat Scheduling/SCH-310 - GC Object Repeat Scheduling.miz and /dev/null differ diff --git a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.lua b/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.lua deleted file mode 100644 index 6c7b7b601..000000000 --- a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.lua +++ /dev/null @@ -1,38 +0,0 @@ ---- --- Name: SCO-100 - Scoring of Statics --- Author: FlightControl --- Date Created: 21 Feb 2017 --- --- # Situation: --- --- A shooting range has been setup. Fly the Ka-50 or the Su-25T to the statics located near the airport, and shoot them. --- --- # Test cases: --- --- 1. Observe the scoring granted to your flight when you hit and kill targets. - - -HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) - -CommandCenter = COMMANDCENTER:New( HQ, "Bravo" ) - -Scoring = SCORING:New( "Shooting Range 1" ) - -Scoring:SetScaleDestroyScore( 10 ) - -Scoring:SetScaleDestroyPenalty( 40 ) - -Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 ) - --- Test for zone scores. - --- This one is to test scoring on normal units. -ShootingRangeZone = ZONE:New( "ScoringZone1" ) -Scoring:AddZoneScore( ShootingRangeZone, 200 ) - --- This one is to test scoring on scenery. --- Note that you can only destroy scenery with heavy weapons. -SceneryZone = ZONE:New( "ScoringZone2" ) -Scoring:AddZoneScore( SceneryZone, 200 ) - -Scoring:AddStaticScore(STATIC:FindByName( "Shooting Range #010" ), 100 ) diff --git a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz b/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz deleted file mode 100644 index e3003e609..000000000 Binary files a/Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100 - Scoring of Statics.miz and /dev/null differ diff --git a/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.lua b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.lua deleted file mode 100644 index 176e709ab..000000000 --- a/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.lua +++ /dev/null @@ -1,21 +0,0 @@ ---- --- Name: SCO-101 - Scoring Client to Client --- Author: FlightControl --- Date Created: 24 Feb 2017 --- --- # Situation: --- --- A shooting range has been setup to test client to client scoring. --- --- # Test cases: --- --- 1. Observe the scoring granted to your flight when you hit and kill other clients. - - -HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) - -CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -Scoring = SCORING:New( "Detect Demo" ) - - diff --git a/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz b/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz deleted file mode 100644 index cf1b30608..000000000 Binary files a/Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to Client/SCO-101 - Scoring Client to Client.miz and /dev/null differ diff --git a/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.lua b/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.lua deleted file mode 100644 index 127735351..000000000 --- a/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.lua +++ /dev/null @@ -1,139 +0,0 @@ ---- --- Name: SCO-500 - Scoring Multi Player Demo Mission 1 --- Author: Pikey and FlightControl --- Date Created: 1 Mar 2017 --- --- # Situation: --- --- A demo mission for the scoring. Read the briefing and have fun. --- --- # Test cases: --- --- 1. Observe the scoring granted to your flight when you hit and kill targets. - --- Define the patrol zones -BlueCapZone = ZONE_POLYGON:New( "BlueCapZone", GROUP:FindByName( "Blue CAP Zone Patrol" ) ) -RedCapZone = ZONE_POLYGON:New( "RedCapZone", GROUP:FindByName( "Red CAP Zone Patrol" ) ) - --- Define the engage zones -BlueEngageZone = ZONE_POLYGON:New( "BlueEngageZone", GROUP:FindByName( "Blue CAP Zone Engage" ) ) -RedEngageZone = ZONE_POLYGON:New( "RedEngageZone", GROUP:FindByName( "Red CAP Zone Engage" ) ) - --- Define the Spawn zones for ground vehicles -BlueSpawnGroundZone = ZONE_POLYGON:New( "BlueSpawnGroundZone", GROUP:FindByName( "Blue Spawn Zone" ) ) ---RedSpawnGroundZone = ZONE_POLYGON:New( "RedSpawnGroundZone", GROUP:FindByName( "Red Spawn Zone" ) ) - -RedSpawnGroundZone = ZONE:New( "Red Spawn Zone" ) - --- Define the Scoring zones that define the shelters -BlueShelterZone = ZONE_POLYGON:New( "Blue Shelters", GROUP:FindByName( "Blue Shelters" ) ) -RedShelterZone = ZONE_POLYGON:New( "Red Shelters", GROUP:FindByName( "Red Shelters" ) ) - --- Define the Set of Clients that are used for the AI Balancers -BluePlanesClientSet = SET_CLIENT:New():FilterCoalitions( "blue" ):FilterCategories( "plane" ):FilterPrefixes( "Blue Player") -RedPlanesClientSet = SET_CLIENT:New():FilterCoalitions( "red" ):FilterCategories( "plane" ):FilterPrefixes( "Red Player") - --- Define the Spawn objects for the AI planes -BluePlanesSpawn = SPAWN:New( "BlueAICAP" ):InitCleanUp( 120 ):InitLimit( 5, 0 ) -RedPlanesSpawn = SPAWN:New( "RedAICAP" ):InitCleanUp( 120 ):InitLimit( 5, 0 ) - --- Define the AI Balancers for the planes -BlueAIB = AI_BALANCER:New( BluePlanesClientSet, BluePlanesSpawn ):InitSpawnInterval( 60, 1200 ) -RedAIB = AI_BALANCER:New( RedPlanesClientSet, RedPlanesSpawn ):InitSpawnInterval( 60, 1200 ) - --- Define the Spawn objects for the airbase defenses -BlueAirbaseDefense1Spawn = SPAWN:New( "Blue Airbase Defense 1" ):InitLimit( 10, 10 ):SpawnScheduled( 60, 0 ) -BlueAirbaseDefense2Spawn = SPAWN:New( "Blue Airbase Defense 2" ):InitLimit( 2, 10 ):SpawnScheduled( 60, 0 ) -RedAirbaseDefense1Spawn = SPAWN:New( "Red Airbase Defense 1" ):InitLimit( 10, 10 ):SpawnScheduled( 60, 0 ) -RedAirbaseDefense2Spawn = SPAWN:New( "Red Airbase Defense 2" ):InitLimit( 2, 10 ):SpawnScheduled( 60, 0 ) - --- Define the ground forces spawning engines... - --- First define the template arrays. -BlueGroundTemplates = { "Blue Ground Forces 1", "Blue Ground Forces 2", "Blue Ground Forces 3" } -RedGroundTemplates = { "Red Ground Forces 2", "Red Ground Forces 2", "Red Ground Forces 3" } - --- New we are using these templates to define the spawn objects for the ground forces. --- We spawn them at random places into the define zone. -BlueGroundSpawn = SPAWN - :New( "Blue Ground Forces" ) - :InitLimit( 12, 30 ) - :InitRandomizeZones( { BlueSpawnGroundZone } ) - :InitRandomizeTemplate( BlueGroundTemplates ) - :SpawnScheduled( 60, 0.2 ) - -RedGroundSpawn = SPAWN - :New( "Red Ground Forces" ) - :InitLimit( 12, 30 ) - :InitRandomizeTemplate( RedGroundTemplates ) - :InitRandomizeZones( { RedSpawnGroundZone } ) - :SpawnScheduled( 60, 0.2 ) - - - -BlueCap = {} -RedCap = {} - --- Define the OnAfterSpawned events of the AI balancer Spawn Groups -function BlueAIB:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) - if AIGroup then - BlueCap[AIGroup] = BlueCap[AIGroup] or AI_CAP_ZONE:New( BlueCapZone, 500, 3000, 450, 1200 ) - local Cap = BlueCap[AIGroup] -- AI.AI_CAP#AI_CAP_ZONE - - Cap:ManageFuel( 0.4, 180 ) - Cap:SetEngageZone( BlueEngageZone ) - Cap:SetControllable( AIGroup ) - Cap:Start() - end -end - -function RedAIB:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) - - if AIGroup then - RedCap[AIGroup] = RedCap[AIGroup] or AI_CAP_ZONE:New( RedCapZone, 500, 3000, 450, 1200 ) - local Cap = RedCap[AIGroup] -- AI.AI_CAP#AI_CAP_ZONE - - Cap:ManageFuel( 0.4, 180 ) - Cap:SetEngageZone( BlueEngageZone ) - Cap:SetControllable( AIGroup ) - Cap:Start() - end -end - - - - --- Okay, now that we defined all this stuff, now make the SCORING setup ... - --- First define a Scoring object -local Scoring = SCORING:New( "SCO-500 - Scoring Demonstration Mission" ) - --- Define within the scoring the shelter designated targets. -Scoring:AddZoneScore( BlueShelterZone, 50 ) -- Per shelter destroyed, 50 extra points are granted. -Scoring:AddZoneScore( RedShelterZone, 50 ) -- Per shelter destroyed, 50 extra points are granted. - --- Define the static objects that give extra scores -Scoring:AddStaticScore( STATIC:FindByName( "Red Factory 1"), 100 ) -Scoring:AddStaticScore( STATIC:FindByName( "Red Factory 2"), 100 ) -Scoring:AddStaticScore( STATIC:FindByName( "Red Factory 3"), 100 ) -Scoring:AddStaticScore( STATIC:FindByName( "Red Factory 4"), 100 ) - -Scoring:AddStaticScore( STATIC:FindByName( "Red Truck #001"), 80 ) -Scoring:AddStaticScore( STATIC:FindByName( "Red Truck #002"), 80 ) -Scoring:AddStaticScore( STATIC:FindByName( "Red Truck #003"), 80 ) -Scoring:AddStaticScore( STATIC:FindByName( "Red Truck #004"), 80 ) - -Scoring:AddStaticScore( STATIC:FindByName( "Blue Factory 1" ), 100 ) -Scoring:AddStaticScore( STATIC:FindByName( "Blue Factory 2" ), 100 ) -Scoring:AddStaticScore( STATIC:FindByName( "Blue Factory 3" ), 100 ) -Scoring:AddStaticScore( STATIC:FindByName( "Blue Factory 4" ), 100 ) - -Scoring:AddStaticScore( STATIC:FindByName( "Blue Truck #001" ), 80 ) -Scoring:AddStaticScore( STATIC:FindByName( "Blue Truck #002" ), 80 ) -Scoring:AddStaticScore( STATIC:FindByName( "Blue Truck #003" ), 80 ) -Scoring:AddStaticScore( STATIC:FindByName( "Blue Truck #004" ), 80 ) - --- Scale the scoring rewarded. -Scoring:SetScaleDestroyScore( 30 ) -Scoring:SetScaleDestroyPenalty( 90 ) -- Penalties are punished more than normal destroys. - diff --git a/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.miz b/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.miz deleted file mode 100644 index c3a3b6e7b..000000000 Binary files a/Moose Test Missions/SCO - Scoring/SCO-500 - Scoring Multi Player Demo Mission 1/SCO-500 - Scoring Multi Player Demo Mission 1.miz and /dev/null differ diff --git a/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.lua b/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.lua deleted file mode 100644 index e53f6d936..000000000 --- a/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.lua +++ /dev/null @@ -1,16 +0,0 @@ - -BlueAirbaseSet = SET_AIRBASE:New():FilterCoalitions("blue"):FilterStart() - -RedAirbaseSet = SET_AIRBASE:New():FilterCoalitions("red"):FilterStart() - -RedAirbaseHelipadSet = SET_AIRBASE:New():FilterCoalitions("red"):FilterCategories("helipad"):FilterStart() - -BlueAirbaseShipSet = SET_AIRBASE:New():FilterCoalitions("blue"):FilterCategories("ship"):FilterStart() - -BlueAirbaseSet:Flush() - -RedAirbaseSet:Flush() - -RedAirbaseHelipadSet:Flush() - -BlueAirbaseShipSet:Flush() \ No newline at end of file diff --git a/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz b/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz deleted file mode 100644 index 2b8015437..000000000 Binary files a/Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 - Airbase Sets.miz and /dev/null differ diff --git a/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.lua b/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.lua deleted file mode 100644 index 534d78e34..000000000 --- a/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.lua +++ /dev/null @@ -1,157 +0,0 @@ - - - - -SetVehicles = SET_GROUP:New() - -SetVehicles:AddGroupsByName( { "Vehicle A", "Vehicle B", "Vehicle C" } ) - -SetVehicles:ForEachGroup( - --- @param Wrapper.Group#GROUP MooseGroup - function( MooseGroup ) - for UnitId, UnitData in pairs( MooseGroup:GetUnits() ) do - local UnitAction = UnitData -- Wrapper.Unit#UNIT - UnitAction:SmokeGreen() - end - end -) - - - -SetBluePlanesGroup = SET_GROUP:New() - :FilterCoalitions( "blue" ) - :FilterCategories( "plane" ) - :FilterStart() - -SetNorthKoreaGroup = SET_GROUP:New() - :FilterCountries( "RUSSIA" ) - :FilterStart() - -SetSAMGroup = SET_GROUP:New() - :FilterPrefixes( "SAM" ) - :FilterStart() - :SetIteratorIntervals( 5, 10 ) - -SpawnUS_Plane = SPAWN:New( 'Spawn Test USA Plane') -GroupUS_Plane = SpawnUS_Plane:Spawn() - -SpawnUS_Vehicle = SPAWN:New( 'Spawn Test USA Vehicle') -GroupUS_Vehicle = SpawnUS_Vehicle:Spawn() - -SpawnUS_Ship = SPAWN:New( 'Spawn Test USA Ship') -GroupUS_Ship = SpawnUS_Ship:Spawn() - -SpawnRU_Vehicle = SPAWN:New( 'Spawn Test RUSSIA Vehicle') -GroupRU_Vehicle = SpawnRU_Vehicle:Spawn() - -SpawnRU_Ship = SPAWN:New( 'Spawn Test RUSSIA Ship') -GroupRU_Ship = SpawnRU_Ship:Spawn() - -SpawnM2A2_AttackVehicle = SPAWN:New( 'Spawn Test M2A2 Attack Vehicle' ):InitRandomizeUnits( true, 10, 4 ) -SpawnSAM_AttackVehicle = SPAWN:New( 'Spawn Test SAM Attack Vehicle' ):InitRandomizeUnits( true, 10, 4 ) - -for i = 1, 30 do - GroupM2A2_AttackVehicle = SpawnM2A2_AttackVehicle:SpawnInZone( ZONE:New("Spawn Zone") ) - GroupSAM_AttackVehicle = SpawnSAM_AttackVehicle:SpawnInZone( ZONE:New("Spawn Zone") ) -end - -SetVehicleCompletely = SET_GROUP:New() - :FilterPrefixes( "Spawn Vehicle Zone Completely" ) - :FilterStart() - -SetVehiclePartly = SET_GROUP:New() - :FilterPrefixes( "Spawn Vehicle Zone Partly" ) - :FilterStart() - -SetVehicleNot = SET_GROUP:New() - :FilterPrefixes( "Spawn Vehicle Zone Not" ) - :FilterStart() - -Spawn_Vehicle_Zone_Completely = SPAWN:New( 'Spawn Vehicle Zone Completely' ):InitRandomizeUnits( true, 10, 4) -Spawn_Vehicle_Zone_Partly = SPAWN:New( 'Spawn Vehicle Zone Partly' ):InitRandomizeUnits( true, 10, 4 ) -Spawn_Vehicle_Zone_Not = SPAWN:New( 'Spawn Vehicle Zone Not' ):InitRandomizeUnits( true, 10, 4 ) -for i = 1, 30 do - Spawn_Vehicle_Zone_Completely:SpawnInZone( ZONE:New("Spawn Zone Completely") ) - Spawn_Vehicle_Zone_Partly:SpawnInZone( ZONE:New("Spawn Zone Partly") ) - Spawn_Vehicle_Zone_Not:SpawnInZone( ZONE:New("Spawn Zone Not") ) -end - ---DBBlue:TraceDatabase() ---SCHEDULER:New( DBBluePlanes, DBBluePlanes.Flush, { }, 1 ) ---SCHEDULER:New( DBRedVehicles, DBRedVehicles.Flush, { }, 1 ) ---SCHEDULER:New( DBShips, DBShips.Flush, { }, 1 ) ---SCHEDULER:New( DBBelgium, DBBelgium.Flush, { }, 1 ) ---SCHEDULER:New( DBNorthKorea, DBNorthKorea.Flush, { }, 1 ) ---SCHEDULER:New( DBKA50Vinson, DBKA50Vinson.Flush, { }, 1 ) --- ---SCHEDULER:New( DBBluePlanesGroup, DBBluePlanesGroup.Flush, { }, 1 ) ---SCHEDULER:New( DBNorthKoreaGroup, DBNorthKoreaGroup.Flush, { }, 1 ) - -SetBluePlanesGroup:ForEachGroup( - --- @param Wrapper.Group#GROUP MooseGroup - function( MooseGroup ) - for UnitId, UnitData in pairs( MooseGroup:GetUnits() ) do - local UnitAction = UnitData -- Wrapper.Unit#UNIT - UnitAction:SmokeBlue() - end - end -) - -SetNorthKoreaGroup:ForEachGroup( - --- @param Wrapper.Group#GROUP MooseGroup - function( MooseGroup ) - for UnitId, UnitData in pairs( MooseGroup:GetUnits() ) do - local UnitAction = UnitData -- Wrapper.Unit#UNIT - UnitAction:SmokeRed() - end - end -) - -SetSAMGroup:ForEachGroup( - --- @param Wrapper.Group#GROUP MooseGroup - function( MooseGroup ) - for UnitId, UnitData in pairs( MooseGroup:GetUnits() ) do - local UnitAction = UnitData -- Wrapper.Unit#UNIT - UnitAction:SmokeOrange() - end - end -) - -GroupZoneCompletely = GROUP:FindByName( "Zone Completely" ) -GroupZonePartly = GROUP:FindByName( "Zone Partly" ) -GroupZoneNot = GROUP:FindByName( "Zone Not" ) - -ZoneCompletely = ZONE_POLYGON:New( "Zone Completely", GroupZoneCompletely ):SmokeZone( SMOKECOLOR.White ) -ZonePartly = ZONE_POLYGON:New( "Zone Partly", GroupZonePartly ):SmokeZone( SMOKECOLOR.White ) -ZoneNot = ZONE_POLYGON:New( "Zone Not", GroupZoneNot ):SmokeZone( SMOKECOLOR.White ) - -SetVehicleCompletely:ForEachGroupCompletelyInZone( ZoneCompletely, - --- @param Wrapper.Group#GROUP MooseGroup - function( MooseGroup ) - for UnitId, UnitData in pairs( MooseGroup:GetUnits() ) do - local UnitAction = UnitData -- Wrapper.Unit#UNIT - UnitAction:SmokeBlue() - end - end -) - -SetVehiclePartly:ForEachGroupPartlyInZone( ZonePartly, - --- @param Wrapper.Group#GROUP MooseGroup - function( MooseGroup ) - for UnitId, UnitData in pairs( MooseGroup:GetUnits() ) do - local UnitAction = UnitData -- Wrapper.Unit#UNIT - UnitAction:SmokeBlue() - end - end -) - -SetVehicleNot:ForEachGroupNotInZone( ZoneNot, - --- @param Wrapper.Group#GROUP MooseGroup - function( MooseGroup ) - for UnitId, UnitData in pairs( MooseGroup:GetUnits() ) do - local UnitAction = UnitData -- Wrapper.Unit#UNIT - UnitAction:SmokeBlue() - end - end -) - \ No newline at end of file diff --git a/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz b/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz deleted file mode 100644 index b3bb2e970..000000000 Binary files a/Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 - Group Sets.miz and /dev/null differ diff --git a/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.lua b/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.lua deleted file mode 100644 index af79af84f..000000000 --- a/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.lua +++ /dev/null @@ -1,3 +0,0 @@ - -SetClient = SET_CLIENT:New():FilterCoalitions("blue"):FilterCategories("plane"):FilterCountries("USA"):FilterStart() - diff --git a/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz b/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz deleted file mode 100644 index fbedd028d..000000000 Binary files a/Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 - Client Sets.miz and /dev/null differ diff --git a/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.lua b/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.lua deleted file mode 100644 index ea28a16be..000000000 --- a/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.lua +++ /dev/null @@ -1,11 +0,0 @@ - - - --- CCCP SEAD Defenses -SEAD_RU_SAM_Defenses = SEAD - :New( - { 'SAM Test' - } - ) - - \ No newline at end of file diff --git a/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz b/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz deleted file mode 100644 index 3657c63f6..000000000 Binary files a/Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001 - SEAD Evasion.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.lua b/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.lua deleted file mode 100644 index a6a808e92..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.lua +++ /dev/null @@ -1,145 +0,0 @@ - - - --- Tests Anapa: Spawn Basics --- ------------------------- --- Spawning groups using Spawn function. -Spawn_Plane = SPAWN:New( "Spawn Plane" ) -Group_Plane = Spawn_Plane:Spawn() - -Spawn_Helicopter = SPAWN:New( "Spawn Helicopter" ):InitRandomizeRoute( 1, 1, 1000 ) -Group_Helicopter = Spawn_Helicopter:Spawn() -Group_Helicopter2 = Spawn_Helicopter:Spawn() - -Spawn_Ship = SPAWN:New( "Spawn Ship" ):InitRandomizeRoute( 0, 0, 2000 ) -Group_Ship1 = Spawn_Ship:Spawn() -Group_Ship2 = Spawn_Ship:Spawn() - -Spawn_Vehicle = SPAWN:New( "Spawn Vehicle" ):InitRandomizeRoute( 1, 0, 50 ) -Group_Vehicle1 = Spawn_Vehicle:Spawn() -Group_Vehicle2 = Spawn_Vehicle:Spawn() -Group_Vehicle3 = Spawn_Vehicle:Spawn() -Group_Vehicle4 = Spawn_Vehicle:Spawn() -Group_Vehicle5 = Spawn_Vehicle:Spawn() -Group_Vehicle6 = Spawn_Vehicle:Spawn() - -Group_Vehicle1:TaskRouteToZone( ZONE:New( "Landing Zone" ), true, 40, "Cone" ) - --- Now land the spawned plane on to the Vinson, by copying the route of another object. -Route_Plane = GROUP:FindByName( "Spawn Helicopter Route Copy" ):CopyRoute( 1, 0 ) - -Group_Plane:Route( Route_Plane ) - ---Route_Helicopter[#Route_Helicopter].linkUnit = Group_Ship1:GetDCSUnit(1) ---Route_Helicopter[#Route_Helicopter].helipadId = Group_Ship1:GetDCSUnit(1) ---Route_Helicopter[#Route_Helicopter].x = Group_Ship1:GetUnit(1):GetPointVec2().x ---Route_Helicopter[#Route_Helicopter].y = Group_Ship1:GetUnit(1):GetPointVec2().y ---env.info( Route_Helicopter[#Route_Helicopter].type .. " on " .. Group_Ship1:GetUnit(1):GetID() ) ---Group_Helicopter:Route( Route_Helicopter ) - - --- Tests Batumi: Scheduled Spawning --- -------------------------------- --- Unlimited spawning of groups, scheduled every 30 seconds ... -Spawn_Plane_Scheduled = SPAWN:New( "Spawn Plane Scheduled" ):SpawnScheduled( 30, 0.4 ) -Spawn_Helicopter_Scheduled = SPAWN:New( "Spawn Helicopter Scheduled" ):SpawnScheduled( 30, 1 ) -Spawn_Ship_Scheduled = SPAWN:New( "Spawn Ship Scheduled" ):SpawnScheduled( 30, 0.5 ) -Spawn_Vehicle_Scheduled = SPAWN:New( "Spawn Vehicle Scheduled" ):SpawnScheduled( 30, 0.5 ) - --- Tests Tbilisi: Limited Spawning and repeat --- ------------------------------------------ --- Spawing one group, and respawning the same group when it lands ... -Spawn_Plane_Limited_Repeat = SPAWN:New( "Spawn Plane Limited Repeat" ):InitLimit( 1, 1 ):InitRepeat():Spawn() -Spawn_Plane_Limited_RepeatOnLanding = SPAWN:New( "Spawn Plane Limited RepeatOnLanding" ):InitLimit( 1, 1 ):InitRepeatOnLanding():Spawn() -Spawn_Plane_Limited_RepeatOnEngineShutDown = SPAWN:New( "Spawn Plane Limited RepeatOnEngineShutDown" ):InitLimit( 1, 1 ):InitRepeatOnEngineShutDown():Spawn() -Spawn_Helicopter_Limited_Repeat = SPAWN:New( "Spawn Helicopter Limited Repeat" ):InitLimit( 1, 1 ):InitRepeat():Spawn() -Spawn_Helicopter_Limited_RepeatOnLanding = SPAWN:New( "Spawn Helicopter Limited RepeatOnLanding" ):InitLimit( 1, 1 ):InitRepeatOnLanding():Spawn() -Spawn_Helicopter_Limited_RepeatOnEngineShutDown = SPAWN:New( "Spawn Helicopter Limited RepeatOnEngineShutDown" ):InitLimit( 1, 1 ):InitRepeatOnEngineShutDown():Spawn() - - --- Tests Soganlug --- -------------- --- Limited spawning of groups, scheduled every 30 seconds ... -Spawn_Plane_Limited_Scheduled = SPAWN:New( "Spawn Plane Limited Scheduled" ):InitLimit( 2, 10 ):SpawnScheduled( 30, 0 ) -Spawn_Helicopter_Limited_Scheduled = SPAWN:New( "Spawn Helicopter Limited Scheduled" ):InitLimit( 2, 10 ):SpawnScheduled( 30, 0 ) -Spawn_Ground_Limited_Scheduled = SPAWN:New( "Spawn Vehicle Limited Scheduled" ):InitLimit( 1, 20 ):SpawnScheduled( 90, 0 ) - --- Tests Sukhumi --- ------------- --- Limited spawning of groups, scheduled every seconds with route randomization. -Spawn_Plane_Limited_Scheduled_RandomizeRoute = SPAWN:New( "Spawn Plane Limited Scheduled RandomizeRoute" ):InitLimit( 5, 10 ):InitRandomizeRoute( 1, 1, 4000 ):SpawnScheduled( 2, 0 ) -Spawn_Helicopter_Limited_Scheduled_RandomizeRoute = SPAWN:New( "Spawn Helicopter Limited Scheduled RandomizeRoute" ):InitLimit( 5, 10 ):InitRandomizeRoute( 1, 1, 4000 ):SpawnScheduled( 2, 0 ) -Spawn_Vehicle_Limited_Scheduled_RandomizeRoute = SPAWN:New( "Spawn Vehicle Limited Scheduled RandomizeRoute" ):InitLimit( 10, 10 ):InitRandomizeRoute( 1, 1, 1000 ):SpawnScheduled( 1, 0 ) - - --- Tests Kutaisi --- ------------- --- Tests the CleanUp functionality. --- Limited spawning of groups, scheduled every 10 seconds, who are engaging into combat. Some helicopters may crash land on the ground. --- Observe when helicopters land but are not dead and are out of the danger zone, that they get removed after a while (+/- 180 seconds) and ReSpawn. -Spawn_Helicopter_Scheduled_CleanUp = SPAWN:New( "Spawn Helicopter Scheduled CleanUp" ):InitLimit( 3, 100 ):InitRandomizeRoute( 1, 1, 1000 ):CleanUp( 60 ):SpawnScheduled( 10, 0 ) -Spawn_Vehicle_Scheduled_CleanUp = SPAWN:New( "Spawn Vehicle Scheduled CleanUp" ):InitLimit( 3, 100 ):InitRandomizeRoute( 1, 1, 1000 ):SpawnScheduled( 10, 0 ) - --- Maykop --- ------ --- Creates arrays of groups ready to be spawned and dynamic spawning of groups from another group. - --- SpawnTestVisible creates an array of 200 groups, every 20 groups with 20 meters space in between, and will activate a group of the array every 10 seconds with a 0.2 time randomization. -SpawnTestVisible = SPAWN:New( "Spawn Vehicle Visible Scheduled" ):InitLimit( 200, 200 ):InitArray( 59, 20, 30, 30 ):SpawnScheduled( 10, 0.2 ) - --- Spawn_Templates_Visible contains different templates... -Spawn_Templates_Visible = { "Spawn Vehicle Visible Template A", - "Spawn Vehicle Visible Template B", - "Spawn Vehicle Visible Template C", - "Spawn Vehicle Visible Template D", - "Spawn Vehicle Visible Template E", - "Spawn Vehicle Visible Template F", - "Spawn Vehicle Visible Template G", - "Spawn Vehicle Visible Template H", - "Spawn Vehicle Visible Template I", - "Spawn Vehicle Visible Template J" - } - --- Spawn_Vehicle_Visible_RandomizeTemplate_Scheduled creates an array of 40 vehicle groups, spread out by 20 groups each, with an 8 meter distance, --- and chooses for each group from the templates specified in Spawn_Templates_Visible. - -Spawn_Vehicle_Visible_RandomizeTemplate_Scheduled = SPAWN:New( "Spawn Vehicle Visible RandomizeTemplate Scheduled" ) - :InitLimit( 80, 80 ) - :InitRandomizeTemplate( Spawn_Templates_Visible ) - :InitRandomizeRoute( 1, 1, 300 ) - :InitArray( 49, 20, 8, 8 ) - :SpawnScheduled( 1, 0.2 ) - --- Spawn_Infantry allows to spawn 10 Infantry groups. -Spawn_Infantry = SPAWN:New( "Spawn Infantry" ) - :InitLimit( 10, 10 ) - --- Spawn_Vehicle_Host reserves 10 vehicle groups, shown within an array arranged by 5 vehicles in a row with a distance of 8 meters, and schedules a vehicle each 10 seconds with a 20% variation. -Spawn_Vehicle_Host = SPAWN:New( "Spawn Vehicle Host" ) - :InitLimit( 10, 10 ) - :InitArray( 0, 5, 8, 8 ) - :SpawnScheduled( 10, 0.2 ) - --- Spawn_Vehicle_SpawnToZone allows to spawn 10 vehicle groups. --- The vehicles will drive to waypoint 1, where it will spawn infantry that will walk to a certain point. --- --------------------------------------------------------------------------------------------- --- local InfantryGroup = Spawn_Infantry:SpawnFromUnit( GROUP:Find(...):GetUnit(1), 100, 5 ) --- local InfantryRoute = InfantryGroup:CopyRoute( 1, 0, true, 1000 ) --- InfantryGroup:Route( InfantryRoute ) --- --------------------------------------------------------------------------------------------- -Spawn_Vehicle_SpawnToZone = SPAWN:New( "Spawn Vehicle SpawnToZone" ) - :InitLimit( 10, 10 ) - --- Spawn_Helicopter_SpawnToZone will fly to a location, hover, and spawn one vehicle on the ground, the helicopter will land --- and the vehicle will drive to a random location within the defined zone. --- For this, the following code is activated within the mission on waypoint 3: --- ------------------------------------------------------------------------------------------------------ --- local InfantryDropGroup = Spawn_Vehicle_SpawnToZone:SpawnFromUnit( GROUP:Find( ... ):GetUnit(1) ) --- local InfantryDropRoute = InfantryDropGroup:CopyRoute( 1, 0 ) --- InfantryDropGroup:TaskRouteToZone( ZONE:New( "Target Zone" ), true, 80 ) --- ------------------------------------------------------------------------------------------------------ -Spawn_Helicopter_SpawnToZone = SPAWN:New( "Spawn Helicopter SpawnToZone" ) - :InitLimit( 10, 10 ) - :SpawnScheduled( 60, 0.2 ) - - diff --git a/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz b/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz deleted file mode 100644 index 74ecbf75b..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 - Spawn Demo.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.lua b/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.lua deleted file mode 100644 index 4272c98f0..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.lua +++ /dev/null @@ -1,22 +0,0 @@ --- Name: SPA-011 - Ground Ops - Simple Spawning --- Author: FlightControl --- Date Created: 10 Jan 2017 --- --- # Situation: --- --- At Gudauta spawn a ground vehicle. --- --- # Test cases: --- --- 1. Observe that the ground vehicle is spawned. - - - --- Tests Gudauta --- ------------- --- Spawn a gound vehicle... -Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ) -Spawn_Group_1 = Spawn_Vehicle_1:Spawn() - - - diff --git a/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz deleted file mode 100644 index c884e1fdd..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple Spawning/SPA-011 - Ground Ops - Simple Spawning.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.lua b/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.lua deleted file mode 100644 index 36ce824e3..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.lua +++ /dev/null @@ -1,26 +0,0 @@ --- Name: SPA-012 - Ground Ops - Multiple Spawns --- Author: FlightControl --- Date Created: 10 Jan 2017 --- --- # Situation: --- --- At Gudauta spawn multiple ground vehicles. --- --- # Test cases: --- --- 1. Observe that the ground vehicles are spawned at the position declared within the mission editor. - - - --- Tests Gudauta --- ------------- --- Spawn a gound vehicle... -Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ) -Spawn_Group_1 = Spawn_Vehicle_1:Spawn() -Spawn_Group_2 = Spawn_Vehicle_1:Spawn() -Spawn_Group_3 = Spawn_Vehicle_1:Spawn() -Spawn_Group_4 = Spawn_Vehicle_1:Spawn() -Spawn_Group_5 = Spawn_Vehicle_1:Spawn() - - - diff --git a/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz b/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz deleted file mode 100644 index 1cf21d951..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.lua b/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.lua deleted file mode 100644 index 3549c9a4e..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.lua +++ /dev/null @@ -1,22 +0,0 @@ --- Name: SPA-013 - Ground Ops - Scheduled Spawns --- Author: FlightControl --- Date Created: 10 Jan 2017 --- --- # Situation: --- --- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. --- --- # Test cases: --- --- 1. Observe that the ground vehicles are spawned at the position declared within the mission editor. --- 2. The vehicles should spawn according the scheduler parameters. - - - --- Tests Gudauta --- ------------- -Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ):SpawnScheduled( 10, 0.5 ) - - - - diff --git a/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz b/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz deleted file mode 100644 index 9405e3210..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.lua b/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.lua deleted file mode 100644 index 2f8ccdc9f..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.lua +++ /dev/null @@ -1,21 +0,0 @@ --- Name: SPA-014 - Ground Ops - Scheduled Spawns Limited --- Author: FlightControl --- Date Created: 10 Jan 2017 --- --- # Situation: --- --- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. --- --- # Test cases: --- --- 1. Observe that the ground vehicles are spawned at the position declared within the mission editor. --- 2. The vehicles should spawn according the scheduler parameters. --- 3. There should not be more than 5 groups spawned. - - - --- Tests Gudauta --- ------------- -Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ):InitLimit( 5, 10 ):SpawnScheduled( 5, .5 ) - - diff --git a/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz b/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz deleted file mode 100644 index 3d7f19ed3..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.lua b/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.lua deleted file mode 100644 index c6c88f56a..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.lua +++ /dev/null @@ -1,27 +0,0 @@ --- Name: SPA-015 - Ground Ops - Randomize Route --- Author: FlightControl --- Date Created: 10 Jan 2017 --- --- # Situation: --- --- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. --- --- # Test cases: --- --- 1. Observe that the ground vehicles are spawned at the position declared within the mission editor. --- 2. The vehicles should spawn according the scheduler parameters. --- 3. There should not be more than 5 groups spawned. --- 4. Observe that the route that the vehicles follow is randomized starting from point 1 till point 3. - - - --- Tests Gudauta --- ------------- -Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ) - :InitLimit( 10, 10 ) - :InitRandomizeRoute( 1, 1, 200 ) -- Randomize route starting from point 1 till point 3, with a radius of 200 meters around each point. - :SpawnScheduled( 5, .5 ) - - - - diff --git a/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz b/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz deleted file mode 100644 index b92d9ae48..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize Route/SPA-015 - Ground Ops - Randomize Route.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.lua b/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.lua deleted file mode 100644 index 3bf1f8bfb..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.lua +++ /dev/null @@ -1,29 +0,0 @@ --- Name: SPA-016 - Ground Ops - Randomize Zones --- Author: FlightControl --- Date Created: 10 Jan 2017 --- --- # Situation: --- --- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. --- --- # Test cases: --- --- 1. Observe that the ground vehicles are spawned at the position declared within the mission editor. --- 2. The vehicles should spawn according the scheduler parameters. --- 3. There should not be more than 5 groups spawned. --- 4. Observe that the route that the vehicles follow is randomized starting from point 1 till point 3. --- 5. Observe that the position where the units are spawned, is randomized according the zones. - - - --- Tests Gudauta --- ------------- --- Create a zone table of the 2 zones. -ZoneTable = { ZONE:New( "Zone1" ), ZONE:New( "Zone2" ) } - -Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ) - :InitLimit( 10, 10 ) - :InitRandomizeRoute( 1, 1, 200 ) - :InitRandomizeZones( ZoneTable ) - :SpawnScheduled( 5, .5 ) - diff --git a/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz b/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz deleted file mode 100644 index 5c2065f26..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize Zones/SPA-016 - Ground Ops - Randomize Zones.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.lua b/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.lua deleted file mode 100644 index 54d89d920..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.lua +++ /dev/null @@ -1,31 +0,0 @@ --- Name: SPA-017 - Ground Ops - Set AI inactive while spawning --- Author: FlightControl --- Date Created: 24 Jan 2017 --- --- # Situation: --- --- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. --- But set the AI inactive when spawning. --- --- # Test cases: --- --- 1. Observe that the ground vehicles are spawned at the position declared within the mission editor. --- 2. The vehicles should spawn according the scheduler parameters. --- 3. There should not be more than 5 groups spawned. --- 4. Observe that the AI is inactivated, and thus, the vehicles aren't moving. --- 5. Observe that the position where the units are spawned, is randomized in the zones perimeters. - - - --- Tests Gudauta --- ------------- --- Create a zone table of the 2 zones. -ZoneTable = { ZONE:New( "Zone1" ), ZONE:New( "Zone2" ) } - -Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ) - :InitLimit( 10, 10 ) - :InitRandomizeRoute( 1, 1, 200 ) - :InitRandomizeZones( ZoneTable ) - :InitAIOnOff( false ) -- This will disable the AI. You can also use :InitAIOff(). Set AI On (for those groups with AI Off in the ME), with :InitAIOn(). - :SpawnScheduled( 5, .5 ) - diff --git a/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz deleted file mode 100644 index 0b36979f5..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while spawning.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.lua b/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.lua deleted file mode 100644 index 4258bd86c..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.lua +++ /dev/null @@ -1,28 +0,0 @@ ---- --- Name: SPA-018 - Ground Ops - Randomize Templates --- Author: FlightControl --- Date Created: 10 Jan 2017 --- --- # Situation: --- --- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. --- --- # Test cases: --- --- 1. Observe that the ground vehicles are spawned with randomized templates. - - --- Tests Gudauta --- ------------- --- Create a zone table of the 2 zones. -ZoneTable = { ZONE:New( "Zone1" ), ZONE:New( "Zone2" ) } - -TemplateTable = { "A", "B", "C" } - -Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ) - :InitLimit( 10, 10 ) - :InitRandomizeRoute( 1, 1, 200 ) - :InitRandomizeTemplate( TemplateTable ) - --:InitRandomizeZones( ZoneTable ) - :SpawnScheduled( 5, .5 ) - diff --git a/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz b/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz deleted file mode 100644 index d6b0ce568..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize Templates/SPA-018 - Ground Ops - Randomize Templates.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.lua b/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.lua deleted file mode 100644 index e636abb57..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.lua +++ /dev/null @@ -1,27 +0,0 @@ ---- --- Name: SPA-019 - Ground Ops - Randomize Templates with Waypoints --- Author: FlightControl --- Date Created: 24 Feb 2017 --- --- # Situation: --- --- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. --- --- # Test cases: --- --- 1. Observe that the ground vehicles are spawned with randomized templates. --- 2. Observe that the ground vehicles are spread around the spawning area and are not stacked upon each other. - - --- Tests Gudauta --- ------------- --- Create a zone table of the 2 zones. -ZoneTable = { ZONE:New( "Zone1" ), ZONE:New( "Zone2" ) } - -TemplateTable = { "A", "B", "C" } - -Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ) - :InitLimit( 10, 10 ) - :InitRandomizeTemplate( TemplateTable ) - :SpawnScheduled( 5, .5 ) - diff --git a/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz b/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz deleted file mode 100644 index 161740a5c..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates without Waypoints.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.lua b/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.lua deleted file mode 100644 index ba61228a3..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.lua +++ /dev/null @@ -1,29 +0,0 @@ ---- --- Name: SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints --- Author: FlightControl --- Date Created: 24 Feb 2017 --- --- # Situation: --- --- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. --- --- # Test cases: --- --- 1. Observe that the ground vehicles are spawned with randomized templates. --- 2. Observe that the ground vehicles are spread around the spawning area and are not stacked upon each other. --- 3. Observe that the ground vehicles are spread over the random zones, and that the initial templates formations are kept. - - --- Tests Gudauta --- ------------- --- Create a zone table of the 2 zones. -ZoneTable = { ZONE:New( "Zone1" ), ZONE:New( "Zone2" ) } - -TemplateTable = { "A", "B", "C" } - -Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ) - :InitLimit( 100, 10 ) - :InitRandomizeTemplate( TemplateTable ) - :InitRandomizeZones( ZoneTable ) - :SpawnScheduled( 5, .5 ) - diff --git a/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz b/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz deleted file mode 100644 index 7fd38af8e..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints/SPA-020 - Ground Ops - Randomize Templates in Random Zones without Waypoints.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.lua b/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.lua deleted file mode 100644 index f3f1ad8d1..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.lua +++ /dev/null @@ -1,27 +0,0 @@ ---- --- Name: SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names --- Author: FlightControl --- Date Created: 14 Mar 2017 --- --- # Situation: --- --- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. --- --- # Test cases: --- --- 1. Observe that the ground vehicles are spawned at the position declared within the mission editor. --- 2. The vehicles should spawn according the scheduler parameters. --- 3. There should not be more than 5 groups spawned. --- 4. Observe the unit names, they should have the name as defined within the ME. - - - --- Tests Gudauta --- ------------- -Spawn_Vehicle_1 = SPAWN - :New( "Spawn Vehicle 1" ) - :InitKeepUnitNames() - :InitLimit( 5, 10 ) - :SpawnScheduled( 5, .5 ) - - diff --git a/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.miz b/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.miz deleted file mode 100644 index cde7720ba..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names/SPA-021 - Ground Ops - Scheduled Spawns Limited Keep Unit Names.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval.lua b/Moose Test Missions/SPA - Spawning/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval.lua deleted file mode 100644 index 5096cddc5..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval.lua +++ /dev/null @@ -1,23 +0,0 @@ --- Name: SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval --- Author: FlightControl --- Date Created: 18 Mar 2017 --- --- # Situation: --- --- At Gudauta spawn multiple ground vehicles, in a scheduled fashion. --- The vehicle should respawn when killed. --- --- # Test cases: --- --- 1. Observe that the ground vehicles are spawned at the position declared within the mission editor. --- 2. The vehicles should spawn according the scheduler parameters. - - - --- Tests Gudauta --- ------------- -Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ):InitLimit( 1, 0 ):SpawnScheduled( 30, .5 ) - - - - diff --git a/Moose Test Missions/SPA - Spawning/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval.miz b/Moose Test Missions/SPA - Spawning/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval.miz deleted file mode 100644 index 9e6718215..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval/SPA-022 - Ground Ops - Scheduled Spawns Limited with long interval.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.lua b/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.lua deleted file mode 100644 index c7d62a568..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.lua +++ /dev/null @@ -1,8 +0,0 @@ --- Tests Kutaisi --- ------------- --- Tests the CleanUp functionality. --- Limited spawning of groups, scheduled every 10 seconds, who are engaging into combat. Some helicopters may crash land on the ground. --- Observe when helicopters land but are not dead and are out of the danger zone, that they get removed after a while (+/- 180 seconds) and ReSpawn. -Spawn_Helicopter_Scheduled_CleanUp = SPAWN:New( "Spawn Helicopter Scheduled CleanUp" ):InitLimit( 3, 100 ):InitRandomizeRoute( 1, 1, 1000 ):InitCleanUp( 60 ):SpawnScheduled( 10, 0 ) -Spawn_Vehicle_Scheduled_CleanUp = SPAWN:New( "Spawn Vehicle Scheduled CleanUp" ):InitLimit( 3, 100 ):InitRandomizeRoute( 1, 1, 1000 ):SpawnScheduled( 10, 0 ) - diff --git a/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz b/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz deleted file mode 100644 index ca7dfb593..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive Units/SPA-100 - CleanUp Inactive Units.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.lua b/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.lua deleted file mode 100644 index 1ff4c90c0..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.lua +++ /dev/null @@ -1,17 +0,0 @@ ---- --- Tests Gudauta --- ------------- --- Limited scheduled spawning of groups... -Spawn_Plane_Limited_Scheduled = SPAWN:New( "Spawn Plane Limited Scheduled" ):InitLimit( 4, 20 ):SpawnScheduled( 30, 0 ) -Spawn_Helicopter_Limited_Scheduled = SPAWN:New( "Spawn Helicopter Limited Scheduled" ):InitLimit( 4, 20 ):SpawnScheduled( 30, 0 ) -Spawn_Ground_Limited_Scheduled = SPAWN:New( "Spawn Vehicle Limited Scheduled" ):InitLimit( 4, 20 ):SpawnScheduled( 90, 0 ) - ---- --- Tests Sukhumi --- ------------- --- Limited scheduled spawning of groups with destruction... -Spawn_Plane_Limited_Scheduled_RandomizeRoute = SPAWN:New( "Spawn Plane Limited Scheduled Destroy" ):InitLimit( 4, 20 ):SpawnScheduled( 10, 0 ) -Spawn_Helicopter_Limited_Scheduled_RandomizeRoute = SPAWN:New( "Spawn Helicopter Limited Scheduled Destroy" ):InitLimit( 4, 20 ):SpawnScheduled( 10, 0 ) -Spawn_Vehicle_Limited_Scheduled_RandomizeRoute = SPAWN:New( "Spawn Vehicle Limited Scheduled Destroy" ):InitLimit( 4, 20 ):SpawnScheduled( 10, 0 ) - - diff --git a/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz deleted file mode 100644 index e8d4f5309..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 - Limit Spawning.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.lua b/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.lua deleted file mode 100644 index 7001950cc..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.lua +++ /dev/null @@ -1,41 +0,0 @@ ---- --- Name: SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit --- Author: FlightControl --- Date Created: 05 Feb 2017 --- --- # Situation: --- --- One airplane and one helicopter will be spawned. --- Only one airplane and one helicopter can be alive at the same time. --- Upon landing, the airplane and helicopter will respawn at Kutaisi. --- --- # Test cases: --- --- 1. Observe the spawning of the airplane and helicopter --- 2. There should not be more airplanes alive than there are set by InitLimit. --- 3. Upon landing, the planes should respawn. --- 4. The KA-50 should respawn itself directly when landed. --- 5. The A-10C should respawn itself when the air unit has parked at the ramp. - - -do - - -- Declare SPAWN objects - Spawn_KA_50 = SPAWN:New("KA-50"):InitLimit( 1, 10 ) - Spawn_A_10C = SPAWN:New("A-10C"):InitLimit( 1, 10 ) - - -- Choose repeat functionality - - -- Repeat on landing - Spawn_KA_50:InitRepeatOnLanding() - - -- Repeat on enging shutdown (when landed on the airport) - Spawn_A_10C:InitRepeatOnEngineShutDown() - - -- Now SPAWN the GROUPs - Spawn_KA_50:SpawnScheduled(30,0) - Spawn_A_10C:SpawnScheduled(30,0) - - -- Now run the mission and observe the behaviour. - -end diff --git a/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz b/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz deleted file mode 100644 index dde395580..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn with Repeat on Landing with Limit.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-120 - Repeat Spawning/SPA-120 - Repeat Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-120 - Repeat Spawning/SPA-120 - Repeat Spawning.miz deleted file mode 100644 index 34bd1c6fc..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-120 - Repeat Spawning/SPA-120 - Repeat Spawning.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.lua b/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.lua deleted file mode 100644 index 2d6d11ccd..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.lua +++ /dev/null @@ -1,47 +0,0 @@ ---- --- Name: SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit --- Author: FlightControl --- Date Created: 05 Feb 2017 --- --- # Situation: --- --- Multiple airplanes will be spawned at a scheduled interval. --- There is a limit on how many airplanes can be alive at the same time. --- Upon landing, the airplanes will respawn at Kutaisi. --- --- # Test cases: --- --- 1. Observe the spawning of the airplanes --- 2. There should not be more airplanes alive than there are set by InitLimit. --- 3. Upon landing, the planes should respawn. --- 4. The KA-50 and the C-101EB should respawn itself directly when landed. --- 5. the MI-8MTV2 and the A-10C should respawn itself when the air unit has parked at the ramp. - - -do - - -- Declare SPAWN objects - Spawn_KA_50 = SPAWN:New("KA-50"):InitLimit( 2, 10 ) - Spawn_MI_8MTV2 = SPAWN:New("MI-8MTV2"):InitLimit( 2, 10 ) - Spawn_C_101EB = SPAWN:New("C-101EB"):InitLimit( 2, 10 ) - Spawn_A_10C = SPAWN:New("A-10C"):InitLimit( 2, 10 ) - - -- Choose repeat functionality - - -- Repeat on landing - Spawn_KA_50:InitRepeatOnLanding() - Spawn_C_101EB:InitRepeatOnLanding() - - -- Repeat on enging shutdown (when landed on the airport) - Spawn_MI_8MTV2:InitRepeatOnEngineShutDown() - Spawn_A_10C:InitRepeatOnEngineShutDown() - - -- Now SPAWN the GROUPs - Spawn_KA_50:SpawnScheduled(180,0) - Spawn_C_101EB:SpawnScheduled(180,0) - Spawn_MI_8MTV2:SpawnScheduled(180,0) - Spawn_A_10C:SpawnScheduled(180,0) - - -- Now run the mission and observe the behaviour. - -end diff --git a/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz b/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz deleted file mode 100644 index 5987ff301..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled Spawns with Repeat on Landing with Limit.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-122 - Air Ops - OnLand test for Scheduled Spawns/SPA-122 - Air Ops - OnLand test for Scheduled Spawns.lua b/Moose Test Missions/SPA - Spawning/SPA-122 - Air Ops - OnLand test for Scheduled Spawns/SPA-122 - Air Ops - OnLand test for Scheduled Spawns.lua deleted file mode 100644 index b2e2028c1..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-122 - Air Ops - OnLand test for Scheduled Spawns/SPA-122 - Air Ops - OnLand test for Scheduled Spawns.lua +++ /dev/null @@ -1,47 +0,0 @@ ---- --- Name: SPA-122 - Air Ops - OnLand test for Scheduled Spawns --- Author: FlightControl --- Date Created: 21 Mar 2017 --- --- # Situation: --- --- An airplane is spawned at a scheduled interval. --- There is a limit on how many airplanes can be alive at the same time. --- Upon landing, the airplane will respawn in the air. --- --- # Test cases: --- --- 1. Observe the spawning of the airplanes --- 2. There should not be more airplanes alive than there are set by InitLimit. --- 3. Upon landing, the planes should respawn. --- 4. The KA-50 and the C-101EB should respawn itself directly when landed. --- 5. the MI-8MTV2 and the A-10C should respawn itself when the air unit has parked at the ramp. - - -do - - -- Declare SPAWN objects - Spawn_KA_50 = SPAWN:New("KA-50"):InitLimit( 1, 0 ) - Spawn_MI_8MTV2 = SPAWN:New("MI-8MTV2"):InitLimit( 1, 0 ) - Spawn_C_101EB = SPAWN:New("C-101EB"):InitLimit( 1, 0 ) - Spawn_A_10C = SPAWN:New("A-10C"):InitLimit( 1, 0 ) - - -- Choose repeat functionality - - -- Repeat on landing - Spawn_KA_50:InitRepeatOnLanding() - Spawn_C_101EB:InitRepeatOnLanding() - - -- Repeat on enging shutdown (when landed on the airport) - Spawn_MI_8MTV2:InitRepeatOnEngineShutDown() - Spawn_A_10C:InitRepeatOnEngineShutDown() - - -- Now SPAWN the GROUPs - Spawn_KA_50:SpawnScheduled(30,0) - Spawn_C_101EB:SpawnScheduled(30,0) - Spawn_MI_8MTV2:SpawnScheduled(30,0) - Spawn_A_10C:SpawnScheduled(30,0) - - -- Now run the mission and observe the behaviour. - -end diff --git a/Moose Test Missions/SPA - Spawning/SPA-122 - Air Ops - OnLand test for Scheduled Spawns/SPA-122 - Air Ops - OnLand test for Scheduled Spawns.miz b/Moose Test Missions/SPA - Spawning/SPA-122 - Air Ops - OnLand test for Scheduled Spawns/SPA-122 - Air Ops - OnLand test for Scheduled Spawns.miz deleted file mode 100644 index 01b18b695..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-122 - Air Ops - OnLand test for Scheduled Spawns/SPA-122 - Air Ops - OnLand test for Scheduled Spawns.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.lua b/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.lua deleted file mode 100644 index 6eb08c792..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.lua +++ /dev/null @@ -1,47 +0,0 @@ ---- --- Name: SPA-130 - Uncontrolled Spawning --- Author: FlightControl --- Date Created: 04 Feb 2017 --- --- # Situation: --- --- A plane will be spawned Uncontrolled and later one will be spawned Controlled. --- Only the Controlled plane will move, the other will remain idle at the parking spot. --- --- # Test cases: --- --- 1. Observe the spawning of the UnControlled Plane. --- 2. Observe the spawning of the Controlled Plane. - - --- Create the SPAWN object looking for the group (template) "Plane". -SpawnPlane = SPAWN:New( "Plane" ) - --- Set the spawn mode to UnControlled. -SpawnPlane:InitUnControlled( true ) - --- Spawn the UnControlled Group -UnControlledPlane = SpawnPlane:Spawn() - --- Set the spawn mode back to Controlled. -SpawnPlane:InitUnControlled( false ) - -ControlledPlane = SpawnPlane:Spawn() - --- Now, let's create a menu option at a player slot plane... --- We can only create the menu option if the player has joined the slot ... -PlayerPlane = CLIENT:FindByName( "PlayerPlane", "Select Menu item to activate UnControlled plane" ) - -PlayerPlane:Alive( - function( Client, SpawnPlane ) - - --- @param Functional.Spawn#SPAWN SpawnPlane - local function ActivatePlane( SpawnPlane ) - SpawnPlane:InitUnControlled( false ) - SpawnPlane:ReSpawn( 1 ) - end - - local Menu = MENU_CLIENT_COMMAND:New( Client, "Select to activate UnControlled plane", nil, ActivatePlane, SpawnPlane ) - end - , SpawnPlane -) \ No newline at end of file diff --git a/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz b/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz deleted file mode 100644 index c44a55be7..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled Spawning/SPA-130 - Uncontrolled Spawning.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.lua b/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.lua deleted file mode 100644 index 4302dd371..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.lua +++ /dev/null @@ -1,11 +0,0 @@ ---- --- Tests Gudauta --- -------------- --- Limited and scheduled spawning of groups, with RandomizeTemplate ... - -Templates = { "Template1", "Template2", "Template3", "Template4" } - -Spawn_Ground1 = SPAWN:New( "Spawn Vehicle1" ):InitLimit( 4, 20 ):InitRandomizeTemplate(Templates):SpawnScheduled( 15, 0 ) -Spawn_Ground2 = SPAWN:New( "Spawn Vehicle2" ):InitLimit( 4, 20 ):InitRandomizeTemplate(Templates):SpawnScheduled( 15, 0 ) - - diff --git a/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz b/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz deleted file mode 100644 index cc8ee53f9..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit Types/SPA-200 - Randomize Unit Types.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz b/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz deleted file mode 100644 index 7837a0589..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 - Randomize Zones.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-220 - Randomize Zones.lua b/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-220 - Randomize Zones.lua deleted file mode 100644 index c194c6cb3..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-220 - Randomize Zones.lua +++ /dev/null @@ -1,51 +0,0 @@ --- This test will create 3 different zones of different types. --- 100 groups of 1 unit will be spawned. --- The test is about testing the zone randomization, and the place where the units are created. - -local Iterations = 100 -local Iteration = 1 - --- The PolygonGroup route defines zone 1 -ZonePolygonGroup = GROUP:FindByName( "ZonePolygon" ) - --- The ZoneUnit defines zone 4. -ZoneUnit = UNIT:FindByName( "ZoneUnit" ) - --- The ZoneGroup defines zone 5 -ZoneGroup = GROUP:FindByName( "ZoneGroup" ) - --- This is the array that models the different zones types. --- The selection of the zones is done by taking into account the probability of the zone. --- The zone probabibility is 0 = 0%, 1 = 100% --- The default value of the probability is 1. --- Note that the SetZoneProbability is a method, that returns the self object of the zone, --- allowing to use the method within the zone array declaration! -local SpawnZones = { - ZONE_POLYGON:New( "Zone 1", ZonePolygonGroup ):SetZoneProbability( 0.8 ), - ZONE_RADIUS:New( "Zone 2", ZONE:New( "GroundZone2" ):GetVec2(), 5000 ):SetZoneProbability( 0.2 ), - ZONE:New( "GroundZone3" ):SetZoneProbability( 0.2 ), - ZONE_UNIT:New( "Zone 4", ZoneUnit, 5000 ):SetZoneProbability( 0.6 ), - ZONE_GROUP:New( "Zone 5", ZoneGroup, 5000 ):SetZoneProbability( 0.4 ), - } - -HeightLimit = 500 - -SpawnGrounds = SPAWN - :New("Ground") - :InitLimit( 100, 100 ) - -- This method will randomize the selection of the zones for each spawned Group during initialization, - -- taking into account the probability factors. - -- When you explore the code behind this method, you'll see that the GetZoneMaybe() method is used to select "maybe" the zone. - :InitRandomizeZones( SpawnZones ) - ---- Spawns these groups slowly. -SCHEDULER:New( nil, - - function( Interation, Iterations ) - do - -- Spawn Ground - SpawnGrounds:Spawn() - end - - end, {}, 0, 1, 0 -) diff --git a/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.lua b/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.lua deleted file mode 100644 index 90df88ec7..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.lua +++ /dev/null @@ -1,50 +0,0 @@ - -local Iterations = 10 -local Iteration = 1 - -GroundStatics = { "GroundStatic1", "GroundStatic2", "GroundStatic3" } -AirplaneStatics = { "AirplaneStatic1", "AirplaneStatic2", "AirplaneStatic3" } -HelicopterStatics = { "HelicopterStatic1", "HelicopterStatic2", "HelicopterStatic3" } -ShipStatics = { "ShipStatic1", "ShipStatic2", "ShipStatic3" } - -HeightLimit = 500 - -SpawnGrounds = SPAWN:New("Ground"):InitLimit( 20, 10 ):InitRandomizeUnits( true, 500, 100 ) -SpawnAirplanes = SPAWN:New("Airplane"):InitLimit( 20, 10 ):InitRandomizeUnits( true, 500, 100 ) -SpawnHelicopters = SPAWN:New("Helicopter"):InitLimit( 20, 10 ):InitRandomizeUnits( true, 500, 100 ) -SpawnShips = SPAWN:New("Ship"):InitLimit( 20, 10 ):InitRandomizeUnits( true, 500, 100 ) - ---- Spawns these groups slowly. -SCHEDULER:New( nil, - - function( Interation, Iterations ) - do - -- Spawn Ground - local StaticName = GroundStatics[ math.random( 1, 3 ) ] - local SpawnStatic = STATIC:FindByName( StaticName ) - SpawnGrounds:SpawnFromUnit( SpawnStatic ) - end - - do - -- Spawn Airplanes - local StaticName = AirplaneStatics[ math.random( 1, 3 ) ] - local SpawnStatic = STATIC:FindByName( StaticName ) - SpawnAirplanes:SpawnFromUnit( SpawnStatic ) - end - - do - -- Spawn Helicopters - local StaticName = HelicopterStatics[ math.random( 1, 3 ) ] - local SpawnStatic = STATIC:FindByName( StaticName ) - SpawnHelicopters:SpawnFromUnit( SpawnStatic ) - end - - do - -- Spawn Ships - local StaticName = ShipStatics[ math.random( 1, 3 ) ] - local SpawnStatic = STATIC:FindByName( StaticName ) - SpawnShips:SpawnFromUnit( SpawnStatic ) - end - - end, {}, 0, 15, 0.5 -) diff --git a/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz b/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz deleted file mode 100644 index 7469f6d64..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static position/SPA-310 - Spawn at Static position.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.lua b/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.lua deleted file mode 100644 index b9e1ae6f2..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.lua +++ /dev/null @@ -1,50 +0,0 @@ - -local Iterations = 10 -local Iteration = 1 - -GroundUnits = { "GroundUnit1", "GroundUnit2", "GroundUnit3" } -AirplaneUnits = { "AirplaneUnit1", "AirplaneUnit2", "AirplaneUnit3" } -HelicopterUnits = { "HelicopterUnit1", "HelicopterUnit2", "HelicopterUnit3" } -ShipUnits = { "ShipUnit1", "ShipUnit2", "ShipUnit3" } - -HeightLimit = 500 - -SpawnGrounds = SPAWN:New("Ground"):InitLimit( 20, 10 ):InitRandomizeUnits( true, 10, 3 ) -SpawnAirplanes = SPAWN:New("Airplane"):InitLimit( 20, 10 ) -SpawnHelicopters = SPAWN:New("Helicopter"):InitLimit( 20, 10 ) -SpawnShips = SPAWN:New("Ship"):InitLimit( 20, 10 ) - ---- Spawns these groups slowly. -SCHEDULER:New( nil, - - function( Interation, Iterations ) - do - -- Spawn Ground - local UnitName = GroundUnits[ math.random( 1, 3 ) ] - local SpawnUnit = UNIT:FindByName( UnitName ) - SpawnGrounds:SpawnFromUnit( SpawnUnit ) - end - - do - -- Spawn Airplanes - local UnitName = AirplaneUnits[ math.random( 1, 3 ) ] - local SpawnUnit = UNIT:FindByName( UnitName ) - SpawnAirplanes:SpawnFromUnit( SpawnUnit ) - end - - do - -- Spawn Helicopters - local UnitName = HelicopterUnits[ math.random( 1, 3 ) ] - local SpawnUnit = UNIT:FindByName( UnitName ) - SpawnHelicopters:SpawnFromUnit( SpawnUnit ) - end - - do - -- Spawn Ships - local UnitName = ShipUnits[ math.random( 1, 3 ) ] - local SpawnUnit = UNIT:FindByName( UnitName ) - SpawnShips:SpawnFromUnit( SpawnUnit ) - end - - end, {}, 0, 15, 0.5 -) diff --git a/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz b/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz deleted file mode 100644 index e56f869b4..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit position/SPA-320 - Spawn at Unit position.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.lua b/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.lua deleted file mode 100644 index 43fdbe219..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.lua +++ /dev/null @@ -1,59 +0,0 @@ - -local Iterations = 10 -local Iteration = 1 - -GroundZones = { "GroundZone1", "GroundZone2", "GroundZone3" } -GroundRandomizeZones = { "GroundRandomizeZone1", "GroundRandomizeZone2", "GroundRandomizeZone3" } -AirplaneZones = { "AirplaneZone1", "AirplaneZone2", "AirplaneZone3" } -HelicopterZones = { "HelicopterZone1", "HelicopterZone2", "HelicopterZone3" } -ShipZones = { "ShipZone1", "ShipZone2", "ShipZone3" } - -HeightLimit = 500 - -SpawnGrounds = SPAWN:New("Ground"):InitLimit( 20, 10 ) -SpawnRandomizeGrounds = SPAWN:New("GroundRandomize"):InitLimit( 20, 10 ):InitRandomizeUnits( true, 500, 100 ) -SpawnAirplanes = SPAWN:New("Airplane"):InitLimit( 20, 10 ) -SpawnHelicopters = SPAWN:New("Helicopter"):InitLimit( 20, 10 ) -SpawnShips = SPAWN:New("Ship"):InitLimit( 20, 10 ) - ---- Spawns these groups slowly. -SCHEDULER:New( nil, - - function( Interation, Iterations ) - do - -- Spawn Ground - local ZoneName = GroundZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnGrounds:SpawnFromVec2( SpawnVec3:GetVec2() ) - end - - do - -- Spawn Ground Randomize - local ZoneName = GroundRandomizeZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnRandomizeGrounds:SpawnFromVec2( SpawnVec3:GetVec2() ) - end - - do - -- Spawn Airplanes - local ZoneName = AirplaneZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnAirplanes:SpawnFromVec2( SpawnVec3:GetVec2() ) - end - - do - -- Spawn Helicopters - local ZoneName = HelicopterZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnHelicopters:SpawnFromVec2( SpawnVec3:GetVec2() ) - end - - do - -- Spawn Ships - local ZoneName = ShipZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnShips:SpawnFromVec2( SpawnVec3:GetVec2() ) - end - - end, {}, 0, 15, 0.5 -) diff --git a/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz b/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz deleted file mode 100644 index cb8538f7f..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2 position/SPA-330 - Spawn at Vec2 position.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.lua b/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.lua deleted file mode 100644 index df0d253ef..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.lua +++ /dev/null @@ -1,59 +0,0 @@ - -local Iterations = 10 -local Iteration = 1 - -GroundZones = { "GroundZone1", "GroundZone2", "GroundZone3" } -GroundRandomizeZones = { "GroundRandomizeZone1", "GroundRandomizeZone2", "GroundRandomizeZone3" } -AirplaneZones = { "AirplaneZone1", "AirplaneZone2", "AirplaneZone3" } -HelicopterZones = { "HelicopterZone1", "HelicopterZone2", "HelicopterZone3" } -ShipZones = { "ShipZone1", "ShipZone2", "ShipZone3" } - -HeightLimit = 500 - -SpawnGrounds = SPAWN:New("Ground"):InitLimit( 20, 10 ) -SpawnRandomizeGrounds = SPAWN:New("GroundRandomize"):InitLimit( 20, 10 ):InitRandomizeUnits( true, 500, 100 ) -SpawnAirplanes = SPAWN:New("Airplane"):InitLimit( 20, 10 ) -SpawnHelicopters = SPAWN:New("Helicopter"):InitLimit( 20, 10 ) -SpawnShips = SPAWN:New("Ship"):InitLimit( 20, 10 ) - ---- Spawns these groups slowly. -SCHEDULER:New( nil, - - function( Interation, Iterations ) - do - -- Spawn Ground - local ZoneName = GroundZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnGrounds:SpawnFromVec3( SpawnVec3:GetVec3() ) - end - - do - -- Spawn Ground Randomize - local ZoneName = GroundRandomizeZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnRandomizeGrounds:SpawnFromVec3( SpawnVec3:GetVec3() ) - end - - do - -- Spawn Airplanes - local ZoneName = AirplaneZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnAirplanes:SpawnFromVec3( SpawnVec3:GetVec3() ) - end - - do - -- Spawn Helicopters - local ZoneName = HelicopterZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnHelicopters:SpawnFromVec3( SpawnVec3:GetVec3() ) - end - - do - -- Spawn Ships - local ZoneName = ShipZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnShips:SpawnFromVec3( SpawnVec3:GetVec3() ) - end - - end, {}, 0, 15, 0.5 -) diff --git a/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz b/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz deleted file mode 100644 index be17d1af6..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3 position/SPA-340 - Spawn at Vec3 position.miz and /dev/null differ diff --git a/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.lua b/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.lua deleted file mode 100644 index e3ea49632..000000000 --- a/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.lua +++ /dev/null @@ -1,73 +0,0 @@ ---- --- Name: SPA-350 - Spawn at Vec3 position RandomzePosition --- Author: FlightControl --- Date Created: 14 Mar 2017 --- --- # Situation: --- --- Ground troops, Airplanes, Helicopters and Ships are spawning from Vec3 points. --- The API InitRandomizePosition is tested here, ensure that groups are replaced within a 900 to 1000 zone band at random positions. --- --- # Test cases: --- --- 1. Observe the random positioning of the groups. There should be no scattering of units. --- - -local Iterations = 10 -local Iteration = 1 - -GroundZones = { "GroundZone1", "GroundZone2", "GroundZone3" } -GroundRandomizeZones = { "GroundRandomizeZone1", "GroundRandomizeZone2", "GroundRandomizeZone3" } -AirplaneZones = { "AirplaneZone1", "AirplaneZone2", "AirplaneZone3" } -HelicopterZones = { "HelicopterZone1", "HelicopterZone2", "HelicopterZone3" } -ShipZones = { "ShipZone1", "ShipZone2", "ShipZone3" } - -HeightLimit = 500 - -SpawnGrounds = SPAWN:New("Ground"):InitLimit( 20, 10 ):InitRandomizePosition( true , 1000, 900 ) -SpawnRandomizeGrounds = SPAWN:New("GroundRandomize"):InitLimit( 20, 10 ):InitRandomizePosition( true , 1000, 900 ) -SpawnAirplanes = SPAWN:New("Airplane"):InitLimit( 20, 10 ):InitRandomizePosition( true , 1000, 900 ) -SpawnHelicopters = SPAWN:New("Helicopter"):InitLimit( 20, 10 ):InitRandomizePosition( true , 1000, 900 ) -SpawnShips = SPAWN:New("Ship"):InitLimit( 20, 10 ):InitRandomizePosition( true , 1000, 900 ) - ---- Spawns these groups slowly. -SCHEDULER:New( nil, - - function( Interation, Iterations ) - do - -- Spawn Ground - local ZoneName = GroundZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnGrounds:SpawnFromVec3( SpawnVec3:GetVec3() ) - end - - do - -- Spawn Ground Randomize - local ZoneName = GroundRandomizeZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnRandomizeGrounds:SpawnFromVec3( SpawnVec3:GetVec3() ) - end - - do - -- Spawn Airplanes - local ZoneName = AirplaneZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnAirplanes:SpawnFromVec3( SpawnVec3:GetVec3() ) - end - - do - -- Spawn Helicopters - local ZoneName = HelicopterZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnHelicopters:SpawnFromVec3( SpawnVec3:GetVec3() ) - end - - do - -- Spawn Ships - local ZoneName = ShipZones[ math.random( 1, 3 ) ] - local SpawnVec3 = POINT_VEC3:NewFromVec3( ZONE:New( ZoneName ):GetVec3() ) - SpawnShips:SpawnFromVec3( SpawnVec3:GetVec3() ) - end - - end, {}, 0, 15, 0.5 -) diff --git a/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.miz b/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.miz deleted file mode 100644 index 5d9b646dd..000000000 Binary files a/Moose Test Missions/SPA - Spawning/SPA-350 - Spawn at Vec3 position RandomzePosition/SPA-350 - Spawn at Vec3 position RandomzePosition.miz and /dev/null differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.lua deleted file mode 100644 index 4753f2c73..000000000 --- a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.lua +++ /dev/null @@ -1,46 +0,0 @@ ---- --- Name: TAD-010 - A2G Task Dispatching Destroy Test --- Author: FlightControl --- Date Created: 17 Mar 2017 --- --- # Situation: --- --- This tests if an accepted task successful completion does finish the processes correctly. --- --- # Test cases: --- --- -HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) - -CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -Scoring = SCORING:New( "Detect Demo" ) - -Mission = MISSION - :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) - :AddScoring( Scoring ) - -FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() - -FACAreas = DETECTION_UNITS:New( FACSet ) - - -AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() - -TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, AttackGroups, FACAreas ) - --- Now this is REALLY neat. I set the goal of the mission to be the destruction of Target #004. --- This is just an example, but many more examples can follow... - --- Every time a Task becomes Successful, it will trigger the Complete event in the Mission. --- The mission designer NEED TO OVERRIDE the OnBeforeComplete to prevent the mission from getting into completion --- too early! - -function Mission:OnBeforeComplete( From, Event, To ) - local Group004 = GROUP:FindByName( "Target #004" ) - if Group004 and Group004:IsAlive() == false then - Mission:GetCommandCenter():MessageToCoalition( "Mission Complete!" ) - return true - end - return false -end diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.miz deleted file mode 100644 index 7fbfb0299..000000000 Binary files a/Moose Test Missions/TAD - Task Dispatching/TAD-010 - A2G Task Dispatching Destroy Test/TAD-010 - A2G Task Dispatching Destroy Test.miz and /dev/null differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.lua deleted file mode 100644 index 23f6dc52d..000000000 --- a/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.lua +++ /dev/null @@ -1,35 +0,0 @@ - --- --- Name: TAD-100 - A2G Task Dispatching DETECTION_AREAS --- Author: FlightControl --- Date Created: 06 Mar 2017 --- --- # Situation: --- --- This mission demonstrates the dynamic task dispatching for Air to Ground operations. --- FACA's and FAC's are patrolling around the battle zone, while detecting targets. --- The detection method used is the DETECTION_AREAS method, which groups detected targets into zones. --- --- # Test cases: --- --- 1. Observe the FAC(A)'s detecting targets and grouping them. --- For test, each zone will have a circle of tyres, that are visible on the map too. --- 2. Check that the HQ provides menus to engage on a task set by the FACs. --- -HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) - -CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -Scoring = SCORING:New( "Detect Demo" ) - -Mission = MISSION - :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) - :AddScoring( Scoring ) - -FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() - -FACAreas = DETECTION_AREAS:New( FACSet, 500 ) -FACAreas:BoundDetectedZones() - -AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() -TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, HQ, AttackGroups, FACAreas ) - diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.miz deleted file mode 100644 index 079a85e18..000000000 Binary files a/Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching DETECTION_AREAS.miz and /dev/null differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.lua deleted file mode 100644 index a7479b92f..000000000 --- a/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.lua +++ /dev/null @@ -1,35 +0,0 @@ - --- --- Name: TAD-105 - A2G Task Dispatching DETECTION_AREAS --- Author: FlightControl --- Date Created: 12 Mar 2017 --- --- # Situation: --- --- This mission demonstrates the dynamic task dispatching for Air to Ground operations. --- FACA's and FAC's are patrolling around the battle zone, while detecting targets. --- The detection method used is the DETECTION_AREAS method, which groups detected targets into zones. --- --- # Test cases: --- --- 1. Observe the FAC(A)'s detecting targets and grouping them. --- For test, each zone will have a circle of tyres, that are visible on the map too. --- 2. Check that the HQ provides menus to engage on a task set by the FACs. --- -HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) - -CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -Scoring = SCORING:New( "Detect Demo" ) - -Mission = MISSION - :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) - :AddScoring( Scoring ) - -FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() - -FACAreas = DETECTION_AREAS:New( FACSet, 500 ) -FACAreas:BoundDetectedZones() - -AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() -TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, HQ, AttackGroups, FACAreas ) - diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.miz deleted file mode 100644 index c78fc040c..000000000 Binary files a/Moose Test Missions/TAD - Task Dispatching/TAD-105 - A2G Task Dispatching DETECTION_AREAS/TAD-105 - A2G Task Dispatching DETECTION_AREAS.miz and /dev/null differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.lua deleted file mode 100644 index 37f7367e6..000000000 --- a/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.lua +++ /dev/null @@ -1,35 +0,0 @@ - --- --- Name: TAD-100 - A2G Task Dispatching DETECTION_AREAS --- Author: FlightControl --- Date Created: 06 Mar 2017 --- --- # Situation: --- --- This mission demonstrates the dynamic task dispatching for Air to Ground operations. --- FACA's and FAC's are patrolling around the battle zone, while detecting targets. --- The detection method used is the DETECTION_AREAS method, which groups detected targets into zones. --- --- # Test cases: --- --- 1. Observe the FAC(A)'s detecting targets and grouping them. --- For test, each zone will have a circle of tyres, that are visible on the map too. --- 2. Check that the HQ provides menus to engage on a task set by the FACs. --- -HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) - -CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -Scoring = SCORING:New( "Detect Demo" ) - -Mission = MISSION - :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) - :AddScoring( Scoring ) - -FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() - -FACAreas = DETECTION_TYPES:New( FACSet ) - - -AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() -TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, HQ, AttackGroups, FACAreas ) - diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.miz deleted file mode 100644 index f76492202..000000000 Binary files a/Moose Test Missions/TAD - Task Dispatching/TAD-110 - A2G Task Dispatching DETECTION_TYPES/TAD-110 - A2G Task Dispatching DETECTION_TYPES.miz and /dev/null differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.lua deleted file mode 100644 index 67a481dda..000000000 --- a/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.lua +++ /dev/null @@ -1,50 +0,0 @@ ---- --- Name: TAD-120 - A2G Task Dispatching DETECTION_UNITS --- Author: FlightControl --- Date Created: 13 Mar 2017 --- --- # Situation: --- --- This mission demonstrates the dynamic task dispatching for Air to Ground operations. --- FACA's and FAC's are patrolling around the battle field, while detecting targets. --- The detection method used is the DETECTION_UNITS method, which groups detected targets per detected unit. --- --- # Test cases: --- --- 1. Observe the FAC(A)'s detecting targets and grouping them. --- 2. Check that the HQ provides menus to engage on a task set by the FACs. --- -HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) - -CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -Scoring = SCORING:New( "Detect Demo" ) - -Mission = MISSION - :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) - :AddScoring( Scoring ) - -FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() - -FACAreas = DETECTION_UNITS:New( FACSet ) - - -AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() - -TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, AttackGroups, FACAreas ) - --- Now this is REALLY neat. I set the goal of the mission to be the destruction of Target #004. --- This is just an example, but many more examples can follow... - --- Every time a Task becomes Successful, it will trigger the Complete event in the Mission. --- The mission designer NEED TO OVERRIDE the OnBeforeComplete to prevent the mission from getting into completion --- too early! - -function Mission:OnBeforeComplete( From, Event, To ) - local Group004 = GROUP:FindByName( "Target #004" ) - if Group004:IsAlive() == false then - Mission:GetCommandCenter():MessageToCoalition( "Mission Complete!" ) - return true - end - return false -end \ No newline at end of file diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.miz deleted file mode 100644 index f5cdc95a4..000000000 Binary files a/Moose Test Missions/TAD - Task Dispatching/TAD-120 - A2G Task Dispatching DETECTION_UNITS/TAD-120 - A2G Task Dispatching DETECTION_UNITS.miz and /dev/null differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-200 - A2G Task Dispatching with SCORING/TAD-200 - A2G Task Dispatching with SCORING.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-200 - A2G Task Dispatching with SCORING/TAD-200 - A2G Task Dispatching with SCORING.lua deleted file mode 100644 index 4df46302e..000000000 --- a/Moose Test Missions/TAD - Task Dispatching/TAD-200 - A2G Task Dispatching with SCORING/TAD-200 - A2G Task Dispatching with SCORING.lua +++ /dev/null @@ -1,64 +0,0 @@ ---- --- Name: TAD-200 - A2G Task Dispatching with SCORING --- Author: FlightControl --- Date Created: 19 Mar 2017 --- --- # Situation: --- --- This mission demonstrates the scoring of dynamic task dispatching for Air to Ground operations. --- --- # Test cases: --- --- 1. Observe the FAC(A)'s detecting targets and grouping them. --- 2. Check that the HQ provides menus to engage on a task set by the FACs. --- 3. Engage on a task and destroy a target. Check if scoring is given for that target. --- 4. Engage all targets in the task, and check if mission success is achieved and that a scoring is given. --- 5. Restart the mission, and crash into the ground, check if you can get penalties. --- -local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) - -local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -local Scoring = SCORING:New( "Detect Demo" ) - -local Mission = MISSION - :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) - :AddScoring( Scoring ) - -local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() - -local FACAreas = DETECTION_UNITS:New( FACSet ) - - -local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() - -TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, AttackGroups, FACAreas ) - ---- @param #TaskDispatcher self --- @param From --- @param Event --- @param To --- @param Tasking.Task_A2G#TASK_A2G Task --- @param Wrapper.Unit#UNIT TaskUnit --- @param #string PlayerName -function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) - Task:SetScoreOnDestroy( "Player " .. PlayerName .. " destroyed a target", 20, TaskUnit ) - Task:SetScoreOnSuccess( "The task has been successfully completed!", 200, TaskUnit ) - Task:SetPenaltyOnFailed( "The task has failed completion!", -100, TaskUnit ) -end - --- Now this is REALLY neat. I set the goal of the mission to be the destruction of Target #004. --- This is just an example, but many more examples can follow... - --- Every time a Task becomes Successful, it will trigger the Complete event in the Mission. --- The mission designer NEED TO OVERRIDE the OnBeforeComplete to prevent the mission from getting into completion --- too early! - -function Mission:OnBeforeComplete( From, Event, To ) - local Group004 = GROUP:FindByName( "Target #004" ) - if Group004:IsAlive() == false then - Mission:GetCommandCenter():MessageToCoalition( "Mission Complete!" ) - return true - end - return false -end \ No newline at end of file diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-200 - A2G Task Dispatching with SCORING/TAD-200 - A2G Task Dispatching with SCORING.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-200 - A2G Task Dispatching with SCORING/TAD-200 - A2G Task Dispatching with SCORING.miz deleted file mode 100644 index 79616d9c4..000000000 Binary files a/Moose Test Missions/TAD - Task Dispatching/TAD-200 - A2G Task Dispatching with SCORING/TAD-200 - A2G Task Dispatching with SCORING.miz and /dev/null differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-210 - A2G Task Dispatching per AREAS and SCORING/TAD-210 - A2G Task Dispatching per AREAS and SCORING.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-210 - A2G Task Dispatching per AREAS and SCORING/TAD-210 - A2G Task Dispatching per AREAS and SCORING.lua deleted file mode 100644 index 2a5427bcc..000000000 --- a/Moose Test Missions/TAD - Task Dispatching/TAD-210 - A2G Task Dispatching per AREAS and SCORING/TAD-210 - A2G Task Dispatching per AREAS and SCORING.lua +++ /dev/null @@ -1,65 +0,0 @@ ---- --- Name: TAD-210 - A2G Task Dispatching for AREAS and SCORING --- Author: FlightControl --- Date Created: 19 Mar 2017 --- --- # Situation: --- --- This mission demonstrates the scoring of dynamic task dispatching for Air to Ground operations. --- --- # Test cases: --- --- 1. Observe the FAC(A)'s detecting targets and grouping them. --- 2. Check that the HQ provides menus to engage on a task set by the FACs. --- 3. Engage on a task and destroy a target. Check if scoring is given for that target. --- 4. Engage all targets in the task, and check if mission success is achieved and that a scoring is given. --- 5. Restart the mission, and crash into the ground, check if you can get penalties. --- -local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) - -local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -local Scoring = SCORING:New( "Detect Demo" ) - -local Mission = MISSION - :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) - :AddScoring( Scoring ) - -local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() - -local FACAreas = DETECTION_AREAS:New( FACSet, 400 ) -FACAreas:BoundDetectedZones() - - -local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() - -TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, AttackGroups, FACAreas ) - ---- @param #TaskDispatcher self --- @param From --- @param Event --- @param To --- @param Tasking.Task_A2G#TASK_A2G Task --- @param Wrapper.Unit#UNIT TaskUnit --- @param #string PlayerName -function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) - Task:SetScoreOnDestroy( "Player " .. PlayerName .. " destroyed a target", 20, TaskUnit ) - Task:SetScoreOnSuccess( "The task has been successfully completed!", 200, TaskUnit ) - Task:SetPenaltyOnFailed( "The task has failed completion!", -100, TaskUnit ) -end - --- Now this is REALLY neat. I set the goal of the mission to be the destruction of Target #004. --- This is just an example, but many more examples can follow... - --- Every time a Task becomes Successful, it will trigger the Complete event in the Mission. --- The mission designer NEED TO OVERRIDE the OnBeforeComplete to prevent the mission from getting into completion --- too early! - -function Mission:OnBeforeComplete( From, Event, To ) - local Group004 = GROUP:FindByName( "Target #004" ) - if Group004:IsAlive() == false then - Mission:GetCommandCenter():MessageToCoalition( "Mission Complete!" ) - return true - end - return false -end \ No newline at end of file diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-210 - A2G Task Dispatching per AREAS and SCORING/TAD-210 - A2G Task Dispatching per AREAS and SCORING.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-210 - A2G Task Dispatching per AREAS and SCORING/TAD-210 - A2G Task Dispatching per AREAS and SCORING.miz deleted file mode 100644 index ab99064fc..000000000 Binary files a/Moose Test Missions/TAD - Task Dispatching/TAD-210 - A2G Task Dispatching per AREAS and SCORING/TAD-210 - A2G Task Dispatching per AREAS and SCORING.miz and /dev/null differ diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-220 - A2G Task Dispatching per TYPE and SCORING/TAD-220 - A2G Task Dispatching per TYPE and SCORING.lua b/Moose Test Missions/TAD - Task Dispatching/TAD-220 - A2G Task Dispatching per TYPE and SCORING/TAD-220 - A2G Task Dispatching per TYPE and SCORING.lua deleted file mode 100644 index 7ebd6c1ac..000000000 --- a/Moose Test Missions/TAD - Task Dispatching/TAD-220 - A2G Task Dispatching per TYPE and SCORING/TAD-220 - A2G Task Dispatching per TYPE and SCORING.lua +++ /dev/null @@ -1,64 +0,0 @@ ---- --- Name: TAD-220 - A2G Task Dispatching per TYPE and SCORING --- Author: FlightControl --- Date Created: 20 Mar 2017 --- --- # Situation: --- --- This mission demonstrates the scoring of dynamic task dispatching for Air to Ground operations. --- --- # Test cases: --- --- 1. Observe the FAC(A)'s detecting targets and grouping them. --- 2. Check that the HQ provides menus to engage on a task set by the FACs. --- 3. Engage on a task and destroy a target. Check if scoring is given for that target. --- 4. Engage all targets in the task, and check if mission success is achieved and that a scoring is given. --- 5. Restart the mission, and crash into the ground, check if you can get penalties. --- -local HQ = GROUP:FindByName( "HQ", "Bravo HQ" ) - -local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -local Scoring = SCORING:New( "Detect Demo" ) - -local Mission = MISSION - :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) - :AddScoring( Scoring ) - -local FACSet = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterCoalitions("red"):FilterStart() - -local DetectionTypes = DETECTION_TYPES:New( FACSet ) - -local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Attack" ):FilterStart() - -TaskDispatcher = TASK_A2G_DISPATCHER:New( Mission, AttackGroups, DetectionTypes ) - ---- @param #TaskDispatcher self --- @param From --- @param Event --- @param To --- @param Tasking.Task_A2G#TASK_A2G Task --- @param Wrapper.Unit#UNIT TaskUnit --- @param #string PlayerName -function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) - self:E( "I am in assigned ... " ) - Task:SetScoreOnDestroy( "Player " .. PlayerName .. " destroyed a target", 50, TaskUnit ) - Task:SetScoreOnSuccess( "The task has been successfully completed!", 200, TaskUnit ) - Task:SetPenaltyOnFailed( "The task has failed completion!", 100, TaskUnit ) -end - --- Now this is REALLY neat. I set the goal of the mission to be the destruction of Target #004. --- This is just an example, but many more examples can follow... - --- Every time a Task becomes Successful, it will trigger the Complete event in the Mission. --- The mission designer NEED TO OVERRIDE the OnBeforeComplete to prevent the mission from getting into completion --- too early! - -function Mission:OnBeforeComplete( From, Event, To ) - local Group004 = GROUP:FindByName( "Target #004" ) - if Group004:IsAlive() == false then - Mission:GetCommandCenter():MessageToCoalition( "Mission Complete!" ) - return true - end - return false -end \ No newline at end of file diff --git a/Moose Test Missions/TAD - Task Dispatching/TAD-220 - A2G Task Dispatching per TYPE and SCORING/TAD-220 - A2G Task Dispatching per TYPE and SCORING.miz b/Moose Test Missions/TAD - Task Dispatching/TAD-220 - A2G Task Dispatching per TYPE and SCORING/TAD-220 - A2G Task Dispatching per TYPE and SCORING.miz deleted file mode 100644 index dd66cc842..000000000 Binary files a/Moose Test Missions/TAD - Task Dispatching/TAD-220 - A2G Task Dispatching per TYPE and SCORING/TAD-220 - A2G Task Dispatching per TYPE and SCORING.miz and /dev/null differ diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.lua b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.lua deleted file mode 100644 index d51287027..000000000 --- a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.lua +++ /dev/null @@ -1,175 +0,0 @@ --- Name: TSK-010 - Task Modelling - SEAD --- Author: FlightControl --- Date Created: 15 Dec 2016 --- --- # Situation --- --- This test mission is a test bed for the TASKING framework. --- It creates an head quarters (HQ), which contains one mission with one task to be accomplished. --- When the pilot joins the plane, it will need to accept the task using the HQ menu. --- Once the task is accepted, the group of the pilot will be assigned to the task. --- The pilot will need to fly to the attack zone and elimitate all targets reported. --- A smoking system is available that the pilot can use the acquire targets. --- Once all targets are elimitated, the task is finished, and the mission is set to complete. --- If the pilot crashes during flying, the task will fail, and the mission is set to failed. --- --- Uses the Tracing functions from BASE within the DCS.log file. Check the DCS.log file for the results. --- Create a new SCHEDULER object. --- Check the DCS.log. --- --- # Test cases: --- --- There should only be one Task listed under Others Menu -> HQ -> SEAD Targets -> SEAD. This is the TaskSEAD2, that is copied from TaskSEAD. --- TaskSEAD is removed from the mission once TaskSEAD2 is created. --- --- ## Run this mission in DCS Single Player: --- --- * Once started, a slot. --- * When in the plane, join the SEAD task through the Others Menu -> HQ -> SEAD Targets -> SEAD -> SEAD Radars Vector 2. --- * When flying, watch the messages appear. It should say that you've been assigned to the task, and that you need to route your plane to a coordinate. --- * Exit your plane by pressing ESC, and go back to the spectators. When in single player mode, just click on Back, and then click Spectators. --- * Immediately rejoin a Slot, select an other plane. --- * When in the plane, you should now not be able to join the Task. No menu options are given. That is because the Task is "Aborted". --- * However, the aborted task is replanned within 30 seconds. As such, go back to spectators, and after 30 seconds, rejoin a slot in a plane. --- * When in the plane, you should not be able to join the Task through the Others Menu -> HQ -> SEAD Targets -> SEAD -> SEAD Radars Vector 2. --- * Once accepted, watch the messages appear. Route to the attach zone, following the coordinates. --- * Once at the attack zone, you'll see a message how many targets are left to be destroyed. Attack the radar emitting SAM with a kh-25. --- * When you HIT the SAM, you'll see a scoring message appear. One point is granted. --- * Maybe you've fired two missiles, so, you'll see another HIT maybe on the SAM, again granting a point. --- * When the SAM is DEAD (it may take a while), you'll see a scoring message that 10 points have been granted. --- * You'll see a scoring message appear that grants 25 points because you've hit a target of the Task. (This was programmed below). --- * You'll see a scoring message appear that grants 250 points because all Task targets have been elimitated. (This was also programmed below). --- * You'll see a message appear that you have Task success. The Task will be flagged as 'Success', and cannot be joined anymore. --- * You'll see a message appear that the Mission "SEAD Targets" has been "Completed". --- --- ## Run this mission in DCS Multiple Player, with one player: --- --- * Retry the above scenario, but now running this scenario on a multi player server, while connecting with one player to the mission. Watch the consistency of the messages. --- --- ## Run this mission in DCS Multiple Player, with two to three players simultaneously: --- --- * Retry the above scenario running this scenario on a multi player server, while connecting with two or three players to the mission. Watch the consistency of the messages. --- * When the first player has accepted the Task, the 2nd and 3rd player joining the Task, will be automatically assigned to the Task. --- --- ## Others things to watch out for: --- --- * When flying to the attack zone, a message should appear every 30 seconds with the coordinates. --- * When in the attack zone, a message should appear every 30 seconds how many targes are left within the task. --- * When a player aborts the task, a message is displayed of the player aborting, but only to the group assigned to execute the task. --- * When a player joins the task, a message is displayed of the player joining, but only to the group assigned to execute the task. --- * When a player crashes into the ground, a message is displayed of that event. --- * In multi player, when the Task was assigned to the group, but all players in that group aborted the Task, the Task should become Aborted. It will be replanned in 30 seconds. - --- Create the HQ object. -local HQ = COMMANDCENTER:New( GROUP:FindByName( "HQ" ) ) - --- MOOSE contains a MISSION class. Use the MISSION class to setup missions, containing tasks to be executed. --- Create the Mission object, and attach the Mission to the HQ object. --- The Mission accepts 4 parameters: --- 1. The HQ object --- 2. The name of the Mission --- 3. The type of Mission, this can be any word like "Strategic", "Tactical", "Urgent", "Optional", "Secondary"... --- 4. The briefing of the Mission. This briefing is shown when the pilot joins a Task within the Mission. -local Mission = MISSION:New( HQ, 'SEAD Targets', "Strategic", "SEAD the enemy" ) - - --- MOOSE contains a SCORING class. Use the SCORING class to account the scores of achievements made by the pilots. --- The scoring system is a standalone object, so here the Scoring object is created. -local Scoring = SCORING:New( "SEAD" ) - --- The Scoring object is attached to the Mission object. --- By doing this, now the Mission can set at defined states in tasks ( and in processes within the tasks ) scoring values, and a text. See later. -Mission:AddScoring( Scoring ) - --- Define the set of group of planes that can be assigned to the Mission object. -local SEADSet = SET_GROUP:New():FilterPrefixes( "Test SEAD"):FilterStart() -SEADSet:Flush() - --- Define the set of units that are the targets. --- Note that I use FilterOnce, which means that the set will be defined only once, --- and will not be continuously updated! -local TargetSet = SET_UNIT:New():FilterPrefixes( "US Hawk SR" ):FilterStart() - --- Define the RendezVous Zone where the pilot needs to RendezVous with other players before engaging. -local RendezVousZone = ZONE:New( "RendezVous Zone" ) - --- Define the zone to where the pilot needs to navigate. -local TargetZone = ZONE:New( "Target Zone" ) - --- MOOSE contains a TASK class. Use the TASK class to define a new Task object and attach it to a Mission object. --- Here we define a new TaskSEAD object, and attach it to the Mission object. --- ( The TASK class is the base class for ALL derived Task templates. --- Task templates are TASK classes that quickly setup a Task scenario with given parameters. ) --- --- The TASK class is thus the primary task, and a task scenario will need to be provided to the TaskSEAD of the states and events that form the task. --- TASK gets a couple of parameters: --- 1. The Mission for which the Task needs to be achieved. --- 2. The set of groups of planes that pilots can join. --- 3. The name of the Task... This can be any name, and will be provided when the Pilot joins the task. --- 4. A set of Targets for the Task. - --- We are going to create a couple of variations of TASK_SEAD... - --- A TASK_SEAD that will route the player towards a Rendez-Vous point, and once arrived, route the player to the Target Zone. -local SEADTaskRendezVousPoint = TASK_SEAD:New( Mission, SEADSet, "Route to Rendez-Vous Point, then route to Target Zone", TargetSet ) -- Tasking.Task_SEAD#TASK_SEAD -SEADTaskRendezVousPoint:SetRendezVousPointVec2( RendezVousZone:GetPointVec2(), 6000 ) -- Done to test the RendezVousPointVec2 mechanism. -SEADTaskRendezVousPoint:SetTargetZone( TargetZone ) - --- A TASK_SEAD that will route the player towards a Rendez-Vous zone, --- and once arrived, route the player to the Target Zone. -local SEADTaskRendezVousZone = TASK_SEAD:New( Mission, SEADSet, "Route to Rendez-Vous Zone, then route to Target Zone", TargetSet ) -- Tasking.Task_SEAD#TASK_SEAD -SEADTaskRendezVousZone:SetRendezVousZone( RendezVousZone ) -- Done to test the Route to Zone mechanism. -SEADTaskRendezVousZone:SetTargetZone( TargetZone ) - --- A TASK_SEAD that has no Rendez_vous, --- and routes the player straight to the Target Zone. -local SEADTaskToTargetZone = TASK_SEAD:New( Mission, SEADSet, "Route to Target Zone", TargetSet ) -- Tasking.Task_SEAD#TASK_SEAD -SEADTaskToTargetZone:SetTargetZone( TargetZone ) - --- A TASK_SEAD that has no Rendez_vous, --- and routes the player to each Target in the Set. -local SEADTaskToTargetNoZone = TASK_SEAD:New( Mission, SEADSet, "Route to Target per Target", TargetSet ) -- Tasking.Task_SEAD#TASK_SEAD - --- This is now an important part of the Task process definition. --- Each TASK contains a "Process Template". --- You need to define this process Template by added Actions and Processes, otherwise, the task won't do anything. --- This call retrieves the Finite State Machine template of the Task. --- This template WILL NEVER DIRECTLY BE EXECUTED. --- But, when a Pilot joins a UNIT as defined within the SEADSet, the TaskSEAD will COPY the FsmSEAD to a NEW INTERNAL OBJECT and assign the COPIED FsmSEAD to the UNIT of the player. --- There can be many copied FsmSEAD objects internally active within TaskSEAD, for each pilot that joined the Task one is instantiated. --- The reason why this is done, is that each unit as a role within the Task, and can have different status. --- Therefore, the FsmSEAD is a TEMPLATE PROCESS of the TASK, and must be designed as a UNIT with a player is executing that PROCESS. - -local SEADProcess = SEADTaskToTargetNoZone:GetUnitProcess() -- #SEADProcess - -SEADProcess:AddScoreProcess( "Engaging", "Account", "Account", "destroyed a radar", 25 ) -SEADProcess:AddScoreProcess( "Engaging", "Account", "Failed", "failed to destroy a radar", -10 ) - --- Now we will set the SCORING. Scoring is set using the TaskSEAD object. --- Scores can be set on the status of the Task, and on Process level. -SEADProcess:AddScore( "Success", "Destroyed all target radars", 250 ) -SEADProcess:AddScore( "Failed", "Failed to destroy all target radars", -100 ) - --- Here we handle the PlayerAborted event, which is fired when a Player leaves the unit while being assigned to the Task. --- Within the event handler, which is passed the PlayerUnit and PlayerName parameter, --- we check if the SEADTask has still AlivePlayers assigned to the Task. --- If not, the Task will Abort. --- And it will be Replanned within 30 seconds. -function SEADTaskToTargetNoZone:OnEnterPlayerCrashed( PlayerUnit, PlayerName ) - if not SEADTaskToTargetNoZone:HasAliveUnits() then - SEADTaskToTargetNoZone:__Abort() - end -end - - ---local TaskSEAD2 = TASK:New( Mission, SEADSet, "SEAD Radars Vector 2", "SEAD" ) -- Tasking.Task#TASK ---TaskSEAD2:SetUnitProcess( SEADTask:GetUnitProcess():Copy() ) ---Mission:AddTask( TaskSEAD2 ) --- ---Mission:RemoveTask( SEADTask ) --- ---SEADTask = nil ---SEADProcess = nil - - -collectgarbage() diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz b/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz deleted file mode 100644 index 14629c7b9..000000000 Binary files a/Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling - SEAD/TSK-010 - Task Modelling - SEAD.miz and /dev/null differ diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.lua b/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.lua deleted file mode 100644 index e9a776183..000000000 --- a/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.lua +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - - - - - - - -do - local Mission = MISSION:New( 'Pickup', 'Operational', 'Pickup Troops', 'NATO' ) - - Mission:AddClient( CLIENT:FindByName( 'DE Pickup Test 1' ):Transport() ) - Mission:AddClient( CLIENT:FindByName( 'DE Pickup Test 2' ):Transport() ) - - local CargoTable = {} - - local EngineerNames = { "Alpha", "Beta", "Gamma", "Delta", "Theta" } - - Cargo_Pickup_Zone_1 = CARGO_ZONE:New( 'Pickup Zone 1', 'DE Communication Center 1' ):BlueSmoke() - Cargo_Pickup_Zone_2 = CARGO_ZONE:New( 'Pickup Zone 2', 'DE Communication Center 2' ):RedSmoke() - - for CargoItem = 1, 2 do - CargoTable[CargoItem] = AI_CARGO_GROUP:New( 'Engineers', 'Team ' .. EngineerNames[CargoItem], math.random( 70, 100 ) * 3, 'DE Infantry', Cargo_Pickup_Zone_1 ) - end - - for CargoItem = 3, 5 do - CargoTable[CargoItem] = AI_CARGO_GROUP:New( 'Engineers', 'Team ' .. EngineerNames[CargoItem], math.random( 70, 100 ) * 3, 'DE Infantry', Cargo_Pickup_Zone_2 ) - end - - --Cargo_Package = CARGO_INVISIBLE:New( 'Letter', 0.1, 'DE Secret Agent', 'Pickup Zone Package' ) - --Cargo_Goods = CARGO_STATIC:New( 'Goods', 20, 'Goods', 'Pickup Zone Goods', 'DE Collection Point' ) - --Cargo_SlingLoad = CARGO_SLING:New( 'Basket', 40, 'Basket', 'Pickup Zone Sling Load', 'DE Cargo Guard' ) - - - -- Assign the Pickup Task - local PickupTask = PICKUPTASK:New( 'Engineers', CLIENT.ONBOARDSIDE.LEFT ) - PickupTask:FromZone( Cargo_Pickup_Zone_1 ) - PickupTask:FromZone( Cargo_Pickup_Zone_2 ) - PickupTask:InitCargo( CargoTable ) - PickupTask:SetGoalTotal( 3 ) - Mission:AddTask( PickupTask, 1 ) - - - Cargo_Deploy_Zone_1 = CARGO_ZONE:New( 'Deploy Zone 1', 'DE Communication Center 3' ):RedFlare() - Cargo_Deploy_Zone_2 = CARGO_ZONE:New( 'Deploy Zone 2', 'DE Communication Center 4' ):WhiteFlare() - - -- Assign the Pickup Task - local DeployTask = DEPLOYTASK:New( 'Engineers' ) - DeployTask:ToZone( Cargo_Deploy_Zone_1 ) - DeployTask:ToZone( Cargo_Deploy_Zone_2 ) - DeployTask:SetGoalTotal( 3 ) - Mission:AddTask( DeployTask, 2 ) - - MISSIONSCHEDULER.AddMission( Mission ) -end - -do - local Mission = MISSION:New( 'Deliver secret letter', 'Operational', 'Pickup letter to the commander.', 'NATO' ) - - Client_Package_1 = CLIENT:FindByName( 'BE Package Test 1' ):Transport() - - Mission:AddClient( Client_Package_1 ) - - Package_Pickup_Zone = CARGO_ZONE:New( 'Package Pickup Zone', 'DE Guard' ):GreenSmoke() - - Cargo_Package = AI_CARGO_PACKAGE:New( 'Letter', 'Letter to Command', 0.1, Client_Package_1 ) - --Cargo_Goods = CARGO_STATIC:New( 'Goods', 20, 'Goods', 'Pickup Zone Goods', 'DE Collection Point' ) - --Cargo_SlingLoad = CARGO_SLING:New( 'Basket', 40, 'Basket', 'Pickup Zone Sling Load', 'DE Cargo Guard' ) - - - -- Assign the Pickup Task - local PickupTask = PICKUPTASK:New( 'Letter', CLIENT.ONBOARDSIDE.FRONT ) - PickupTask:FromZone( Package_Pickup_Zone ) - PickupTask:InitCargo( { Cargo_Package } ) - PickupTask:SetGoalTotal( 1 ) - Mission:AddTask( PickupTask, 1 ) - - - Package_Deploy_Zone = CARGO_ZONE:New( 'Package Deploy Zone', 'DE Secret Car' ):GreenFlare() - - -- Assign the Pickup Task - local DeployTask = DEPLOYTASK:New( 'Letter' ) - DeployTask:ToZone( Package_Deploy_Zone ) - DeployTask:SetGoalTotal( 1 ) - Mission:AddTask( DeployTask, 2 ) - - MISSIONSCHEDULER.AddMission( Mission ) -end - -do - local Mission = MISSION:New( 'Sling load Cargo', 'Operational', 'Sling Load Cargo to Deploy Zone.', 'NATO' ) - - Mission:AddClient( CLIENT:FindByName( 'Sling Load Test Client 1' ):Transport() ) - Mission:AddClient( CLIENT:FindByName( 'Sling Load Test Client 2' ):Transport() ) - - Sling_Load_Pickup_Zone = CARGO_ZONE:New( 'Sling Load Pickup Zone', 'Sling Load Guard' ):RedSmoke() - - Cargo_Sling_Load = CARGO_SLINGLOAD:New( 'Sling', 'Food Boxes', 200, 'Sling Load Pickup Zone', 'Sling Load Guard', country.id.USA ) - --Cargo_Goods = CARGO_STATIC:New( 'Goods', 20, 'Goods', 'Pickup Zone Goods', 'DE Collection Point' ) - --Cargo_SlingLoad = CARGO_SLING:New( 'Basket', 40, 'Basket', 'Pickup Zone Sling Load', 'DE Cargo Guard' ) - - - -- Assign the Pickup Task - local PickupTask = PICKUPTASK:New( 'Sling', CLIENT.ONBOARDSIDE.FRONT ) - PickupTask:FromZone( Sling_Load_Pickup_Zone ) - PickupTask:InitCargo( { Cargo_Sling_Load } ) - PickupTask:SetGoalTotal( 1 ) - Mission:AddTask( PickupTask, 1 ) - - MISSIONSCHEDULER.AddMission( Mission ) -end - - - --- MISSION SCHEDULER STARTUP -MISSIONSCHEDULER.Start() -MISSIONSCHEDULER.ReportMenu() -MISSIONSCHEDULER.ReportMissionsHide() - -env.info( "Test Mission loaded" ) diff --git a/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz b/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz deleted file mode 100644 index 3f3a50344..000000000 Binary files a/Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling - Pickup/TSK-020 - Task Modelling - Pickup.miz and /dev/null differ diff --git a/Moose Test Missions/XXX - Header Template.lua b/Moose Test Missions/XXX - Header Template.lua deleted file mode 100644 index c25e774fa..000000000 --- a/Moose Test Missions/XXX - Header Template.lua +++ /dev/null @@ -1,14 +0,0 @@ ---- --- Name: XXX-999 - Title --- Author: YYY --- Date Created: DD Mmm YYYY --- --- # Situation: --- --- --- --- # Test cases: --- --- 1. - - diff --git a/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.lua b/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.lua deleted file mode 100644 index 82baa1977..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.lua +++ /dev/null @@ -1,32 +0,0 @@ ---- --- Name: ZON-100 - Normal Zone --- Author: FlightControl --- Date Created: 21 Feb 2017 --- --- # Situation: --- --- A ZONE has been defined, which boundaries are smoking. --- A vehicle is driving through the zone perimeters. --- When the vehicle is driving in the zone, a red smoke is fired from the vehicle location. --- --- # Test cases: --- --- 1. Observe the zone perimeter smoke. --- 2. Observe the vehicle smoking a red smoke when driving through the zone. - - -GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) - -ZoneA = ZONE:New( "Zone A" ) -ZoneA:SmokeZone( SMOKECOLOR.White, 90 ) - -Messager = SCHEDULER:New( nil, - function() - GroupInside:MessageToAll( ( GroupInside:IsCompletelyInZone( ZoneA ) ) and "Inside Zone A" or "Outside Zone A", 1 ) - if GroupInside:IsCompletelyInZone( ZoneA ) then - GroupInside:GetUnit(1):SmokeRed() - end - end, - {}, 0, 1 ) - diff --git a/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz b/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz deleted file mode 100644 index 175545901..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal Zone.miz and /dev/null differ diff --git a/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.lua b/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.lua deleted file mode 100644 index 60f255fcd..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.lua +++ /dev/null @@ -1,43 +0,0 @@ ---- --- Name: ZON-101 - Normal Zone - Random Point --- Author: FlightControl --- Date Created: 18 Feb 2017 --- --- # Situation: --- --- Three zones are defined. --- 15 points are smoked in each zone. --- The first 15 points are blue smoked using the GetRandomVec2() API. --- The second 15 points are orange smoked using the GetRandomPointVec2() API. --- The third 15 points are red smoked using the GetRandomPointVec3() API. --- Note: The zones perimeters are also smoked in white, so you can observe the random point placement. --- Note: At each zone an vehicle is placed, so you can view the smoking in external view. --- --- # Test cases: --- --- 1. Observe smoking of Blue smoke in Zone 1. --- 2. Observe smoking of Orange smoke in Zone 2. --- 3. Observe smoking of Red smoke in Zone 3. - -Zone1 = ZONE:New( "Zone 1" ) -Zone2 = ZONE:New( "Zone 2" ) -Zone3 = ZONE:New( "Zone 3" ) - -Zone1:SmokeZone( SMOKECOLOR.White, 18 ) -Zone2:SmokeZone( SMOKECOLOR.White, 18 ) -Zone3:SmokeZone( SMOKECOLOR.White, 18 ) - -for i = 1, 15 do - -- Zone 1 - local Vec2 = Zone1:GetRandomVec2() - local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) - PointVec2:SmokeBlue() - - -- Zone 2 - local PointVec2 = Zone2:GetRandomPointVec2() - PointVec2:SmokeOrange() - - -- Zone 3 - local PointVec3 = Zone3:GetRandomPointVec3() - PointVec3:SmokeRed() -end diff --git a/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz deleted file mode 100644 index 93221c846..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-101 - Normal Zone - Random Point/ZON-101 - Normal Zone - Random Point.miz and /dev/null differ diff --git a/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.lua b/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.lua deleted file mode 100644 index 361f2a7f9..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.lua +++ /dev/null @@ -1,32 +0,0 @@ ---- --- Name: ZON-100 - Normal Zone --- Author: FlightControl --- Date Created: 21 Feb 2017 --- --- # Situation: --- --- A ZONE has been defined, which boundaries are smoking. --- A vehicle is driving through the zone perimeters. --- When the vehicle is driving in the zone, a red smoke is fired from the vehicle location. --- --- # Test cases: --- --- 1. Observe the zone perimeter smoke. --- 2. Observe the vehicle smoking a red smoke when driving through the zone. - - -GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) - -ZoneA = ZONE:New( "Zone A" ) -ZoneA:BoundZone( 90 ) - -Messager = SCHEDULER:New( nil, - function() - GroupInside:MessageToAll( ( GroupInside:IsCompletelyInZone( ZoneA ) ) and "Inside Zone A" or "Outside Zone A", 1 ) - if GroupInside:IsCompletelyInZone( ZoneA ) then - GroupInside:GetUnit(1):SmokeRed() - end - end, - {}, 0, 1 ) - diff --git a/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.miz b/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.miz deleted file mode 100644 index 325a3ce98..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-102 - Normal Zone Boundary/ZON-102 - Normal Zone Boundary.miz and /dev/null differ diff --git a/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.lua b/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.lua deleted file mode 100644 index c59865b09..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.lua +++ /dev/null @@ -1,36 +0,0 @@ ---- --- Name: ZON-200 - Group Zone --- Author: FlightControl --- Date Created: 21 Feb 2017 --- --- # Situation: --- --- A ZONE_GROUP has been defined, which boundaries are smoking. --- A vehicle is driving through the zone perimeters. --- When the vehicle is driving in the zone, a red smoke is fired from the vehicle location. --- --- # Test cases: --- --- 1. Observe the zone perimeter smoke. --- 2. Observe the vehicle smoking a red smoke when driving through the zone. - -GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) - -Tank = GROUP:FindByName( "Tank" ) -ZoneA = ZONE_GROUP:New( "Zone A", Tank, 100 ) - -Messager = SCHEDULER:New( nil, - function() - GroupInside:MessageToAll( ( GroupInside:IsCompletelyInZone( ZoneA ) ) and "Inside Zone A" or "Outside Zone A", 1 ) - if GroupInside:IsCompletelyInZone( ZoneA ) then - GroupInside:GetUnit(1):SmokeRed() - end - end, - {}, 0, 1 ) - -TankZoneColoring = SCHEDULER:New( nil, - function() - ZoneA:FlareZone( FLARECOLOR.White, 90, 60 ) - end, - {}, 0, 5 ) \ No newline at end of file diff --git a/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz b/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz deleted file mode 100644 index 46f458a43..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group Zone.miz and /dev/null differ diff --git a/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.lua b/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.lua deleted file mode 100644 index 38d073689..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.lua +++ /dev/null @@ -1,43 +0,0 @@ ---- --- Name: ZON-201 - Group Zone - Random Point --- Author: FlightControl --- Date Created: 18 Feb 2017 --- --- # Situation: --- --- Three zones are defined. --- 15 points are smoked in each zone. --- The first 15 points are blue smoked using the GetRandomVec2() API. --- The second 15 points are orange smoked using the GetRandomPointVec2() API. --- The third 15 points are red smoked using the GetRandomPointVec3() API. --- Note: The zones perimeters are also smoked in white, so you can observe the random point placement. --- Note: At each zone an vehicle is placed, so you can view the smoking in external view. --- --- # Test cases: --- --- 1. Observe smoking of Blue smoke in Zone 1. --- 2. Observe smoking of Orange smoke in Zone 2. --- 3. Observe smoking of Red smoke in Zone 3. - -Zone1 = ZONE_GROUP:New( "Zone 1", GROUP:FindByName( "Zone 1" ), 300 ) -Zone2 = ZONE_GROUP:New( "Zone 2", GROUP:FindByName( "Zone 2" ), 300 ) -Zone3 = ZONE_GROUP:New( "Zone 3", GROUP:FindByName( "Zone 3" ), 300 ) - -Zone1:SmokeZone( SMOKECOLOR.White, 18 ) -Zone2:SmokeZone( SMOKECOLOR.White, 18 ) -Zone3:SmokeZone( SMOKECOLOR.White, 18 ) - -for i = 1, 15 do - -- Zone 1 - local Vec2 = Zone1:GetRandomVec2() - local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) - PointVec2:SmokeBlue() - - -- Zone 2 - local PointVec2 = Zone2:GetRandomPointVec2() - PointVec2:SmokeOrange() - - -- Zone 3 - local PointVec3 = Zone3:GetRandomPointVec3() - PointVec3:SmokeRed() -end diff --git a/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz deleted file mode 100644 index 156ce8687..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-201 - Group Zone - Random Point/ZON-201 - Group Zone - Random Point.miz and /dev/null differ diff --git a/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.lua b/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.lua deleted file mode 100644 index e27334e35..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.lua +++ /dev/null @@ -1,36 +0,0 @@ ---- --- Name: ZON-300 - Unit Zone --- Author: FlightControl --- Date Created: 21 Feb 2017 --- --- # Situation: --- --- A ZONE_UNIT has been defined, which boundaries are smoking. --- A vehicle is driving through the zone perimeters. --- When the vehicle is driving in the zone, a red smoke is fired from the vehicle location. --- --- # Test cases: --- --- 1. Observe the zone perimeter smoke. --- 2. Observe the vehicle smoking a red smoke when driving through the zone. - -GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) - -Tank = UNIT:FindByName( "Tank" ) -ZoneA = ZONE_UNIT:New( "Zone A", Tank, 100 ) - -Messager = SCHEDULER:New( nil, - function() - GroupInside:MessageToAll( ( GroupInside:IsCompletelyInZone( ZoneA ) ) and "Inside Zone A" or "Outside Zone A", 1 ) - if GroupInside:IsCompletelyInZone( ZoneA ) then - GroupInside:GetUnit(1):SmokeRed() - end - end, - {}, 0, 1 ) - -TankZoneColoring = SCHEDULER:New( nil, - function() - ZoneA:FlareZone( FLARECOLOR.White, 90, 60 ) - end, - {}, 0, 5 ) \ No newline at end of file diff --git a/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz b/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz deleted file mode 100644 index 454bf3947..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit Zone.miz and /dev/null differ diff --git a/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.lua b/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.lua deleted file mode 100644 index db53722a8..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.lua +++ /dev/null @@ -1,43 +0,0 @@ ---- --- Name: ZON-301 - Unit Zone - Random Point --- Author: FlightControl --- Date Created: 18 Feb 2017 --- --- # Situation: --- --- Three zones are defined. --- 15 points are smoked in each zone. --- The first 15 points are blue smoked using the GetRandomVec2() API. --- The second 15 points are orange smoked using the GetRandomPointVec2() API. --- The third 15 points are red smoked using the GetRandomPointVec3() API. --- Note: The zones perimeters are also smoked in white, so you can observe the random point placement. --- Note: At each zone an vehicle is placed, so you can view the smoking in external view. --- --- # Test cases: --- --- 1. Observe smoking of Blue smoke in Zone 1. --- 2. Observe smoking of Orange smoke in Zone 2. --- 3. Observe smoking of Red smoke in Zone 3. - -Zone1 = ZONE_UNIT:New( "Zone 1", UNIT:FindByName( "Zone 1" ), 300 ) -Zone2 = ZONE_UNIT:New( "Zone 2", UNIT:FindByName( "Zone 2" ), 300 ) -Zone3 = ZONE_UNIT:New( "Zone 3", UNIT:FindByName( "Zone 3" ), 300 ) - -Zone1:SmokeZone( SMOKECOLOR.White, 18 ) -Zone2:SmokeZone( SMOKECOLOR.White, 18 ) -Zone3:SmokeZone( SMOKECOLOR.White, 18 ) - -for i = 1, 15 do - -- Zone 1 - local Vec2 = Zone1:GetRandomVec2() - local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) - PointVec2:SmokeBlue() - - -- Zone 2 - local PointVec2 = Zone2:GetRandomPointVec2() - PointVec2:SmokeOrange() - - -- Zone 3 - local PointVec3 = Zone3:GetRandomPointVec3() - PointVec3:SmokeRed() -end diff --git a/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz deleted file mode 100644 index 79d8438ea..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-301 - Unit Zone - Random Point/ZON-301 - Unit Zone - Random Point.miz and /dev/null differ diff --git a/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.lua b/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.lua deleted file mode 100644 index c73a6360e..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.lua +++ /dev/null @@ -1,32 +0,0 @@ ---- --- Name: ZON-400 - Radius Zone --- Author: FlightControl --- Date Created: 21 Feb 2017 --- --- # Situation: --- --- A ZONE_RADIUS has been defined, which boundaries are smoking. --- A vehicle is driving through the zone perimeters. --- When the vehicle is driving in the zone, a red smoke is fired from the vehicle location. --- --- # Test cases: --- --- 1. Observe the polygon perimeter smoke. --- 2. Observe the vehicle smoking a red smoke when driving through the zone. - -GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) - -House = STATIC:FindByName( "House" ) -ZoneA = ZONE_RADIUS:New( "Zone A", House:GetVec2(), 300 ) -ZoneA:SmokeZone( SMOKECOLOR.White, 90 ) - -Messager = SCHEDULER:New( nil, - function() - GroupInside:MessageToAll( ( GroupInside:IsCompletelyInZone( ZoneA ) ) and "Inside Zone A" or "Outside Zone A", 1 ) - if GroupInside:IsCompletelyInZone( ZoneA ) then - GroupInside:GetUnit(1):SmokeRed() - end - end, - {}, 0, 1 ) - diff --git a/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz b/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz deleted file mode 100644 index 6922a5501..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius Zone.miz and /dev/null differ diff --git a/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.lua b/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.lua deleted file mode 100644 index b3d511267..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.lua +++ /dev/null @@ -1,48 +0,0 @@ ---- --- Name: ZON-401 - Radius Zone - Random Point --- Author: FlightControl --- Date Created: 18 Feb 2017 --- --- # Situation: --- --- Three zones are defined. --- 15 points are smoked in each zone. --- The first 15 points are blue smoked using the GetRandomVec2() API. --- The second 15 points are orange smoked using the GetRandomPointVec2() API. --- The third 15 points are red smoked using the GetRandomPointVec3() API. --- Note: The zones perimeters are also smoked in white, so you can observe the random point placement. --- Note: At each zone an vehicle is placed, so you can view the smoking in external view. --- --- # Test cases: --- --- 1. Observe smoking of Blue smoke in Zone 1. --- 2. Observe smoking of Orange smoke in Zone 2. --- 3. Observe smoking of Red smoke in Zone 3. - -Unit1 = UNIT:FindByName( "Zone 1" ) -Unit2 = UNIT:FindByName( "Zone 2" ) -Unit3 = UNIT:FindByName( "Zone 3" ) - - -Zone1 = ZONE_RADIUS:New( "Zone 1", Unit1:GetVec2(), 300 ) -Zone2 = ZONE_RADIUS:New( "Zone 2", Unit2:GetVec2(), 300 ) -Zone3 = ZONE_RADIUS:New( "Zone 3", Unit3:GetVec2(), 300 ) - -Zone1:SmokeZone( SMOKECOLOR.White, 18 ) -Zone2:SmokeZone( SMOKECOLOR.White, 18 ) -Zone3:SmokeZone( SMOKECOLOR.White, 18 ) - -for i = 1, 15 do - -- Zone 1 - local Vec2 = Zone1:GetRandomVec2() - local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) - PointVec2:SmokeBlue() - - -- Zone 2 - local PointVec2 = Zone2:GetRandomPointVec2() - PointVec2:SmokeOrange() - - -- Zone 3 - local PointVec3 = Zone3:GetRandomPointVec3() - PointVec3:SmokeRed() -end diff --git a/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz deleted file mode 100644 index 9092b58e2..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-401 - Radius Zone - Random Point/ZON-401 - Radius Zone - Random Point.miz and /dev/null differ diff --git a/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.lua b/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.lua deleted file mode 100644 index b5201c1f0..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.lua +++ /dev/null @@ -1,33 +0,0 @@ ---- --- Name: ZON-500 - Polygon Zone --- Author: FlightControl --- Date Created: 18 Feb 2017 --- --- # Situation: --- --- A ZONE_POLYGON has been defined, which boundaries are smoking. --- A vehicle is driving through the zone perimeters. --- When the vehicle is driving in the zone, a red smoke is fired from the vehicle location. --- --- # Test cases: --- --- 1. Observe the polygon perimeter smoke. --- 2. Observe the vehicle smoking a red smoke when driving through the zone. - -GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) - -GroupPolygon = GROUP:FindByName( "Polygon A" ) - -PolygonZone = ZONE_POLYGON:New( "Polygon A", GroupPolygon ) -PolygonZone:SmokeZone( SMOKECOLOR.White, 20 ) - -Messager = SCHEDULER:New( nil, - function() - GroupInside:MessageToAll( ( GroupInside:IsCompletelyInZone( PolygonZone ) ) and "Inside Polygon A" or "Outside Polygon A", 1 ) - if GroupInside:IsCompletelyInZone( PolygonZone ) then - GroupInside:GetUnit(1):SmokeRed() - end - end, - {}, 0, 1 ) - diff --git a/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz b/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz deleted file mode 100644 index f346cd547..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 - Polygon Zone.miz and /dev/null differ diff --git a/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.lua b/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.lua deleted file mode 100644 index 545c7d9b6..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.lua +++ /dev/null @@ -1,43 +0,0 @@ ---- --- Name: ZON-501 - Polygon Zone - Random Point --- Author: FlightControl --- Date Created: 18 Feb 2017 --- --- # Situation: --- --- Three zones are defined. --- 15 points are smoked in each zone. --- The first 15 points are blue smoked using the GetRandomVec2() API. --- The second 15 points are orange smoked using the GetRandomPointVec2() API. --- The third 15 points are red smoked using the GetRandomPointVec3() API. --- Note: The zones perimeters are also smoked in white, so you can observe the random point placement. --- Note: At each zone an vehicle is placed, so you can view the smoking in external view. --- --- # Test cases: --- --- 1. Observe smoking of Blue smoke in Zone 1. --- 2. Observe smoking of Orange smoke in Zone 2. --- 3. Observe smoking of Red smoke in Zone 3. - -Zone1 = ZONE_POLYGON:New( "Zone 1", GROUP:FindByName( "Zone 1" ) ) -Zone2 = ZONE_POLYGON:New( "Zone 2", GROUP:FindByName( "Zone 2" ) ) -Zone3 = ZONE_POLYGON:New( "Zone 3", GROUP:FindByName( "Zone 3" ) ) - -Zone1:SmokeZone( SMOKECOLOR.White, 4 ) -Zone2:SmokeZone( SMOKECOLOR.White, 4 ) -Zone3:SmokeZone( SMOKECOLOR.White, 4 ) - -for i = 1, 15 do - -- Zone 1 - local Vec2 = Zone1:GetRandomVec2() - local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) - PointVec2:SmokeBlue() - - -- Zone 2 - local PointVec2 = Zone2:GetRandomPointVec2() - PointVec2:SmokeOrange() - - -- Zone 3 - local PointVec3 = Zone3:GetRandomPointVec3() - PointVec3:SmokeRed() -end diff --git a/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz b/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz deleted file mode 100644 index 9d1d25461..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-501 - Polygon Zone - Random Point/ZON-501 - Polygon Zone - Random Point.miz and /dev/null differ diff --git a/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.lua b/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.lua deleted file mode 100644 index c3075153c..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.lua +++ /dev/null @@ -1,33 +0,0 @@ ---- --- Name: ZON-502 - Polygon Zone Boundary --- Author: FlightControl --- Date Created: 18 Feb 2017 --- --- # Situation: --- --- A ZONE_POLYGON has been defined, which boundaries are tires. --- A vehicle is driving through the zone perimeters. --- When the vehicle is driving in the zone, a red smoke is fired from the vehicle location. --- --- # Test cases: --- --- 1. Observe the polygon perimeter smoke. --- 2. Observe the vehicle smoking a red smoke when driving through the zone. - -GroupInside = GROUP:FindByName( "Test Inside Polygon" ) -GroupOutside = GROUP:FindByName( "Test Outside Polygon" ) - -GroupPolygon = GROUP:FindByName( "Polygon A" ) - -PolygonZone = ZONE_POLYGON:New( "Polygon A", GroupPolygon ) -PolygonZone:BoundZone() - -Messager = SCHEDULER:New( nil, - function() - GroupInside:MessageToAll( ( GroupInside:IsCompletelyInZone( PolygonZone ) ) and "Inside Polygon A" or "Outside Polygon A", 1 ) - if GroupInside:IsCompletelyInZone( PolygonZone ) then - GroupInside:GetUnit(1):SmokeRed() - end - end, - {}, 0, 1 ) - diff --git a/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.miz b/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.miz deleted file mode 100644 index 8aa3dd0ba..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-502 - Polygon Zone Boundary/ZON-502 - Polygon Zone Boundary.miz and /dev/null differ diff --git a/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.lua b/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.lua deleted file mode 100644 index 2cb69852d..000000000 --- a/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.lua +++ /dev/null @@ -1,86 +0,0 @@ --- Name: ZON-510 - Send message if Clients fly the first time in the Polygon Zones --- Author: Wingthor and FlightControl --- Date Created: 20 January 2017 --- --- # Situation: --- --- There are a couple of player slots of Su-25Ts, that need to fly through two poly zones. --- Once a player flies through a poly zone, a message will be sent. But only once. If he flies back through the same zone, --- nothing is displayed anymore. Unless he logs off and rejoins the mission. --- --- # Test cases: --- --- - --- MOOSE wraps each Group alive in the mission into a GROUP class object. The GROUP class object is a wrapper object, wrapping --- the Group object from DCS and adding methods to it. --- Get the GROUP wrapper objects that were created by MOOSE at mission startup, by using the GROUP:FindByName() method. --- The Group name is the parameter to be searched for. --- Note that late activated groups are also "alive" and have a corresponding GROUP object in the running mission. -PolyZoneGroup1 = GROUP:FindByName("PolyZone1") -PolyZoneGroup2 = GROUP:FindByName("PolyZone2") - --- Create 2 Polygon objects, using the ZONE_POLYGON:New constructor. --- The first parameter gives a name to the zone, the second is the GROUP object that defines the zone form. -PolyZone1 = ZONE_POLYGON:New( "PolyZone1", PolyZoneGroup1 ) -PolyZone2 = ZONE_POLYGON:New( "PolyZone2", PolyZoneGroup2 ) - --- Create a SET of Moose CLIENT wrapper objects. At mission startup, a SET of Moose client wrapper objects is created. --- Note that CLIENT objects don't necessarily need to be alive!!! --- So this set contains EVERY RED coalition client defined within the mission. -RedClients = SET_CLIENT:New():FilterCoalitions("red"):FilterStart() - - - -RedClients:ForEachClient( - function( MooseClient ) - - -- Here we register the state of the client in which step he is in. - -- We set the state of the client "ZoneStep" to 0, indicating that he is not out of the first zone. - local function ResetClientForZone( MooseClient ) - BASE:E("Reset") - MooseClient:SetState( MooseClient, "ZoneStep", "0" ) - end - - BASE:E( { "Alive Init", Client = MooseClient } ) - MooseClient:Alive( ResetClientForZone ) - end -) - -Scheduler, SchedulerID = SCHEDULER:New( nil, - function () - - RedClients:ForEachClientInZone( PolyZone1, - function( MooseClient ) - BASE:E( { Client = MooseClient, State = MooseClient:GetState( MooseClient, "ZoneStep" ) } ) - if MooseClient:GetState( MooseClient, "ZoneStep" ) == "0" then - MooseClient:SetState( MooseClient, "ZoneStep", "1" ) - MESSAGE:New("Lorem Ipsum", 15, "Pilot Update" ):ToClient( MooseClient ) - end - end - ) - - RedClients:ForEachClientNotInZone( PolyZone1, - function( MooseClient ) - BASE:E( { Client = MooseClient, State = MooseClient:GetState( MooseClient, "ZoneStep" ) } ) - if MooseClient:GetState( MooseClient, "ZoneStep" ) == "1" then - MooseClient:SetState( MooseClient, "ZoneStep", "2" ) - MESSAGE:New("Ipsum Ipsum", 15, "Pilot Update" ):ToClient( MooseClient ) - end - end - ) - - RedClients:ForEachClientInZone( PolyZone2, - function( MooseClient ) - BASE:E( { Client = MooseClient, State = MooseClient:GetState( MooseClient, "ZoneStep" ) } ) - if MooseClient:GetState( MooseClient, "ZoneStep" ) == "2" then - MooseClient:SetState( MooseClient, "ZoneStep", "3" ) - MESSAGE:New("Lorem Lorem", 15, "Pilot Update" ):ToClient( MooseClient ) - end - end - ) - - end, {}, 10, 1 - ) - - \ No newline at end of file diff --git a/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz b/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz deleted file mode 100644 index adf919376..000000000 Binary files a/Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones/ZON-510 - Send message if Clients fly the first time in the Polygon Zones.miz and /dev/null differ diff --git a/Moose Training/Document.bat b/Moose Training/Document.bat deleted file mode 100644 index 63ec378b2..000000000 --- a/Moose Training/Document.bat +++ /dev/null @@ -1 +0,0 @@ -luadocumentor --d "Documentation" --s "%1\Moose Training\Stylesheet\stylesheet.css" "%1\Moose Development\Moose" "%1\Moose Development\dcs" diff --git a/Moose Training/Moose Web Site.txt b/Moose Training/Moose Web Site.txt deleted file mode 100644 index a6acfb655..000000000 --- a/Moose Training/Moose Web Site.txt +++ /dev/null @@ -1,80 +0,0 @@ -# Context - -MOOSE is a **M**ission **O**bject **O**riented **S**cripting **E**nvironment, and is meant for mission designers and mission hosters. -It allows to quickly setup complex missions using pre-scripted scenarios using the available classes within the MOOSE Framework. -MOOSE is currently still in alpha version, but will evolve over time. Right now, it has been updated to work with DCS world 1.5. and 2.0. - -You can find the source of MOOSE here on GITHUB. It is free for download: -https://github.com/FlightControl-Master/MOOSE/ - -Note: MOOSE is complementary to [MIST](https://github.com/mrSkortch/MissionScriptingTools/releases), so if you use MIST in parallel with MOOSE objects, this should work. - -# Goals - -The goal of MOOSE is to allow mission designers to enhance their scripting with mission orchestration objects, which can be instantiated from defined classes within the framework. This will allow to write mission scripts with minimal code embedded. Of course, the richness of the framework will determine the richness of the misson scenarios. We can expect that MOOSE will evolve over time, as more missions will be designed within the framework. - -# Mission Design with Moose - -In order to create a mission using MOOSE, you'll have to include a file named **Moose.lua**: - -1. Create a new mission in the DCS World Mission Editor. -2. In the mission editor, create a new trigger. -3. Name the trigger Moose Load and let it execute only at MISSION START. -4. Add an action DO SCRIPT FILE (without a condition, so the middle column must be empty). -5. In the action, browse to the **[Moose.lua](https://github.com/FlightControl-Master/MOOSE/tree/master/Moose%20Mission%20Setup)** file in the **Moose Mission Setup** directory, and include this file within your mission. -6. Make sure that the "Moose Load" trigger is completely at the top of your mission. - -Voila, MOOSE is now included in your mission. During the execution of this mission, all MOOSE classes will be loaded, and all MOOSE initializations will be exectuted before any other mission action is executed. - -IMPORTANT NOTE: When a new version of MOOSE is released, you'll have to UPDATE the Moose.lua file in EACH OF YOUR MISSION. -This can be a tedious task, and for this purpose, a tool has been developed that will update the Moose.lua files automatically within your missions. -Refer to the tool at [Moose Mission Setup\Moose Mission Update](https://github.com/FlightControl-Master/MOOSE/tree/master/Moose%20Mission%20Setup/Moose%20Mission%20Update) directory for further information included in the [READ.ME]() file. - - -# MOOSE LUA Classes - -The following classes are currently embedded within MOOSE and can be included within your mission scripts: - -* [BASE](Moose Training/Documentation/Base.html): The main class from which all MOOSE classes are derived from. The BASE class contains essential functions to support inheritance and MOOSE object execution tracing (logging within the DCS.log file in the saved games folder of the user). - -* [GROUP](Moose Training/Documentation/Group.html): This class wraps a DCS Group object within the simulator. It provides a more extensive API set, as well takes an abstraction of the complexity to give tasks, commands and set various options to DCS Groups. Additionally, the GROUP class provides a much richer API to identify various properties of the DCS Group. For each DCS Group created object within a running mission, a GROUP object will be created automatically, that is managed within the DATABASE, under the _DATABASE object. - -* [UNIT](Moose Training/Documentation/Unit.html): This class wraps a DCS Unit object within the simulator. It provides a more extensive API set, as well takes an abstraction of the complexity to give commands and set various options to DCS Units. Additionally, the UNIT class provides a much richer API to identify various properties of the DCS Unit. For each DCS Unit object created within a running mission, a UNIT object will be created automatically, that is stored within the DATABASE, under the _DATABASE object. - -* [CLIENT](Moose Training/Documentation/Client.html): This class wraps a DCS Unit object within the simulator, which has a skill Client or Player. The CLIENT class derives from the UNIT class, thus contains the complete UNIT API set, and additionally, the CLIENT class provides an API set to manage players joining or leaving clients, sending messages to players, and manage the state of units joined by players. For each DCS Unit object created within a running mission that can be joined by a player, a CLIENT object will be created automatically, that is stored within the DATABASE, under the _DATABASE object. - -* [DATABASE](Moose Training/Documentation/Database.html): Creates a collection of GROUPS[], UNITS[], CLIENTS[] and managed these sets automatically. Provides an API set to retrieve a GROUP, UNIT or CLIENT instance from the _DATABASE object using defined APIs. The collections are maintained dynamically during the execution of the mission, so when players join, leave, when units are created or destroyed, the collections are dynamically updated. - -* [SPAWN](Moose Training/Documentation/Spawn.html): Spawn new groups (and units) during mission execution. - -* [ESCORT](Moose Training/Documentation/Escort.html): Makes groups consisting of helicopters, airplanes, ground troops or ships within a mission joining your flight. You can control these groups through the ratio menu during your flight. Available commands are around: Navigation, Position Hold, Reporting (Target Detection), Attacking, Assisted Attacks, ROE, Evasion, Mission Execution and more ... - -* [MISSILETRAINER](Moose Training/Documentation/MissileTrainer.html): Missile trainer, it destroys missiles when they are within a certain range of player airplanes, displays tracking and alert messages of missile launches; approach; destruction, and configure with radio menu commands. Various APIs available to configure the trainer. - - -# MOOSE Tutorials and Examples - -You can download [test missions](https://github.com/FlightControl-Master/MOOSE/tree/master/Test%20Missions) to have examples and learn the syntax and usage of the MOOSE classes. - -There are [Video Tutorials](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) on my YOUTUBE channel on which you can learn some coding aspects with mission execution demos, as well as some of the tooling and internal mechanisms within MOOSE. - - -In the triggers section, there is a flag (9999) that will decide how MOOSE will be loaded. When flag 9999 is set, MOOSE will be dynamically loaded. When flag 9999 is off, MOOSE will be loaded embedded. Note that when loaded embedded, **your mission must include the last Moose_Embedded.lua file**. So, ensure that the last file is included in the DO SCRIPT section when MOOSE got an update! - -# Credits - -Note that the framework is based on code i've written myself, but some of it is also based on code that i've seen as great scripting code and ideas, and which i've revised. I see this framework evolving towards a broader public, and the ownership may dissapear (or parts of it). Consider this code public domain. Therefore a list of credits to all who have or are contributing (this list will increase over time): Grimes, Prof_Hilactic, xcom, the 476 virtual squadron team, ... - -You'll notice that within this framework, there are functions used from mist. I've taken the liberty to copy those atomic mist functions that are very nice and useful, and used those. - -2. Prof_hilactic & others: -SEAD Defenses. I've taken the script, and reworded it to fit within MOOSE. The script within MOOSE is hardly recognizable anymore from the original. Find here the posts: http://forums.eagle.ru/showpost.php?...59&postcount=1 - -3. xcom -His contribution is related to the eStatHandler. I've analyzed and studied his scripts, and reworked it a bit to use it also within the framework (I've also tweaked it a bit). Find his post here: http://forums.eagle.ru/showpost.php?...73&postcount=1 - -4. The rest of the framework functions and class definitions were my own developments. Trust i've spent hours and hours investigating, trying and writing and documenting code building this framework. - -Note that there is a vast amount of other scripts out there. I may contact you personally to ask for your contribution / permission if i can use your idea or script to tweak it to the framework. Parts of these scripts will have to be redesigned to fit it into an OO framework. - -Hope you think the idea is great and useful. \ No newline at end of file diff --git a/Moose Training/Presentations/AI Balancer/DCS World - MOOSE - AI Balancer - Part 1 - Explanation.pptx b/Moose Training/Presentations/AI Balancer/DCS World - MOOSE - AI Balancer - Part 1 - Explanation.pptx deleted file mode 100644 index 582c16967..000000000 Binary files a/Moose Training/Presentations/AI Balancer/DCS World - MOOSE - AI Balancer - Part 1 - Explanation.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Cargo/DCS World - MOOSE - Cargo.pptx b/Moose Training/Presentations/Cargo/DCS World - MOOSE - Cargo.pptx deleted file mode 100644 index 1b6971fc8..000000000 Binary files a/Moose Training/Presentations/Cargo/DCS World - MOOSE - Cargo.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts.pptx b/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts.pptx deleted file mode 100644 index 8d4a6afd8..000000000 Binary files a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia1.JPG b/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia1.JPG deleted file mode 100644 index 61217a2e1..000000000 Binary files a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia1.JPG and /dev/null differ diff --git a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia2.JPG b/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia2.JPG deleted file mode 100644 index 442d4c0e3..000000000 Binary files a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia2.JPG and /dev/null differ diff --git a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia3.JPG b/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia3.JPG deleted file mode 100644 index 6aac8cbc0..000000000 Binary files a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia3.JPG and /dev/null differ diff --git a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia4.JPG b/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia4.JPG deleted file mode 100644 index 54b039046..000000000 Binary files a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia4.JPG and /dev/null differ diff --git a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia5.JPG b/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia5.JPG deleted file mode 100644 index 5341ffd0d..000000000 Binary files a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia5.JPG and /dev/null differ diff --git a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia6.JPG b/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia6.JPG deleted file mode 100644 index cfa208b57..000000000 Binary files a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia6.JPG and /dev/null differ diff --git a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia7.JPG b/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia7.JPG deleted file mode 100644 index 6ac282097..000000000 Binary files a/Moose Training/Presentations/Core/FSM/MOOSE - FSM - 1. Concepts/Dia7.JPG and /dev/null differ diff --git a/Moose Training/Presentations/Detection/DCS World - MOOSE - Detection - Part 1 - Teaser.pptx b/Moose Training/Presentations/Detection/DCS World - MOOSE - Detection - Part 1 - Teaser.pptx deleted file mode 100644 index d3385d469..000000000 Binary files a/Moose Training/Presentations/Detection/DCS World - MOOSE - Detection - Part 1 - Teaser.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Detection/DCS World - MOOSE - Detection - Part 2 - Demo.pptx b/Moose Training/Presentations/Detection/DCS World - MOOSE - Detection - Part 2 - Demo.pptx deleted file mode 100644 index 8f48e806f..000000000 Binary files a/Moose Training/Presentations/Detection/DCS World - MOOSE - Detection - Part 2 - Demo.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Detection/DCS World - MOOSE - Detection and Fac.pptx b/Moose Training/Presentations/Detection/DCS World - MOOSE - Detection and Fac.pptx deleted file mode 100644 index 4c578e417..000000000 Binary files a/Moose Training/Presentations/Detection/DCS World - MOOSE - Detection and Fac.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Escorting/DCS World - MOOSE - Escorting - Part 2 - APIs.pptx b/Moose Training/Presentations/Escorting/DCS World - MOOSE - Escorting - Part 2 - APIs.pptx deleted file mode 100644 index ccedaa222..000000000 Binary files a/Moose Training/Presentations/Escorting/DCS World - MOOSE - Escorting - Part 2 - APIs.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Installation and Usage/DCS World - MOOSE - Development - Part 1 - Tools and Installation.pptx b/Moose Training/Presentations/Installation and Usage/DCS World - MOOSE - Development - Part 1 - Tools and Installation.pptx deleted file mode 100644 index b40406f48..000000000 Binary files a/Moose Training/Presentations/Installation and Usage/DCS World - MOOSE - Development - Part 1 - Tools and Installation.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Installation and Usage/DCS World - MOOSE - Development - Part 2 - Using Eclipse and MOOSE - Copy.pptx b/Moose Training/Presentations/Installation and Usage/DCS World - MOOSE - Development - Part 2 - Using Eclipse and MOOSE - Copy.pptx deleted file mode 100644 index 44d5a6499..000000000 Binary files a/Moose Training/Presentations/Installation and Usage/DCS World - MOOSE - Development - Part 2 - Using Eclipse and MOOSE - Copy.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Installation and Usage/DCS World - MOOSE - Development - Part 3 - The DATABASE - UNIT - CLIENT - GROUP - ZONE, .pptx b/Moose Training/Presentations/Installation and Usage/DCS World - MOOSE - Development - Part 3 - The DATABASE - UNIT - CLIENT - GROUP - ZONE, .pptx deleted file mode 100644 index 62b627f62..000000000 Binary files a/Moose Training/Presentations/Installation and Usage/DCS World - MOOSE - Development - Part 3 - The DATABASE - UNIT - CLIENT - GROUP - ZONE, .pptx and /dev/null differ diff --git a/Moose Training/Presentations/Missile Trainer/DCS World - MOOSE - Missile Trainer.pptx b/Moose Training/Presentations/Missile Trainer/DCS World - MOOSE - Missile Trainer.pptx deleted file mode 100644 index e894e5374..000000000 Binary files a/Moose Training/Presentations/Missile Trainer/DCS World - MOOSE - Missile Trainer.pptx and /dev/null differ diff --git a/Moose Training/Presentations/NATO SYMBOLS/basic-unit-and-operationa.ppt b/Moose Training/Presentations/NATO SYMBOLS/basic-unit-and-operationa.ppt deleted file mode 100644 index 6cdfa0360..000000000 Binary files a/Moose Training/Presentations/NATO SYMBOLS/basic-unit-and-operationa.ppt and /dev/null differ diff --git a/Moose Training/Presentations/Sets/DCS World - MOOSE - Sets - Part 1 - SET_GROUP.pptx b/Moose Training/Presentations/Sets/DCS World - MOOSE - Sets - Part 1 - SET_GROUP.pptx deleted file mode 100644 index f2eeb21b8..000000000 Binary files a/Moose Training/Presentations/Sets/DCS World - MOOSE - Sets - Part 1 - SET_GROUP.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Spawning/DCS World - MOOSE - Spawning - Part 2 - APIs.pptx b/Moose Training/Presentations/Spawning/DCS World - MOOSE - Spawning - Part 2 - APIs.pptx deleted file mode 100644 index 589ffcb07..000000000 Binary files a/Moose Training/Presentations/Spawning/DCS World - MOOSE - Spawning - Part 2 - APIs.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Tasking/MOOSE - Tasking - 1. Concepts.jpg b/Moose Training/Presentations/Tasking/MOOSE - Tasking - 1. Concepts.jpg deleted file mode 100644 index 6bf98dab5..000000000 Binary files a/Moose Training/Presentations/Tasking/MOOSE - Tasking - 1. Concepts.jpg and /dev/null differ diff --git a/Moose Training/Presentations/Tasking/MOOSE - Tasking - 1. Concepts.pptx b/Moose Training/Presentations/Tasking/MOOSE - Tasking - 1. Concepts.pptx deleted file mode 100644 index 2fec1dab6..000000000 Binary files a/Moose Training/Presentations/Tasking/MOOSE - Tasking - 1. Concepts.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Wrapper/DCS World - MOOSE - Development - Part 4 - Wrapper Classes.pptx b/Moose Training/Presentations/Wrapper/DCS World - MOOSE - Development - Part 4 - Wrapper Classes.pptx deleted file mode 100644 index f336e0011..000000000 Binary files a/Moose Training/Presentations/Wrapper/DCS World - MOOSE - Development - Part 4 - Wrapper Classes.pptx and /dev/null differ diff --git a/Moose Training/Presentations/Zones/DCS World - MOOSE - Zones - Part 1 - Use zones with GROUP and UNIT.pptx b/Moose Training/Presentations/Zones/DCS World - MOOSE - Zones - Part 1 - Use zones with GROUP and UNIT.pptx deleted file mode 100644 index f7d467324..000000000 Binary files a/Moose Training/Presentations/Zones/DCS World - MOOSE - Zones - Part 1 - Use zones with GROUP and UNIT.pptx and /dev/null differ diff --git a/Moose Training/Release Announce/Release Announce.bbc b/Moose Training/Release Announce/Release Announce.bbc deleted file mode 100644 index 4dddcf4a2..000000000 --- a/Moose Training/Release Announce/Release Announce.bbc +++ /dev/null @@ -1,224 +0,0 @@ - -Hi everyone, - -It's FlightControl here ... It is been quite a ride ... But here it is, the release of the MOOSE framework for DCS World is a fact. This release is adding the following functionality to MOOSE, on top of the pre-release version: - -[list] -[*] Task orchestration: CommandCenters, Missions, Tasks, Task Actions ... -[*] AI balancing and AI bahaviour. AIBalancer, Patrol Zones, ... -[*] Cargo handling: Board, Unboard, Transfer cargo of different types. -[*] Dynamic detection and task assignment through FACs. -[*] Additional functions in existing classes. -[/list] - -Preparing this release was a large work ... I hope you like using it. - -There are various test missions created to demonstrate the framework. It is worth to have a close look to the lua files for each test mission. You can find in each directory to lua file that is embedded in the test mission. -The documentation is not yet up-to-date, but that will improve next year. On top, I plan to create training videos for you to demonstrate the capabilities of the introduced new concepts. These things take time and a good preparation, so pls be patient. - -[size=24][b][u]API Changes![/u][/b][/size] - -The [color=blue]good[/color] and [color=red]bad [/color]news is that there are a couple of API changes that I had to push through. The reason why these API changes appear in the release is, I made some bad design decisions in the past, and I've made the interface more consistent. I would like you to review and update your missions to adapt and incorporate these API changes. Implementing these API changes can be quickly done I believe ... The changes are minor. - -In a nuttshell: -- I changed the name of the SPAWN initialization functions, adding [color=blue]Init[/color] to the name. I wanted to make sure that the difference between Initialization and Spawning in the SPAWN methods were clear, because many people had questions and were confused! Please refer to the SPA-1xx for a demonstration on these new Init functions (they are the same, only the name changed). -- Spawning of new units can be done now from a Unit location, a Static location, a PointVec2 location, a PointVec3 location and a Zone location. In order to make the interface consistent over all these functions, I added a new function called [color=blue]InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )[/color] and added, revised the APIs of the Spawning functions. Please refer to the test missions SPA-3xx for demo scripts to use these functions. -- Zones can now also be randomized during Spawning using the method [color=blue]InitRandomizeZones( SpawnZones )[/color]. Please refer to the test mission SPA-220 for a demonstration to use this function. - -Here are the changes: - -[list] -[*][b]BASE[/b] - -[list] -[*]DCS Event handling functions have been added to the base class. These functions start with the [color=blue]OnEvent...[/color] prefix. These functions now allow you to DCS events in a class like when a birth of a unit happens, or when a crash happens etc. -[/list] - -[*][b]SPAWN[/b] - -[list] -[*][color=blue]OnSpawnGroup[/color]( SpawnCallBackFunction, ... ) replaces [color=red]SpawnFunction[/color]( SpawnCallBackFunction, ... ) -[/list] - -[list] -[*][b]SpawnInZone[/b]( Zone, [color=blue]RandomizeGroup[/color], SpawnIndex ) replaces SpawnInZone( Zone, [color=red]RandomizeUnits, OuterRadius, InnerRadius,[/color] SpawnIndex ) -[*][b]SpawnFromVec3[/b]( Vec3, SpawnIndex ) replaces SpawnFromVec3( Vec3, [color=red]RandomizeUnits, OuterRadius, InnerRadius,[/color] SpawnIndex ) -[*][b]SpawnFromVec2[/b]( Vec2, SpawnIndex ) replaces SpawnFromVec2( Vec2, [color=red]RandomizeUnits, OuterRadius, InnerRadius,[/color] SpawnIndex ) -[*][b]SpawnFromUnit[/b]( SpawnUnit, SpawnIndex ) replaces SpawnFromUnit( SpawnUnit, [color=red]RandomizeUnits, OuterRadius, InnerRadius,[/color] SpawnIndex ) -[*][b]SpawnFromStatic[/b]( SpawnUnit, SpawnIndex ) replaces SpawnFromStatic( SpawnStatic, [color=red]RandomizeUnits, OuterRadius, InnerRadius,[/color] SpawnIndex ) -[/list] - - -[list] -[*][color=blue][b]InitRandomizeUnits[/b]( RandomizeUnits, OuterRadius, InnerRadius )[/color] added. -[*][color=blue][b]InitLimit[/b][/color]( SpawnMaxUnitsAlive, SpawnMaxGroups ) replaces [color=red]Limit[/color]( SpawnMaxUnitsAlive, SpawnMaxGroups ) -[*][color=blue][b]InitArray[/b][/color]( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) replaces [color=red]Array[/color]( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) -[*][color=blue][b]InitRandomizeRoute[/b][/color]( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) replaces [color=red]RandomizeRoute[/color]( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) -[*][color=blue][b]InitRandomizeTemplate[/b][/color]( SpawnTemplatePrefixTable ) replaces [color=red]RandomizeTemplate[/color]( SpawnTemplatePrefixTable ) -[*][color=blue][b]InitUnControlled[/b][/color]() replaces [color=red]UnControlled[/color]() -[*][color=blue][b]InitCleanUp[/b][/color]( SpawnCleanUpInterval ) replaces [color=red]CleanUp[/color]( SpawnCleanUpInterval ) -[*][color=blue][b]InitRandomizeZones[/b]( SpawnZones )[/color] added -[/list] - - -[*][b]AIBALANCER[/b] -[list] -[*]Has been completely reworked. I don't think anybody has been using this class beside hijack. -[/list] - - -[*][b]PATROLZONE[/b]: -[list] -[*]Has been completely reworked. I don't think anybody has been using this class beside hijack. -[/list] - - -[*][b]POINT_VEC3[/b] and references in other classes methods to POINT_VEC3 objects: -[list] -[*][color=blue]Translate( Distance, Angle )[/color]added. -[*]Replaced methods ending with [color=red]Point_Vec3[/color]() to [color=blue]Vec3[/color]() where the code manages a Vec3. Replaced all references to the method. -[*]Replaced method [color=red]Point_Vec2()[/color] to [color=blue]Vec2[/color]() where the code manages a Vec2. Replaced all references to the method. -[*]Replaced method [color=red]Random_Point_Vec3()[/color] to [color=blue]RandomVec3[/color]() where the code manages a Vec3. Replaced all references to the method. -[/list] - - -[*][b]SCHEDULER[/b] has been reworked, see below. -[/list] - -[size=24][b][u]What's new![/u][/b][/size] - -Find below a comprehensive summary of the MOOSE new features in this release: - -[size=22][b][u]1. Task Orchestration[/u][/b][/size] - -[size=18][b][u]1.1. Comand Centers[/u][/b][/size] - -[b]COMMANDCENTER[/b]: Governs the communication and existence of Missions, Tasks and Actions for a Coalition. -[list=1] -[*]Create a new CommandCenter. Multiple CommandCenters can be defined within one Mission. -[*]Maintain Missions. Add, Remove and CleanUp Missions, and undelying Tasks and Actions. -[*]Provide a navigation menu to orchestrate for different groups the Tasking.[*]Send Reports to players of the Tasks within all Missions of a CommandCenter. -[*]Send Messages to all players alive within all Missions of a CommandCenter. -[/list] - -[size=18][b][u]1.2. Missions[/u][/b][/size] - -[b]MISSION[/b]: Governs the process flow of the Mission, Tasks and Actions for a CommandCenter for a Coalition.[list=1] -[*]Create a new Mission for a CommandCenter. -[*]Provide Mission status flow, implemented through a Finite State Machine. -[*]Expose Mission event- and state functions to influence the Mission orchestration. -[*]Maintain Tasks: Add, Remove and CleanUp Tasks and underlying Actions. -[*]Send Reports to players of the Tasks within the Mission. -[*]Send Messages to all players within the Mission. -[*]Attach a Scoring and event log implemented through the SCORING class. -[/list] - -[size=18][b][u]1.3. Tasks[/u][/b][/size] - -[b]TASK[/b]: Governs the process flow of the Task state and the execution of the Taskprocess and hierarchical processes by players. -[list=1] -[*]Create a new Task for a Mission governed by a CommandCenter. -[*]Provide Task status flow, implemented through a Finite State Machine. -[*]Expose Task event- and state functions to influence the Task orchestration. -[*]Create a Task Actions, implemented through a Finite State Machine Process, that the players will need to follow when the Task is Assigned to a Player (Unit). -[*]Maintain Tasks: Add, Remove and CleanUp Task Actions and hierarchical Actions. -[*]Expose Mission event- and state functions to influence the Task state. - -- Flag a Task as Planned. -- Flag a Task as Assigned. -- Flag a Task as Successful. -- Flag a Task as Failed. -- Flag a Task as Aborted. -- Flag a Task as Cancelled. -[*]Send Reports of the Task to the players. -[*]Send Messages to the player executing and assigned to the Task. -[*]Create a Task Action workbook. -[*]Provide a mechanism to assign players to the Task. -[*]Provide a mechanism to abort players from the Task. -[*]Each assigned player to the Task, will have a Task Action flow executing, governed by the TASK object. -[*]Provide a mechanism to attach a Scoring scheme when certain states are reached in the Task and in the Task Action flow. -[/list] - -[size=18][b][u]1.4. Task Actions[/u][/b][/size] - -[b]ACT_ASSIGN[/b], [b]ACT_ROUTE[/b], [b]ACT_ACCOUNT[/b], [b]ACT_SMOKE[/b]: Governs Task Action Subroutines that can be embedded within a Task Action flow. These ACT_ classes will be further enhanced and expanded now the baseline of MOOSE is there. This will result in mission designed being able to quickly combine these actions to implement different Task flows. -[list=1] -[*]Create a new Task Action Subroutine for a Task Action flow, governed by a Task. -[*]ACT_ASSIGN: Base class to assign a player to a Task. If the player is in a Group that is already assigned to the Task, the Player will be assigned automatically. -[*]ACT_ASSIGN_ACCEPT: Assign a player to a Task, and automatically Accept the Task. -[*]ACT_ASSIGN_MENU_ACCEPT: Assign a player to a Task, and let the Player Accept or Reject the Task within 30 seconds. -[*]ACT_ROUTE: Base class to route a player. -[*]ACT_ROUTE_ZONE: Route a player to a zone. -[*]ACT_ACCOUNT: Base class to "account" events or things within a running mission. -[*]ACT_ACCOUNT_DEADS: Account if certain DCS Units are dead. -[*]ACT_ASSIST: Base class to assist players with certain actions through the menu. -[*]ACT_ASSIST_SMOKE: Assist players with smoking target areas while in flight through the menu. -[*]ACT_...: Expect in the future more ACT classes to be created and added to the MOOSE framework. It is upon our creativity to identify good functions to be added. -[/list] - -[size=22][b][u]2. Finite State Machines[/u][/b][/size] - -[b]FSM[/b], [b]FSM_CONTROLLABLE[/b], [b]FSM_ACTION[/b], [b]FSM_TASK[/b], [b]FSM_SET[/b]: Finite State Machine base classes that implement the necessary functionality to realise a workflow following various state transitions triggered through events being fired externally or internally within the FSM implementation. -[list=1] -[*]FSM: The Finitite State Machine base class. It provides functions to create a fsm workflow, adding state transitions schemes and adding sub processes. -[*]FSM_CONTROLLABLE: An fsm governing the workflow for a Controllable object within DCS, which can be a UNIT or a GROUP. -[*]FSM_PROCESS: An fsm governing a workflow for a Controllable object within DCS for a Task. Note that all ACT_... classes are derived classes from FSM_PROCESS, implementing an fsm to govern the Player unit for the Task given. -[*]FSM_TASK: An fsm governing a workflow for a Task. Note that the TASK class is derived from FSM_TASK. -[*]FSM_SET: An fsm governing a workflow for a Set. -[/list] - -[size=22][b][u]3. Balance and Control AI[/u][/b][/size] - -[b]AIBALANCER[/b]: Balances AI within a Mission. It is up to the mission designer to capture the different AIBalancer events, and attach different AI processes to accomodate AI behaviour. -[list=1] -[*]Spawn new AI as there aren't players in the Mission. In other words, spawn as many AI required to simulate player behaviour. -[*]Attach various AI processes to let the AI execute certain tasks. -[*]Implements an fsm to accomodate a workflow to let the mission designer attach various AI workflows for AI implementation behaviour. -[*]PATROLZONE: -[/list] - -[b]PATROLZONE[/b]: Is an AI behaviour class, governing AI to patrol a zone, used by the AI balancer. -[list=1] -[*]Provide an fsm process, implementing AI patrol behaviour so that AI is patrolling a zone for a defined time. -[*]Expose various event functions to influence the AI patrol behaviour. -[*]The PATROLZONE class can be used in AIBALANCER to simulate players. -[/list] - -[size=22][b][u]4. Cargo Handling[/u][/b][/size] - -[b]CARGO[/b]: Cargo Handling of CONTROLLABLE object, thus UNITs and GROUPs. CARGO provides a dynamic way to Load, Unload, Board, UnBoard and Transfer Cargo between Carriers. [u]The cargo handling of units is animated, that means, you'll see the units moving towards or from the carriers ... -[/u][list=1] -[*]Board Cargo to a Carrier. -[*]Unboard Cargo from a Carrier to a point. -[*]Transfer Cargo from a Carrier to another Carrier. -[*]CARGO is the base class implementing the cargo workflow. -[*]CARGO_GROUP: Implements the Cargo handling for a GROUP. -[*]CARGO_UNIT: Implements the Cargo handling for one UNIT. -[*]CARGO_GROUPED: Implements the Cargo handling for multiple GROUPs. -[*]CARGO_PACKAGE: Under construction. This would simulate a package being carried by a carrier. -[/list] -Note: There are various Task Actions (ACT_) classes planned that would allow to deploy and pickup cargo in a battle field. -Note: There are various AI_ classes planned that would allow to deploy and pickup cargo in a battlefield.[/td] - -[size=22][b][u]5. Scheduling[/u][/b][/size] - -[b]SCHEDULER[/b]: The scheduling of methods has been intensively reworked. In the previous version of MOOSE, each SCHEDULER object controlled the scheduling of the method provided. However, with the new SCHEDULER implementation, the actually scheduling is now done by a SCHEDULEDISPATCHER class, which has one instance within the MOOSE framework, under _SCHEDULEDISPATCHER... SCHEDULER has some minor changes to the API. One of them is that SCHEDULER now allows to schedule more than one repeated schedule. When the method SCHEDULE.Schedule() is called, a ScheduleID is returned. This ScheduleID can then be used to Stop or (Re-)Start the schedule using the SCHEDULER object and the ScheduleID. The actual object controlling all the schedules is _SCHEDULEDISPATCHER. - - -[size=24][b][u]Other comments ...[/u][/b][/size] - -There are many more changes done within the framework, but these are very technical and hidden from the API set that the users will use. Some of the work that was done includes: rework scheduling, adding a scheduler dispatcher. - -There is still some work to be done on the [b]TASK_DISPATCHER[/b], but that will be done the coming weeks, so don't spend too much time on that for the moment ... - -Also, this release builds the foundation of many many other classes to come. Now that we have state machines and the object model is now more or less stable, other functions can be built upon this framework. It would be great that a community would see the benefits of this development and endorse it, like many have already done. - -The documentation is not completely up-to-date, but that will come and flatten out the next weeks. -Also, demonstration videos will be published on my youtube channel next year to demo some of the new functions, and I'll rework a few of the older versions. - -If you have problems using this release and somehow feel blocked, you can use the previous commit on the master branch. Just click in GITHUB on the previous commit, sync and you'll be fine. - -I hope you will have the same pleasure using this framework as the creators had making it. - -Thanks all; -FC diff --git a/Release 2.1.bb b/Release 2.1.bb new file mode 100644 index 000000000..afa8bc203 --- /dev/null +++ b/Release 2.1.bb @@ -0,0 +1,362 @@ +[SIZE=7]MOOSE Release 2.1.0[/SIZE] + +Finally it is here, release 2.1.0 of MOOSE! +It took some time to prepare this release, as it was a lot of work to get the building blocks of the framework developed and tested. You'll find in this release a lot of new features as well as a couple of important bug fixes. + +Release 2.1.0 is now published into the [B]master-release-2.1[/B] branch of this repository on github. +You can download the file moose.lua below to use MOOSE in your missions. +The moose.lua file is also located [URL="https://github.com/FlightControl-Master/MOOSE/blob/master-release-2.1/Moose%20Mission%20Setup/Moose.lua"]here[/URL] in the [B]master-release-2.1[/B] branch. + +Those who are using the [B]master[/B] branch can continue to beta test, as new bleeding edge features will be added soon in preparation for release 2.2.0! There are many topics on the agenda to be added. + +[B]This release would not have been possible without the help and contribution of many members of this community. THANK YOU![/B] + + + +[SIZE=6]In summary:[/SIZE] + +This release brings you [B]an improved tasking mechanism[/B]. +Tasking is the system in MOOSE that allows to: + + * Execute [B]co-op[/B] missions and tasks + * [B]Detect[/B] targets dynamically + * Define new tasks [B]dynamically[/B] + * Execute the tasks + * Complete the mission [B]goals[/B] + * Extensive menu system and briefings/reports for [B]player interaction[/B] + * Improved Scoring of mission goal achievements, and task achievements. + +On top, release brings you new functionality by the introduction of new classes to: + + * [B]Designate targets[/B] (lase, smoke or illuminate targets) by AI, assisting your attack. Allows to drop laser guides bombs. + * A new [B]tasking[/B] system to [B]transport cargo[/B] of various types + * Dynamically [B]spawn static objects[/B] + * Improved [B]coordinate system[/B] + * Build [B]large formations[/B], like bombers flying to a target area + + + + +[SIZE=6]1. TASKING SYSTEM![/SIZE] + +A lot of work has been done in improving the tasking framework within MOOSE. + +**The tasking system comes with TASK DISPATCHING mechanisms, that DYNAMICALLY +allocate new tasks based on the tactical or strategical situation in the mission!!! +These tasks can then be engaged upon by the players!!!** + +The [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Task_A2G_Dispatcher.html"]TASK_A2G_DISPATCHER[/URL] class implements the dynamic dispatching of tasks upon groups of detected units determined a Set of FAC (groups). The FAC will detect units, will group them, and will dispatch Tasks to groups of players. Depending on the type of target detected, different tasks will be dispatched. Find a summary below describing for which situation a task type is created: + + * [B]CAS Task[/B]: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter. + * [B]BAI Task[/B]: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter. + * [B]SEAD Task[/B]: Is created when there are enemy ground units wihtin range of the FAC, with air search radars. + +More TASK_... dispatcher classes are to come in the future, like A2A, G2G, etc... + +Improvements on the TASKING are in summary: + + * A COMMANDCENTER has a dedicated menu. + * A MISSION has a dedicated menu system. + * A MISSION has a briefing report. + * A MISSION has dedicated status reports. + * A MISSION has for each TASK TYPE a menu. + * A MISSION has for each TASK TYPE a dedicated menu system for each TASK defined. + * A MISSION has an "assigned" task menu that contains menu actions relevant to the assigned task. + * A TASK (of various types) has a dedicated menu system. + * A TASK has a briefing report. + * A TASK has dedicated status reports. + * Player reports can be retrieved that explain which player is at which task. + * ... + +TASKING is vast, and at the moment there is too much to explain. +[B]The best way to explore the TASKING is to TRY it...[/B] +I suggest you have a look at the [URL="https://www.youtube.com/watch?v=v2Us8SS1-44&t=1070s"]GORI Valley Mission - Iteration 3[/URL]. + +Many people have contributed in the testing of the mechanism, especially: +@baluballa, @doom, @whiplash + + + +[SIZE=6]2. New MOOSE classes have been added.[/SIZE] + +MOOSE 2.1.0 comes with new classes that extends the functionality of the MOOSE framework and allow you to do new things in your missions: + + + +[SIZE=5]2.1. Target designation by laser, smoke or illumination.[/SIZE] + +[URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Designate.html"]DESIGNATE[/URL] is orchestrating the designation of potential targets executed by a Recce group, +and communicates these to a dedicated attacking group of players, +so that following a dynamically generated menu system, +each detected set of potential targets can be lased or smoked... + +Targets can be: + + * [B]Lased[/B] for a period of time. + * [B]Smoked[/B]. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.) + * [B]Illuminated[/B] through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready. + +This class was made with the help of @EasyEB and many others. + +[URL="https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0dQ9UKQMb7YL8z2sKSqemH"]DESIGNATE is demonstrated on youtube[/URL] + +DESIGNATE demonstration missions: + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/DES%20-%20Designation"]DES - Designation[/URL] + + + +[SIZE=5]2.2. Transport cargo of different types to various locations as a human task within a mission.[/SIZE] + +The Moose framework provides various CARGO classes that allow DCS physical or logical objects to be transported or sling loaded by Carriers. +The CARGO_ classes, as part of the moose core, are able to Board, Load, UnBoard and UnLoad cargo between Carrier units. +This collection of classes in this module define tasks for human players to handle these cargo objects. +Cargo can be transported, picked-up, deployed and sling-loaded from and to other places. + +[URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Task_Cargo.html#TASK_CARGO_TRANSPORT"]TASK_CARGO_TRANSPORT[/URL] defines a task for a human player to transport a set of cargo between various zones. +It is the first class that forms part of the TASK_CARGO classes suite. + +The TASK_CARGO classes provide you with a flexible tasking sytem, +that allows you to transport cargo of various types between various locations +and various dedicated deployment zones. + +A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J). +The player needs to accept the task from the task overview list within the mission, using the radio menus. +Once the TASK_CARGO_TRANSPORT is assigned to the player and accepted by the player, the player will obtain +an extra [B]Cargo Handling Radio Menu[/B] that contains the CARGO objects that need to be transported. +Cargo can be transported towards different [B]Deployment Zones[/B], but can also be deployed anywhere within the battle field. + +The Cargo Handling Radio Menu system allows to execute [B]various actions[/B] to handle the cargo. +In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed. +Depending on the location of your Carrier unit, the menu options will vary. + +The [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Cargo.html#CARGO_GROUP"]CARGO_GROUP[/URL] class defines a +cargo that is represented by a GROUP object within the simulator, and can be transported by a carrier. + +The [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Cargo.html#CARGO_UNIT"]CARGO_UNIT[/URL] class defines a +cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. + +Mission designers can use the [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Set.html#SET_CARGO"]SET_CARGO[/URL] +class to build sets of cargos. + +Note 1: [B]Various other CARGO classes are defined and are WIP[/B]. +Now that the foundation for Cargo handling is getting form, future releases will bring other types of CARGO handling +classes to the MOOSE framework quickly. Sling-loading, package, beacon and other types of CARGO will be released soon. + +Note 2: [B]AI_CARGO has been renamed to CARGO and now forms part of the Core or MOOSE[/B]. +If you were using AI_CARGO in your missions, please rename AI_CARGO with CARGO... + +TASK_TRANSPORT_CARGO is demonstrated at the [URL="https://www.youtube.com/watch?v=v2Us8SS1-44&t=1070s"]GORI Valley Mission - Iteration 4[/URL] + +TASK_TRANSPORT_CARGO demonstration missions: + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-110%20-%20Ground%20-%20Transport%20Cargo%20Group"]TSK-110 - Ground - Transport Cargo Group[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-210%20-%20Helicopter%20-%20Transport%20Cargo%20Group"]TSK-210 - Helicopter - Transport Cargo Group[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-211%20-%20Helicopter%20-%20Transport%20Multiple%20Cargo%20Groups"]TSK-211 - Helicopter - Transport Multiple Cargo Groups[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-212%20-%20Helicopter%20-%20Cargo%20handle%20PickedUp%20and%20Deployed%20events"]TSK-212 - Helicopter - Cargo handle PickedUp and Deployed events[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-213%20-%20Helicopter%20-%20Cargo%20Group%20Destroyed"]TSK-213 - Helicopter - Cargo Group Destroyed[/URL] + + + +[SIZE=5]2.3. Dynamically spawn STATIC objects into your mission.[/SIZE] + +The [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/SpawnStatic.html#SPAWNSTATIC"]SPAWNSTATIC[/URL] class allows to spawn dynamically new Statics. +By creating a copy of an existing static object template as defined in the Mission Editor (ME), SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy" these properties to create a new static object and place it at the desired coordinate. +New spawned Statics get the same name as the name of the template Static, or gets the given name when a new name is provided at the Spawn method. + +SPAWNSTATIC demonstration missions: + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/SPS%20-%20Spawning%20Statics/SPS-100%20-%20Simple%20Spawning"]SPS-100 - Simple Spawning[/URL] + + + +[SIZE=5]2.4. Better coordinate management in MGRS or LLor LLDecimal.[/SIZE] + +The [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Point.html#COORDINATE"]COORDINATE[/URL] class +defines a 2D coordinate in the simulator. A COORDINATE can be expressed in LL or in MGRS. + + + +[SIZE=5]2.5. Improved scoring system[/SIZE] + +Scoring is implemented throught the [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Scoring.html"]SCORING[/URL] class. +The scoring system has been improved a lot! Now, the scoring is correctly counting scores on normal units, statics and scenary objects. +Specific scores can be registered for specific targets. The scoring works together with the tasking system, so players can achieve +additional scores when they achieve goals! + +SCORING demonstration missions: + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring/SCO-100%20-%20Scoring%20of%20Statics"]SCO-100 - Scoring of Statics[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring/SCO-101%20-%20Scoring%20Client%20to%20Client"]SCO-101 - Scoring Client to Client[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring/SCO-500%20-%20Scoring%20Multi%20Player%20Demo%20Mission%201"]SCO-500 - Scoring Multi Player Demo Mission 1[/URL] + + + +[SIZE=5]2.6. Beacons and Radio[/SIZE] + +The Radio contains 2 classes : RADIO and BEACON + +What are radio communications in DCS ? + + * Radio transmissions consist of [B]sound files[/B] that are broadcasted on a specific [B]frequency[/B] (e.g. 115MHz) and [B]modulation[/B] (e.g. AM), + * They can be [B]subtitled[/B] for a specific [B]duration[/B], the [B]power[/B] in Watts of the transmiter's antenna can be set, and the transmission can be [B]looped[/B]. + +These classes are the work of @Grey-Echo. + +RADIO and BEACON demonstration missions: + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-000%20-%20Transmission%20from%20Static"]RAD-000 - Transmission from Static[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-001%20-%20Transmission%20from%20UNIT%20or%20GROUP"]RAD-001 - Transmission from UNIT or GROUP[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-002%20-%20Transmission%20Tips%20and%20Tricks"]RAD-002 - Transmission Tips and Tricks[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-010%20-%20Beacons"] RAD-010 - Beacons[/URL] + + + +[SIZE=5]2.7. Build large formations of AI.[/SIZE] + +[URL="http://flightcontrol-master.github.io/MOOSE/Documentation/AI_Formation.html"]AI_FORMATION[/URL] makes AI @{GROUP}s fly in formation of various compositions. +The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! +The purpose of the class is to: + + * Make formation building a process that can be managed while in flight, rather than a task. + * Human players can guide formations, consisting of larget planes. + * Build large formations (like a large bomber field). + * Form formations that DCS does not support off the shelve. + +AI_FORMATION Demo Missions: [URL=""]FOR - AI Group Formation[/URL] + +AI_FORMATION demonstration missions: + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-100%20-%20Bomber%20Left%20Line%20Formation"]FOR-100 - Bomber Left Line Formation[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-101%20-%20Bomber%20Right%20Line%20Formation"]FOR-101 - Bomber Right Line Formation[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-102%20-%20Bomber%20Left%20Wing%20Formation"]FOR-102 - Bomber Left Wing Formation[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-103%20-%20Bomber%20Right%20Wing%20Formation"]FOR-103 - Bomber Right Wing Formation[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-104%20-%20Bomber%20Center%20Wing%20Formation"]FOR-104 - Bomber Center Wing Formation[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-105%20-%20Bomber%20Trail%20Formation"]FOR-105 - Bomber Trail Formation[/URL] + * [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-106%20-%20Bomber%20Box%20Formation"]FOR-106 - Bomber Box Formation[/URL] + +Note: The AI_FORMATION is currently a first version showing the potential, a "building block". From this class, further classes will be derived and the class will be fine-tuned. + + + +[SIZE=6]3. A lot of components have been reworked and bugs have been fixed.[/SIZE] + + + +[SIZE=5]3.1. Better event handling and event dispatching.[/SIZE] + +The underlying mechanisms to handle DCS events has been improved. Bugs have been fixed. +The MISSION_END event is now also supported. + + + +[SIZE=5]2.2. Cargo handling has been made much better now.[/SIZE] + +As a result, some of the WIP cargo classes that were defined earlier are still WIP. +But as mentioned earlier, new CARGO classes can be published faster now. +The framework is now more consistent internally. + + + +[SIZE=6]3. A lot of new methods have been defined in several existing or new classes.[/SIZE] + +AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefing ) --R2.1 +AI_FORMATION:TestSmokeDirectionVector( SmokeDirection ) --R2.1 +AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart ) --R2.1 +AI_FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace ) --R2.1 +AI_FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationVic( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels ) --R2.1 +AI_FORMATION:SetFlightRandomization( FlightRandomization ) --R2.1 +AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1 + +CARGO:GetName() +CARGO:GetObjectName() + +DATABASE:ForEachStatic( IteratorFunction, FinalizeFunction, ... ) + +EVENT:Reset( EventObject ) --R2.1 + +POINT_VEC3:IsLOS( ToPointVec3 ) --R2.1 + +COORDINATE:New( x, y, LandHeightAdd ) --R2.1 Fixes issue #424. +COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) --R2.1 Fixes issue #424. +COORDINATE:NewFromVec3( Vec3 ) --R2.1 Fixes issue #424. +COORDINATE:ToStringLL( LL_Accuracy, LL_DMS ) --R2.1 Fixes issue #424. +COORDINATE:ToStringMGRS( MGRS_Accuracy ) --R2.1 Fixes issue #424. +COORDINATE:ToString() --R2.1 Fixes issue #424. +COORDINATE:CoordinateMenu( RootMenu ) --R2.1 Fixes issue #424. +COORDINATE:MenuSystem( System ) --R2.1 Fixes issue #424. +COORDINATE:MenuLL_Accuracy( LL_Accuracy ) --R2.1 Fixes issue #424. +COORDINATE:MenuLL_DMS( LL_DMS ) --R2.1 Fixes issue #424. +COORDINATE:MenuMGRS_Accuracy( MGRS_Accuracy ) --R2.1 Fixes issue #424. + +SET_BASE:FilterDeads() --R2.1 allow deads to be filtered to automatically handle deads in the collection. +SET_BASE:FilterCrashes() --R2.1 allow crashes to be filtered to automatically handle crashes in the collection. + +SET_UNIT:ForEachUnitPerThreatLevel( FromThreatLevel, ToThreatLevel, IteratorFunction, ... ) --R2.1 Threat Level implementation + +SET_CARGO:New() --R2.1 +SET_CARGO:AddCargosByName( AddCargoNames ) --R2.1 +SET_CARGO:RemoveCargosByName( RemoveCargoNames ) --R2.1 +SET_CARGO:FindCargo( CargoName ) --R2.1 +SET_CARGO:FilterCoalitions( Coalitions ) --R2.1 +SET_CARGO:FilterTypes( Types ) --R2.1 +SET_CARGO:FilterCountries( Countries ) --R2.1 +SET_CARGO:FilterPrefixes( Prefixes ) --R2.1 +SET_CARGO:FilterStart() --R2.1 +SET_CARGO:AddInDatabase( Event ) --R2.1 +SET_CARGO:FindInDatabase( Event ) --R2.1 +SET_CARGO:ForEachCargo( IteratorFunction, ... ) --R2.1 +SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) --R2.1 +SET_CARGO:IsIncludeObject( MCargo ) --R2.1 +SET_CARGO:OnEventNewCargo( EventData ) --R2.1 +SET_CARGO:OnEventDeleteCargo( EventData ) --R2.1 SpawnStatic.lua (5 matches) + +SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, CountryID ) --R2.1 +SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, CountryID ) --R2.1 +SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1 +SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName ) --R2.1 + +ZONE_BASE:GetCoordinate( Height ) --R2.1 + +DESIGNATE:SetFlashStatusMenu( FlashMenu ) --R2.1 +DESIGNATE:SetLaserCodes( LaserCodes ) --R2.1 +DESIGNATE:GenerateLaserCodes() --R2.1 +DESIGNATE:SetAutoLase( AutoLase ) --R2.1 +DESIGNATE:SetThreatLevelPrioritization( Prioritize ) --R2.1 + +DETECTION_BASE:CleanDetectionItems() --R2.1 Clean the DetectionItems list +DETECTION_BASE:GetDetectedItemID( Index ) --R2.1 +DETECTION_BASE:GetDetectedID( Index ) --R2.1 +DETECTION_AREAS:DetectedReportDetailed() --R2.1 Fixed missing report + +REPORT:HasText() --R2.1 +REPORT:SetIndent( Indent ) --R2.1 +REPORT:AddIndent( Text ) --R2.1 + +MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure + +TASK:SetMenu( MenuTime ) --R2.1 Mission Reports and Task Reports added. Fixes issue #424. +TASK:ReportSummary() --R2.1 fixed report. Now nicely formatted and contains the info required. +TASK:ReportOverview() --R2.1 fixed report. Now nicely formatted and contains the info required. +TASK:GetPlayerCount() --R2.1 Get a count of the players. +TASK:GetPlayerNames() --R2.1 Get a map of the players. +TASK:ReportDetails() --R2.1 fixed report. Now nicely formatted and contains the info required. + +UTILS.tostringMGRS = function(MGRS, acc) --R2.1 + +POSITIONABLE:GetBoundingBox() --R2.1 +POSITIONABLE:GetHeight() --R2.1 +POSITIONABLE:GetMessageText( Message, Name ) --R2.1 added +POSITIONABLE:GetMessage( Message, Duration, Name ) --R2.1 changed callsign and name and using GetMessageText +POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Name ) --R2.1 +POSITIONABLE:GetRadio() --R2.1 +POSITIONABLE:GetBeacon() --R2.1 +POSITIONABLE:LaseUnit( Target, LaserCode, Duration ) --R2.1 +POSITIONABLE:LaseOff() --R2.1 +POSITIONABLE:IsLasing() --R2.1 +POSITIONABLE:GetSpot() --R2.1 +POSITIONABLE:GetLaserCode() --R2.1 + +UNIT:IsDetected( TargetUnit ) --R2.1 +UNIT:IsLOS( TargetUnit ) --R2.1 diff --git a/Release 2.1.md b/Release 2.1.md new file mode 100644 index 000000000..d274188b2 --- /dev/null +++ b/Release 2.1.md @@ -0,0 +1,363 @@ +# MOOSE Release 2.1.0 + +Finally it is here, release 2.1.0 of MOOSE! +It took some time to prepare this release, as it was a lot of work to get the building blocks of the framework developed and tested. You'll find in this release a lot of new features as well as a couple of important bug fixes. + +Release 2.1.0 is now published into the **master-release-2.1** branch of this repository on github. +You can download the file moose.lua below to use MOOSE in your missions. +The moose.lua file is also located [here](https://github.com/FlightControl-Master/MOOSE/blob/master-release-2.1/Moose%20Mission%20Setup/Moose.lua) in the **master-release-2.1** branch. + +Those who are using the **master** branch can continue to beta test, as new bleeding edge features will be added soon in preparation for release 2.2.0! There are many topics on the agenda to be added. + +**This release would not have been possible without the help and contribution of many +members of this community. THANK YOU!** + + + +## In summary: + +This release brings you **an improved tasking mechanism**. +Tasking is the system in MOOSE that allows to: + + * Execute **co-op** missions and tasks + * **Detect** targets dynamically + * Define new tasks **dynamically** + * Execute the tasks + * Complete the mission **goals** + * Extensive menu system and briefings/reports for **player interaction** + * Improved Scoring of mission goal achievements, and task achievements. + +On top, release brings you new functionality by the introduction of new classes to: + + * **Designate targets** (lase, smoke or illuminate targets) by AI, assisting your attack. Allows to drop laser guides bombs. + * A new **tasking** system to **transport cargo** of various types + * Dynamically **spawn static objects** + * Improved **coordinate system** + * Build **large formations**, like bombers flying to a target area + + + + +## 1. TASKING SYSTEM! + +A lot of work has been done in improving the tasking framework within MOOSE. + +**The tasking system comes with TASK DISPATCHING mechanisms, that DYNAMICALLY +allocate new tasks based on the tactical or strategical situation in the mission!!! +These tasks can then be engaged upon by the players!!!** + +The [TASK\_A2G\_DISPATCHER](http://flightcontrol-master.github.io/MOOSE/Documentation/Task_A2G_Dispatcher.html) class implements the dynamic dispatching of tasks upon groups of detected units determined a Set of FAC (groups). The FAC will detect units, will group them, and will dispatch Tasks to groups of players. Depending on the type of target detected, different tasks will be dispatched. Find a summary below describing for which situation a task type is created: + + * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter. + * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter. + * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars. + +More TASK_... dispatcher classes are to come in the future, like A2A, G2G, etc... + +Improvements on the TASKING are in summary: + + * A COMMANDCENTER has a dedicated menu. + * A MISSION has a dedicated menu system. + * A MISSION has a briefing report. + * A MISSION has dedicated status reports. + * A MISSION has for each TASK TYPE a menu. + * A MISSION has for each TASK TYPE a dedicated menu system for each TASK defined. + * A MISSION has an "assigned" task menu that contains menu actions relevant to the assigned task. + * A TASK (of various types) has a dedicated menu system. + * A TASK has a briefing report. + * A TASK has dedicated status reports. + * Player reports can be retrieved that explain which player is at which task. + * ... + +TASKING is vast, and at the moment there is too much to explain. +**The best way to explore the TASKING is to TRY it...** +I suggest you have a look at the [GORI Valley Mission - Iteration 3](https://www.youtube.com/watch?v=v2Us8SS1-44&t=1070s). + +Many people have contributed in the testing of the mechanism, especially: +@baluballa, @doom, @whiplash + + + +## 2. New MOOSE classes have been added. + +MOOSE 2.1.0 comes with new classes that extends the functionality of the MOOSE framework and allow you to do new things in your missions: + + + +### 2.1. Target designation by laser, smoke or illumination. + +[DESIGNATE](http://flightcontrol-master.github.io/MOOSE/Documentation/Designate.html) is orchestrating the designation of potential targets executed by a Recce group, +and communicates these to a dedicated attacking group of players, +so that following a dynamically generated menu system, +each detected set of potential targets can be lased or smoked... + +Targets can be: + + * **Lased** for a period of time. + * **Smoked**. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.) + * **Illuminated** through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready. + +This class was made with the help of @EasyEB and many others. + +[DESIGNATE is demonstrated on youtube](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0dQ9UKQMb7YL8z2sKSqemH) + +DESIGNATE demonstration missions: + * [DES - Designation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/DES%20-%20Designation) + + + +### 2.2. Transport cargo of different types to various locations as a human task within a mission. + +The Moose framework provides various CARGO classes that allow DCS physical or logical objects to be transported or sling loaded by Carriers. +The CARGO_ classes, as part of the moose core, are able to Board, Load, UnBoard and UnLoad cargo between Carrier units. +This collection of classes in this module define tasks for human players to handle these cargo objects. +Cargo can be transported, picked-up, deployed and sling-loaded from and to other places. + +[TASK\_CARGO\_TRANSPORT](http://flightcontrol-master.github.io/MOOSE/Documentation/Task_Cargo.html#TASK_CARGO_TRANSPORT) defines a task for a human player to transport a set of cargo between various zones. +It is the first class that forms part of the TASK_CARGO classes suite. + +The TASK_CARGO classes provide you with a flexible tasking sytem, +that allows you to transport cargo of various types between various locations +and various dedicated deployment zones. + +A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J). +The player needs to accept the task from the task overview list within the mission, using the radio menus. +Once the TASK\_CARGO\_TRANSPORT is assigned to the player and accepted by the player, the player will obtain +an extra **Cargo Handling Radio Menu** that contains the CARGO objects that need to be transported. +Cargo can be transported towards different **Deployment Zones**, but can also be deployed anywhere within the battle field. + +The Cargo Handling Radio Menu system allows to execute **various actions** to handle the cargo. +In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed. +Depending on the location of your Carrier unit, the menu options will vary. + +The [CARGO_GROUP](http://flightcontrol-master.github.io/MOOSE/Documentation/Cargo.html#CARGO_GROUP) class defines a +cargo that is represented by a GROUP object within the simulator, and can be transported by a carrier. + +The [CARGO_UNIT](http://flightcontrol-master.github.io/MOOSE/Documentation/Cargo.html#CARGO_UNIT) class defines a +cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. + +Mission designers can use the [SET_CARGO](http://flightcontrol-master.github.io/MOOSE/Documentation/Set.html#SET_CARGO) +class to build sets of cargos. + +Note 1: **Various other CARGO classes are defined and are WIP**. +Now that the foundation for Cargo handling is getting form, future releases will bring other types of CARGO handling +classes to the MOOSE framework quickly. Sling-loading, package, beacon and other types of CARGO will be released soon. + +Note 2: **AI_CARGO has been renamed to CARGO and now forms part of the Core or MOOSE**. +If you were using AI_CARGO in your missions, please rename AI_CARGO with CARGO... + +TASK\_TRANSPORT\_CARGO is demonstrated at the [GORI Valley Mission - Iteration 4](https://www.youtube.com/watch?v=v2Us8SS1-44&t=1070s) + +TASK_TRANSPORT_CARGO demonstration missions: + * [TSK-110 - Ground - Transport Cargo Group](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-110%20-%20Ground%20-%20Transport%20Cargo%20Group) + * [TSK-210 - Helicopter - Transport Cargo Group](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-210%20-%20Helicopter%20-%20Transport%20Cargo%20Group) + * [TSK-211 - Helicopter - Transport Multiple Cargo Groups](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-211%20-%20Helicopter%20-%20Transport%20Multiple%20Cargo%20Groups) + * [TSK-212 - Helicopter - Cargo handle PickedUp and Deployed events](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-212%20-%20Helicopter%20-%20Cargo%20handle%20PickedUp%20and%20Deployed%20events) + * [TSK-213 - Helicopter - Cargo Group Destroyed](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-213%20-%20Helicopter%20-%20Cargo%20Group%20Destroyed) + + + +### 2.3. Dynamically spawn STATIC objects into your mission. + +The [SPAWNSTATIC](http://flightcontrol-master.github.io/MOOSE/Documentation/SpawnStatic.html#SPAWNSTATIC) class allows to spawn dynamically new Statics. +By creating a copy of an existing static object template as defined in the Mission Editor (ME), SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy" these properties to create a new static object and place it at the desired coordinate. +New spawned Statics get the same name as the name of the template Static, or gets the given name when a new name is provided at the Spawn method. + +SPAWNSTATIC demonstration missions: + * [SPS-100 - Simple Spawning](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/SPS%20-%20Spawning%20Statics/SPS-100%20-%20Simple%20Spawning) + + + +### 2.4. Better coordinate management in MGRS or LLor LLDecimal. + +The [COORDINATE](http://flightcontrol-master.github.io/MOOSE/Documentation/Point.html#COORDINATE) class +defines a 2D coordinate in the simulator. A COORDINATE can be expressed in LL or in MGRS. + + + +### 2.5. Improved scoring system + +Scoring is implemented throught the [SCORING](http://flightcontrol-master.github.io/MOOSE/Documentation/Scoring.html) class. +The scoring system has been improved a lot! Now, the scoring is correctly counting scores on normal units, statics and scenary objects. +Specific scores can be registered for specific targets. The scoring works together with the tasking system, so players can achieve +additional scores when they achieve goals! + +SCORING demonstration missions: + * [SCO-100 - Scoring of Statics](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring/SCO-100%20-%20Scoring%20of%20Statics) + * [SCO-101 - Scoring Client to Client](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring/SCO-101%20-%20Scoring%20Client%20to%20Client) + * [SCO-500 - Scoring Multi Player Demo Mission 1](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring/SCO-500%20-%20Scoring%20Multi%20Player%20Demo%20Mission%201) + + + +### 2.6. Beacons and Radio + +The Radio contains 2 classes : RADIO and BEACON + +What are radio communications in DCS ? + + * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM), + * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**. + +These classes are the work of @Grey-Echo. + +RADIO and BEACON demonstration missions: + * [RAD-000 - Transmission from Static](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-000%20-%20Transmission%20from%20Static) + * [RAD-001 - Transmission from UNIT or GROUP](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-001%20-%20Transmission%20from%20UNIT%20or%20GROUP) + * [RAD-002 - Transmission Tips and Tricks](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-002%20-%20Transmission%20Tips%20and%20Tricks) + * [ RAD-010 - Beacons](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-010%20-%20Beacons) + + + +### 2.7. Build large formations of AI. + +[AI_FORMATION](http://flightcontrol-master.github.io/MOOSE/Documentation/AI_Formation.html) makes AI @{GROUP}s fly in formation of various compositions. +The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! +The purpose of the class is to: + + * Make formation building a process that can be managed while in flight, rather than a task. + * Human players can guide formations, consisting of larget planes. + * Build large formations (like a large bomber field). + * Form formations that DCS does not support off the shelve. + +AI_FORMATION Demo Missions: [FOR - AI Group Formation]() + +AI\_FORMATION demonstration missions: + * [FOR-100 - Bomber Left Line Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-100%20-%20Bomber%20Left%20Line%20Formation) + * [FOR-101 - Bomber Right Line Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-101%20-%20Bomber%20Right%20Line%20Formation) + * [FOR-102 - Bomber Left Wing Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-102%20-%20Bomber%20Left%20Wing%20Formation) + * [FOR-103 - Bomber Right Wing Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-103%20-%20Bomber%20Right%20Wing%20Formation) + * [FOR-104 - Bomber Center Wing Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-104%20-%20Bomber%20Center%20Wing%20Formation) + * [FOR-105 - Bomber Trail Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-105%20-%20Bomber%20Trail%20Formation) + * [FOR-106 - Bomber Box Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-106%20-%20Bomber%20Box%20Formation) + +Note: The AI_FORMATION is currently a first version showing the potential, a "building block". From this class, further classes will be derived and the class will be fine-tuned. + + + +## 3. A lot of components have been reworked and bugs have been fixed. + + + +### 3.1. Better event handling and event dispatching. + +The underlying mechanisms to handle DCS events has been improved. Bugs have been fixed. +The MISSION_END event is now also supported. + + + +### 2.2. Cargo handling has been made much better now. + +As a result, some of the WIP cargo classes that were defined earlier are still WIP. +But as mentioned earlier, new CARGO classes can be published faster now. +The framework is now more consistent internally. + + + +## 3. A lot of new methods have been defined in several existing or new classes. + +AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefing ) --R2.1 +AI_FORMATION:TestSmokeDirectionVector( SmokeDirection ) --R2.1 +AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart ) --R2.1 +AI_FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace ) --R2.1 +AI_FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationVic( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1 +AI_FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels ) --R2.1 +AI_FORMATION:SetFlightRandomization( FlightRandomization ) --R2.1 +AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1 + +CARGO:GetName() +CARGO:GetObjectName() + +DATABASE:ForEachStatic( IteratorFunction, FinalizeFunction, ... ) + +EVENT:Reset( EventObject ) --R2.1 + +POINT_VEC3:IsLOS( ToPointVec3 ) --R2.1 + +COORDINATE:New( x, y, LandHeightAdd ) --R2.1 Fixes issue #424. +COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) --R2.1 Fixes issue #424. +COORDINATE:NewFromVec3( Vec3 ) --R2.1 Fixes issue #424. +COORDINATE:ToStringLL( LL_Accuracy, LL_DMS ) --R2.1 Fixes issue #424. +COORDINATE:ToStringMGRS( MGRS_Accuracy ) --R2.1 Fixes issue #424. +COORDINATE:ToString() --R2.1 Fixes issue #424. +COORDINATE:CoordinateMenu( RootMenu ) --R2.1 Fixes issue #424. +COORDINATE:MenuSystem( System ) --R2.1 Fixes issue #424. +COORDINATE:MenuLL_Accuracy( LL_Accuracy ) --R2.1 Fixes issue #424. +COORDINATE:MenuLL_DMS( LL_DMS ) --R2.1 Fixes issue #424. +COORDINATE:MenuMGRS_Accuracy( MGRS_Accuracy ) --R2.1 Fixes issue #424. + +SET_BASE:FilterDeads() --R2.1 allow deads to be filtered to automatically handle deads in the collection. +SET_BASE:FilterCrashes() --R2.1 allow crashes to be filtered to automatically handle crashes in the collection. + +SET_UNIT:ForEachUnitPerThreatLevel( FromThreatLevel, ToThreatLevel, IteratorFunction, ... ) --R2.1 Threat Level implementation + +SET_CARGO:New() --R2.1 +SET_CARGO:AddCargosByName( AddCargoNames ) --R2.1 +SET_CARGO:RemoveCargosByName( RemoveCargoNames ) --R2.1 +SET_CARGO:FindCargo( CargoName ) --R2.1 +SET_CARGO:FilterCoalitions( Coalitions ) --R2.1 +SET_CARGO:FilterTypes( Types ) --R2.1 +SET_CARGO:FilterCountries( Countries ) --R2.1 +SET_CARGO:FilterPrefixes( Prefixes ) --R2.1 +SET_CARGO:FilterStart() --R2.1 +SET_CARGO:AddInDatabase( Event ) --R2.1 +SET_CARGO:FindInDatabase( Event ) --R2.1 +SET_CARGO:ForEachCargo( IteratorFunction, ... ) --R2.1 +SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) --R2.1 +SET_CARGO:IsIncludeObject( MCargo ) --R2.1 +SET_CARGO:OnEventNewCargo( EventData ) --R2.1 +SET_CARGO:OnEventDeleteCargo( EventData ) --R2.1 SpawnStatic.lua (5 matches) + +SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, CountryID ) --R2.1 +SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, CountryID ) --R2.1 +SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1 +SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName ) --R2.1 + +ZONE_BASE:GetCoordinate( Height ) --R2.1 + +DESIGNATE:SetFlashStatusMenu( FlashMenu ) --R2.1 +DESIGNATE:SetLaserCodes( LaserCodes ) --R2.1 +DESIGNATE:GenerateLaserCodes() --R2.1 +DESIGNATE:SetAutoLase( AutoLase ) --R2.1 +DESIGNATE:SetThreatLevelPrioritization( Prioritize ) --R2.1 + +DETECTION_BASE:CleanDetectionItems() --R2.1 Clean the DetectionItems list +DETECTION_BASE:GetDetectedItemID( Index ) --R2.1 +DETECTION_BASE:GetDetectedID( Index ) --R2.1 +DETECTION_AREAS:DetectedReportDetailed() --R2.1 Fixed missing report + +REPORT:HasText() --R2.1 +REPORT:SetIndent( Indent ) --R2.1 +REPORT:AddIndent( Text ) --R2.1 + +MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure + +TASK:SetMenu( MenuTime ) --R2.1 Mission Reports and Task Reports added. Fixes issue #424. +TASK:ReportSummary() --R2.1 fixed report. Now nicely formatted and contains the info required. +TASK:ReportOverview() --R2.1 fixed report. Now nicely formatted and contains the info required. +TASK:GetPlayerCount() --R2.1 Get a count of the players. +TASK:GetPlayerNames() --R2.1 Get a map of the players. +TASK:ReportDetails() --R2.1 fixed report. Now nicely formatted and contains the info required. + +UTILS.tostringMGRS = function(MGRS, acc) --R2.1 + +POSITIONABLE:GetBoundingBox() --R2.1 +POSITIONABLE:GetHeight() --R2.1 +POSITIONABLE:GetMessageText( Message, Name ) --R2.1 added +POSITIONABLE:GetMessage( Message, Duration, Name ) --R2.1 changed callsign and name and using GetMessageText +POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Name ) --R2.1 +POSITIONABLE:GetRadio() --R2.1 +POSITIONABLE:GetBeacon() --R2.1 +POSITIONABLE:LaseUnit( Target, LaserCode, Duration ) --R2.1 +POSITIONABLE:LaseOff() --R2.1 +POSITIONABLE:IsLasing() --R2.1 +POSITIONABLE:GetSpot() --R2.1 +POSITIONABLE:GetLaserCode() --R2.1 + +UNIT:IsDetected( TargetUnit ) --R2.1 +UNIT:IsLOS( TargetUnit ) --R2.1 diff --git a/Utils/7-Zip/7-zip.chm b/Utils/7-Zip/7-zip.chm new file mode 100644 index 000000000..1dd03b8e3 Binary files /dev/null and b/Utils/7-Zip/7-zip.chm differ diff --git a/Utils/7-Zip/7-zip.dll b/Utils/7-Zip/7-zip.dll new file mode 100644 index 000000000..c06074bc5 Binary files /dev/null and b/Utils/7-Zip/7-zip.dll differ diff --git a/Utils/7-Zip/7-zip32.dll b/Utils/7-Zip/7-zip32.dll new file mode 100644 index 000000000..c5d427fc5 Binary files /dev/null and b/Utils/7-Zip/7-zip32.dll differ diff --git a/Utils/7-Zip/7z.dll b/Utils/7-Zip/7z.dll new file mode 100644 index 000000000..042cbf1d4 Binary files /dev/null and b/Utils/7-Zip/7z.dll differ diff --git a/Utils/7-Zip/7z.exe b/Utils/7-Zip/7z.exe new file mode 100644 index 000000000..c8feeddf8 Binary files /dev/null and b/Utils/7-Zip/7z.exe differ diff --git a/Utils/7-Zip/7z.sfx b/Utils/7-Zip/7z.sfx new file mode 100644 index 000000000..5a8b5f662 Binary files /dev/null and b/Utils/7-Zip/7z.sfx differ diff --git a/Utils/7-Zip/7zCon.sfx b/Utils/7-Zip/7zCon.sfx new file mode 100644 index 000000000..308663917 Binary files /dev/null and b/Utils/7-Zip/7zCon.sfx differ diff --git a/Utils/7-Zip/7zFM.exe b/Utils/7-Zip/7zFM.exe new file mode 100644 index 000000000..237e97eae Binary files /dev/null and b/Utils/7-Zip/7zFM.exe differ diff --git a/Utils/7-Zip/7zG.exe b/Utils/7-Zip/7zG.exe new file mode 100644 index 000000000..0442d42e3 Binary files /dev/null and b/Utils/7-Zip/7zG.exe differ diff --git a/Moose Mission Setup/Moose Mission Update/History.txt b/Utils/7-Zip/History.txt similarity index 99% rename from Moose Mission Setup/Moose Mission Update/History.txt rename to Utils/7-Zip/History.txt index 677d35b55..89702db1f 100644 --- a/Moose Mission Setup/Moose Mission Update/History.txt +++ b/Utils/7-Zip/History.txt @@ -1,6 +1,19 @@ HISTORY of the 7-Zip -------------------- +16.04 2016-10-04 +------------------------- +- The bug was fixed: 7-Zip 16.03 exe installer under Vista didn't create + links in Start / Programs menu. +- Some bugs were fixed in RAR code. + + +16.03 2016-09-28 +------------------------- +- Installer and SFX modules now use some protection against DLL preloading attack. +- Some bugs were fixed in 7z, NSIS, SquashFS, RAR5 and another code. + + 16.02 2016-05-21 ------------------------- - 7-Zip now can extract multivolume ZIP archives (z01, z02, ... , zip). diff --git a/Utils/7-Zip/Lang/af.txt b/Utils/7-Zip/Lang/af.txt new file mode 100644 index 000000000..c578d4530 --- /dev/null +++ b/Utils/7-Zip/Lang/af.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.09 : Petri Jooste +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Afrikaans +Afrikaans +401 +OK +Kanselleer + + + +&Ja +&Nee +A&fsluit +Hulp + +&Gaan voort +440 +Ja vir &almal +Nee vir a&lmal +Stop +Herbegin +&Agtergrond +&Voorgrond +&Wag +Wagtend +Is u seker dat u wil kanselleer? +500 +&Lêer +R&edigeer +&Vertoon +G&unstelinge +&Gereedskap +&Hulp +540 +&Open +Open &Binne +Open B&uite +&Wys +R&edigeer +Her&noem +&Kopieer na... +&Verskuif na... +Ve&rwyder +Ver&deel lêer... +Kom&bineer lêers... +E&ienskappe +Komme&ntaar + + +Maak gids +Maak lêer +A&fsluit +600 +Selekteer &alles +Deselekteer a&lles +Keer &seleksie om +Selekteer... +Deselekteer... +Selekteer op Soort +Deselekteer op Soort +700 +&Groot ikone +&Klein ikone +&Lys +&Detail +730 +Ongesorteer + +&2 Panele +&Nutsbalke +Maak wortelgids oop +Een vlak hoër +Gidse geskiedenis... +&Verfris +750 +Argiveernutsbalk +Standaardnutsbalk +Groot knoppies +Wys teks op knoppies +800 +Voeg gids by gunstelinge &as +Boekmerk +900 +&Opsies... +&Normtoetsing +960 +&Inhoud... +&Aangaande 7-Zip... +1003 +Pad +Naam +Uitgang +Gids +Grootte +Ingepakte grootte +Kenmerke +Gemaak +Laatste toegang +Gewysig +Kompak +Kommentaar +Versleuteld +Verdeel Voor +Verdeel Na +Woordeboek +CRC +Tipe +Anti +Metode +Gasheer BS +Lêersstelsel +Gebruiker +Groep +Blok +Kommentaar +Posisie + + + + + + + + + + + + + + + + + + + + + + + + + +Fout +Totale grootte +Vrye ruimte +Klustergrootte +Etiket +Plaaslike naam +Verskaffer +2100 +Opsies +Taal +Taal: +Redigeerder +R&edigeerder: + +2200 +Stelsel +Assosieer 7-Zip met: +2301 +Integreer 7-Zip in kontekskeuselys. +Trapsgewyse kontekskeuselys +Kontekskeuselysitems: +2320 + + +Open +Pak lêers uit... +Voeg by argief... +Toets argief +Pak hier uit +Pak uit in {0} +Voeg by {0} +Saampers en e-pos... +Pers saam in {0} en e-pos +2400 +Gidse +&Werkgids +&TEMP-gids van het stelsel +&Huidige gids +&Gespesifiseerde gids: +Gebruik slegs vir verwisselbare media. +Spesifiseer die stoorplek vir tydelyke argieflêers. +2500 +Instellings +Wys ".." &item +Wys &ware lêerikone +Wys &stelselkeuselys +Wys seleksie oor &hele ry(e) +Wys &roosterlyne + + + +2900 +Aangaande 7-Zip +7-Zip is gratis programmatuur. Indien u egter so baie van 7-Zip hou dat u die verdere ontwikkeling wil ondersteun, registreer dan asb. met 'n geldelike donasie aan die 7-Zip Projek. +3000 + +Daar is geen foute nie +{0} objekt(e) is geselekteer +Kan gids '{0}' nie maak nie +Bywerk-funksie word vir hierdie argief nie ondersteun nie. + + + + +Lêer '{0}' is gewysig.\nWil u dit bywerk in die argief? +Kan lêer\n'{0}' nie bywerk nie +Redigeerder nie aan die gang gesit word nie. + + + + +Te veel items +3300 +Besig om uit te pak +Besig met saampersing +Besig om te toets +Besig om oop te maak... + +3400 +&Uitpak +U&itpak in: +Gee 'n bestemming vir uitgepakte lêers. +3410 +Pad-metode +Volledige padname +Geen padname +3420 +Vervang lêers +Vervang slegs met bevestiging +Vervang sonder bevestiging +Slaan bestaande lêers oor +Hernoem outomaties +Hernoem bestaande lêers outomaties +3500 +Bevestig lêeroorskrywing +Doelgids bevat alreeds 'n lêer met hierdie naam. +Wil u die bestaande lêer vervang +deur hierdie lêer ? +{0} grepe +O&utomaties hernoem +3700 +Nie-ondersteunde saampersmetode vir '{0}'. +Datafout in '{0}'. Lêer is beskadig. +CRC het misluk in '{0}'. Lêer is beskadig. + + +3800 +Tik wagwoord in +Tik wagwoord in: + +&Wys wagwoord + + + +&Wagwoord +3900 +Tydsduur sovêr: +Oorblywende tyd: +Grootte: +Spoed: + + +Foute: + +4000 +Voeg by argief +&Argief: +&Bywerkwyse: +Argie&fformaat: +Saampersingv&lak: +&Saampersmetode: +&Woordeboekgrootte: +&Woordgrootte: + + +Pa&rameters: +Opsies +Maak SF&X argief + + + +Enkripteer lêer&name +Geheuegebruik vir saampersing: +Geheuegebruik vir uitpakking: +4050 +Stoor +Vinnigste +Vinnig +Normaal +Maksimum +Ultra +4060 +Lêers byvoeg en vervang +Lêers bywerk en byvoeg +Verfris bestaande lêers +Sinkroniseer lêers +4070 +Blaai +Alle lêers + + +6000 +Kopieer +Verskuif +Kopieer na: +Verskuif na: +Besig met kopiëring... +Besig met verskuiwing... +Besig met hernoeming... + +Bewerking word nie ondersteun nie. +Fout by hernoeming van lêer of gids + + +6100 +Bevestig lêerverwydering +Bevestig gidsverwydering +Bevestig verwydering van meerdere lêers +Is u seker dat u '{0}' wil verwyder? +Is u seker dat u gids '{0}' asook die inhoud daarvan wil verwyder? +Is u seker dat u hierdie {0} items wil verwyder? +Besig met verwydering... +Fout by verwydering van lêer of gids + +6300 +Maak gids +Maak lêer +Naam van die gids: +Lêernaam: +Nuwe gids +Nuwe lêer +Fout by maak van gids +Fout by maak van nuwe lêer +6400 + + +Selekteer +Deselekteer +Masker: +6600 + +Gidse-geskiedenis +Diagnostiese boodskappe +Boodskap +7100 +Rekenaar +Network + +Stelsel +7200 +Voeg by +Pak uit +Toets +Kopiëer +Skuif +Vee uit +Intigting +7300 +Verdeel lêer +&Verdeel na: +Verdeel in &volumes, aantal grepe: +Besig met verdeling... + + + + + +7400 +Kombineer lêers +&Kombineer na: +Besig met kombinering... + + + +7500 + + + + +7600 +Meet +Geheuegebruik: +Inpakking +Uitpakking +Gradering +Totale gradering +Huidige +Resultaat + + +Lopies: diff --git a/Utils/7-Zip/Lang/an.txt b/Utils/7-Zip/Lang/an.txt new file mode 100644 index 000000000..6264849e9 --- /dev/null +++ b/Utils/7-Zip/Lang/an.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; : Feliciano Martínez Tur +; 9.07 : Juan Pablo Martínez +; +; +; +; +; +; +; +; +; +0 +7-Zip +Aragonese +Aragonés +401 +Acceptar +Cancelar + + + +&Sí +&No +&Zarrar +Aduya + +&Continar +440 +Sí a &tot +No a t&ot +Aturar +Tornar a empecipiar +Se&gundo plano +P&rimer plano +&Pausa +Aturau +Yes seguro que quiers cancelar? +500 +&Fichero +&Editar +&Veyer +&Favoritos +&Ferramientas +Ad&uya +540 +&Ubrir +Ubrir &adintro +Ubrir &difuera +&Veyer +&Editar +Re&nombrar +&Copiar en... +&Mover ta... +&Borrar +Di&vidir o fichero... +C&ombinar os fichers... +&Propiedatz +Comen&tario +Calcular a suma de comprebación +Diff +Creyar carpeta +Creyar fichero +&Salir +600 +Seleccionar-lo &tot +Deseleccionar-lo tot +&Invertir selección +Seleccionar... +Deseleccionar... +Seleccionar por tipo +Deseleccionar por tipo +700 +Iconos g&rans +&Iconos chicotz +&Lista +&Detalles +730 +Desordenau +Anvista plana +&2 panels +&Barras de ferramientas +Ubrir a carpeta radiz +Carpeta mai +Historial de carpetas... +&Esviellar +750 +Barra de ferramientas d'archivo +Barras de ferramientas estandard +Botons grans +Amostrar texto en os botons +800 +&Adhibir carpeta a favoritos como +Adhibir a favoritos +900 +&Opcions... +&Prebas de referencia (benchmark) +960 +&Conteniu... +A&rredol de 7-Zip... +1003 +Rota +Nombre +Tipo de fichero +Carpeta +Grandaria +Grandaria comprimida +Atributos +Creyau +Zaguer acceso +Zaguera modificación +Compacto +Comentario +Zifrau +Expandiu antis +Expandiu dimpués +Diccionario +CRC +Tipo +Anti +Metodo +SO d'orichen +Sistema de fichers +Usuario +Grupo +Bloque +Comentario +Posición +Prefixo de rota +Carpeta +Fichers +Versión +Fragmento +Multiframento +Desplazamiento +Vinclos +Bloques +Fragmentos + +64-bit +Big-endian +CPU +Grandaria fisica +Grandaria d'as cabeceras +Suma de comprebación +Caracteristicas +Adreza virtual +ID +Nombre curto +Aplicación creyadera +Grandaria de sector +Modo +Vinclo +Error +Espacio total +Espacio libre +Grandaria de sector +Etiqueta +Nombre local +Proveyedor +2100 +Opcions +Luenga +Luenga: +Editor +&Editor: +&Diff: +2200 +Sistema +Asociar 7-Zip con: +2301 +Integrar 7-Zip en o menú contextual de Windows +Menú contextual en cascada +Elementos d'o menú contextual: +2320 + + +Ubrir archivo +Extrayer-ne os fichers... +Adhibir a l'archivo... +Comprebar l'archivo +Extrayer aquí +Extrayer en {0} +Adhibir a {0} +Comprimir y ninviar por correu... +Comprimir en {0} y ninviar por correu +2400 +Carpeta +Carpeta de &treballo +Carpeta temporal d'o &sistema +Carpeta &actual +&Especificar una carpeta: +No emplegar que ta dispositivos extrayibles +Especificar una carpeta ta archivos temporals. +2500 +Propiedatz +Amostrar l'elemento ".." +Amostrar iconos propios +Amostrar o menú d'o sistema +&Seleccionar ringlera(s) completa(s) +Amostrar as linias d'a &quadricla +Clicar una vegada ta ubrir elemento +Modo de selección &alternativo +Emplegar pachinas de memoria &grans +2900 +Arredol de 7-Zip +7-Zip ye un programa libre y gratuito. Si quiers, puetz colaborar en o desembolique de 7-Zip rechistrando-te ta contribuyir a amillorar o programa. +3000 +O sistema no ha puesto asignar a cantidat necesaria de memoria +No i hai errors +{0} elemento(s) seleccionau(s) +No se puet creyar a carpeta '{0}' +Ista mena d'archivo no permite actualización. +No se puet ubrir o fichero '{0}' como archivo comprimiu +No se puet ubrir l'archivo zifrau '{0}'. Comprebe si a clau ye incorrecta. +Tipo d'archivo no admeso +O fichero {0} ya existe +O fichero '{0}' s'ha modificau.\nQuiers esviellar-lo en l'archivo? +No se puet esviellar o fichero\n'{0}' +No se puet executar l'editor. +O fichero pareix un virus (o nombre d'o fichero contiene espacios largos). +No se puet execitar ista operación dende una carpeta que tienga una rota larga. +Has de seleccionar un fichero +Has de seleccionar un u más fichers +Masiaus elementos +3300 +Extrayendo +comprimindo +Prebando +Ubrindo... +Buscando... +3400 +Extrayer +E&xtrayer a: +Selecciona un destín ta os fichers extrayius. +3410 +Modo de rota +Rotas completas +Sin rotas +3420 +Modo de sobrescritura +Con confirmación +Sin confirmación +Conservar os fichers ya existents +Renombrar automaticament +Renombrar automaticament os fichers ya existents +3500 +Confirmar a substitución de fichers +A carpeta de destín ya contiene un fichero con o mesmo nombre. +Quiers substituyir o fichero existent +por iste atro? +{0} bytes +Renombrar a&utomaticament +3700 +Metodo de compresión no valido ta '{0}'. +Error de datos en '{0}'. O fichero ye corrompiu. +O CRC ha fallau en '{0}'. O fichero ye corrompiu. +Error de datos en o fichero zifrau '{0}'. Verifica a clau. +Error de CRC en o fichero zifrau '{0}'. Verifica a clau. +3800 +Escribe a clau +Escribe a clau: +Torne a escribir a clau: +&Amostrar a clau +As claus son diferents. Por favor, torne a escribir-la. +Emplega en a clau nomás as letras de l'alfabeto anglés, numeros y caracters especials (!, #, $, ...) +A clau ye masiau larga. +Contrasenya +3900 +Tiempo transcorriu: +Tiempo pendient: +Grandaria: +Velocidat: +Procesau: +Razón de compresión: +Errors: +Archivos: +4000 +Adhibir a l'archivo +&Archivo: +M&odo d'actualización: +&Formato d'archivo: +&Libel de compresión: +&Metodo de compresión: +Grandaria de &diccionario: +Granda&ria d'a parola: +Grandaria de bloque compacto: +Numero de filos d'a CPU: +&Parametros: +Opcions +Creyar archivo SF&X (autoextrayible) +Comprimir fichers compartius +Encriptación +Metodo d'e &zifrau: +Zifrar &nombres de fichero +Memoria emplegada ta comprimir: +Memoria emplegada ta descomprimir: +4050 +Sin compresión +A mas rapida +Rapida +Normal +Maxima +Ultra +4060 +Adhibir y substituyir fichers +Esviellar y adhibir-ie fichers +Esviellar fichers ya presents +Sincronizar fichers +4070 +Explorar +Totz os fichers +No compacto +Compacto +6000 +Copiar +Mover +Copiar en: +Mover ta: +Copiando... +Movendo... +Renombrando... +Selecciona a carpeta de destín +Operación no permitida. +Error en enombrar un fichero u carpeta +Confirmar a copia d'o fichero +Yes seguro de que quiers copiar os fichers en l'archivo +6100 +Confirmar borrau de fichero +Confirmar borrau de carpeta +Confirmar borrau multiple fichers +Yes seguro que quiers borrar '{0}'? +Yes seguro que quiers borrar a carpeta '{0}' y tot o suyo conteniu? +Yes seguro que quiers borrar istos {0} elementos? +Borrando... +Error borrando fichero u carpeta +O sistema no puet mover un fichero con rota larga ta la Papelera de Reciclache +6300 +Creyar carpeta +Creyar fichero +Nombre de'a carpeta: +Nombre de'o fichero: +Carpeta nueva +Fichero nuevo +Error en creyar carpeta +Error en creyar o fichero: +6400 +Comentario +&Comentario: +Seleccionar +Deseleccionar +Patrón: +6600 +Propiedatz +Historial de carpetas +Mensaches de diagnostico +Mensache +7100 +O mío ordinador +Entorno de ret +Documentos +Sistema +7200 +Adhibir +Extrayer +Prebar +Copiar +Mover +Borrar +Información +7300 +Dividir fichero +Di&vidir a: +Dividir en fra&gmentos (bytes): +Dividindo... +Confirmar a división +Yes seguro que quiers dividir o fichero en {0} fragmentos? +A grandaria d'os fragmentos ha d'estar menor que a d'o fichero orichinal +Grandaria de fragmento incorrecta +Grandaria de fragmento especificada: {0} bytes.\nYe seguro que quiere dividir o fichero en fragmentos d'ixa grandaria? +7400 +Combinar fichers +&Combinar en: +Combinando... +Selecciona només o primer fichero +No s'ha puesto detectar o fichero como parti d'un fichero por fragmentos +No s'ha puesto trobar que un fragmento d'o fichero por fragmentos +7500 +Calculando a suma de verificación... +Suma de verificación (CRC) +CRC d'os datos: +CRC d'os datos y os nombres: +7600 +Prebas de referencia (benchmark) +Emplego de memoria: +Compresión +Descompresión +Taxa +Taxa total +Actual +Resultant +Emplego de CPU +Taxa / Emplego +Pasadas: diff --git a/Utils/7-Zip/Lang/ar.txt b/Utils/7-Zip/Lang/ar.txt new file mode 100644 index 000000000..f62e23c62 --- /dev/null +++ b/Utils/7-Zip/Lang/ar.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 9.07 :ترجمةالاستاذ:عوض ال-عائض الغامدي +; 9.07 : Awadh A Al-Ghaamdi +; +; 15.00 : 2016-08-28 : تعديل وتصحيح الترجمة: سي٠حسام الاسدي +; 15.00 : 2016-08-28 : Saif H Al-asadi (edited and corrected) +; +; +; +; +; +; +0 +7-Zip +Arabic +عربي +401 +حسنا +إلغاء الأمر + + + +&نعم +&لا +&إغلاق +المساعدة + +&استمرار +440 +نعم للكل +لا للكل +إيقا٠+إعادة تشغيل +&بالخلÙية +&بالمقدمة +&إيقا٠مؤقت +متوق٠مؤقتا +هل أنت متأكد من الإلغاء؟ +500 +&المل٠+&التعديل +&العرض +التÙ&ضيلات +&ألادوات +&المساعدة +540 +&ÙØªØ­ +&Ø§ÙØªØ­ بالداخل +&Ø§ÙØªØ­ للخارج +&عرض +&تعديل +إعادة التسمية +..نسخ إلى +..نقل إلى +&حذ٠+&تقسيم الملÙ... +دم&ج Ø§Ù„Ù…Ù„ÙØ§Øª... +الخ&صائص +تعليق& +حساب قيمة التدقيق +ÙØ±Ù‚ +إنشاء مجلد +إنشاء مل٠+خروج +الربط +&الجداول البديلة +600 +تحديد الكل +عدم تحديد الكل +عكس التحديد +تحديد... +عدم تحديد... +تحديد حسب النوع +عدم تحديد حسب النوع +700 +رموز كبيرة +رموز صغيرة +&القائمة +&Ø§Ù„ØªÙØ§ØµÙŠÙ„ +730 +غير مرتب +العرض الاÙقي +&مجموعتين +&أشرطة الأدوات +ÙØªØ­ المجلد الرئيسي +للأعلى بمستوى واحد +...محÙوظات المجلدات +&تحديث +تحديث تلقائي +750 +شريط أدوات Ø§Ù„Ø£Ø±Ø´ÙØ© +شريط ألادوات قياسي +أزرار كبيرة +إظهار تسميات الازرار +800 +&Ø¥Ø¶Ø§ÙØ© المجلد Ù„Ù„Ù…ÙØ¶Ù„Ø© باسم +العلامات المرجعية +900 +...&الخيارات +&معالج الاداء +960 +..&المحتويات +7-Zip &حول... +1003 +المسار +الإسم +الإمتداد +المجلد +الحجم +الحجم المضغوط +الخواص +تم انشاءه +تم الدخول +تم تعديله +ثابت +تم التعيلق +Ù…Ø´ÙØ± +التقسيم قبل +التقسيم بعد +القاموس + +النوع +متعدد +الطريقة +نظام التشغيل +Ù…Ù„ÙØ§Øª النظام +المستخدم +المجموعة +التكتل +التعليق +الموقع +بادئة المسار +المجلدات +Ø§Ù„Ù…Ù„ÙØ§Øª +الإصدار +وحدة التخزين +وحدة تخزين متعددة +التعويض +روابط +كتل +وحدات تخزين + +64-بت +Big-endian +CPU +الحجم الطبيعي +حجم المقدمات +قيمة التحقق +المميزات +العنوان الظاهري +الهوية +إسم قصير +برنامج الانشاء +حجم القطاع +الوضع +الرابط الرمزي +خطأ +الحجم الكلي +المساحة الحرة +حجم الجزء +العلامة +الإسم المحلي +المجهز +NT أمان +الجداول البديلة +Aux +تم Ø­Ø°ÙØ© +Ø§Ù„ØªÙØ±Ø¹Ø§Øª + + +خطأ ÙÙŠ النوع +اخطاء +اخطاء +تحذيرات +تحذير +الجداول +الجداول البديلة +حجم الجداول البديلة +الحجم الظاهري +حجم الاخراج +الحجم الطبيعي الكلي +دليل الحجم +النوع Ø§Ù„ÙØ±Ø¹ÙŠ +تعليق قصير +رمز Ø§Ù„ØµÙØ­Ø© + + + +حجم التذييل +حجم العقبات المضمن +الارتباط +الارتباط الصعب +iNode + +للقراءة-Ùقط +2100 +الخيارات +اللغة +ترجمةالأستاذ عوض الغامدي -التعديل سي٠الاسدي +المحرر +&المحرر: +ال&ÙØ±Ù‚: +2200 +النظام +تكامل البرنامج مع : +جميع المستخدمين +2301 +الإندماج مع قائمة الزر الايمن +تتالي قائمة الزر الايمن +عناصر قائمة الزر الايمن : +الايقونات ÙÙŠ قائمة الزر الايمن +2320 +<مجلد> +<إرشيÙ> +ÙØªØ­ ارشي٠+استخراج Ø§Ù„Ù…Ù„ÙØ§Øª... +Ø¥Ø¶Ø§ÙØ© إلى الإرشيÙ... +ÙØ­Øµ الإرشي٠+إستخرج هنا +إستخرج إلى {0}Ù€ +Ø¥Ø¶Ø§ÙØ© إلى {0}Ù€ +ضغط ثم إرسال... +ضغط إلى {0} ثم إرسال +2400 +مجلدات +&مجلد العمل +&مجلد النظام المؤقت +&الحالي +&محدد: +الإستخدام Ùقط للأقراص المتحركة +تحديد موضع للإرشي٠المؤقت Ù„Ù„Ù…Ù„ÙØ§Øª +2500 +الاعدادات +".." إظهار عنصر +إظهار أيقونات Ø§Ù„Ù…Ù„Ù Ø§Ù„ÙØ¹Ù„ية +إظهار قائمة النظام +&تحديد الص٠بالكامل +إظهار خطوط الشبكة +نقرة واحدة Ù„ÙØªØ­ أي مادة +&نظام تحديد بديل +إستعمال &ØµÙØ­Ø§Øª ذاكرة كبيرة +2900 +حول البرنامج +7-Zip البرنامج مجاني +3000 +لا يمكن للنظام تخصيص الكمية المطلوبة للذاكرة +لا يوجد أخطاء +تم تحديد {0} عنصر +'{0}' لا يمكن إنشاء المجلد +تحديث العمليات غير مدعوم لهذا الإرشي٠+كأرشي٠'{0}' لا يمكن ÙØªØ­ المل٠+كلمة المرور خطأ ØŸ . '{0}' تعذر ÙØªØ­ Ø§Ù„Ø£Ø±Ø´ÙŠÙ Ø§Ù„Ù…Ø´ÙØ± +نوع أرشي٠غير معتمد +موجود مسبقا {0} المل٠+أتريد تحديثه ÙÙŠ الإرشي٠؟\nالمل٠'{0}' تم تعديله +لا يمكن تحديث الملÙ\n'{0}' +لا يمكن بدء المحرر +(المل٠يشبه Ùيروس (إسم المل٠يحتوي على Ù…Ø³Ø§ÙØ§Øª طويلة +لا يمكن إستدعاء العملية من مجلد له مسار طويل +يجب تحديد مل٠واحد +يجب تحديد مل٠واحد أو أكثر +عناصر كثيرة جداً +لايمكن ÙØªØ­ المل٠{0} كأرشي٠+Ø§Ù„Ù…Ù„Ù Ù…ÙØªÙˆØ­ {0} كأرشي٠+ان Ø§Ù„Ø§Ø±Ø´ÙŠÙ Ù…ÙØªÙˆØ­ بطريقة الطباعة Ø¨Ø§Ù„Ø§ÙˆÙØ³ÙŠØª +3300 +استخراج +الضغط +ÙØ­Øµ +...Ùـتـح... +...يتم Ø§Ù„ØªÙØ­Øµ +تتم الازالة +3320 +يتم Ø§Ù„Ø§Ø¶Ø§ÙØ© +يتم التحديث +يتم التحليل +يتم التكرار +يتم اعادة الضغط +يتم التجاهل +يتم الحذ٠+يتم انشاء المقدمة +3400 +استخراج +استخراج إلى: +تحديد الموضع Ù„Ù„Ù…Ù„ÙØ§Øª المستخرجة +3410 +حالة المسار +اسم المسار بالكامل +بدون اسم المسار +اسم المسار المطلق +اسم المسار النسبي +3420 +حالة إعادة الكتابة +السؤال قبل إعادة الكتابة +إعادة الكتابة بدون تنبيه +تجاوز Ø§Ù„Ù…Ù„ÙØ§Øª Ø§Ù„Ù…ØªÙˆÙØ±Ø© +تسمية تلقائية +تسمية Ø§Ù„Ù…Ù„ÙØ§Øª الموجودة تلقائياً +3430 +ازالة تكرارات المجلد الرئيسي +استعادة مل٠الامان +3500 +تأكيد استبدال المل٠+المجلد الهد٠يحوي مسبقاً المل٠+أتريد استبدال المل٠الموجود؟ +بهذا Ø§Ù„Ù…Ù„ÙØŸ +{0} بايت +إعادة تسمية تلقائية +3700 +'{0}'طريقة ضغط غير معتمدة لـ +'{0}'خطأ بيانات ÙÙŠ. المل٠تال٠+'{0}' خطأ بصمة ÙÙŠ . المل٠تال٠+خطأ بيانات ÙÙŠ Ø§Ù„Ù…Ù„Ù Ø§Ù„Ù…Ø´ÙØ± '{0}' . كلمة المرور غير صحيحة ØŸ +ÙØ´Ù„ Ø§Ù„ÙØ­Øµ الدوري ÙÙŠ Ø§Ù„Ù…Ù„Ù Ø§Ù„Ù…Ø´ÙØ± '{0}' . كلمة المرور غير صحيحة ØŸ +3710 +كلمة مرور خاطئة? +3721 +طريقة ضغط غير مدعومة +بيانات خطأ +CRC ÙØ´Ù„ +بيانات غير متاحة +النهاية Ø§Ù„Ù…ÙØ§Ø¬Ø¦Ø© للبيانات +هناك بعض البيانات بعد نهاية بيانات الحمولة +ليس Ø§Ø±Ø´ÙŠÙØ§Ù‹ +مقدمات خطأ +كلمة مرور خاطئة +3763 +البداية غير Ù…ØªÙˆÙØ±Ø© للارشي٠+البداية غير مؤكدة للارشي٠+ + + +ميزة غير مدعومة +3800 +أدخل كلمة المرور +أدخل كلمة المرور: +إعادة كلمة المرور : +&إظهار كلمة المرور +كلمتا المرور غير متطابقتان +أستخدم Ùقط حرو٠إنجليزية Ùˆ أرقام Ùˆ علامات خاصة (!,#,,$ ...) لكلمة المرور +كلمة المرور طويلة جدا +كلمة المرور +3900 +الزمن المنقضي: +الزمن المتبقي: +الحجم الكلي: +السرعة: +المعالج: +نسبة الضغط: +الأخطاء: +Ù…Ù„ÙØ§Øª الإرشيÙ: +4000 +Ø¥Ø¶Ø§ÙØ© للإرشي٠+&الإرشيÙ: +&حالة التحديث: +صيغة الإرشيÙ: +مستوى الضغط&: +طريقة الضغط: +&حجم المعجم: +&حجم الكلمة: +حجم الكتلة الخاصة : +CPU عدد مؤشرات الـ : +&أوامر إضاÙية: +الخيارات +إنشاء إرشي٠ذاتي الإستخراج +ضغط Ø§Ù„Ù…Ù„ÙØ§Øª المشاركة +التشÙير +طريقة التشÙير : +تشÙير أسماء ال&Ù…Ù„ÙØ§Øª +إستهلاك الذاكرة للضغط : +إستهلاك الذاكرة لإلغاء الضغط : +Ø­Ø°Ù Ø§Ù„Ù…Ù„ÙØ§Øª بعد الضغط +4040 +خزن الاتباطات الرمزية +خزن الارتباطات الصعبة +خزن جداول البيانات البديلة +خزن مل٠الامان +4050 +للخزن +ألاسرع +السريع +الطبيعي +Ø§Ù„Ù…Ø±ØªÙØ¹ +ألاقصى +4060 +Ø¥Ø¶Ø§ÙØ© واستبدال Ø§Ù„Ù…Ù„ÙØ§Øª +تحديث Ùˆ Ø¥Ø¶Ø§ÙØ© Ø§Ù„Ù…Ù„ÙØ§Øª +تجديد Ø§Ù„Ù…Ù„ÙØ§Øª Ø§Ù„Ù…ØªÙˆÙØ±Ø© Ùقط +مزامنة Ø§Ù„Ù…Ù„ÙØ§Øª +4070 +إستعراض +ÙƒØ§ÙØ© Ø§Ù„Ù…Ù„ÙØ§Øª +غير خالص +خالص +6000 +نسخ +نقل +نسخ إلى: +نقل إلى: +...نـسـخ... +...نـقـل... +إعادة تسمية... +تحديد مجلد الوجهة +العملية غير معتمدة +حدوث خطأ لدي إعادة تسمية المل٠أو المجلد +تأكيد نسخ المل٠+هل أنت متأكد من نسخ Ø§Ù„Ù…Ù„ÙØ§Øª Ù„Ù„Ø¥Ø±Ø´ÙŠÙØŸ +6100 +تأكيد الحذ٠للمل٠+تأكيد الحذ٠للمجلد +تأكيد Ø§Ù„Ø­Ø°Ù Ù„Ù„Ù…Ù„ÙØ§Øª +'ØŸ {0}'هل أنت متأكد من حذ٠+هل أنت متأكد من حذ٠المجلد '{0}' Ùˆ ÙƒØ§ÙØ© محتوياته ØŸ +ØŸ {0} هل أنت متأكد من حذ٠العناصر التالية +حذÙ... +حدوث خطأ لدي حذ٠المل٠أو المجلد +لا يمكن للنظام نقل مل٠له مسار طويل إلى سلة Ø§Ù„Ù…Ø­Ø°ÙˆÙØ§Øª +6300 +إنشاء مجلد +إنشاء مل٠+اسم المجلد: +اسم الملÙ: +مجلد جديد +مل٠جديد +حدوث خطأ لدي إنشاء المجلد +حدوث خطأ لدي إنشاء المل٠+6400 +تعليق +&تعليق: +تحديد +عدم تحديد +Ø¥Ø®ÙØ§Ø¡: +6600 +خصائص +محÙوظات المجلدات +رسائل Ø§Ù„ÙØ§Ø­Øµ +رسالة +7100 +جهاز الكمبيوتر +الشبكة +المستندات +النظام +7200 +Ø¥Ø¶Ø§ÙØ© +إستخراج +ÙØ­Øµ +نسخ +نقل +حذ٠+معلومات +7300 +تقسيم المل٠+&التقسيم إلى: +التقسيم لكتل أو بايتات: +تقسيم... +تأكيد التقسيم +هل تريد ÙØ¹Ù„ا تقسيم المل٠إلى {0} كتلة ØŸ +يجب أن يكون حجم الكتلة أصغر من حجم المل٠الأصلي +كتلة الحجم غير صحيحة +حجم الكتلة المعين : {0} بايت\nهل تريد ÙØ¹Ù„ا تقسيم الأرشي٠إلى مثل هذه الكتل ØŸ +7400 +دمج Ø§Ù„Ù…Ù„ÙØ§Øª +&دمج إلى: +دمج... +حدد Ùقط الجزء الأول من مل٠التقسيم +لا يمكن إكتشا٠المل٠كجزء من مل٠التقسيم +لا يمكن العثور على أكثر من جزء واحد من مل٠التقسيم +7500 +...يتم حساب مجموعة الإختبار +معلومات مجموعة الإختبار +للبيانات CRC مجموعة الإختبار: +للبيانات Ùˆ للأسماء CRC مجموعة الإختبار: +7600 +تقييم الأداء +إستخدام الذاكرة: +ضغط +ÙÙƒ ضغط +المعدل +المعدل الكلي +الحالي +الناتج +CPU إستهلاك الـ +التقدير / الإستهلاك +العمليات: +7700 +الرابط +الارتباط +ربط من: +ربط الى: +7710 +نوع الربط +ربط قوي +ربط المل٠الرمزي +ربط القاموس الرمزي +قاموس الربط diff --git a/Utils/7-Zip/Lang/ast.txt b/Utils/7-Zip/Lang/ast.txt new file mode 100644 index 000000000..623ac9063 --- /dev/null +++ b/Utils/7-Zip/Lang/ast.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.07 : Dinamiteru +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Asturian +Asturianu +401 +Val +Torgar + + + +&Si +&Non +&Zarrar +Axuda + +&Siguir +440 +Si a &Too +Non a T&oo +Parar +Reentamar +&De fondu +&En primer planu +&Posar +Posao +¿Tas fixu que quies paralo? +500 +F&icheru +&Remanar +&Ver +F&avoritos +&Ferramientes +A&xuda +540 +&Abrir +Abrir &Dientro +Abrir F&uera +&Ver +&Remanar +Reno&mar +&Copiar a... +&Mover a... +&Borrar +&Partir ficheru... +Com&binar ficheros... +P&ropiedaes +Come&ntariu + + +Crear carpeta +Crear ficheru +Co&lar +600 +Seleicionar &Too +Deseleicionar too +&Invertir seleición +Seleicionar... +Deseleicionar... +Seleicionar por Tipu +Deseleicionar por Tipu +700 +Miniatures &Grandes +&Miniatures Pequeñes +&Llista +&Detalles +730 +Ensín Ordenar + +&2 Paneles +&Barres de Ferramientes +Abrir Carpeta Raiz +Xubir Un Nivel +Hestorial de Carpetes... +Actualiza&r +750 +Barra Ferramientes d´Archivu +Barra Ferramientes Normal +Botones Grandes +Amosar Testu nos Botones +800 +&Añedir carpeta a Favoritos como +Marca +900 +&Opciones... +&Bancu de Pruebes +960 +&Conteníos... +&Al rodiu 7-Zip... +1003 +Ruta +Nome +Estensión +Carpeta +Tamañu +Tamañu comprimío +Atributos +Creao +Accedío +Cambiao +Sólidu +Comentao +Cifrao +Partir antes +Partir dempués +Diccionariu +CRC +Tipu +Anti +Métodu +S.O. d´Acoyida +Sistema de ficheros +Usuariu +Grupu +Bloque +Comentariu +Posición + + + + + + + + + + + + + + + + + + + + + + + + + +Error +Tamañu total +Espaciu llibre +Tamañu del clúster +Etiqueta +Nome llocal +Suministrador +2100 +Opciones +Llingua +Llingua: +Remanaor +&Remanaor: + +2200 +Sistema +Asociar 7-Zip con: +2301 +Integrar 7-Zip nel menú contestual +Menú contestual en ´cascada´ +Artículos del menú contestual: +2320 + + +Abrir archivu +Estrayer ficheros... +Añedir al archivu... +Probar archivu +Estrayer equí +Estrayer a {0} +Añedir a {0} +Comprimir y mandar per correu... +Comprimir en {0} y mandar per correu +2400 +Carpetes +&Carpeta de trabayu +Carpeta &temporal de sistema +&Actual +&Especificar: +Usar sólo pa dispositivos estrayibles +Especificar llocalización pa ficheros d´archivos temporales. +2500 +Igües +Amosar ".." artículu +Amosar les miniatures reales del ficheru +Amosar menú del sistema +Seleicionar tola &fila +Amosar les llinies de la &cuadrícula + + + +2900 +Al rodiu 7-Zip +7-Zip ye software llibre. De toos moos, tú pues sofitar el desendolcu de 7-Zip rexistrándote. +3000 + +Ensín errores +{0} oxetu(os) seleicionaos +Nun se puede crear la carpeta '{0}' +Esti archivu nun permite les operaciones d´actualización. + + + + +El ficheru '{0}' foi modificáu.\nDo ¿Quies actualizalu nel archivu? +Nun se pudo actualizar l´archivu\n'{0}' +Nun se pudo entamar el Remanaor. + + + + +Demasiaos artículos +3300 +Estrayendo +Comprimiendo +Probando +Abriendo... + +3400 +Estrayer +E&strayer a: +Especificar llocalización pa ficheros estrayíos. +3410 +Mou de ruta +Nomes de ruta completos +Ensín nomes de ruta +3420 +Mou de sobreescritura +Entrugar enantes de sobreescribir +Sobreescribir ensín confirmación +Dexar ficheros esistentes +Auto renomar +Auto renomar ficheros esistentes +3500 +Confirmar sustitución de ficheros +La carpeta destín yá tien el ficheru procesáu. +¿Quiés sustituyir el ficheru esistente +con esti otru? +{0} bytes +A&uto Renomar +3700 +Métodu de compresión nun permitíu pa '{0}'. +Error de datos en '{0}'. El ficheru ta rotu. +El CRC falló en '{0}'. El ficheru ta rotu. + + +3800 +Introduz clave +Introduz clave: + +Amo&sar clave + + + +Clave +3900 +Tiempu trescurríu: +Tiempu pa finar: +Tamañu: +Velocidá: + + +Errores: + +4000 +Añedir al archivu +&Archivu: +Mo&u d´actualización: +&Formatu del archivu: +Nive&l de compresión: +&Métodu de compresión: +Tamañu del &Diccionariu: +Tamañu de la pa&llabra: + + +&Parámetros: +Opciones +Crear archivu SF&X + + + +Cifrar &nomes de ficheru +Usu de memoria pa la compresión: +Usu de memoria pa la descompresión: +4050 +Nenguna +Más rápida +Rápida +Normal +Másima +Ultra +4060 +Añedir y sustituyir ficheros +Actualizar y añedir ficheros +Actualizar ficheros esistentes +Sincronizar ficheros +4070 +Agüeyar +Tolos ficheros + + +6000 +Copiar +Mover +Copiar a: +Mover a: +Copiando... +Moviendo... +Renomando... + +La operación nun tá permitía. +Error al renomar el ficheru o carpeta + + +6100 +Confirmar Borráu de Ficheru +Confirmar Borráu de Carpeta +Confirmar Borráu Múltiple de Ficheros +¿Tas fixu que quies borrar '{0}'? +¿Tas fixu que quies borrar la carpeta '{0}' y tolos sos conteníos? +¿Tas fixu que quies borrar estos {0} artículos? +Borrando... +Error al borrar el ficheru o carpeta + +6300 +Crear Carpeta +Crear ficheru +Nome de la carpeta: +Nome del ficheru: +Nueva carpeta +Nuevu ficheru +Error al crear la carpeta +Error al crear el ficheru +6400 +Comentariu +&Comentariu: +Seleicionar +Deseleicionar +Mazcarita: +6600 + +Hestorial de carpetes +Mensaxes de diagnósticu +Mensax +7100 +Ordenador +Rede de Trabayu + +Sistema +7200 +Añedir +Estrayer +Probar +Copiar +Mover +Borrar +Información +7300 +Partir Ficheru +&Partir a: +Partir en &cachos, bytes: +Partiendo... + + + + + +7400 +Combinar Ficheros +&Combinar a: +Combinando... + + + +7500 + + + + +7600 +Bancu de Pruebes +Usu de memoria: +Comprimiendo +Descomprimiendo +Valoración +Valoración total +Actual +Resultáu + + +Correutos: diff --git a/Utils/7-Zip/Lang/az.txt b/Utils/7-Zip/Lang/az.txt new file mode 100644 index 000000000..712b82833 --- /dev/null +++ b/Utils/7-Zip/Lang/az.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 9.07 : F@rhad +; 15.02 : 22/05/2015 : Г. ГаÑымов +; +; +; +; +; +; +; +; +; +0 +7-Zip +Azerbaijani +AzÉ™rbaycanca +401 +OLDU +İmtina + + + +&BÉ™li +&Xeyr +&BaÄŸla +KömÉ™k + +&Davam +440 +&Hamısına BÉ™li +Ha&mısına Xeyr +Dayan +YenidÉ™n baÅŸla +&Arxa planda +Ö&ndÉ™ +F&asilÉ™ +FasilÉ™dÉ™ +HÉ™qiqÉ™tÉ™n É™mÉ™liyyatı dayandırmaq istÉ™yirsiniz? +500 +&Fayl +&DüzÉ™liÅŸ +&Görünüş +S&eçilmiÅŸlÉ™r +&VasitÉ™lÉ™r +&Arayış +540 +&Aç +&DaxildÉ™ Aç +B&ayırda aç +&Baxış +&DüzÉ™liÅŸ +Ye&nidÉ™n Adlandır +&NüsxÉ™lÉ™... +&Köçür... +&Sil +Faylı &Böl... +Faylları B&irləşdir... +X&üsusiyyÉ™tlÉ™r +Şər&h... +Yoxlama CÉ™mi +MüqayisÉ™ +Qovluq Yarat +Fayl Yarat +Ç&ıxış +İstinad +&ÆvÉ™zedici Axınlar +600 +&Hamısını Seç +Seçimi Ləğv Et +&Seçimi Çevir +Seç... +Seçimi Ləğv Et... +NövünÉ™ GörÉ™ Seç +NövünÉ™ GörÉ™ Seçimi Ləğv Et +700 +&Böyük İşarÉ™lÉ™r +K&içik İşarÉ™lÉ™r +&Siyahı +&CÉ™dvÉ™l +730 +ÇeÅŸidsiz +MüstÉ™vi Görünüş +&2 LövhÉ™ +&AlÉ™tlÉ™r LövhÉ™lÉ™ri +Kök QovluÄŸu Aç +Bir SÉ™viyyÉ™ Yuxarı +Qovluqlar Tarixçəsi... +&YenilÉ™ +BilavasitÉ™ YenilÉ™mÉ™ +750 +ArxivlÉ™yicinin DüymÉ™lÉ™r LövhÉ™si +SadÉ™ DüymÉ™lÉ™r LövhÉ™si +Böyük DüymÉ™lÉ™r +DüymÉ™lÉ™r ÜzÉ™rindÉ™ MÉ™tnlÉ™r +800 +&QovluÄŸu SeçilmiÅŸlÉ™rÉ™ FÉ™rqli ÆlavÉ™ Et +ÆlfÉ™cin +900 +TÉ™nzimlÉ™mÉ™lÉ™r... +SÉ™mÉ™rÉ™ Sınağı +960 +&BaÅŸlıq... +7-Zip &Haqqında... +1003 +İstiqamÉ™t +Ad +GeniÅŸlÉ™mÉ™ +Qovluq +Ölçü +Sıxılmış +RÉ™mzlÉ™r +Yaranıb +Açıq +DÉ™yiÅŸilib +Aramsız +Şərh +ÅžifrÉ™lÉ™nib +ÆvvÉ™lki parça +Sonrakı parça +Lüğət + +Növ +ÆleyhinÉ™ +Sıxılma Üsulu +QuruluÅŸ +Fayl QuruluÅŸu +İstifadəçi +DÉ™stÉ™ +BölmÉ™ +Şərh +Mövqe +İstiqamÉ™t +Qovluq +Fayl +Buraxılış +Cild +Çox Cildli +UzlaÅŸma +İstinad +BölmÉ™ +Cild + + + +Prosessor +Fiziki Ölçü +BaÅŸlıqlar Ölçüsü +Yoxlama CÉ™mi +XassÉ™lÉ™r +XÉ™yali Ünvan + +Qısa Ad +Yaradıcı +SahÉ™ Ölçüsü +VÉ™ziyyÉ™t +RÉ™mzi İstinad +XÉ™ta +CÉ™mi HÉ™cm +Azad HÉ™cm +Toplam HÉ™cm +NiÅŸan +Qısa Ad +TÉ™chizatcı +NT TÉ™hlükÉ™sizlik +ÆvÉ™zedici Axın + +SilinmiÅŸ +AÄŸac + + +XÉ™ta Növü +XÉ™talar +XÉ™talar +XÉ™bÉ™rdarlıqlar +XÉ™bÉ™rdarlıq +Axınlar +ÆvÉ™zedici Axınlar +ÆvÉ™zedici Axınlar Ölçüsü +XÉ™yali Ölçü +CeÅŸidli Ölçü +Ümumi Fiziki Ölçü +Cild NömrÉ™si +Növaltı +Qısa Şərh +ÅžifrÉ™lÉ™nmiÅŸ SÉ™hifÉ™ + + + +Qalıq Ölçüsü +TÉ™tbiq OlunmuÅŸ BölmÉ™ Ölçüsü +İstinad +SÉ™rt İstinad +iNode + +Yalnız Oxumaq Üçün +2100 +TÉ™nzimlÉ™mÉ™lÉ™r +Dil +Dil: +DüzÉ™liÅŸ +&DüzÉ™liÅŸ: +&MüqayisÉ™: +2200 +QuruluÅŸ +7-Zip Fayllarla ÆlaqÉ™lÉ™nsin +Bütün İstifadəçilÉ™r +2301 +7-Zip É™hatÉ™ tÉ™klifinÉ™ tÉ™tbiq edilsin +ZÉ™ncirvari É™hatÉ™ tÉ™klifi +Ünsürlü É™hatÉ™ tÉ™klifi: +ÆhatÉ™ tÉ™klifindÉ™ niÅŸanlar +2320 + +<Ðrxiv> +Arxivi Aç +ÇeÅŸidlÉ™ +ArxivÉ™ ÆlavÉ™ Et... +Sınaq +Burda ÇeÅŸidlÉ™ +{0} ÇeÅŸidlÉ™ +{0} ÆlavÉ™ Et +Sıx VÉ™ E-Poçtla GöndÉ™r... +{0} Sıx VÉ™ E-Poçtla GöndÉ™r +2400 +Qovluqlar +&İş QovluÄŸu +&MüvÉ™qqÉ™ti QuruluÅŸ QovluÄŸu +&Cari +&TÉ™yin Et: +Yalnız dÉ™yiÅŸkÉ™n daşıyıcılar üçün istifadÉ™ +MüvÉ™qqÉ™ti arxivlÉ™r üçün yer göstÉ™r. +2500 +TÉ™nzimlÉ™mÉ™lÉ™r +".." ünsürünü göstÉ™r +HÉ™qiqi fayl iÅŸarÉ™lÉ™ri göstÉ™r +QuruluÅŸ tÉ™klifini göstÉ™r +Ox bütün sÉ™tir üzrÉ™ +Ayrıcıları GöstÉ™r +Bir ToxunuÅŸla Aç +ÆvÉ™zedici vÉ™ziyyÉ™t iÅŸarÉ™lÉ™nmÉ™si +Böyük yaddaÅŸ sÉ™hifÉ™lÉ™ri istifadÉ™si +2900 +7-Zip Haqqında +7-Zip SÉ™rbÉ™st Yayılan Proqramdır +3000 +KifayÉ™t qÉ™dÉ™r boÅŸ yaddaÅŸ yoxdur +SÉ™hvlÉ™r tapılmadı +{0} Ayrılmış hÉ™dÉ™f +'{0}' QovluÄŸunu yaratmaq mümkün olmadı +Bu arxiv üçün dÉ™yiÅŸiklik É™mÉ™liyatı dÉ™stÉ™klÉ™nmir +'{0}' Faylını arxiv kimi açmaq alınmadı +ÅžifrÉ™lÉ™nmiÅŸ '{0}' arxivini açmaq alınmadı. Yanlış ÅŸifrÉ™? +DÉ™stÉ™klÉ™nmÉ™yÉ™n arxiv +{0} faylı artıq mövcuddur +'{0}' faylına dÉ™yiÅŸiklik edildi.\nSiz onu arxivdÉ™ yenilÉ™mÉ™k istÉ™yirsiz? +\n'{0}' faylını yenilÉ™mÉ™k alınmadı +RedaktÉ™ni iÅŸÉ™ salmaq alınmadı +Fayl zÉ™rÉ™rvericiyÉ™ oxÅŸayır (faylın adı uzun boÅŸluq ardıcıllığı təşkil edir). +ÆmÉ™liyyat uzun yol təşkil edÉ™n qovluqdan icra oluna bilmir. +Siz bir fayl seçmÉ™lisiniz +Siz bir vÉ™ ya bir neçə fayl seçmÉ™lisiniz +HÉ™ddÉ™n çox ünsür +Faylı, '{0}' arxivi kimi açmaq alınmadı +Fayl, {0} arxivi kimi açıldı +Arxiv É™vÉ™zlÉ™nÉ™rÉ™k açıldı +3300 +ÇeÅŸidlÉ™nmÉ™ +Sıxılma +Sınaq +Açılma... +Yoxlama... +SilinmÉ™ +3320 +ÆlavÉ™ EtmÉ™k +YenilÉ™nmÉ™ +TÉ™hlil +NüsxÉ™lÉ™nmÉ™ +YenidÉ™n ÇeÅŸidlÉ™nmÉ™ +KeçmÉ™ İcazÉ™si +SilinmÉ™ +BaÅŸlıqlar Yaradılması +3400 +Ayır +Ç&eçidlÉ™: +Ayrılan faylların yerini göstÉ™rin. +3410 +Fayllara istiqamÉ™t: +Tam istiqamÉ™tlÉ™r +İstiqamÉ™tsiz +MütlÉ™q istiqamÉ™tlÉ™r +Nisbi istiqamÉ™tlÉ™r +3420 +YenidÉ™n yazılma: +TÉ™sdiqli +TÉ™sdiqsiz +ÖtürmÉ™k +BirbaÅŸa yenidÉ™n adlandır +Mövcud olanları yenidÉ™n adlandır +3430 +Kök qovluqla bÉ™nzÉ™rliyi aradan qaldır +Faylın tÉ™hlükÉ™sizlik bÉ™rpası +3500 +Faylın É™vÉ™zlÉ™nmÉ™ tÉ™sdiqi +Qovluq artıq emal olunan fayl təşkil edir. +Mövcud faylı É™vÉ™zlÉ™ +növbÉ™ti faylla? +{0} bayt +BirbaÅŸa yenidÉ™n adlandır +3700 +'{0}' faylının dÉ™stÉ™klÉ™nmÉ™yÉ™n sıxılma üsulu. +'{0}' mÉ™lumatlarında xÉ™ta. Fayl korlanıb. +'{0}' CRC xÉ™tası. Fayl korlanıb. +'{0}' ÅŸifrÉ™li fayl mÉ™lumatlarında xÉ™ta. Yanlış ÅŸifrÉ™? +'{0}' ÅŸifrÉ™li faylı üçün CRC xÉ™tası. Yanlış ÅŸifrÉ™? +3710 +Yanlış ÅŸifrÉ™? +3721 +DÉ™stÉ™klÉ™nmÉ™yÉ™n sıxılma üsulu +DÉ™lillÉ™rdÉ™ xÉ™ta +CRC xÉ™tası +Mövcud olmayan dÉ™lillÉ™r +DÉ™lillÉ™rin gözlÉ™nilmÉ™z sonu +BölmÉ™nin sonunda faydalı dÉ™lillÉ™r olduÄŸuna dÉ™lillÉ™r var +Arxiv deyil +BaÅŸlıqlarda XÉ™ta +Yanlış ÅŸifrÉ™ +3763 +Arxivin É™vvÉ™li mövcud deyil +TÉ™sdiqsiz arxiv É™vvÉ™li + + + +DÉ™stÉ™klÉ™nmÉ™yÉ™n xüsusiyyÉ™t +3800 +ÅžifrÉ™ daxil etmÉ™ +&ÅžifrÉ™ daxil edin: +ÅžifrÉ™ni tÉ™krarlayın: +&ÅžifrÉ™ni göstÉ™r +ÅžifrÉ™lÉ™r uyÄŸun deyil +ÅžifrÉ™ üçün yalnız latın hÉ™rflÉ™ri, rÉ™qÉ™mlÉ™r vÉ™ xüsusi rÉ™mzlÉ™r istifadÉ™ edin (!, #, $, ...) +ÅžifrÉ™ ÅŸox uzundur +&ÅžifrÉ™ +3900 +Keçdi: +Qalıb: +CÉ™mi: +SürÉ™t: +Ölçü: +Sıxılma dÉ™rÉ™cÉ™si: +XÉ™ta: +Arxiv: +4000 +ArxivÉ™ É™lavÉ™ etmÉ™k +&Arxiv: +&DÉ™yiÅŸmÉ™ vÉ™ziyyÉ™ti: +Arxiv &qismi: +Sıxılma &dÉ™rÉ™cÉ™si: +Sıxılma &üsulu: +&Lüğət ölçüsü: +&Söz ölçüsü: +BölmÉ™ ölçüsü: +Axın sayı: +&Nizamlar: +&SeçimlÉ™r +SF&X arxiv yarat +Yazılış üçün açıq faylları sıx +ÅžifrÉ™lÉ™mÉ™ +ÅžifrÉ™lÉ™mÉ™ üsulu: +Fayl adlarını &ÅŸifrÉ™lÉ™ +QablaÅŸdırma üçün yaddaÅŸ hÉ™cmi: +ÇeÅŸidlÉ™mÉ™ üçün yaddaÅŸ hÉ™cmi: +Sıxılmadan sonra faylları sil +4040 +RÉ™mzli keçidlÉ™ri saxla +SÉ™rt keçidlÉ™ri saxla +ÆvÉ™zedici axınları saxla +GiriÅŸ hüququnu saxla +4050 +Sıxılmasız +SürÉ™tli +CÉ™ld +MÉ™qbul +YüksÉ™k +Daha YüksÉ™k (Ultra) +4060 +ÆlavÉ™ et vÉ™ É™vÉ™zlÉ™ +YenilÉ™ vÉ™ É™lavÉ™ et +YenilÉ™ +Eyniləşdir (Sinxron) +4070 +VÉ™rÉ™qlÉ™ +Bütün Fayllar +Fayl ölçüsünÉ™ görÉ™ +Aramsız +6000 +NüsxÉ™lÉ™ +Köçür +NüsxÉ™lÉ™: +Köçür: +NüsxÉ™lÉ™lÉ™nmÉ™... +KöçürülmÉ™... +YenidÉ™n adlandırılma... +QovluÄŸu göstÉ™rin. +ÆmÉ™liyyat bu qovluq üçün dÉ™stÉ™klÉ™nmir. +Fayl vÉ™ ya QovluÄŸun YenidÉ™n Adlandırılması Zamanı XÉ™ta +Faylların NüsxÉ™lÉ™lÉ™nmÉ™ TÉ™sdiqi +Siz hÉ™qiqÉ™tÉ™n bu faylları arxivÉ™ nüsxÉ™lÉ™mÉ™k istÉ™yirsiz +6100 +Fayl silinmÉ™ tÉ™sdiqi +Qovluq silinmÉ™ tÉ™sdiqi +Fayllar dÉ™stÉ™sinin silinmÉ™ tÉ™sdiqi +Siz hÉ™qiqÉ™tÉ™n "{0}" silmÉ™k istÉ™yirsiz? +Siz hÉ™qiqÉ™tÉ™n "{0}" qovluÄŸunu vÉ™ onun bütün mÉ™zmununu silmÉ™k istÉ™yirsiz? +Siz hÉ™qiqÉ™tÉ™n {0} hÉ™dÉ™fi silmÉ™k istÉ™yirsiz? +SilinmÉ™... +Fayl vÉ™ ya Qovluq SilinmÉ™si Zamanı XÉ™ta +QuruluÅŸ uzun yollu faylların sÉ™bÉ™tÉ™ silinmÉ™si É™mÉ™liyyatını dÉ™stÉ™klÉ™mir +6300 +Qovluq Yarat +Fayl Yarat +Qovluq Adı: +Fayl Adı: +Yeni Qovluq +Yeni Fayl +Qovluq Yaradılması Zamanı ZÉ™ta +Fayl Yaradılması Zamanı ZÉ™ta +6400 +Şərh +&Şərh: +Seçin +Seçimi Ləğv Edin +Üzlük (Maska): +6600 +XassÉ™lÉ™r +Qovluqlar Tarixçəsi +İsmarıclar +İsmarıc +7100 +Kompüter +ŞəbÉ™kÉ™ +SÉ™nÉ™dlÉ™r +QuruluÅŸ +7200 +ÆlavÉ™ Et +Ayır +Sınaq +NüsxÉ™lÉ™ +Köçür +Sil +MÉ™lumat +7300 +Fayl BölmÉ™k +&BölmÉ™k: +CildlÉ™rÉ™ &bölmÉ™k, (bayt) ölçüdÉ™: +BölünmÉ™... +BölünmÉ™ TÉ™sdiqi +Siz hÉ™qiqÉ™tÉ™n faylı {0} hissÉ™yÉ™ bölmÉ™k istÉ™yirsiz? +Cild ölçüsü ilkin fayl ölçüsündÉ™n kiçik olmalıdır +CildlÉ™rin ölçü tÉ™yin etmÉ™ sahÉ™sindÉ™ xÉ™ta +TÉ™yin edilmiÅŸ cild ölçüsü: {0} bayt.\nSiz hÉ™qiqÉ™tÉ™n arxivi belÉ™ cildlÉ™rÉ™ bölmÉ™k istÉ™yirsiniz? +7400 +Faylları Birləşdir +&BirləşdirmÉ™k: +BirləşmÉ™... +Bölünmüş faylın yalnız birinci hissÉ™sini seçmÉ™k lazımdır +Bölünmüş faylın tanınması mümkün olmadı +Bölünmüş faylın birdÉ™n çox hissÉ™sini tapmaq mümkün olmadı +7500 +Yoxlanma cÉ™minin hesablanması... +Yoxlanma cÉ™mi +DÉ™lillÉ™r üçün CRC yoxlanma cÉ™mi: +DÉ™lillÉ™r vÉ™ adlar üçün CRC yoxlanma cÉ™mi: +7600 +SÉ™mÉ™rÉ™ Sinağı +YaddaÅŸ HÉ™cmi: +QablaÅŸdırma +ÇeÅŸidlÉ™nmÉ™ +DÉ™rÉ™cÉ™ +Ümumi DÉ™rÉ™cÉ™ +Cari +NÉ™ticÉ™ +YüklÉ™nmÉ™ +YüklÉ™nmÉ™ DÉ™rÉ™cÉ™si +Keçid: +7700 +İstinad +ÆlaqÉ™lÉ™ndir +MÉ™nbÉ™: +MÉ™qsÉ™d: +7710 +İstinad Qismi +SÉ™rt İstinad +RÉ™mzi İstinad (Fayl) +RÉ™mzi İstinad (Qovluq) +BaÄŸlantı NöqtÉ™si (QovÅŸaq) diff --git a/Utils/7-Zip/Lang/ba.txt b/Utils/7-Zip/Lang/ba.txt new file mode 100644 index 000000000..947d4a808 --- /dev/null +++ b/Utils/7-Zip/Lang/ba.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.20 : Haqmar : www.bashqort.com +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Bashkir +БашҡортÑа +401 +Тамам +Кире ал + + + +&Эйе +&Юҡ +&Яп +Ярҙам + +&Дауам +440 +Бөтәһенә лә Э&йе +Бө&тәһенә лә Юҡ +Туҡта +Яңынан башла +&Ðртҡы планда +Ð&лғы планда +&Туҡтатып тор +Паузала +Был Ñште Ñ‹Ñынлап та өҙөргә теләйһегеҙме? +500 +&Файл +Һа&йлау +&Күренеш +Òº&айланмалар +&Ð¡ÐµÑ€Ð²Ð¸Ñ +&Белешмә +540 +&ÐÑырға +&ЭÑендә аÑырға +&Тышта аÑырға +Ҡа&рау +&Мөхәррирләргә +&ИÑемен алыштырырға +&Күбәйтергә... +Кү&Ñерергә... +&Юйырға +Файлды &бүл... +Файлдар бер&ләштереү... +Мәғ&лүмәт +ТаÑ&уирлама +Тикшереү һаны +Diff +Яңы папка... +Яңы &файл... +С&ығырға +600 +&Бөтәһен дә һайларға +Һай&лауҙы кире алырға +Бил&дәләнгәндәрҙе кире әйләндерергә +МаÑка менән һайларға... +Һайлауҙы Ñбырға... +Төр буйынÑа һайларға +Төр буйынÑа һайлауҙы Ñбырға +700 +&Эре тамғалар +&Бәләкәй тамғалар +&ИÑемлек +&Ентекле +730 +Тәртип булмаһын +Барыһын да күрһәт +&2 Панель +&Ҡоралдар панеле +Төп папканы Ð°Ñ +Бер кимәл юғары +Папкалар тарихы... +&Яңырт +750 +&Ðрхивлау төймәләре +Стандарт төймәләр +Эре төймәләр +Төймәләр аңлатмалы +800 +Ғәмәлдәге папканы һайланғандарға өҫтәргә: +Урын +900 +&Көйләү... +&ЕтештереүÑәнлекте үлÑÓ™Ò¯ +960 +&ЭÑтәлек... +7-Zip &тураhында... +1003 +Юл +ИÑем +Киңәйеүе +Папка +Күләм +Ðрхивдағы күләме +Ðтрибуттар +Яһалған +ÐÑылған +Үҙгәртелгән +Өҙөлмәгән +Ðңлатма +Серләнгән +Ðлдағы киҫәк +Киләһе киҫәк +Һүҙлек +CRC +Төр +Ðнти +ЫÑул +СиÑтема +Файл ÑиÑтемаһы +ҠулланыуÑÑ‹ +Tөркөм +Блок +ТаÑуирлама +Урын +Юл префикÑÑ‹ +Папкалар +Файлдар +ВерÑÐ¸Ñ +Том +Күп томлы +Шылыу +Һылтанмалар +Блоктар +Томдар + +64-bit +Big-endian +ПроцеÑÑор +Физик күләме +Башлыҡтар күләме +Тикшереү Ñуммаһы +ХарактериÑтикалар +Виртуаль Ð°Ð´Ñ€ÐµÑ +ID +Ҡыҫҡа иÑем +ЯһауÑÑ‹ +Сектор күләме +Режим +Һылтанма +Хата +Бар күләм +Буш урын +КлаÑтер күләме +Билдә +Урындағы ИÑем +Провайдер +2100 +Көйләүҙәр +Тел һайлау +Тел: +МөхәррирләүÑе +&МөхәррирләүÑе: +&Diff: +2200 +СиÑтема +7-Zip менән килештер: +2301 +КонтекÑÑ‚ менюла 7-Zip-ты күрһәтергә +КаÑкадлы контекÑÑ‚ меню +КонтекÑÑ‚ меню Ñлементтары: +2320 +<Папка> +<Ðрхив> +Ðрхивты аÑырға +Файлдар Ñығарыу... +Ðрхивғ өҫтәргә... +Ðрхивты һынарға +Бында Ñығарырға +{0} папкаһына Ñығарырға +{0} итеп архивла +Ðрхивлап, e-mail менән ебәрергә... +{0} итеп архивларға һәм e-mail менән ебәрергә +2400 +Папкалар +&Эш папкаһы +&ВаҡытлыÑа ÑиÑтема папкаһы +&Ðғымдағы +&Билдәләргә: +Ðлмаш ташығыÑтар Ó©Ñөн генә ҡулланырға +ВаҡытлыÑа архивтар Ó©Ñөн урын күрһәтегеҙ. +2500 +Көйләүҙәр +".." Ñлементы күренһен +Файлдарҙың Ñ‹Ñын тамғалары күренһен +СиÑтема менюһы күренһен +Бөтә юл һайланһын +Һыҙыҡтар күренһен +Бер Ñиртеү менән аÑырға +Ðльтернатив һайлау Ñ‹Ñулы +Ҙур хәтер биттәрен ҡуллан +2900 +7-Zip тураhында +7-Zip – ирекле таратылған программа. +3000 +Буш хәтер етмәй +Хата табылманы +{0} объект һайланған +{0} папкаһын Ñһап булмай +Был архивды үҙгәртеү ғәмәлен үтәп булмай. +'{0}' файлын архив һымаҡ аÑып булмай +Шифрланған '{0}' файлын аÑып булманы. Хаталы пароль? +Терәкләнмәгән архив төрө +{0} файлы бар +'{0}' файлы мөхәррирләнде.\nБыл файл архивда Ñңыртылһынмы? +Файлды Ñңыртып булманы\n'{0}' +МөхәррирләүÑене аÑып булманы. +Файл вируÑҡа оҡшаған (файл иÑемендә бер-бер артлы килгән күп бушлыҡтар бар). +Ғәмәлде бындай оҙон юллы папканан үтәп булмай. +Бер файлды һайлау кәрәк +Бер йәки күберәк файлды һайлау кәрәк +Бик күп Ñлемент +3300 +Сығарыу бара... +Ҡыҫыу бара... +Һынау +ÐÑыла... +Тарау бара... +3400 +Сығар +Бында &Ñығар: +СығарылаÑаҡ файлдар Ó©Ñөн урын һайлағыҙ. +3410 +Юл иÑемдәре +&Тулы юл иÑемдәре +Юл иÑемдәре булмаһын +3420 +Өҫтөнә Ñҙыу +&Өҫтөнә Ñҙыу алдынан һора +&Өҫтөнә Ñҙыу алдынан һорама +Булған файлдарҙы үтеп кит +Яңы иÑем ҡуш +Булған файлдарға Ñңы иÑем ҡуш +3500 +Файл алмаштырыуҙы раҫлағыҙ +СығарылаÑаҡ папкала Ñшкәртелгән файл бар. +Булған +файлын киләһе менән алыштырырғамы? +{0} байт +&Яңы иÑем ҡушылһын +3700 +'{0}' файлын ҡыҫыу Ñ‹Ñулын табып булманы. +'{0}' файлында мәғлүмәт хатаһы бар. Файл боҙоҡ. +'{0}' файлында CRC хатаһы бар. Файл боҙоҡ. +Шифрланған '{0}' файлы мәғлүмәттәрендә хата. Хаталы пароль? +Шифрланған '{0}' файлында CRC хатаһы. Хаталы пароль? +3800 +Пароль керетеү +&Паролде керетегеҙ: +Па&ролде Ñңынан керетегеҙ: +П&ароль күренһен +Паролдәр тап килмәй +Пароль Ó©Ñөн тик латин хәрефтәрен, һандарҙы һәм махÑÑƒÑ Ñимволдарҙы (!, #, $, ...) ҡулланығыҙ +Пароль бик оҙон +Пароль +3900 +Үткән ваҡыт: +Ҡалған ваҡыт: +Барыһы: +Тиҙлек: +Күләм: +Ҡыҫыу ниÑбәте: +Хаталар: +Ðрхив: +4000 +Ðрхивлау +&Ðрхив: +&Яңыртыу Ñ‹Ñулы: +Ð&рхив төрө: +Ҡыҫыу &дәрәжәһе: +Ò &ыҫыу Ñ‹Ñулы: +ÒºÒ¯Ò™&лек күләме: +ÒºÒ¯Ò™ күлә&ме: +Блоктар күләме: +Ðғымдар һаны: +&Параметрҙар: +&Көйләү +SFX ар&хивын Ñһау +Яҙыуға аÑыҡ файлдарҙы ҡыҫырға +Шифрлау +Шифрлау методы: +&Файл иÑемдәрен шифрла +Ҡыҫҡанда хәтер ҡулланыу: +Сығарғанда хәтер ҡулланыу: +4050 +Ҡыҫыуһыҙ +Бик тиҙ +Тиҙ +Ғәҙәти +МакÑимум +Ультра +4060 +Өҫтәргә һәм алмаштырырға +Яңыртырға һәм өҫтәргә +Яңыртырға +Синхронларға +4070 +Ҡарау +Бар файлдар +Файл күләме буйынÑа +Өҙөлмәгән +6000 +КопиÑһын ал +КүÑер +ÐšÐ¾Ð¿Ð¸Ñ Ò¡ÑƒÐ¹Ñ‹Ð»Ð°Ñаҡ урын: +КүÑереләÑәк урын: +КопиÑһын алыу... +КүÑереү... +Яңынан иÑемләү бара... +Папканы күрһәтегеҙ. +Ғәмәлде үтәп булмай +Файлға йәки папкаға Ñңы иÑем биреү хатаһы +Файлдарҙы күбәйтеүҙе раҫлау +Был файлдар архивға ҡуйылһынмы? +6100 +Файл юйыуҙы раҫлау +Папка юйыуҙы раҫлау +Берҙән күп файл юйыуҙы раҫлау +'{0}' юйылһынмы? +'{0}' папкаһы һәм ÑÑендәгеләр юйылһынмы? +{0} еÑеме юйылһынмы? +Юйыу бара... +Файл йәки папка юйыу хатаһы +Оҙон юллы файлдарҙы кәрзингә юйыуҙы ÑиÑтема терәкләмәй +6300 +Папка Ñhа +Файл Ñhа +Папка иÑеме: +Файл иÑеме: +Яңы папка +Яңы файл +Папка Ñһау хатаһы +Файл Ñһау хатаһы +6400 +ТаÑуирлама +&ÐÑыҡлама: +hайла +Һайлауҙы кире ал +МаÑка: +6600 +ҮҙенÑәлектәр +Папкалар тарихы +Белдереүҙәр +Белдереү +7100 +Компьютер +Селтәр +Документтар +СиÑтема +7200 +Өҫтәргә +Сығарырға +Һынарға +КопиÑһын алырға +КүÑерергә +Юйырға +Мәғлүмәт +7300 +Файлды бүл +&Ошо папкаға бүл: +Киҫәк/&байт итеп бүл: +Бүлеү бара... +Бүлеүҙе раҫлау +Был файлды {0} киҫәккә бүлеүҙе раҫлайһығыҙмы? +Том күләме Ñығанак файлдан бәләкәй булырға тейеш +Хаталы том күләме +Том күләме : {0} байт.\nФайлды бындай томдарға бүлеүҙе раҫлайһығыҙмы? +7400 +Файлдарҙы берләштер +&Ошо папкала берләштер: +Берләштереү бара... +Бүленгән файлдың беренÑе киҫәген генә һайлағыҙ +Бүленгән файлды танып булманы +Бүленгән файлдың берҙән күп киҫәген табып булманы +7500 +Тикшереү һанын иҫәпләү бара... +Тикшереү һаны +Мәғлүмәттәр Ó©Ñөн CRC тикшереү һаны: +Мәғлүмәттәр һәм иÑемдәр Ó©Ñөн CRC тикшереү һаны: +7600 +ЕтештереүÑәнлекте тикшереү +Хәтер ҡулланыу: +Ҡыҫыу +Сығарыу +Рейтинг +Дөйөм рейтинг +Хәҙерге +Һөҙөмтә +ПроцеÑÑор ҡулланыу +Рейтинг / Проц. ҡулл. +Үтеүҙәр: diff --git a/Utils/7-Zip/Lang/be.txt b/Utils/7-Zip/Lang/be.txt new file mode 100644 index 000000000..5a16ebdc9 --- /dev/null +++ b/Utils/7-Zip/Lang/be.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; : Kirill Gulyakevitch +; 9.07 : 2011-03-15 : Drive DRKA +; +; +; +; +; +; +; +; +; +0 +7-Zip +Belarusian +БеларуÑÐºÐ°Ñ +401 +OK +Ðдмена + + + +&Так +&Ðе +&Зачыніць +Дапамога + +&ПрацÑгнуць +440 +Так Ð´Ð»Ñ &уÑÑ–Ñ… +Ðе Ð´Ð»Ñ Ñž&ÑÑ–Ñ… +Стоп +ПеразапуÑк +&Фонам +&Ðа пÑÑ€Ñдні план +&Паўза +Ðа паўзе +Ð’Ñ‹ Ñапраўды жадаеце перапыніць аперацыю? +500 +&Файл +&Праўка +&ВыглÑд +&Ðбранае +С&ÐµÑ€Ð²Ñ–Ñ +&Даведка +540 +&Ðдкрыць +Ðдкрыць &уÑÑÑ€Ñдзіне +Ðдкрыць з&вонку +ПраглÑд +&РÑдагаваць +Пера&назваць +&КапіÑваць у... +&ПерамÑÑціць у... +&Выдаліць +Ра&збіць файл... +Ð&б'Ñднаць файлы... +Сво&йÑтва +Коме&нтар +ÐšÐ°Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ñума +Diff +&Стварыць папку +Ств&арыць Файл +Ð’&ыхад +600 +Вылучыць у&ÑÑ‘ +Прыбраць вылучÑнне +&ЗвÑрнуць в&ыдзÑленне +Вылучыць... +Прыбраць вылучÑнне... +Вылучыць па тыпе +Прыбраць вылучÑнне па тыпе +700 +&Ð‘ÑƒÐ¹Ð½Ñ‹Ñ Ð·Ð½Ð°Ñ‡ÐºÑ– +&Ð”Ñ€Ð¾Ð±Ð½Ñ‹Ñ Ð·Ð½Ð°Ñ‡ÐºÑ– +Спі&Ñ +&Табліца +730 +Без ÑÐ°Ñ€Ñ‚Ð°Ð²Ð°Ð½Ð½Ñ +ПлоÑкі Ñ€Ñжым +&2 ПанÑлі +&ПанÑлі прылад +Ðдкрыць каранёвую папку +Пераход на адзін узровень уверх +ГіÑÑ‚Ð¾Ñ€Ñ‹Ñ Ð¿Ð°Ð¿Ð°Ðº... +Ð&бнавіць +750 +ПанÑль кнопак архіватара +Ð¡Ñ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð°Ñ Ð¿Ð°Ð½Ñль кнопак +Ð’ÑÐ»Ñ–ÐºÑ–Ñ ÐºÐ½Ð¾Ð¿ÐºÑ– +ÐадпіÑÑ‹ на кнопках +800 +Дадаць папку Ñž &абранае Ñк +Закладка +900 +Ðалады... +ТÑÑтаванне прадукцыйнаÑці +960 +&ЗмеÑÑ‚... +Ðб &праграме... +1003 +ШлÑÑ… +Ð†Ð¼Ñ +ПашырÑнне +Папка +Памер +СціÑнуты +Ðтрыбуты +Створаны +Ðдчынены +Зменены +БеÑперапынны +Каментар +Зашыфраваны +Пабіты Да +Пабіты ПаÑÐ»Ñ +Слоўнік +CRC +Тып +Ðнты +Метад +СіÑÑ‚Ñма +Ð¤Ð°Ð¹Ð»Ð°Ð²Ð°Ñ Ð¡Ñ–ÑÑ‚Ñма +КарыÑтальнік +Група +Блок +Каментар +ÐŸÐ°Ð·Ñ–Ñ†Ñ‹Ñ +ШлÑÑ… +Папак +Файлаў +ВерÑÑ–Ñ +Тым +Шматтомны +ЗрушÑнне +СпаÑылак +Блокаў +Тамоў + +64-bit +Big-endian +ПрацÑÑар +Фізічны Памер +Памер Загалоўкаў +ÐšÐ°Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ð¡ÑƒÐ¼Ð° +ХарактарыÑтыкі +Віртуальны ÐÐ´Ñ€Ð°Ñ +ID +Кароткае IÐ¼Ñ +Создатель +Памер Сектара +РÑжiм +СÑылка +Памылка +ÐміÑтаÑць +Вольна +Памер клаÑтара +Пазнака +Лакальнае Ñ–Ð¼Ñ +ПравайдÑÑ€ +2100 +Ðалады +Мова +Мова: +РÑдактар +&РÑдактар: +&Diff: +2200 +СіÑÑ‚Ñма +ÐÑацыÑваць 7-Zip з файламі: +2301 +Убудаваць 7-Zip у кантÑкÑтнае меню абалонкі +КаÑкаднае кантÑкÑтнае меню +Элементы кантÑкÑтнага меню: +2320 +<Папка> +<Ðрхіў> +Ðдкрыць архіў +РаÑпакаваць +Дадаць да архіва... +ТÑÑтаваць +РаÑпакаваць тут +РаÑпакаваць у {0} +Дадаць да {0} +СціÑнуць Ñ– адправіць па email... +СціÑнуць у {0} Ñ– адправіць па email +2400 +Папкі +&ÐŸÑ€Ð°Ñ†Ð¾ÑžÐ½Ð°Ñ ÐŸÐ°Ð¿ÐºÐ° +&СіÑÑ‚ÑÐ¼Ð½Ð°Ñ Ñ‡Ð°ÑÐ°Ð²Ð°Ñ ÐŸÐ°Ð¿ÐºÐ° +&БÑÐ³ÑƒÑ‡Ð°Ñ +&Задаць: +ВыкарыÑтаць толькі Ð´Ð»Ñ Ð·Ð¼ÐµÐ½Ð½Ñ‹Ñ… ноÑьбітаў +Пакажыце Ñтановішча Ð´Ð»Ñ Ñ‡Ð°Ñавых архіваў. +2500 +Ðалады +Паказваць Ñлемент ".." +Паказваць Ñ€ÑÐ°Ð»ÑŒÐ½Ñ‹Ñ Ð°Ð±Ñ€Ð°Ð·ÐºÑ– файлаў +Паказваць ÑÑ–ÑÑ‚Ñмнае меню +КурÑор на ўвеÑÑŒ радок +Паказваць падзельнікі +ÐдчынÑць Ñлемент адным клiкам +ÐльтÑрнатыўны Ñ€Ñжым пазнакі +ВыкарыÑтаць вÑÐ»Ñ–ÐºÑ–Ñ Ñтаронкі памÑці +2900 +Ðб праграме 7-Zip +7-Zip з'ÑўлÑецца вольна раÑпаўÑюджваемай праграмай. Ðднак калі вы жадаеце падтрымаць раÑпрацоўку 7-Zip, вы можаце зарÑгіÑтраваць праграму.Праграма перакладена Drive DRKA.ÐœÐ°Ñ ÑÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ ÑкрынÑ: drka2003@mail.ru.Пераклад зроблен у вераÑні 2007 года. +3000 +ÐÑдоÑыць вольнай памÑці +Памылак не знойдзена +Вылучана аб'ектаў: {0} +Ðе атрымалаÑÑ Ñтварыць папку '{0}' +Ðперацыі змены не падтрымліваюцца Ð´Ð»Ñ Ð³Ñтага архіва. +Ðе атрымалаÑÑ Ð°Ð´ÐºÑ€Ñ‹Ñ†ÑŒ файл '{0}' Ñк архіў +Ðе атрымалаÑÑ Ð°Ð´ÐºÑ€Ñ‹Ñ†ÑŒ зашыфраваны архіў '{0}'. ÐÑÑлушны пароль? +Ðепадтрымоўваны тып архіва +Файл {0} ужо Ñ–Ñнуе +Файл '{0}' быў зменены.\nÐ’Ñ‹ жадаеце абнавіць Ñго Ñž архіве? +Ðе атрымалаÑÑ Ð°Ð±Ð½Ð°Ð²Ñ–Ñ†ÑŒ файл\n'{0}' +Ðе атрымалаÑÑ Ð·Ð°Ð¿ÑƒÑціць Ñ€Ñдактар +Файл падобны на Ð²Ñ–Ñ€ÑƒÑ (Ñ–Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° ўтрымоўвае доўгую паÑлÑдоўнаÑць прабелаў). +ÐÐ¿ÐµÑ€Ð°Ñ†Ñ‹Ñ Ð½Ðµ можа быць Ð²Ñ‹ÐºÐ°Ð½Ð°Ð½Ð°Ñ Ð· папкі, ÑÐºÐ°Ñ Ð¼Ð°Ðµ доўгі шлÑÑ…. +Ð’Ñ‹ павінны вылучыць адзін файл +Ð’Ñ‹ павінны вылучыць адзін або некалькі файлаў +Занадта шмат Ñлементаў +3300 +РаÑпакаванне +КампрÑÑÑ–Ñ +ТÑÑтаванне +Ðдкрыццё... +Сканаванне... +3400 +ВынÑць +&РаÑпакаваць у: +Пакажыце Ñтановішча Ð´Ð»Ñ Ð²Ñ‹Ð¼Ð°ÐµÐ¼Ñ‹Ñ… файлаў. +3410 +ШлÑÑ…Ñ– +По&ўные шлÑÑ…Ñ– +&Без шлÑхоў +3420 +ÐŸÐµÑ€Ð°Ð·Ð°Ð¿Ñ–Ñ +&З пацверджаннем +Бы&ез пацверджанні +Прап&уÑкаць +Пераназваць аўтам. +Переім. аўтам. Ñ–Ñтот. +3500 +Пацверджанне замены файла +Папка ўжо ўтрымоўвае апрацоўваемы файл. +ЗамÑніць наÑўны файл +наÑтупным файлам? +{0} байтаў +Пераназваць аўтам. +3700 +Ðепадтрымоўваны метад ÑціÑку Ð´Ð»Ñ Ñ„Ð°Ð¹Ð»Ð° '{0}'. +Памылка Ñž дадзеных у '{0}'. Файл ÑапÑаваны. +Памылка CRC у '{0}'. Файл ÑапÑаваны. +Памылка Ñž дадзеных зашыфраванага файла '{0}'. ÐÑÑлушны пароль? +Памылка CRC Ð´Ð»Ñ Ð·Ð°ÑˆÑ‹Ñ„Ñ€Ð°Ð²Ð°Ð½Ð°Ð³Ð° файла '{0}'. ÐÑÑлушны пароль? +3800 +Увод Ð¿Ð°Ñ€Ð¾Ð»Ñ +&УвÑдзіце пароль: +&Паўтарыце пароль: +&Паказаць пароль +Паралі не Ñупадаюць +Ð”Ð»Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð²Ñ‹ÐºÐ°Ñ€Ñ‹Ñтайце толькі знакі лацінÑкага алфавіту, лічбы Ñ– адмыÑÐ»Ð¾Ð²Ñ‹Ñ Ð·Ð½Ð°ÐºÑ– (!, #, $, ...) +Пароль занадта доўгі +&Пароль +3900 +Мінула: +ЗаÑталоÑÑ: +УÑÑго: +ХуткаÑць: +Памер: +Ступень ÑціÑку: +Памылак: +Ðрхіваў: +4000 +Дадаць да архіва +&Ðрхіў: +&РÑжым змены: +&Фармат архіва: +&Узровень ÑціÑку: +&Метад ÑціÑку: +Памер &Ñлоўніка: +Памер з&лоўлі: +Памер блока: +Лік ÑтруменÑÑž: +&Параметры: +&Опцыі +Стварыць SF&X-архіў +СціÑкаць Ð°Ð´Ñ‡Ñ‹Ð½ÐµÐ½Ñ‹Ñ Ð´Ð»Ñ Ð·Ð°Ð¿Ñ–Ñу файлы +Шыфраванне +Метад шыфраваннÑ: +&Шыфраваць імёны файлаў +Ðб'ём памÑці Ð´Ð»Ñ Ð¿Ð°ÐºÐ°Ð²Ð°Ð½Ð½Ñ: +Ðб'ём памÑці Ð´Ð»Ñ Ñ€Ð°ÑпакаваннÑ: +4050 +Без ÑціÑку +ХуткаÑны +Хуткі +Ðармалёвы +МакÑімальны +Ультра +4060 +Дадаць Ñ– замÑніць +Ðбнавіць Ñ– дадаць +Ðбнавіць +Сінхранізаваць +4070 +Прагартаць +УÑе файлы +Па памеры файла +БеÑперапынны +6000 +КапіÑваць +ПерамÑÑціць +КапіÑваць у: +ПерамÑÑціць у: +КапіÑванне... +ПерамÑшчÑнне... +Пераназванне... +Пакажыце папку. +ÐÐ¿ÐµÑ€Ð°Ñ†Ñ‹Ñ Ð½Ðµ падтрымліваецца Ð´Ð»Ñ Ð³Ñтай папкі. +Памылка пры пераназванні файла або папкі +Пацверджанне капіÑÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð°Ñž +Ð’Ñ‹ Ñапраўды жадаеце ÑкапіÑваць гÑÑ‚Ñ‹Ñ Ñ„Ð°Ð¹Ð»Ñ‹ Ñž архіў +6100 +Пацверджанне Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð° +Пацверджанне Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¿Ð°Ð¿ÐºÑ– +Пацверджанне Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð³Ñ€ÑƒÐ¿Ñ‹ файлаў +Ð’Ñ‹ Ñапраўды жадаеце выдаліць "{0}"? +Ð’Ñ‹ Ñапраўды жадаеце выдаліць папку "{0}" Ñ– ÑžÑÑ‘ Ñе змеÑціва? +Ð’Ñ‹ Ñапраўды жадаеце выдаліць гÑÑ‚Ñ‹Ñ Ð°Ð±'екты ({0} шт.)? +Выдаленне... +Памылка пры выдаленні файла або папкі +СіÑÑ‚Ñма не падтрымлівае аперацыю Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð°Ñž з доўгімі шлÑхамі Ñž кошык +6300 +Стварыць папку +Стварыць файл +Ð†Ð¼Ñ Ð¿Ð°Ð¿ÐºÑ–: +Ð†Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°: +ÐÐ¾Ð²Ð°Ñ ÐŸÐ°Ð¿ÐºÐ° +Ðовы файл +Памылка пры ÑтварÑнні папкі +Памылка пры ÑтварÑнні файла +6400 +Каментар +&Каментар: +Вылучыць +Прыбраць вылучÑнне +МаÑка: +6600 +УлаÑціваÑці +ГіÑÑ‚Ð¾Ñ€Ñ‹Ñ Ð¿Ð°Ð¿Ð°Ðº +Паведамленні +Паведамленне +7100 +Кампутар +Сетка +Дакументы +СіÑÑ‚Ñма +7200 +Дадаць +ВынÑць +ТÑÑтаваць +КапіÑваць +ПерамÑÑціць +Выдаліць +Ð†Ð½Ñ„Ð°Ñ€Ð¼Ð°Ñ†Ñ‹Ñ +7300 +Разбіць файл +&Разбіць у: +Разбіць на &тамы памерам (у байтах): +Разбіццё... +Пацверджанне Ñ€Ð°Ð·Ð±Ñ–Ñ†Ñ†Ñ +Ð’Ñ‹ Ñапраўды жадаеце разбіць файл на {0} чаÑтак? +Памер тома павінен быць менш памеру зыходнага файла +Памылка Ñž поле Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð°Ð¼ÐµÑ€Ñƒ тамоў +УÑталÑваны памер тома: {0} байтаў.\nÐ’Ñ‹ Ñапраўды жадаеце разбіць архіў на Ñ‚Ð°ÐºÑ–Ñ Ñ‚Ð°Ð¼Ñ‹? +7400 +Ðб'Ñднаць файлы +&Ðб'Ñднаць у: +Ðб'Ñднанне... +Ðеабходна вылучыць толькі першую чаÑтку пабітага файла +Ðе атрымалаÑÑ Ñ€Ð°Ñпазнаць пабіты файл +Ðе атрымалаÑÑ Ð·Ð½Ð°Ð¹Ñці больш адной чаÑткі пабітага файла +7500 +ВылічÑнне кантрольнай Ñумы... +ÐšÐ°Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ñума +ÐšÐ°Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ñума CRC Ð´Ð»Ñ Ð´Ð°Ð´Ð·ÐµÐ½Ñ‹Ñ…: +ÐšÐ°Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ñума CRC Ð´Ð»Ñ Ð´Ð°Ð´Ð·ÐµÐ½Ñ‹Ñ… Ñ– імёнаў: +7600 +ТÑÑтаванне прадукцыйнаÑці +Ðб'ём памÑці: +Пакаванне +РаÑпакаванне +РÑйтынг +Ðгульны Ñ€Ñйтынг +БÑгучы +Выніковы +Ðагрузка +РÑйтынг / Ðагр. +Праходаў: diff --git a/Utils/7-Zip/Lang/bg.txt b/Utils/7-Zip/Lang/bg.txt new file mode 100644 index 000000000..4346f1660 --- /dev/null +++ b/Utils/7-Zip/Lang/bg.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; : chavv +; : icobgr +; 4.65 : Vassia Atanassova +; +; +; +; +; +; +; +; +0 +7-Zip +Bulgarian +БългарÑки +401 +OK +Отказ + + + +&Да +&Ðе +&ЗатварÑне +Помощ + +Пр&одължаване +440 +Да за &вÑички +Ðе за &вÑички +Стоп +От начало +&Фонов режим +&Ðормален режим +&Пауза +Ð’ пауза +ÐаиÑтина ли желаете да прекратите? +500 +&Файл +&Редактиране +&Показване +&Любими +&ИнÑтрументи +&Помощ +540 +&ОтварÑне +ОтварÑне &в +ОтварÑне &извън +&Показване +&Редактиране +Преи&менуване +&Копиране в... +Пре&меÑтване в... +Из&триване +Р&азделÑне на файл... +О&бединÑване на файлове... +&СвойÑтва +Ком&ентар +ИзчиÑлÑване на контролна Ñума + +Създаване на Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ +Създаване на файл +Из&ход +600 +&Маркиране на вÑички +&Размаркиране на вÑички +&Обръщане на избора +Маркиране... +Размаркиране... +Маркиране по тип +Размаркиране по тип +700 +&Големи икони +&Малки икони +&СпиÑък +&Детайли +730 +ÐеÑортиран +ПлоÑък изглед +&2 панела +&Ленти Ñ Ð¸Ð½Ñтрументи +ОтварÑне на главната Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ +Едно ниво нагоре +ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð½Ð° директориите... +&ОпреÑнÑване +750 +Лента на архива +Стандартна лента +Големи бутони +Показване на текÑÑ‚ под бутоните +800 +&ДобавÑне на директориÑта като любима: +Отметка +900 +&ÐаÑтройки... +&СтатиÑтика +960 +&Съдържание... +&За 7-zip... +1003 +Път +Име +Разширение +Ð”Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ +Размер +КомпреÑиран размер +Ðтрибути +Създаден +ОтварÑн +Изменен +Солиден +Коментар +Зашифрован +Разделен до +Разделен Ñлед +Речник +CRC +Тип +Ðнти +Метод +Операционна ÑиÑтема +Файлова ÑиÑтема +Потребител +Група +Блок +Коментар +ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ +ÐŸÑ€ÐµÑ„Ð¸ÐºÑ Ð½Ð° Ð¿ÑŠÑ‚Ñ +Директории +Файлове +ВерÑÐ¸Ñ +Том +Multivolume +Offset +Links +Blocks +Томове + +64-битов +Big-endian +CPU +ФизичеÑки размер +Размер на заглавната чаÑÑ‚ +Контролна Ñума +ХарактериÑтики +Виртуален Ð°Ð´Ñ€ÐµÑ + + + + + + +Грешка +Пълен размер +Свободно проÑтранÑтво +Размер на клъÑтер +Етикет +Локално име +ДоÑтавчик +2100 +ÐаÑтройки +Език +Език: +Редактор +&Редактор: + +2200 +СиÑтема +ÐÑоцииране на 7-Zip Ñ: +2301 +Интегриране на 7-Zip в контекÑтното меню на шела +КаÑкадно контекÑтно меню +Елементи на контекÑтното меню: +2320 +<ДиректориÑ> +<Ðрхив> +ОтварÑне на архив +Разархивиране на файловете... +ДобавÑне към архив... +Проверка на архива +Разархивиране тук +Разархивиране в {0} +ДобавÑне в {0} +Ðрхивиране и изпращане... +Ðрхивиране в {0} и изпращане +2400 +Директории +&Работна Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ +&СиÑтемната TEMP Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ +&Текущата +&Друга: +Използване Ñамо за преноÑими ноÑители +Указване на мÑÑто за временните архиви. +2500 +ÐаÑтройки +Показване на обекта ".." +Показване на иÑтинÑките икони на файловете +Показване на ÑиÑтемното меню +&Избор на цÑл ред +Показване на помощни &линии + +&Ðлтернативен режим на избор +Използване на &големи Ñтраници от паметта +2900 +Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ +7-Zip Ñе разпроÑтранÑва като Ñвободен Ñофтуер. Ð’Ñе пак, можете да подпомогнете разработката на 7-zip, като Ñе региÑтрирате. +3000 +СиÑтемата не може да задели необходимото количеÑтво памет. +ÐÑма грешки в архива +{0} обект(и) избрани +Ðе може да бъде Ñъздадена Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ '{0}' +Този архив не поддържа операции за изменение. +Файлът '{0}' не може да Ñе отвори като архив +КриптираниÑÑ‚ архив '{0}' не може да Ñе отвори. Грешка в паролата? +Ðрхив от тип, който не Ñе поддържа +Файлът {0} вече ÑъщеÑтвува +Файлът '{0}' е бил променен.\nИÑкате ли да обновите копието му в архива? +Ðе може да бъде обновен файл \n'{0}' +Ðе може да бъде Ñтартиран редактора. +Файлът прилича на Ð²Ð¸Ñ€ÑƒÑ (името му Ñъдържа дълги поредици интервали). +ОперациÑта не може да бъде извикана от Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ Ñ Ñ‚Ð¾Ð»ÐºÐ¾Ð²Ð° дълъг път. +ТрÑбва да Ñе избере един файл +ТрÑбва да Ñе изберат един или повече файлове +Твърде много обекти +3300 +Разархивиране +КомпреÑÐ¸Ñ +Проверка +ОтварÑне... +ПретърÑване... +3400 +Разархивиране +&Разархивиране в: +Избор на мÑÑто за разархивираните файлове. +3410 +Режим за пътищата +Пълни пътища +Без пътища +3420 +Режим за презапиÑване +Потвърждение преди презапиÑване +ПрезапиÑване без потвърждение +ПропуÑкане на ÑъщеÑтвуващите файлове +Ðвтоматично преименуване +Ðвтоматично преименуване на ÑъщеÑтвуващите файлове +3500 +Подтвърдете замÑната на файла +ДиректориÑта вече Ñъдържа файл Ñ Ñ‚Ð°ÐºÐ¾Ð²Ð° име. +Желаете ли да замените ÑъщеÑÑ‚Ð²ÑƒÐ²Ð°Ñ‰Ð¸Ñ Ñ„Ð°Ð¹Ð» +Ñ Ñ‚Ð¾Ð·Ð¸ файл? +{0} байта +&Ðвтоматично преименуване +3700 +Ðеподдържан метод за компреÑÐ¸Ñ Ð²ÑŠÐ² файл '{0}'. +Грешка в данните в '{0}'. Файлът е повреден. +Проверката на Ñ†Ð¸ÐºÐ»Ð¸Ñ‡Ð½Ð¸Ñ Ð¾Ñтатък даде грешка в '{0}'. Файлът е повреден. +Грешка в данните в ÐºÑ€Ð¸Ð¿Ñ‚Ð¸Ñ€Ð°Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð» '{0}'. Грешка в паролата? +Проверката на Ñ†Ð¸ÐºÐ»Ð¸Ñ‡Ð½Ð¸Ñ Ð¾Ñтатък даде грешка в ÐºÑ€Ð¸Ð¿Ñ‚Ð¸Ñ€Ð°Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð» '{0}'. Грешка в паролата? +3800 +Въведете парола +Въведете парола: +Въведете повторно парола: +&Показване на паролата +Двете пароли не Ñъвпадат +За паролата Ñи използвайте Ñамо латинÑки букви, цифри и Ñпециални Ñимволи (!, #, $, ...) +Паролата е твърде дълга +&Парола +3900 +Изминало време: +ОÑтаващо време: +Размер: +СкороÑÑ‚: +Обработени: +Коефициент на компреÑиÑ: +Грешки: +Ðрхиви: +4000 +ДобавÑне към архив +&Ðрхив: +Режим за изменение: +Формат на архива: +&Ðиво на компреÑиÑ: +Метод за компреÑиÑ: +Размер на &речника: +Размер на &думата: +Размер на непрекъÑнат блок: +Брой процеÑорни нишки: +&Параметри: +&Опции +Самора&зархивиращ Ñе архив +КомпреÑирани Ñподелени файлове +Криптиране +Метод за криптиране: +Криптиране на файловите &имена +Използвана памет за архивиране: +Използвана памет за разархивиране: +4050 +Без компреÑÐ¸Ñ +Ðай-бърза +Бърза +Ðормална +МакÑимална +Ултра +4060 +ДобавÑне и замÑна на файлове +ОбновÑване и добавÑне на файлове +ОпреÑнÑване на ÑъщеÑтвуващите файлове +Синхронизиране на файловете +4070 +Разглеждане +Ð’Ñички файлове +Non-solid +ÐепрекъÑната (solid) компреÑÐ¸Ñ +6000 +Копиране +ПремеÑтване +Копиране в: +ПремеÑтване в: +Копиране... +МеÑтене... +Преименуване... +Избор на целева директориÑ. +ОперациÑта не Ñе поддържа за тази директориÑ. +Грешка при преименуването на файл или Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ +Потвърждение за копирането на файл +Сигурни ли Ñте, че иÑкате да копирате файлове към архива? +6100 +Потвърждение за изтриването на файл +Потвърждение за изтриването на Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ +Потвърждение за изтриването на множеÑтво файлове +Сигурни ли Ñте, че иÑкате да изтриете '{0}'? +Сигурни ли Ñте, че иÑкате да изтриете диркеториÑта '{0}' Ñ Ñ†Ñлото й Ñъдържание? +Сигурни ли Ñте, че иÑкате да изтриете тези {0} обекта? +Изтриване... +Грешка при изтриване на файл или Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ +СиÑтемата не може да изтрие файл Ñ Ñ‚Ð¾Ð»ÐºÐ¾Ð²Ð° дълъг път +6300 +Създаване на Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ +Създаване на файл +Име на директориÑ: +Име на файл: +Ðова Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ +Ðов файл +Грешка при Ñъздаване на Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ +Грешка при Ñъздаване на файл +6400 +Коментар +&Коментар: +Маркиране +Размаркиране +МаÑка: +6600 +СвойÑтва +ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð½Ð° директориите +ДиагноÑтични ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ +Съобщение +7100 +Компютър +Мрежа +Документи +СиÑтема +7200 +ДобавÑне +Извличане +ТеÑтване +Копиране +ПремеÑтване +Изтриване +Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ +7300 +РазделÑне на файл +&РазделÑне на: +РазделÑне на &томове, байтове: +РазделÑне... +Потвърждение на разделÑнето +Сигурни ли Ñте, че иÑкате да разделите файла на {0} тома? +Размерът на том трÑбва да бъде по-малък от размера на Ð¾Ñ€Ð¸Ð³Ð¸Ð½Ð°Ð»Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð» +Ðевалиден размер на том +Указан размер на том: {0} байта.\nСигурни ли Ñте, че иÑкате да разделите архива на томове Ñ Ñ‚Ð°ÐºÑŠÐ² размер? +7400 +ОбединÑване на файлове +&ОбединÑване в: +ОбединÑване... +Избиране Ñамо на първата чаÑÑ‚ от Ñ€Ð°Ð·Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð» +Файлът не Ñе разпознава като чаÑÑ‚ от разделен оригинален файл +Ðе Ñе открива повече от една чаÑÑ‚ от Ñ€Ð°Ð·Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð» +7500 +ИзчиÑлÑване на контролната Ñума... +Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° контролната Ñума +CRC контролна Ñума за данни: +CRC контролна Ñума за данни и имена: +7600 +СтатиÑтика +Използвана памет: +Ðрхивиране +Разархивиране +Оценка +Обща оценка +Текущо +Резултат + + +УÑпешно преминали: diff --git a/Utils/7-Zip/Lang/bn.txt b/Utils/7-Zip/Lang/bn.txt new file mode 100644 index 000000000..3e1865694 --- /dev/null +++ b/Utils/7-Zip/Lang/bn.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.46 : Team Oruddho (Fahad Mohammad Shaon, Mahmud Hassan) : http://www.oruddho.com +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Bangla +বাংলা +401 +ঠিক আছে +বাতিল + + + +&হà§à¦¯à¦¾à¦ +&না +&বনà§à¦§ করা +সাহাযà§à¦¯ + +&চালিয়ে যাওয়া +440 +&সবগà§à¦²à§‹à¦° জনà§à¦¯ হà§à¦¯à¦¾à¦ +স&বগà§à¦²à§‹à¦° জনà§à¦¯ না +বনà§à¦§ +আবার শà§à¦°à§ +&পটভূমি +& সামনে +&বিরতি +বিরতিতে অবসà§à¦¥à¦¾à¦¨à¦°à¦¤ +আপনি বাতিল করতে ইচà§à¦›à§à¦•? +500 +&ফাইল +&পরিবরà§à¦¤à¦¨ +পà§à¦°à¦¦à¦°à§à¦¶à¦¨& +&পà§à¦°à¦¿à§Ÿ +&দরকারি +&সহায়তা +540 +&উনà§à¦®à§à¦•à§à¦¤ করা +7-zip-ঠউনà§à¦®à§à¦•à§à¦¤ করা +বাহিরে উনà§à¦®à§à¦•à§à¦¤ করা +&পà§à¦°à¦¦à¦°à§à¦¶à¦¨ +&পরিবরà§à¦¤à¦¨ +নাম পরিবরà§à¦¤à¦¨ +&অনà§à¦²à¦¿à¦ªà¦¿ হবে... +পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨ হবে... +&মà§à¦›à§‡ ফেলা +&ফাইল খনà§à¦¡à¦¾à§Ÿà¦¨... +ফাইল সংযোজন... +বৈশিষà§à¦Ÿà¦¾à¦¬à¦²à¦¿ +মনà§à¦¤à¦¬à§à¦¯ +Checksum গননা করা + +ফোলà§à¦¡à¦¾à¦° সৃষà§à¦Ÿà¦¿ +ফাইল সৃষà§à¦Ÿà¦¿ +বাহির +600 +সব নিরà§à¦¬à¦¾à¦šà¦¨ +নিরà§à¦¬à¦¾à¦šà¦¨ রদ করা +উলà§à¦Ÿà§‹ নিরà§à¦¬à¦¾à¦šà¦¨ +নিরà§à¦¬à¦¾à¦šà¦¨... +নিরà§à¦¬à¦¾à¦šà¦¨ রদ করা... +ধরণ অনà§à¦¯à¦¾à§Ÿà§€ নিরà§à¦¬à¦¾à¦šà¦¨ +ধরণ অনà§à¦¯à¦¾à§Ÿà§€ নিরà§à¦¬à¦¾à¦šà¦¨ রদ করা +700 +বৃহৎ পà§à¦°à¦¤à¦¿à¦• +ছোটà§à¦Ÿ পà§à¦°à¦¤à¦¿à¦• +&তালিকা +&বিবরণ +730 +অসজà§à¦œà¦¿à¦¤ +সমতল সজà§à¦œà¦¾ +&বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾à¦ªà¦• দà§à¦¬à¦¿à¦–নà§à¦¡à¦¨ +&দরকারিখà§à¦Ÿà¦¿ +মূল ফোলà§à¦¡à¦¾à¦° উনà§à¦®à§à¦•à§à¦¤ করা +à¦à¦• পরà§à¦¯à¦¾à§Ÿ উপরে ... +ফোলà§à¦¡à¦¾à¦°à§‡à¦° অতীত বিবরণ... +&সতেজতা +750 +সংকোচোন বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾à¦ªà¦• খà§à¦Ÿà¦¿ +সাধারণ খà§à¦Ÿà¦¿ +বৃহৎ বোতাম +বোতামের শিরোনাম পà§à¦°à¦¦à¦°à§à¦¶à¦¨ +800 +&পà§à¦°à¦¿à§Ÿ ফোলà§à¦¡à¦¾à¦° হিসাবে সংযোজন ... +পছনà§à¦¦à§‡à¦° তালিকা +900 +&পছনà§à¦¦à¦—à§à¦²à§‹... +&বেঞà§à¦šà¦®à¦¾à¦°à§à¦• +960 +&সাহাযà§à¦¯... +&7-Zip সরà§à¦®à§à¦ªà¦•ে... +1003 +অবসà§à¦¥à¦¾à¦¨ +নাম +পরিচয় +ফোলà§à¦¡à¦¾à¦° +আকার +সংকà§à¦šà¦¿à¦¤ আকার +বৈশিষà§à¦Ÿ +সৃষà§à¦Ÿà¦¿ হয়েছে +বà§à¦¯à¦¬à¦¹à¦¾à¦° হয়েছে +পরিবরà§à¦§à¦¨ হয়েছে +দৃৠ+Commented +আটকানো +খনà§à¦¡à¦¨à§‡à¦° পূরà§à¦¬à§‡ +খনà§à¦¡à¦¨à§‡à¦° পরে +অভিধান +CRC +ধরন +বিরোধী +পদà§à¦§à¦¤à¦¿ +চলতি অপারেটিং সিসà§à¦Ÿà§‡à¦® +ফাইল বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾ +বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•ারী +দল +বাধা +মনà§à¦¤à¦¬à§à¦¯ +অবসà§à¦¥à¦¾à¦¨ +পথের বিশেষায়ণ (Path Prefix) + + + + + + + + + + + + + + + + + + + + + + + + +তà§à¦°à§à¦Ÿà¦¿ +সমà§à¦ªà§‚রà§à¦£ আকার +অবশিষà§à¦Ÿ জায়গা +কà§à¦²à¦¾à¦¸à§à¦Ÿà¦¾à¦°à§‡à¦° আকার +শিরোনাম +সà§à¦¥à¦¾à¦¨à§€à§Ÿ নাম +বিতরণকারী +2100 +পছনà§à¦¦à¦—à§à¦²à§‹ +ভাষা +ভাষা: +সমà§à¦ªà¦¾à¦¦à¦• +বরà§à¦¤à¦®à¦¾à¦¨ সমà§à¦ªà¦¾à¦¦à¦• : + +2200 +বরà§à¦¤à¦®à¦¾à¦¨ অবসà§à¦¥à¦¾ +7-Zip-à¦à¦° সাথে সমà§à¦ªà¦°à§à¦•িত : +2301 +সাহাযà§à¦¯à¦•ারী তালিকায় 7-Zip সংযোজন +সাহাযà§à¦¯à¦•ারী তালিকায় à¦à¦•ের ভিতর সব গà§à¦Ÿà¦¿à§Ÿà§‡ ফেলা +সাহাযà§à¦¯à¦•ারী তালিকার বিষয়সমূহ: +2320 +<ফোলà§à¦¡à¦¾à¦°> +<সংকà§à¦šà¦¿à¦¤ ফাইল> +সংকà§à¦šà¦¿à¦¤ ফাইল চালৠকরা +ফাইল সমà§à¦ªà§à¦°à¦¸à¦¾à¦°à¦£... +সংকà§à¦šà¦¿à¦¤ ফাইলে সংযোজন... +সংকà§à¦šà¦¿à¦¤ ফাইল নিরীকà§à¦·à¦£ +à¦à¦–ানেই সমà§à¦ªà§à¦°à¦¸à¦¾à¦°à¦£ +সমà§à¦ªà§à¦°à¦¸à¦¾à¦°à¦£ করা হবে {0} +সযোজন করা হবে {0} +সংকোচন à¦à¦¬à¦‚ ই-মেইল... +সংকোচন - {0} à¦à¦¬à¦‚ ই-মেইল +2400 +ফোলà§à¦¡à¦¾à¦° +&কারà§à¦¯à¦°à¦¤ ফোলà§à¦¡à¦¾à¦° +&অসà§à¦¥à¦¾à§Ÿà§€ ফোলà§à¦¡à¦¾à¦° +&পà§à¦°à¦šà¦²à¦¿à¦¤ +&নিরà§à¦¦à¦¿à¦·à§à¦Ÿ: +অসà§à¦¥à¦¾à§Ÿà§€ অংশের জনà§à¦¯ বà§à¦¯à¦¬à¦¹à¦¾à¦° করা +অসà§à¦¥à¦¾à§Ÿà§€ ফোলà§à¦¡à¦¾à¦° নিরà§à¦¬à¦¾à¦šà¦¨ করà§à¦¨à¥¤ +2500 +পছনà§à¦¦à¦—à§à¦²à§‹ +".." ফাইল পà§à¦°à¦¦à¦°à§à¦¶à¦¨ +ফাইলের আসল পà§à¦°à¦¤à¦¿à¦• দেখানো +কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦° চালকের তালিকা দেখানো +পূরà§à¦£ পরà§à¦¯à¦¾à§Ÿ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ +ছকের লাইন পà§à¦°à¦¦à¦°à§à¦¶à¦¨ + +পরিপূরক নিবাচনের পদà§à¦§à¦¤à¦¿ +বেশি সà§à¦®à§ƒà¦¤à¦¿à¦° বà§à¦¯à¦¬à¦¹à¦¾à¦° +2900 +7-Zip সমà§à¦ªà¦°à§à¦•ে +7-Zip à¦à¦•টি মà§à¦•à§à¦¤ পà§à¦°à§‹à¦—à§à¦°à¦¾à¦® কিনà§à¦¤à§ à¦à¦Ÿà¦¿ 7-Zip à¦à¦° কতৃপকà§à¦·à§‡à¦° কাচে নিবনà§à¦§à¦¨à§‡à¦° মাধà§à¦¯à¦®à§‡ আপনি উনà§à¦¨à¦¤ সেবা পেতে পারেন +3000 + +কোন তà§à¦°à§à¦Ÿà¦¿ নেই +{0} ফাইল(সমূহ) নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ +'{0}' ফোলà§à¦¡à¦¾à¦° সৃষà§à¦Ÿà¦¿ করা সমà§à¦­à¦¬ হচà§à¦›à§‡à¦¨à¦¾ +à¦à¦‡ সংকোচনের কà§à¦·à§‡à¦¤à§à¦°à§‡ à¦à¦‡ সেবা পà§à¦°à¦¦à¦¾à¦¨ করা সমà§à¦­à¦¬ হচà§à¦›à§‡ না। +'{0}' -কে সংকà§à¦šà¦¿à¦¤ ফাইল হিসেবে চালৠকরা সমà§à¦­à¦¬ হচà§à¦›à§‡à¦¨à¦¾ +'{0}' বদà§à¦§ সংকà§à¦šà¦¿à¦¤ ফাইল চালৠকরা সমà§à¦­à¦¬ হচà§à¦›à§‡à¦¨à¦¾. ভà§à¦² পাসওয়ারà§à¦¡? + + +ফাইলটি '{0}' পরিমারà§à¦œà¦¿à¦¤.\nআপনি সংকà§à¦šà¦¿à¦¤ ফাইলটি ও পরিমারà§à¦œà¦¨ করতে চান? +পরিমারà§à¦œà¦¨ করা সমà§à¦­à¦¬ হয়নি\n'{0}' +সমà§à¦ªà¦¾à¦¦à¦• চালৠকরা সমà§à¦­à¦¬ নয় + + + + +অনেক বেশী ফাইল +3300 +সমà§à¦ªà§à¦°à¦¸à¦¾à¦°à¦£ করা হচà§à¦›à§‡ +সংকোচায়ন পà§à¦°à¦•à§à¦°à¦¿à§Ÿà¦¾à¦§à§€à¦¨ +নিরকà§à¦·à¦£ করছে ... +উনà§à¦®à§à¦•à§à¦¤ করা হচà§à¦›à§‡... +তথà§à¦¯ সংগà§à¦°à¦¹ চলছে... (Scanning...) +3400 +সমà§à¦ªà§à¦°à¦¸à¦¾à¦°à¦£ +&সমà§à¦ªà§à¦°à¦¸à¦¾à¦°à¦£ করা হবে: +ফাইল সমà§à¦ªà§à¦°à¦¸à¦¾à¦°à¦¨à§‡à¦° ঠিকানা +3410 +ঠিকানা নিরà§à¦¬à¦¾à¦šà¦¨ পদà§à¦§à¦¤à¦¿ +পূরà§à¦£ ঠিকানাসমূহ +ঠিকানাবিহীন +3420 +পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨ পদà§à¦§à¦¤à¦¿ +পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨à§‡à¦° পূরà§à¦¬à¦¾à¦­à¦¾à¦¸ +আভাসবিহীন পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨ +à¦à¦•ই পরিচয় পà§à¦°à¦¾à¦ªà§à¦¤ ফাইল à¦à§œà¦¿à§Ÿà§‡ চলা +সà§à¦¬à§Ÿà¦‚কà§à¦°à¦¿à§Ÿ পà§à¦ƒà¦¨à¦¾à¦®à¦•রণ +à¦à¦•ই পরিচয় পà§à¦°à¦¾à¦ªà§à¦¤ ফাইলের নাম পরিবরà§à¦¤à§à¦¨ +3500 +ফাইল পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨ নিশà§à¦šà¦¿à¦¤ করণ +নিরà§à¦§à¦¾à¦°à¦¿à¦¤ ফোলà§à¦¡à¦¾à¦°à§‡ ফাইলটি আগেথেকেই আছে +আপনিকি বরà§à¦¤à¦®à¦¾à¦¨ ফাইলটি পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨ করতে চান? +à¦à¦‡à¦Ÿà¦¿à¦° সাথে? +{0} bytesবাইট +সà§à¦¬à§Ÿà¦‚কà§à¦°à¦¿à§Ÿ পà§à¦ƒà¦¨à¦¾à¦®à¦•রণ +3700 +অসমরà§à¦¥à¦¿à¦¤ সংকোচন পদà§à¦§à¦¤à¦¿ -'{0}'. +'{0}' ফাইলে তà§à¦°à§à¦Ÿà¦¿à¦ªà§‚রà§à¦£ তথà§à¦¯. ফাইলটি খনà§à¦¡à¦¿à¦¤ +'{0}' ফাইলে CRC বà§à¦¯à¦°à§à¦¥. ফাইলটি খনà§à¦¡à¦¿à¦¤ +'{0}' বদà§à¦§ ফাইলে তথà§à¦¯à§‡ তà§à¦°à§à¦Ÿà¦¿. ভà§à¦² পাসওয়ারà§à¦¡? +'{0}' বদà§à¦§ ফাইলে CRC বà§à¦¯à¦°à§à¦¥. ভà§à¦² পাসওয়ারà§à¦¡? +3800 +পাসওয়ারà§à¦¡à¦Ÿà¦¿ পà§à¦°à¦¬à§‡à¦¶ করà§à¦¨à¦ƒ +পাসওয়ারà§à¦¡à¦Ÿà¦¿ পà§à¦°à¦¬à§‡à¦¶ করà§à¦¨à¦ƒ +আবার পাসওয়ারà§à¦¡ পà§à¦°à¦¬à§‡à¦¶ করà§à¦¨à¦ƒ +&পাসওয়ারà§à¦¡ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ +পাসওয়ারà§à¦¡ দà§à¦Ÿà¦¿ à¦à¦•ই নয় +শà§à¦§à§ ইংলিশ বরà§à¦£, সংখà§à¦¯à¦¾ à¦à¦¬à¦‚ বিশেষ বরà§à¦£ (!, #, $, ...) পাসওয়ারà§à¦¡ হিসেবে বà§à¦¯à¦¬à¦¹à¦¾à¦° করà§à¦¨ +পাসওয়ারà§à¦¡à¦Ÿà¦¿ খà§à¦¬ বেশী বড় হয়েছে +পাসওরà§à§Ÿà¦¾à¦¡ +3900 +অতিবাহিত সময়ঃ +সময় বাকি আছেঃ +আকার: +গতি: + + +বিফলতা : + +4000 +সংকোচনে সংযোজন +&সংকোচন +&পরিমারà§à¦œà¦¨ পদà§à¦§à¦¤à¦¿: +সংকোচনের & পরিচয়: +সংকোচনের &পরà§à¦¯à¦¾à§Ÿ: +সংকোচন &পদà§à¦§à¦¤à¦¿: +&Dictionary size: +&Word size: +Solid block size: +CPU-à¦à¦° thread-à¦à¦° সংখà§à¦¯à¦¾: +&Parameters: +পছনà§à¦¦à¦¨à§€à§Ÿ +সà§à¦¬à§Ÿà¦‚কà§à¦°à¦¿à§Ÿ সংকোচন পà§à¦°à§‹à¦—à§à¦°à¦¾à¦® তৈরি +বিনিময়যোগà§à¦¯ ফাইল সংকোচন +বদà§à¦§ করা +বদà§à¦§ করার পদà§à¦§à¦¤à¦¿: +ফাইলের নাম &আটকে ফেলা +সংকোচনের জনà§à¦¯ সà§à¦®à§ƒà¦¤à¦¿à¦° বà§à¦¯à¦¬à¦¹à¦¾à¦°: +সমà§à¦ªà§à¦°à¦¸à¦¾à¦°à¦¨à§‡à¦° জনà§à¦¯ সà§à¦®à§ƒà¦¤à¦¿à¦° বà§à¦¯à¦¬à¦¹à¦¾à¦°: +4050 +অতি সংকোচায়ন +অতি দà§à¦°à§à¦¤ +দà§à¦°à§à¦¤ +সাধারন +সরà§à¦¬à§à¦¬à§‹à¦šà§à¦š +পলকের গতি +4060 +সংকোচন ও ফাইল পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨ +পরিমারà§à¦œà¦¨ ও ফাইল পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨ +উলà§à¦²à§‡à¦–িত ফাইলে সতেজতা পà§à¦°à¦¦à¦¾à¦¨ +ফাইল সাজিয়ে রাখা +4070 +বিচরণ +সকল ফাইল +Non-solid +Solid +6000 +অনà§à¦²à¦¿à¦ªà¦¿ গà§à¦°à¦¹à¦¨ +অনà§à¦²à¦¿à¦ªà¦¿ গà§à¦°à¦¹à¦¨ à¦à¦¬à¦‚ মà§à¦›à§‡ ফেলা +অনà§à¦²à¦¿à¦ªà¦¿ করা হবে: +পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¿à¦¤ হবে: +অনà§à¦²à¦¿à¦ªà¦¿ করা হচà§à¦›à§‡... +পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¿à¦¤ হচà§à¦›à§‡... +নাম পরিবরà§à¦¤à¦¨... +গনà§à¦¤à¦¬à§à¦¯ ফোলà§à¦¡à¦¾à¦° নিরà§à¦¬à¦¾à¦šà¦¨. +কারà§à¦¯à¦Ÿà¦¿ সমà§à¦­à¦¬ নয় +ফাইল বা ফোলà§à¦¡à¦¾à¦°à§‡à¦° নাম পরিবরà§à¦¤à¦¨à§‡ সমà§à¦­à¦¬ নয় +ফাইল অনà§à¦²à¦¿à¦ªà¦¿ নিশà§à¦šà¦¿à¦¤à¦•রণ +আপনি কি ফাইলগà§à¦²à§‹à¦•ে সংকà§à¦šà¦¿à¦¤ ফাইলে অনà§à¦²à¦¿à¦ªà¦¿ গà§à¦°à¦¹à¦£ করতে চান। +6100 +ফাইলটি মà§à¦›à§‡ ফেলতে কি আপনি নিশà§à¦šà¦¿à¦¤ +ফোলà§à¦¡à¦¾à¦°à¦Ÿà¦¿ মà§à¦›à§‡ ফেলতে কি আপনি নিশà§à¦šà¦¿à¦¤ +ফাইলটি মà§à¦›à§‡ ফেলতে কি আপনি নিশà§à¦šà¦¿à¦¤ +মà§à¦›à§‡ ফেলতে আপনি কি নিশà§à¦šà¦¿à¦¤ - '{0}'? +'{0}' ফোলà§à¦¡à¦¾à¦° à¦à¦¬à¦‚ à¦à¦° সব ফাইল আপনি কি মà§à¦›à§‡ ফেলতে নিশà§à¦šà¦¿à¦¤? +নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ {0} টি ফাইল আপনি কি মà§à¦›à§‡ ফেলতে নিশà§à¦šà¦¿à¦¤? +মà§à¦›à§‡ ফেলা হচà§à¦›à§‡... +ফাইল বা ফোলà§à¦¡à¦¾à¦° মà§à¦›à§‡ ফেলাতে সমসà§à¦¯à¦¾ হচà§à¦›à§‡ + +6300 +ফোলà§à¦¡à¦¾à¦° সৃষà§à¦Ÿà¦¿ +ফাইল সৃষà§à¦Ÿà¦¿ +ফোলà§à¦¡à¦¾à¦°à§‡à¦° নাম: +ফাইল নাম: +নতà§à¦¨ ফোলà§à¦¡à¦¾à¦° +নতà§à¦¨ ধারক +ফোলà§à¦¡à¦¾à¦° সৃষà§à¦Ÿà¦¿à¦¤à§‡ সমসà§à¦¯à¦¾ +ফাইল সৃষà§à¦Ÿà¦¿à¦¤à§‡ সমসà§à¦¯à¦¾ +6400 +মনà§à¦¤à¦¬à§à¦¯ +&মনà§à¦¤à¦¬à§à¦¯: +নিরà§à¦¬à¦¾à¦šà¦¨ +নিরà§à¦¬à¦¾à¦šà¦¨ রদ করা +আড়াল করা: +6600 + +ফোলà§à¦¡à¦¾à¦°à§‡à¦° অতিত বিবরন +সমসà§à¦¯à¦¾ নিরাময় আভাস +আভাস +7100 +কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦° +আনà§à¦¤à¦ƒ সমà§à¦ªà¦°à§à¦• + +কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦° চালক +7200 +সংজোযন +সমà§à¦ªà§à¦°à¦¸à¦¾à¦°à¦¨ +নিরীকà§à¦·à¦£ +অনà§à¦²à¦¿à¦ªà¦¿ গà§à¦°à¦¹à¦¨ +পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨ +মà§à¦›à§‡ ফেলা +তথà§à¦¯ +7300 +ফাইল খনà§à¦¡à¦¾à§Ÿà¦¨ +&ফাইল খনà§à¦¡à¦¾à§Ÿà¦¿à¦¤ হবে: +volumes(খনà§à¦¡à§‡), bytes(বাইটস)-ঠখনà§à¦¡à¦¾à§Ÿà¦¨à¦ƒ +ফাইল খনà§à¦¡à¦¾à§Ÿà¦¨ চলছে... +ফাইল খনà§à¦¡à¦¾à§Ÿà¦¨ নিশà§à¦šà¦¿à¦¤à¦•রণ +আপনি কি সংকà§à¦šà¦¿à¦¤ ফাইলটিকে {0} খনà§à¦¡à§‡ খনà§à¦¡à¦¾à§Ÿà¦¨ করতে চান? +খনà§à¦¡à§‡à¦° আকার অবশà§à¦¯à¦‡ মূল ফাইলের চেয়ে ছোট হতে হবে +খনà§à¦¡à§‡à¦° আকারে ভà§à¦² +উলà§à¦²à§‡à¦•à§à¦·à¦¿à¦¤ খনà§à¦¡à§‡à¦° আকার : {0} bytes.\nআপনি কি সংকোচিত ফাইলটিকে ঠভাবেই খনà§à¦¡à§‡ খনà§à¦¡à¦¾à§Ÿà¦¨ করতে চান? +7400 +ফাইল à¦à¦•ীভূতি করণ +&à¦à¦•ীভূতি করা হবে: +à¦à¦•ীভূতি চলছে... +শà§à¦§à§ পà§à¦°à¦¥à¦® ফাইলটি নিরà§à¦¬à¦¾à¦šà¦¨ করà§à¦¨ + + +7500 +Checksum গননা চলছে... +Checksum তথà§à¦¯ +তথà§à¦¯à§‡à¦° জনà§à¦¯ CRC checksum: +তথà§à¦¯ à¦à¦¬à¦‚ নামের জনà§à¦¯ CRC checksum: +7600 +বেঞà§à¦šà¦®à¦¾à¦°à§à¦• +বà§à¦¯à¦¬à¦¹à§ƒà¦¤ সà§à¦®à§ƒà¦¤à¦¿ : +সংকোচায়ন ... +সমà§à¦ªà§à¦°à¦¸à¦¾à¦°à¦£ ... +রেটিং +মোট রেটিং +চলতি +ফলাফল +CPU বà§à¦¯à¦¬à¦¹à¦¾à¦° করছে +Rating / বà§à¦¯à¦¬à¦¹à¦¾à¦° করছে +সফলতা : diff --git a/Utils/7-Zip/Lang/br.txt b/Utils/7-Zip/Lang/br.txt new file mode 100644 index 000000000..f7f7fa3b8 --- /dev/null +++ b/Utils/7-Zip/Lang/br.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 3.12 : KAD-Korvigelloù An Drouizig (drouizig.org). +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Breton +Brezhoneg +401 +Mat eo +Nullañ + + + +&Ya +&Ket +&Serriñ +Skoazell + +&Kenderc'hel +440 +Ya be&pred +Ket &bepred +Paouez +Adloc'hañ +&Drekleur +&Rakleur +&Ehan +Ehanet +Ha fellout a ra deoc'h nullañ ? +500 +&Restr +&Aozañ +&Gwelout +Di&baboù +&Ostilhoù +&Skoazell +540 +&Digeriñ +Digeriñ a-zia&barzh +Digeriñ a-zia&vaez +&Gwelout +&Aozañ +Adenv&el +&Kopiañ diwar... +&Dilec'hiañ diwar... +D&ilemel +&Troc'hañ restr... +&Kendeuziñ restroù... +P&erzhioù +Evezhia&denn + + +Sevel un teul +Sevel ur restr +&Kuitaat +600 +Diuz pep &tra +Diziuz pe tra +Lakaat an &diuzad war an tu gin +Diuz... +Diziuz... +Diuz diouzh ar rizh +Diziuz diouzh ar rizh +700 +Arlunioù &bras +Arlunioù &bihan +&Roll +&Munudoù +730 +Dirummet + +&2 brenestr +&Barrennoù ostilhoù +Digeriñ an teul gwrizienn +Teul kerent +Roll istor an teul... +Fresk&aat +750 +Barrenn ziell +Barrenn skouerek +Meudellioù bras +Diskouez an destenn +800 +&Ouzhpennañ ar c'havlec'h d'ar sinedoù +Sined +900 +&Dibaboù... +&Amprouiñ +960 +&Roll ar pennadoù... +A-&zivout 7-Zip... +1003 +Treug +Anv +Astenn +Teul +Ment +Gwasket +Doareennoù +Savet d'ar +Stoket d'ar +Kemmet d'ar +Solut +Evezhiadenn +Ennodet +Rannañ a-raok +Rannañ war-lerc'h +Geriadur +CRC +Rizh +Enep +Hentenn +OS ostiz +Reizhiad restroù +Implijour +Strollad +Bloc'h +Evezhiadenn +Lec'hiadur + + + + + + + + + + + + + + + + + + + + + + + + + +Fazi +Ment en holl +Egor vak +Ment ar c'hleusteurioù +Skritellig +Anv lec'hel +Pourchaser +2100 +Dibaboù +Yezh +Yezh : +Embanner +&Embanner : + +2200 +Reizhiad +Kenstagañ 7-Zip ouzh : +2301 +Lakaat 7-Zip el lañser kemperzhel +Lañser kemperzhel a-steud +Elfennoù al lañser kemperzhel : +2320 + + +Digeriñ +Eztennañ ar restroù... +Ouzhpennañ d'an diell... +Gwiriañ an diell +Eztennañ amañ +Eztennañ diwar {0} +Ouzhpennañ da {0} +Gwaskañ ha kas dre postel... +Gwaskañ diwar {0} ha kas dre postel. +2400 +Teulioù +Teulioù &labour +Teul dibadelus ar &reizhiad +Teul &red +Teul &spisaet : +Implijout nemet evit ar mediaoù dilec'hus +Spisait un teul evit lakaat ar restroù diell dibadelus. +2500 +Perzhioù +Diskouez an elfenn ".." +Diskouez arlunioù gwirion ar restroù +Diskouez al lañser reizhiad +&Diuz ar bannoù a-bezh +Diskouez al &linennoù kael + + + +2900 +Keloù +Digoust eo ar meziant 7-Zip. Mar plij deoc'h 7-zip ha mar fell deoc'h skoazellañ ar raktres-mañ e c'hellit donezoniñ argant da 7-Zip. +3000 + +N'eus fazi ebet +{0} elfenn ziuzet +N'haller ket sevel ar restr '{0}' +N'haller ket ober gant an oberiadennoù nevesaat evti an diell-mañ. + + + + +Kemmet ez eo bet ar restr '{0}'.\nHa fellout a ra deoc'h he nevesaat en diell ? +N'haller ket nevesaat\n'{0}' +N'haller ket loc'hañ an embanner. + + + + +Re a elfennoù +3300 +Eztennañ +O waskañ +Gwiriañ +O tigeriñ... + +3400 +Eztennañ +E&ztennañ diwar : +Dibabit un teul evit eztennañ an restroù. +3410 +Treugoù +Treugoù klok +Treug ebet +3420 +Mod erlec'hiañ +Goulenn a-raok erlec'hiañ +Erlec'hiañ hep goulenn +Lakaat ar restroù a zo anezho a-gostez +Adenvel ent-emgefre ar restroù a zo anezho + +3500 +Kadarnaat a-raok erlec'hiañ ur restr +Emañ dija ur restr gant ar memes anv en teul bukenn. +Ha fellout a ra deoc'h lakaat e-lec'h +ar restr da heul ? +{0} eizhtet +Adenvel ent-&emgefre +3700 +Hentenn waskañ direizh evit '{0}'. +Stlenn faziet e-barzh '{0}'. Gwastet eo ar restr. +Fazi ar reoliñ CRC evit '{0}'. Gwastet eo ar restr. + + +3800 +Roit ar ger-tremen +Roit ar ger-tremen : + +&Diskouez ar ger-tremen + + + +Ger-tremen +3900 +Amzer dremenet : +Amzer o chom : +Ment : +Tizh : + + +Fazioù : + +4000 +Ouzhpennañ d'an diell +&Diell : +&Mod nevesaat : +&Mentrezh an diell : +L&ive gwaskañ : +Rizh &gwaskañ: +&Ment ar geriadur : +Me&nt ar gerioù : + + +&Perzhioù: +&Dibaboù +Sevel un diell SF&X + + + +Ennodiñ an &anvioù restroù +Memor evit ar waskerezh : +Memor evit an diwaskerezh : +4050 +Gwaskañ ebet +Primañ +Prim +Reizh +Uhelañ +Gour +4060 +Ouzhpennañ hag erlec'hiañ ar restroù +Nevesaat hag ouzhpennañ ar restroù +Freskaat ar restroù a zo anezho +Goubredañ ar restroù +4070 +Furchal +An holl restroù + + +6000 +Kopiañ +Dilec'hiañ +Kopiañ e-barzh : +Dilec'hiañ diwar : +O kopiañ... +O tilec'hiañ... +Oc'h adenvel... + +N'haller ket ober an oberiadenn-mañ. +Fazi oc'h adenvel ar restr pe an teul + + +6100 +Kadarnañ a-raok dilemel ar restr +Kadarnañ a-raok dilemel an teul +Kadarnañ a-raok dilemel an holl restroù +Ha fellout a ra deoc'h dilemel '{0}' ? +Ha fellout a ra deoc'h dilemel an teul '{0}' ha pep tra a zo e-barzh ? +Ha fellout a ra deoc'h dilemel ar {0} elfenn-mañ ? +O tilemel... +Fazo o tilemel ar restr pe an teul + +6300 +Sevel un teul +Sevel ur restr +Anv an teul : +Anv restr : +Teul nevez +Restr nevez +Fazi o sevel an teul +Fazi o sevel ar restr +6400 +Evezhiadenn +&Evezhiadenn : +Diuz +Diziuz +Kuzh : +6600 + +Roll istor an teulioù +Kemennoù yalc'h +Kemenn +7100 +Urzhiataer +Rouedad + +Reizhiad +7200 +Ouzhpennañ +Eztennañ +Amprouiñ +Kopiañ +Dilec'hiañ +Dilemel +Keloù +7300 +Troc'hañ restr +&Troc'hañ da : +Troc'hañ e &levrennoù, eizhtetoù : +O troc'hañ... + + + + + +7400 +Kendeuziñ restroù +&Kendeuziñ da : +O kendeuziñ... + + + +7500 + + + + +7600 +Amprouiñ +Implij ar vemor : +Gwaskerezh +Diwaskerezh +Feur +Feur en holl +Red +Da heul + + +Tremenioù : diff --git a/Utils/7-Zip/Lang/ca.txt b/Utils/7-Zip/Lang/ca.txt new file mode 100644 index 000000000..767d93521 --- /dev/null +++ b/Utils/7-Zip/Lang/ca.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.07 : Josep Casals, Marc Folch +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Catalan +Català +401 +D'acord +Cancel.la + + + +&Sí +&No +Tan&ca +Ajuda + +&Continua +440 +Sí a &tot +No a t&ot +Atura +Reinicia +Rerefons +Primer pla +&Pausa +Aturat +Esteu segur que voleu cancel.lar? +500 +&Fitxer +&Edita +&Visualitza +&Preferits +E&ines +Aj&uda +540 +&Obre +Obre d&ins +Obre fora +&Visualitza +&Edita +Renom&ena +&Copia a... +&Moure a... +Suprimeix +&Divideix el fitxer... +Com&bina el fitxer... +P&ropietats +Come&ntari +Calcula el checksum +Diff +Crea carpeta +Crea fitxer +Surt +600 +Seleccion&a-ho tot +Deselecciona-ho tot +&Inverteix selecció +Selecciona... +Deselecciona... +Selecciona per tipus +Deselecciona per tipus +700 +Icones g&rans +Icones petites +&Llista +&Detalls +730 +No ordenat +Vista plana +&2 Taules +&Barres d'eines +Obre carpeta arrel +Carpeta pare +Historial de carpetes... +&Actualitza +750 +Barra d'eines afegeix/extreu +Barra d'eines estàndard +Botons grans +Mostra botons amb text +800 +&Afegeix la carpeta als Preferits com +Personal +900 +Opcions... +&Test de referència +960 +&Contingut... +Quant a 7-Zip... +1003 +Adreça +Nom +Tipus de fitxer +Carpeta +Mida +Mida comprimit +Atributs +Creat +Darrer accés +Darrera modificació +Compacte +Comentari +Xifrat +Expandit abans +Expandit després +Diccionari +CRC +Tipus +Anti +Mètode +SO orígen +Sistema de fitxers +Usuari +Grup +Bloc +Comentari +Posició +Path Prefix +Carpetes +Fitxers +Versió +Volum +Multivolum +Desplaçament +Enllaços +Blocs +Volums + +64-bits +Big-endian +CPU +Mida física +Mida capçaleres +Suma de verificació +Característiques +Adreça virtual +ID +Nom curt +Aplicació creadora +Mida del sector +Mode +Enllaç +Error +Mida total +Espai lliure +Mida sector +Etiqueta +Nom local +Proveïdor +2100 +Opcions +Llengua +Llengua: +Editor +&Editor: +&Diff: +2200 +Sistema +Associa 7-Zip amb: +2301 +Integra 7-Zip dins el menú contextual de Windows +Menú contextual en cascada +Objectes del menú contextual: +2320 + + +Obre +Extreu fitxers... +Afegeix al fitxer... +Comprova el fitxer +Extreu a aquesta carpeta +Extreu a {0} +Afegeix a {0} +Comprimeix i envia per correu electrònic... +Comprimeix a {0} i envia per correu electrònic +2400 +Carpetes +Carpeta de &treball +Carpeta temporal del &sistema +Carpeta a&ctual +E&specifica una carpeta: +Utilitza únicament per a discs extraïbles +Especifica una carpeta pels fitxers temporals. +2500 +Selecció +Mostra l'element ".." +Mostra icones reals dels fitxers +Mostra el menú de sistema +&Selecció de columna completa +Mostra les línies de &graella +Obre amb un sol clic els elements +Mode de selecció &alternatiu +Usa pàgines de memòria &grans +2900 +Informació sobre 7-Zip +7-Zip és programari lliure. De totes maneres, podeu col.laborar en el seu desenvolupament registrant el programa. +3000 +El sistema no pot assignar la quantitat de memòria requerida +No hi ha errors +{0} objecte(s) seleccionat(s) +No es pot crear la carpeta '{0}' +Aquest tipus de fitxer no permet actualització. +No es pot obrir el fitxe '{0}' com a arxiu +No es pot obrir el fitxer xifrat '{0}'. La contrasenya és incorrecta? +Tipus d'arxiu no admès +El fitxer {0} ja existeix +El fitxer '{0}' ha estat modificat.\nVoleu actualitzar-lo a l'arxiu? +No pot actualitzar-se el fitxer\n'{0}' +No pot executar-se l'editor. +El fitxer sembla un virus (el nom del fitxer conté espais molt llargs al nom). +L'operació no es pot cridar des d'una carpeta amb una ruta llarga. +Heu de seleccionar un fitxer +Heu de seleccionar un o més fitxers +Massa objectes +3300 +Extraient +Comprimint +Provant +Obrint... +Scanning... +3400 +Extreu +E&xtreu a: +Seleccioneu una destinació pels fitxers extrets. +3410 +Mode d'adreça +Adreça completa +Sense adreça +3420 +Sobreescriure +Amb confirmació +Sense confirmació +Conserva els fitxers ja existents +Reanomena automàticament +Auto-reanomena fitxers existents +3500 +Confirmeu substitució de fitxers +La carpeta de destí conté un fitxer amb el mateix nom. +Voleu substituir el fitxer existent +per aquest altre? +{0} bytes +Renomena a&utomàticament +3700 +Mètode de compressió no vàlid per a '{0}'. +Error de dades en '{0}'. El fitxer és corrupte. +CRC ha fallat en '{0}'. El fitxer és corrupte. +Error de dades al fitxer xifrat '{0}'. Contrasenya errònia? +CRC ha fallat al fitxer xifrat '{0}'. Contrasenya errònia? +3800 +Introduïu la contrasenya +Introduïu la contrasenya: +Torneu a introduir la contrasenya: +Mo&stra la contrasenya +Les contrasenyes no coincideixen +Utilitza només lletres (sense accents), números i caràcters especials (!, #, $, ...) a la contrasenya +La contrasenya és massa llarga +Contrasenya +3900 +Temps transcorregut: +Temps restant: +Mida: +Taxa: +Processat: +Ràtio de compressió: +Errors: +Arxius: +4000 +Afegir al fitxer +&Fitxer: +Mode d'act&ualització: +&Format del fitxer: +Nivell de &compressió: +&Tipus de compressió: +Mida del &diccionari: +Mida de la paraula: +Mida de bloc sòlid: +Nombre de fils de la CPU: +&Paràmetres: +Opcions +Crea fitxer SF&X +Comprimeix fitxers compartits +Xifrat +Mètode de xifrat: +Xifra el nom dels fitxers +Ús de memòria per comprimir: +Ús de memòria per descomprimir: +4050 +Sense compressió +Ràpida + +Normal +Màxima +Ultra +4060 +Afegeix i substitueix fitxers +Actualitza i afegeix fitxers +Actualitza fitxers ja presents +Sincronitza fitxers +4070 +Visualitza +Tots els fitxers +No sòlid +Sòlid +6000 +Copia +Mou +Copia a: +Mou a: +Copiant... +Movent... +Renomenant... +Seleccioneu una carpeta de destí. +Operació no permesa. +Error renomenant fitxer o carpeta +Confirmeu la còpia del fitxer +Esteu segur que voleu copiar els fitxers a l'arxiu +6100 +Confirmeu la supressió del fitxer +Confirmeu la supressió de la carpeta +Confirmeu supressió múltiple de fitxers +Esteu segur que voleu suprimir '{0}'? +Esteu segur que voleu suprimir la carpeta '{0}' i tot el seu contingut? +Esteu segur que voleu esborrar aquests {0} elements? +Suprimint... +Error esborrant fitxer o carpeta +El sistema no pot moure un fitxer amb una ruta llarga a la paperea de reciclatge +6300 +Crea carpeta +Crea arxiu +Nom de carpeta: +Nom d'arxiu: +Carpeta nova +Fitxer nou +Error creant carpeta +Error creant el fitxer +6400 +Comentari +&Comentari: +Selecciona +No selecciona +Màscara: +6600 +Properties +Historial de carpetes +Missatges de diagnosi +Missatge +7100 +El meu ordinador +Entorn de xarxa +Documents +Sistema +7200 +Afegeix +Extreu +Prova +Copia +Mou +Suprimeix +Info +7300 +Divideix fitxer +&Divideix a: +Divideix en &volums, bytes: +Dividint... +Confirmació de la divisió +Esteu segur que voleu dividir el fitxer en {0} volums? +La mida del volum ha de ser més petita que la mida del fitxer original +Mida del volum incorrecte +Mida de volum especificada: {0} bytes.\nEsteu segur que voleu dividir el fitxer en aquests volums? +7400 +Combina fitxers +&Combina a: +Combinant... +Seleccioneu només el primer fitxer +No es pot detectar un fitxer com a una part del fitxer dividit +No es pot trobar més d'una part del fitxer dividit +7500 +Calculant el checksum... +Informació del checksum +CRC checksum per les dades: +CRC checksum per les dades i els noms: +7600 +Test de referència +Ús de la memòria: +Comprimint +Decomprimint +Taxa +Taxa total +Actual +Resultant +Ús de la CPU +Taxa / Ús +Passades: diff --git a/Utils/7-Zip/Lang/co.txt b/Utils/7-Zip/Lang/co.txt new file mode 100644 index 000000000..4b05003b6 --- /dev/null +++ b/Utils/7-Zip/Lang/co.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 15.00 : Patriccollu di Santa Maria è Sichè +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Corsican +Corsu +401 +OK +Abbandunà + + + +&Iè +I&nnò +&Chjode +Aiutu + +&Cuntinuà +440 +Iè per &Tutti +Innò per T&utti +Piantà +Riavvià +&Arci pianu +P&rimu pianu +&Pausa +In pausa +Site sicuru di vulè abbandunà ? +500 +&Schedariu +&Mudificà +&Affissà +&Favuriti +A&ttrezzi +A&iutu +540 +&Apre +Apre Den&tru +Apre F&ora +A&ffissà +&Mudificà +&Rinumà +&Cupià Ver Di... +&Dispiazzà Ver Di... +S&quassà +&Sparte u schedariu... +&Unisce i schedarii... +&Pruprietà +Cumme&ntu... +Calculà a somma di cuntrollu +Paragunà e sfarenze +Creà un Cartulare +Creà un Schedariu +&Esce +Leia +Flussi A<ernativi +600 +Selezziunà &Tuttu +Ùn selezziunà &Nunda +&Arritrusà a Selezzione +&Selezziunà... +Ùn selezziunà &micca... +Selezziunà da u Tipu +Ùn Selezziunà da u Tipu +700 +Icone &Maiò +Icone &Chjuche +&Lista +&Detaglii +730 +Non &Ordinatu +&Vista Sparta +&2 Finestre +&Barre d'Attrezzi +Apre u Cartulare di &Radica +Livellu &Superiore +Crunulugia di i Cartulari... +&Attualizà +Attualisazione Autumatica +750 +Barra d'Attrezzi d'Archiviu +Barra d'Attrezzi Classica +Buttoni Maiò +Affissà u Testu di i Buttoni +800 +&Aghjunghje u cartulare à i Favuriti cum'è +Indetta +900 +&Ozzioni... +&Sperimentu di pussibilità +960 +&Cuntenutu (in inglese)... +&Apprupositu di 7-Zip... +1003 +Passeghju +Nome +Estensione +Cartulare +Dimensione +Dimensione Cumpressa +Attributi +Creatu u +Accessu u +Mudificatu u +Solidu +Cummentatu +Cifratu +Frazziunà Nanzu +Frazziunà Dopu +Dizziunariu + +Tipu +Anti +Metoda +OS ospite +Sistema di Schedariu +Utilizatore +Gruppu +Bloccu +Cummentu +Pusizione +Prefissu di Passeghju +Cartulari +Schedarii +Versione +Vulume +Multi-Vulume +Offset +Leie +Blocchi +Vulumi + +64-bit +Big-endian +CPU +Dimensione Fisica +Dimensione di l'Intestature +Somma di cuntrollu +Caratteristiche +Indirizzu Virtuale +ID +Nome Cortu +Appiecazione d'Urigine +Dimensione di Settore +Modu +Leia Simbolica +Sbagliu +Dimensione Tutale +Spaziu Liberu +Dimensione di Cluster +Nome di Vulume +Nome Lucale +Furnidore +Sicurità NT +Flussu Alternativu +Ausiliaru +Squassatu +In Arburu + + +Tipu di Sbagliu +Sbaglii +Sbaglii +Avertimenti +Avertimentu +Flussi +Flussi Alternativi +Dimensione di i Flussi Alternativi +Dimensione Virtuale +Dimensione Senza Compressione +Dimensione Fisica Tutale +Indice di Vulume +SottuTipu +Cummentu Cortu +Pagina di Codice + + + +Dimensione di a Coda +Dimensione di u Mozzicone Incurpuratu +Leia +Leia Solida +iNode + +Lettura sola +2100 +Ozzioni +Lingua +Lingua : +Editore +&Editore : +Paragunà e sfarenze : +2200 +Sistema +Assucià 7-Zip cù : +Tutti l'utilizatori +2301 +Integrà 7-Zip à l'interfaccia cuntestuale +Interfaccia cuntestuale in cascata +Elementi di l'interfaccia cuntestuale : +Icone in l'interfaccia cuntestuale +2320 + + +Apre l'archiviu +Estrae i schedarii... +Aghjunghje à l'archiviu... +Verificà l'archiviu +Estrae Quì +Estrae ver di {0} +Aghjunghje à {0} +Cumprime è mandà da email... +Cumprime ver di {0} è mandà da email. +2400 +Cartulari +Cartulare di &travagliu +Cartulare timpurariu di &sistema +&Currente +&Specificatu : +Impiegà solu per i dischi amuvibili +Specificate un cartulare per i schedarii timpurarii d'archiviu. +2500 +Preferenze +Affissà l'elementu ".." +Affissà e vere icone di i schedarii +Affissà l'interfaccia sistema +Selezziunà tutta a linea +Affissà linee &quadrittate +Cliccu unicu per apre un elementu +Modu di selezzione &alternativa +Impiegà pagine maiò di memoria +2900 +Apprupositu di 7-Zip +7-Zip hè un prugramma liberu.\n\nTraduttu in lingua corsa da Patriccollu di Santa Maria è Sichè. +3000 +U sistema ùn pò micca attribuisce a quantità richiesta di memoria +Ùn ci hè micca sbagliu +{0} ughjettu(i) selezziunatu(i) +U cartulare '{0}' ùn pò micca esse creatu +L'azzioni di mudificazione ùn sò micca pussibule per quessu archiviu. +U schedariu '{0}' ùn pò micca esse apertu cum'è un archiviu +L'archiviu cifratu '{0}' ùn pò micca esse apertu. Parolla d'intrata falsa ? +Stu tipu d'archiviu ùn hè micca accettatu +U schedariu {0} esiste dighjà +U schedariu '{0}' hè statu mudificatu.\nVulete cambiallu in l'archiviu ? +Ùn hè micca pussibule di cambià u schedariu\n'{0}' +Ùn hè micca pussibule d'avvià l'editore. +U schedariu hè podasse infettatu da un virus (u so nome cuntene spazii numerosi). +St'azzione ùn pò micca fassi dapoi un cartulare cù u nome di passeghju cusì longu. +Ci vole à selezziunà un schedariu +Ci vole à selezziunà al menu un schedariu +Troppu elementi +Ùn hè micca pussibule d'apre u schedariu cum'è un archiviu {0} +U schedariu hè apertu cum'è un archiviu {0} +L'archiviu hè apertu cù offset +3300 +Estrazzione +Cumpressione +Verificazione +Apertura... +Esplurazione... +Cacciatura +3320 +Aghjuntu +Mudificazione +Analisa +Riproduzzione +Rimballasgiu +Tralasciamentu +Squassatura +Creazione di l'intestatura in corsu +3400 +Estrae +E&strae ver di : +Sciglite un cartulare per l'estrazzione di i schedarii. +3410 +Modu di passeghju : +Nomi cumpletti di passeghju +Alcunu nome di passeghju +Nomi assuluti di passeghju +Nomi relativi di passeghju +3420 +Modu di rimpiazzamentu : +Cunfirmà nanzu di rimpiazzà +Rimpiazzà senza dumandà +Ignurà i schedarii esistenti +Rinumà autumaticamente +Rinumà autumaticamente i schedarii esistenti +3430 +Ùn cupià micca u cartulare di radica +Risturà a sicurità di i schedarii +3500 +Cunfirmà u Rimpiazzamentu di Schedariu +U cartulare di distinazione cuntene dighjà un schedariu cù stu nome. +Vulete rimpiazzà u schedariu esistentu +cù quessu ? +{0} ottetti +Rinumà &Autumaticamente +3700 +Metoda di cumpressione micca accettatu per '{0}'. +Sbagliu di dati in '{0}'. U schedariu hè alteratu. +Fiascu di l'ispezzione CRC per u schedariu '{0}'. U schedariu hè alteratu. +Sbagliu di dati in u schedariu cifratu '{0}'. Parolla d'intrata falsa ? +Fiascu di l'ispezzione CRC per u schedariu cifratu '{0}'. Parolla d'intrata falsa ? +3710 +Parolla d'intrata falsa ? +3721 +Metoda di compressione micca accettatu +Sbagliu di dati +Fiascu di CRC +Dati micca dispunibule +Fine inaspettata di dati +Ci hè d'altri dati dopu à a fine di i dati ghjuvevule +Ùn hè un archiviu +Sbagliu d'Intestature +Parolla d'intrata falsa +3763 +Principiu di l'archiviu micca dispunibule +Principiu di l'archiviu micca confirmatu + + + +Funzione micca accettata +3800 +Scrivite a parolla d'intrata +Scrivite a parolla d'intrata : +Scrivite torna a parolla d'intrata : +&Affissà a parolla d'intrata +E parolle d'intrata sò sfarente +Per a parolla d'intrata, pudete impiegà solu : lettere senza aletta, cifri è segni particulari (!, #, $, ...) +A parolla d'intrata hè troppu longa +Parolla d'intrata +3900 +Tempu passatu : +Tempu rimanentu : +Dimensione tutale : +Celerità : +Prucessu : +Reditu di cumpressione : +Sbaglii : +Archivii : +4000 +Aghjunghje à l'archiviu +&Archiviu : +Modu di m&udificazione : +&Forma d'archiviu : +&Livellu de cumpressione : +&Metoda di cumpressione : +&Dimensione di u dizziunariu : +Dimensione di &e parolle : +Dimensione di u bloccu solidu : +Numeru di flussi CPU : +&Parametri : +Ozzioni +Creà un archiviu SF&X +Cumprime schedarii sparti +Cifratura +Metoda di cifratura : +Cifrà i &nomi di schedariu +Memoria impiegata da a Cumpressione : +Memoria impiegata da a Scumpressione : +Squassà i schedarii dopu à a cumpressione +4040 +Cunservà e leie simboliche +Cunservà e leie solide +Cunservà i flussi di dati alternativi +Cunservà a sicurità di i schedarii +4050 +Alcuna +A più rapida +Rapida +Nurmale +Massima +Ultra +4060 +Aghjunghje è rimpiazzà i schedarii +Mudificà è aghjunghje i schedarii +Attualizà i schedarii esistenti +Sincrunizà i schedarii +4070 +Sfuglià +Tutti i Schedarii +Non-solidu +Solidu +6000 +Cupià +Dispiazzà +Cupià ver di : +Dispiazzà ver di : +Copia in corsu... +Dispiazzamentu in corsu... +Cambiamentu di nome in corsu... +Selezziunà u cartulare di distinazione. +St'azzione ùn hè micca accettata per stu cartulare. +Sbagliu durante u Cambiu di Nome di Schedariu o di Cartulare +Cunfirmazione di a Copia di Schedariu +Site sicuru di vulè cupià u(i) schedariu(i) ver di l'archiviu +6100 +Cunfirmà a Squassatura di u Schedariu +Cunfirmà a Squassatura di u Cartulare +Cunfirmà a Squassatura di Schedarii Multiplice +Site sicuru di vulè squassà '{0}' ? +Site sicuru di vulè squassà u cartulare '{0}' è tuttu u so cuntenutu ? +Site sicuru di vulè squassà sti {0} elementi ? +Squassatura in corsu... +Sbagliu durante a Squassatura di Schedariu o di Cartulare +U sistema ùn pò micca mette à a Rumenzula un schedariu cù u nome di passeghju cusì longu +6300 +Creà un Cartulare +Creà un Schedariu +Nome di cartulare : +Nome di schedariu : +Novu Cartulare +Novu Schedariu +Sbagliu durante a Creazione di Cartulare +Sbagliu durante a Creazione di Schedariu +6400 +Cummentu +&Cummentu : +Selezziunà +Ùn Selezziunà +Filtru : +6600 +Pruprietà +Crunulugia di i Cartulari +Messaghji di diagnosticu +Messaghju +7100 +Urdinatore +Reta +Ducumenti +Sistema +7200 +Aghjunghje +Estrae +Verificà +Cupià +Dispiazzà +Squassà +Infurmazione +7300 +Sparte u schedariu +&Sparte in : +Sparte in &vulumi, ottetti : +Spartimentu... +Cunfirmà u Spartimentu +Site sicuru di vulè sparte u schedariu in {0} vulumi ? +A dimensione di u vulume deve esse più chjucu chì u schedariu d'urighjine +Dimensione di vulume falsa +Dimensione di vulume specificata : {0} ottetti.\nSite sicuru di vulè taglià l'archiviu in tali vulumi ? +7400 +Unisce i Schedarii +&Unisce in : +Unione... +Selezziunà solu a prima parte di l'archiviu spartutu +Ùn si trova alcuna parte d'archiviu spartutu +Ùn si trova micca più d'una parte d'archiviu spartutu +7500 +Calculu di a somma di cuntrollu... +Infurmazione nant'à a somma di cuntrollu +Somma di cuntrollu CRC per i dati : +Somma di cuntrollu CRC per i dati è i nomi : +7600 +Sperimentu di pussibilità +Memoria impiegata : +Cumpressione +Scumpressione +Percentuale +Percentuale tutale +Attuale +Risultante +Impiegu CPU +Estimatu / Impiegatu +Passagi : +7700 +Leia +Ligà +Leia d'urigine : +Leia di distinazione : +7710 +Tipu di Leia +Leia Solida +Leia Simbolica di Schedariu +Leia Simbolica di Cartulare +Unione di Cartulare diff --git a/Utils/7-Zip/Lang/cs.txt b/Utils/7-Zip/Lang/cs.txt new file mode 100644 index 000000000..8eb5a2a3d --- /dev/null +++ b/Utils/7-Zip/Lang/cs.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.30 : Milan Hrubý +; 4.33 : Michal Molhanec +; 9.07 : Jiří Malák +; +; +; +; +; +; +; +; +0 +7-Zip +Czech +ÄŒeÅ¡tina +401 +OK +Storno + + + +&Ano +&Ne +Zavří&t +NápovÄ›da + +Po&kraÄovat +440 +Ano na &vÅ¡echno +N&e na vÅ¡echno +Zastavit +Spustit znovu +&Pozadí +P&opÅ™edí +Po&zastavit +Pozastaveno +Jste si jistí, že to chcete stornovat? +500 +&Soubor +Úpr&avy +&Zobrazení +&Oblíbené +&Nástroje +Nápo&vÄ›da +540 +&Otevřít +Otevřít u&vnitÅ™ +Otevřít &mimo +&Zobrazit +&Upravit +&PÅ™ejmenovat +Kopírovat &do... +PÅ™&esunout do... +Vymaza&t +&RozdÄ›lit soubor... +&SlouÄit soubory... +Vlast&nosti +Poznámk&a +VypoÄítat kontrolní souÄet +Porovnat soubory +VytvoÅ™it složku +VytvoÅ™it soubor +&Konec +600 +Vybrat &vÅ¡e +ZruÅ¡it výbÄ›r vÅ¡e +&Invertovat výbÄ›r +Vybrat... +ZruÅ¡it výbÄ›r... +Vybrat podle typu +ZruÅ¡it výbÄ›r podle typu +700 +&Velké ikony +&Malé ikony +&Seznam +&Podrobnosti +730 +&Bez třídÄ›ní +"Ploché" zobrazení +&2 panely +Nástrojové liÅ¡ty +Otevřít koÅ™enovou složku +O úroveň výš +Historie složek... +&Obnovit +750 +ArchivaÄní liÅ¡ta +Standardní liÅ¡ta +Velká tlaÄítka +Zobrazovat text tlaÄítek +800 +&PÅ™idat složku do oblíbených jako +Záložka +900 +&Možnosti... +&ZkouÅ¡ka výkonu +960 +&Obsah... +O progr&amu 7-Zip... +1003 +Cesta +Název +Přípona +Složka +Velikost +Komprimovaná velikost +Atributy +VytvoÅ™en +Použit +ZmÄ›nÄ›n +Pevný +S poznámkou +Zakódovaný +RozdÄ›len do +RozdÄ›len od +Slovník +CRC +Typ +Anti +Metoda +Hostitelský OS +Souborový systém +Uživatel +Skupina +Blok +Poznámka +Pozice +Cesta +Složky +Soubory +Verze +Díl +Vícedílný +Offset +Odkazy +Bloků +Dílů + +64-bit +Big-endian +Pocesor +Fyzická velikost +Velikost hlaviÄek +Kontrolní souÄet +Charakteristiky +Virtuální adresa +ID +Krátké jméno +Autor +Velikost sektoru +Režim +Odkaz +Chyba +Celková velikost +Volné místo +Velikost clusteru +OznaÄení +Místní název +Poskytovatel +2100 +Možnosti +Jazyk +Jazyk: +Editor +&Editor: +Program pro &porovnání souborů: +2200 +Systém +Asociovat 7-Zip s: +2301 +&Integrovat 7-Zip do kontextového menu +S&tupňovité kontextové menu +&Položky kontextového menu: +2320 + + +Otevřít +Rozbalit soubory... +PÅ™idat do archivu... +Zkontrolovat archiv +Rozbalit zde +Rozbalit do {0} +PÅ™idat do {0} +Zkomprimovat a odeslat poÅ¡tou... +Zkomprimovat do {0} a odeslat poÅ¡tou +2400 +Složky +Pracovní složka +&Systémová složka pro doÄasné soubory +&Aktuální +S&ložka: +&Používat pouze pro vyjímatelné disky +Vyberte umístÄ›ní pro doÄasné komprimované soubory. +2500 +Nastavení +Zobrazovat položku ".." +Zobrazovat skuteÄnou ikonu souboru +Zobrazovat systémové menu +&Vybírat celý řádek +Zobrazovat &mřížku +Otevřít položku jedním kliknutím +&Alternativní způsob výbÄ›ru +&Používat velké stránky pamÄ›ti +2900 +O programu 7-Zip +7-Zip je svobodný software. NicménÄ› můžete podpoÅ™it jeho vývoj registrací. +3000 +Systém nemůže pÅ™idÄ›lit požadovanou velikost pamÄ›ti +NedoÅ¡lo k žádným chybám +vybráno {0} objekt(ů) +Nelze vytvoÅ™it složku '{0}' +Aktualizace není podporována pro tento archiv. +Soubor '{0}' nelze otevřít jako archiv +Zakódovaný archiv '{0}' nelze otevřít. Å patné heslo? +Nepodporovaný typ archivu +Soubor {0} již existuje +Soubor '{0}' byl zmÄ›nÄ›n.\nChcete ho aktualizovat v archivu? +Nelze aktualizovat soubor\n'{0}' +Editor nelze spustit. +Soubor se jeví jako virus (ve jménu souboru jsou dlouhé mezery). +Operace nemůže být provedena ze složky s dlouhou cestou. +Musíte vybrat jeden soubor +Musíte vybrat jeden nebo více souborů +PříliÅ¡ mnoho položek +3300 +Rozbalování +Komprimování +Konrola +Otevírání... +Prohledávání... +3400 +Rozbalit +&Rozbalit do: +Vyberte umístÄ›ní pro rozbalené soubory. +3410 +Cesty +Plné cesty +Bez cesty +3420 +Způsob pÅ™episování +Zeptat se pÅ™ed pÅ™episem +PÅ™epsat bez výzvy +PÅ™eskoÄit existující soubory +Automatické pÅ™ejmenování +Automatické pÅ™ejmenování existujících souborů +3500 +Potvrzení nahrazení souboru +Cílová složka již obsahuje zpracovaný soubor. +Chcete nahradit existující soubor +tímto? +{0} bajtů +A&utomaticky pÅ™ejmenovat +3700 +Nepodporovaná komprimaÄní metoda pro '{0}'. +Chyba dat v '{0}'. Soubor je poÅ¡kozený. +Chyba CRC v '{0}'. Soubor je poÅ¡kozený. +Chyba dat v zakódovaném souboru '{0}'. Chybné heslo? +Chyba CRC v zakódovaném souboru '{0}'. Chybné heslo? +3800 +Vložit heslo +Vložit heslo: +Potvrzení hesla: +Zobrazit he&slo +Heslo nesouhlasí +Pro heslo použíjte pouze anglická písmena, Äíslice a speciální znaky (!, #, $, ...) +Heslo je příliÅ¡ dlouhé +Heslo +3900 +Uplynulý Äas: +Zbývající Äas: +Celková velikost: +Rychlost: +Zpracováno: +KomprimaÄní pomÄ›r: +Chyb: +Archívy: +4000 +PÅ™idat do archivu +&Archiv: +Způsob aktualizace: +&Formát archivu: +Ú&roveň komprese: +&KomprimaÄní metoda: +Ve&likost slovníku: +V&elikost slova: +Velikost bloku: +PoÄet vláken procesoru: +&Parametry: +Možnosti +VytvoÅ™it SF&X archiv +Zkomprimovat otevÅ™ené soubory +Zakódování +Metoda zakódování: +Zakódovat &názvy souborů +SpotÅ™eba pamÄ›ti pro zabalení: +SpotÅ™eba pamÄ›ti pro rozbalení: +4050 +Skladovací +Nejrychlejší +Rychlá +Normální +Maximální +Ultra +4060 +PÅ™idat a nahradit soubory +Aktualizovat a pÅ™idat soubory +Aktualizovat existující soubory +Synchronizovat soubory +4070 +Procházet +VÅ¡echny soubory +Podle velikosti souboru +Pevný +6000 +Kopírovat +PÅ™esunout +Kopírovat do: +PÅ™esunout do: +Kopírování... +PÅ™esouvání... +PÅ™ejmenování... +Vyberte cílovou složku. +Operace není podporována. +Chyba pÅ™i pÅ™ejmenování souboru nebo složky +Potvrzení kopírování souborů +Jste si jistí, že chcete zkopírovat soubory do archivu +6100 +Potvrdit vymazání souboru +Potvrdit vymazání složky +Potvrdit mnohonásobné vymazání souboru +Jste si jistí, že chcete vymazat '{0}'? +Jste si jistí, že chcete vymazat složku '{0}' a vÅ¡echno co obsahuje? +Jste si jistí, že chcete vymazat tyto {0} položky? +Mazání... +Chyba pÅ™i mazání souboru nebo složky +Systém nepodporuje pÅ™esun soubor s dlouhou cestou do Odpadkového koÅ¡e +6300 +VytvoÅ™it složku +VytvoÅ™it soubor +Název složky: +Název souboru: +Nová složka +Nový soubor +Chyba pÅ™i vytváření složky +Chyba pÅ™i vytváření souboru +6400 +Poznámka +&Poznámka: +Vybrat +ZruÅ¡it výbÄ›r +Maska: +6600 +Vlastnosti +Historie složek +Diagnostické zprávy +Zpráva +7100 +PoÄítaÄ +Síť +Dokumenty +Systém +7200 +PÅ™idat +Rozbalit +Zkontrolovat +Kopírovat +PÅ™esunout +Vymazat +Informace +7300 +RozdÄ›lit soubor +RozdÄ›lit do: +RozdÄ›lit na díly, bajtů: +RozdÄ›lování... +Potvrdit rozdÄ›lování +Jste si jistí, že chcete rozdÄ›lit soubor na {0} dílů? +Velikost dílu musí být menší než velikost původního souboru +Nesprávná velikost dílu +Zadaná velikost dílu: {0} bytů.\nJste si jistí, že chcete rozdÄ›lit archiv do takových dílů? +7400 +SlouÄit soubory +SlouÄit do: +SluÄování... +Musí se vybrat pouze první díl rozdÄ›leného soubor +NepodaÅ™ilo se rozpoznat rozdÄ›lený soubor +NepodaÅ™ilo se nalézt více než jeden díl rozdÄ›leného souboru +7500 +VypoÄítávání kontrolního souÄtu... +Informace o kontrolním souÄtu +CRC kontrolní souÄet pro data: +CRC kontrolní souÄet pro data a jména: +7600 +ZkouÅ¡ka výkonu +SpotÅ™eba pamÄ›ti: +Komprimování +Rozbalování +Výkon +Celkový výkon +Aktuální +Výsledné +Využití procesoru +Výkon / Využití +Průchodů: diff --git a/Utils/7-Zip/Lang/cy.txt b/Utils/7-Zip/Lang/cy.txt new file mode 100644 index 000000000..38be91b3b --- /dev/null +++ b/Utils/7-Zip/Lang/cy.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.37 : Owain Lewis +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Welsh +Cymraeg +401 +Iawn +Canslo + + + +&Iawn +&Na +&Cau +Cymorth + +P&arhau +440 +Iawn i'r &Cwbwl +Na i'r C&wbwl +Stopio +Ailgychwyn +&Cefndir +&Blaendir +&Pwyllo +Pwyllo +Ydych chi am canslo? +500 +&Ffeil +&Golygu +Gwe&ld +Ff&efrynnau +&Offer +&Cymorth +540 +&Agor +Agor tu &Mewn +Agor tu &Fas +Gwe&ld +&Golygu +A&ilenwi +&Copïo i... +&Symud i... +&Dileu +&Hollti ffeil... +Cy&funo ffeilau... +&Priodweddau +Syl&wad +Cyfrifo swm-gwirio + +Creu Ffolder +Creu Ffeil +Alla&n +600 +Dewis y C&yfan +Dad-ddewis y Cyfan +Gwrt&hdroi'r Dewis +Dewis... +Dad-ddewis... +Dewis trwy Math +Dad-ddewis trwy Math +700 +Eiconau &Mawr +Eiconau &Bach +&Rhestr +Ma&nylion +730 +Dad-dosbarthu +Golwg Flat +&2 Paneli +Bariau &Offer +Agor Ffolder Gwraidd +Lan Un Lefel +Hanes Ffolderi... +&Adnewyddu +750 +Bar Offer Archif +Bar Offer Arferol +Botwmau Fawr +Dangos Testun Botwmau +800 +&Ychwanegu ffolder i Ffefrynnau fel +Llyfrnod +900 +&Dewisiadau... +&Meincnod +960 +&Cynnwys... +&Manylion 7-Zip... +1003 +Llwybr +Enw +Estyniad +Ffolder +Maint +Maint wedi'i Cywasgu +Priodweddau +Crëwyd +Cyrchwyd +Addaswyd +Solet +Sylwad +Amgryptio +Hollti Cyn +Hollti ar Ôl +Geiriadur +CRC +Math +Anti +Dull +SW Cynnal +System Ffeiliau +Ddefnyddiwr +Grŵp +Bloc +Sylwad +Safle +Rhagddodiad y Llwybr + + + + + + + + + + + + + + + + + + + + + + + + +Gwall +Cyfanswm Maint +Lle Rhydd +Maint Clwstwr +Label +Enw Lleol +Darparwr +2100 +Dewisiadau +Iaith +Iaith: +Golygydd +&Golygydd: + +2200 +System +Cysylltu 7-Zip gyda: +2301 +Cyfuno 7-Zip mewn i dewislen cyd-destun y cribyn +Dewislen cyd-destun wedi'i rhaeadru +Eitemau dewislen cyd-destun: +2320 + + +Agor archif +Echdynnu ffeiliau... +Ychwanegu i'r archif... +Profi archif +Echdynnu Yma +Echdynnu i {0} +Ychwanegu i {0} +Cywasgu ac e-bostio... +Cywasgu i {0} ac e-bostio +2400 +Ffolderi +Ffolder &gweithio +Ffolder tymor byr y &system +&Cyfredol +&Penodol: +Defnyddiwch am gyriant symudadwy yn unig +Nodwch lleoliad am ffeiliau archif tymor byr. +2500 +Gosodiadau +Dangos eitem ".." +Dangos eicon ffeil go iawn +Dangos dewislen y system +Dethol &holl rhes +Dangos llinellau &grid + +Modd dethol &arallddewisiol +Defnyddiwch tudalenau cof &mawr +2900 +Manylion 7-Zip +Mae 7-Zip yn meddalwedd am ddim. Ond, gallwch cefnogi y \ndatblygiad o 7-Zip trwy cofrestru. +3000 + +Mae na ddim wallau +{0} gwrthrych(au) dethol +Ddim yn gallu creu y ffolder '{0}' +Mae gweithrediadau diweddaru ddim yn ateg am yr archif hyn. + + + + +Roedd ffeil '{0}' wedi'i addasu.\nWyt ti moen ei diweddaru yn yr \narchif? +Ddim yn gallu diweddaru ffeil\n'{0}' +Ddim yn gallu dechrau golygydd. + + + + +Gormod o eitemau +3300 +Echdynnu +Cywasgu +Ymbrofi +Agor... +Sganio... +3400 +Echdynnu +E&chdynnu i: +Nodwch lleoliad am echdynnu ffeiliau. +3410 +Modd llwybr +Enwau llwybr llawn +Dim enwau llwybr +3420 +Modd disodli +Gofyn cyn disodli +Disodli heb awgrymeb +Crychneidio ffeiliau presennol +Ailenwu yn awtomatig +Ailenwu ffeiliau presennol yn awtomatig +3500 +Cadarnhau Disodli Ffeil +Mae'r ffolder hon eisioes yn cynnwys y ffeil cyrchfan. +Hoffech chi ddisodli'r ffeil sy'n bodoli eisioes +Gyda hon? +{0} beit +Ailenwi A&wtomatig +3700 +Modd cywasgu ddim yn dilys am '{0}'. +Gwall data mewn '{0}'. Ffeil wedi'i torri. +CRC wedi'i methu mewn '{0}'. Ffeil wedi'i torri. + + +3800 +Mewnbynnwch cyfrinair +Mewnbynnwch cyfrinair: + +&Dangos cyfrinair + + + +Cyfrinair +3900 +Wedi treiglo: +Amser a'r ôl: +Maint: +Cyflymder: + + +Gwallau: + +4000 +Ychwanegu i'r archif +&Archif: +Modd &diweddaru: +&Fformat yr archif: +&Lefel cywasgu: +Dull &cywasgu: +Maint &geiriadur: +Maint geiria&u: + + +&Paramedrau: +Dewisiadau +Creu archif SF&X + + + +Amgryptio &enwau ffeiliau +Defnydd cof am Cywasgu: +Defnydd cof am Datgywasgu: +4050 +Storio +Cyflymach +Cyflum +Arferol +Uchafswm +Ultra +4060 +Ychwanegu ac amnewid ffeiliau +Diweddaru ac ychwanegu ffeiliau +Adnewyddu y ffeiliau presennol +Cyfamseru ffeiliau +4070 +Pori +Pob Ffeil + + +6000 +Copïo +Symud +Copïo i: +Symud i: +Copïo... +Symud... +Ailenwi... + +Mae'r gweithrediad ddim wedi'i ategu. +Gwall wrth Ailenwi Ffeil neu Ffolder +Cadarnhau Copi Ffeil +Ydych chi'n siŵr eich bod am copïo'r ffeiliau i'r archif +6100 +Cadarnhau Dileu Ffeil +Cadarnhau Dileu Ffolder +Cadarnhau Dileu Ffeiliau Amryfal +Ydych chi'n siŵr eich bod am dileu '{0}'? +Ydych chi'n siŵr eich bod am dileu y ffolder '{0}' ac ei holl \ncynnwys? +Ydych chi'n siŵr eich bod am dileu yr eitemau hyn {0}? +Dileu... +Gwall Dileu Ffeil neu Ffolder + +6300 +Creu Ffolder +Creu Ffeil +Enw Ffolder: +Enw Ffeil: +Ffolder Newydd +Ffeil Newydd +Gwall wrth Creu Ffolder +Gwall wrth Creu Ffeil +6400 +Sylwad +&Sylwad: +Dewis +Dad-ddewis +Mwgwd: +6600 + +Hanes Ffolderi +Neges diagnostig +Neges +7100 +Cyfrifiadur +Rhyngrwyd + +System +7200 +Ychwanegu +Echdynnu +Profi +Copïo +Symud +Dileu +Gwybodaeth +7300 +Hollti Ffeil +&Hollti i: +Hollti i &cyfeintiau, beitiau: +Hollti... + + + + + +7400 +Cyfuno Ffeiliau +&Cyfuno i: +Cyfuno... + + + +7500 +Cyfrifo swm-gwirio... +Gwybodaeth swm-gwirio +Swm-gwirio CRC am data: +Swm-gwirio CRC am data ac enwau: +7600 +Meincnod +Defnyddiad cof: +Cywasgu +Datgywasgu +Amcangyfrif +Amcangyfrif llwyr +Presennol +Canlyniad + + +Pasio: diff --git a/Utils/7-Zip/Lang/da.txt b/Utils/7-Zip/Lang/da.txt new file mode 100644 index 000000000..563b1978a --- /dev/null +++ b/Utils/7-Zip/Lang/da.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; : Jakob Schmidt +; 9.07 : Kian Andersen, Jørgen Rasmussen +; 15.00 : 2016-08-19 : scootergrisen +; +; +; +; +; +; +; +; +0 +7-Zip +Danish +Dansk +401 +OK +Annuller + + + +&Ja +&Nej +&Luk +Hjælp + +&Fortsæt +440 +Ja til &alle +Nej til a&lle +Stop +Genstart +&Baggrund +&Forgrund +&Pause +Pauset +Er du sikker pÃ¥, at du vil annullere? +500 +&Filer +R&ediger +&Vis +F&avoritter +Funk&tioner +&Hjælp +540 +&Ã…bn +Ã…bn &inden i +Ã…bn &uden for +&Vis +&Rediger +O&mdøb +&Kopier til... +&Flyt til... +S&let +&Opdel fil... +Kom&biner filer... +&Egenskaber +Komme&ntar... +Udregn checksum +Sammenlign +Opret mappe +Opret fil +&Afslut +Opret/rediger henvisning +&Alternative strømme +600 +Vælg &alle +Fravælg alle +&Omvendt markering +Vælg... +Fravælg... +Vælg efter type +Fravælg efter type +700 +Sto&re ikoner +S&mÃ¥ ikoner +&Liste +&Detaljer +730 +Usorteret +Flad visning +&2 paneler +&Værktøjslinjer +Ã…bn rodmappe +Et niveau op +Mappehistorik... +&Opdater +Opdater automatisk +750 +Arkivlinje +Standardlinje +Store knapper +Vis knappernes tekst +800 +&Føj mappe til Favoritter som +Bogmærke +900 +&Funktioner... +&Benchmark +960 +&Indhold... +&Om 7-Zip... +1003 +Sti +Navn +Filtype +Mappe +Størrelse +Pakket størrelse +Attributter +Oprettet +Ã…bnet +Ændret +Solid +Kommenteret +Krypteret +Opdel før +Opdel efter +Leksikon + +Type +Anti +Metode +Vært OS +Filsystem +Bruger +Gruppe +Blok +Kommentar +Placering +Sti præfiks +Mapper +Filer +Version +Bind +Flerbindsarkiv +Forskydning +Henvisning +Blokke +Bind + +64-bit +Big-endian +CPU +Fysisk størrelse +Hovedernes størrelse +Checksum +Karakteristika +Virtuel adresse +ID +Kort navn +Oprettende program +Sektorstørrelse +Tilstand +Symbolsk henvisning +Fejl +Samlet størrelse +Fri plads +Klyngestørrelse +Etiket +Lokalt navn +Udbyder +NT Security +Alternativ strøm +Aux +Slettet +Er træ + + +Fejltype +Fejl +Fejl +Advarsler +Advarsel +Strømme +Alternative strømme +Størrelse pÃ¥ alternative strømme +Virtuel størrelse +Udpakket størrelse +Samlet fysiske størrelse +Bind-indeks +Undertype +Kort kommentar +Tegnsæt + + + +Halestørrelse +Indlejret stumpstørrelse +Henvisning +Fast henvisning +iNode + +Skrivebeskyttet +2100 +Funktioner +Sprog +Sprog: +Redigering +&Rediger: +&Sammenlign: +2200 +System +Knyt 7-Zip til: +Alle brugere +2301 +Vis i Windows genvejsmenu +Brug undermenu +Punkter i genvejsmenu: +Ikoner i genvejsmenu +2320 + + +Ã…bn arkiv +Udpak filer... +Føj til arkiv... +Test arkiv +Udpak her +Udpak til {0} +Føj til {0} +Komprimer og vedhæft i e-mail... +Komprimer til {0} og vedhæft i e-mail +2400 +Mapper +&Arbejdsmappe +&Systemets midlertidige mappe +&Aktuelle +&Brugerdefineret: +Brug kun til flytbare drev +Angiv en placering til midlertidige arkivfiler. +2500 +Indstillinger +Vis ".."-element +Vis rigtige filikoner +Vis systemmenu +&Marker hele rækken +Vis &gitter +Ã…bn element med ét klik +&Alternativ markeringsmetode +Brug &store hukommelsessider +2900 +Om 7-Zip +7-Zip er fri software +3000 +Systemet kan ikke tildele den nødvendige mængde hukommelse +Der er ingen fejl +{0} markerede element(er) +Kan ikke oprette mappen "{0}" +Arkivet understøtter ikke opdateringshandlinger. +Kan ikke Ã¥bne filen "{0}" som arkiv +Kan ikke Ã¥bne det krypterede arkiv "{0}". Forkert adgangskode? +Arkivtypen er ikke understøttet +Filen {0} findes allerede +Filen "{0}" er blevet ændret.\nVil du opdatere den i arkivet? +Kan ikke opdatere filen\n"{0}" +Kan ikke starte redigering. +Filen ligner en virus (filnavnet indeholder lange mellemrum i navn). +Handlingen kan ikke kaldes fra en mappe som har en lang sti. +Du skal vælge en fil +Du skal vælge en eller flere filer +For mange elementer +Kan ikke Ã¥bne filen som {0}-arkiv +Filen er Ã¥bnet som {0}-arkiv +Arkivet er Ã¥bnet med forskydning +3300 +Udpakker +Komprimerer +Tester +Ã…bner... +Skanner... +Fjerner +3320 +Tilføjer +Opdaterer +Analyserer +Replikerer +Ompakker +Springer over +Sletter +Opretter hovede +3400 +Udpak +U&dpak til: +Angiv en placering til udpakkede filer. +3410 +Stitilstand: +Fulde stinavne +Ingen stinavne +Absolutte stinavne +Relative stinavne +3420 +Overskrivningstilstand: +Spørg før overskrivning +Overskriv uden at spørge +Spring eksisterende filer over +Automatisk omdøbning +Automatisk omdøbning af eksisterende filer +3430 +Forhindr duplikering af rodmappe +Genskab filsikkerhed +3500 +Bekræft filerstatning +Destinationsmappen indeholder allerede behandlet fil. +Vil du erstatte den eksisterende fil +med denne? +{0} byte +A&utomatisk omdøbning +3700 +Komprimeringsmetode for "{0}" er ikke understøttet. +Datafejl i "{0}". Filen er ødelagt. +CRC mislykkedes i "{0}". Filen er ødelagt. +Datafejl i den krypterede fil "{0}". Forkert adgangskode? +CRC mislykkedes i den krypterede fil "{0}". Forkert adgangskode? +3710 +Forkert adgangskode? +3721 +Komprimeringsmetoden er ikke understøttet +Datafejl +CRC mislykkedes +Utilgængelig data +Uventet slutning pÃ¥ data +Der er data efter slutningen af nyttedataene +Ikke et arkiv +Fejl i hoveder +Forkert adgangskode +3763 +Utilgængelig begyndelse pÃ¥ arkiv +Ubekræftet begyndelse pÃ¥ arkiv + + + +Faciliteten understøttes ikke +3800 +Indtast adgangskode +Indtast adgangskode: +Indtast adgangskode igen: +&Vis adgangskode +Adgangskoderne er ikke ens +Brug kun engelske bogstaver, numre og specialtegn (!, #, $, ...) til adgangskoden +Adgangskoden er for lang +Adgangskode +3900 +Forløbet tid: +Resterende tid: +Samlet størrelse: +Hastighed: +Behandlet: +Komprimeringsforhold: +Fejl: +Arkiver: +4000 +Føj til arkiv +&Arkiv: +O&pdateringstilstand: +Arkiv&format: +Komprimerings&niveau: +Komprimerings&metode: +Størrelse pÃ¥ or&dbog: +Størrelse pÃ¥ &ord: +Størrelse pÃ¥ solid blok: +Antal CPU-trÃ¥de: +Pa&rametre: +Funktioner +Opret SF&X-arkiv +Komprimer delte filer +Kryptering +Krypteringsmetode: +Kr&ypter filnavne +Hukommelsesforbrug ved komprimering: +Hukommelsesforbrug ved udpakning: +Slet filer efter komprimering +4040 +Gem symbolske henvisninger +Gem hÃ¥rde henvisninger +Gem alternative datastrømme +Gem filsikkerhed +4050 +Gem +Hurtigst +Hurtig +Normal +Maksimum +Ultra +4060 +Tilføj og erstat filer +Opdater og tilføj filer +Opdater eksisterende filer +Synkroniser filer +4070 +Gennemse +Alle filer +Ikke-solid +Solid +6000 +Kopier +Flyt +Kopier til: +Flyt til: +Kopierer... +Flytter... +Omdøber... +Vælg destinationsmappen. +Handlingen understøttes ikke af denne mappe. +Fejl under omdøbning af fil eller mappe +Bekræft kopiering af fil +Er du sikker pÃ¥, at du vil kopiere filer til arkiv +6100 +Bekræft sletning af fil +Bekræft sletning af mappe +Bekræft sletning af flere filer +Er du sikker pÃ¥, at du vil slette "{0}"? +Er du sikker pÃ¥, at du vil slette mappen "{0}" og alt dens indhold? +Er du sikker pÃ¥, at du vil slette disse {0} elementer? +Sletter... +Fejl under sletning af fil eller mappe +Systemet kan ikke flytte en fil med lang sti til Papirkurven +6300 +Opret mappe +Opret fil +Mappenavn: +Filnavn: +Ny mappe +Ny fil +Fejl under oprettelse af mappe +Fejl under oprettelse af fil +6400 +Kommentar +&Kommentar: +Vælg +Fravælg +Maske: +6600 +Egenskaber +Mappers historik +Diagnostiske meddelelser +Meddelelse +7100 +Computer +Netværk +Dokumenter +System +7200 +Tilføj +Udpak +Test +Kopier +Flyt +Slet +Info +7300 +Opdel fil +&Opdel til: +Opd&el i bind, byte: +Opdeler... +Bekræft opdeling +Er du sikker pÃ¥, at du vil opdele filen i {0} bind? +Størrelsen pÃ¥ bind skal være mindre end størrelsen pÃ¥ den oprindelige fil +Forkert størrelse pÃ¥ bind +Angivet størrelse pÃ¥ bind: {0} byte.\nEr du sikker pÃ¥, at du vil opdele arkivet i disse bind? +7400 +Kombiner filer +&Kombiner til: +Kombinerer... +Vælg kun første del af opdelt fil +Kan ikke genkende fil, som del af opdelt fil +Kan ikke finde mere end én del af opdelt fil +7500 +Beregner checksum... +Checksum information +CRC-checksum for data: +CRC-checksum for data og navne: +7600 +Benchmark +Hukommelsesforbrug: +Komprimering +Udpakning +Vurdering +Samlet vurdering +Aktuelt +Resultat +CPU forbrug +Vurdering/forbrug +Gennemløb: +7700 +Opret/rediger henvisning +Opret/rediger henvisning +Henvisning fra: +Henvisning til: +7710 +Henvisningstype +Fast henvisning +Fil symbolsk henvisning +Mappe symbolsk henvisning +Mappe forbindelsespunkt diff --git a/Utils/7-Zip/Lang/de.txt b/Utils/7-Zip/Lang/de.txt new file mode 100644 index 000000000..828dd17d6 --- /dev/null +++ b/Utils/7-Zip/Lang/de.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 2.30 : Soeren Finster +; 4.07 : JAK-Software.DE +; 9.07 : Joachim Henke +; +; +; +; +; +; +; +; +0 +7-Zip +German +Deutsch +401 +OK +Abbrechen + + + +&Ja +&Nein +&Schließen +Hilfe + +&Fortsetzen +440 +Ja für &alle +Nein für a&lle +Stopp +Neustart +&Hintergrund +&Vordergrund +&Pause +Pause +Möchten Sie wirklich abbrechen? +500 +&Datei +&Bearbeiten +&Ansicht +&Favoriten +&Extras +&Hilfe +540 +Ö&ffnen +I&ntern öffnen +E&xtern öffnen +&Ansehen +&Bearbeiten +&Umbenennen +&Kopieren nach... +&Verschieben nach... +&Löschen +Datei auf&splitten... +Dateien &zusammenfügen... +E&igenschaften +K&ommentieren +&Prüfsumme berechnen +Ver&gleichen +Ordner erstellen +Datei erstellen +Be&enden +Verknüpfung... +&Alternative Datenströme +600 +Alles &markieren +Alles abwählen +Markierung &umkehren +Auswählen... +Auswahl aufheben... +Nach Typ auswählen +Nach Typ abwählen +700 +&Große Symbole +&Kleine Symbole +&Liste +&Details +730 +Unsortiert +Alles in einer &Ebene +&Zweigeteiltes Fenster +&Symbolleisten +Wurzelverzeichnis +Übergeordneter Ordner +Ordnerverlauf... +&Aktualisieren +Auto-Aktualisierung +750 +Archivfunktionen +Standardfunktionen +Große Schaltflächen +Schaltflächenbeschriftung +800 +&Ordner hinzufügen als +Favorit +900 +&Optionen... +&Benchmark +960 +&Hilfethemen +Ü&ber 7-Zip... +1003 +Pfad +Name +Erweiterung +Ordner +Größe +Gepackte Größe +Attribute +Erstellt am +Letzter Zugriff +Geändert am +Kompakt (solid) +Kommentiert +Verschlüsselt +Vorher geteilt +Danach geteilt +Wörterbuch +CRC +Typ +Anti +Verfahren +Herkunft +Dateisystem +Besitzer +Gruppe +Block +Kommentar +Position +Pfad +Ordner +Dateien +Version +Teilarchiv +Mehrteiliges Archiv +Offset +Verknüpfungen +Blöcke +Teilarchive + +64 Bit +Big-Endian +CPU +Gesamtgröße +Header-Größe +Prüfsumme +Kenndaten +Virtuelle Adresse +ID +Kurzname +Erstellt durch +Sektorgröße +Zugriffsrechte +Link +Fehler +Gesamtgröße +Freier Speicherplatz +Cluster-Größe +Name +Lokaler Name +Provider +NT-Sicherheit +Alternativer Datenstrom +Aux +Gelöscht +Ist Baum + + +Fehlertyp +Fehler +Fehler +Warnungen +Warnung +Datenströme +Alternative Datenströme +Größe der alternativen Datenströme +Virtuelle Größe +Entpackte Größe +Gesamte physikalische Größe +Teilstück Index +Untertyp +Kurzkommentar +Code-Seite + + + +Endungsgröße +integrierte Stub-Größe +Verknüpfung +Harte Verknüpfung +iNode + +Schreibgeschützt +2100 +Optionen +Sprache +Sprache: +Editor +&Editor: +Programm zum &Vergleichen: +2200 +System +7-Zip verknüpfen mit: +alle Benutzer +2301 +7-Zip in Kontextmenü integrieren +Kontextmenü kaskadieren +Einträge im Kontextmenü: +Symbole im Kontextmenü +2320 + + +Öffnen +Dateien entpacken... +Zu einem Archiv hinzufügen... +Archiv überprüfen +Hier entpacken +Entpacken nach {0} +Hinzufügen zu {0} +Archivieren und versenden... +Archivieren in {0} und versenden +2400 +Ordner +&Arbeitsverzeichnis +&TEMP-Ordner des Systems +Aktueller &Ordner +&Benutzerdefiniert: +Nur bei &Wechselmedien benutzen +Wählen Sie einen Ordner für temporäre Archivdateien: +2500 +Einstellungen +&Verzeichniseintrag ".." anzeigen +Symbole aus &Dateien laden und anzeigen +System-Kontext&menü im Dateimenü anzeigen +Dateiauswahl markiert ganze &Zeile +&Gitternetzlinien anzeigen +Einfacher &Klick zum Öffnen +&Alternativer Dateiauswahl-Modus +Große &Speicherseiten verwenden +2900 +Info über 7-Zip +7-Zip ist freie Software. Sie können jedoch das Projekt durch eine Registrierung unterstützen. +3000 +Das System kann die benötigte Speichermenge nicht bereit stellen. +Es sind keine Fehler aufgetreten. +{0} Objekt(e) markiert +Kann den Ordner "{0}" nicht erstellen. +Aktualisierungen werden für dieses Archiv nicht unterstützt. +Die Datei "{0}" kann nicht als Archiv geöffnet werden. +Das verschlüsselte Archiv "{0}" kann nicht geöffnet werden. Falsches Passwort? +Typ des Archives wird nicht unterstützt +Die Datei {0} existiert bereits. +Die Datei "{0}" wurde geändert.\nSoll sie im Archiv aktualisiert werden? +Die Datei konnte nicht aktualisiert werden.\n"{0}" +Kann Editor nicht starten +Die Datei scheint ein Virus zu sein (Dateiname enthält lange Reihen von Leerzeichen). +Die Operation kann nicht aus einem Ordner mit langem Pfad aufgerufen werden. +Bitte genau eine Datei auswählen. +Bitte mindestens eine Datei auswählen. +Zu viele Objekte +Die Datei kann nicht als {0}-Archiv geöffnet werden. +Die Datei wurde als {0}-Archiv geöffnet. +Die Datei wurde mit einem Offset geöffnet. +3300 +Entpacken +Komprimiere +Überprüfen +Öffne... +Durchsuche... +Entferne +3320 +Hinzufügen +Aktualisieren +Analysieren +Replizieren +Neu Packen +Überspringen +Löschen +Header erstellen +3400 +Entpacken +&Entpacken nach: +Wählen Sie einen Ordner für die entpackten Dateien: +3410 +Verzeichnisstruktur wiederherstellen +Komplette Pfadangaben +Keine Pfadangaben +Absolute Pfadangaben +Relative Pfadangaben +3420 +Dateien überschreiben +Nur mit Bestätigung +Ohne Bestätigung +Vorhandene Dateien überspringen +Automatisch umbenennen +Vorhandene Dateien umbenennen +3430 +Verdoppelung des Wurzelordners vermeiden +Dateirechte wiederherstellen +3500 +Überschreiben bestätigen +Der Zielordner beinhaltet bereits eine Datei diesen Namens. +Wollen Sie diese Datei +durch diese ersetzen? +{0} Bytes +A&utomatisch umbenennen +3700 +Das Kompressionsverfahren in "{0}" wird nicht unterstützt. +Datenfehler in "{0}". Die Datei ist beschädigt. +CRC-Prüfsummenfehler. Die Datei "{0}" ist beschädigt. +Datenfehler in der verschlüsselten Datei "{0}". Falsches Passwort? +CRC-Prüfsummenfehler bei verschlüsselter Datei "{0}". Falsches Passwort? +3710 +Falsches Passwort? +3721 +Nicht unterstützte Kompressionsmethode +Datenfehler +CRC-Fehler +Daten stehen nicht zur Verfügung +Unerwartetes Datenende +Es gibt noch Daten hinter den Hauptdaten +Ist kein Archiv +Headers-Fehler +Falsches Passwort +3763 +Anfang des Archivs fehlt +Anfang des Archivs nicht bestätigt + + + +Nicht unterstützte Funktion +3800 +Kennworteingabe +Passwort eingeben: +Passwort bestätigen: +Passwort an&zeigen +Die Passwörter stimmen nicht überein. +Bitte nur Buchstaben des englischen Alphabets, Ziffern und Sonderzeichen (!, #, $, ...) im Passwort verwenden! +Das Passwort ist zu lang. +Passwort +3900 +Verstrichene Zeit: +Verbleibende Zeit: +Gesamtdatenmenge: +Geschwindigkeit: +Verarbeitet: +Kompressionsrate: +Fehler: +Archive: +4000 +Zu Archiv hinzufügen +&Archiv: +Art der Akt&ualisierung: +Archiv&format: +&Kompressionsstärke: +Kompressions&verfahren: +Wörter&buchgröße: +&Wortgröße: +Größe &solider Blöcke: +Anzahl &CPU-Threads: +&Parameter: +Optionen +Selbstentpackendes Archiv (SF&X) erstellen +Zum Schreiben &geöffnete Dateien einbeziehen +Verschlüsselung +Verfahren: +Datei&namen verschlüsseln +Speicherbedarf beim Komprimieren: +Speicherbedarf beim Entpacken: +Dateien nach Komprimierung löschen +4040 +Symbolische Verknüpfungen speichern +Harte Verknüpfungen speichern +Alternative Datenströme speichern +Dateirechte speichern +4050 +Speichern +Schnellste +Schnell +Normal +Maximum +Ultra +4060 +Hinzufügen und Ersetzen +Aktualisieren und Hinzufügen +Vorhandene Dateien aktualisieren +Synchronisieren +4070 +Durchsuchen +Alle Dateien +Nicht solide +Solide +6000 +Kopieren +Verschieben +Kopieren nach: +Verschieben nach: +Kopiere... +Verschiebe... +Umbenennen... +Zielordner auswählen +Die Operation wird für diesen Ordner nicht unterstützt. +Fehler beim Umbenennen von Datei oder Ordner +Kopieren bestätigen +Sollen die Dateien wirklich in dieses Archiv kopiert werden: +6100 +Löschen von Datei bestätigen +Löschen von Ordner bestätigen +Löschen von mehreren Dateien bestätigen +Soll "{0}" wirklich gelöscht werden? +Soll der Ordner "{0}" und sein gesamter Inhalt wirklich gelöscht werden? +Sollen diese {0} Objekte wirklich gelöscht werden? +Lösche... +Fehler beim Löschen von Datei oder Ordner +Das System kann Dateien mit langem Pfad nicht in den Papierkorb verschieben. +6300 +Ordner erstellen +Datei erstellen +Ordnername: +Dateiname: +Neuer Ordner +Neue Datei +Fehler beim Erstellen des Ordners +Fehler beim Erstellen der Datei +6400 +Kommentar +&Kommentar: +Auswählen +Auswahl aufheben +Filter: +6600 +Eigenschaften +Ordnerverlauf +Diagnosemeldungen +Meldung +7100 +Arbeitsplatz +Netzwerk +Dokumente +System +7200 +Hinzufügen +Entpacken +Überprüfen +Kopieren +Verschieben +Löschen +Eigenschaften +7300 +Datei aufsplitten +Teildateien &nach: +In &Teildateien aufsplitten (Bytes): +Aufsplitten... +Aufsplitten bestätigen +Sind Sie sicher, die Datei in {0} Teildateien aufsplitten zu wollen? +Die Größe der Teildateien muss kleiner sein als die der ursprünglichen Datei. +Ungültiger Wert für Dateigrößen +Angegebene Größe für Teildateien: {0} Bytes.\nSind Sie sicher, dass das Archiv dementsprechend aufgesplittet werden soll? +7400 +Dateien zusammenfügen +Zieldatei &nach: +Zusammenfügen... +Bitte nur den ersten Teil der Datei auswählen. +Datei nicht als Teil einer aufgesplitteten Datei erkannt +Kann nicht mehr als eine Teildatei finden. +7500 +Berechne Prüfsumme... +Prüfsummen-Information +CRC-Prüfsumme über die Daten: +Prüfsumme über Daten und Namen: +7600 +Benchmark +Speichernutzung: +Komprimierung +Dekomprimierung +Bewertung +Gesamtwertung +Aktuell +Ergebnis +CPU-Nutzung +Bewert./Nutzung +Durchläufe: +7700 +Verknüpfung +Verknüpfung +Verknüpfung von: +Verknüpfung zu: +7710 +Verknüpfungsart +Harte Verknüpfung +Datei Symbolische Verknüpfung +Ordner Symbolische Verknüpfung +Ordner Verbindung diff --git a/Utils/7-Zip/Lang/el.txt b/Utils/7-Zip/Lang/el.txt new file mode 100644 index 000000000..faa7b3650 --- /dev/null +++ b/Utils/7-Zip/Lang/el.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; : Vasileios Karakoidas, Jacaranda Bill, Vasilis Kosmidis +; 9.07 : SkyHi [HDManiacs Team] +; 15.00 : 2015-05-07: Pete D +; +; +; +; +; +; +; +; +0 +7-Zip +Greek +Ελληνικά +401 +OK +ΆκυÏο + + + +&Îαι +ÎŒ&χι +&Κλείσιμο +Βοήθεια + +&Συνέχεια +440 +Îαι σε &όλα +Όχι σε ÏŒ&λα +&ΠαÏση +Επανεκκίνηση +Στο &παÏασκήνιο +Στο &Ï€Ïοσκήνιο +&ΠαÏση +ΠαÏθηκε +Είστε σίγουÏοι ότι θέλετε να ακυÏώσετε; +500 +&ΑÏχείο +&ΕπεξεÏγασία +Π&Ïοβολή +Αγα&πημένα +ΕÏγα&λεία +&Βοήθεια +540 +Άν&οιγμα +Άνοιγμα στο &ίδιο παÏάθυÏο +Άνοιγμα σε &νέο παÏάθυÏο +Π&Ïοβολή +Επε&ξεÏγασία +Με&τονομασία +Αντι&γÏαφή... +Μετα&κίνηση... +ΔιαγÏα&φή +&Τεμαχισμός αÏχείου... +&Συνένωση αÏχείων... +&Ιδιότητες +Σχόλιο +Υπολογισμός αθÏοίσματος ελέγχου +Diff +ΔημιουÏγία φακέλου +ΔημιουÏγία αÏχείου +Έ&ξοδος +ΣÏνδεσμος +&Alternate Streams +600 +Ε&πιλογή όλων +Αποεπιλογή όλων +ΑντιστÏοφή επιλογής +Επιλογή... +Αποεπιλογή... +Επιλογή σÏμφωνα με τον Ï„Ïπο αÏχείου +Αποεπιλογή σÏμφωνα με τον Ï„Ïπο αÏχείου +700 +Μεγάλα εικονί&δια +&ΜικÏά εικονίδια +&Λίστα +Λε&πτομέÏειες +730 +ΧωÏίς ταξινόμηση +Επίπεδη Ï€Ïοβολή +2 πάνελ +&ΓÏαμμές εÏγαλείων +Άνοιγμα κεντÏÎ¹ÎºÎ¿Ï Ï†Î±ÎºÎ­Î»Î¿Ï… +Μετάβαση ένα επίπεδο πάνω +&ΙστοÏικό φακέλων... +Α&νανέωση +Αυτόματη ανανέωση +750 +Συμπίεσης +Βασική +Μεγάλα εικονίδια +Εμφάνιση κειμένου +800 +&ΠÏοσθήκη καταλόγου στα Αγαπημένα ως +Σελιδοδείκτης +900 +&Ρυθμίσεις... +&Ελεγχος επιδόσεων +960 +&ΠεÏιεχόμενα... +Π&εÏί του 7-Zip... +1003 +Θέση +Όνομα +ΤÏπος +Φάκελος +Μέγεθος +Συμπιεσμένο μέγεθος +Ιδιότητες +ΔημιουÏγήθηκε +ΠÏοσπελάστηκε +ΤÏοποποιήθηκε +Συμπαγές +Σχόλιο +Κωδικοποιημένο +Τεμαχισμός Ï€Ïιν +Τεμαχισμός μετά +Λεξικό + +ΤÏπος +Αντί +Μέθοδος +ΛειτουÏγικό +ΣÏστημα αÏχείων +ΧÏήστης +Ομάδα +Μπλοκ +Σχόλιο +Θέση +ΠÏοκαθοÏισμένη διαδÏομή +Φάκελοι +ΑÏχεία +Έκδοση +Τόμος +Πολυτόμος +Offset +ΣÏνδεσμοι +Μπλοκ +Τόμοι + +64-bit +Big-endian +CPU +Φυσικό μέγεθος +Μέγεθος headers +ΆθÏοισμα ελέγχου +ΧαÏακτηÏιστικά +Εικονική διεÏθυνση +ID +ΣÏντομο όνομα +ΕφαÏμογή δημιουÏγίας +Μέγεθος τομέα +Κατάσταση λειτουÏγίας +ΣÏνδεσμος +Σφάλμα +Συνολικό μέγεθος +ΕλεÏθεÏος χώÏος +Μέγεθος συμπλέγματος +Ετικέτα +Τοπικό όνομα +ΠαÏοχέας +NT Security +Alternate Stream +Aux +Deleted +Is Tree + + +ΤÏπος λάθους +Λάθη +Λάθη +ΠÏοειδοποιήσεις +ΠÏοειδοποίηση +Streams +Alternate Streams +Alternate Streams Size +Εικονικό μέγεθος +Αποσυμπιεσμένο μέγεθος +Συνολικό φυσικό μέγεθος +ΕυÏετήÏιο τόμου +SubType +ΣÏντομο σχόλιο +Κωδικοσελίδα + + + +Tail Size +Embedded Stub Size +ΣÏνδεσμος +ΣταθεÏός σÏνδεσμος +iNode + +Ανάγνωση-Μόνο +2100 +Ρυθμίσεις +Γλώσσα +Γλώσσα: +ΠÏόγÏαμμα επεξεÏγασίας +&ΠÏόγÏαμμα επεξεÏγασίας: +&Diff: +2200 +ΣÏστημα +Συσχέτιση του 7-Zip με τα αÏχεία: +Ολοι οι χÏήστες +2301 +Ενσωμάτωση του 7-Zip στο λειτουÏγικό +Με ομαδοποίηση των επιλογών +Στοιχεία του αναδυόμενου μενοÏ: +Εικονίδια στο αναδυόμενο Î¼ÎµÎ½Î¿Ï +2320 +<Φάκελος> +<ΑÏχείο Συμπίεσης> +Άνοιγμα +Αποσυμπίεση αÏχείων... +ΠÏοσθήκη σε αÏχείο συμπίεσης... +Έλεγχος αÏχείου συμπίεσης +Αποσυμπίεση εδώ +Αποσυμπίεση στο φάκελο {0} +ΠÏοσθήκη στο {0} +Συμπίεση και αποστολή με e-mail... +Συμπίεση στο {0} και αποστολή με e-mail +2400 +Φάκελοι +&Φάκελος εÏγασίας +&ΠÏοσωÏινός φάκελος συστήματος +&ΤÏέχων φάκελος +&ΚαθοÏισμένος: +ΧÏήση μόνο για αφαιÏοÏμενες μονάδες δίσκου +ΚαθοÏίστε μια τοποθεσία για τα Ï€ÏοσωÏινά αÏχεία συμπίεσης. +2500 +Επιλογές +Εμφάνιση αντικειμένου ".." +Εμφάνιση των κανονικών εικονιδίων των αÏχείων +Εμφάνιση Î¼ÎµÎ½Î¿Ï ÏƒÏ…ÏƒÏ„Î®Î¼Î±Ï„Î¿Ï‚ +&Επιλογή ολόκληÏης διαδÏομής +Εμφάνιση γÏαμμών πλέγματος +Άνοιγμα αντικειμένου με μονό κλικ +Εναλλακτική κατάσταση επιλογής +ΧÏήση μεγάλων &σελίδων μνήμης +2900 +ΠληÏοφοÏίες για το 7-Zip +Το 7-Zip είναι ελεÏθεÏο λογισμικό. Ωστόσο μποÏείτε να υποστηÏίξετε την πεÏαιτέÏω ανάπτυξη του με την εγγÏαφή σας. +3000 +Το σÏστημα δεν μποÏεί να διαθέσει την απαιτοÏμενη ποσότητα μνήμης. +Δεν υπάÏχουν σφάλματα +{0} επιλεγμένα στοιχεία +ΑδÏνατη η δημιουÏγία του φακέλου '{0}' +Οι λειτουÏγίες ενημέÏωσης δεν είναι διαθέσιμες για αυτόν τον Ï„Ïπο συμπιεσμένου αÏχείου. +Δεν μποÏεί να ανοιχθεί το αÏχείο '{0}' σαν αÏχείο συμπίεσης +Δεν μποÏεί να ανοιχθεί το κÏυπτογÏαφημένο αÏχείο '{0}'. Λάθος κωδικός; +Μη υποστηÏιζόμενο αÏχείο συμπίεσης. +Το αÏχείο {0} ήδη υπάÏχει. +Το αÏχείο '{0}' Ï„Ïοποποιήθηκε.\nΘέλετε να ενημεÏώσετε το αÏχείο συμπίεσης; +Αδυνατή η ενημέÏωση του αÏχείου\n'{0}' +Δεν είναι δυνατή η εκκίνηση του Ï€ÏογÏάμματος επεξεÏγασίας. +Το αÏχείο μοιάζει με ιό (το όνομα του πεÏιέχει μεγάλα κενά). +Η λειτουÏγία δεν μποÏεί να κληθεί από ένα φάκελο που έχει μεγάλη διαδÏομή. +ΠÏέπει να επιλέξετε ένα αÏχείο. +ΠÏέπει να επιλέξετε ένα ή πεÏισσότεÏα αÏχεία. +ΠάÏα πολλά στοιχεία +Αδυναμία ανοίγματος του αÏχείου ως {0} συμπιεσμένο +Το αÏχείο είναι ανοιχτό ως {0} συμπιεσμένο +Το συμπιεσμένο αÏχείο είναι ανοιχτό με offset +3300 +Αποσυμπίεση +Συμπίεση +Έλεγχος +Άνοιγμα... +ΣάÏωση... +ΔιαγÏαφή... +3320 +ΠÏοσθήκη.. +ΕνημέÏωση.. +Ανάλυση.. +ΑντιγÏαφή.. +Επανασυμπίεση.. +ΠαÏάλειψη.. +ΔιαγÏαφή.. +ΔημιουÏγία κεφαλίδας.. +3400 +Αποσυμπίεση +&Αποσυμπίεση στο φάκελο: +ΚαθοÏίστε τον φάκελο αποσυμπίεσης. +3410 +Επιλογές διαδÏομών φακέλων +ΠλήÏεις διαδÏομές φακέλων +ΧωÏίς διαδÏομές φακέλων +Απόλυτες διαδÏομές φακέλων +Σχετικές διαδÏομές φακέλων +3420 +Επιλογές αντικατάστασης αÏχείων +Αντικατάσταση με εÏώτηση +Αντικατάσταση χωÏίς εÏώτηση +ΠαÏάβλεψη των υπαÏχόντων αÏχείων +Αυτόματη μετονομασία +Αυτόματη μετονομασία των υπαÏχόντων αÏχείων +3430 +Απαλοιφή ÏÎ¹Î¶Î¹ÎºÎ¿Ï ÎºÎ±Ï„Î±Î»ÏŒÎ³Î¿Ï… +ΕπαναφοÏά ασφάλειας αÏχείου +3500 +Επιβεβαίωση αντικατάστασης του αÏχείου +Ο φάκελος Ï€ÏοοÏÎ¹ÏƒÎ¼Î¿Ï Ï€ÎµÏιέχει ήδη ένα αÏχείο με το ίδιο όνομα. +Θέλετε να αντικαταστήσετε το υπάÏχον αÏχείο +με αυτό; +{0} bytes +Αυτόματη &μετονομασία +3700 +Μη υποστηÏιζόμενη μέθοδος συμπίεσης για το '{0}'. +Λάθος δεδομένων στο {0}. Το αÏχείο είναι φθαÏμένο. +Ο έλεγχος CRC απέτυχε στο '{0}'. Το αÏχείο είναι φθαÏμένο. +Λάθος δεδομένων στο κÏυπτογÏαφημένο αÏχείο '{0}'. Λάθος κωδικός; +Ο έλεγχος CRC απέτυχε στο κÏυπτογÏαφημένο αÏχείο '{0}'. Λάθος κωδικός; +3710 +Λάθος κωδικός; +3721 +Μη υποστηÏιζόμενη μέθοδος συμπίεσης +Λάθος δεδομένων +Ο έλεγχος CRC απέτυχε +Μη διαθέσιμα δεδομένα +ΑπÏόβλεπτο τελείωμα δεδομένων +ΥπάÏχουν μεÏικα δεδομένα στο τέλος των χÏήσιμων δεδομένων +Δεν είναι συμπιεσμένο αÏχείο +Λάθος επικεφαλίδων +Λάθος κωδικός +3763 +Μη διαθέσιμη αÏχή του συμπιεσμένου αÏχείου +Ανεπιβεβαίωτη αÏχή του συμπιεσμένου αÏχείου + + + +Μη υποστηÏιζόμενη λειτουÏγία +3800 +Εισαγωγή ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης +Κωδικός Ï€Ïόσβασης: +Επανεισάγετε τον κωδικό Ï€Ïόσβασης: +&Εμφάνιση ÎºÏ‰Î´Î¹ÎºÎ¿Ï +Οι κωδικοί δεν ταιÏιάζουν. +ΧÏησιμοποιήστε μόνο Αγγλικά γÏάμματα, αÏιθμοÏÏ‚ και ειδικοÏÏ‚ χαÏακτήÏες (!, #, ...) για κωδικό. +Ο κωδικός είναι Ï€Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿Ï‚. +Κωδικός Ï€Ïόσβασης +3900 +ΔιανÏθηκε: +Απομένει: +Μέγεθος: +ΤαχÏτητα: +ΕπεξεÏγάσθηκαν: +Αναλογία συμπίεσης +Σφάλματα: +ΑÏχεία συμπίεσης: +4000 +ΠÏοσθήκη σε συμπιεσμένο αÏχείο +&Όνομα αÏχείου συμπίεσης: +&ΤÏόπος ενημέÏωσης: +Συμπίεση στη μοÏ&φή: +Επίπεδο συμπίεσης: +&Μέθοδος συμπίεσης: +Μέγεθος &λεξικοÏ: +Μέγεθος &λέξης: +Μέγεθος συμπαγοÏÏ‚ μπλοκ: +ΑÏιθμός νημάτων CPU +&ΠαÏάμετÏοι: +Επιλογές συμπίεσης +Με αυτόματη αποσυμπίεση (SF&X) +Συμπίεση κοινών αÏχείων +ΚÏυπτογÏάφηση +Μέθοδος κÏυπτογÏάφησης +ΚÏυπτογÏάφηση των &ονομάτων +ΧÏήση μνήμης για συμπίεση: +ΧÏήση μνήμης για αποσυμπίεση: +ΔιαγÏαφή αÏχείων μετά τη συμπίεση +4040 +Αποθήκευση συμβολικών συνδέσμων +Αποθήκευση σταθεÏών συνδέσμων +Αποθήκευση ενναλλακτικών Ïοών δεδομένων +Αποθήκευση ασφάλειας αÏχείου +4050 +Αποθήκευση +Î Î¿Î»Ï Î³ÏήγοÏη +ΓÏήγοÏη +Κανονική +Μέγιστη +ΥπεÏσυμπίεση +4060 +ΠÏοσθήκη και αντικατάσταση αÏχείων +ΠÏοσθήκη και ενημέÏωση αÏχείων +ΕνημέÏωση των υπαÏχόντων αÏχείων +ΣυγχÏονισμός αÏχείων +4070 +Αναζήτηση +Όλα τα αÏχεία +Μη-συμπαγές +Συμπαγές +6000 +ΑντιγÏαφή +Μετακίνηση +ΑντιγÏαφή στο: +Μετακίνηση στο: +ΑντιγÏαφή... +Μετακίνηση... +Μετονομασία... +Επιλέξτε φάκελο Ï€ÏοοÏισμοÏ. +Η λειτουÏγία δεν υποστηÏίζεται. +ΠαÏουσιάστηκε σφάλμα κατά τη μετονομασία. +Επιβεβαίωση αντιγÏαφής αÏχείων +Είστε βέβαιος ότι θέλετε να αντιγÏάψετε τα αÏχεία στο αÏχείο συμπίεσης; +6100 +Επιβεβαίωση διαγÏαφής του αÏχείου +Επιβεβαίωση διαγÏαφής του φακέλου +Επιβεβαίωση διαγÏαφής πολλών αÏχείων +Είστε βέβαιοι ότι θέλετε να διαγÏάψετε το '{0}' ; +Είστε βέβαιοι ότι θέλετε να διαγÏάψετε το φάκελο '{0}' και όλα τα πεÏιεχόμενα του; +Είστε βέβαιοι ότι θέλετε να διαγÏάψετε αυτά τα {0} στοιχεία; +ΔιαγÏαφή... +ΠαÏουσιάστηκε σφάλμα κατά τη διαγÏαφή. +Το σÏστημα δεν μποÏεί να μετακινήσει ένα αÏχείο με μεγάλη διαδÏομή στον Κάδο ΑνακÏκλωσης. +6300 +ΔημιουÏγία φακέλου +ΔημιουÏγία αÏχείου +Όνομα φακέλου: +Όνομα αÏχείου: +Îέος φάκελος +Îέο αÏχείο +Σφάλμα κατά την δημιουÏγία φακέλου. +Σφάλμα κατά την δημιουÏγία αÏχείου. +6400 +Σχόλιο +&Σχόλιο: +Επιλογή +Αποεπιλογή +Με όνομα: +6600 +Ιδιότητες +ΙστοÏικό φακέλων +Διαγνωστικά μηνÏματα +Μήνυμα +7100 +Υπολογιστής +ΔικτÏο +ΈγγÏαφα +ΣÏστημα +7200 +ΠÏοσθήκη +Αποσυμπίεση +Έλεγχος +ΑντιγÏαφή +Μετακίνηση +ΔιαγÏαφή +ΠληÏοφοÏίες +7300 +Τεμαχισμός αÏχείου +&Τεμαχισμός σε: +Τεμαχισμός σε τόμους: +Τεμαχισμός... +Επιβεβαίωση Ï„ÎµÎ¼Î±Ï‡Î¹ÏƒÎ¼Î¿Ï +Είστε βέβαιος ότι θέλετε να τεμαχίσετε το αÏχείο σε {0} τόμους; +Το μέγεθος του τόμου Ï€Ïέπει να είναι μικÏότεÏο από αυτό του αÏÏ‡Î¹ÎºÎ¿Ï Î±Ïχείου. +Λάθος μέγεθος τόμου +ΚαθοÏισμένο μέγεθος τόμου: {0} bytes.\nΕίστε σίγουÏος ότι θέλετε να χωÏίσετε το αÏχείο σε τέτοιους τόμους; +7400 +Συνένωση αÏχείων +&Συνένωση σε: +Συνένωση... +Επιλέξτε μόνο το Ï€Ïώτο αÏχείο +Το αÏχείο δεν μποÏεί να ανιχνευθεί ως μέÏος τεμαχισμένου αÏχείου. +Δεν μποÏοÏν να βÏεθοÏν πάνω από ένα μέÏη τεμαχισμένου αÏχείου. +7500 +Υπολογισμός αθÏοίσματος ελέγχου... +ΠληÏοφοÏίες αθÏοίσματος ελέγχου +ΆθÏοισμα ελέγχου CRC για δεδομένα: +ΆθÏοισμα ελέγχου CRC για δεδομένα και ονόματα: +7600 +Αξιολόγηση επιδόσεων +ΧÏήση μνήμης: +Συμπίεση +Αποσυμπίεση +Εκτίμηση +Συνολ. εκτίμηση +ΤÏέχων πέÏασμα +Αποτέλεσμα +ΧÏήση CPU +Ταξ/μιση/ΧÏήση +ΠεÏάσματα: +7700 +ΣÏνδεσμος +ΣÏνδεσμος +ΣÏνδεσμος από: +ΣÏνδεσμος έως: +7710 +ΤÏπος συνδέσμου +ΣταθεÏός σÏνδεσμος +Συμβολικός σÏνδεσμος αÏχείου +Συμβολικός σÏνδεσμος καταλόγου +Συνένωση καταλόγου diff --git a/Utils/7-Zip/Lang/en.ttt b/Utils/7-Zip/Lang/en.ttt new file mode 100644 index 000000000..d94df3816 --- /dev/null +++ b/Utils/7-Zip/Lang/en.ttt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 15.00 : 2015-03-29 : Igor Pavlov +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +English +English +401 +OK +Cancel + + + +&Yes +&No +&Close +Help + +&Continue +440 +Yes to &All +No to A&ll +Stop +Restart +&Background +&Foreground +&Pause +Paused +Are you sure you want to cancel? +500 +&File +&Edit +&View +F&avorites +&Tools +&Help +540 +&Open +Open &Inside +Open O&utside +&View +&Edit +Rena&me +&Copy To... +&Move To... +&Delete +&Split file... +Com&bine files... +P&roperties +Comme&nt... +Calculate checksum +Diff +Create Folder +Create File +E&xit +Link +&Alternate Streams +600 +Select &All +Deselect All +&Invert Selection +Select... +Deselect... +Select by Type +Deselect by Type +700 +Lar&ge Icons +S&mall Icons +&List +&Details +730 +Unsorted +Flat View +&2 Panels +&Toolbars +Open Root Folder +Up One Level +Folders History... +&Refresh +Auto Refresh +750 +Archive Toolbar +Standard Toolbar +Large Buttons +Show Buttons Text +800 +&Add folder to Favorites as +Bookmark +900 +&Options... +&Benchmark +960 +&Contents... +&About 7-Zip... +1003 +Path +Name +Extension +Folder +Size +Packed Size +Attributes +Created +Accessed +Modified +Solid +Commented +Encrypted +Split Before +Split After +Dictionary + +Type +Anti +Method +Host OS +File System +User +Group +Block +Comment +Position +Path Prefix +Folders +Files +Version +Volume +Multivolume +Offset +Links +Blocks +Volumes + +64-bit +Big-endian +CPU +Physical Size +Headers Size +Checksum +Characteristics +Virtual Address +ID +Short Name +Creator Application +Sector Size +Mode +Symbolic Link +Error +Total Size +Free Space +Cluster Size +Label +Local Name +Provider +NT Security +Alternate Stream +Aux +Deleted +Is Tree + + +Error Type +Errors +Errors +Warnings +Warning +Streams +Alternate Streams +Alternate Streams Size +Virtual Size +Unpack Size +Total Physical Size +Volume Index +SubType +Short Comment +Code Page + + + +Tail Size +Embedded Stub Size +Link +Hard Link +iNode + +Read-only +2100 +Options +Language +Language: +Editor +&Editor: +&Diff: +2200 +System +Associate 7-Zip with: +All users +2301 +Integrate 7-Zip to shell context menu +Cascaded context menu +Context menu items: +Icons in context menu +2320 + + +Open archive +Extract files... +Add to archive... +Test archive +Extract Here +Extract to {0} +Add to {0} +Compress and email... +Compress to {0} and email +2400 +Folders +&Working folder +&System temp folder +&Current +&Specified: +Use for removable drives only +Specify a location for temporary archive files. +2500 +Settings +Show ".." item +Show real file icons +Show system menu +&Full row select +Show &grid lines +Single-click to open an item +&Alternative selection mode +Use &large memory pages +2900 +About 7-Zip +7-Zip is free software +3000 +The system cannot allocate the required amount of memory +There are no errors +{0} object(s) selected +Cannot create folder '{0}' +Update operations are not supported for this archive. +Can not open file '{0}' as archive +Can not open encrypted archive '{0}'. Wrong password? +Unsupported archive type +File {0} is already exist +File '{0}' was modified.\nDo you want to update it in the archive? +Can not update file\n'{0}' +Cannot start editor. +The file looks like a virus (the file name contains long spaces in name). +The operation cannot be called from a folder that has a long path. +You must select one file +You must select one or more files +Too many items +Can not open the file as {0} archive +The file is open as {0} archive +The archive is open with offset +3300 +Extracting +Compressing +Testing +Opening... +Scanning... +Removing +3320 +Adding +Updating +Analyzing +Replicating +Repacking +Skipping +Deleting +Header creating +3400 +Extract +E&xtract to: +Specify a location for extracted files. +3410 +Path mode: +Full pathnames +No pathnames +Absolute pathnames +Relative pathnames +3420 +Overwrite mode: +Ask before overwrite +Overwrite without prompt +Skip existing files +Auto rename +Auto rename existing files +3430 +Eliminate duplication of root folder +Restore file security +3500 +Confirm File Replace +Destination folder already contains processed file. +Would you like to replace the existing file +with this one? +{0} bytes +A&uto Rename +3700 +Unsupported compression method for '{0}'. +Data error in '{0}'. File is broken. +CRC failed in '{0}'. File is broken. +Data error in encrypted file '{0}'. Wrong password? +CRC failed in encrypted file '{0}'. Wrong password? +3710 +Wrong password? +3721 +Unsupported compression method +Data error +CRC failed +Unavailable data +Unexpected end of data +There are some data after the end of the payload data +Is not archive +Headers Error +Wrong password +3763 +Unavailable start of archive +Unconfirmed start of archive + + + +Unsupported feature +3800 +Enter password +Enter password: +Reenter password: +&Show password +Passwords do not match +Use only English letters, numbers and special characters (!, #, $, ...) for password +Password is too long +Password +3900 +Elapsed time: +Remaining time: +Total size: +Speed: +Processed: +Compression ratio: +Errors: +Archives: +4000 +Add to archive +&Archive: +&Update mode: +Archive &format: +Compression &level: +Compression &method: +&Dictionary size: +&Word size: +Solid block size: +Number of CPU threads: +&Parameters: +Options +Create SF&X archive +Compress shared files +Encryption +Encryption method: +Encrypt file &names +Memory usage for Compressing: +Memory usage for Decompressing: +Delete files after compression +4040 +Store symbolic links +Store hard links +Store alternate data streams +Store file security +4050 +Store +Fastest +Fast +Normal +Maximum +Ultra +4060 +Add and replace files +Update and add files +Freshen existing files +Synchronize files +4070 +Browse +All Files +Non-solid +Solid +6000 +Copy +Move +Copy to: +Move to: +Copying... +Moving... +Renaming... +Select destination folder. +The operation is not supported for this folder. +Error Renaming File or Folder +Confirm File Copy +Are you sure you want to copy files to archive +6100 +Confirm File Delete +Confirm Folder Delete +Confirm Multiple File Delete +Are you sure you want to delete '{0}'? +Are you sure you want to delete the folder '{0}' and all its contents? +Are you sure you want to delete these {0} items? +Deleting... +Error Deleting File or Folder +The system cannot move a file with long path to the Recycle Bin +6300 +Create Folder +Create File +Folder name: +File Name: +New Folder +New File +Error Creating Folder +Error Creating File +6400 +Comment +&Comment: +Select +Deselect +Mask: +6600 +Properties +Folders History +Diagnostic messages +Message +7100 +Computer +Network +Documents +System +7200 +Add +Extract +Test +Copy +Move +Delete +Info +7300 +Split File +&Split to: +Split to &volumes, bytes: +Splitting... +Confirm Splitting +Are you sure you want to split file into {0} volumes? +Volume size must be smaller than size of original file +Incorrect volume size +Specified volume size: {0} bytes.\nAre you sure you want to split archive into such volumes? +7400 +Combine Files +&Combine to: +Combining... +Select only first part of split file +Can not detect file as part of split file +Can not find more than one part of split file +7500 +Checksum calculating... +Checksum information +CRC checksum for data: +CRC checksum for data and names: +7600 +Benchmark +Memory usage: +Compressing +Decompressing +Rating +Total Rating +Current +Resulting +CPU Usage +Rating / Usage +Passes: +7700 +Link +Link +Link from: +Link to: +7710 +Link Type +Hard Link +File Symbolic Link +Directory Symbolic Link +Directory Junction diff --git a/Utils/7-Zip/Lang/eo.txt b/Utils/7-Zip/Lang/eo.txt new file mode 100644 index 000000000..45261353a --- /dev/null +++ b/Utils/7-Zip/Lang/eo.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.37 : Dmitri Gabinski +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Esperanto +Esperanto +401 +B&one +Nuligu + + + +&Jes +&Ne +&Fermu +Helpo + +&DaÅ­rigu +440 +Jes por ĉ&iuj +Ne por ĉi&uj +&Haltu +Restartigu +&Fono +&Malfono +&PaÅ­zo +PaÅ­zita +Ĉu vi vere volas nuligi? +500 +&Dosiero +&Redakto +&Vido +&Favoritaj +&Agordoj +&Helpo +540 +&Malfermu +Malfermu &ene +Malfermu ek&stere +&Vidigu +&Redaktu +Åœ&anÄu nomon +&Kopiu en... +M&ovu en... +&Forigu +&Erigu dosierojn... +Komb&inu dosierojn... +A&tributoj +Ko&mentu +Kalkulu kontrolsumon + +Kreu &dosierujon +Kre&u dos&ieron +E&liru +600 +M&arku ĉiun +Ma&lmarku ĉiun +&Inversigu markon +Marku... +Malmarku... +Marku laÅ­ tipo +Malmarku laÅ­ tipo +700 +&Grandaj bildetoj +&Malgrandaj bildetoj +&Listo +&Detale +730 +&Neordigite +Ununivela vido +&2 paneloj +&Ilobretoj +Malfermu radikan dosierujon +Supren je unu nivelo +Dosierujhistorio... +Äœ&isdatigu +750 +ArÄ¥ivo-ilobreto +Norma ilobreto +Grandaj bildetoj +Montru butontekston +800 +&Aldonu dosierujon al favorataj kiel +Legosigno +900 +&Agordoj... +&Etalono +960 +&Enhavo... +&Pri 7-Zip... +1003 +Dosierindiko +Nomo +Dosiernoma sufikso +Dosierujo +Grando +EnarÄ¥iva grando +Atributoj +Kreita +Malfermita +ÅœanÄita +Solida +Komento +Ĉifra +Disigita antaÅ­ +Disigita post +Vortaro +CRC +Tipo +KontraÅ­ +Metodo +Gastiga operaciumo +Dosiersistemo +Uzulo +Grupo +Bloko +Komento +Pozicio +Vojprefikso + + + + + + + + + + + + + + + + + + + + + + + + +Eraro +Kapacito +Libera +Faskogrando +Marko +Loka nomo +Provizanto +2100 +Agordoj +Lingvo +Lingvo: +Redaktilo +&Redaktilo: + +2200 +Sistemo +Asociu 7-Zip-on kun dosieroj: +2301 +Metu 7-Zip'on en kuntekstan menuon de Åelo +Kaskada kunteksta menuo +Punktoj de kunteksta menuo: +2320 + + +Malfermu +ElarÄ¥ivigu dosierojn... +EnarÄ¥ivigu... +Testu arÄ¥ivon +ElarÄ¥ivigu ĉi-tien +ElarÄ¥ivigu en {0} +Aldonu al {0} +EnarÄ¥ivigu kaj enretpoÅtigu... +EnarÄ¥ivigu en {0} kaj enretpoÅtigu... +2400 +Dosierujoj +&Kuranta dosierujo +&Sistema labora dosierujo +&Ĉi tiu +&Specifu: +&Uzi nur por demeteblaj datumportiloj +Specifu ujon por laboraj dosieroj. +2500 +Agordoj +Montru ".."-elementon +Montru realajn dosierbildetojn +Montru sisteman menuon +Marku &tutan linion +Montru &kradliniojn + +&Alternativa markreÄimo +Uzu &grandajn memorpaÄojn +2900 +Informo +7-Zip estas senpaga programo. Tamen, vi povas subteni evoluadon de 7-Zip per enregistriÄo. +3000 + +Eraroj ne trovitaj +{0} objekto(j) markita(j) +Fiaskis krei dosierujon '{0}' +Äœisdatigoperacioj ne estas subtenataj por ĉi-tiu arÄ¥ivo. + + + + +Dosiero '{0}' ÅanÄiÄis.\nĈu vi volas Äistadigi Äin enraÄ¥ive? +Fiaskis Äisdatigi dosieron\n'{0}'' +Fiaskis startigi redaktilon. + + + + +Troaj elementoj +3300 +ElarÄ¥ivigo +Densigo +Testado +Malfermo... +Analizante... +3400 +&ElarÄ¥ivigu +E&larÄ¥ivigu en: +Specifu ujon por elarÄ¥ivendaj dosieroj. +3410 +Dosierindikoj +&Absolutaj dosierindikoj +&Sen dosierindikoj +3420 +AnstataÅ­iga skribreÄimo +&Kun konfirmo +&Sen konfirmo +&Preterlasu estantaj dosieroj +AÅ­tonomÅanÄo +AÅ­tonomÅanÄo de ekzistantaj dosieroj +3500 +Konfirmo de nomÅanÄo +Dosierujo jam enhavas prilaboratan dosieron. +AnstataÅ­igu estantan dosieron +per ĉi-tiu? +{0} bajtoj +&AÅ­tonomÅanÄo. +3700 +Ne estas subtenata densigmetodo por dosiero '{0}'. +Datumeraro en '{0}'. DifektiÄinta dosiero. +CRC-eraro en '{0}'. DifektiÄinta dosiero. + + +3800 +Pasvorto +Enigu pasvorton: + +&Montru pasvorton + + + +&Pasvorto +3900 +Pasinta tempo: +Restanta tempo: +Grando: +Rapideco: + + +Eraroj: + +4000 +EnarÄ¥ivigu +&ArÄ¥ivo: +A&nstataÅ­igreÄimo: +A&rÄ¥ivformato: +Densigo&nivelo +&Densigmetodo: +&Vortarogrando: +Vo&rtogrando: + + +&Parametroj: +Agordoj +Kreu SF&X-arÄ¥ivon + + + +Ĉifru dosier&nomojn +Memoruzo por densigo: +Memoruzo por maldensigo: +4050 +Sen densigo +Plej rapide +Rapide +Normala densigo +Maksimuma densigo +Ultra +4060 +Aldonu kaj anstataÅ­igu dosierojn +Äœisdatigu kaj aldonu dosierojn +RefreÅigu estantajn dosierojn +Sinkronigu dosierojn +4070 +Foliumu +Ĉiuj dosieroj + + +6000 +Kopiu +Movu +Kopiu en: +Movu en: +Kopio... +Movo... +NomÅanÄo... + +Operacio ne estas subtenata. +Eraro dum nomÅanÄo de dosiero aÅ­ dosierujo +Konfirmu dosierkopion +Ĉu vere kopii dosierojn enarÄ¥iven +6100 +Konfirmo de forigo de dosiero +Konfirmo de forigo de dosierujo +Konfirmo de forigo de dosieraro +Ĉu vi vere volas forigi '{0}'? +Ĉu vi vere volas forigi dosierujon "{0}" kaj tutan Äian enhavon? +Ĉu vi vere volas forigi ĉi tiajn {0} objektojn? +Forigo... +Eraro dum forigo de dosiero aÅ­ dosierujo + +6300 +Kreu dosierujon +Kreu dosieron +Dosierujnomo: +Dosiernomo: +Nova dosierujo +Nova dosiero +Eraro dum dosierujkreo +Eraro dum dosierkreo +6400 +Komento +&Komento: +Marku +Malmarku +Masko: +6600 + +Dosierujhistorio +Diagnozaj mesaÄoj +MesaÄo +7100 +Komputilo +Reto + +Sistemo +7200 +EnarÄ¥ivigu +ElarÄ¥ivigu +Testu +Kopiu +Movu +Forigu +Informo +7300 +Erigu dosieron +&Erigu en: +&Plurvolumigu, bajtoj: +Erigo... + + + + + +7400 +Kombinu dosierojn +&Kombinu en: +Kombino... + + + +7500 +Kalkulante kontrolsumon... +Informo pri kontrolsumo +CRC-kontrolsumo por datumoj: +CRC-kontrolsumo por datumoj kaj nomoj: +7600 +Etalono +Memoruzo: +Densigo +Maldensigo +Takso +Tuta takso +Kuranta +Rezulta + + +Pasoj: diff --git a/Utils/7-Zip/Lang/es.txt b/Utils/7-Zip/Lang/es.txt new file mode 100644 index 000000000..15b17071d --- /dev/null +++ b/Utils/7-Zip/Lang/es.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; : Pablo Rodriguez +; : Jbc25 +; : 2007-09-05 : Guillermo Gabrielli +; 9.07 : 2010-06-10 : Purgossu +; 2010-10-23 : Sergi Medina (corrected) +; +; +; +; +; +; +0 +7-Zip +Spanish +Español +401 +Aceptar +Cancelar + + + +&Sí +&No +&Cerrar +Ayuda + +&Continuar +440 +Sí a &todo +No a t&odo +Parar +Volver a empezar +Se&gundo plano +P&rimer plano +&Pausa +Pausado +¿Estás seguro de que deseas cancelar? +500 +&Archivo +&Editar +&Ver +&Favoritos +&Herramientas +Ay&uda +540 +&Abrir +Abrir &dentro +Abrir &fuera +&Ver +&Editar +Re&nombrar +&Copiar a... +&Mover a... +&Borrar +Di&vidir archivo... +C&ombinar archivos... +&Propiedades +Comen&tario +Suma de verificación +Diff +Crear carpeta +Crear archivo +&Salir +600 +Seleccionar &todo +Deseleccionar todo +&Invertir selección +Seleccionar... +Deseleccionar... +Seleccionar por tipo +Deseleccionar por tipo +700 +Iconos g&randes +&Iconos pequeños +&Lista +&Detalles +730 +Desordenado +Vista plana (flat view) +&2 paneles +&Barras de herramientas +Abrir directorio raíz +Subir un directorio +Historia de carpetas... +&Actualizar +750 +Barra de herramientas Archivo +Barras de herramientas estándar +Botones grandes +Mostrar texto en los botones +800 +&Añadir carpeta a favoritos como +Agregar a favoritos +900 +&Opciones... +&Pruebas (benchmark) +960 +&Contenido... +&Acerca de 7-Zip... +1003 +Ruta +Nombre +Tipo de archivo +Directorio +Tamaño +Tamaño comprimido +Atributos +Creado +Acceso +Modificado +Compacto +Comentado +Encriptado +expandido antes +expandido después +Diccionario +CRC +Tipo +Anti +Método +SO de origen +Sistema de archivos +Usuario +Grupo +Bloque +Comentario +Posición +Ruta +Directorios +Ficheros +Versión +Volumen +Multivolumen +Desplazamiento +Vínculos +Bloques +Volúmenes + +64-bit +Big-endian +CPU +Tamaño físico +Tamaño de las cabeceras +Verificación de suma +Características +Dirección virtual +ID +Nombre corto +Aplicación de creación +Tamaño de sector +Modo +Enlace +Error +Espacio total +Espacio libre +Tamaño de clúster +Etiqueta +Nombre local +Proveedor +2100 +Opciones +Lengua +Lengua: +Editor +&Editor: +&Diff: +2200 +Sistema +Asociar 7-Zip con: +2301 +Integrar 7-Zip en el menú contextual de Windows +Menú contextual en cascada +Elementos en el menú contextual: +2320 + + +Abrir comprimido +Extraer ficheros... +Añadir al archivo... +Comprobar archivo +Extraer aquí +Extraer en {0} +Añadir a {0} +Comprimir y enviar por correo... +Comprimir a {0} y enviar por correo +2400 +Directorios +Directorio de &trabajo +Directorio temporal del &sistema +Directorio &actual +&Especificar directorio: +Usar solo para dispositivos extraíbles +Especificar un directorio para archivos temporales. +2500 +Propiedades +Mostrar el elemento ".." +Mostrar iconos propios +Mostrar menú del sistema +&Seleccionar fila(s) entera(s) +Mostrar &cuadrícula +Clicar una vez para abrir elemento +Modo de selección &alternativo +Usar páginas &grandes de memoria +2900 +Acerca de 7-Zip +7-Zip es un programa excelente; además, es libre y gratuito. Tú puedes apoyar el desarrollo de 7-Zip registrándote para contribuir a mejorar el programa. +3000 +El sistema no ha podido asignar la cantidad necesaria de memoria +No hay errores +{0} elemento(s) seleccionado(s) +No se puede crear el directorio '{0}' +Este tipo de archivo no permite actualización. +No se puede abrir '{0}' como un archivo comprimido +No se puede abrir el archivo encriptado '{0}'. Verifique la contraseña. +Tipo de archivo no soportado +El fichero {0} ya existe +El fichero '{0}' ha sido modificado.\n¿Quieres actualizarlo en el archivo? +No puede actualizarse el fichero\n'{0}' +No puede ejecutarse el editor. +El fichero parece un virus (el nombre del fichero contiene espacios largos). +No puede realizarse la operación desde una carpeta que tenga una ruta larga. +Debes seleccionar un fichero +Debes seleccionar uno o más ficheros +Demasiados elementos +3300 +extrayendo +comprimiendo +probando +abriendo... +Buscando... +3400 +Extraer +E&xtraer a: +Selecciona destino para los archivos extraídos. +3410 +Modo de directorio +Directorio completo +Sin directorio +3420 +Sobreescribir +Con confirmación +Sin confirmación +Conservar archivos existentes +Renombrar automáticamente +Autorrenombrar archivos existentes +3500 +Confirmar sustitución de archivos +El directorio ya contiene un archivo con el mismo nombre. +¿Deseas sustituir el archivo existente +por este otro? +{0} bytes +Renombrar a&utomáticamente +3700 +Método de compresión no válido para '{0}'. +Error de datos en '{0}'. El archivo está corrupto. +CRC ha fallado en '{0}'. El archivo está corrupto. +Error de datos en el archivo encriptado '{0}'. Verifica la contraseña. +Fallo de CRC en el archivo encriptado '{0}'. Verifica la contraseña. +3800 +Introduce la contraseña +E&scribe la contraseña: +Escribe nue&vamente la contraseña: +&Mostrar la contraseña +Las contraseñas son diferentes. Por favor, vuelve a escribirlas. +Usa en la contraseña solamente letras del alfabeto inglés, números y caracteres especiales (!, #, $, ...) +La contraseña es demasiado larga. +Contraseña +3900 +Tiempo transcurrido: +Tiempo pendiente: +Tamaño: +Velocidad: +Procesado: +Razón de compresión: +Errores: +Archivos: +4000 +Añadir al archivo +&Archivo: +M&odo de actualización: +&Formato de archivo: +Nive&l de compresión: +&Tipo de compresión: +Tamaño de &diccionario: +Tama&ño de la palabra: +Tamaño de bloque compacto: +Número de hilos de la CPU: +&Parámetros: +Opciones +Crear archivo SF&X (autoextraíble) +Comprimir archivos abiertos para escritura +Encriptación +Método de &encriptación: +Encriptar &nombres de fichero +Memoria usada para comprimir: +Memoria usada para descomprimir: +4050 +Sin compresión +La más rápida +Rápida +Normal +Máxima +Ultra +4060 +Añadir y sustituir archivos +Actualizar y añadir archivos +Solo actualizar archivos +Sincronizar archivos +4070 +Explorar +Todos los archivos +No compacto +Sin límite +6000 +Copiar +Mover +Copiar a: +Mover a: +copiado... +movido... +Renombrando... +Selecciona la carpeta de destino +Operación no permitida. +Error renombrando fichero o carpeta +Confirmar copia de ficheros +¿Estás seguro de que deseas copiar los ficheros al archivo +6100 +Confirmar borrado de archivo +Confirmar borrado de carpeta +Confirmar borrado de numerosos ficheros +¿Estás seguro de querer borrar '{0}'? +¿Estás seguro de querer borrar la carpeta '{0}' y todo su contenido? +¿Estás seguro de querer borrar estos {0} elementos? +Borrando... +Error borrando fichero o carpeta +El sistema no puede mover un fichero con ruta larga a la Papelera de Reciclaje +6300 +Crear carpeta +Crear archivo +Nombre de carpeta: +Nombre de archivo: +Carpeta nueva +Archivo nuevo +Error creando carpeta +Error creando archivo +6400 +Comentario +&Comentario: +Seleccionar +Deseleccionar +Máscara: +6600 +Propiedades +Historial de carpetas +Mensajes de diagnóstico +Mensaje +7100 +Mi PC +Entorno de red +Documentos +Sistema +7200 +Agregar +Extraer +Probar +Copiar +Mover +Borrar +Información +7300 +Dividir archivo +Di&vidir a: +Dividir en fra&gmentos (bytes): +Dividiendo... +Confirmar división +¿Estás seguro de que deseas dividir el archivo en {0} partes? +El tamaño de los fragmentos debe ser menor que el del archivo original +Tamaño de fragmento incorrecto +Tamaño de fragmento especificado: {0} bytes.\n¿Estás seguro de que deseas dividir el archivo en fragmentos de ese tamaño? +7400 +Combinar archivos +&Combinar a: +Combinando... +Selecciona solamente el primer archivo +No se ha podido detectar el fichero como parte de un fichero por volúmenes +No se ha podido encontrar más que un fragmento del fichero por volúmenes +7500 +Calculando suma de verificación... +Suma de verificación (CRC) +CRC de los datos: +CRC de los datos y nombres: +7600 +Pruebas (benchmark) +Uso de memoria: +Compresión +Descompresión +Tasa +Tasa total +Actual +Resultante +Uso de CPU +Resultante/uso +Pasos: diff --git a/Utils/7-Zip/Lang/et.txt b/Utils/7-Zip/Lang/et.txt new file mode 100644 index 000000000..de2282f23 --- /dev/null +++ b/Utils/7-Zip/Lang/et.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 3.09 : Kaupo Suviste +; 9.07 : Mihkel Tõnnov +; +; +; +; +; +; +; +; +; +0 +7-Zip +Estonian +eesti keel +401 +OK +Loobu + + + +&Jah +&Ei +&Sulge +Abi + +&Jätka +440 +Kõigile j&ah +Kõigile e&i +Seiska +Restardi +&Taustal +&Esiplaanile +&Paus +Pausiks peatatud +Kas soovite kindlasti loobuda? +500 +&Fail +&Redigeeri +&Vaade +&Lemmikud +&Tööriistad +&Abi +540 +&Ava +Ava s&ees +Ava väljasp&ool +Vaat&ur +&Redigeeri +&Nimeta ümber +&Kopeeri asukohta... +&Teisalda asukohta... +Ku&stuta +Tükel&da fail... +Ü&henda failid... +Atri&buudid +Ko&mmentaar... +Arvuta kontrollsumma +Võrdle +Loo kaust +Loo fail +&Välju +600 +V&ali kõik +Tühista kõik valikud +&Pööra valik +Vali... +Tühista valik... +Vali tüübi järgi +Tühista tüübi järgi valik +700 +&Suured ikoonid +Väik&esed ikoonid +&Loend +Üksikasja&d +730 +Sortimata +Lame vaade +&Kaks paani +&Tööriistaribad +Ava juurkaust +Taseme võrra üles +Kaustaajalugu... +&Värskenda +750 +Arhiiviriistariba +Standardnupuriba +Suured nupud +Kuva nupusildid +800 +&Lisa kaust lemmikute hulka järjehoidjana +Järjehoidja +900 +&Häälestus... +&Jõudlustest +960 +&Sisukord... +&Teave 7-Zipi kohta... +1003 +Tee +Nimi +Laiend +Kaust +Maht +Maht tihendatult +Atribuudid +Loodud +Avatud +Muudetud +Ühtne +Kommenteeritud +Krüptitud +Tükeldatud enne +Tükeldatud pärast +Sõnastik +CRC +Tüüp +Anti +Meetod +Opsüsteem +Failisüsteem +Kasutaja +Rühm +Plokk +Kommentaar +Koht +Tee prefiks +Kaustu +Faile +Versioon +Köide +Mitmeköiteline +Nihe +Linke +Plokke +Köiteid + +64-bitine +Big-Endian +Protsessor +Füüsiline maht +Päiste maht +Kontrollsumma +Karakteristikud +Virtuaalaadress +ID +Lühinimi +Loomisrakendus +Sektori maht +Režiim +Link +Tõrge +Kogumaht +Vaba ruum +Klastri suurus +Silt +Kohalik nimi +Teenusepakkuja +2100 +Häälestus +Keel +Keel: +Redaktor +&Redaktor: +&Võrdlusprogramm: +2200 +Süsteem +Seosta 7-Zip laienditega: +2301 +Integreeri 7-Zip kesta hüpikmenüüsse +Kaskaad-hüpikmenüü +Hüpikmenüü käsud: +2320 + + +Ava arhiiv +Eralda failid... +Lisa arhiivi... +Testi arhiivi +Eralda siia +Eralda kausta {0} +Lisa arhiivi {0} +Tihenda ja meili... +Tihenda arhiiviks {0} ja meili +2400 +Kaustad +&Töökaust +&Süsteemi ajutiste failide kaust +&Praegune kaust +&Kasutaja määratud: +Kasuta ainult irddraivide puhul +Määrake ajutiste arhiivifailide asukoht. +2500 +Sätted +Kuva element ".." +Kuva tegelikud failiikoonid +Kuva süsteemimenüü +&Vali terve rida +Kuva &ruudujooned +Ava üksus ühe klõpsuga +&Alternatiivne valikurežiim +Kasuta &suuri mälulehekülgi +2900 +Teave 7-Zipi kohta +7-Zip on vaba tarkvara. Kuid kui soovite toetada 7-Zipi arendamist, siis saate programmi registreerida.\n\n7-Zipi Eesti koduleht:\nhttp://www.hot.ee/somberg/7zip.html +3000 +Süsteem ei saa eraldada nõutavat mälumahtu. +Vigu ei leitud. +{0} üksus(t) valitud +Ei saa luua kausta {0} +Selle arhiivi värskendamistoiminguid ei toetata. +Ei saa avada faili {0} arhiivina. +Ei saa avada krüptitud arhiivi {0}. Kas vale parool? +Toetamata arhiivitüüp. +Fail {0} on juba olemas. +Faili {0} on muudetud.\nKas soovite selle arhiivis värskendada? +Ei saa värskendada faili\n{0} +Ei saa käivitada redaktorit. +See fail sarnaneb viirusega (faili nimi sisaldab pikka tühikute jada). +Toimingut ei saa käivitada kaustast, millel on pikk tee. +Te peate valima ühe faili. +Te peate valima ühe või mitu faili. +Liiga palju üksusi. +3300 +välja eraldatud +tihendatud +Testimine +Avamine... +Läbivaatamine... +3400 +Väljaeraldamine +&Eralda välja kausta: +Määrake väljaeraldatud failide asukoht. +3410 +Teed +&Täielikud teenimed +Teenime&deta +3420 +Ülekirjutus +Küsi e&nne ülekirjutamist +Ki&rjuta küsimata üle +&Jäta olemasolevad failid vahele +Nimeta a&utomaatselt ümber +Nimeta &olemasolevad f. autom. ümber +3500 +Failiasenduse kinnitamine +Sihtkaust juba sisaldab töödeldavat faili. +Kas soovite asendada olemasoleva faili +selle failiga? +{0} baiti +Nimeta a&utomaatselt ümber +3700 +Toetuseta tihendusmeetod failile {0}. +Andmeviga failis {0}. Fail on rikutud. +Tsükkelkoodkontroll (CRC) failis {0} nurjus. Fail on rikutud. +Andmeviga krüptitud failis {0}. Kas vale parool? +Tsükkelkoodkontroll (CRC) krüptitud failis {0} nurjus. Kas vale parool? +3800 +Parooli sisestamine +Sisestage parool: +Parooli kordus: +&Kuva parool +Paroolid ei kattu. +Kasutage paroolis ainult inglise keele tähti, numbreid ja erimärke (!, #, $, ...). +Parool on liiga pikk. +&Parool +3900 +Kulunud aeg: +Järelejäänud aeg: +Kogumaht: +Kiirus: +Töödeldud: +Tihendussuhe: +Vigu: +Arhiive: +4000 +Arhiivi lisamine +&Arhiiv: +Värskend&usrežiim: +Arhiivi&vorming: +&Tihendusaste: +Tihendus&meetod: +Sõnaraamatu ma&ht: +&Sõna maht: +Ühtse ploki maht: +Protsessorilõimede arv: +Pa&rameetrid: +Suvandid +Loo is&eavanev arhiiv +Tihenda kirjutuseks avatud failid +Krüptimine +Krüptimismeetod: +Krüpti faili&nimed +Mälu hõivatus tihendamisel: +Mälu hõivatus hõrendamisel: +4050 +Tihenduseta +Kiireim tihendus +Kiirtihendus +Normaaltihendus +Maksimaaltihendus +Ultratihendus +4060 +Lisa ja asenda failid +Värskenda ja lisa failid +Värskenda olemasolevad failid +Sünkrooni failid +4070 +Sirvi +Kõik failid +Mitteühtne +Ühtne +6000 +Kopeerimine +Teisaldamine +Kopeeri asukohta: +Teisalda asukohta: +Kopeerimine... +Teisaldamine... +Ümbernimetamine... +Valige sihtkaust. +See toiming pole selles kaustas toetatud. +Tõrge faili või kausta ümbernimetamisel +Failikopeerimise kinnitamine +Kas soovite kindlasti kopeerida arhiivi järgmised failid: +6100 +Failikustutuse kinnitamine +Kaustakustutuse kinnitamine +Mitme faili kustutamise kinnitamine +Kas soovite kindlasti kustutada faili {0}? +Kas soovite kindlasti kustutada kausta {0} ja kogu selle sisu? +Kas soovite kindlasti kustutada need {0} üksust? +Kustutamine... +Tõrge faili või kausta kustutamisel +Süsteem ei saa teisaldada prügikasti pika teega faili. +6300 +Kausta loomine +Faili loomine +Kausta nimi: +Faili nimi: +Uus kaust +Uus fail +Tõrge kausta loomisel +Tõrge faili loomisel +6400 +- kommentaar +&Kommentaar: +Valimine +Valiku tühistamine +Mask: +6600 +Atribuudid +Kaustaajalugu +Diagnostikateated +Teade +7100 +Arvuti +Võrk +Dokumendid +Süsteem +7200 +Lisa +Eralda välja +Testi +Kopeeri +Teisalda +Kustuta +Teave +7300 +Faili tükeldamine: +&Tükelda asukohta: +&Tükelda köideteks (baitides): +Tükeldamine... +Tükeldamise kinnitamine +Kas soovite kindlasti tükeldada faili {0} köiteks? +Köite maht peab olema algfaili mahust väiksem. +Sobimatu köitemaht. +Määratud köitemaht: {0} baiti.\nKas soovite kindlasti tükeldada arhiivi niisugusteks köideteks? +7400 +Failide ühendamine: +Ü&henda asukohta: +Ühendamine... +Valige ainult tükeldatud faili esimene osa. +Ei õnnestu tuvastada, et see fail oleks tükeldatud faili osa. +Ei leia rohkem kui ühte tükeldatud faili osa. +7500 +Kontrollsumma arvutamine... +Kontrollsumma +Andmete CRC-kontrollsumma: +Andmete ja nimede CRC-kontrollsumma: +7600 +Jõudlustest +Mälu hõivatus: +Tihendamine +Hõrendamine +Hinnang +Üldine jõudlushinnang +Praegune: +Lõpptulemus: +CPU hõivatus +Hinnang/hõivatus +Töötiire: diff --git a/Utils/7-Zip/Lang/eu.txt b/Utils/7-Zip/Lang/eu.txt new file mode 100644 index 000000000..e9be78746 --- /dev/null +++ b/Utils/7-Zip/Lang/eu.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 15.12 : 2015-12-04 : Xabier Aramendi +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Basque +Euskara +401 +&Ongi +E&zeztatu + + + +&Bai +&Ez +It&xi +&Laguntza + +&Jarraitu +440 +Bai &Guztiari +Ez G&uztiari +Gelditu +Berrabiarazi +Ba&rrenean +&Gainean +&Pausatu +Pausatuta +Zihur zaude ezeztatzea nahi duzula? +500 +&Agiria +&Editatu +&Ikusi +&Gogokoenak +&Tresnak +&Laguntza +540 +&Ireki +Ireki &Barnean +Ireki &Kanpoan +Ik&usi +&Editatu +Berrize&ndatu +Kopiatu &Hona... +&Mugitu Hona... +E&zabatu +Banan&du agiria... +Nahas&tu agiriak... +Ezau&garriak +&Aipamena... +Ka&lkulatu egiaztapen-batura +Ezber +Sortu Agiritegia +S&ortu Agiria +I&rten +Lotura +&Aldikatu Jarioak +600 +Hautatu &Guztiak +Deshatutau G&uztiak +&Alderantzizkatu Hautapena +&Hautatu... +&Deshautatu... +Hautatu &Motaz +Deshautatu M&otaz +700 +Ikur &Handiak +Ikur Txi&kiak +&Zerrenda +&Xehetasunak +730 +Ant&olatugabe +Ik&uspegi Laua +&2 Panel +&Tresnabarrak +Ireki &Erro Agiritegia +Maila Bat &Gora +Agiritegi &Historia... +&Berritu +Be&rez Berritu +750 +Artxibo Tresnabarra +Tresnabarra Estandarra +Botoi Handiak +Erakutsi Botoien Idazkia +800 +&Gehitu agiritegia Gogokoenetara honela +Lastermarka +900 +A&ukerak... +&Benchmark +960 +&Edukiak... +&7-Zip Buruz... +1003 +Helburua +Izena +Luzapena +Agiritegia +Neurria +Pakete Neurria +Ezaugarriak +Sortua +Sartua +Aldatua +Solidoa +Aipatua +Enkriptatua +Banandu Aurretik +Banandu Ondoren +Hiztegia + +Mota +Anti +Metodoa +Hostalari SE +Agiri Sistema +Erabiltzailea +Taldea +Blokea +Aipamena +Kokapena +Helburu Aurrizkia +Agiritegiak +Agiriak +Bertsioa +Bolumena +Bolumen-anitz +Oreka +Loturak +Blokeak +Bolumenak + +64-bit +Big-endian +CPU +Neurri Fisikoa +Idazburu Neurria +Egiaztapen-batura +Ezaugarriak +Helbide Birtuala +ID-a +Izen Laburra +Aplikazio Sortzailea +Sektore Neurria +Modua +Lotura Sinbolikoa +Akatsa +Neurria Guztira +Toki Askea +Kluster Neurria +Etiketa +Tokiko Izena +Hornitzailea +NT Segurtasuna +Aldikatu Jarioa +Aux +Ezabatuta +Zuhaitza da + + +Akats Mota +Akatsak +Akatsak +Ohartarazpenak +Ohartarazpena +Jarioak +Aldikatu Jarioak +Aldikatu Jario Neurriak +Neurri Birtuala +Despaketatu Neurria +Neurri Fisikoa Guztira +Bolumen Aurkibidea +AzpiMota +Aipamen Labura +Kode Orrialdea + + + +Isats Neurria +Barneratutako Stub Neurria +Lotura +Lotura Gogorra +iNode + +Irakurtzeko-bakarrik +2100 +Aukerak +Hizkuntza +Hizkuntza: +Editatzailea +&Editatzailea: +E&zberdintasunak: +2200 +Sistema +Elkartu 7-Zip hauekin: +Erabiltzaile guztiak +2301 +&Bateratu 7-Zip shell hitzinguru menura +&Urjauzi hitzinguru menua +Hitzinguru menuko gaiak: +&Ikurrak hitzinguru menuan +2320 + + +Ireki artxiboa +Atera agiriak... +Gehitu &artxibora... +Aztertu artxiboa +Atera Hemen +Atera hona: {0} +Gehitu hona: {0} +Konprimitu eta &bidali post@z... +Konprimitu hona {0} eta bidali post@z +2400 +Agiritegiak +&Lan agiritegia +&Sistemaren aldibaterako agiritegia +&Oraingoa +A&dierazia: +&Erabili gidagailu kengarrientzat bakarrik +Adierazi aldibaterako artxibo agirientzako kokalekua +2500 +Ezarpenak +&Erakutsi ".." gai +E&rakutsi egizko agiriaren ikurrak +Erakutsi siste&maren menua +&Lerro osoko hautapena +Erakutsi &saretxo lerroak +&Klik-bakarra gai bat irekitzeko +A&ukerazko hautapen modua +Erabili &oroimen handiko orrialdeak +2900 +7-Zip buruz +7-Zip software askea da +3000 +Sistemak ezin du beharrezko oroimen kopurua esleitu +Ez dago akatsik +{0} objetu hautaturik +Ezin da '{0}' agiritegia sortu +Eguneratze eragiketak ez daude sostengaturik artxibo honentzat. +Ezinezkoa '{0}' agiria artxibo bezala irekitzea +Ezinezkoa '{0}' artxibo enkriptatua ireki. Sarhitz okerra? +Artxibo mota ez dago sostengatua +{0} agiria badago jadanik +'{0}' agiria aldatu egin da.\nArtxioboan eguneratzea nahi duzu? +Ezinezkoa agiria eguneratzea\n'{0}' +Ezin da editatzailea abiarazi. +Agiriak birus bat ematen du (agiri izenak tarte luzeak ditu izenean). +Eragiketa ezin da helburu luze bat duen agiritegi batetik deitu +Agiri bat hautatu behar duzu +Agiri bat edo gehiago hautatu behar dituzu +Gai gehiegi +Ezin da agiria {0} artxibo bezala ireki +Agiria {0} artxibo bezala dago irekita +Artxiboa orekaz irekita dago +3300 +Ateratzen +Konprimitzen +Aztertzen +Atzeratzen... +Mihatzen... +Kentzen +3320 +Gehitzen +Eguneratzen +Aztertzen +Erreplikatzen +Berpaketatzen +Jauzten +Ezabatzen +Idazburua sortzen +3400 +&Atera +Atera &hona: +A&dierazi ateratako agirientzako kokaleku bat. +3410 +Helburu mo&dua: +Helburu-izen osoak +Helburu-izenik ez +Helburu-izen absolutoak +Helburu-izen erlatiboak +3420 +Gainidazketa modua: +Galdetu gainidatzi aurretik +Gainidazi galdetu gabe +Jauzi dauden agiriak +Berez berrizendatu +Berez berrizendatu dauden agiriak +3430 +Ezabatu erro agiritegi bikoizketa +Leheneratu agiri segurtasuna +3500 +Baieztatu Agiri Ordeztea +Helmuga agiritegiak jadanik badu prozesatutako agiria. +Dagoen agiria ordeztea nahi duzu +beste honekin? +{0} byte +&Berez Berrizendatu +3700 +Konpresio metodo sostengatu gabea '{0}'-rako. +Datu akatsa '{0}'. Agiria hautsita dago. +CRC hutsegitea '{0}'. Agiria hautsita dago. +Datu akatsa '{0}' enkriptaturiko agirian. Sarhitz okerra? +CRC hutsegitea '{0}' enkriptaturiko agirian. Sarhitz okerra? +3710 +Sarhitz okerra? +3721 +Konpresio metodoa ez dago sostengatua +Datu akatsa +CRC hutsegitea +Datu eskuraezinak +Ustekabeko datu amaiera +Zenbait akats daude gertaketa amaieraren ondoren +Ez da artxiboa +Idazburu Akatsa +Sarhitz okerra +3763 +Artxibo hasiera eskuraezina +Artxibo hasiera baieztatugabe + + + +Ezaugarri sostengu gabea +3800 +Sartu sarhitza +Sartu sarhitza: +Bersartu sarhitza: +&Erakutsi sarhitza +Sarhitzak ez datoz bat +Erabili bakarrik Ingelerazko hizkiak, zenbakiak eta hizki bereziak (!, #, $, ...) sarhitzarentzat +Sarhitza luzeegia da +Sarhitza +3900 +Igarotako denbora: +Gelditzen den denbora: +Neurria guztira: +Abiadura: +Prozesatuta: +Konpresio maila: +Akatsak: +Artxiboak: +4000 +Gehitu artxibora +&Artxiboa: +Egu&neraketa modua: +Artxibo he&uskarria: +Konpre&sio maila: +Konpresio &metodoa: +&Hiztegi neurria: +Hi&tz neurria: +&Bloke solidoaren neurria: +&CPU hari zenbatekoa: +&Parametroak: +Aukerak +Sortu SF&X artxiboa +&Konprimitu elkarbanaturiko agiriak +Enkriptaketa +Enkriptaketa metodoa: +Enkriptatu agiri &izenak +Oroimen erabilera Konprimitzeko: +Oroimen erabilera Deskonprimitzeko: +Ezabatu agi&riak konprimitu ondoren +4040 +Biltegiratu lotura sinbolikoak +Biltegiratu lotura gogorrak +Biltegiratu aldikatu datu jarioak +Biltegiratu agiri segurtasuna +4050 +Biltegia +Azkarrena +Azkarra +Arrunta +Gehiena +Ultra +4060 +Gehitu eta ordeztu agiriak +Eguneratu eta gehitu agiriak +Berritu dauden agiriak +Aldiberetu agiriak +4070 +Bilatu +Agiri Denak +Ez-solidoa +Solidoa +6000 +Kopiatu +Mugitu +Kopiatu hona: +Mugitu hona: +Kopiatzen... +Mugitzen... +Berrizendatzen... +Hautatu helmuga agiritegia. +Eragiketa hau ez dago sostengatua agiritegi honentzat. +Akatsa Agiria edo Agiritegia Berrizendatzerakoan +Baieztatu Agiri Kopiatzea +Zihur zaude agiriak artxibora kopiatzea nahi dituzula +6100 +Baieztatu Agiri Ezabapena +Baieztatu Agiritegi Ezabapena +Baieztatu Agiri Anitz Ezabapena +Zihur zaude '{0}' ezabatzea nahi duzula? +Zihur zaude '{0}' agiritegia eta bere eduki guztiak ezabatzea nahi dituzula? +Zihur zaude {0} gai hauek ezabatzea nahi dituzula? +Ezabatzen... +Akatsa Agiria edo Agiritegia Ezabatzerakoan +Sistemak ezin du helburu luzeko agiria Birziklapen Ontzira mugitu +6300 +Sortu Agiritegia +Sortu Agiria +Agiritegi izena: +Agiri Izena: +Agiritegi Berria +Agiri Berria +Akatsa Agiritegia Sortzerakoan +Akatsa Agiria Sortzerakoan +6400 +Aipamena +&Aipamena: +Hautatu +Deshautatu +Mozorroa: +6600 +Ezaugarriak +Agiritegi Historia +Azterketa mezuak +Mezuak +7100 +Ordenagailua +Sarea +Agiriak +Sistema +7200 +Gehitu +Atera +Aztertu +Kopiatu +Mugitu +Ezabatu +Argibideak +7300 +Banandu Agiria +Banandu &hona: +Banandu &bolumenetan, byte: +Banantzen... +Baieztatu Banantzea +Zihur zaude agiria {0} bolumenetan banantzea nahi duzula? +Bolumen neurria jatorrizko agiriaren neurria baino txikiagoa izan behar da +Bolumen neurri okerra +Adierazitako bolumen neurria: {0} byte.\nZihur zaude artxiboa bolumenetan banantzea nahi duzula? +7400 +Nahastu Agiriak +&Nahastu hona: +Nahasten... +Hautatu bakarrik banantzeko agiriaren lehen atala +Ezin da agiria atzeman banandutako agiriaren atal bezala +Ezin da aurkitu banandutako agiriaren atal bat baino gehiago +7500 +Egiaztapen-batura kalkulatzen... +Egiaztapen-batura argibideak +CRC Egiaztapen-batura datuentzat: +CRC Egiaztapen-batura datu eta izenentzat: +7600 +Benchmark +Oroimen erabilpena: +Konprimitzen +Deskonprimitzen +Mailaketa +Mailaketa Guztira +Oraingoa +Emaitza +CPU Erabilpena +Mailaketa / Erabilpena +Pasaldiak: +7700 +Lotura +Lotura +Lotura hemendik: +Lotura hona: +7710 +Lotura Mota +Lotura Gogorra +Agiri Lotura Sinbolikoa +Zuzenbide Lotura Sinbolikoa +Zuzenbide Elkargunea diff --git a/Utils/7-Zip/Lang/ext.txt b/Utils/7-Zip/Lang/ext.txt new file mode 100644 index 000000000..469cd18f4 --- /dev/null +++ b/Utils/7-Zip/Lang/ext.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; : Miguel Angel +; 9.07 : Purgossu +; +; +; +; +; +; +; +; +; +0 +7-Zip +Extremaduran +Estremeñu +401 +Acetal +Cancelal + + + +&Sí +&Nu +&Fechal +Ayua + +A&continal +440 +Sí &a tó +Nu a &tó +Paral +Reinicial +Se&gundu pranu +&Primel pranu +&Paral +Parau +De siguru que quieri cancelal la operación? +500 +&Archivu +&Eital +&Vel +A&tihus +&Herramientas +A&yua +540 +&Abril +Abril &dentru +Abril &huera +&Vel +&Eital +Renom&bral +&Copial a... +&Movel pa... +&Eliminal +De&sapartal ficheru... +Com&binal ficherus... +P&ropieais +Come&ntariu +Calculal suma e verificación +Diff +Creal diretóriu +Creal ficheru +&Salil +600 +Selecional &tó +Deselecional tó +&Invertil seleción +Selecional... +Deselecional... +Selecional pol tipu +Deselecional pol tipu +700 +Iconus g&randis +Iconus caquerus +&Listau +&Detallis +730 +Nu soportau +Vista prana +&2 panelis +Barra e herramien&tas +Abril diretóriu raí +Subil un nivel +Estorial de diretórius... +&Atualizal +750 +Barra e herramientas 'archivu' +Barra e herramientas estándal +Botonis grandis +Muestral testu enus botonis +800 +&Añíl diretóriu a Atihus comu +Atihu +900 +&Ocionis... +&Prebas ('benchmark') +960 +&Contenius... +&Al tentu 7-Zip... +1003 +Ruta +Nombri +Estensión +Diretóriu +Tamañu +Tamañu comprimiu +Atributus +Creau'l +Úrtimu acesu +Escambiau'l +Sóliu +Comentau +Encrihtau +Desapartau enantis +Desapartau endispués +Izionáriu +CRC +Tipu +Anti +Métou +S.O. orihin +Sistema d'archivus +Usuariu +Grupu +Broqui +Comentariu +Posición +Prefihu la ruta +Diretórius +Ficherus +Velsión +Volumin +Murtivolumin +Desprazamientu +Enlacis +Broquis +Volúmenis + +64-bit +Big-endian +UCP +Tamañu físicu +Tamañu las cabiceras +Suma e cumprebación +Caraterísticas +Direción virtual +ID +Nombri cortu +Apricación criaora +Tamañu el setol +Mou +Enlaci +Yerru +Tamañu total +Espaciu dispunibri +Tamañu el 'cluster' +Etiqueta +Nombri local +Proveol +2100 +Ocionis +Palra +Palra: +Eitol +&Eitol: +&Diff: +2200 +Sistema +Asocial 7-Zip a hormatus: +2301 +Integral 7-Zip nel menú contestual +Menú contestual en cascá +Elementus del menú contestual: +2320 + + +Abril archivu +Estrayel ficherus... +Añíl al archivu... +Comprebal archivu +Estrayel aquina +Estrayel en {0} +Añiil a {0} +Comprimil i envial pol correu-e... +Comprimil en {0} i envial pol correu +2400 +Diretórius +Diretóriu e labu&tu +Diretóriu temporal del &sistema +Diretóriu &ahtual +&Especifical: +Usal sólu pa dispositivus estrayíbris +Especifical ruta pa ficherus d'archivus temporalis. +2500 +Configuración +Muestral l'elementu \.." +Muestral icunus propius del ficheru +Muestral menú el sistema +&Selecional fila(s) enteriza(s) +Muestral línias e la &cuairícula +'Click' únicu p'abril un elementu +Móu e seleción &alternativu +Usa&l páhinas de memoria grandis +2900 +About 7-Zip +7-Zip es una apricación libri i a gastus pagus. Peru puei apoyal el desarrollu e 7-Zip meyanti'l rehistru el pograma. +3000 +El sistema nu á síu escapás d'asinal la cantiá prehisa de memoria +Sin yerrus +{0} elementu(s) selecionaus +Nu s'á puiu crial el diretóriu '{0}' +Nu se puei atualizal esti tipu d'archivu. +Nu s'á puiu abril '{0}' comu archivu +Nu s'a puiu abril l'archivu encritau '{0}'. Conseña yerronia? +Tipu archivu nu suportau +File {0} is already exist +El ficheru '{0}' á síu moificau.\nAtualizalu nel archivu? +Nu s'á puíu atualizal l'archivu\n'{0}' +Nu s'á puíu ehecutal el eitol. +El ficheru pahi un virus (el nombri'l ficheru contieni espacius largus nel nombri). +Nu puei realizasi la operación dendi un diretóriu que tenga una ruta larga. +You must select one file +You must select one or more files +Ai elementus en demasía +3300 +Estrayendu +Comprimiendu +Comprebandu +Abriendu... +Escaneandu... +3400 +Estrayel +E&strayel a: +Especifical destinu palus ficherus estrayíus. +3410 +Mou e ruta +Ruta compreta +Sin ruta +3420 +Mou e sobrescrebil +Preguntal enantis +Sobrescrebil sin preguntal +Conserval ficherus esistentis +Renombral de horma automática +Autu-renombral ficherus esistentis +3500 +Confirmal remprazu de ficherus +El diretóriu ya contieni un ficheru el mesmu nombri. +Escambial el ficheru esistenti +pol esti otru? +{0} bytes +Escambial nombri a&utomáticamenti +3700 +Métou e compresión nu soportau pa '{0}'. +Yerru datus en '{0}'. El ficheru está changarrau. +Yerru e CRC '{0}'. El ficheru está changarrau. +Yerru e datus nel ficheru encritau '{0}'. Conseña yerrónia? +Yerru e CRC nel ficheru encritau '{0}'. Conseña yerrónia? +3800 +Introuzil conseña +Introuzil conseña: +Repitil conseña: +Mue&stral conseña +Las cunseñas nu coinciín +Usi sólu letras ingresas, númirus i caráteris especialis (!, #, $, ...) pala conseña +La conseña tieni largura'n demasía +Conseña +3900 +Tiempu trascurríu: +Tiempu restanti: +Tamañu total: +Velociá: +Procesau: +Tasa e compresión: +Yerrus: +Archivus: +4000 +Añiil a archivu +&Archivu: +&Mou d'atualización: +&Hormatu d'archivu: +Nive&l de compresión: +Mé&tou e compresión: +Tamañu el i&zionariu: +Tama&ñu e parabra: +Tamañu el bloqui sóliu: +Númiru hilus e la UCP: +&Parámetrus: +Ocionis +Creal archivu 'SF&X' +Comprimil ficherus compartíus +Encritación +Métou encritación: +Encrital &nombris de ficheru +Usu e memoria pa compresión: +Usu e memoria pa decompresión: +4050 +Almacenal +La más respahila +Respahila +Normal +Másima +Ultra +4060 +Añiil i remprazal ficherus +Atualizal i añiil ficherus +Atualizal sólu ficherus esistentis +Sincronizal ficherus +4070 +Esproral +Tolos ficherus +Nu sóliu +Sóliu +6000 +Copial +Movel +Copial a: +Movel pa: +Copiandu... +Moviendu... +Renombrandu... +Selecional diretóriu destinu. +Operación nu soportá. +S'alcuentrau'n yerru al renombral el ficheru u diretóriu +Confirmal copia el ficheru +De siguru que quieri copiar estus ficherus al archivu? +6100 +Confirmal eliminación del ficheru +Confirmal eliminación del direhtoriu +Confirmal eliminación de varius ficherus +De siguru que quieri eliminal '{0}'? +De siguru que quieri eliminal el diretóriu '{0}' i tol su conteniu? +De siguru que quieri eliminal estus {0} elementus? +Eliminandu... +S'alcuentrau'n yerru al eliminal el ficheru u diretóriu +El sistema nu puei movel un ficheru con ruta larga a la Papelera Recicrahi +6300 +Creal diretóriu +Creal ficheru +Nombri e diretóriu: +Nombri el ficheru: +Nuevu diretóriu +Nuevu ficheru +S'alcuentrau'n yerru al creal el diretóriu +S'alcuentrau'n yerru al creal el ficheru +6400 +Comentáriu +&Comentáriu: +Selecional +Deselecional +Máscara: +6600 +Propieais +Estorial de diretórius +Mensahis de dianósticu +Mensahi +7100 +Mi PC +Entorno de red +Documentos +Sistema +7200 +Añiil +Estrayel +Comprebal +Copial +Movel +Eliminal +Inhormazión +7300 +Desapartal ficheru +&Desapartal a: +Desapartal en &balandronis, 'bytes': +Desapartandu... +Confirmal desapartamientu +De siguru que quieri desapartal el ficheru'n {0} volúmenis? +El tamañu lus volúmenis debi sel mas caqueru que'l del ficheru orihinal +Tamañu el volumin nu váliu +Introuza'l tamañu el volumin: {0} 'bytes'.\nDe siguru que quieri desapartal l'archivu'n tantus volúmenis? +7400 +Uñil ficherus +&Uñil en: +Uñiendu... +Selecional sólu'l primel ficheru +Nu s'á puíu detetal el ficheru comu parti d'un ficheru desapartau +Nu s'á puíu alcuentral más d'un balandrón del ficheru desapartau +7500 +Calculandu la suma e verificación... +Inhormazión de suma e verificación +Suma e verificación de 'CRC' pala inhormazión: +Suma e verificación de 'CRC' pala inhormazión i lus nombris: +7600 +Prebas ('benchmark') +Usu e memoria: +Comprimiendu +Comprimiendu +Razón +Razón total +Atual +Resurtau +Usu e CPU +Razón / Usu +Pasis: diff --git a/Utils/7-Zip/Lang/fa.txt b/Utils/7-Zip/Lang/fa.txt new file mode 100644 index 000000000..c524ed468 --- /dev/null +++ b/Utils/7-Zip/Lang/fa.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 3.12 : Edris Naderan +; 4.53 : Mehdi Farhadi +; 9.22 : Hessam Mohamadi +; +; +; +; +; +; +; +; +0 +7-Zip +Farsi +ÙØ§Ø±Ø³ÛŒ +401 +تایید +لغو + + + +بله&Ù‡ +&خیر +بستن +راهنما + +ادامه +440 +بله روی همه +نه روی همه +توق٠+ریستارت +پس زمینه +پیش زمینه +وقÙÙ‡ +متوق٠+آیا میخواهید عملکرد را لغو میکنید؟ +500 +ÙØ§ÛŒÙ„ +ویرایش +مشاهده +موردعلاقه ها +ابزارها +راهنما +540 +بازکردن +بازکردن از داخل +بازکردن از خارج +مشاهده +ویرایش +تغییرنام +Ú©Ù¾ÛŒ به... +انتقال به... +حذ٠+تقسیم ÙØ§ÛŒÙ„... +ادغام ÙØ§ÛŒÙ„.. +مشخصات +توضيح.. +محاسبه مجموع مقابله ای +مقایسه گر +ایجاد پوشه +ایجاد ÙØ§ÛŒÙ„ +خروج +600 +انتخاب همه +لغو انتخاب‌ همه +انتخاب معکوس +انتخاب... +لغو انتخاب... +انتخاب بر حسب نوع +لغو انتخاب بر حسب نوع +700 +آیکون‌ بزرگ +آیکون‌ Ú©ÙˆÚ†Ú© +لیست +جزئیات +730 +نامرتب +مشاهده یکنواخت +2 پانل +نوارابزارها +بازکردن ریشه پوشه +یک مرحله به بالا +تاریخچه پوشه ها.. +تازه کردن +750 +نوارابزار آرشیو +نوارابزار استاندارد +دکمه بزرگ +نمایش متن دکمه ها +800 +Ø§ÙØ²ÙˆØ¯Ù† پوشه به مورد علاقه بعنوان +بوکمارک +900 +گزینه ها.. +سنجش +960 +محتویات.. +درباره برنامه.. +1003 +مسیر +نام +پسوند +پوشه +حجم +حجم ÙØ´Ø±Ø¯Ù‡ شده +ØµÙØ§Øª +ساخته شده +دستیابی شده +تغییر ÛŒØ§ÙØªÙ‡ +یکدست +توضیح دار +رمزگذاری شده +تقسیم قبل از +تقسیم بعد از +لغت نامه +سی آر سی +نوع +Anti +روش +سیستم میزبان +ÙØ§ÛŒÙ„ سیستمی +کاربر +گروه +بلوک +توضيح‌ +موقعیت +مسیر پیشوند +پوشه ها +ÙØ§ÛŒÙ„ ها +نسخه +بخش +چند بخشی +Ø¢ÙØ³Øª +لینک +بلوک ها +بخش ها + +64 بیت +Big-endian +پردازنده +حجم Ùیزیکی +حجم سرساز ها +مجموع مقابله ای +خصوصیات +آدرس مجازی +شناسه +نام کوتاه +سازنده برنامه +حجم قطاع +حالت +لینک +خطا +مجموع حجم +ÙØ¶Ø§ÛŒ خالی +حجم کلاستر +برچسب +نام محلی +ارائه دهنده +2100 +گزینه ها +زبان +زبان: +ویرایشگر +ویرایشگر: +مقایسه گر: +2200 +سیستم +وابسته سازی برنامه با: +2301 +Ø§ÙØ²ÙˆØ¯Ù† منوی برنامه به منوی ویندوز +منوها به صورت آبشاری در یک منو +آیتم های منوی برنامه: +2320 +<پوشه> +<آرشیو> +بازکردن آرشیو +استخراج ÙØ§ÛŒÙ„‌ها... +Ø§ÙØ²ÙˆØ¯Ù† به آرشیو... +تست آرشیو +استخراج در اینجا +استخراج به {0} +Ø§ÙØ²ÙˆØ¯Ù† به {0} +ÙØ´Ø±Ø¯Ù‡ سازی Ùˆ ارسال با ایمیل... +ÙØ´Ø±Ø¯Ù‡â€ŒØ³Ø§Ø²ÛŒ در {0} Ùˆ ارسال با ایمیل +2400 +پوشه ها +پوشه در حال کار +پوشه موقت سیستم +ÙØ¹Ù„ÛŒ +مشخص کنید: +Ùقط برای درایوهای پرتابل Ø§Ø³ØªÙØ§Ø¯Ù‡ شود +یک مکان برای ÙØ§ÛŒÙ„ های موقتی آرشیو مشخص کنید +2500 +تنظیمات +نمایش آیتم ".." +نمایش آیکون واقعی ÙØ§ÛŒÙ„ +نمایش منوی سیستم +انتخاب سطر به طور کامل +نمایش خطوط توری +تک کلیک برای بازکردن یک آیتم +حالت انتخاب جايگزين +Ø§Ø³ØªÙØ§Ø¯Ù‡ از ØµÙØ­Ø§Øª Ø­Ø§ÙØ¸Ù‡ حجیم +2900 +درباره برنامه +برنامه‌ای Ú©Ù‡ در پیش رو دارید یک برنامه رایگان است، اما شما Ù…ÛŒ توانید با پرداخت مبلغی جزئی به توسعه این Ù†Ø±Ù…â€ŒØ§ÙØ²Ø§Ø± Ú©Ù…Ú© کنید. +3000 +سیستم مقدار Ø­Ø§ÙØ¸Ù‡ موردنیاز را نمیتواند اختصاص دهد +خطایی وجود ندارد +{0} آیتم انتخاب شده +ایجاد پوشه '{0}' ممکن نیست +عملکرد بروزرسانی برای این آرشیو بروزرسانی نمیشود +ÙØ§ÛŒÙ„ '{0}' را بعنوان یک آرشیو نمیتوان باز کرد +آرشیو رمزگذاری شده '{0}' را نمیتوان باز کرد.رمزعبور اشتباه است؟ +نوع آرشیو پشتیبانی نشده +ÙØ§ÛŒÙ„ {0} از قبل موجود است +ÙØ§ÛŒÙ„ '{0}' تغییر کرده است.\n آیا میخواهید این ÙØ§ÛŒÙ„ در آرشیو بروزرسانی شود؟ +این ÙØ§ÛŒÙ„ قابل بروزرسانی نمی‌باشد\n'{0}' +ویرایشگر را نمیتوان اجرا کرد +ÙØ§ÛŒÙ„ شبیه ویروس است (نام ÙØ§ÛŒÙ„ حاوی ÙØ¶Ø§ÛŒ بلندی در نام هست). +عملکرد نمیتواند از پوشه ای Ú©Ù‡ مسیر بلند دارد ÙØ±Ø§Ø®ÙˆØ§Ù†ÛŒ شود +شما باید یک ÙØ§ÛŒÙ„ انتخاب کنید +شما باید یک یا تعداد بیشتری ÙØ§ÛŒÙ„ انتخاب کنید +تعداد آیتم ها بسیار زیاد است +3300 +در حال استخراج +ÙØ´Ø±Ø¯Ù‡ سازی +تست +درحال بازکردن.. +در حال اسکن.. +3400 +استخراج +استخراج به: +یک مکان برای ÙØ§ÛŒÙ„ های استخراج شده تعیین کنید +3410 +نوع مسیر +نام مسیر کامل +بدون نام مسیر +3420 +حالت جایگزینی +پرسش قبل از جایگزینی +جایگزینی بدون اخطار +چشمپوشی ÙØ§ÛŒÙ„‌های موجود +تغییرنام خودکار +تغییرنام خودکار ÙØ§ÛŒÙ„ های موجود +3500 +تایید جایگزینی ÙØ§ÛŒÙ„ +پوشه مقصد از قبل حاوی ÙØ§ÛŒÙ„ در حال پردازش هست +آیا میخواهید ÙØ§ÛŒÙ„ موجود با +این یکی جایگزین شود؟ +{0} بایت +تغییرنام خودکار +3700 +روش ÙØ´Ø±Ø¯Ù‡ سازی برای '{0}' پشتیبانی نشذه است +خطای داده در '{0}'. اين ÙØ§ÙŠÙ„ شکسته هست +سی آر سی در '{0}' موÙÙ‚ نشد.ÙØ§ÛŒÙ„ شکسته هست +خطای داده در ÙØ§ÙŠÙ„ رمزگذارى شده '{0}'. رمزعبور اشتباه است؟ +سی آر سی در ÙØ§ÛŒÙ„ رمزگذاری شده '{0}' موÙÙ‚ نشد.رمزعبور اشتباه است +3800 +رمزعبور +رمزعبور: +تكرار رمزعبور: +نمایش رمزعبور +رمزعبورها یکسان نیست +Ùقط از حرو٠انگليسى، اعداد Ùˆ علامت‌هاى خاص (!ØŒ #ØŒ $ØŒ ...) در رمزعبور Ø§Ø³ØªÙØ§Ø¯Ù‡ كنيد +رمزعبور خیلی بلند است +رمزعبور +3900 +زمان سپری شده: +زمان باقیمانده: +مجموع حجم: +سرعت: +پردازش شده: +نسبت ÙØ´Ø±Ø¯Ù‡ سازی: +خطاها: +آرشیو‌: +4000 +Ø§ÙØ²ÙˆØ¯Ù† به آرشیو +آرشیو: +حالت بروزرسانی: +ÙØ±Ù…ت آرشیو: +ميزان ÙØ´Ø±Ø¯Ù‡â€ŒØ³Ø§Ø²ÛŒ: +روش ÙØ´Ø±Ø¯Ù‡ سازی: +حجم لغت نامه: +اندازه لغت: +حجم بلوک یکدست: +تعداد هسته پردازنده +پارامترها: +گزینه ها +ایجاد ارشیو خوداستخراجگر +ÙØ´Ø±Ø¯Ù‡ سازی ÙØ§ÛŒÙ„ های مشترک درسایر برنامه ها +رمزگذاری +روش رمزگذاری: +رمزگذاری نام ÙØ§ÛŒÙ„ ها +Ø­Ø§ÙØ¸Ù‡ مصرÙÛŒ برای ÙØ´Ø±Ø¯Ù‡ سازی: +Ø­Ø§ÙØ¸Ù‡ مصرÙÛŒ برای استخراج: +4050 +ذخیره +سریعترین +سریع +عادی +حداکثر +ماÙوق +4060 +Ø§ÙØ²ÙˆØ¯Ù† Ùˆ جایگزینی ÙØ§ÛŒÙ„ ها +بروزرسانی Ùˆ Ø§ÙØ²ÙˆØ¯Ù† ÙØ§ÛŒÙ„ ها +تازه کردن ÙØ§ÛŒÙ„ های موجود +همگامسازی ÙØ§ÛŒÙ„ ها +4070 +جستجو +همه ÙØ§ÛŒÙ„ ها +غیر-یکدست +یکدست +6000 +Ú©Ù¾ÛŒ +انتقال +Ú©Ù¾ÛŒ به: +انتقال به: +در حال Ú©Ù¾ÛŒ... +در حال انتقال... +تغییرنام... +پوشه مقصد را انتخاب كنيد +عملکرد برای این پوشه پشتیبانی نمیشود +خطای تغییر نام ÙØ§ÛŒÙ„ یا پوشه +تایید Ú©Ù¾ÛŒ ÙØ§ÛŒÙ„ +آیا میخواهید ÙØ§ÛŒÙ„ ها را به ارشیو Ú©Ù¾ÛŒ کنید +6100 +تایید Ø­Ø°Ù ÙØ§ÛŒÙ„ +تایید حذ٠پوشه +تایید Ø­Ø°Ù ÙØ§ÛŒÙ„ ها +آیا میخواهید '{0}' را حذ٠کنید؟ +آیا میخواهید پوشه '{0}' Ùˆ همه محتویات آن را حذ٠کنید؟ +آیا میخواهید این {0} آیتم را حذ٠کنید؟ +در حال حذÙ... +خطای Ø­Ø°Ù ÙØ§ÛŒÙ„ یا پوشه +سیستم ÙØ§ÛŒÙ„ با مسیر بلند را نمیتواند به سطل زباله انتقال دهد +6300 +ایجاد پوشه +ایجاد ÙØ§ÛŒÙ„ +نام پوشه: +نام ÙØ§ÛŒÙ„: +پوشه جدید +ÙØ§ÛŒÙ„ جدید +خطای ایجاد پوشه +خطای ایجاد ÙØ§ÛŒÙ„ +6400 +توضيح +توضيح: +انتخاب +لغو انتخاب +ماسك: +6600 +مشخصات +تاریخچه پوشه‌ها +پیغام‌های تشخيصی +پیام +7100 +رایانه +شبکه +اسناد +سیستم +7200 +Ø§ÙØ²ÙˆØ¯Ù† +استخراج +تست +Ú©Ù¾ÛŒ +انتقال +حذ٠+اطلاعات +7300 +تقسیم ÙØ§ÛŒÙ„ +تقسیم در: +تقسیم به چندین بخش،برحسب بایت: +در حال تقسیم... +تاييد تقسیم ÙØ§ÛŒÙ„ +آیا میخواهید اين ÙØ§ÙŠÙ„ را به {0} بخش تقسيم كنيد؟ +حجم بخش ها باید کوچکتر از اندازه ÙØ§ÛŒÙ„ اصلی باشد +حجم بخش نادرست است +حجم بخش های مشخص شده: {0} بايت.\nآیا میخواهید آرشيو را به بخش هایی به اين اندازه تقسيم كنيد؟ +7400 +ادغام ÙØ§ÛŒÙ„ ها +ادغام در: +در حال ادغام... +Ùقط اولین بخش ÙØ§ÛŒÙ„ را انتخاب کنید +ÙØ§ÛŒÙ„ را نمی تواند بعنوان ÙØ§ÛŒÙ„ تقسیمی تشخیص دهد +بیشتر از یک بخش از ÙØ§ÛŒÙ„ تقسیمی را نمیتواند پیدا کند +7500 +محاسبه مجموع مقابله ای.. +اطلاعات مجموع مقابله ای +مجموع مقابله ای سی آر سی برای داده: +مجموع مقابله ای سی آر سی برای داده Ùˆ نام: +7600 +سنجش +Ø­Ø§ÙØ¸Ù‡ مصرÙÛŒ: +ÙØ´Ø±Ø¯Ù‡ سازی +استخراج +رتبه بندی +رتبه بندی Ú©Ù„ÛŒ: +ÙØ¹Ù„ÛŒ +دستاورد +مصر٠پردازنده +رتبه بندی / مصر٠+تعداد عبور: diff --git a/Utils/7-Zip/Lang/fi.txt b/Utils/7-Zip/Lang/fi.txt new file mode 100644 index 000000000..c40152594 --- /dev/null +++ b/Utils/7-Zip/Lang/fi.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 3.08 : Ari Ryynanen +; 4.30 : Jarko P. +; 4.42 : Juhani Valtasalmi +; 9.35b : T.Sakkara +; 15.05 : 2015-08-07 : Lauri Kenttä +; +; +; +; +; +; +0 +7-Zip +Finnish +Suomi +401 +OK +Peruuta + + + +&Kyllä +&Ei +&Sulje +Ohje + +&Jatka +440 +Kyllä k&aikkiin +E&i kaikkiin +Pysäytä +Aloita uudelleen +&Tausta-ajona +&Normaali prioriteetti +&Pysäytä +Pysäytetty +Keskeytetäänkö toiminto? +500 +&Tiedosto +&Muokkaa +&Näytä +&Suosikit +Työ&kalut +&Ohje +540 +&Avaa +Avaa s&isäisesti +Avaa ulkoisesti +&Näytä +&Muokkaa +Nimeä &uudelleen +&Kopioi... +&Siirrä... +&Poista +&Jaa osiin... +&Yhdistä jaetut... +&Ominaisuudet +Ku&vaus +Laske tarkiste +Erot +Luo kansio +Luo tiedosto +&Lopeta +Linkitä +Vaihtoehtoiset tietovirrat +600 +V&alitse kaikki +Poista &valinnat +&Käänteinen valinta +Valitse... +Poista valinta... +Valitse lajeittain +Poista valinta lajeittain +700 +Suu&ret kuvakkeet +&Pienet kuvakkeet +&Luettelo +&Tiedot +730 +Alkuperäinen järjestys +Kansioiden sisältö luetteloituna +&Kaksi panelia +&Työkalupalkki +Avaa pääkansio +Avaa yläkansio +Kansiohistoria... +P&äivitä +Automaattipäivitys +750 +Pakettipalkki +Vakiopalkki +Suuret painikkeet +Näytä painiketekstit +800 +&Lisää kansio Suosikkeihin +Kirjanmerkki +900 +&Asetukset... +&Nopeustesti +960 +&Sisältö... +&Tietoja ohjelmasta... +1003 +Polku +Nimi +Pääte +Kansio +Koko +Pakattu koko +Määritteet +Luotu +Käytetty +Muokattu +Kiinteä +Kuvailtu +Suojaus +Jaa ennen +Jaa jälkeen +Sanakirja +Tarkiste +Laji +Vastakohta +Menetelmä +Isäntäjärjestelmä +Tiedostojärjestelmä +Käyttäjä +Ryhmä +Lohkoja +Kuvaus +Kohta +Polun etuliite +Kansioita +Tiedostoja +Versio +Nimi +Jaettu osiin +Siirtymä +Linkit +Lohkot +Nimet + +64-bittinen +Big-endian +Suoritin +Fyysinen koko +Otsikon koko +Tarkiste +Ominaisuudet +Näennäisosoite +ID +Lyhyt nimi +Luontisovellus +Sektorikoko +Tila +Symbolinen linkki +Virhe +Aseman koko +Vapaata tilaa +Varausyksikön koko +Nimi +Paikallinen nimi +Palveluntarjoja +NT-suojaukset +Vaihtoehtoinen tietovirta +Muu +Poistettu +On kansiorakenne + + +Virhelaji +Virheet +Virheet +Varoitukset +Varoitus +Tietovirrat +Vaihtoehtoiset tietovirrat +Vaihtoehtoisten tietovirtojen koko +Näennäiskoko +Pakkaamaton koko +Fyysinen koko +Aseman indeksi +Alilaji +Kuvaus +Koodisivu + + + +Loppupään koko +Upotetun segmentin koko +Linkki +Kiinteä linkki +iNode + +Vain luku +2100 +Asetukset +Kieli +Kieli: +Muokkausohjelmat +&Tekstieditori: +&Erojen etsintä: +2200 +Järjestelmäliitännät +Liitä 7-Zip seuraaviin tiedostotyypeihin: +Kaikki käyttäjät +2301 +Liitä 7-Zip järjestelmän valikoihin. +Ryhmitetty järjestelmävalikko +Järjestelmävalikon sisältö: +Näytä järjestelmävalikon kuvakkeet +2320 + + +Avaa +Pura... +Lisää pakettiin... +Eheystarkastus +Pura tänne +Pura kansioon {0} +Lisää pakettiin {0} +Pakkaa tiedostot ja lähetä... +Lisää pakettiin {0} ja lähetä +2400 +Kansiot +&Työkansio +&Järjestelmän väliaikaiskansio +&Nykyinen kansio +&Valittu kansio: +Käytä vain siirrettäville tietovälineille +Määritä väliaikaistiedostojen sijainti. +2500 +Tiedostoikkuna +Näytä &yläkansion symboli +Näytä &aidot tiedostokuvakkeet +Näytä &järjestelmävalikko +&Valitse koko rivi +Näytä &ruudukko +Avaa &kertanapsautuksella +Vaihtoehtoinen valintojen &esitystapa +Käytä &suuria muistisivuja +2900 +Tietoja +7-Zip on ilmaisohjelmisto. +3000 +Ei riittävästi muistia toimintoa varten +Ei virheitä. +{0} tiedosto(a) valittu +Ei voida luoda kansioita '{0}.' +Tätä pakettia ei voi päivittää. +Tiedoston '{0}' pakkausta ei voi purkaa. +Ei voi avata suojattua pakettia '{0}'. Väärä salasana? +Pakkaustapaa ei tueta +Tiedosto {0} on jo olemassa +Tiedostoa '{0}' on muutettu.\nHaluatko päivittää paketin? +Ei voida päivittää tiedostoa \n'{0}'. +Editoria ei voida käynnistää. +Tiedosto vaikuttaa haittaohjelmalta, sillä sen nimi sisältää peräkkäisiä välilyöntejä. +Toimintoa ei voi suorittaa kansiosta, jonka nimi on pitkä. +Valitse yksi tiedosto +Valitse vähintään yksi tiedosto +Liian monta kohdetta. +Ei voida avata tiedostoa {0}-pakettina +Tiedosto on avattu {0}-pakettina +Paketti on auki eri alkukohdasta (offset) +3300 +Puretaan paketti +Lisätään pakettiin +Eheystarkistus +Avataan... +Etsitään... +Poistetaan +3320 +Lisätään +Päivitetään +Analysoidaan +Kopioidaan +Pakataan uudestaan +Ohitetaan +Poistetaan +Luodaan otsikkoa +3400 +Pura +&Pura kansioon +Määritä puretuille tiedostoille sijainti. +3410 +Polut: +Täydet polut +Ei polkuja (vain nimi) +Absoluuttiset polut +Suhteelliset polut +3420 +Samannimiset tiedostot: +Kysy tapauskohtaisesti +Korvaa +Ohita +Uudelleennimeä automaattisesti +Uudelleennimeä entiset automaattisesti +3430 +Estä pääkansion kahdennus +Palauta tiedostojen suojaukset +3500 +Vahvista tiedoston korvaus +Kansiossa on jo samanniminen tiedosto. +Korvataanko aiempi tiedosto +tällä tiedostolla? +{0} tavua +&Uudelleennimeä automaattisesti +3700 +Tiedoston '{0}' pakkaustapaa ei tueta. +Sisältövirhe tiedostossa '{0}'. Tiedosto on viottunut. +Tiedoston '{0}' eheystarkistus epäonnistui. Tiedosto on vioittunut. +Virhe avattaessa suojattua tiedostoa '{0}'. Väärä salasana? +Tarkistevirhe avattaessa suojattua tiedostoa '{0}'. Väärä salasana? +3710 +Väärä salasana? +3721 +Pakkaustapaa ei tueta +Sisältövirhe +Tarkistevirhe +Sisältö ei ole käytettävissä +Sisällön ennenaikainen loppuminen +Varsinaisen tietosisällön jälkeen on ylimääräistä sisältöä +Tiedosto ei ole pakattu +Otsikkovirhe +Väärä salasana +3763 +Pakkauksen alku ei käytettävissä +Pakkauksen alkua ei vahvistettu + + + +Toiminto ei tuettu +3800 +Syötä salasana +Syötä salasana: +Toista salasana: +Näytä &salasana +Salasanat eivät täsmää +Salasanassa voi käyttää numeroita ja erikoismerkkejä, mutta EI skandinaavisia kirjaimia. +Liian pitkä salasana +Salasana +3900 +Aikaa kulunut: +Aikaa jäljellä: +Koko: +Nopeus: +Käsitelty: +Pakkaussuhde: +Virheitä: +Paketteja: +4000 +Lisää pakettiin +&Paketti: +&Päivitystapa: +Pakkaus&: +Pakkauksen &taso: +Pakkaus&menetelmä: +&Sanakirjan koko: +&Sanan koko: +Lohkokoko +Prosessorisäikeet: +&Parametrit: +Lisäasetukset +Luo itsepurkautuva paketti +Pakkaa yhteiset tiedostot +Suojaus +Salausalgoritmi +Tiedosto&nimien suojaus +Pakkaamiseen käytettävä muisti: +Purkamiseen käytettävä muisti: +Poista pakkauksen jälkeen +4040 +Tallenna symboliset linkit +Tallenna kiinteät linkit +Tallenna vaihtoehtoiset tietovirrat +Tallenna tiedostojen suojaukset +4050 +Ei pakkausta +Nopein +Nopea +Normaali +Maksimi +Ultra +4060 +Lisää ja korvaa +Päivitä ja lisää +Päivitä +Synkronoi +4070 +Selaa +Kaikki tiedostot +Muuttuva +Kiinteä +6000 +Kopioi +Siirrä +Kopioi kansioon: +Siirrä kansioon: +Kopioidaan... +Siirretään... +Nimetään uudelleen... +Valitse kohdekansio. +Toiminto ei ole tuettu. +Virhe uudelleennimettäessä tiedostoa tai kansiota +Vahvista tiedoston kopioiminen +Kopioidaanko tiedostot pakettiin +6100 +Vahvista tiedoston poisto +Vahvista kansion poisto +Vahvista useiden kohteiden poisto +Poistetaanko tiedosto '{0}'? +Poistetaanko kansio '{0}' ja kaikki sen sisältö? +Poistetaanko nämä {0} kohdetta? +Poistetaan... +Virhe poistettaessa tiedostoa tai kansiota +Roskakoriin ei voida siirtää tiedostoa, jolla on pitkä nimi +6300 +Luo uusi kansio +Luo uusi tiedosto +Kansion nimi: +Tiedostonimi: +Uusi kansio +Uusi tiedosto +Virhe luotaessa kansiota +Virhe luotaessa tiedostoa +6400 +Kuvaus +&Kuvaus: +Valitse +Poista valinta +Maski: +6600 +Ominaisuudet +Kansiohistoria +Tietoja +Viesti +7100 +Tietokone +Verkko +Dokumentit +Järjestelmä +7200 +Lisää +Pura +Eheystarkistus +Kopioi +Siirrä +Poista +Ominaisuudet +7300 +Jaa tiedosto osiin +&Jaa kansioon: +&Osien koko jaettaessa: +Jaetaan osiin... +Vahvista jakaminen. +Jaetaanko tiedosto {0} osaan? +Jaetun osan koon pitää olla alkuperäistä pienempi. +Jaetun osan koko ei kelpaa +Jaettavien osien koko: {0} tavua.\nJaetaanko tiedosto sen kokoisiin osiin? +7400 +Yhdistä jaetut tiedostot +&Yhdistä kansioon: +Yhdistetään... +Valitse jaetusta tiedostosta vain ensimmäinen osa +Tiedosto ei ole jaetun tiedoston osa +Jaetun tiedoston osista löydettiin vain yksi +7500 +Lasketaan tarkiste... +Tarkisteet +Sisällön tarkiste: +Sisällön ja tiedostonimien tarkiste: +7600 +Nopeustesti +Muistin käyttö: +Pakkaaminen +Purkaminen +Luokitus +Kokonaisluokitus +Nykyinen +Tulos +Suoritinkäyttö +Luokitus / käyttö +Läpäisty: +7700 +Linkitys +Linkitä +Linkitys kohteesta: +Linkitys kohteeseen: +7710 +Linkkilaji +Kiinteä linkki +Symbolinen tiedostolinkki +Symbolinen kansiolinkki +Kansioliitos diff --git a/Utils/7-Zip/Lang/fr.txt b/Utils/7-Zip/Lang/fr.txt new file mode 100644 index 000000000..fa661c7a0 --- /dev/null +++ b/Utils/7-Zip/Lang/fr.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 4.07 : Nicolas Sierro +; 9.07 : Philippe Berthault +; 15.14 : Sylvain St-Amand (SSTSylvain) +; +; +; +; +; +; +; +; +0 +7-Zip +French +Français +401 +OK +Annuler + + + +&Oui +&Non +&Fermer +Aide + +&Continuer +440 +Oui pour &Tous +Non pour T&ous +Arrêter +Redémarrer +&Arrière-plan +P&remier plan +&Pause +En pause +Êtes-vous sur de vouloir annuler ? +500 +&Fichier +&Édition +Affic&hage +Fa&voris +&Outils +&Aide +540 +&Ouvrir +Ouvrir à l'&intérieur +Ouvrir à l'e&xtérieur +&Voir +&Édition +Reno&mmer +&Copier vers... +&Déplacer vers... +S&upprimer +Diviser le &fichier... +Combiner les fic&hiers... +P&ropriétés +Comme&ntaire... +Somme de contrôle +Diff +Créer un dossier +Créer un fichier +&Quitter +Lien +Flux &Alternatif +600 +Sélectionner &Tout +Désélectionner Tout +&Inverser la Sélection +Sélectionner... +Désélectionner... +Sélectionner par Sorte +Désélectionner par Sorte +700 +&Grandes Icônes +&Petites Icônes +&Liste +&Détails +730 +Non trié +Vue à plat +&2 Fenêtres +&Barres d'outils +Ouvrir le dossier racine +Dossier parent +Historique des dossiers... +Actualis&er +Actualiser Automatiquement +750 +Barre d'Archive +Barre Standard +Grands Boutons +Montrer le texte des Boutons +800 +&Ajouter le répertoire aux Favoris +Signet +900 +&Options... +&Test de performance +960 +&Contenu... +À &propos de 7-Zip... +1003 +Chemin +Nom +Extension +Dossier +Taille +Compressé +Attributs +Créé le +Accédé le +Modifié le +Solide +Commentaire +Chiffrer +Diviser Avant +Diviser Après +Dictionnaire + +Sorte +Anti +Méthode +OS hôte +Système de Fichiers +Utilisateur +Groupe +Bloc +Commentaire +Position +Préfixe +Dossiers +Fichiers +Version +Volume +Multivolume +Offset +Liens +Blocs +Volumes + +64 bits +Big-endian +CPU +Taille physique +Taille des en-têtes +Somme de contrôle +Caractéristiques +Adresse virtuelle +ID +Nom court +Application créatrice +Taille de secteur +Mode +Lien Symbolique +Erreur +Taille Totale +Espace Libre +Taille des clusters +Nom de volume +Nom local +Fournisseur +Sécurité NT +Flux alternatif +Aux +Effacer +En Arbre + + +Type d'erreur +Erreurs +Erreurs +Avertissements +Avertissement +Flux +Flux Alterné +Grosseur du Flux Alterné +Grosseur Virtuel +Grosseur Décompressé +Grosseur Physique Totale +Indexe du Volume +Sous-Type +Commentaire Bref +Code Page + + + +Grosseur Tail +Grosseur Embedded Stub +Lien +Lien Solide +iNode + +Lecture-seulement +2100 +Options +Langue +Langue : +Éditeur +&Éditeur : +&Diff : +2200 +Système +Associer avec 7-Zip : +Tous les utilisateurs +2301 +Intégrer 7-Zip au menu contextuel +Menu contextuel en cascade +Éléments du menu contextuel : +Icônes dans le menu contextuel +2320 + + +Ouvrir archive +Extraire les fichiers... +Ajouter à l'archive... +Contrôler l'archive +Extraire Ici +Extraire vers {0} +Ajouter à {0} +Compresser et envoyer par courriel... +Compresser vers {0} et envoyer par courriel +2400 +Dossiers +Dossier de &travail +Dossier temporaire du &système +&Courant +S&pécifié : +N'utiliser que pour les médias amovibles +Spécifiez un dossier pour les fichiers d'archive temporaires. +2500 +Paramètres +Afficher l'élément ".." +Afficher les icônes réelles des fichiers +Afficher le menu système +&Sélectionner toute la ligne +Afficher la &grille +Simple clic pour ouvrir un item +Utiliser la sélection &alternative +Utiliser des &grosses pages mémoire +2900 +A propos de 7-Zip +7-Zip est un logiciel libre +3000 +Le système ne peut allouer la quantité de mémoire nécessaire +Il n'y a pas d'erreurs +{0} objet(s) sélectionné(s) +Le dossier '{0}' ne peut pas être créé +Les opérations de mise à jour ne sont pas disponibles pour cette archive. +Le fichier '{0}' ne peut être ouvert comme une archive +L'archive cryptée '{0}' ne peut être ouverte. Mauvais mot de passe ? +Ce type d'archive n'est pas supporté +Le fichier {0} existe déjà +Le fichier '{0}' a été modifié.\nVoulez-vous le mettre à jour dans l'archive ? +Impossible de mettre à jour\n'{0}' +Impossible de démarrer l'éditeur. +Le fichier est peut-être un virus (le nom contient des grands espacements pour masquer l'extension). +Cette opération ne peut être effectuée depuis un dossier ayant un trop long chemin d'accès. +Vous devez sélectionner un fichier +Vous devez sélectionner un ou plusieurs fichiers +Trop d'objets +Ne peut ouvrir les fichiers {0} comme une archive +Le fichier {0} est ouvert comme une archive +L'archive est ouverte avec un décalage +3300 +Extraction +Compression +Contrôle +Ouverture... +Exploration... +Enlever +3320 +Ajoute +Modifie +Analyse +Réplique +Remballage +Passe +Efface +Création d'un en-tête +3400 +Extraire +E&xtraire vers : +Choisissez un dossier pour l'extraction des fichiers. +3410 +Mode de chemin : +Nom de chemin complet +Pas de nom de chemin +Nom de chemin absolu +Nom de chemin relatif +3420 +Mode de remplacement : +Confirmer avant de remplacer +Remplacer sans demander +Ignorer les fichiers existants +Renommer automatiquement +Renommer les fichiers existants +3430 +Élimine la duplication du répertoire racine +Restauration du fichier de sécurité +3500 +Confirmer le remplacement de fichier +Le dossier de destination contient déjà un fichier avec ce nom. +Voulez-vous remplacer le fichier existant +par celui-ci ? +{0} octets +Renommer &automatiquement +3700 +Méthode de compression non valide pour '{0}'. +Donnée erronée dans le fichier '{0}'. Le fichier est corrompu. +Échec du contrôle CRC dans le fichier '{0}'. Le fichier est corrompu. +Donnée erronée dans le fichier crypté '{0}'. Mauvais mot de passe ? +Échec du contrôle CRC dans le fichier crypté '{0}'. Mauvais mot de passe ? +3710 +Pas le bon mot de passe ? +3721 +Méthode de compression non supportée +Erreur de donnée +Échec de CRC +Donnée non disponible +Fin de données inattendues +Il y a des données après la fin des données utiles +N'est pas une archive +Erreur en-têtes +Mauvais mot de passe +3763 +Démarrage non disponible d'une archive +Démarrage non confirmé d'une archive + + + +Option non supportée +3800 +Entrez le mot de passe +Entrez le mot de passe : +Entrez le mot de passe à nouveau : +&Afficher le mot de passe +Les mots de passe ne correspondent pas +Pour le mot de passe, n'utilisez que des lettres non accentuées, des chiffres et des caractères spéciaux (!, #, $, ...) +Le mot de passe est trop long +Mot de passe +3900 +Temps écoulé : +Temps restant : +Taille totale : +Vitesse : +Traité : +Taux de compression : +Erreurs : +Archive : +4000 +Ajouter à l'archive +&Archive : +&Mode de mise à jour : +&Format de l'archive : +N&iveau de compression : +Méthode de &compression : +&Taille du dictionnaire : +Tai&lle des mots : +Taille de bloc solide : +Nombre de threads CPU : +&Paramètres : +Options +Créer une archive SF&X +Compresser des fichiers partagés +Chiffrement +Méthode de chiffrement : +Chiffre les &noms de fichiers +Mémoire pour la compression : +Mémoire pour la décompression : +Effacer les fichiers après compression +4040 +Emmagasine liens symboliques +Emmagasine liens solides +Emmagasine flux de données alternatifs +Emmagasine fichier de sécurité +4050 +Aucune +Le plus rapide +Rapide +Normale +Maximum +Ultra +4060 +Ajouter et remplacer les fichiers +Mettre à jour et ajouter les fichiers +Rafraîchir les fichiers existants +Synchroniser les fichiers +4070 +Choisir +Tous les fichiers +Non-solide +Solide +6000 +Copier +Déplacer +Copier dans : +Déplacer vers : +Copie... +Déplacement... +Renommage... +Sélectionnez le dossier de destination. +L'opération n'est pas possible pour ce dossier. +Erreur durant le Renommage du Fichier ou du Dossier +Confirmation de la Copie de(s) Fichier(s) +Confirmer la copie de(s) fichier(s) à archiver +6100 +Confirmer la Suppression du Fichier +Confirmer la Suppression du Dossier +Confirmer la Suppression de Multiple Fichiers +Êtes-vous sûr de vouloir supprimer '{0}' ? +Êtes-vous sûr de vouloir supprimer le dossier '{0}' et tout ce qu'il contient ? +Êtes-vous sûr de vouloir supprimer ces {0} objets ? +Suppression... +Erreur durant la suppression du fichier ou du dossier +Le système ne peut mettre à la Corbeille un fichier avec un trop long chemin d'accès +6300 +Créer un Dossier +Créer un Fichier +Nom du dossier : +Nom du fichier : +Nouveau dossier +Nouveau fichier +Erreur durant la création du dossier +Erreur durant la création du fichier +6400 +Commentaire +&Commentaire : +Sélectionner +Désélectionner +Masquer : +6600 +Propriétés +Historique des dossiers +Messages de diagnostic +Message +7100 +Ordinateur +Réseau +Documents +Système +7200 +Ajouter +Extraire +Tester +Copier +Déplacer +Supprimer +Informations +7300 +Diviser le fichier +&Diviser en : +Diviser en &volumes, octets : +Découper... +Confirmez le découpage +Êtes-vous sûr de vouloir découper le fichier en {0} volumes ? +La taille de volume doit être inférieure à la taille du fichier d'origine +Taille de volume incorrecte +Taille de volume spécifiée : {0} octets.\nÊtes-vous sûr de vouloir découper l'archive dans de tels volumes ? +7400 +Combiner les fichiers +&Combiner en : +Combinaison... +Ne sélectionnez que le premier fichier +Ne trouve aucun fichier faisant partie d'une archive divisée +Ne trouve qu'un seul fichier faisant partie d'une archive divisée +7500 +Calcul de la somme de contrôle... +Informations sur la somme de contrôle +Somme de contrôle des données : +Somme de contrôle des données et des noms : +7600 +Test de performance +Utilisation de la mémoire : +Compression +Décompression +Taux +Taux total +Actuel +Résultant +Utilisation CPU +Estimé / Usage +Passe : +7700 +Lien +Lien +Lien depuis : +Lien ver : +7710 +Sorte de lien +Lien solide +Lien symbolique de fichier +Lien symbolique de répertoire +Jonction de répertoire diff --git a/Utils/7-Zip/Lang/fur.txt b/Utils/7-Zip/Lang/fur.txt new file mode 100644 index 000000000..ef4ae0dbe --- /dev/null +++ b/Utils/7-Zip/Lang/fur.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.60 : Andrea Decorte (Klenje) : http://softfurlan.altervista.org : secont l'ortografie uficiâl de Provincie di Udin +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Friulian +Furlan +401 +Va ben +Scancele + + + +&Sì +&No +&Siare +&Jutori + +&Continue +440 +Sì &a ducj +No a &ducj +Ferme +Torne a inviâ +&Sfont +P&rin plan +&Pause +In pause +Sêstu sigûr di volê scancelâ? +500 +&File +&Modifiche +&Viodude +&Preferîts +&Imprescj +&Jutori +540 +&Viarç +Viarç dentri 7-&Zip +V&iarç fûr di 7-Zip +&Mostre +M&odifiche +Gambie &non +&Copie in... +Mô&f in... +&Elimine +&Divît file... +Torne a &unî files... +P&ropietâts +Comen&t +Calcole so&me di control + +Cree cartele +Cree file +V&a fûr +600 +Selezione d&ut +&Deselezione dut +&Invertìs selezion +Selezione... +Deselezione... +Selezione par gjenar +Deselezione par gjenar +700 +Iconis &grandis +Iconis &piçulis +&Liste +&Detais +730 +Cence ordin +Viodude plane +&2 panei +Sbaris dai impresc&j +Viarç cartele principâl +Parsore di un nivel +Storic des cartelis... +&Atualize +750 +Sbare dai imprescj par l'archivi +Sbare dai imprescj standard +Botons larcs +Mostre test dai botons +800 +&Zonte cartele ai Preferîts sicu +Preferît +900 +&Opzions... +&Banc di prove +960 +&Argoments... +&Informazions su 7-Zip... +1003 +Percors +Non +Estension +Cartele +Dimension +Dimension comprimude +Atribûts +Creât +Ultin acès +Modificât +Solit +Comentât +Cifrât +Divît prin di +Divît daspò di +Dizionari +CRC +Gjenar +Anti +Metodi +SO di origjin +Sisteme dai files +Utent +Grup +Bloc +Coment +Posizion +Prefìs dal troi +Cartelis +Files +Version +Volum +Multivolum +Offset +Leams +Blocs +Volums + +64-bit +Big-endian +CPU +Dimension fisiche +Dimension intestazions +Some di control +Caracteristichis +Direzion virtuâl + + + + + + +Erôr +Dimension totâl +Puest libar +Dimension setôr +Etichete +Non locâl +Furnidôr +2100 +Opzions +Lenghe +Lenghe: +Editôr +&Editôr: + +2200 +Sisteme +Associe 7-Zip cun: +2301 +Integre 7-Zip intal menù contestuâl de shell +Menù contestuâl in discjadude +Elements dal menù contestuâl: +2320 + + +Viarç archivi +Tire fûr files... +Zonte a un archivi... +Prove archivi +Tire fûr ca +Tire fûr in {0} +Zonte a {0} +Comprim e mande par email... +Comprim in {0} e mande par email +2400 +Cartelis +Cartele di &vore +Cartele &provisorie dal sisteme +&Corinte +&Specificade: +Dopre dome pai drives che si puedin gjavâ +Specifiche une posizion pai files provisoris di un archivi. +2500 +Configurazion +Mostre l'element ".." +Mostre lis veris iconis dai files +Mostre il menù dal sisteme +&Selezione la rie intire +Mostre les liniis de &gridele sot + +Mût di selezion &alternatîf +Dopre pagjinis di memorie &largjis +2900 +Informazions su 7-Zip +7-Zip al è un program libar. Purpûr, tu puedis supuartâ il disvilup di 7-Zip cu la regjistrazion. I utents regjistrâts a podaran otignî supuart tecnic. +3000 +Il sisteme nol rive a cjoli la cuantitât di memorie che e covente +Nissun erôr cjatât +{0} ogjet(s) selezionât(s) +No si pues creâ la cartele '{0}' +Lis operazions di atualizazion no son supuartadis par chest archivi. +No si pues viarzi il file '{0}' come archivi +No si pues viarzi l'archivi cifrât '{0}'. Ise sbaliade la peraule clâf? +Gjenar di archivi no supuartât +Il file {0} al esist za +Il file '{0}' al è stât modificât.\nVuelistu atualizâlu intal archivi? +No si pues atualizâ il file\n'{0}' +No si pues inviâ l'editôr. +Il file al samee un virus (il non al à dentri un grum di spazis). +Cheste operazion no pues jessi clamade di une cartele cuntun troi lunc. +Tu scugnis sielzi un file +Tu scugnis sielzi un o plui files +Masse elements +3300 +Daûr a tirâ fûr... +Daûr a comprimi +Daûr a provâ +Daûr a viarzi... +Daûr a scandaiâ... +3400 +Tire fûr +Tir&e fûr in: +Specifiche une posizion pai files tirâts fûr. +3410 +Struture des cartelis +Percors intîrs +Nissun percors +3420 +Sore scriture +Domande prin di scrivi parsore +Scrîf parsore cence domandâ +Salte i files che esistin +Gambie nons in automatic +Gambie nons in automatic se a esistin +3500 +Conferme de sostituzion dal file +Inte cartele di destinazion al è za il file processât. +Vuelistu sostituî il file esistint +cun chest file? +{0} bytes +&Gambie non in automatic +3700 +Il metodi di compression nol è supuartât par '{0}'. +Erôr di dâts in '{0}'. Il file al è corot. +CRC falît in '{0}'. Il file al è corot. +Erôr di dâts tal file cifrât '{0}'. Peraule clâf sbaliade? +CRC falît tal file cifrât '{0}'. Peraule clâf sbaliade? +3800 +Scrîf peraule clâf +Scrîf la peraule clâf: +Torne a inserî la peraule clâf: +&Mostre la peraule clâf +Lis peraulis clâfs no son compagnis +Dopre dome lis letaris inglesis (no acentadis), i numars e i caratars speciâi (!, #, $, ...) inte peraule clâf +La peraule clâf e je masse lungje +Peraule clâf +3900 +Timp passât: +Timp restant: +Dimension: +Sveltece: +Elaborât: +Tas di compression: +Erôrs: +Archivis: +4000 +Zonte a un archivi +&Archivi: +Mût di at&ualizazion: +&Formât archivi: +Nive&l di compression: +&Metodi di compression: +&Dimension dizionari: +Dimension &peraule: +Dimension bloc solit: +Numar di threads de CPU: +&Parametris: +Opzions +Cree archivi SF&X +Comprim i files condividûts +Ciframent +Metodi di ciframent: +Cifre i &nons dai files +Utilizazion memorie comprimint: +Utilizazion memorie decomprimint: +4050 +Cence compression +Il pi svelt +Svelt +Normâl +Massim +Super +4060 +Zonte e sostituìs files +Atualize e zonte files +Atualize i files che esistin +Sincronize i files +4070 +Sgarfe +Ducj i files +No-solit +Solit +6000 +Copie +Môf +Copie in: +Môf in: +Daûr a copiâ... +Daûr a movi... +Daûr a gambiâ non... +Sielç la cartele di destinazion. +L'operazion no je supuartade. +Erôr gambiant non a un file o une cartele +Conferme de copie dai files +Sêstu sigûr di volê copiâ i files tal archivi +6100 +Conferme de eliminazion dal file +Conferme de eliminazion de cartele +Conferme de eliminazion di plui files +Sêstu sigûr di volê eliminâ '{0}'? +Sêstu sigûr di volê eliminâ la cartele '{0}' e dut ce ch'al è lì dentri? +Sêstu sigûr di volê eliminâ chescj {0} elements? +Daûr a eliminâ... +Erôr eliminant un file o une cartele +Il sisteme nol pues movi un file cuntun troi lunc te Scovacere +6300 +Cree cartele +Cree file +Non de cartele: +Non dal file: +Gnove cartele +Gnûf file +Erôr inte creazion de cartele +Erôr inte creazion dal file +6400 +Coment +&Coment: +Selezione +Deselezione +Filtri: +6600 +Propietâts +Storic des cartelis +Messaçs diagnostics +Messaç +7100 +Ordenadôr +Rêt +Documents +Sisteme +7200 +Zonte +Tire fûr +Prove +Copie +Môf +Elimine +Info +7300 +Divît file +&Divît in: +Divît in &volums, grandece in bytes: +Daûr a dividi... +Conferme de division +Sêstu sigûr di volê dividi il file in {0} tocs? +La dimension di un volum e à di jessi plui piçule di chê dal file origjinâl +Dimension dai volums sbaliade +Dimension dai volums volude: {0} bytes.\nSêstu sigûr di volê dividi l'archivi in tocs di cheste dimension? +7400 +Torne a unî files +&Torne a unî in: +Daûr a tornâ a unî... +Sielç dome il prin file +No si pues rilevâ il file come toc di un file dividût +No son stâts cjatâts plui tocs di file dividûts +7500 +Daûr a calcolâ la some di control... +Informazions su la some di control +Some di control CRC pai dâts: +Some di control CRC pai dâts e i nons: +7600 +Banc di prove +Utilizazion memorie: +Comprimint +Decomprimint +Valutazion +Valutazion totâl +Corint +Risultant +Utilizazion CPU +Judizi / Utilizazion +Passaçs: diff --git a/Utils/7-Zip/Lang/fy.txt b/Utils/7-Zip/Lang/fy.txt new file mode 100644 index 000000000..aed26dfb4 --- /dev/null +++ b/Utils/7-Zip/Lang/fy.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.53 : Berend Ytsma +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Frisian +Frysk +401 +Okee +Ofbrekke + + + +&Jawis +&Nee +&Slute +Help + +&Ferfetsje +440 +Jawis foar &Alles +Nee foar A&lles +Stopje +Opnij begjinne +&Eftergrûn +&Foargrûn +&Skoftsje +Skoft +Binne jo wis dat jo ôfbrekke wolle? +500 +&Triem +&Bewurkje +&Byld +B&lêdwizers +&Ark +&Help +540 +&Iepenje +Iepenje &yn +Iepenje bû&ten +&Byld +&Bewurkje +Omne&ame +&Kopiearje nei... +&Ferpleats nei... +&Wiskje +Triemmen &spjalte... +Triemmen Kom&binearje... +E&igenskippen +Komme&ntaar +Kontrôlesom berekenje + +Map meitsje +Triem meitsje +U&tgong +600 +&Alles selektearje +Alles net selektearje +&Seleksje omdraaien +Selektearje... +Net selektearje... +Selektearje neffens type +Net selektearje neffens type +700 +Gru&tte Ikoanen +L&ytse Ikoanen +&List +&Details +730 +Net Sortearre +Platte werjefte +&2 Panielen +&Arkbalke +Haadmap iepenje +Ien nivo omheech +Maphistoarje... +&Ferfarskje +750 +Argyf arkbalke +Standert arkbalke +Grutte knoppen +Knoptekst sjen litte +800 +Map oan blêdwizers &taheakje as +Blêdwizer meitsje +900 +&Opsjes... +&Ykpunt +960 +&Ynhâld... +&7-Zip it hoe en het... +1003 +Paad +Namme +Taheaksel +Map +Grutte +Ynpakte grutte +Skaaimerk +Makke +Lêste tagong +Feroare +Kompakt +Kommentaar +Fersifere +Spjalt foar +Spjalt efter +Wurdboek +CRC +Type +Anty +Metoade +Host OS +Triemsysteem +Brûker +Keppel +Blok +Kommentaar +Posysje +Paad foarheaksel +Mappen +Triemmen +Ferzje +Folume +Multifolume +Offset +Links +Blokken +Folumes + + + + + + + + + + + + + + + +Flater +Totale grutte +Frije romte +Kluster grutte +Kaartsje +Lokale namme +Ferskaffer +2100 +Opsjes +Taal +Taal: +Bewurker +&Bewurker: + +2200 +Systeem +Ferbyn 7-Zip Mei: +2301 +Yntegraasje fan 7-Zip yn ferbânmenu +Ferbânmenu yn trepfoarm +Ferbânmenu-items: +2320 + + +Argyf iepenje +Triemmen útpakke... +Oan argyf taheakje... +Argyf teste +Hjir útpakke +Utpakke yn {0} +Oan {0} taheakje +komprimearje en ferstjoere... +Komprimearje nei {0} en poste +2400 +Mappen +&Wurkmap +&Tydlikesysteemmap +&Aktive +&Oantsjutte: +Allinnich brûke foar útnimbere skiven +ynfiere fan de lokaasje foar tydlike argyftriemmen. +2500 +Ynstellings +".." item sjen litte +Echte triem ikoanen sjen litte +Systeemmenu sjen litte +&Folsleine rige selektearje +&Roaster sjen litte + +&Alternative seleksje modus +Brûk &grut ûnthâld siden +2900 +7-Zip it hoe en het +7-Zip is fergees. Mar, jo kinne de ûntwikkeling stypje troch jo te registrearjen. +3000 + +Der binne gjin flaters +{0} objekt(en) selektearre +Kin map '{0}' net meitsje +Bywurk operaasje waard net stipe troch dit argyf. +Kin triem '{0} net as argyf iepenje +Kin fersifere argyf '{0}' net iepenje. Ferkeard wachtwurd? + + +Triem '{0}' is wizige.\nWolle jo it bywurkje yn it argyf? +Kin triem '{0}'\nnet bywurkje +Kin bewurker net starte. + + + + +Tefolle items +3300 +Utpakke +Oan it komprimearjen +Oan it Testen +Oan it iepenjen... +Oan it skennen... +3400 +Utpakke +U&tpakke nei: +De lokaasje om nei út te pakken oantsjutte. +3410 +Paad modus +Folsleine paadnammen +Gjin paadnammen +3420 +Oerskriuw modus +Freegje foardat jo oerskriuwe +Oerskriuwe sûnder pront +Besteande triemmen oerslaan +Automatysk omneame +Automatysk ek by besteande triemmen +3500 +It ferfangen fan de triem befêstigje +Bestimmingsmap befettet al in triem mei dizze namme. +Wolle jo de triem ferfange +Mei dizze? +{0} bytes +A&utomatysk omneame +3700 +Net stipe kompresjemetoade foar '{0}'. +Data flater yn '{0}'. Triem is beskeadige. +CRC mislearre yn '{0}'. Triem is beskeadige. +Data flater yn fersifere triem '{0}'. Ferkeard wachtwurd? +CRC mislearre yn fersifere triem '{0}'. Ferkeard wachtwurd? +3800 +Wachtwurd ynfiere +Wachtwurd ynfiere: +Wachwurd opnij ynfiere: +Wachtwurd &sjen litte +Wachtwurden komme net oerien +Brûk allinne ingelske letters, nûmers en spesjale karakters (!, #, $, ...) foar it wachtwurd +Wachtwurd is te lang +Wachtwurd +3900 +Ferstrutsen tiid: +Tiid noch te gean: +Totale grutte: +Fluggens: +Ferwurke: +Kompresje nivo: +Flaters: +Argiven: +4000 +Oan argyf taheakje +&Argyf: +&Bywurkmodus: +Argyf &formaat: +Kompresje&nivo: +Kompresje&metoade: +&Wurdboekgrutte: +&Wurdgrutte: +Kompakte blokgrutte: +Tal CPU trieden: +&Parameters: +Opsjes +Meitsje SF&X-argyf +Dielde triemmen komprimearje +Fersifering +Fersiferingmetoade: +Fersiferje triem&nammen +Unthâld gebrûk by komprimearjen: +Unthâld gebrûk by ûntkomprimearjen: +4050 +Bewarje +Fluchst +Fluch +Normaal +Maksimum +Ultra +4060 +Triemmen taheakje en ferfange +Triemmen taheakje en bywurkje +Besteande triemmen ferfarskje +Triemmen lyk rinne litte +4070 +Blêdzje +Alle triemmen +net-kompakt +Kompakt +6000 +Kopiearje +Ferpleatse +Kopiearje nei: +Ferpleats nei: +Oan it kopiearren... +Oan it ferpleatsen... +Omneame... +Bestimmingsmap selektearje. +Operaasje wurdt net stipe. +Flater by it omneamen fan triem of map +Triem kopiearje befêstigje +Binne jo wis dat jo de triemmen nei it argyf kopiearje wolle? +6100 +It wiskjen fan de triem befêstigje +It wiskjen fan de map befêstigje +It wiskjen fan meardere triemmen befêstigje +Binne jo wis dat jo '{0}' wiskje wolle? +Binne jo wis dat jo de map '{0}' en al syn ynhâld wiskje wolle? +Binne jo wis dat jo {0} items wiskje wolle? +Oan it wiskjen +Flater by it wiskjen fan triem of map + +6300 +Map meitsje +Triem meitsje +Mapnamme: +Triem namme: +Nije map +Nije triem +Flater by it meitsjen fan map +Flater by’t meitsjen fan triem +6400 +Kommentaar +&Kommentaar: +Selektearje +Net Selektearje +Masker: +6600 +Eigenskippen +Maphistoarje +Diagnostyk berjocht +Berjocht +7100 +Kompjûter +Netwurk + +Systeem +7200 +Taheakje +Utpakke +Test +Kopiearje +Ferpleatse +Wiskje +Ynformaasje +7300 +Triem spjalte +&Spjalt nei: +Spjalte nei &folumes, bytes: +Oan it Spjalten... +Spjalte befêstigje +Wolle jo de triem spjalte yn {0} dielen? +Folumegrutte moat lytser wêze dan de grutte fan it orizjineel +Net juste folumegrutte +Oantsjutte folumegrutte: {0} bytes.\nWolle jo it argyf yn sokke folumes spjalte? +7400 +Triemmen kombinearje +&Kombinearje nei: +Oan it kombinearjen... +Allinne earste triem selektearje + + +7500 +Kontrôlesom oan it berekenjen... +Kontrôlesom ynformaasje +CRC kontrôlesom foar data: +CRC kontrôlesom foar data en nammen: +7600 +Benchmark +Unthâld gebrûk: +Oan it komprimearren +Oan it ûntkomprimearren +Wurdearring +Totale Wurdearring +Hjoeddeiske +Resultaat +CPU brûkens +Beoardieling / Brûkens +Kear foarbei: diff --git a/Utils/7-Zip/Lang/ga.txt b/Utils/7-Zip/Lang/ga.txt new file mode 100644 index 000000000..0c5a8a966 --- /dev/null +++ b/Utils/7-Zip/Lang/ga.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.07 : Seanán Ó Coistín +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Irish +Gaeilge +401 +Tá go maith +Cealaigh + + + +&Tá +&Níl +&Dún +Cabhair + +&Lean ar aghaidh +440 +Tá do gach ceann +Níl go gach ceann +Stad +Atosaigh +&Cúlra +&Tulra +&Cuir ar sos +Ar sos +An bhfuil tú cinnte gur mian leat é a chealú? +500 +&Comhad +&Leagan +Am&harc +Ceanáin +&Uirlisí +&Cabhair +540 +&Oscail +Oscail &istigh +Oscail &lasamuigh +&Amharc +&Eagar +Athainmnigh +&Macasamhlaigh go... +&Bog go... +S&crios +Scar an comhad... +Cumascaigh na comhaid... +Airíonna +Nóta tráchta +Ríomh an tsuim sheiceála +Diff +Cruthaigh fillteán +Cruthaigh comhad +&Scoir +600 +Roghnaigh &uile +Díroghnaigh uile +&Aisiompaigh an roghnúchán +Roghnaigh... +Díroghnaigh... +Roghnaigh de réir cineál +Díroghnaigh de réir cineál +700 +&Deilbhíní móra +&Deilbhíní beaga +&Liosta +&Sonraí +730 +Neamhaicmithe +Gach rud in aon chiseal +&2 fhuinneog +&Barraí na n-uirlisí +Oscail an fréamhfhillteán +Suas fillteán amháin +Oireas na bhfillteán... +Athnuaigh +750 +Barra cartlanna +Barra na n-uirlisí caighdeánacha +Cnaipí móra +Taispeáin an téacs ar na cnaipí +800 +&Cuir an fillteán leis na Ceanáin +Leabharmharc +900 +&Roghanna... +Tástáil fheidhmíochta +960 +&Inneachair... +Maidir le 7-Zip... +1003 +Conair +Ainm +Breiseán +Fillteán +Méid +Comhbhrúite +Tréithe +Cruthaithe an +Rochtain faighte ar an +Mionathraithe ar an +Fothúil +Trácht +Criptithe +Roinn roimh +Roinn i ndiaidh +Foclóir +CRC +Cineál +Frith +Modh +Óstach an CO +Córas na gcomhad +Úsáideoir +Aicme +Ceap +Nóta tráchta +Suíomh +Réimír na conaire +Fillteáin +Comhaid +Leagan +Imleabhar +Il-imleabhair +Fritháirigh +Naisc +Ceapa +Imleabhair + +64 bheart +Foirceann mór +LAP +Méid aiceanta +Méid na gceanntásc +Suim seiceála +Tréithe +Seoladh fíorúil +ID +Ainm gearr +Feidhmchlár a chruthaigh í +Méid na hearnála +Modh +Nasc +Botún +Méid iomlán +Slí atá saor +Méid na braisle +Lipéad +Ainm logánta +Soláthraí +2100 +Roghanna +Teanga +Teanga: +Eagarthóir +&Eagarthóir: +&Diff: +2200 +Córas +Comhthiomsaigh 7-Zip le: +2301 +Comhtháthaigh 7-Zip sa roghchlár comhthéacs +Roghchlár comhthéacs ag titim +Nithe an roghchláir chomhthéacs: +2320 + + +Oscail +Asbhain na comhaid... +Cuir leis an gcartlann... +Tástáil an chartlann +Asbhain anseo +Bain go {0} +Cuir le {0} +Comhbhrúigh agus seol i ríomphost iad... +Comhbhrúigh go {0} agus seol i ríomhphost iad +2400 +Fillteáin +Fillteán oibre +Fillteán sealadach an chórai&s +An comhad reatha +Fillteán &sonraithe: +Bain feidhm as do thiomántáin inaistrithe amháin +Sonraigh suíomh i gcomhair comhaid chartlainne sealadacha. +2500 +Socruithe +Taispeáin an ní ".." +Taispeáin fíordheilbhíní an chomhaid +Taispeáin roghchlár an chórais +Roghnaigh an tsraith ar fad +Taispeáin línte na greille +Brúigh uair amháin chun ní a oscailt +Bain feidhm as modh roghnaithe malartach +Bain feidhm as leathanaigh chuimhne mhóra +2900 +Maidir le 7-Zip +Is saorearra é 7-Zip. Is féidir tacaíocht a thabhairt dá fhorbairt amach anseo, áfach, trí chlárú. +3000 +Ní féidir leis an gcóras an méid cuimhne atá de dhíth a leathdháileadh dó +Níl aon bhotún ann +{0} ní/nithe roghnaithe +Ní féidir an fillteán '{0}' a chruthú +Ní féidir an chartlann seo a nuashonrú. +Ní féidir an comhad '{0}' a oscailt mar chartlann +Ní féidir an chartlann chriptithe '{0}' a oscailt. An bhfuil an focal faire mícheart? +Ní thugtar tacaíocht don chineál comhad seo +Tá an comhad {0} ann cheana +Mionathraíodh an comhad '{0}'.\nAr mhaith leat é a nuashonrú sa chartlann? +Ní féidir an comhad a leanas a nuashonrú\n'{0}' +Ní féidir an t-eagarthóir a thosú. +B'fhéidir gur aicíd é an comhad (tá bearnaí móra in ainm an chomhaid). +Ní féidir an oibríocht seo a chur i bhfeidhm ó fhillteán a bhfuil conair fhada aige. +Caithfear comhad amháin a roghnú +Caithfear comhad amháin nó níos mó a roghnú +An iomarca nithe +3300 +Asbhaint +Ag comhbhrú +Ag tástáil +Ag oscailt... +Ag taiscéal... +3400 +Bain +Bain go: +Roghnaigh fillteán do na comhaid asbhainte. +3410 +Conairí +Conairí iomlána +Níl conair ann +3420 +Modh forscríofa +Deimhnigh sular forscríobhtar +Forscríobh gan iarraidh +Déan neamhaird de na comhaid atá ann cheana +Athainmnigh go huathoibríoch +Athainmnigh na comhaid atá ann cheana +3500 +Deimhnigh ionadú comhaid +Tá comhad leis an ainm seo sa spriocfhillteán cheana féin. +Ar mhaith leat an comhad atá ann cheana a ionadú +leis an gceann seo? +{0} bearta +Athainmnigh go huathoibríoch +3700 +Níl an bealach comhbhrúite bailí i gcomhair '{0}'. +Botún sna sonraí sa chomhad '{0}'. Tá an comhad briste. +Theip ar CRC sa chomhad '{0}'. Tá an comhad briste. +Sonra mícheart sa chomhad criptithe '{0}'. Focal faire mícheart? +Theip ar CRC sa chomhad chriptithe '{0}'. Focal faire mícheart? +3800 +Cuir isteach an focal faire +Cuir an focal faire isteach: +Cuir an focal faire isteach arís: +&Taispeáin an focal faire +Ní hionann an dá fhocal faire +Bain feidhm as litreacha gan síntí fada, uimhreacha agus carachtair shainiúla (!, #, $, ...) +Tá an focal faire rófhada +Focal faire +3900 +Am caite: +Am fágtha: +Méid iomlán: +Luas: +Déanta: +Luas an chomhbhrúite: +Botúin: +Cartlanna: +4000 +Cuir leis an gcartlann +&Cartlann: +&Modh nuashonraithe: +&Formáid na cartlainne: +Méid comhbhrúite: +Modh comhbhrúite: +&Méid an fhoclóra: +Méid na bhfocal: +Méid an chip fhothúla: +Líon na snáitheanna LAP: +&Teorainneacha: +Roghanna +Cruthaigh cartlann SF&X +Comhbhrúigh na comhaid a roinneadh +Criptiú +Modh criptiúcháin: +Criptigh ainmneacha na gcomhad +Cuimhne don chomhbhrúigh: +Cuimhne don dhíchomhbhrúigh: +4050 +Taisc +Is gasta +Gasta +Gnáth +Uasmhéid +Fíorghasta +4060 +Cuir comhaid leis agus ionadaigh +Nuashonraigh na comhaid agus cuir leo +Athnuaigh comhaid atá ann cheana +Comhionannaigh na comhaid +4070 +Siortaigh +Gach comhad +Neamhfhothúil +Fothúil +6000 +Macasamhlaigh +Bog +Macasamhlaigh chuig: +Bog chuig: +Ag acasamhlú... +Ag bogadh... +Ag athainmniú... +Roghnaigh an spriocfhillteán. +Ní féidir sin a dhéanamh. +Tharla botún ag athainmniú an comhad nó an fillteán +Deimhnigh macasamhlú an chomhaid +An bhfuil tú cinnte gur mian leat na comhaid a mhacasamhlú go dtí an chartlann? +6100 +Deimhnigh scriosadh an chomhaid +Deimhnigh scriosadh an chomhaid +Deimhnigh scriosadh iliomad comhad +An bhfuil tú cinnte gur mian leat '{0}' a scriosadh? +An bhfuil tú cinnte gur mian leat an fillteán '{0}' agus gach rud ann a scriosadh? +An bhfuil tú cinnte gur mian leat na {0} nithe seo a scriosadh? +Ag scriosadh... +Tharla botún ag scriosadh an comhad nó an fillteán +Ní thig leis an gcóras comhad le conair fhada a bhogadh go dtí an bosca athchúrsála +6300 +Cruthaigh comhad +Cruthaigh comhad +Ainm an chomhaid: +Ainm an chomhaid: +Comhad nua +Comhad nua +Tharla botún fad is a bhí an comhad á chruthú +Tharla botún fad is a bhí an comhad á chruthú +6400 +Trácht +&Trácht: +Roghnaigh +Díroghnaigh +Folaigh: +6600 +Airíonna +Oireas na bhfillteán +Teachtaireachtaí diagnóiseach +Teachtaireacht +7100 +Ríomhaire +Líonra +Cáipéisí +Córas +7200 +Cuir leis +Asbhain +Tástáil +Macasamhlaigh +Bog +Scrios +Faisnéis +7300 +Roinn an comhad +&Roinn chuig: +Roinn in imleabhair, bearta: +Ag roinnt... +Deimhnigh an roinnt +An bhfuil tú cinnte go dteastaíonn uait an comhad a roinnt i {0} imleabhair? +Ní mór do mhéid an imleabhair a bheith níos lú ná méid an chomhaid bhunaidh +Tá méid an cholúin mícheart +Méid an cholúin sonraithe: {0} bearta.\nAn bhfuil tú cinnte gur mian leat an chartlann a roinnt in imleabhair? +7400 +Cumaisc comhaid +&Cumaisc go: +Ag cumasc... +Ná roghnaigh an chéad chomhad +Ní féidir an comhad a bhraith mar chuid de chomhad scoilte +Ní féidir níos mó ná aon chuid amháin den chomhad scoilte a aimsiú +7500 +Ag ríomh na suime seiceála... +Faisnéis maidir leis an suim sheiceála +Suim sheiceála do shonraí: +Suim sheiceála do shonraí agus d'ainmneacha +7600 +Tástáil fheidhmíochta +Gnáthaíocht na cuimhne: +Ag comhbhrú +Ag díchomhbhrú +Measúnú +Measúnú iomlán +Reatha +Toradh +Gnáthaíocht an LAP +Measúnú / Gnáthaíocht +Bealaí: diff --git a/Utils/7-Zip/Lang/gl.txt b/Utils/7-Zip/Lang/gl.txt new file mode 100644 index 000000000..46c4ff908 --- /dev/null +++ b/Utils/7-Zip/Lang/gl.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 15.00 : 2016-02-01 : enfeitizador +; +; 9.20 : 2014-11-26 : enfeitizador +; +; 3.12 : 2007-11-22 : Xosé Calvo +; +; +; +; +; +; +0 +7-Zip +Galician +Galego +401 +De acordo +Cancelar + + + +&Si +&Non +Pe&char +Axuda + +&Continuar +440 +Si &a todo +Non a &todo +Parar +Reiniciar +Poñer por de&baixo +Traer ao &fronte +&Pausa +Pausado +Ten a certeza de querer cancelar? +500 +&Ficheiro +&Editar +&Ver +F&avoritos +Ferramen&tas +A&xuda +540 +&Abrir +Abrir &dentro +Abrir &fora +&Ver +&Editar +Rena&me +&Copiar a... +&Mover a... +&Eliminar +&Dividir ficheiro... +Com&binar ficheiros... +P&ropiedades +Come&ntario... +Calcular suma de verificación +Diferenzas +Crear cartafol +Crear ficheiro +Sa&ír +Ligazón +&Alternar fluxos +600 +Seleccion&ar todo +Desmarcar todo +&Inverter selección +Seleccionar... +Desmarcar... +Seleccionar por tipo +Desmarcar por tipo +700 +Iconas lon&gas +Iconas &miúdas +&Lista +&Detalles +730 +Sen orde +Vista plana +&2 paneis +Barras de ferramen&tas +Abrir cartafol raíz +Subir un nivel +Históricos de cartafoles... +&Recargar +Recargar auto. +750 +Barra de ferramentas de arquivos +Barra de ferramentas normal +Botóns grandes +Amosar texto dos botóns +800 +Eng&adir cartafol a favoritos como +Marcador +900 +&Opcións... +&Rendemento +960 +&Contidos... +&Acerca de 7-Zip... +1003 +Camiño +Nome +Extensión +Cartafol +Tamaño +Tamaño comprimido +Atributos +Creado +Último acceso +Modificado +Sólido +Comentado +Cifrado +Partir antes +Partir despois +Dicionario +CRC +Tipo +Anti +Método +SO servidor +Sistema de ficheiros +Usuario +Grupo +Bloque +Comentario +Posición +Prefixo do camiño +Cartafoles +Ficheiros +Versión +Volume +Varios volumes +Desprazamento +Ligazóns +Bloques +Volumes + +64-bit +Big-Endian +Procesador +Tamaño físico +Tamaño cabeceiras +Suma de verificación +Características +Enderezo virtual +ID +Nome curto +Aplicación orixinal +Tamaño sector +Modo +Ligazón simbólica +Erro +Tamaño total +Tamaño libre +Tamaño do clúster +Etiqueta +Nome local +Fornecedor +Seguridade NT +Fluxos alternos +Aux !BUSCA +Eliminado +Is Tree !BUSCA + + +Tipo de error +Erros +Erros +Avisos +Aviso +Fluxos +Fluxos alternos +Tamaño fluxos alternos +Tamaño virtual +Tamaño sen comprimir +Total tamaño físico +Indexar volume +SubTipo +Comentario curto +Código de páxina + + + +Tamaño de cola +Tamaño temporal inserido +Ligazón +Ligazón forte +iNode + +Só lectura +2100 +Opcións +Idioma +Idioma: +Editor +&Editor: +&Diferenza: +2200 +Sistema +Asociar 7-Zip con: +Todos os usuarios +2301 +Integrar 7-Zip no menú de contexto +Menú do contexto en cascada +Elementos do menú do contexto: +Iconas no menú de contexto +2320 + + +Abrir arquivo +Extraer ficheiros... +Engadir ao arquivo... +Comprobar arquivo +Extraer aquí +Extraer a {0} +Engadir a {0} +Comprimir e enviar por correo... +Comprimir a {0} e enviar por correo +2400 +Cartafoles +Cartafol en &uso +Cartafol temporal do &sistema +Cartafol a&ctual +E&specificar: +Usar só en unidades extraíbles +Especificar unha localización para os arquivos temporais. +2500 +Axustes +Amosar o elemento ".." +Amosar iconas reais do ficheiro +Amosar menú do sistema +Seleccionar &fila completa +Amosar liñas de &grella +Clic simple para abrir un elemento +Modo de selección &alternativo +Usar memoria &longa de páxinas +2900 +Acerca de 7-Zip +7-Zip é un programa gratuíto +3000 +O sistema non puido atribuír a memoria requirida +Non se produciron erros +{0} obxecto(s) seleccionados +Non se pode crear o cartafol '{0}' +Este tipo de arquivo non permite actualizacións. +Non se pode abrir o ficheiro '{0}' como arquivo +Non se pode abrir o ficheiro cifrado '{0}'. Contrasinal incorrecto? +Tipo de arquivo non admitido +O ficheiro {0} xa existe +Modificouse o ficheiro '{0}'.\nQuéreo actualizar no arquivo? +Non se pode actualizar o ficheiro\n'{0}' +Non se pode iniciar o editor. +O ficheiro parécese a un virus (o nome do ficheiro contén espazos longos). +A operación non pode responder dende un cartafol cun camiño longo. +Tes que seleccionar un ficheiro +Tes que seleccionar un ou máis ficheiros +Demasiados elementos +Non se pode abrir o ficheiro como arquivo {0} +O ficheiro está aberto como arquivo {0} +O arquivo está aberto con desprazamento +3300 +Extraendo +Comprimindo +Probando +Abrindo... +Escaneando... +Eliminando +3320 +Engadindo +Actualizando +Analizando +Replicando +Volvendo comprimir +Omitindo +Eliminando +Creando cabeceira +3400 +Extraer +E&xtraer a: +Especificar unha localización para os ficheiros extraídos. +3410 +Modo camiño: +Nomes de camiño completos +Sen nomes de camiño +Nomes de camiño absoluto +Nomes de camiño relativo +3420 +Modo sobrescribir: +Preguntar antes de sobrescribir +Sobrescribir sen preguntar +Omitir ficheiros existentes +Renomeo automático +Renomeo automático de ficheiros existentes +3430 +Eliminar duplicidade do cartafol raíz +Restaurar seguridade do ficheiro +3500 +Confirmar a substitución de ficheiro +O cartafol destino xa ten un ficheiro co mesmo nome. +Desexa substituír o ficheiro existente +con estoutro? +{0} bytes +Renomeo a&utomático +3700 +Método de compresión non admitido para '{0}'. +Erro de datos en '{0}'. O ficheiro está danado. +Fallou a verificación CRC en '{0}'. O ficheiro está danado. +Erro de datos no ficheiro cifrado '{0}'. Contrasinal incorrecto? +Fallou a verificación CRC no ficheiro cifrado '{0}'. Contrasinal incorrecto? +3710 +Contrasinal incorrecto? +3721 +Método de compresión non admitido +Erro de datos +Fallou a verificación CRC +Datos non dispoñibles +Remate de datos inesperado +Hai certos datos despois do remate dos datos cargados +Non é arquivo +Erro de cabeceiras +Contrasinal incorrecto +3763 +Inicio de arquivo non dispoñible +Inicio de arquivo non confirmado + + + +Característica non admitida +3800 +Insira contrasinal +Insira contrasinal: +Insírao de novo: +Amo&sar contrasinal +Os contrasinais non coinciden +Use só letras latinas, números e caracteres especiais (!, #, $, ...) para o contrasinal +O contrasinal é moi longo +Contrasinal +3900 +Tempo transcorrido: +Tempo restante: +Tamaño total: +Velocidade: +Procesado: +Proporción de compresión: +Erros: +Arquivos: +4000 +Engadir ao arquivo +&Arquivo: +Función act&ualizar: +&Formato de arquivo: +Nive&l de compresión: +&Método de compresión: +Tamaño do &dicionario: +Tamaño de &palabra: +Tamaño de bloque sólido: +Número de procesos da CPU: +&Parámetros: +Opcións +Crear ficheiro SF&X +Comprimir ficheiros compartidos +Cifrado +Método de cifrado: +Cifrar &nomes de ficheiro +Uso de memoria para compresión: +Uso de memoria para extracción: +Eliminar ficheiros despois de comprimir +4040 +Arquivar ligazóns simbólicas +Arquivar ligazóns fortes +Arquivar fluxo de datos alternos +Arquivar seguridade de ficheiro +4050 +Arquivar +Máis rápida +Rápida +Normal +Máxima +Ultra +4060 +Engadir e substituír ficheiros +Actualizar e engadir ficheiros +Só actualizar ficheiros existentes +Sincronizar ficheiros +4070 +Navegar +Todos os ficheiros +Non sólido +Sólido +6000 +Copiar +Mover +Copiar a: +Mover a: +Copiando... +Movendo... +Renomeando... +Seleccione cartafol destino. +A operación non é admitida para este cartafol. +Erro ao renomear ficheiro ou cartafol +Confirmar copia de ficheiro +Desexa copiar os ficheiros ao arquivo +6100 +Confirmar eliminar ficheiro +Confirmar eliminar cartafol +Confirmar eliminar ficheiro múltiplo +Ten a certeza de eliminar '{0}'? +Ten a certeza de eliminar o cartafol '{0}' e todo o que contén? +Ten a certeza de eliminar estes {0} elementos? +Eliminando... +Erro ao eliminar ficheiro ou cartafol +O sistema non pode mover un ficheiro cun nome tan longo á papeleira +6300 +Crear cartafol +Crear ficheiro +Nome do cartafol: +Nome do ficheiro: +Novo cartafol +Novo ficheiro +Erro ao crear cartafol +Erro ao crear ficheiro +6400 +Comentario +&Comentario: +Seleccionar +Desmarcar +Máscara: +6600 +Propiedades +Histórico de cartafoles +Mensaxes de diagnose +Mensaxe +7100 +Computador +Rede +Documentos +Sistema +7200 +Engadir +Extraer +Probar +Copiar +Mover +Eliminar +Información +7300 +Dividir ficheiro +&Dividir a: +Dividir en &volumes, bytes: +Dividindo... +Confirmar división +Ten a certeza de dividir o ficheiro en {0} volumes? +O tamaño do volume ten que ser menor que o tamaño do ficheiro orixinal +Tamaño de volume incorrecto +Tamaño de volume especificado: {0} bytes.\nTen a certeza de dividir o arquivo nestes volumes? +7400 +Combinar ficheiros +&Combinar a: +Combinando... +Seleccionar só a primeira parte do ficheiro dividido +Non se detecta o ficheiro como parte dun ficheiro dividido +Non se atopan máis ca unha parte do ficheiro dividido +7500 +Calculando suma de verificación... +Información da suma de verificación +Verificación CRC para datos: +Verificación CRC para datos e nomes: +7600 +Rendemento +Uso de memoria: +Comprimindo +Extraendo +Proporción +Proporción total +Actual +Resultante +Uso de CPU +Proporción / Uso +Pases: +7700 +Ligazón +Ligazón +Ligazón desde: +Ligazón a: +7710 +Tipo de ligazón +Ligazón forte +Ligazón simbólica de ficheiro +Directorio ligazón simbólica +Directorio nexo diff --git a/Utils/7-Zip/Lang/gu.txt b/Utils/7-Zip/Lang/gu.txt new file mode 100644 index 000000000..abcedc727 --- /dev/null +++ b/Utils/7-Zip/Lang/gu.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.07 : Vinayy Sharrma : વિનય શરà«àª®àª¾ દà«àªµàª¾àª°àª¾ અનà«àªµàª¾àª¦àª¿àª¤ મેહનત કરેલી છે તો પોતાનà«àª‚ નામ લખવામાં કાંઇ બà«àª°àª¾àªˆ નથી. હિનà«àª¦ પર ગરà«àªµ કરો, જય હિનà«àª¦ ! જય હિનà«àª¦à«€ ! જય ગરવી ગà«àªœàª°àª¾àª¤ ! જય ગà«àªœàª°àª¾àª¤à«€ +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Gujarati, Indian, હિનà«àª¦à«àª¸à«àª¤àª¾àª¨ +ગà«àªœàª°àª¾àª¤à«€ +401 +સારà«àª‚ +રદà«àª¦ + + + +&હાઠ+&ના +&બંદ કરો +મદદ + +&જારી રાખો +440 +&બધા માટે હાઠ+&બધા માટે ના +રૂકો +પà«àª¨àªƒ શà«àª°à« કરો +&પૄષà«àª à«àª­à«‚મિ +&અગà«àª°àª­à«‚મિ(ડેસà«àª•à«àªŸà«‹àªª) +&વિશà«àª°àª¾àª® +થોભેલà«àª‚ +તà«àª®à«‡ રદà«àª¦ કરવા ચાહો છો. શà«àª‚ તમને યકીન છે? +500 +&ફ઼ાઇલ +&સંપાદન +&દરà«àª¶àª¨ +&મનપસંદ +&ઔજાર +&મદદ +540 +&ખોલો +&અંદર ખોલો +&બાહર ખોલો +&દૃશà«àª¯ +&સંપાદન +&પà«àª¨: નામકરણ +&માં નકલ બનાવો... +&માં લઇ જાઓ... +&મિટાવો +&ફ઼ાઇલનો વિભાજન કરો... +&ફ઼ાઇલનો સંયોજન કરો... +&સંપતà«àª¤àª¿àª¯àª¾àª યા ગà«àª£ +&ટિપà«àªªàª£à«€ +&જાàªàªš યોગની ગણના કરો +&અનà«àª¤àª° +&ફ઼ોલà«àª¡àª° તૈયાર કરો +&ફ઼ાઇલ તૈયાર કરો +&નિરà«àª—મન +600 +&બધા ચયન કરો +&બધા અચયનિત કરો +&ચયન ઊંધà«àª‚ કરો +ચયન કરો... +અચયન કરો... +પà«àª°àª•ાર દà«àªµàª¾àª°àª¾ ચયન +પà«àª°àª•ાર દà«àªµàª¾àª°àª¾ અચયન +700 +મોટાં પà«àª°àª¤à«€àª• +લઘૠપà«àª°àª¤à«€àª• +&સૂચી +&વરà«àª£àª¨ +730 +અવિતરિત +ચૌડ઼ા દૃશà«àª¯ +&૨ ફ઼લક +&ઔજાર પટà«àªŸà«€àª“ +મૂલ ફ઼ોલà«àª¡àª° ખોલો +àªàª• સà«àª¤àª° ઊપર ચઢ઼ો +ફ઼ોલà«àª¡àª°à«‹ નો ઇતિહાસ... +&તાજા કરો +750 +સંગà«àª°àª¹ ઔજાર પટà«àªŸà«€ +માનક ઔજાર પટà«àªŸà«€ +મોટા ખટકા(બટન) +ખટકા(બટન)ના શબà«àª¦ દિખાવો +800 +&ફ઼ોલà«àª¡àª° મનપસંદમાં àªàªµà«€ રીતે જોડો... +પà«àª¸à«àª¤àªšàª¿àª¨à«àª¹ +900 +&વિકલà«àªª... +&બેઞà«àªšàª®àª¾àª°à«àª•(પà«àª°àª¾àª®àª¾àª£àª¿àª• તà«àª²àª¨àª¾) +960 +&સામગà«àª°à«€... +à«­-જિપ ના વિશેમાં... +1003 +મારà«àª— +નામ +વિસà«àª¤àª¾àª° +ફ઼ોલà«àª¡àª° +આકાર +કà«àª² આકાર +વિશેષતા યા ગà«àª£àª§àª°à«àª® +સરà«àªœàª¿àª¤ +ચલાવેલી +પરિવરà«àª§àª¿àª¤ +ઠોસ +ટિપà«àªªàª£à«€ +ગà«àªªà«àª¤àª¿àª•ૃત +ના પૂરà«àªµà«‡ વિભાજન(ટà«àª•ડે) કરો +ના બાદ વિભાજન(ટà«àª•ડે) કરો +શબà«àª¦àª•ોશ +સીઆરસી +પà«àª°àª•ાર +વિરોધી +પદà«àª§àª¤àª¿ +યજમાન આજà«àªžàª¾àªµàª²à«€(ઓપરેટિંગ સિસà«àªŸàª®) +ફ઼ાઇલ પà«àª°àª£àª¾àª²à«€ +પà«àª°àª¯à«‹àª—કરà«àª¤àª¾ +સમૂહ +રોક કે ટà«àª•ડાઓ +પà«àª°àª¤àª¿àª•à«àª°àª¿àª¯àª¾ +સà«àª¥àª¾àª¨ +મારà«àª— પà«àª°àª¤à«àª¯àª¯ +ફોલà«àª¡àª°àª¸ +ફાઇલà«àª¸ +સંસà«àª•રણ +જતà«àª¥àª¾ +અનેક જતà«àª¥àª¾àª“ +ઓફસેટ +કડિયાઠ+ટà«àª•ડ઼ે +જતà«àª¥à«‡ + +૬૪-બિટ +મોટà«àª‚-àªàª¨à«àª¡àª¿àª¯àª¨ +સીપીયૂ +ભૌતિક આકાર +શીરà«àª·àª•ોંના આકાર +જાàªàªšàª¯à«‹àª— +ચરિતà«àª°àª¤àª¾àª“ +આભાસી પતà«àª¤à«‹ +આઈડી +સંકà«àª·àª¿àªªà«àª¤ નામ +સરà«àªœàª• અનà«àªªà«àª°àª¯à«‹àª— +સેકà«àªŸàª°àª¨à«‹ આકાર +સà«àª¥àª¿àª¤àª¿ +કડ઼ી +તà«àª°à«àªŸàª¿ +કà«àª² આકાર +સà«àªµàª¤àª¨à«àª¤à«àª° રિકà«àª¤àª¸à«àª¥àª¾àª¨(ખાલી જગહ) +કà«àª²àª¸à«àªŸàª°(સમૂહ) આકાર +ધà«àª¯àª¾àª¨àª¾àª•રà«àª·àª•(લેબલ) +સà«àª¥àª¾àª¨àª¿àª• નામ +પà«àª°àª¦àª¾àª¯àª• +2100 +વિકલà«àªª +ભાષા +ભાષા: +સંપાદક +&સંપાદક: +&અનà«àª¤àª°: +2200 +પà«àª°àª£àª¾àª²à«€ કે તંતà«àª° +સંબધિત કરો à«­-જિપ ના સાથે: +2301 +à«­-જિપ ના શેલ (કવચ) પà«àª°àª¸àª‚ગ મેનૠમાં જોડો +સોપાનીકૃત(કેસà«àª•ેડેડ) પà«àª°àª¸àª‚ગ મેનૠ+પà«àª°àª¸àª‚ગ(કોનà«àªŸà«‡àª•à«àª¸à«àªŸ) મેનૠવસà«àª¤à«àªàª: +2320 +<ફોલà«àª¡àª°> +<સંગà«àª°àª¹(આરà«àªšàª¿àªµ)> +સંગà«àª°àª¹ ખોલો +ફ઼ાઇલà«àª¸ બાહર કાઢો... +સંગà«àª°àª¹àª®àª¾àª‚ જોડો... +સંગà«àª°àª¹àª¨à«€ જાàªàªš કરો +અહિયાં બાહર કાઢો +{0} માં બાહર કાઢો +{0} માં જોડો +દબાવો(સંકà«àªšàª¨) અને ઇમેલ કરો... +{0} માં દબાવો અને ઈમેલ કરો +2400 +ફોલà«àª¡àª°à«àª¸ +&કારà«àª¯àª°àª¤ ફોલà«àª¡àª° +&પà«àª°àª£àª¾àª²à«€àª¨à«àª‚ અસà«àª¥àª¾àª¯à«€(ટેમà«àªªàª°àª°à«€) ફોલà«àª¡àª° +&ચાલૠ+&નિરà«àª¦àª¿àª·à«àªŸ: +ફકà«àª¤ હટાવવા યોગà«àª¯(રિમૂવેબલ) ડà«àª°àª¾àªˆàªµ માટે જ પà«àª°àª¯à«‹àª— કરો +અસà«àª¥àª¾àª¯à«€ સંગà«àª°àª¹ ફાઇલ માટે સà«àª¥àª¾àª¨ નિરà«àª¦àª¿àª·à«àªŸ કરો(બતાવો). +2500 +વà«àª¯àªµàª¸à«àª¥àª¾àª“ +દિખાવો ".."વસà«àª¤à« +વાસà«àª¤àªµàª¿àª• ફ઼ાઇલ પà«àª°àª¤à«€àª• બતાવો +તંતà«àª° નો મેનૠબતાવો +&આખી પનà«àª•à«àª¤àª¿àª¨à«àª‚ ચયન +&ગà«àª°àª¿àª¡(જાલ) રેખા દિખાવો +વસà«àª¤à« ખોલવા માટે àªàª• જ(સિંગલ)-કà«àª²àª¿àª• +&વૈકલà«àªªàª¿àª• ચયન સà«àª¥àª¿àª¤àª¿ +&મોટા સà«àª®à«ƒàª¤àª¿ પૃષà«àª àª¨à«‹ પà«àª°àª¯à«‹àª— કરો +2900 +7-જિપ ના વિશે +7-જિપ ઠનિઃશà«àª²à«àª• સૉફ઼à«àªŸàªµà«‡àª¯àª° છે. તો પણ, આપ પંજીકૃત(રજિસà«àªŸàª°à«àª¡) થઈને à«­-જ઼િપ ના વિકાસમાં સહયોગ કરી શકો છો. +3000 +તંતà«àª° જરૂરી માતà«àª°àª¾àª®àª¾àª‚ મેમોરી(સà«àª®à«ƒàª¤àª¿) વિતરિત નથી કરી શકતà«àª‚ +આમાં કોઈ પણ તà«àª°à«àªŸàª¿ નથી +{0} ચયનિત વસà«àª¤à«(ઓ) +'{0}' ફ઼ોલà«àª¡àª° સરà«àªœàª¿àª¤ નથી કરી શકતà«àª‚ +આ સંગà«àª°àª¹ માટે અદà«àª¯àª¤àª¨à«€àª•ૃત સંચાલન સમરà«àª¥àª¿àª¤ નથી. +'{0}' ફાઇલને સંગà«àª°àª¹àª¨àª¾àª‚ રૂપમાં નથી ખોલી શકતà«àª‚ +'{0}' ગà«àªªà«àª¤àª¿àª•ૃત સંગà«àª°àª¹àª¨à«‡ નથી ખોલી શકતà«. ગલત કૂટશબà«àª¦? +અસમરà«àª¥àª¿àª¤ સંગà«àª°àª¹ પà«àª°àª•ાર +ફાઇલ {0} પહેલાથી હયાત છે +'{0}' ફ઼ાઇલ પરિવરà«àª§àª¿àª¤ થઈ છે.\nશà«àª‚ તમે સંગà«àª°àª¹àª®àª¾àª‚ આને અદà«àª¯àª¤àª¨à«€àª•ૃત કરવા માગો છો? +ફ઼ાઇલ ને અદà«àª¯àª¤àª¨à«€àª•ૃત નથી કરી શકતà«àª‚\n'{0}' +સંપાદકને શરૂ નથી કરી શકતà«àª‚. +આ ફાઇલ àªàª• વિષાણà«(વાયરસ) જેવી લાગે છે (ફાઇલ નામ લાંબી ખાલી જગહ નામમાં રાખતો છે). +જે ફોલà«àª¡àª°àª¨à«‹ લાંબો મારà«àª— છે તેનાથી સઞà«àªšàª¾àª²àª¨ કà«àª°àª¿àª¯àª¾ બોલાવી નથી શકાતી. +તમારે àªàª• ફાઇલનો ચયન તો કરવો જ પડશે +તમારે àªàª• કે જà«àª¯àª¾àª¦àª¾ ફાઇલોંનો ચયન તો કરવો જ પડશે +બહૠવધારે વસà«àª¤à«àª“ +3300 +બાહર કાઢી રહà«àª¯à«àª‚ છે +સંકà«àªšàª¨ કરી રહà«àª¯à«àª‚ છે +પરીકà«àª·àª£ +ખોલી રહà«àª¯à« છે... +તલાશી(સà«àª•ૈનિંગ) કરી રહà«àª¯à« છે... +3400 +બાહર કાઢો +&બાહર કાઢો: +બાહર કાઢેલી ફ઼ાઇલોં માટે સà«àª¥àª¾àª¨ નિરà«àª¦àª¿àª·à«àªŸ કરો. +3410 +મારà«àª— સà«àª¥àª¿àª¤àª¿ +આખો મારà«àª—નામ +કોઈ મારà«àª— નામ નથી +3420 +અધિલેખન રીત +અધિલેખન કરતાં પહલાં પૂછો +વગર પૂછે અધિલેખન(જà«àª¨à«àª‚ મટાવવà«àª‚) કરો +પહેલાથી હયાત ફ઼ાઇલસને છોડો +સà«àªµàªšàª¾àª²àª¿àª¤ પà«àª¨: નામકરણ +પહેલાથી હયાત ફ઼ાઇલસનો સà«àªµàªšàª¾àª²àª¿àª¤(ઓટોમેટિક) પà«àª¨: નામકરણ કરો +3500 +ફ઼ાઇલ પà«àª°àª¤àª¿àª¸à«àª¥àª¾àªªàª¨ ને પાકà«àª•ૠકરો +ગનà«àª¤àªµà«àª¯ ફોલà«àª¡àª°àª®àª¾àª‚ પહેલાથી જ પà«àª°àª•à«àª°àª¿àª¯àª¾ થàªàª²à«€ ફ઼ાઇલ છે. +શà«àª‚ આપ પહેલાથી હયાત ફ઼ાઇલ ને બદલવà«àª‚ પસંદ કરશો? +આની જોડે? +{0} બાઇટà«àª¸ +સà«àªµàªšàª¾àª²àª¿àª¤ પà«àª¨: નામકરણ +3700 +'{0}' ના માટે અસહાયક દબાવાવાની પદà«àª§àª¤àª¿. +ડેટા તà«àª°à«àªŸàª¿â€™{0}' માં. ફ઼ાઇલ તૂટેલી છે. +'{0}' માં સીઆરસી અસફલ. ફ઼ાઇલ તૂટેલી છે. +'{0}' ગà«àªªà«àª¤àª¿àª•ૃત(àªàª¨àª•à«àª°àª¿àªªà«àªŸà«‡àª¡) ફાઇલ માં ડેટા તà«àª°à«àªŸàª¿. ગલત કૂટશબà«àª¦? +'{0}' ગà«àªªà«àª¤àª¿àª•ૃત(àªàª¨àª•à«àª°àª¿àªªà«àªŸà«‡àª¡) ફાઇલ માં સીઆરસી અસફલ. ગલત કૂટશબà«àª¦? +3800 +કૂટશબà«àª¦(પાસવરà«àª¡) ડાલે +કૂટશબà«àª¦(પાસવરà«àª¡) ડાલે: +કૂટશબà«àª¦ પà«àª¨àªƒ નાખો: +&કૂટશબà«àª¦(પાસવરà«àª¡) દિખાવો +કૂટશબà«àª¦ સહેજેલાંથી જà«àª¦à«‚ં છે +કૂટશબà«àª¦ માટે ફકà«àª¤ ઇંગà«àª²àª¿àª¶ વરà«àª£àª®àª¾àª²àª¾, અંકો અને વિશેષ અકà«àª·àª°à«‹àª‚ (!, #, $, ...) નો જ ઉપયોગ કરો +કૂટશબà«àª¦ ખૂબ જ મોટà«àª‚ છે +કૂટશબà«àª¦(પાસવરà«àª¡) +3900 +વà«àª¯àª¤à«€àª¤ સમય: +શેષ બચેલà«àª‚ સમય: +કà«àª² આકાર: +ગતિ: +પà«àª°àª•à«àª°àª¿àª¯àª¾ કરેલà«àª‚: +દબાવાનà«àª‚(આકાર છોટા કરવાનà«àª‚)અનà«àªªàª¾àª¤: +તà«àª°à«àªŸàª¿àª¯àª¾àª: +સંગà«àª°àª¹: +4000 +સંગà«àª°àª¹àª®àª¾àª‚ જોડો +&સંગà«àª°àª¹: +&અદà«àª¯àª¤àª¨à«€àª•રણ સà«àª¥àª¿àª¤àª¿(મોડ): +સંગà«àª°àª¹ &ઢાàªàªšàª¾: +&સંકà«àªšàª¨ સà«àª¤àª°: +&સંકà«àªšàª¨ વિધિ: +&શબà«àª¦àª•ોશ આકાર: +&શબà«àª¦ આકાર: +ઠોસ ટà«àª•ડાનો આકાર: +સીપીયૂ સૂતà«àª° સંખà«àª¯àª¾: +&પરિમાપ: +વિકલà«àªª +&àªàª¸àªàª«àª¼àªàª•à«àª¸(SFX) સંગà«àª°àª¹ તૈયાર કરો +સાàªà«€ ફાઇલો સંકà«àªšàª¿àª¤ કરો +ગà«àªªà«àª¤àª¿àª•રણ +ગà«àªªà«àª¤àª¿àª•રણ પદà«àª§àª¤àª¿: +ફ઼ાઇલ &નામ ગà«àªªà«àª¤àª¿àª•રણ કરો +સંકà«àªšàª¨ માટે સà«àª®à«ƒàª¤àª¿ પà«àª°àª¯à«‹àª—: +પà«àª°àª¸àª¾àª°àª£ માટે સà«àª®à«ƒàª¤àª¿ પà«àª°àª¯à«‹àª—: +4050 +ભંડારણ +સરà«àªµàª¾àª§àª¿àª• તેજ +તેજ +સાધારણ +અધિકતમ +અતà«àª¯àª¨à«àª¤ +4060 +ફ઼ાઇલેં જોડો અને પà«àª°àª¤àª¿àª¸à«àª¥àª¾àªªàª¿àª¤ કરો +ફાઇલો અદà«àª¯àª¤àª¨à«€àª•ૃત કરો અને જોડો +અવસà«àª¥àª¿àª¤ ફાઇલોં તાજા કરો +ફાઇલોં સમકà«àª°àª®àª£(સિંકà«àª°à«‹àª¨àª¾àªˆàªœàª¼) કરો +4070 +બà«àª°àª¾àª‰àªœ યા ઘૂમો +બધા ફાઇલોં +અ-ઠોસ +ઠોસ +6000 +નકલ +લઇ જાઓ +માં નકલ: +માં લઇ જાઓ: +નકલ... +લઇ જાઇ રહà«àª¯à« છે... +પà«àª¨àªƒ નામકરણ... +ગનà«àª¤àªµà«àª¯ ફોલà«àª¡àª° ચયનિત કરો. +આ ફોલà«àª¡àª° માટે આ સઞà«àªšàª¾àª²àª¨ કà«àª°àª¿àª¯àª¾ સમરà«àª¥àª¿àª¤ નથી. +ફ઼ાઇલ કે ફ઼ોલà«àª¡àª°àª¨àª¾ પà«àª¨àªƒ નામકરણ માં તà«àª°à«àªŸàª¿ +ફ઼ાઇલની નકલ કરવૠપાકà«àª•à«àª‚ કરો +તમે સંગà«àª°àª¹àª®àª¾àª‚ ફાઇલ ની પà«àª°àª¤àª¿àª²àª¿àªªàª¿ કરવા ચાહો છો શà«àª‚ તમને યકીન છે +6100 +ફ઼ાઇલ મિટાવો આ પાકà«àª•à«àª‚ કરો +ફ઼ોલà«àª¡àª° મિટાવો પાકà«àª•à«àª‚ કરો +અનેક ફ઼ાઇલ મિટાવો પાકà«àª•à«àª‚ કરો +શà«àª‚ તમને યકીન છે કે તમે મેટવવા ચાહો છો '{0}'? +શà«àª‚ તમને યકીન છે કે તમે ફ઼ોલà«àª¡àª° મિટાવવા માંગો છો ’{0}' અને આની બધી સામગà«àª°à«€ પણ? +શà«àª‚ તમને યકીન છે કે તમે મિટાવવા માંગો છો આ {0} વસà«àª¤à«àª“ં ને? +મેટવી રહà«àª¯à« છે... +ફ઼ાઇલ કિંવા ફ઼ોલà«àª¡àª° મિટાવામાં તà«àª°à«àªŸàª¿ +તંતà«àª° લાંબા મારà«àª— વાલી ફાઇલને પà«àª¨àªƒàªšàª•à«àª°àª£ પેટી(રિસાઈકલ બિન)માં નથી લઇ જાઇ શકતà«. +6300 +ફ઼ોલà«àª¡àª° તૈયાર કરો +ફ઼ાઇલ તૈયાર કરો +ફ઼ોલà«àª¡àª° નામ: +ફ઼ાઇલ નામ: +નવૠફ઼ોલà«àª¡àª° +નવી ફ઼ાઇલ +ફ઼ોલà«àª¡àª° તૈયાર કરવામાં તà«àª°à«àªŸàª¿ +ફ઼ાઇલ તૈયાર કરવામાં તà«àª°à«àªŸàª¿ +6400 +ટિપà«àªªàª£à«€ +&ટિપà«àªªàª£à«€: +ચયન +ચયન રદà«àª¦ +મà«àª–ૌટો: +6600 +ગà«àª£ યા સંપતà«àª¤àª¿àª¯àª¾àª +ફ઼ોલà«àª¡àª°à«‹àª‚નો ઇતિહાસ +નિદાનાતà«àª®àª• સંદેશ +સંદેશ +7100 +સંગણક +સઞà«àªœàª¾àª² +દસà«àª¤àª¾àªµà«‡àªœ +પà«àª°àª£àª¾àª²à«€ +7200 +જોડો +બાહર કાઢો +પરીકà«àª·àª£ +નકલ +લઈ જાઓ +મિટાવો +સૂચના +7300 +ફ઼ાઇલનà«àª‚ વિભાજન કરો +&માં વિભાજન: +જતà«àª¥àª¾àª“માં વિભાજન, બાઇટà«àª¸: +વિભાજન કરી રહà«àª¯à« છે... +વિભાજન કરવાનà«àª‚ પાકà«àª•à«àª‚ કરો +શà«àª‚ તમને યકીન છે કે તમે ફાઇલ ને {0} જતà«àª¥àª¾àª“માં વિભાજિત કરવા માગો છો? +મૂલ ફાઇલના આકારની તà«àª²àª¨àª¾àª®àª¾àª‚ જતà«àª¥àª¾àª¨à«àª‚ આકાર નાનો જ હોવો જોઇઠ+જતà«àª¥àª¾àª¨à«‹ આકાર ખોટà«àª‚ છે +નિરà«àª¦à«‡àª¶àª¿àª¤ જતà«àª¥àª¾ આકાર: {0} બાઇટસ.\n આપ સંગà«àª°àª¹àª¨à«‡ àªàªµàª¾ જતà«àª¥àª¾àª“માં વિભાજિત કરવા ચાહો છો, શà«àª‚ તમને યકીન છે? +7400 +ફાઇલો સંયોજિત કરો +&માં સંયોજન કરો: +સંયોજન થઇ રહà«àª¯à« છે... +વિભાજિત ફાઇલનો ફકà«àª¤ પà«àª°àª¥àª® ભાગ જ ચયનિત કરો +ફાઇલ ને વિભાજિત ફાઇલના ભાગના રૂપમાં ઓળખી નથી શકતૠ+વિભાજિત ફાઇલના àªàª• થી વધારે ભાગ શોધી નથી શકતૠ+7500 +જાàªàªšàª¯à«‹àª—(ચેકસમ)ની ગણના કરી રહà«àª¯à« છે... +જાàªàªšàª¯à«‹àª—(ચેકસમ) માહિતી +સીઆરસી જાàªàªšàª¯à«‹àª—(ચેકસમ) આàªàª•ડ઼ોં માટે : +સીઆરસી જાàªàªšàª¯à«‹àª—(ચેકસમ) આàªàª•ડ઼ોં અને નામોં માટે : +7600 +(કસૌટી ચિનà«àª¹)બેઞà«àªšàª®àª¾àª°à«àª• +સà«àª®à«ƒàª¤àª¿ ઉપયોગ: +સંકà«àªšàª¨ કરી રહà«àª¯à«àª‚ છે +પà«àª°àª¸àª¾àª°àª£ કરી રહà«àª¯à«àª‚ છે +કà«àª°àª®àª¾àª‚કન +કà«àª² કà«àª°àª®àª¾àª‚કન +વરà«àª¤àª®àª¾àª¨ +પરિણામ +સીપીયૂ ઉપયોગ +કà«àª°àª®àª¾àª‚કન / ઉપયોગ +પાસ: diff --git a/Utils/7-Zip/Lang/he.txt b/Utils/7-Zip/Lang/he.txt new file mode 100644 index 000000000..c4e9d39d9 --- /dev/null +++ b/Utils/7-Zip/Lang/he.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; : peterg +; : Gal Brill +; 9.13 : 2010-04-30 : Jonathan Lahav +; +; +; +; +; +; +; +; +0 +7-Zip +Hebrew +עברית +401 +×ישור +ביטול + + + +&כן +&×œ× +&סגור +עזרה + +×”&משך +440 +כן ל&הכל +ל&× ×œ×”×›×œ +עצור +התחל מחדש +&רקע +&קדמה +×”&שהה +מושהה +?×”×× ×ת/×” בטוח/×” שברצונך לבטל +500 +&קובץ +&עריכה +&תצוגה +&×ž×•×¢×“×¤×™× +&×›×œ×™× +×¢&זרה +540 +&פתח +פתח ב&תוכנה +פתח ב&חלון +&הצג +&ערוך +&שנה ×©× +...העתק ל& +...העבר ל& +&מחק +...פצל קובץ& +...מזג קבצי×& +מ&××¤×™×™× ×™× +הערה +חשב ×¡×™×›×•× ×‘×™×§×•×¨×ª +הבדל +צור תיקייה +צור קובץ +×™&צי××” +600 +בח&ר הכל +בטל בחירה +×”&פוך בחירה +...בחר +...בטל בחירה +בחר לפי סוג +בטל בחירה לפי סוג +700 +×¡×ž×œ×™× &×’×“×•×œ×™× +×¡×ž×œ×™× &×§×˜× ×™× +&רשימה +&×¤×¨×˜×™× +730 +×œ× ×ž×¡×•×“×¨ +תצוגה בפריסה מל××” +&שני חלונות +&סרגלי ×›×œ×™× +פתח תיקיית שורש +חזור שלב ×חד +...היסטוריית תיקיות +ר&ענן +750 +סרגל ×רכיון +סרגל רגיל +×›×¤×ª×•×¨×™× ×’×“×•×œ×™× +הצג ×ת פעולת הכפתור מתחתיו +800 +&הוסף תיקייה ×œ×ž×•×¢×“×¤×™× ×› +סימנייה +900 +&...×פשרויות +&מבחן ×‘×™×¦×•×¢×™× +960 +&...תוכן +&...7-Zip ×ודות +1003 +נתיב +×©× +סיומת +תיקייה +גודל +גודל מכווץ +תכונות +נוצר +נצפה +השתנה +רצוף +נרשמה הערה +הוצפן +פצל לפני +פצל ×חרי +מילון +CRC +סוג +×נטי +שיטה +מערכת הפעלה +מערכת ×§×‘×¦×™× +משתמש +קבוצה +קטע +הערה +×ž×™×§×•× +תחילית נתיב +תיקיות +×§×‘×¦×™× +גירסה +חלק +ריבוי ×—×œ×§×™× +קיזוז +×§×™×©×•×¨×™× +×§×˜×¢×™× +×—×œ×§×™× + +64ביט +×חסון בסדר חשיבות יורד +מש×בי מערכת/מעבד +גודל פיזי +גודל כותרת עליונה +בדיקת ×¡×™×›×•× +מ××¤×™×™× ×™× +כתובת וירטו×לית +מס' זיהוי +×©× ×§×¦×¨ +תוכנה יוצרת +גודל סקטור +מצב +קישור +שגי××” +גודל כללי +שטח פנוי +גודל קלסטר/יחידת ×חסון +תווית +×©× ×ž×§×•×ž×™ +ספק +2100 +×פשרויות +Language / שפה +Language / שפה: +עורך +&עורך: +&הבדל: +2200 +מערכת +7-Zip שייך ל : +2301 +שלב ×ת התוכנה בתפריט-ההקשר הכללי +תפריט מדורג +פריטי תפריט: +2320 +<תיקייה> +<×רכיון> +פתח ×רכיון +...חלץ ×§×‘×¦×™× +...הוסף ל×רכיון +בדוק ×רכיון +חלץ לכ×ן +{0} -חלץ ל +{0} -הוסף ל +כווץ ושלח בדו×ר ×לקטרוני +כווץ ל- {0} ושלח בדו×ר ×לקטרוני +2400 +תיקיות +&תיקיית עבודה +של המ&ערכת "Temp" תיקיית ×” +&נוכחית +&בחר תיקייה: +השתמש בהגדרות ×לו עבור ×›×•× × ×™× × ×™×™×“×™× ×‘×œ×‘×“ +.בחר ×ž×™×§×•× ×œ×§×‘×¦×™ ×רכיון ×–×ž× ×™×™× +2500 +הגדרות +".." הצג ×ת הפריט +הצג סמלי ×§×‘×¦×™× ××ž×™×ª×™×™× +הצג ×ת תפריט המערכת +בחר ×ת כל ×”&שורה +הצג קווי &טבלה +לחיצה ×חת לפתיחת פריט +שיטת &בחירה חלופית +השתמש בקטעי זיכרון &×’×“×•×œ×™× ×™×•×ª×¨ +2900 +7-Zip ×ודות +זו ×”×™× ×ª×•×›× ×” חופשית. ×¢× ×–×ת, ביכולתך לתמוך בפיתוח התוכנה על ידי הרשמה +3000 +המערכת ××™× ×” יכולה להקצוות ×ת כמות הזיכרון הדרושה +×ין שגי×ות +(נבחרו {0} פריט(×™× +'{0}' ×ין ×פשרות ליצור ×ת התיקייה +.פעולות עדכון ×œ× × ×ª×ž×›×•×ª עבור ×רכיון ×–×” +'×œ× ×”×™×” ניתן לפתוח ×ת הקובץ '{0}' ×›×רכיון +'?×œ× ×”×™×” ניתן לפתוח ×ת ×”×רכיון המקודד '{0}'. ×¡×™×¡×ž× ×©×’×•×™×™×” +סוג ×רכיון ×ינו נתמך +הקובץ {0} כבר ×§×™×™× +?×”×× ×‘×¨×¦×•× ×š לעדכן ×ותו ב×רכיון\n.הקובץ '{0}' שונה +'{0}'\n×ין ×פשרות לעדכן ×ת הקובץ +.×ין ×פשרות להפעיל ×ת העורך +(ייתכן והקובץ היינו וירוס (×™×©× × ×¨×•×•×—×™× ×’×“×•×œ×™× ×‘×©× ×”×§×•×‘×¥ +.הפעולה ××™× ×” יכולה להתבצע מתיקייה בעלת נתיב ×רוך +הינך חייב/ת לבחור קובץ ×חד +הינך חייב לבחור קובץ ×חד ×ו יותר +יותר מדי ×¤×¨×™×˜×™× +3300 +מחלץ +מכווץ +בודק +...פותח +...סורק +3400 +חלץ +&חלץ ל: +.ציין יעד לחילוץ ×”×§×‘×¦×™× +3410 +שיטת ×”× ×ª×™×‘×™× +× ×ª×™×‘×™× ×ž×œ××™× +×œ×œ× × ×ª×™×‘×™× +3420 +מצב שכתוב ×§×‘×¦×™× +ש×ל לפני שכתוב +שכתב בלי לש×ול +דלג על ×§×‘×¦×™× ×§×™×™×ž×™× +שינוי ×©× ×וטומטי +שינוי ×©× ×וטומטי ×œ×§×‘×¦×™× ×§×™×™×ž×™× +3500 +×שר החלפת קובץ +.תיקיית היעד מכילה כבר קובץ ×‘×©× ×–×” +×”×× ×‘×¨×¦×•× ×š להחליף ×ת הקובץ ×”×§×™×™× +?בקובץ ×”×–×” +{בתי×: {0 +שינוי ×©× &×וטומטי +3700 +.'{0}' שיטת הדחיסה ××™× ×” נתמכת עבור +.שגי×ת מידע ב '{0}'. הקובץ ×ינו תקין +.בדיקת ×¡×›×•× ×”× ×ª×•× ×™× × ×›×©×œ×” ב '{0}'. הקובץ ×ינו תקין +'?שגי×ת נתון בקובץ המקודד '{0}'. ×¡×™×¡×ž× ×©×’×•×™×™×” +'?שגי××” בבדיקת ×¡×›×•× ×”× ×ª×•× ×™× ×‘×§×•×‘×¥ המקודד '{0}'. ×¡×™×¡×ž× ×©×’×•×™×™×” +3800 +הכנס ×¡×™×¡×ž× +הכנס סיסמ×: +הכנס שנית ×ת הסיסמ×: +&הצג ×¡×™×¡×ž× +הסיסמ×ות ×ינן תו×מות +(!, #, $, ...) ליצירת ×”×¡×™×¡×ž× ×”×©×ª×ž×© רק ב×ותיות ×נגליות, ×ž×¡×¤×¨×™× ×ו ×ª×•×•×™× ×ž×™×•×—×“×™× +×”×¡×™×¡×ž× ×רוכה מידי +×¡×™×¡×ž× +3900 +הזמן שעבר: +הזמן שנותר: +גודל כולל: +מהירות: +התקדמות: +יחס כיווץ: +שגי×ות: +×רכיוני×: +4000 +הוסף ל×רכיון +&×רכיון: +מצב &עדכון: +&פורמט ×”×רכיון: +רמת &דחיסה: +&שיטת דחיסה: +גודל מי&לון: +גודל &מילה: +גודל קטע רצוף: +כמות הליכי מעבד/מערכת: +&פרמטרי×: +×פשרויות +צור ×רכיון &חילוץ-עצמי +דחוס ×§×‘×¦×™× ×©×‘×©×™×ª×•×£ +הצפנה +שיטת הצפנה: +&קדד שמות ×§×‘×¦×™× +זיכרון הדרוש לדחיסה: +זיכרון הדרוש לחילוץ: +4050 +×חסון +×”×›×™ מהירה +מהירה +רגילה +מקסימ×לית +×ולטרה +4060 +הוסף והחלף ×§×‘×¦×™× +עדכן והוסף ×§×‘×¦×™× +רענן ×§×‘×¦×™× ×§×™×™×ž×™× +סנכרן ×§×‘×¦×™× +4070 +עיון +כל ×”×§×‘×¦×™× +×œ× ×¨×¦×•×£ +רצוף +6000 +העתק +העבר +העתק ל: +העבר ל: +...מעתיק +...מעביר +...משנה ×©× +בחר תיקיית יעד. +הפעולה ××™× ×” נתמכת עבור תיקייה זו +×ירעה שגי××” בשינוי ×©× ×©×œ קובץ ×ו תיקייה +×שר העתקת קובץ +?×”×× ×ת/×” בטוח/×” שברצונך להעתיק ×§×‘×¦×™× ×œ×רכיון +6100 +×שר מחיקת קובץ +×שר מחיקת תיקייה +×שר מחיקת מספר ×§×‘×¦×™× +?'{0}' ×”×× ×ת/×” בטוח/×” שברצונך למחוק ×ת +?×”×× ×ת/×” בטוח/×” שברצונך למחוק ×ת התיקייה '{0}' ו×ת כל תוכנה +?×”×× ×ת/×” בטוח/×” שברצונך למחוק ×ת {0} ×”×¤×¨×™×˜×™× ×”×לה +...מוחק +×ירעה שגי××” במהלך מחיקת קובץ ×ו תיקייה +המערכת ××™× ×” יכולה להעביר קובץ ×¢× × ×ª×™×‘ ×רוך לפח המחזור +6300 +צור תיקייה +צור קובץ +×©× ×”×ª×™×§×™×™×”: +×©× ×”×§×•×‘×¥: +תיקייה חדשה +קובץ חדש +שגי××” ביצירת תיקייה +שגי××” ביצירת קובץ +6400 +הערה +&הערה: +בחר +בטל בחירה +מיסוך: +6600 +מ××¤×™×™× ×™× +היסטוריית תיקיות +הודעות ×יבחון +הודעה +7100 +מחשב +רשת +×ž×¡×ž×›×™× +מערכת +7200 +הוסף +חלץ +בדוק +העתק +העבר +מחק +מידע +7300 +פצל קובץ +&פצל ל: +פצל לחלקי×, בתי×: +...מפצל +×שר פיצול +? ×”×× ×ת/×” בטוח/×” שברצונך לפצל ×ת הקובץ ל{0} ×—×œ×§×™× +גודל החלק חייב להיות קטן יותר מהקובץ המקורי +גודל חלק שגוי +?×”×× ×ת/×” בטוח/×” שברצונך לפצל ×ת ×”×רכיון ×œ×—×œ×§×™× ×לה\nגודל החלק המוגדר: {0} ×‘×ª×™× +7400 +מזג ×§×‘×¦×™× +&מזג ל: +...מבצע מיזוג +בחר רק ×ת החלק הר×שון של הקובץ המפוצל +×œ× ×”×™×” ניתן ×œ×ž×¦×•× ×§×•×‘×¥ ×©×”×•× ×—×œ×§ מהקובץ המפוצל +×œ× × ×™×ª×Ÿ ×œ×ž×¦×•× ×™×•×ª×¨ מחלק ×חד של הקובץ המפוצל +7500 +...מחשב ×¡×™×›×•× ×‘×™×§×•×¨×ª +תוצ×ות ×¡×™×›×•× ×‘×™×§×•×¨×ª +עבור ×”× ×ª×•× ×™× CRC בדיקת ×¡×™×›×•× ×‘×™×§×•×¨×ª : +עבור ×”× ×ª×•× ×™× ×•×©×ž×•×ª CRC בדיקת ×¡×™×›×•× ×‘×™×§×•×¨×ª : +7600 +מבחן ×‘×™×¦×•×¢×™× +זיכרון בשימוש: +מכווץ +מחלץ +קצב +×¡×™×›×•× ×”×§×¦×‘ +נוכחי +סופי +מש×בי מערכת/מעבד +קצב / שימוש +הצלחות: diff --git a/Utils/7-Zip/Lang/hi.txt b/Utils/7-Zip/Lang/hi.txt new file mode 100644 index 000000000..03a75a199 --- /dev/null +++ b/Utils/7-Zip/Lang/hi.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.07 : Vinayy Sharrma : अनà¥à¤µà¤¾à¤¦ विनय शरà¥à¤®à¤¾ मेहनत की है तो अपना नाम लिखने मे कोई बà¥à¤°à¤¾à¤ˆ तो है नही. हिनà¥à¤¦à¥€ पर गरà¥à¤µ करो, जय हिनà¥à¤¦ ! जय हिनà¥à¤¦à¥€ ! +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Hindi, Indian, हिनà¥à¤¦à¥à¤¸à¥à¤¤à¤¾à¤¨ +हिनà¥à¤¦à¥€ +401 +ठीक है +रदà¥à¤¦ + + + +&हाठ+&नहीं +&बंद करो +मदद + +&जारी रखे +440 +&सभी के लिये हाठ+&सभी के लिये नहीं +रूको +पà¥à¤¨à¤ƒ शà¥à¤°à¥ करें +&पॄषà¥à¤ à¥à¤­à¥‚मि +&अगà¥à¤°à¤­à¥‚मि(डेसà¥à¤•à¥à¤Ÿà¥‹à¤ª) +&विशà¥à¤°à¤¾à¤® +विशà¥à¤°à¤¾à¤®à¤¿à¤¤ +तà¥à¤® रदà¥à¤¦ करना चाहते हो. तà¥à¤®à¥à¤¹à¥‡à¤‚ यकीन है कà¥à¤¯à¤¾? +500 +&फ़ाइल +&संपादन +&दरà¥à¤¶à¤¨ +&मनपसंद +&औजार +&मदद +540 +&खोले +&अंदर खोले +&बाहर खोले +&दृशà¥à¤¯ +&संपादन +&पà¥à¤¨: नामकरण +&में नकल बनाये... +&में ले जायें... +&मिटायें +&फ़ाइल का विभाजन करें... +&फ़ाइल का संयोजन करें... +&संपतà¥à¤¤à¤¿à¤¯à¤¾à¤ या गà¥à¤£ +&टिपà¥à¤ªà¤£à¥€ +&जाà¤à¤š योग की गणना करें +&अनà¥à¤¤à¤° +&फ़ोलà¥à¤¡à¤° तैयार करें +&फ़ाइल तैयार करें +&निरà¥à¤—मन +600 +&सभी चयन करे +&सभी अचयनित करें +&चयन उलटा करें +चयन करें... +अचयन करे... +पà¥à¤°à¤•ार दà¥à¤µà¤¾à¤°à¤¾ चयन +पà¥à¤°à¤•ार दà¥à¤µà¤¾à¤°à¤¾ अचयन +700 +बड़ी पà¥à¤°à¤¤à¥€à¤• +लघॠपà¥à¤°à¤¤à¥€à¤• +&सूची +&वरà¥à¤£à¤¨ +730 +अवितरित +चौड़ा दृशà¥à¤¯ +&२ फ़लक +&औजार पटà¥à¤Ÿà¥€à¤¯à¤¾à¤ +मूल फ़ोलà¥à¤¡à¤° खोले +à¤à¤• सà¥à¤¤à¤° ऊपर चà¥à¥‡ +फ़ोलà¥à¤¡à¤°à¥‹ का इतिहास... +&ताजा करें +750 +संगà¥à¤°à¤¹ उपकरणपटà¥à¤Ÿà¥€ +मानक औजार पटà¥à¤Ÿà¥€ +बड़े खटके(बटन) +खटके(बटन) के शबà¥à¤¦ दिखायें +800 +&फ़ोलà¥à¤¡à¤° मनपसंद में ऎसे जोड़े... +पà¥à¤¸à¥à¤¤à¤šà¤¿à¤¨à¥à¤¹ +900 +&विकलà¥à¤ª... +&बेञà¥à¤šà¤®à¤¾à¤°à¥à¤•(पà¥à¤°à¤¾à¤®à¤¾à¤£à¤¿à¤• तà¥à¤²à¤¨à¤¾) +960 +&सामगà¥à¤°à¥€... +7-जिप के बारे में... +1003 +मारà¥à¤— +नाम +विसà¥à¤¤à¤¾à¤° +फ़ोलà¥à¤¡à¤° +आकार +कà¥à¤² आकार +विशेषता या गà¥à¤£à¤§à¤°à¥à¤® +सरà¥à¤œà¤¿à¤¤ +चलायी गई +परिवरà¥à¤§à¤¿à¤¤ +ठोस +टिपà¥à¤ªà¤£à¥€ +गà¥à¤ªà¥à¤¤à¤¿à¤•ृत +के पूरà¥à¤µ विभाजन(टà¥à¤•डे) करें +के बाद विभाजन(टà¥à¤•डे) करें +शबà¥à¤¦à¤•ोश +सीआरसी +पà¥à¤°à¤•ार +विरोधी +पदà¥à¤§à¤¤à¤¿ +यजमान आजà¥à¤žà¤¾à¤µà¤²à¥€(ओपरेटिंग सिसà¥à¤Ÿà¤®) +फ़ाइल पà¥à¤°à¤£à¤¾à¤²à¥€ +पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾ +समूह +रोक या टà¥à¤•ड़े +पà¥à¤°à¤¤à¤¿à¤•à¥à¤°à¤¿à¤¯à¤¾ +सà¥à¤¥à¤¾à¤¨ +मारà¥à¤— पà¥à¤°à¤¤à¥à¤¯à¤¯ +फोलà¥à¤¡à¤°à¥à¤¸ +फाइलà¥à¤¸ +संसà¥à¤•रण +जतà¥à¤¥à¤¾ +अनेक जतà¥à¤¥à¥‡ +ओफसेट +कडियाठ+टà¥à¤•ड़े +जतà¥à¤¥à¥‡ + +64-बिट +बड़ा-à¤à¤¨à¥à¤¡à¤¿à¤¯à¤¨ +सीपीयू +भौतिक आकार +शीरà¥à¤·à¤•ों के आकार +जाà¤à¤šà¤¯à¥‹à¤— +चरितà¥à¤°à¤¤à¤¾à¤Žà¤‚ +आभासी पता +आईडी +संकà¥à¤·à¤¿à¤ªà¥à¤¤ नाम +सरà¥à¤œà¤• अनà¥à¤ªà¥à¤°à¤¯à¥‹à¤— +सेकà¥à¤Ÿà¤° का आकार +सà¥à¤¥à¤¿à¤¤à¤¿ +कड़ी +तà¥à¤°à¥à¤Ÿà¤¿ +कà¥à¤² आकार +सà¥à¤µà¤¤à¤¨à¥à¤¤à¥à¤° रिकà¥à¤¤à¤¸à¥à¤¥à¤¾à¤¨(खाली जगह) +कà¥à¤²à¤¸à¥à¤Ÿà¤°(समूह) आकार +धà¥à¤¯à¤¾à¤¨à¤¾à¤•रà¥à¤·à¤•(लेबल) +सà¥à¤¥à¤¾à¤¨à¤¿à¤¯ नाम +पà¥à¤°à¤¦à¤¾à¤¯à¤• +2100 +विकलà¥à¤ª +भाषा +भाषा: +संपादक +&संपादक: +&अनà¥à¤¤à¤°: +2200 +पà¥à¤°à¤£à¤¾à¤²à¥€ या तंतà¥à¤° +संबधित करें 7-जिप के साथ: +2301 +7-जिप के शेल (कवच) पà¥à¤°à¤¸à¤‚ग मेनॠमें जोडें +सोपानीकृत(केसà¥à¤•ेडेड) पà¥à¤°à¤¸à¤‚ग मेनॠ+पà¥à¤°à¤¸à¤‚ग(कोनà¥à¤Ÿà¥‡à¤•à¥à¤¸à¥à¤Ÿ) मेनॠवसà¥à¤¤à¥à¤à¤: +2320 +<फोलà¥à¤¡à¤°> +<संगà¥à¤°à¤¹(आरà¥à¤šà¤¿à¤µ)> +संगà¥à¤°à¤¹ खोले +फ़ाइलà¥à¤¸ बाहर निकाले... +संगà¥à¤°à¤¹ में जोड़े... +संगà¥à¤°à¤¹ की जाà¤à¤š करे +यहीं बाहर निकाले +{0} में बाहर निकाले +{0} में जोड़े +दबायें(संकà¥à¤šà¤¨) ओर इमेल करें... +{0} में दबायें ओर ईमेल करें +2400 +फ़ोलà¥à¤¡à¤°à¥à¤¸ +&कारà¥à¤¯à¤°à¤¤ फ़ोलà¥à¤¡à¤° +&पà¥à¤°à¤£à¤¾à¤²à¥€ का असà¥à¤¥à¤¾à¤¯à¥€(टेमà¥à¤ªà¤°à¤°à¥€) फोलà¥à¤¡à¤° +&चालू +&निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ: +सिरà¥à¤« हटाने योगà¥à¤¯(रिमूवेबल) डà¥à¤°à¤¾à¤ˆà¤µ के लिये ही पà¥à¤°à¤¯à¥‹à¤— करें +असà¥à¤¥à¤¾à¤¯à¥€ संगà¥à¤°à¤¹ फाइल के लिये सà¥à¤¥à¤¾à¤¨ निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ करें(बतायें). +2500 +वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤à¤ +दिखाओ ".."वसà¥à¤¤à¥ +वासà¥à¤¤à¤µà¤¿à¤• फ़ाइल पà¥à¤°à¤¤à¤¿à¤®à¤¾à¤¯à¥‡à¤‚ दिखाओ +तंतà¥à¤° का मेनॠदिखाओ +&पूरी पनà¥à¤•à¥à¤¤à¤¿ का चयन +&गà¥à¤°à¤¿à¤¡(जाल) रेखा दिखाओ +वसà¥à¤¤à¥ खोलने के लिये à¤à¤• ही(सिंगल)-कà¥à¤²à¤¿à¤• +&वैकलà¥à¤ªà¤¿à¤• चयन सà¥à¤¥à¤¿à¤¤à¤¿ +&बड़े सà¥à¤®à¥ƒà¤¤à¤¿ पृषà¥à¤  का पà¥à¤°à¤¯à¥‹à¤— करे +2900 +7-जिप के बारे में +7-जिप यह निःशà¥à¤²à¥à¤• सॉफ़à¥à¤Ÿà¤µà¥‡à¤¯à¤° है. फिर भी, आप पंजीकृत(रजिसà¥à¤Ÿà¤°à¥à¤¡) होकर७-ज़िप के विकास में सहयोग कर सकते हैं. +3000 +तंतà¥à¤° जरूरी मातà¥à¤°à¤¾ में मेमोरी(सà¥à¤®à¥ƒà¤¤à¤¿) वितरित नही कर सकता है +इनमे कोई भी तà¥à¤°à¥à¤Ÿà¤¿ नहीं है +{0} चयनित वसà¥à¤¤à¥(à¤à¤) +'{0}' फ़ोलà¥à¤¡à¤° सरà¥à¤œà¤¿à¤¤ नहीं कर सकता +इस संगà¥à¤°à¤¹ के लिये अदà¥à¤¯à¤¤à¤¨à¥€à¤•ृत संचालन समरà¥à¤¥à¤¿à¤¤ नहीं हैं. +'{0}' फाइल को संगà¥à¤°à¤¹ के रूप में नही खोल सकता +'{0}' गà¥à¤ªà¥à¤¤à¤¿à¤•ृत संगà¥à¤°à¤¹ को नही खोल सकता. गलत कूटशबà¥à¤¦? +असमरà¥à¤¥à¤¿à¤¤ संगà¥à¤°à¤¹ पà¥à¤°à¤•ार +फाइल {0} पहले से मौजूद है +'{0}' फ़ाइल परिवरà¥à¤§à¤¿à¤¤ हà¥à¤ˆ है.\nकà¥à¤¯à¤¾ तà¥à¤® संगà¥à¤°à¤¹ में इसे अदà¥à¤¯à¤¤à¤¨à¥€à¤•ृत करना चाहते हो? +फ़ाइल को अदà¥à¤¯à¤¤à¤¨à¥€à¤•ृत नही कर सकता\n'{0}' +संपादक को शà¥à¤°à¥‚ नही कर सकता. +यह फाइल à¤à¤• विषाणà¥(वायरस) जैसी लगती है(फाइल नाम लंबी खाली जगह नाम में रखता है). +जिस फोलà¥à¤¡à¤° का लंबा मारà¥à¤— है उससे सञà¥à¤šà¤¾à¤²à¤¨ कà¥à¤°à¤¿à¤¯à¤¾ नही बà¥à¤²à¤¾à¤ˆ जा सकती. +तà¥à¤®à¥à¤¹à¥‡ à¤à¤• फाइल का चयन तो करना ही होगा +तà¥à¤®à¥à¤¹à¥‡ à¤à¤• या जà¥à¤¯à¤¾à¤¦à¤¾ फाइलों को चà¥à¤¨à¤¨à¤¾ ही होगा +बहà¥à¤¤ जà¥à¤¯à¤¾à¤¦à¤¾ वसà¥à¤¤à¥à¤à¤ +3300 +बाहर निकाल रहा है +संकà¥à¤šà¤¨ कर रहा है +परीकà¥à¤·à¤£ +खोल रहा है... +तलाशी(सà¥à¤•ैनिंग) कर रहा है... +3400 +बाहर निकाले +&बाहर निकाले: +बाहर निकाली हà¥à¤ˆ फ़ाइलों के लिये सà¥à¤¥à¤¾à¤¨ निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ करें. +3410 +मारà¥à¤— सà¥à¤¥à¤¿à¤¤à¤¿ +पूरा मारà¥à¤—नाम +कोई मारà¥à¤— नाम नहीं है +3420 +अधिलेखन रीत +अधिलेखन करने से पहले पूछे +बिना पूछे अधिलेखन(पà¥à¤°à¤¾à¤¨à¥‡ को मिटाना) करें +पहले से मौजूद फ़ाइलस को छोड़े +सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ पà¥à¤¨: नामकरण +पहले से मौजूद फ़ाइलस का सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤(ओटोमेटिक) पà¥à¤¨: नामकरण करे +3500 +फ़ाइल पà¥à¤°à¤¤à¤¿à¤¸à¥à¤¥à¤¾à¤ªà¤¨ को पकà¥à¤•ा करें +गनà¥à¤¤à¤µà¥à¤¯ फोलà¥à¤¡à¤° में पहले से ही पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ हà¥à¤ˆ फ़ाइल है. +कà¥à¤¯à¤¾ आप पहले से मौजूद फ़ाइल को बदलना पसंद करेंगे? +इसके साथ? +{0} बाइटà¥à¤¸ +सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ पà¥à¤¨: नामकरण +3700 +'{0}' के लिठअसहायक दबाने की पदà¥à¤§à¤¤à¤¿. +डेटा तà¥à¤°à¥à¤Ÿà¤¿'{0}' में. फ़ाइल टूटी हà¥à¤ˆ है. +'{0}' में सीआरसी असफल. फ़ाइल टूटी हà¥à¤ˆ है. +'{0}' गà¥à¤ªà¥à¤¤à¤¿à¤•ृत(à¤à¤¨à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿà¥‡à¤¡) फाइल में डेटा तà¥à¤°à¥à¤Ÿà¤¿. गलत कूटशबà¥à¤¦? +'{0}'गà¥à¤ªà¥à¤¤à¤¿à¤•ृत(à¤à¤¨à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿà¥‡à¤¡) फाइल में सीआरसी असफल. गलत कूटशबà¥à¤¦? +3800 +कूटशबà¥à¤¦(पासवरà¥à¤¡) डाले +कूटशबà¥à¤¦(पासवरà¥à¤¡) डाले: +कूटशबà¥à¤¦ पà¥à¤¨à¤ƒ डाले: +&कूटशबà¥à¤¦(पासवरà¥à¤¡) दिखाओ +कूटशबà¥à¤¦ सहेजे हà¥à¤ से अलग है +कूटशबà¥à¤¦ के लिये सिरà¥à¥ž इंगà¥à¤²à¤¿à¤¶ वरà¥à¤£à¤®à¤¾à¤²à¤¾, अंको और विशेष अकà¥à¤·à¤°à¥‹à¤‚ (!, #, $, ...) का ही उपयोग करें +कूटशबà¥à¤¦ बहà¥à¤¤ जà¥à¤¯à¤¾à¤¦à¤¾ बड़ा है +कूटशबà¥à¤¦(पासवरà¥à¤¡) +3900 +वà¥à¤¯à¤¤à¥€à¤¤ समय: +बाकी बचा समय: +कà¥à¤² आकार: +गति: +पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ किया हà¥à¤†: +दबाने(आकार छोटा करने) का अनà¥à¤ªà¤¾à¤¤: +तà¥à¤°à¥à¤Ÿà¤¿à¤¯à¤¾à¤: +संगà¥à¤°à¤¹: +4000 +संगà¥à¤°à¤¹ में जोड़े +&संगà¥à¤°à¤¹: +&अदà¥à¤¯à¤¤à¤¨à¥€à¤•रण सà¥à¤¥à¤¿à¤¤à¤¿(मोड): +संगà¥à¤°à¤¹ &ढाà¤à¤šà¤¾: +&संकà¥à¤šà¤¨ सà¥à¤¤à¤°: +&संकà¥à¤šà¤¨ विधि: +&शबà¥à¤¦à¤•ोश आकार: +&शबà¥à¤¦ आकार: +ठोस टà¥à¤•डे का आकार: +सीपीयू सूतà¥à¤° संखà¥à¤¯à¤¾: +&परिमाप: +विकलà¥à¤ª +&à¤à¤¸à¤à¥žà¤à¤•à¥à¤¸(SFX) संगà¥à¤°à¤¹ तैयार करें +साà¤à¥€ फाइलें संकà¥à¤šà¤¿à¤¤ करें +गà¥à¤ªà¥à¤¤à¤¿à¤•रण +गà¥à¤ªà¥à¤¤à¤¿à¤•रण पदà¥à¤§à¤¤à¤¿: +फ़ाइल &नाम गà¥à¤ªà¥à¤¤à¤¿à¤•रण करें +संकà¥à¤šà¤¨ के लिये सà¥à¤®à¥ƒà¤¤à¤¿ पà¥à¤°à¤¯à¥‹à¤—: +पà¥à¤°à¤¸à¤¾à¤°à¤£ के लिये सà¥à¤®à¥ƒà¤¤à¤¿ पà¥à¤°à¤¯à¥‹à¤—: +4050 +भणà¥à¤¡à¤¾à¤°à¤£ +सरà¥à¤µà¤¾à¤§à¤¿à¤• तेज +तेज +साधारण +अधिकतम +अतà¥à¤¯à¤¨à¥à¤¤ +4060 +फ़ाइलें जोड़े और पà¥à¤°à¤¤à¤¿à¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ करे +फ़ाइले अदà¥à¤¯à¤¤à¤¨à¥€à¤•ृत करें और जोड़े +अवसà¥à¤¥à¤¿à¤¤ फ़ाइलें ताजा करें +फाइलें समकà¥à¤°à¤®à¤£(सिंकà¥à¤°à¥‹à¤¨à¤¾à¤ˆà¥›) करें +4070 +बà¥à¤°à¤¾à¤‰à¤œ या घूमे +सभी फ़ाइलें +अ-ठोस +ठोस +6000 +नकल +ले जायें +में नकल: +में ले जायें: +नकल... +ले जा रहा है... +पà¥à¤¨: नामकरण... +गनà¥à¤¤à¤µà¥à¤¯ फोलà¥à¤¡à¤° चयनित करें. +इस फोलà¥à¤¡à¤° के लिये यह सञà¥à¤šà¤¾à¤²à¤¨ कà¥à¤°à¤¿à¤¯à¤¾ समरà¥à¤¥à¤¿à¤¤ नहीं है. +फ़ाइल या फ़ोलà¥à¤¡à¤° के पà¥à¤¨: नामकरण में तà¥à¤°à¥à¤Ÿà¤¿ +फ़ाइल की नकल करना पकà¥à¤•ा करो +तà¥à¤® संगà¥à¤°à¤¹ में फाइल की पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ करना चाहते हो कà¥à¤¯à¤¾ तà¥à¤®à¥à¤¹à¥‡ यकीन है +6100 +फ़ाइल मिटाये यह पकà¥à¤•ा करो +फ़ोलà¥à¤¡à¤° मिटायें पकà¥à¤•ा करो +अनेक फ़ाइल मिटायें पकà¥à¤•ा करो +कà¥à¤¯à¤¾ तà¥à¤®à¥à¤¹à¥‡ यकीन है कि तà¥à¤® मिटाना चाहते हो '{0}'? +कà¥à¤¯à¤¾ तà¥à¤®à¥à¤¹à¥‡ यकीन है कि तà¥à¤® फ़ोलà¥à¤¡à¤° मिटाना चाहते हो '{0}' और इसकी सब सामगà¥à¤°à¥€ भी? +कà¥à¤¯à¤¾ तà¥à¤®à¥à¤¹à¥‡ यकीन है कि तà¥à¤® मिटाना चाहते हो इन {0} वसà¥à¤¤à¥à¤“ं को? +मिटा रहा है... +फ़ाइल किंवा फ़ोलà¥à¤¡à¤° मिटाने में तà¥à¤°à¥à¤Ÿà¤¿ +तंतà¥à¤° लंबे मारà¥à¤— वाली फाइल को पà¥à¤¨à¤ƒà¤šà¤•à¥à¤°à¤£ पेटी(रिसाईकल बिन) में नही ले जा सकता है. +6300 +फ़ॊलà¥à¤¡à¤° तैयार करें +फ़ाइल तैयार करें +फ़ोलà¥à¤¡à¤° नाम: +फ़ाइल नाम: +नया फ़ॊलà¥à¤¡à¤° +नया फ़ाइल +फ़ोलà¥à¤¡à¤° तैयार करने में तà¥à¤°à¥à¤Ÿà¤¿ +फ़ाइल तैयार करने में तà¥à¤°à¥à¤Ÿà¤¿ +6400 +टिपà¥à¤ªà¤£à¥€ +&टिपà¥à¤ªà¤£à¥€: +चयन +चयन रदà¥à¤¦ +मà¥à¤–ौटा: +6600 +गà¥à¤£ या संपतà¥à¤¤à¤¿à¤¯à¤¾à¤ +फ़ोलà¥à¤¡à¤°à¥‹à¤‚ का इतिहास +निदानातà¥à¤®à¤• संदेश +संदेश +7100 +संगणक +सञà¥à¤œà¤¾à¤² +दसà¥à¤¤à¤¾à¤µà¥‡à¤œ +पà¥à¤°à¤£à¤¾à¤²à¥€ +7200 +जोड़े +बाहर निकाले +परीकà¥à¤·à¤£ +नकल +ले जायें +मिटायें +सूचना +7300 +फ़ाइल का विभाजन करें +&में विभाजन: +जतà¥à¤¥à¥‹à¤‚ में विभाजन, बाइटà¥à¤¸: +विभाजन कर रहा है... +विभाजन करना पकà¥à¤•ा करे +कà¥à¤¯à¤¾ तà¥à¤®à¥à¤¹à¥‡ यकीन है कि तà¥à¤® फाइल को {0} जतà¥à¤¥à¥‹à¤‚ में विभाजित करना चाहते हो? +मूल फाइल के आकार की तà¥à¤²à¤¨à¤¾ में जतà¥à¤¥à¥‡ का आकार छोटा ही होना चाहिठ+जतà¥à¤¥à¥‡ का आकार गलत है +निरà¥à¤¦à¥‡à¤¶à¤¿à¤¤ जतà¥à¤¥à¤¾ आकार: {0} बाइटस.\n आप संगà¥à¤°à¤¹ को ऎसे जतà¥à¤¥à¥‹à¤‚ में विभाजित करना चाहते है, कà¥à¤¯à¤¾ आपको यकीन है? +7400 +फ़ाइले संयोजित करें +&मेंसंयोजन करे: +संयोजन हो रहा है... +विभाजित फाइल का सिरà¥à¥ž पà¥à¤°à¤¥à¤® भाग ही चयनित करे +फाइल को विभाजित फाइल के भाग के रूप में पहचान नही सकता +विभाजित फाइल का à¤à¤• से जà¥à¤¯à¤¾à¤¦à¤¾ भाग नही ढूà¤à¤¢ सकता +7500 +जाà¤à¤šà¤¯à¥‹à¤—(चेकसम) की गणना कर रहा है... +जाà¤à¤šà¤¯à¥‹à¤—(चेकसम) माहिती +सीआरसी जाà¤à¤šà¤¯à¥‹à¤—(चेकसम) आà¤à¤•ड़ों के लिये : +सीआरसी जाà¤à¤šà¤¯à¥‹à¤—(चेकसम) आà¤à¤•ड़ों और नामों के लिये : +7600 +(कसौटी चिनà¥à¤¹)बेञà¥à¤šà¤®à¤¾à¤°à¥à¤• +सà¥à¤®à¥ƒà¤¤à¤¿ उपयोग: +संकà¥à¤šà¤¨ कर रहा है +पà¥à¤°à¤¸à¤¾à¤°à¤£ हो रहा है +कà¥à¤°à¤®à¤¾à¤‚कन +कà¥à¤² कà¥à¤°à¤®à¤¾à¤‚कन +वरà¥à¤¤à¤®à¤¾à¤¨ +परिणाम +सीपीयू उपयोग +कà¥à¤°à¤®à¤¾à¤‚कन / उपयोग +पास: diff --git a/Utils/7-Zip/Lang/hr.txt b/Utils/7-Zip/Lang/hr.txt new file mode 100644 index 000000000..dd61b97a1 --- /dev/null +++ b/Utils/7-Zip/Lang/hr.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 3.12 : Alan Å imek +; 4.53 : Hasan Osmanagić +; 9.07 : +; 15.05 : 2015-06-15 : Stjepan Treger +; +; +; +; +; +; +; +0 +7-Zip +Croatian +Hrvatski +401 +U redu +Odustani + + + +&Da +&Ne +&Zatvori +Pomoć + +Nastavi +440 +Da za &Sve +Ne za Sv&e +&Stani +Ponovi +U pozadini +U prvom planu +&Pauza +Pauzirano +PoniÅ¡titi? +500 +&Datoteke +&UreÄ‘ivanje +&Izgled +Omiljene mape +&Alati +&Pomoć +540 +&Otvori +Ot&vori mapu +Otvori u &sustavu +Iz&gled +&UreÄ‘ivanje +Prei&menuj +&Kopiraj u... +Premje&sti u... +O&briÅ¡i +Podije&li datoteku... +Spo&ji datoteke... +Svojs&tva +Komentar +IzraÄun kontrolnog zbroja +UsporeÄ‘ivanje +Stvo&ri mapu +Stvori &datoteku +&Izlaz +Poveznica +&Alternativni tokovi +600 +Odaberi &sve +PoniÅ¡ti odabir +&Obrni odabir +Odaberi... +PoniÅ¡ti odabir... +Odabir po tipu +PoniÅ¡ti odabir tipa +700 +&Velike ikone +&Male ikone +&Popis +&Detalji +730 +Neso&rtirano +Sadržaj mapa +&2 okna +Alatne &trake +&Korijen +&Nadmapa +ProÅ¡&le mape... +O&svježi +Automatski osvježi +750 +Alatna traka arhiva +Standardna alatna traka +Velike tipke +Prikaži tekst +800 +Dod&aj u popis omiljenih kao +ZabiljeÅ¡ka +900 +&Mogućnosti... +M&jerenje +960 +&7-Zip pomoć... +&O programu... +1003 +Putanja +Prema nazivu +Tip +Mapa +Prema veliÄini +Sažeta veliÄina +Atributi +Kreirano +Pristupano +Prema mijenjanju +Zbijeno +Komentar +Enkripcija +Podjeli prije +Podjeli poslije +RjeÄnik + +Prema tipu +Anti +NaÄin +Glavni OS +Sustav datoteka +Korisnik +Grupa +Zbijeno +Komentar +Pozicija +U datoteci +Mape +Datoteke +InaÄica +Dio +ViÅ¡edijelni +Razmak +Veza +Blokovi +Dijelova + +64-bitno +Big-endian +CPU +FiziÄka veliÄina +VeliÄina zaglavlja +Kontrolni zbroj +Karakteristike +Virtualna adresa +Jedinstvena oznaka +Kratko ime +Aplikacija stvaranja +VeliÄina sektora +NaÄin +Poveznica +GreÅ¡ka +Ukupni kapacitet +Slobodni prostor +VeliÄina klastera +Naziv +Lokalni naziv +Pružatelj +NT sigurnost +Alternativni tok +Dodatno +Obrisano +Je stablo + + +Tip greÅ¡ke +GreÅ¡ke +GreÅ¡ke +Upozorenja +Upozorenje +Tokovi +Alternativni tokovi +VeliÄina alternativnih tokova +Virtualna veliÄina +VeliÄina raspakiranog +Ukupna fiziÄka veliÄina +Indeks dijela +Podtip +Kratki komentar +Kodna stranica + + + +VeliÄina repa +VeliÄina ugraÄ‘enog odsjeÄka +Poveznica +ÄŒvrsta poveznica +iNode + +Samo za Äitanje +2100 +Mogućnosti +Jezik +Jezik: +UreÄ‘ivaÄ +&Program za ureÄ‘ivanje: +Program za usporeÄ‘ivanje: +2200 +Sustav +Poveži 7-Zip sa: +Svi korisnici +2301 +Integriraj 7-Zip u padajući kontekstni izbornik +Padajući kontekstni izbornik +Stavke kontekstnog izbornika: +Ikone kontekstnog izbornika +2320 + + +Otvori arhiv +Raspakiraj datoteke... +Dodaj u arhiv... +Testiraj arhiv +Raspakiraj ovdje +Raspakiraj u {0} +Dodaj u {0} +Sažimanje i slanje e-poÅ¡tom +Sažimanje u {0} i slanje e-poÅ¡tom +2400 +Mape +&Radna mapa +&Privremena sistemska mapa +&Trenutna +&Navedena: +Koristi samo za izmjenjive diskove +Lokaciju za smjeÅ¡taj privremenih datoteka. +2500 +Postavke +Nadmapa ".." +Prikaži prave ikone datoteka +Prikaži sistemski izbornik +&OznaÄi cijeli redak +Prikaži &linije mreže +Jedan klik za otvaranje stavke +Drugi n&aÄin oznaÄavanja +Koristi raspo&loživu memoriju +2900 +O 7-Zip-u +7-Zip je besplatan softver. +3000 +Sustav ne može pripremiti potrebnu koliÄinu memorije +Nema greÅ¡aka +{0} objekt(a) izabrano +Ne mogu kreirati mapu '{0}' +Obnova nije podržana za ovaj arhiv. +Nemoguće otvoriti datoteku '{0}' kao arhiv +Nemoguće otvoriti kriptiranu arhivu '{0}'. Kriva lozinka? +Tip arhive nije podržan +Datoteka {0} već postoji +Datoteka '{0}' je izmijenjena.\nObnoviti arhiv? +Nije moguće obnoviti datoteku\n'{0}' +Nije moguće zapoÄeti ureÄ‘ivanje. +Datoteka izgleda kao virus (naziv datoteke sadrži dugaÄke razmake). +Operaciju nemoguće pozvati iz mape koja ima dugaÄku putanju. +Morate obilježiti jednu datoteku +Morate obilježiti jednu ili viÅ¡e datoteka +PreviÅ¡e stavki +Nemoguće otvoriti datoteku kao {0} arhivu +Datoteka je otvorena kao {0} arhiva +Arhiva je otvorena sa pomakom +3300 +Raspakiranje +Sažimanje u arhiv +Testiranje +Otvaranje u tijeku... +Skeniram... +Uklanjanje +3320 +Dodavanje +Ažuriranje +Analiziranje +Replikiranje +Repakiranje +Preskakanje +Brisanje +Stvaranje zaglavlja +3400 +Raspakiraj +&Raspakiraj u: +Lokacija za raspakiranje datoteka. +3410 +Putanja mapa: +Potpune putanje +Bez putanja +Apsolutne putanje +Relativne putanje +3420 +NatpiÅ¡i postojeće +Pitaj prije natpisivanja postojećeg +NatpiÅ¡i postojeće bez upozorenja +PreskoÄi postojeće datoteke +Automatska promjena naziva +Automatska promjena naziva postojećih datoteka +3430 +Eliminiraj dupliranje korijenske mape +Vrati sigurnost datoteka +3500 +Potvrdite zamjenu datoteka +Ciljana mapa već sadrži datoteku koja se trenutno obraÄ‘uje. +Zamijeniti postojeću +datoteku s ovom? +{0} bajte +A&utomatska promjena naziva +3700 +Nije podržan naÄin sažimanja za '{0}'. +Podatkovna greÅ¡ka u '{0}'. Datoteka je neispravna. +CRC greÅ¡ka u '{0}'. Datoteka je neispravna. +GreÅ¡ka u kriptiranoj datoteci '{0}'. Kriva lozinka? +CRC greÅ¡ka u kriptiranoj datoteci '{0}'. Kriva lozinka? +3710 +Kriva lozinka? +3721 +Nepodržana metoda kompresije +GreÅ¡ka podataka +CRC neuspjeÅ¡an +Nedostupni podaci +NeoÄekivan kraj podataka +Postoji joÅ¡ podataka nakon glavnih podataka +Nije arhiva +GreÅ¡ka zaglavlja +Kriva lozinka +3763 +NeoÄekivan poÄetak arhive +NepotvrÄ‘en poÄetak arhive + + + +Nepodržano svojstvo +3800 +Unesite lozinku +Unesite lozinku: +Ponovite lozinku: +&Prikaži lozinku +Lozinka nije jednaka +Koristite samo engleska slova, brojeve i specijalne znake (!, #, $, ...) za lozinku +Lozinka je preduga +&Lozinka +3900 +UtroÅ¡eno vrijeme: +Preostalo vrijeme: +VeliÄina: +Brzina: +ObraÄ‘eno: +Omjer kompresije: +GreÅ¡ke: +Arhive: +4000 +Dodaj u arhiv +&Arhiv: +&NaÄin obnove: +&Format arhiva: +Stupanj sažimanja +&Metoda sažimanja: +VeliÄina &rjeÄnika: +&VeliÄina rijeÄi: +VeliÄina bloka u zbijenom: +Broj niti u CPU: +&Parametri: +&Mogućnosti +Kreiraj E&XE arhiv +Sažmi dijeljene datoteke +Kriptiranje +Metoda kriptiranja: +Enkripcija naziva datoteka +KoriÅ¡tenje memorije za sažimanje: +KoriÅ¡tenje memorije za raspakiranje: +ObriÅ¡i datoteke nakon kompresije +4040 +Spremi simboliÄne poveznice +Spremi Ävrste poveznice +Spremi alternativne tokove podataka +Spremi sigurnost datoteka +4050 +Bez sažimanja +Vrlo brzo +Brzo +UobiÄajeno +NajjaÄe +Ultra +4060 +Dodaj i zamjeni datoteke +Obnovi i dodaj datoteke +Osvježi postojeće datoteke +Sinkroniziraj datoteke +4070 +Traži +Sve datoteke +Bez-zbijanja +Zbijeno +6000 +Kopiraj +Premjesti +Kopiraj u: +Premjesti u: +Kopiranje u tijeku... +PremjeÅ¡tanje u tijeku... +Preimenovanje u tijeku... +Odabir odrediÅ¡ne mape. +Operacija nije podržana. +GreÅ¡ka pri preimenovanju datoteke ili mape +Potvrdite kopiranje datoteka +Kopiranje datoteka u arhiv? +6100 +Potvrdite brisanje datoteke +Potvrdite brisanje mape +Potvrdite viÅ¡estruko brisanje datoteka +Obrisati '{0}'? +Obrisati mapu '{0}' i sav njezin sadržaj? +Obrisati ove {0} podatke? +Brisanje u tijeku... +GreÅ¡ka pri brisanju datoteke ili mape +Sustav ne može premjestiti datoteku sa dugaÄkom putanjom u Kantu za recikliranje +6300 +Kreiraj mapu +Kreiraj datoteku +Naziv mape: +Naziv datoteke: +Nova mapa +Nova datoteka +GreÅ¡ka pri kreiranju mape +GreÅ¡ka pri kreiranju datoteka +6400 +Komentar +&Komentar: +Odaberi +PoniÅ¡ti odabir +Maska: +6600 +Svojstva +Kronologija mapa +DijagnostiÄke poruke +Poruka +7100 +RaÄunalo +Mreža +Dokumenti +Sustav +7200 +Dodaj +Raspakiraj +Testiraj +Kopiraj +Premjesti +ObriÅ¡i +Svojstva +7300 +Podijeli datoteku +&Podijeli u: +Razdvajanje na dijelove, bajta: +Dioba... +Potvrdite diobu +Podijeliti datoteku u {0} dijelova? +VeliÄina diobenog dijela mora biti manja od izvorne datoteke +NetoÄna veliÄina bloka +VeliÄina bloka: {0} bajtova.\nJeste li sigurni da želite podijeliti u takve dijelove? +7400 +Spoji datoteke +&Spoji u: +Spajanje... +OznaÄite samo prvu datoteku +Nije pronaÄ‘ena datoteka koja je dio razdijeljenih datoteka +PronaÄ‘ena samo jedna datoteka od razdijeljenih datoteka +7500 +IzraÄunavanje kontrolnog zbroja... +Info kontrolni zbroj +CRC kontrolni zbroj za podatke: +CRC kontrolni zbroj za podatke i nazive: +7600 +Mjerenje +KoriÅ¡tenje memorije: +Sažimanje +Raspakiranje +Ocjena +Ukupna ocjena +Trenutno +Rezultat +CPU zauzeće +Ocjena / Zauzeće +Prolazi: +7700 +Poveznica +Poveznica +Veza od: +Veza do: +7710 +Tip poveznice +ÄŒvrsta poveznica +SimboliÄna poveznica datoteka +SimboliÄna poveznica mapa +Mapa ÄvoriÅ¡ta diff --git a/Utils/7-Zip/Lang/hu.txt b/Utils/7-Zip/Lang/hu.txt new file mode 100644 index 000000000..b7b40ba94 --- /dev/null +++ b/Utils/7-Zip/Lang/hu.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; : Jozsef Tamas Herczeg +; 9.16 : Nyilas MISY +; +; +; +; +; +; +; +; +; +0 +7-Zip +Hungarian +Magyar +401 +OK +Mégsem + + + +&Igen +&Nem +&Bezárás +Súgó + +&Folytatás +440 +I&gen, mindre +N&em, mindre +Leállítás +Újraindítás +&Háttérben +&ElÅ‘térben +&Szünet +Szünet +Biztos, hogy megszakítja a folyamatot? +500 +&Fájl +S&zerkesztés +&Nézet +Ked&vencek +&Eszközök +&Súgó +540 +M&egnyitás +Megnyitás &belül +Megnyitás kí&vül +&Nézet +S&zerkesztés +Ãtn&evezés +Más&olás mappába... +Ãt&helyezés mappába... +&Törlés +Fájl&darabolás... +Fájl&egyesítés... +T&ulajdonságok +&Megjegyzés +Checksum számolása +Különbség +Mappa létrehozása +Fájl létrehozása +&Kilépés +600 +Min&d kijelölése +Kijelölés megszüntetése +Kijelölés &megfordítása +Kijelölés... +Megszüntetés... +Kijelölés típus alapján +Megszüntetés típus alapján +700 +&Nagy ikonok +&Kis ikonok +&Lista +&Részletek +730 +Rendezetlen +Lapos ikonok +&2 panel +&Eszköztárak +Gyökérmappa megnyitása +Egy szinttel feljebb +Mappa elÅ‘zmények... +&Frissítés +750 +Archiválás eszköztár +Szokásos eszköztár +Nagy gombok +Szövegcímkék megjelenítése +800 +Mappa &hozzáadása a Kedvencekhez mint +KönyvjelzÅ‘ +900 +&Beállítások... +&Teljesítménymérés +960 +&Tartalomjegyzék... +7-Zip &névjegye... +1003 +Útvonal +Név +Kiterjesztés +Mappa +Méret +Tömörített méret +Attribútumok +Létrehozva +Hozzáférés +Módosítva +Tömör +Megjegyzés +Titkosított +Feldarabolás elÅ‘tt +Feldarabolás után +Szótár +CRC +Típus +Anti +Módszer +Gazda OS +Fájlrendszer +Felhasználó +Csoport +Blokk +Megjegyzés +Pozíció +Útvonal elÅ‘tag +Mappák +Fájlok +Verzió +Kötet +Többkötet +Eltolás +Linkek +Blokkok +Kötetek + +64-bit +Big-endian +CPU +Fizikai méret +Fejlécek mérete +Checksum +Karakterisztika +Virtuális cím +ID +Név rendezése +Alkalmazás készítÅ‘je +Szakasz mérete +Mód +Link +Hiba +Teljes méret +Szabad terület +Kluszterméret +Címke +Helyi név +Szolgáltató +2100 +Beállítások +Nyelv +Nyelv: +SzerkesztÅ‘ +&SzerkesztÅ‘: +&Különbség: +2200 +Rendszer +7-Zip társítása: +2301 +7-Zip hozzáadása a parancsértelmezÅ‘ helyi menüjéhez +LépcsÅ‘zetes helyi menü +Helyi menü elemek: +2320 + + +Archívum megnyitása +Fájlok kibontása... +Hozzáad az archívumhoz... +Archívum tesztelése +Kibontás ide +Kibontás ide: {0} +Hozzáadás: {0} +Tömörítés és küldés... +Tömörítés {0} archívumba és küldés +2400 +Mappák +&Munkamappa +A &rendszer ideiglenes mappája +&Jelenlegi +&Meghatározott: +Csak cserélhetÅ‘ meghajtókhoz +Válassza ki az ideiglenes archív fájlok mappáját. +2500 +Beállítások +A ".." elem látható +Az eredeti fájlikonok láthatók +Látható a rendszermenü +&Teljes soros kijelölés +&Rácsvonalak kijelzése +Elem megnyitása egyszeres kattintással +&Alternativ kiválasztási mód +&Nagy memória haszálata +2900 +A 7-Zip névjegye +A 7-Zip ingyenes szoftver. Ha elnyerte a tetszését, s mégis szeretné támogatni a további fejlesztését, regisztrálja 20 USD áron, fizethet hitelkártyával vagy más módon. +3000 +A rendszer nem tudja lefoglalni a szükséges memóriát +Az archívum hibamentes +{0} objektum kijelölve +A(z) '{0}' mappát nem lehet létrehozni +Az aktualizálás ennél az archívumtípusnál nem támogatott. +A(z) '{0}' fájl nem nyitható meg archívként +A(z) '{0}' titkosított archívum nem megnyitható. Hibás a jelszó? +Nem támogatott archívum típus +A(z) {0} fájl már létezik +'{0}'\nA fájl tartalma megváltozott.\nKívánja aktualizálni az archívumban? +A fájl nem aktualizálható:\n'{0}' +A szerkesztÅ‘ nem indítható. +A fájl vírusnak néz ki (a fájlnév hosszú szóközt tartalmaz a nevében). +A művelet nem hívható meg abból a mappából amelynek hosszú az elérési útvonala. +Egy fájlt ki kell jelölnie! +Egy vagy több fájlt ki kell jelölnie +Túl sok elem +3300 +Kibontás +Tömörítés +Tesztelés +Megnyitás... +Vizsgálat... +3400 +Kibontás +&Kibontás ide: +Határozza meg a kibontott fájlok tárolómappáját. +3410 +Útvonal +Teljes útvonal +Nincs útvonal +3420 +Felülírás +Rákérdezés felülírás elÅ‘tt +Felülírás rákérdezés nélkül +A létezÅ‘ fájlok kihagyása +Automatikus átnevezés +A létezÅ‘ fájlok automatikus átnevezése +3500 +Fájlcsere megerÅ‘sítése +A célmappa már tartalmazza a feldolgozott fájlt. +Kívánja lecserélni a létezÅ‘ fájlt +ezzel a fájllal? +{0} bájt +A&utomatikus átnevezés +3700 +A(z) '{0}' tömörítési módja nem támogatott. +Adathiba a következÅ‘ben: '{0}'. A fájl sérült. +CRC-hiba a következÅ‘ben: '{0}'. A fájl sérült. +Adathiba a titkosított fájlban: '{0}'. Hibás a jelszó? +CRC-hiba a titkosított fájlban: '{0}'. Hibás a jelszó? +3800 +Jelszó beírása +Ãrja be a jelszót: +Jelszó újbóli beírása: +A &jelszó megjelenítése +Jelszavak nem egyeznek +Csak ékezetmentes karaktereket, számokat és speciális karaktereket (!, #, $, ...) használjon a jelszavaknak +A jelszó túl hosszú +Jelszó +3900 +Eltelt idÅ‘: +HátralévÅ‘ idÅ‘: +Méret: +Sebesség: +Feldolgozott: +Tömörítési arány: +Hibák: +Archívumok: +4000 +Behelyezés archívumba +&Archívum: +Akt&ualizálás módja: +Arcíhvum &formátuma: +Tömörítés &foka: +Tömörítés &módja: +&Címtár mérete: +&Szó mérete: +Tömör blokk méret: +CPU számok: +&Tulajdonságok: +Beállítások +SF&X archívum létrehozása +Megosztott fájlok tömörítése +Titkosítás +Titkosítási eljárás: +Fájlnevek &titkosítása +A tömörítés memóriahasználata: +A kitömörítés memóriahasználata: +4050 +Raktár +Leggyorsabb +Gyors +Normál +Legnagyobb +Ultra +4060 +Fájlok behelyezése és cseréje +Fájlok aktualizálása és behelyezése +LétezÅ‘ fájlok frissítése +Fájlok szinkronizálása +4070 +Tallózás +Minden fájl +Nem tömör +Tömör +6000 +Másolás +Ãthelyezés +Másolás ide: +Ãthelyezés ide: +Másolás... +Ãthelyezés... +Ãtnevezés... +Válassza ki a cél mappát. +A művelet nem támogatott. +Hiba történt a fájl vagy a mappa átnevezésekor +Fájl másolásának megerÅ‘sítése +Biztos, hogy fájl(oka)t akar másolni az archívumba? +6100 +Fájltörlés megerÅ‘sítése +Mappatörlés megerÅ‘sítése +Több fájltörlés megerÅ‘sítése +Biztos, hogy törölni akarja a következÅ‘t: '{0}'? +Biztos, hogy törölni akarja a(z) '{0}' mappát és annak teljes tartalmát? +Biztos, hogy törölni akarja ezt a(z) {0} elemet? +Törlés... +Hiba történt a fájl vagy a mappa törlésekor +A rendszer nembírja mozgatni a fájlt a hosszú útvonallal a Lomtárba +6300 +Mappa létrehozása +Fájl létrehozása +Mappa neve: +Fájlnév: +Új mappa +Új fájl +Hiba történt a mappa létrehozásakor +Hiba történt a fájl létrehozásakor +6400 +Megjegyzés +&Megjegyzés: +Kijelölés +Megszüntetés +Maszk: +6600 +Tulajdonságok +Mappa elÅ‘zmények +Diagnosztikai üzenetek +Üzenet +7100 +Sajátgép +Hálózati helyek +Dokumentumok +Rendszer +7200 +Hozzáadás +Kibontás +Teszt +Másolás +Ãthelyezés +Törlés +Tulajdonságok +7300 +Fájldarabolás +&Darabolás ide: +Darabolás &kötetekre, bájt: +Darabolás... +Darabolás megerÅ‘sítése +Biztos szét akarja darabolni a fájlt {0} kötetre? +Kötet méretének kissebbnek kell lennie, mint az eredeti fájl mérete +Hibás kötet méret +A megadott kötet mérete: {0} byte.\nBiztos fel akarja darabolni az archívumot ilyen kötetre? +7400 +Fájlegyesítés +&Egyesítés ide: +Egyesítés... +Csak az elsÅ‘ rész kiválasztása a darabolt fájlból +Nem ismeri fel a fájlt, mint darabolt fájl része +Nem talál egynél több részt a darabolt fájlból +7500 +Leírás számolása... +Leírás információ +CRC leírás az adathoz: +CRC leírás az adathoz és névhez: +7600 +Teljesítménymérés +Memóriahasználat: +Tömörítés +Kitömörítés +Értékelés +Összértékelés +Jelenlegi +Eredmény +CPU használata +Becslés / Használat +Menetek: diff --git a/Utils/7-Zip/Lang/hy.txt b/Utils/7-Zip/Lang/hy.txt new file mode 100644 index 000000000..343d0284a --- /dev/null +++ b/Utils/7-Zip/Lang/hy.txt @@ -0,0 +1,501 @@ +;!@Lang2@!UTF-8! +; : Gevorg Papikyan +; 15.00 : Hrant Ohanyan : http://haysoft.org +; +; +; +; +; +; +; +; +; +0 +7-Zip +Armenian +Õ€Õ¡ÕµÕ¥Ö€Õ¥Õ¶ +401 +Ô¼Ô±ÕŽ +Õ‰Õ¥Õ²Õ¡Ö€Õ¯Õ¥Õ¬ + + + +&Ô±ÕµÕ¸ +&ÕˆÕ¹ +&Õ“Õ¡Õ¯Õ¥Õ¬ +Õ•Õ£Õ¶Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ + +&Õ‡Õ¡Ö€Õ¸Ö‚Õ¶Õ¡Õ¯Õ¥Õ¬ +440 +Ô±ÕµÕ¸ Õ¢Õ¸Õ¬Õ¸Ö€Õ« &Õ°Õ¡Õ´Õ¡Ö€ +ÕˆÕ¹ Õ¢Õ¸Õ¬Õ¸Ö€Õ« &Õ°Õ¡Õ´Õ¡Ö€ +Ô¿Õ¡Õ¶Õ£Õ¶Õ¥ÖÕ¶Õ¥Õ¬ +ÕŽÕ¥Ö€Õ½Õ¯Õ½Õ¥Õ¬ +&Ô½Õ¸Ö€Õ¡ÕºÕ¡Õ¿Õ¯Õ¥Ö€Õ¸Ö‚Õ´ +&Ô±Õ¼Õ»Ö‡Õ¸Ö‚Õ´ +&Ô´Õ¡Õ¤Õ¡Ö€ +Ô´Õ¡Õ¤Õ¡Ö€Õ« Õ´Õ¥Õ» Õ§ +Ô¸Õ¶Õ¤Õ°Õ¡Õ¿Õ¥ÕžÕ¬ +500 +&Õ–Õ¡ÕµÕ¬ +&Ô½Õ´Õ¢Õ¡Õ£Ö€Õ¥Õ¬ +&ÕÕ¥Õ½Ö„ +&Ô¸Õ¶Õ¿Ö€ÕµÕ¡Õ¬Õ¶Õ¥Ö€ +Ô³&Õ¸Ö€Õ®Õ«Ö„Õ¶Õ¥Ö€ +&Õ•Õ£Õ¶Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ +540 +&Ô²Õ¡ÖÕ¥Õ¬ +Ô²Õ¡ÖÕ¥Õ¬ &Õ¶Õ¥Ö€Õ½Õ¸Ö‚Õ´ +Ô²Õ¡ÖÕ¥Õ¬ Õ¤Ö€Õ½&Õ¸Ö‚Õ´ +Ô¸Õ¶Õ¿Ö€Õ¥Õ¬ +&Ô½Õ´Õ¢Õ¡Õ£Ö€Õ¥Õ¬ +ÕŽÕ¥Ö€&Õ¡Õ¶Õ¾Õ¡Õ¶Õ¥Õ¬ +&ÕŠÕ¡Õ¿Õ³Õ¥Õ¶Õ¥Õ¬... +&ÕÕ¥Õ²Õ¡ÖƒÕ¸Õ­Õ¥Õ¬... +&Õ‹Õ¶Õ»Õ¥Õ¬ +ÕÖ€Õ¸Õ°Õ¥Õ¬& Ö†Õ¡ÕµÕ¬Õ¨... +Õ€&Õ¡Õ´Õ¡Õ¯ÖÕ¥Õ¬ Ö†Õ¡ÕµÕ¬Õ¨... +Õ€Õ¡Õ¿Õ¯Õ¸Ö‚&Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ +Õ„Õ¥Õ¯Õ¶Õ¡Õ¢Õ¡&Õ¶Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ +Õ€Õ¡Õ¶Ö€Õ¡Õ£Õ¸Ö‚Õ´Õ¡Ö€ +Õ€Õ¡Õ´Õ¥Õ´Õ¡Õ¿Õ¥Õ¬ +&ÕÕ¿Õ¥Õ²Õ®Õ¥Õ¬ Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯ +ÕÕ¿Õ¥Õ²&Õ®Õ¥Õ¬ Ö†Õ¡ÕµÕ¬ +Õ“Õ¡Õ¯&Õ¥Õ¬ +Õ€Õ²Õ¸Ö‚Õ´ +&Ô±ÕµÕ¬Õ¨Õ¶Õ¿Ö€Õ¡Õ¶Ö„Õ¡ÕµÕ«Õ¶ Õ°Õ¸Õ½Ö„Õ¥Ö€ +600 +Õ†Õ·Õ¥Õ¬ &Õ¢Õ¸Õ¬Õ¸Ö€Õ¨ +Ô±ÕºÕ¡Õ¶Õ·Õ¥Õ¬ +&ÔµÕ¿Õ¡Ö€Õ¯Õ¥Õ¬ Õ¶&Õ·Õ¸Ö‚Õ´Õ¨ +Õ†Õ·Õ¥Õ¬... +Ô±ÕºÕ¡Õ¶Õ·Õ¥Õ¬... +Õ†Õ·Õ¥Õ¬ Õ¨Õ½Õ¿ Õ¿Õ¥Õ½Õ¡Õ¯Õ« +Ô±ÕºÕ¡Õ¶Õ·Õ¥Õ¬ Õ¨Õ½Õ¿ Õ¿Õ¥Õ½Õ¡Õ¯Õ« +700 +&Õ„Õ¥Õ® ÕºÕ¡Õ¿Õ¯Õ¥Ö€Õ¡Õ¯Õ¶Õ¥Ö€Õ¸Õ¾ +&Õ“Õ¸Ö„Ö€ ÕºÕ¡Õ¿Õ¯Õ¥Ö€Õ¡Õ¯Õ¶Õ¥Ö€Õ¸Õ¾ +Õ‘Õ¸Ö‚Ö&Õ¡Õ¯ +&Ô±Õ²ÕµÕ¸Ö‚Õ½Õ¡Õ¯ +730 +Ô±Õ¼Õ¡Õ¶Ö Õ¤Õ¡Õ½Õ¡Õ¾Õ¸Ö€Õ¥Õ¬Õ¸Ö‚ +Õ€Õ¡Ö€Õ© Õ¥Õ²Õ¡Õ¶Õ¡Õ¯ +&2 Õ¾Õ¡Õ°Õ¡Õ¶Õ¡Õ¯ +&Ô³Õ¸Ö€Õ®Õ«Ö„Õ¶Õ¥Ö€Õ« Õ¾Õ¡Õ°Õ¡Õ¶Õ¡Õ¯ +Ô²Õ¡ÖÕ¥Õ¬ Õ¡Ö€Õ´Õ¡Õ¿Õ¡ÕµÕ«Õ¶ Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ¨ +Õ„Õ¥Õ¯ Õ´Õ¡Õ¯Õ¡Ö€Õ¤Õ¡Õ¯ Õ¾Õ¥Ö€Ö‡ +Ô¹Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ¶Õ¥Ö€Õ« ÕºÕ¡Õ¿Õ´Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨... +Ô¹&Õ¡Ö€Õ´Õ¡ÖÕ¶Õ¥Õ¬ +Ô»Õ¶Ö„Õ¶Õ¡Õ©Õ¡Ö€Õ´Õ¡ÖÕ¸Ö‚Õ´ +750 +Ô¾Ö€Õ¡Õ£Ö€Õ« Õ¯Õ¸Õ³Õ¡Õ¯Õ¶Õ¥Ö€Õ« Õ¾Õ¡Õ°Õ¡Õ¶Õ¡Õ¯ +Ô¿Õ¸Õ³Õ¡Õ¯Õ¶Õ¥Ö€Õ« Õ½Õ¿Õ¡Õ¶Õ¤Õ¡Ö€Õ¿ Õ¾Õ¡Õ°Õ¡Õ¶Õ¡Õ¯ +Õ„Õ¥Õ® Õ¯Õ¸Õ³Õ¡Õ¯Õ¶Õ¥Ö€Õ¸Õ¾ +Ô¿Õ¸Õ³Õ¡Õ¯Õ¶Õ¥Ö€Õ« Õ¡Õ¶Õ¸Ö‚Õ¶Õ¨ +800 +Ô±Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬ Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ¨ &Õ¨Õ¶Õ¿Ö€ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ«Õ¶ Õ¸Ö€ÕºÕ¥Õ½ +Ô·Õ»Õ¡Õ¶Õ«Õ· +900 +Ô¿Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€... +Ô±Ö€Õ¿Õ¡Õ¤Ö€Õ¸Õ²Õ¡Õ¯Õ¡Õ¶Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ©Õ¥Õ½Õ¿ +960 +&Ô²Õ¸Õ¾Õ¡Õ¶Õ¤Õ¡Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶... +Ô¾Ö€Õ¡Õ£Ö€Õ« &Õ´Õ¡Õ½Õ«Õ¶... +1003 +ÕˆÖ‚Õ²Õ« +Ô±Õ¶Õ¸Ö‚Õ¶ +Ô¸Õ¶Õ¤Õ¬Õ¡ÕµÕ¶Õ¸Ö‚Õ´ +Ô¹Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯ +Õ‰Õ¡Öƒ +ÕÕ¥Õ²Õ´Õ¡Õ® +Õ€Õ¡Õ¿Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ +ÕÕ¿Õ¥Õ²Õ®Õ¾Õ¥Õ¬ Õ§ +Õ„Õ¸Ö‚Õ¿Ö„ +Õ“Õ¸ÖƒÕ¸Õ­Õ¾Õ¥Õ¬ Õ§ +Ô±Õ¶Õ¨Õ¶Õ¤Õ°Õ¡Õ¿ +Õ„Õ¥Õ¯Õ¶Õ¡Õ¢Õ¡Õ¶Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ +Ô¿Õ¸Õ¤Õ¡Õ¾Õ¸Ö€Õ¾Õ¡Õ® Õ§ +ÕÖ€Õ¸Õ°Õ¾Õ¡Õ® Õ§ Õ´Õ«Õ¶Õ¹ +ÕÖ€Õ¸Õ°Õ¾Õ¡Õ® Õ§ Õ°Õ¥Õ¿Õ¸ +Ô²Õ¡Õ¼Õ¡Ö€Õ¡Õ¶ + +ÕÕ¥Õ½Õ¡Õ¯ +Õ€Õ¡Õ¯Õ¡ +Õ„Õ¥Õ©Õ¸Õ¤ +Õ€Õ¡Õ´Õ¡Õ¯Õ¡Ö€Õ£ +Õ–Õ¡ÕµÕ¬Õ¡ÕµÕ«Õ¶ Õ°Õ¡Õ´Õ¡Õ¯Õ¡Ö€Õ£ +Õ•Õ£Õ¿Õ¾Õ¸Õ² +Ô½Õ¸Ö‚Õ´Õ¢ +Ô¿Õ¸Õ²ÕºÕ¸Ö‚Õ´ +Õ„Õ¥Õ¯Õ¶Õ¡Õ¢Õ¡Õ¶Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ +Ô´Õ«Ö€Ö„ +ÕˆÖ‚Õ²Õ« +Ô¹Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ¶Õ¥Ö€ +Õ–Õ¡ÕµÕ¬Õ¥Ö€ +ÕÕ¡Ö€Õ¢Õ¥Ö€Õ¡Õ¯ +Õ€Õ¡Õ¿Õ¸Ö€ +Ô²Õ¡Õ¦Õ´Õ¡Õ°Õ¡Õ¿Õ¸Ö€ +Õ‡Õ¥Õ²Õ¸Ö‚Õ´ +Õ€Õ²Õ¸Ö‚Õ´Õ¶Õ¥Ö€ +Õ€Õ¡Õ¿Õ¾Õ¡Õ®Õ¶Õ¥Ö€ +Õ€Õ¡Õ¿Õ¸Ö€Õ¶Õ¥Ö€ + + + +CPU +Õ–Õ«Õ¦Õ«Õ¯Õ¡Õ¯Õ¡Õ¶ Õ¹Õ¡ÖƒÕ¨ +Ô³Õ¬Õ­Õ¡Õ£Ö€Õ¥Ö€Õ« Õ¹Õ¡ÖƒÕ¨ +Ô±Ö€Õ¤ÕµÕ¸Ö‚Õ¶Ö„Õ¨ +Ô²Õ¶Õ¸Ö‚Õ©Õ¡Õ£Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ +ÕŽÕ«Ö€Õ¿Õ¸Ö‚Õ¡Õ¬ Õ°Õ¡Õ½ÖÕ¥ + +Ô¿Õ¡Ö€Õ³ Õ¡Õ¶Õ¸Ö‚Õ¶ +ÕÕ¿Õ¥Õ²Õ®Õ¸Õ² +Õ€Õ¡Õ¿Õ¾Õ¡Õ®Õ« Õ¹Õ¡Öƒ +ÔµÕ²Õ¡Õ¶Õ¡Õ¯ +Õ†Õ·Õ¡Õ¶Õ¡ÕµÕ«Õ¶ Õ°Õ²Õ¸Ö‚Õ´ +ÕÕ­Õ¡Õ¬ +Ô¾Õ¡Õ¾Õ¡Õ¬ +Ô±Õ¦Õ¡Õ¿ Õ§ +Ô¿Õ¬Õ¡Õ½Õ¿Õ¥Ö€Õ« Õ¹Õ¡Öƒ +ÕÕ¡Õ¼ +ÕÕ¥Õ²Õ¡ÕµÕ«Õ¶ Õ¡Õ¶Õ¸Ö‚Õ¶ +Õ„Õ¡Õ¿Õ¡Õ¯Õ¡Ö€Õ¡Ö€ +Ô±Õ¶Õ¾Õ¿Õ¡Õ¶Õ£Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ +Ô±ÕµÕ¬Õ¨Õ¶Õ¿Ö€Õ¡Õ¶Ö„Õ¡ÕµÕ«Õ¶ Õ°Õ¸Õ½Ö„ + +Õ€Õ¥Õ¼Õ¡Õ¯Õ¡ +Ô¾Õ¡Õ¼ + + +ÕÕ­Õ¡Õ¬Õ« Õ¿Õ¥Õ½Õ¡Õ¯ +ÕÕ­Õ¡Õ¬Õ¶Õ¥Ö€ +ÕÕ­Õ¡Õ¬Õ¶Õ¥Ö€ +Ô¶Õ£Õ¸Ö‚Õ·Õ¡ÖÕ¸Ö‚Õ´ +Ô¶Õ£Õ¸Ö‚Õ·Õ¡ÖÕ¸Ö‚Õ´Õ¶Õ¥Ö€ +Õ€Õ¸Õ½Ö„Õ¥Ö€ +Ô±ÕµÕ¬Õ¨Õ¶Õ¿Ö€Õ¡Õ¶Ö„Õ¡ÕµÕ«Õ¶ Õ°Õ¸Õ½Ö„Õ¥Ö€ +Ô±ÕµÕ¬Õ¨Õ¶Õ¿Ö€Õ¡Õ¶Ö„Õ¡ÕµÕ«Õ¶ Õ°Õ¸Õ½Ö„Õ¥Ö€Õ« Õ¹Õ¡Öƒ +ÕŽÕ«Ö€Õ¿Õ¸Ö‚Õ¡Õ¬ Õ¹Õ¡ÖƒÕ¨ +Ô²Õ¡ÖÕ¾Õ¡Õ® Õ¹Õ¡ÖƒÕ¨ +Ô¸Õ¶Õ¤Õ°Õ¡Õ¶Õ¸Ö‚Ö€ Ö†Õ«Õ¦Õ«Õ¯Õ¡Õ¯Õ¡Õ¶ Õ¹Õ¡ÖƒÕ¨ +Õ€Õ¡Õ¿Õ¸Ö€Õ« Õ°Õ¡Õ´Õ¡Ö€Õ¨ +ÔµÕ¶Õ©Õ¡Õ¿Õ¥Õ½Õ¡Õ¯ +Ô¿Õ¡Ö€Õ³ Õ´Õ¥Õ¯Õ¶Õ¡Õ¢Õ¡Õ¶Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ +Ô¿Õ¸Õ¤Õ¡ÕµÕ«Õ¶ Õ§Õ» + + + +Õ„Õ¶Õ¡ÖÕ¸Ö€Õ¤Õ« Õ¹Õ¡ÖƒÕ¨ +Õ†Õ¥Ö€Õ¯Õ¡Õ¼Õ¸Ö‚ÖÕ¾Õ¡Õ® Õ°Õ¡Õ¿Õ¾Õ¡Õ®Õ« Õ¹Õ¡ÖƒÕ¨ +Õ€Õ²Õ¸Ö‚Õ´ +Ô¿Õ¸Õ·Õ¿ Õ°Õ²Õ¸Ö‚Õ´ +iNode + +Õ„Õ«Õ¡ÕµÕ¶ Õ¯Õ¡Ö€Õ¤Õ¡Õ¬Õ¸Ö‚ + + + + + + +2100 +Ô¿Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€ +Ô¼Õ¥Õ¦Õ¸Ö‚Õ¶ +Ô¼Õ¥Õ¦Õ¸Ö‚Õ¶. +Ô½Õ´Õ¢Õ¡Õ£Õ«Ö€ +&Ô½Õ´Õ¢Õ¡Õ£Õ«Ö€ +&Õ€Õ¡Õ´Õ¥Õ´Õ¡Õ¿Õ¥Õ¬Õ¸Ö‚ Õ®Ö€Õ¡Õ£Õ«Ö€. +2200 +Õ€Õ¡Õ´Õ¡Õ¯Õ¡Ö€Õ£Õ¨ +Ô±Õ½Õ¸ÖÕ«Õ¡ÖÕ¶Õ¥Õ¬ 7-Zip-Õ¨ Õ°Õ¥Õ¿Ö‡ÕµÕ¡Õ¬ Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ« Õ°Õ¥Õ¿Õ +Ô²Õ¸Õ¬Õ¸Ö€ Ö…Õ£Õ¿Õ¾Õ¸Õ²Õ¶Õ¥Ö€Õ¨ +2301 +Õ†Õ¥Ö€Õ¤Õ¶Õ¥Õ¬ 7-Zip-Õ¨ Õ°Õ¡Õ´Õ¡Õ¿Õ¥Ö„Õ½Õ¿Õ¡ÕµÕ«Õ¶ ÖÕ¡Õ¶Õ¯Õ¸Ö‚Õ´ +Ô¿Õ¡Õ½Õ¯Õ¡Õ¤Õ¡ÕµÕ«Õ¶ ÖÕ¡Õ¶Õ¯ +Õ€Õ¡Õ´Õ¡Õ¿Õ¥Ö„Õ½Õ¿Õ¡ÕµÕ«Õ¶ ÖÕ¡Õ¶Õ¯Õ« Õ¢Õ¡Õ²Õ¡Õ¤Ö€Õ«Õ¹Õ¶Õ¥Ö€Õ¨ +ÕŠÕ¡Õ¿Õ¯Õ¥Ö€Õ¡Õ¯Õ¶Õ¥Ö€ Õ°Õ¡Õ´Õ¡Õ¿Õ¥Ö„Õ½Õ¿Õ¡ÕµÕ«Õ¶ ÖÕ¡Õ¶Õ¯Õ¸Ö‚Õ´ +2320 +«Թղթապանակ» +«Արխիվ» +Ô²Õ¡ÖÕ¥Õ¬ Õ¡Ö€Õ­Õ«Õ¾Õ¨ +Ô´Õ¸Ö‚Ö€Õ½ Õ¢Õ¥Ö€Õ¥Õ¬ +Ô±Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬ Õ¡Ö€Õ­Õ«Õ¾Õ«... +Ô¹Õ¥Õ½Õ¿Õ¡Õ¾Õ¸Ö€Õ¥Õ¬ +Ô´Õ¸Ö‚Ö€Õ½ Õ¢Õ¥Ö€Õ¥Õ¬ Õ¡ÕµÕ½Õ¿Õ¥Õ² +Ô´Õ¸Ö‚Ö€Õ½ Õ¢Õ¥Ö€Õ¥Õ¬{0}-Õ¸Ö‚Õ´ +Ô±Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬ {0}-Õ«Õ¶ +ÕÕ¥Õ²Õ´Õ¥Õ¬ Ö‡ Õ¸Ö‚Õ²Õ¡Ö€Õ¯Õ¥Õ¬ Õ§Õ¬. ÖƒÕ¸Õ½Õ¿Õ¸Õ¾... +ÕÕ¥Õ²Õ´Õ¥Õ¬ {0}-Õ¸Ö‚Õ´ Ö‡ Õ¸Ö‚Õ²Õ¡Ö€Õ¯Õ¥Õ¬ Õ§Õ¬. ÖƒÕ¸Õ½Õ¿Õ¸Õ¾ +2400 +Ô¹Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ¶Õ¥Ö€ +&Ô±Õ·Õ­Õ¡Õ¿Õ¡Õ¶Ö„Õ¡ÕµÕ«Õ¶ Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯ +&Õ€Õ¡Õ´Õ¡Õ¯Õ¡Ö€Õ£Õ¡ÕµÕ«Õ¶ ÕªÕ¡Õ´Õ¡Õ¶Õ¡Õ¯Õ¡Õ¾Õ¸Ö€ Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯ +&Ô¸Õ¶Õ©Õ¡ÖÕ«Õ¯ +&Õ†Õ·Õ¡Õ¶Õ¡Õ¯Õ¥Õ¬. +Õ•Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¥Õ¬ Õ´Õ«Õ¡ÕµÕ¶ Õ·Õ¡Ö€ÕªÕ¡Õ¯Õ¡Õ¶ Õ¯Ö€Õ«Õ¹Õ¶Õ¥Ö€Õ« Õ°Õ¡Õ´Õ¡Ö€ +Õ†Õ·Õ¥Ö„ ÕªÕ¡Õ´Õ¡Õ¶Õ¡Õ¯Õ¡Õ¾Õ¸Ö€ Õ¡Ö€Õ­Õ«Õ¾Õ¶Õ¥Ö€Õ« Õ°Õ¡Õ´Õ¡Ö€ Õ¿Õ¥Õ² +2500 +Ô¿Õ¡Ö€Õ£Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´Õ¶Õ¥Ö€ +Õ‘Õ¸Ö‚ÖÕ¡Õ¤Ö€Õ¥Õ¬ Õ¢Õ¡Õ²Õ¡Õ¤Ö€Õ«Õ¹Õ¨ ".." +Õ‘Õ¸Ö‚ÖÕ¡Õ¤Ö€Õ¥Õ¬ Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ« Õ«Ö€Õ¡Õ¯Õ¡Õ¶ ÕºÕ¡Õ¿Õ¯Õ¥Ö€Õ¶Õ¥Ö€Õ¨ +Õ‘Õ¸Ö‚ÖÕ¡Õ¤Ö€Õ¥Õ¬ Õ°Õ¡Õ´Õ¡Õ¯Õ¡Ö€Õ£Õ¡ÕµÕ«Õ¶ ÖÕ¡Õ¶Õ¯Õ¨ +Ô¿Õ¸Ö‚Ö€Õ½Õ¸Ö€Õ¨ Õ¡Õ´Õ¢Õ¸Õ²Õ» Õ¿Õ¸Õ²Õ¸Õ¾ +Õ‘Õ¸Ö‚ÖÕ¡Õ¤Ö€Õ¥Õ¬ Õ¢Õ¡ÕªÕ¡Õ¶Õ¡Ö€Õ¡Ö€Õ¶Õ¥Ö€ +Ô²Õ¡ÖÕ¥Õ¬ Õ´Õ¥Õ¯ Õ½Õ¥Õ²Õ´Õ¡Õ´Õ¢ +Õ†Õ·Õ¥Õ¬Õ¸Ö‚ Õ¡ÕµÕ¬Õ¨Õ¶Õ¿Ö€Õ¡Õ¶Ö„Õ¡ÕµÕ«Õ¶ Õ¥Õ²Õ¡Õ¶Õ¡Õ¯ +Õ•Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¥Õ¬ Õ°Õ«Õ·Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ´Õ¥Õ® Õ®Õ¡Õ¾Õ¡Õ¬Õ¶Õ¥Ö€ +2900 +7-Zip-Õ« Õ´Õ¡Õ½Õ«Õ¶ +7-Zip-Õ¨ Õ¡Õ¦Õ¡Õ¿ Õ¿Õ¡Ö€Õ¡Õ®Õ¾Õ¸Õ² Õ®Ö€Õ¡Õ£Õ«Ö€ Õ§: +3000 +Õ‰Õ¯Õ¡ Õ¡Õ¦Õ¡Õ¿ Õ¿Õ¥Õ² +ÕÕ­Õ¡Õ¬Õ¶Õ¥Ö€ Õ¹Õ¯Õ¡Õ¶ +Ô¸Õ¶Õ¿Ö€Õ¾Õ¡Õ® Õ¥Õ¶ {0} Ö†Õ¡ÕµÕ¬Õ¥Ö€ +Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ§ Õ½Õ¿Õ¥Õ²Õ®Õ¥Õ¬ '{0}' Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ¨: +Õ“Õ¸ÖƒÕ¸Õ­Õ´Õ¡Õ¶ Õ£Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ¡ÕµÕ½ Õ¡Ö€Õ­Õ«Õ¾Õ¨ Õ¹Õ« Õ¡Õ»Õ¡Õ¯ÖÕ¸Ö‚Õ´: +Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ§ Õ¢Õ¡ÖÕ¥Õ¬ '{0}' Ö†Õ¡ÕµÕ¬Õ¨ Õ¸Ö€ÕºÕ¥Õ½ Õ¡Ö€Õ­Õ«Õ¾ +Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ§ Õ¢Õ¡ÖÕ¥Õ¬ '{0}' Õ¯Õ¸Õ²ÖƒÕ¡Õ® Õ¡Ö€Õ­Õ«Õ¾Õ¨: Õ„Õ¸Ö‚Õ¿Ö„Õ¡Õ£Ö€Õ¥Ö„ Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨: +Ô±Ö€Õ­Õ«Õ¾Õ« Õ¿Õ¥Õ½Õ¡Õ¯Õ¨ Õ¹Õ« Õ¡Õ»Õ¡Õ¯ÖÕ¾Õ¸Ö‚Õ´ +{0} Ö†Õ¡ÕµÕ¬Õ¨ Õ¡Ö€Õ¤Õ¥Õ¶ Õ£Õ¸ÕµÕ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ Õ¸Ö‚Õ¶Õ« +'{0}' Ö†Õ¡ÕµÕ¬Õ¨ ÖƒÕ¸ÖƒÕ¸Õ­Õ¾Õ¥Õ¬ Õ§:\n Ô¹Õ¡Ö€Õ´Õ¡ÖÕ¶Õ¥ÕžÕ¬ Õ¡ÕµÕ¶ Õ¡Ö€Õ­Õ«Õ¾Õ¸Ö‚Õ´: +Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ§ Õ©Õ¡Ö€Õ´Õ¡ÖÕ¶Õ¥Õ¬ Ö†Õ¡ÕµÕ¬Õ¨ \n'{0}' +Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ§ Õ¢Õ¡ÖÕ¥Õ¬ Õ­Õ´Õ¢Õ¡Õ£Õ«Ö€Õ¨: +Õ–Õ¡ÕµÕ¬Õ¨ Õ¶Õ´Õ¡Õ¶ Õ§ Õ¾Õ«Ö€Õ¸Ö‚Õ½Õ«: +Ô³Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ¹Õ« Õ¯Õ¡Ö€Õ¸Õ² Õ¯Õ¡Õ¿Õ¡Ö€Õ¾Õ¥Õ¬ Õ¡ÕµÕ¶ Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ«Ö, Õ¸Ö€Õ¨ Õ¸Ö‚Õ¶Õ« Õ¥Ö€Õ¯Õ¡Ö€ Õ³Õ¡Õ¶Õ¡ÕºÕ¡Ö€Õ°: +Ô¸Õ¶Õ¿Ö€Õ¥Ö„ Õ£Õ¸Õ¶Õ¥ Õ´Õ¥Õ¯ Ö†Õ¡ÕµÕ¬ +Ô¸Õ¶Õ¿Ö€Õ¥Ö„ Õ£Õ¸Õ¶Õ¥ Õ´Õ¥Õ¯ Õ¯Õ¡Õ´ Õ´Õ« Ö„Õ¡Õ¶Õ« Ö†Õ¡ÕµÕ¬Õ¥Ö€ +Ô²Õ¡Õ²Õ¡Õ¤Ö€Õ«Õ¹Õ¶Õ¥Ö€Õ¨ Õ·Õ¡Õ¿ Õ¥Õ¶ +Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ¥Õ²Õ¡Õ¾ Õ¢Õ¡ÖÕ¥Õ¬ Ö†Õ¡ÕµÕ¬Õ¨ Õ¸Ö€ÕºÕ¥Õ½ {0} Õ¡Ö€Õ­Õ«Õ¾ +Õ–Õ¡ÕµÕ¬Õ¨ Õ¢Õ¡ÖÕ¾Õ¥Õ¬ Õ§ Õ¸Ö€ÕºÕ¥Õ½ {0} Õ¡Ö€Õ­Õ«Õ¾ +Õ–Õ¡ÕµÕ¬Õ¨ Õ¢Õ¡ÖÕ¾Õ¥Õ¬ Õ§ Õ·Õ¥Õ²Õ¸Ö‚Õ´Õ¸Õ¾ +3300 +Ô´Õ¸Ö‚Ö€Õ½ Õ§ Õ¢Õ¥Ö€Õ¾Õ¸Ö‚Õ´ +ÕÕ¥Õ²Õ´Õ¾Õ¸Ö‚Õ´ Õ§ +Ô¹Õ¥Õ½Õ¿Õ¡Õ¾Õ¸Ö€Õ¾Õ¸Ö‚Õ´ Õ§ +Ô²Õ¡ÖÕ¾Õ¸Ö‚Õ´ Õ§... +ÕÕ¥Õ½Õ¡Õ®Ö€Õ¸Ö‚Õ´... +Õ‹Õ¶Õ»Õ¸Ö‚Õ´ +3320 +Ô±Õ¾Õ¥Õ¬Õ¡ÖÕ¾Õ¸Ö‚Õ´ Õ§ +Ô¹Õ¡Ö€Õ´Õ¡ÖÕ¾Õ¸Ö‚Õ´ Õ§ +ÕŽÕ¥Ö€Õ¬Õ¸Ö‚Õ®Õ¸Ö‚Õ´ +ÕŠÕ¡Õ¿Õ³Õ¥Õ¶Õ¾Õ¸Ö‚Õ´ Õ§ +ÕŽÕ¥Ö€Õ¡ÖƒÕ¡Õ©Õ¥Õ©Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´ +Ô²Õ¡Ö Õ§ Õ©Õ¸Õ²Õ¶Õ¾Õ¸Ö‚Õ´ +Õ‹Õ¶Õ»Õ¸Ö‚Õ´ +Ô³Õ¬Õ­Õ¡Õ£Ö€Õ¥Ö€Õ« Õ½Õ¿Õ¥Õ²Õ®Õ¸Ö‚Õ´ +3400 +Ô´Õ¸Ö‚Ö€Õ½ Õ¢Õ¥Ö€Õ¥Õ¬ +&Ô´Õ¸Ö‚Ö€Õ½ Õ¢Õ¥Ö€Õ¥Õ¬Õ +Õ†Õ·Õ¥Ö„ Õ°Õ¡Õ¶Õ¾Õ¸Õ² Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ« Õ¿Õ¥Õ²Õ¡Õ¤Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨: +3410 +Õ–Õ¡ÕµÕ¬Õ¥Ö€Õ« Õ¸Ö‚Õ²Õ«Õ¶. +Ô±Õ´Õ¢Õ¸Õ²Õ»Õ¡Õ¯Õ¡Õ¶& Õ¸Ö‚Õ²Õ«Õ¶Õ¥Ö€ +&Ô±Õ¼Õ¡Õ¶Ö Õ¸Ö‚Õ²Õ«Õ¶Õ¥Ö€Õ« +Ô²Õ¡ÖÕ¡Ö€Õ±Õ¡Õ¯ Õ¸Ö‚Õ²Õ«Õ¶Õ¥Ö€ +Õ€Õ¡Ö€Õ¡Õ¢Õ¥Ö€Õ¡Õ¯Õ¡Õ¶ Õ¸Ö‚Õ²Õ«Õ¶Õ¥Ö€ +3420 +ÕŽÕ¥Ö€Õ¡Õ£Ö€Õ¡Õ¶ÖÕ¸Ö‚Õ´ +Õ€Õ¡Õ½Õ¿Õ¡Õ¿Õ¸Ö‚Õ´Õ¸Õ¾ +Ô±Õ¼Õ¡Õ¶Ö Õ°Õ¡Õ½Õ¿Õ¡Õ¿Õ´Õ¡Õ¶ +Ô²Õ¡Ö Õ©Õ¸Õ²Õ¶Õ¥Õ¬ +ÕŽÕ¥Ö€Õ¡Õ¶Õ¾Õ¡Õ¶Õ¥Õ¬ +ÕŽÕ¥Ö€Õ¡Õ¶Õ¾Õ¡Õ¶Õ¥Õ¬ Õ£Õ¸ÕµÕ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ Õ¸Ö‚Õ¶Õ¥ÖÕ¸Õ²Õ¶Õ¥Ö€Õ¨ +3430 +ÕŽÕ¥Ö€Õ¡ÖÕ¶Õ¥Õ¬ Õ¡Ö€Õ´Õ¡Õ¿Õ¡ÕµÕ«Õ¶ Õ©Õ²Õ©. Õ¯Ö€Õ¯Õ¶Ö…Ö€Õ«Õ¶Õ¡Õ¯Õ¸Ö‚Õ´Õ¨ +ÕŽÕ¥Ö€Õ¡ÖÕ¶Õ¥Õ¬ Õ´Õ¸Ö‚Õ¿Ö„Õ« Õ©Õ¸Ö‚ÕµÕ¬Õ¿Õ¾Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ +3500 +Õ€Õ¡Õ½Õ¿Õ¡Õ¿Õ¥Ö„ Ö†Õ¡ÕµÕ¬Õ« ÖƒÕ¸Õ­Õ¡Ö€Õ«Õ¶Õ¸Ö‚Õ´Õ¨ +Ô¹Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ¸Ö‚Õ´ Õ¡Ö€Õ¤Õ¥Õ¶ Õ¯Õ¡ Õ´Õ·Õ¡Õ¯Õ¾Õ¡Õ® Ö†Õ¡ÕµÕ¬: +Õ“Õ¸Õ­Õ¡Ö€Õ«Õ¶Õ¥ÕžÕ¬ Õ¡Õ¼Õ¯Õ¡ Ö†Õ¡ÕµÕ¬Õ¨ +Õ¿Õ¾ÕµÕ¡Õ¬ Ö†Õ¡ÕµÕ¬Õ¸Õ¾: +{0} Õ¢Õ¡ÕµÕ© +ÕŽÕ¥Ö€Õ¡Õ¶Õ¾Õ¡Õ¶Õ¥Õ¬ Õ«Õ¶Ö„. +3700 +'{0}' Ö†Õ¡ÕµÕ¬Õ¨ Õ½Õ¥Õ²Õ´Õ¥Õ¬Õ¸Ö‚ Õ¥Õ²Õ¡Õ¶Õ¡Õ¯Õ¨ Õ¹Õ« Õ¡Õ»Õ¡Õ¯ÖÕ¾Õ¸Ö‚Õ´ +ÕÕ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ½Õ­Õ¡Õ¬'{0}'-Õ¸Ö‚Õ´: Õ–Õ¡ÕµÕ¬Õ¨ Õ¾Õ¶Õ¡Õ½Õ¾Õ¡Õ® Õ§: +CRC Õ½Õ­Õ¡Õ¬'{0}'-Õ¸Ö‚Õ´: Õ–Õ¡ÕµÕ¬Õ¨ Õ¾Õ¶Õ¡Õ½Õ¾Õ¡Õ® Õ§: +ÕÕ­Õ¡Õ¬Õ '{0}' Ö†Õ¡ÕµÕ¬Õ« Õ¯Õ¸Õ¤Õ¡Õ¾Õ¸Ö€Õ´Õ¡Õ¶ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¸Ö‚Õ´: Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨ Õ½Õ­Õ¡ÕžÕ¬ Õ§: +CRC Õ½Õ­Õ¡Õ¬ '{0}' Õ¯Õ¸Õ¤Õ¡Õ¾Õ¸Ö€Õ¾Õ¡Õ® Ö†Õ¡ÕµÕ¬Õ« Õ°Õ¡Õ´Õ¡Ö€: Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨ Õ½Õ­Õ¡ÕžÕ¬ Õ§: +3710 +Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨ Õ½Õ­Õ¡ÕžÕ¬ Õ§ +3721 +ÕÕ¥Õ²Õ´Õ¥Õ¬Õ¸Ö‚ Õ¹Õ¡Õ»Õ¡Õ¯ÖÕ¾Õ¸Õ² Õ¥Õ²Õ¡Õ¶Õ¡Õ¯ +ÕÕ­Õ¡Õ¬ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ¸Ö‚Õ´ +CRC Õ½Õ­Õ¡Õ¬ +Ô±Õ¶Õ°Õ¡Õ½Õ¡Õ¶Õ¥Õ¬Õ« Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€ +ÕÕ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ¡Õ¶Õ½ÕºÕ¡Õ½Õ¥Õ¬Õ« Õ¡Õ¾Õ¡Ö€Õ¿ +Ô¿Õ¡Õ¶ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ Ö…Õ£Õ¿Õ¡Õ¯Õ¡Ö€ Õ¿Õ¾ÕµÕ¡Õ¬Õ¶Õ¥Ö€Õ« Õ°Õ¡Õ¿Õ¾Õ¡Õ®Õ« Õ¾Õ¥Ö€Õ»Õ¸Ö‚Õ´ +Ô±Ö€Õ­Õ«Õ¾ Õ¹Õ§ +ÕÕ­Õ¡Õ¬ Õ£Õ¬Õ­Õ¡Õ£Ö€Õ¥Ö€Õ¸Ö‚Õ´ +Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨ Õ½Õ­Õ¡Õ¬ Õ§ +3763 +Ô±Ö€Õ­Õ«Õ¾Õ« Õ¡Õ¶Õ¨Õ¶Õ¤Õ¸Ö‚Õ¶Õ¥Õ¬Õ« Õ½Õ¯Õ«Õ¦Õ¢ +Ô±Ö€Õ­Õ«Õ¾Õ« Õ¹Õ°Õ¡Õ½Õ¿Õ¡Õ¿Õ¾Õ¡Õ® Õ½Õ¯Õ«Õ¦Õ¢ + + + +Õ‰Õ¡Õ»Õ¡Õ¯ÖÕ¾Õ¸Õ² ÕµÕ¸Ö‚Ö€Õ¡Õ°Õ¡Õ¿Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ +3800 +Õ„Õ¸Ö‚Õ¿Ö„Õ¡Õ£Ö€Õ¥Õ¬ Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼ +&Õ„Õ¸Ö‚Õ¿Ö„Õ¡Õ£Ö€Õ¥Õ¬ Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼. +Ô¿Ö€Õ¯Õ¶Õ¥Õ¬ Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨. +&Õ‘Õ¸Ö‚ÖÕ¡Õ¤Ö€Õ¥Õ¬ Õ£Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨ +Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨ Õ¹Õ« Õ°Õ¡Õ´Õ¡ÕºÕ¡Õ¿Õ¡Õ½Õ­Õ¡Õ¶Õ¸Ö‚Õ´ +Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ« Õ°Õ¡Õ´Õ¡Ö€ Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¥Ö„ Õ´Õ«Õ¡ÕµÕ¶ Õ¬Õ¡Õ¿Õ«Õ¶Õ¥Ö€Õ¥Õ¶ Õ¿Õ¡Õ¼Õ¥Ö€, Õ©Õ¾Õ¥Ö€ Ö‡ Õ°Õ¡Õ¿Õ¸Ö‚Õ¯ Õ¶Õ·Õ¡Õ¶Õ¶Õ¥Ö€ (!, #, $, ...) +Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨ Õ¹Õ¡ÖƒÕ¡Õ¦Õ¡Õ¶Ö Õ¥Ö€Õ¯Õ¡Ö€ Õ§ +&Ô³Õ¡Õ²Õ¿Õ¶Õ¡Õ¢Õ¡Õ¼Õ¨. +3900 +Ô±Õ¶ÖÕ¥Õ¬ Õ§. +Õ„Õ¶Õ¡ÖÕ¥Õ¬ Õ§. +Õ‰Õ¡Öƒ. +Ô±Ö€Õ¡Õ£Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶. +Ô¸Õ¶Õ©Õ¡ÖÖ„. +ÕÕ¥Õ²Õ´Õ¥Õ¬Õ¸Ö‚ Õ¡Õ½Õ¿Õ«Õ³Õ¡Õ¶ +ÕÕ­Õ¡Õ¬Õ¶Õ¥Ö€. +Ô±Ö€Õ­Õ«Õ¾Õ¶Õ¥Ö€. +4000 +Ô±Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬ Õ¡Ö€Õ­Õ«Õ¾Õ« +&Ô±Ö€Õ­Õ«Õ¾. +&Õ“Õ¸ÖƒÕ¸Õ­Õ´Õ¡Õ¶ Õ¯Õ¡Ö€Õ£Õ¨. +&Ô±Ö€Õ­Õ«Õ¾Õ« Õ¿Õ¥Õ½Õ¡Õ¯Õ¨. +&ÕÕ¥Õ²Õ´Õ¥Õ¬Õ¸Ö‚ Õ¡Õ½Õ¿Õ«Õ³Õ¡Õ¶Õ¨. +&ÕÕ¥Õ²Õ´Õ¥Õ¬Õ¸Ö‚ Õ´Õ¥Õ©Õ¸Õ¤Õ¨. +Ô²Õ¡Õ¼Õ¡Ö€Õ¡Õ¶Õ« &Õ¹Õ¡ÖƒÕ¨. +Ô²Õ¡Õ¼Õ« Õ¹&Õ¡ÖƒÕ¨. +Õ€Õ¡Õ¿Õ¾Õ¡Õ®Õ« Õ¹Õ¡ÖƒÕ¨. +CPU Õ°Õ¸Õ½Ö„Õ¥Ö€Õ« Ö„Õ¡Õ¶Õ¡Õ¯Õ¨. +&Õ‘Õ¸Ö‚ÖÕ«Õ¹Õ¶Õ¥Ö€. +&Ô¸Õ¶Õ¿Ö€Õ¡Õ¶Ö„Õ¶Õ¥Ö€ +ÕÕ¿Õ¥Õ²Õ®Õ¥Õ¬ SF&X Õ¡Ö€Õ­Õ«Õ¾ +ÕÕ¥Õ²Õ´Õ¥Õ¬ Õ°Õ¡Õ´Õ¡Ö…Õ£Õ¿Õ¡Õ£Õ¸Ö€Õ®Õ¾Õ¸Õ² Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ¨ +Ô¿Õ¸Õ¤Õ¡Õ¾Õ¸Ö€Õ¸Ö‚Õ´ +Ô¿Õ¸Õ¤Õ¡Õ¾Õ¸Ö€Õ´Õ¡Õ¶ Õ¥Õ²Õ¡Õ¶Õ¡Õ¯. +&Ô¿Õ¸Õ¤Õ¡Õ¾Õ¸Ö€Õ¥Õ¬ Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ« Õ¡Õ¶Õ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¨ +Õ€Õ«Õ·Õ¸Õ². Õ®Õ¡Õ¾Õ¡Õ¬Õ¨ Õ½Õ¥Õ²Õ´Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€. +Õ€Õ«Õ·Õ¸Õ². Õ®Õ¡Õ¾Õ¡Õ¬Õ¨ Õ¤Õ¸Ö‚Ö€Õ½ Õ¢Õ¥Ö€Õ¥Õ¬Õ¸Ö‚ Õ°Õ¡Õ´Õ¡Ö€. +ÕÕ¥Õ²Õ´Õ¥Õ¬Õ¸Ö‚Ö Õ°Õ¥Õ¿Õ¸ Õ»Õ¶Õ»Õ¥Õ¬ Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ¨ +4040 +ÕŠÕ¡Õ°ÕºÕ¡Õ¶Õ¥Õ¬ Õ¶Õ·Õ¡Õ¶Õ¡ÕµÕ«Õ¶ Õ°Õ²Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨ +ÕŠÕ¡Õ°ÕºÕ¡Õ¶Õ¥Õ¬ Õ¯Õ¸Õ·Õ¿ Õ°Õ²Õ¸Ö‚Õ´Õ¶Õ¥Ö€Õ¨ +ÕŠÕ¡Õ°ÕºÕ¡Õ¶Õ¥Õ¬ Õ¡ÕµÕ¬Õ¨Õ¶Õ¿Ö€Õ¡Õ¶Ö„Õ¡ÕµÕ«Õ¶ Õ°Õ¸Õ½Ö„Õ¥Ö€Õ¨ +ÕŠÕ¡Õ°ÕºÕ¡Õ¶Õ¥Õ¬ Õ´Õ¸Ö‚Õ¿Ö„Õ« Õ«Ö€Õ¡Õ¾Õ¸Ö‚Õ¶Ö„Õ¨ +4050 +Ô±Õ¼Õ¡Õ¶Ö Õ½Õ¥Õ²Õ´Õ¥Õ¬Õ¸Ö‚ +Ô±Ö€Õ¡Õ£Õ¨Õ¶Õ©Õ¡Ö +Ô±Ö€Õ¡Õ£ +Õ†Õ¸Ö€Õ´Õ¡Õ¬ +Ô±Õ¼Õ¡Õ¾Õ¥Õ¬Õ¡Õ£Õ¸Ö‚ÕµÕ¶ +ÕˆÖ‚Õ¬Õ¿Ö€Õ¡ +4060 +Ô±Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬ Ö‡ ÖƒÕ¸Õ­Õ¡Ö€Õ«Õ¶Õ¥Õ¬ +Ô¹Õ¡Ö€Õ´Õ¡ÖÕ¶Õ¥Õ¬ Ö‡ Õ¡Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬ +Ô¹Õ¡Ö€Õ´Õ¡ÖÕ¶Õ¥Õ¬ +Õ€Õ¡Õ´Õ¡ÕªÕ¡Õ´Õ¥ÖÕ¶Õ¥Õ¬ +4070 +Ô¹Õ¥Ö€Õ©Õ¥Õ¬ +Ô²Õ¸Õ¬Õ¸Ö€ Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ¨ +Õ–Õ¡ÕµÕ¬Õ« Õ¹Õ¡ÖƒÕ¸Õ¾ +Ô±Õ¶Õ¤Õ¡Õ¤Õ¡Ö€ +6000 +ÕŠÕ¡Õ¿Õ³Õ¥Õ¶Õ¥Õ¬ +ÕÕ¥Õ²Õ¡ÖƒÕ¸Õ­Õ¥Õ¬ +ÕŠÕ¡Õ¿Õ³Õ¥Õ¶Õ¥Õ¬Õ +ÕÕ¥Õ²Õ¡ÖƒÕ¸Õ­Õ¥Õ¬Õ +ÕŠÕ¡Õ¿Õ³Õ¥Õ¶Õ¾Õ¸Ö‚Õ´ Õ§... +ÕÕ¥Õ²Õ¡ÖƒÕ¸Õ­Õ¾Õ¸Ö‚Õ´ Õ§... +ÕŽÕ¥Ö€Õ¡Õ¶Õ¾Õ¡Õ¶Õ¾Õ¸Ö‚Õ´ Õ§... +Ô¸Õ¶Õ¿Ö€Õ¥Õ¬ Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯ +Ô³Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ Õ¹Õ« Õ¡Õ»Õ¡Õ¯ÖÕ¾Õ¸Ö‚Õ´ +ÕÕ­Õ¡Õ¬` Ö†Õ¡ÕµÕ¬Õ¨ Õ¯Õ¡Õ´ Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ¨ Õ¡Õ¶Õ¾Õ¡Õ¶Õ¡ÖƒÕ¸Õ­Õ¥Õ¬Õ«Õ½: +Õ€Õ¡Õ½Õ¿Õ¡Õ¿Õ¥Ö„ Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ« ÕºÕ¡Õ¿Õ³Õ¥Õ¶Õ¸Ö‚Õ´Õ¨ +ÕŠÕ¡Õ¿Õ³Õ¥Õ¶Õ¥ÕžÕ¬ Õ¡ÕµÕ½ Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ¨ Õ¡Ö€Õ­Õ«Õ¾Õ« Õ´Õ¥Õ»: +6100 +Õ€Õ¡Õ½Õ¿Õ¡Õ¿Õ¥Õ¬ Ö†Õ¡ÕµÕ¬Õ« Õ»Õ¶Õ»Õ¸Ö‚Õ´Õ¨ +Õ€Õ¡Õ½Õ¿Õ¡Õ¿Õ¥Õ¬ Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ« Õ»Õ¶Õ»Õ¸Ö‚Õ´Õ¨ +Õ€Õ¡Õ½Õ¿Õ¡Õ¿Õ¥Õ¬ Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ« Õ»Õ¶Õ»Õ¸Ö‚Õ´Õ¨ +Ô»Ö€Õ¸ÕžÖ„ ÖÕ¡Õ¶Õ¯Õ¡Õ¶Õ¸Ö‚Õ´ Õ¥Ö„ Õ»Õ¶Õ»Õ¥Õ¬ "{0}"-Õ¨: +Ô»Ö€Õ¸ÕžÖ„ ÖÕ¡Õ¶Õ¯Õ¡Õ¶Õ¸Ö‚Õ´ Õ¥Ö„ Õ»Õ¶Õ»Õ¥Õ¬ "{0}" Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ¨ Ö‡ Õ¶Ö€Õ¡ ÕºÕ¡Ö€Õ¸Ö‚Õ¶Õ¡Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨: +Ô»Ö€Õ¸ÕžÖ„ ÖÕ¡Õ¶Õ¯Õ¡Õ¶Õ¸Ö‚Õ´ Õ¥Ö„ Õ»Õ¶Õ»Õ¥Õ¬ ({0} Õ°Õ¡Õ¿) Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ¨: +Õ‹Õ¶Õ»Õ¸Ö‚Õ´ Õ§... +ÕÕ­Õ¡Õ¬` Ö†Õ¡ÕµÕ¬Õ¨ Õ¯Õ¡Õ´ Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ¨ Õ»Õ¶Õ»Õ¥Õ¬Õ«Õ½: +Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ§ Ô±Õ²Õ¢Õ¡Ö€Õ¯Õ² Õ¿Õ¥Õ²Õ¡ÖƒÕ¸Õ­Õ¥Õ¬ Õ¥Ö€Õ¯Õ¡Ö€ Õ¡Õ¶Õ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ¸Õ¾ Ö†Õ¡ÕµÕ¬Õ¥Ö€: +6300 +ÕÕ¿Õ¥Õ²Õ®Õ¥Õ¬ Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯ +ÕÕ¿Õ¥Õ²Õ®Õ¥Õ¬ Ö†Õ¡ÕµÕ¬ +Ô¹Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ« Õ¡Õ¶Õ¸Ö‚Õ¶Õ¨. +Õ–Õ¡ÕµÕ¬Õ« Õ¡Õ¶Õ¸Ö‚Õ¶Õ¨. +Õ†Õ¸Ö€ Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯ +Õ†Õ¸Ö€ Ö†Õ¡ÕµÕ¬ +ÕÕ­Õ¡Õ¬` Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯ Õ½Õ¿Õ¥Õ²Õ®Õ¥Õ¬Õ«Õ½ +ÕÕ­Õ¡Õ¬` Ö†Õ¡ÕµÕ¬ Õ½Õ¿Õ¥Õ²Õ®Õ¥Õ¬Õ«Õ½ +6400 +Õ„Õ¥Õ¯Õ¶Õ¡Õ¢Õ¡Õ¶Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ +&Õ„Õ¥Õ¯Õ¶Õ¡Õ¢Õ¡Õ¶Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ +Õ†Õ·Õ¥Õ¬ +Ô±ÕºÕ¡Õ¶Õ·Õ¥Õ¬ +Ô´Õ«Õ´Õ¡Õ¯. +6600 +Õ€Õ¡Õ¿Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¶Õ¥Ö€ +Ô¹Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯Õ¶Õ¥Ö€Õ« ÕºÕ¡Õ¿Õ´Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶Õ¨ +Õ€Õ¡Õ²Õ¸Ö€Õ¤Õ¡Õ£Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ +Õ€Õ¡Õ²Õ¸Ö€Õ¤Õ¡Õ£Ö€Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ +7100 +Õ€Õ¡Õ´Õ¡Õ¯Õ¡Ö€Õ£Õ«Õ¹ +Õ‘Õ¡Õ¶Ö +Õ“Õ¡Õ½Õ¿Õ¡Õ©Õ²Õ©Õ¥Ö€ +Õ€Õ¡Õ´Õ¡Õ¯Õ¡Ö€Õ£ +7200 +Ô±Õ¾Õ¥Õ¬Õ¡ÖÕ¶Õ¥Õ¬ +Ô´Õ¸Ö‚Ö€Õ½ Õ¢Õ¥Ö€Õ¥Õ¬ +Ô¹Õ¥Õ½Õ¿Õ¡Õ¾Õ¸Ö€Õ¥Õ¬ +ÕŠÕ¡Õ¿Õ³Õ¥Õ¶Õ¥Õ¬ +ÕÕ¥Õ²Õ¡ÖƒÕ¸Õ­Õ¥Õ¬ +Õ‹Õ¶Õ»Õ¥Õ¬ +ÕÕ¥Õ²Õ¥Õ¯Õ¸Ö‚Õ©ÕµÕ¸Ö‚Õ¶ +7300 +ÕÖ€Õ¸Õ°Õ¥Õ¬ Ö†Õ¡ÕµÕ¬Õ¨ +&ÕÖ€Õ¸Õ°Õ¥Õ¬Õ +ÕÖ€Õ¸Õ°Õ¥Õ¬ &Õ´Õ¡Õ½Õ¥Ö€Õ« (Õ¢Õ¡ÕµÕ©Õ¥Ö€Õ¸Õ¾)` +ÕÖ€Õ¸Õ°Õ¸Ö‚Õ´... +Õ€Õ¡Õ½Õ¿Õ¡Õ¿Õ¥Ö„ Õ¿Ö€Õ¸Õ°Õ¸Ö‚Õ´Õ¨ +Իրո˚ք Õ¿Ö€Õ¸Õ°Õ¥Õ¬ Ö†Õ¡ÕµÕ¬Õ¨ {0} Õ°Õ¡Õ¿Õ¸Ö€Õ¶Õ¥Ö€Õ« +Õ€Õ¡Õ¿Õ¸Ö€Õ« Õ¹Õ¡ÖƒÕ¨ ÕºÕ¥Õ¿Ö„ Õ§ ÖƒÕ¸Ö„Ö€ Õ¬Õ«Õ¶Õ« Õ¨Õ¶Õ©Õ¡ÖÕ«Õ¯ Ö†Õ¡ÕµÕ¬Õ« Õ¹Õ¡ÖƒÕ«Ö +Ô³Õ¸Ö€Õ®Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ½Õ­Õ¡Õ¬ +Õ€Õ¡Õ¿Õ¸Ö€Õ« Õ¶Õ·Õ¾Õ¡Õ® Õ¹Õ¡ÖƒÕ¨. {0} Õ¢Õ¡ÕµÕ©:\nÕրոհե˚լ Õ¡Ö€Õ­Õ«Õ¾Õ¨ Õ°Õ¡Õ¿Õ¸Ö€Õ¶Õ¥Ö€Õ«: +7400 +Õ€Õ¡Õ´Õ¡Õ¯ÖÕ¥Õ¬ Ö†Õ¡ÕµÕ¬Õ¨ +&Õ€Õ¡Õ´Õ¡Õ¯ÖÕ¥Õ¬. +Õ€Õ¡Õ´Õ¡Õ¯ÖÕ¸Ö‚Õ´... +Ô¸Õ¶Õ¿Ö€Õ¥Õ¬ Õ¿Ö€Õ¸Õ°Õ¾Õ¡Õ® Ö†Õ¡ÕµÕ¬Õ« Õ´Õ«Õ¡ÕµÕ¶ Õ¡Õ¼Õ¡Õ»Õ«Õ¶ Õ´Õ¡Õ½Õ¨ +Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ¥Õ²Õ¡Õ¾ Õ³Õ¡Õ¶Õ¡Õ¹Õ¥Õ¬ Õ¿Ö€Õ¸Õ°Õ¾Õ¡Õ® Ö†Õ¡ÕµÕ¬Õ¨ +Õ€Õ¶Õ¡Ö€Õ¡Õ¾Õ¸Ö€ Õ¹Õ¥Õ²Õ¡Õ¾ Õ£Õ¿Õ¶Õ¥Õ¬ Õ¿Ö€Õ¸Õ°Õ¾Õ¡Õ® Ö†Õ¡ÕµÕ¬Õ« Õ´Õ¡Õ½Õ¥Ö€Õ¨ +7500 +Checksum-Õ« Õ¸Ö€Õ¸Õ·Õ¸Ö‚Õ´... +Checksum +CRC checksum Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ« Õ°Õ¡Õ´Õ¡Ö€. +CRC checksum Ö†Õ¡ÕµÕ¬Õ¥Ö€Õ« Ö‡ Õ¡Õ¶Õ¸Ö‚Õ¶Õ¶Õ¥Ö€Õ« Õ°Õ¡Õ´Õ¡Ö€. +7600 +Ô±Ö€Õ¿Õ¡Õ¤Ö€Õ¸Õ²Õ¡Õ¯Õ¡Õ¶Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ©Õ¥Õ½Õ¿ +Õ€Õ«Õ·Õ¸Õ²Õ¸Ö‚Õ©ÕµÕ¡Õ¶ Õ®Õ¡Õ¾Õ¡Õ¬Õ¨. +ÕÕ¥Õ²Õ´Õ¥Õ¬ +Ô´Õ¸Ö‚Ö€Õ½ Õ¢Õ¥Ö€Õ¥Õ¬ +ÕŽÕ¡Ö€Õ¯Õ¡Õ¶Õ«Õ· +Ô¸Õ¶Õ¤Õ°Õ¡Õ¶Õ¸Ö‚Ö€ Õ¾Õ¡Ö€Õ¯Õ¡Õ¶Õ«Õ·Õ¨ +Ô¸Õ¶Õ©Õ¡ÖÕ«Õ¯Õ¨ +Ô±Õ¾Õ¡Ö€Õ¿Õ¸Ö‚Õ¶ Õ¡Ö€Õ¤ÕµÕ¸Ö‚Õ¶Ö„Õ¨ +CPU-Õ« Ö…Õ£Õ¿-Õ¨ +ÕŽÕ¡Ö€Õ¯./Õ•Õ£Õ¿. +Ô±Õ¶ÖÕ¸Ö‚Õ´Õ¶Õ¥Ö€. +7700 +Õ€Õ²Õ¸Ö‚Õ´ +Ô¿Õ¡ÕºÕ¥Õ¬ +Ô±Õ²Õ¢ÕµÕ¸Ö‚Ö€. +Õ†ÕºÕ¡Õ¿Õ¡Õ¯. +7710 +Õ€Õ²Õ´Õ¡Õ¶ Õ¿Õ¥Õ½Õ¡Õ¯ +Ô¿Õ¸Õ·Õ¿ Õ°Õ²Õ¸Ö‚Õ´ +Õ†Õ·Õ¡Õ¶Õ¡ÕµÕ«Õ¶ Õ°Õ²Õ¸Ö‚Õ´ (Ö†Õ¡ÕµÕ¬) +Õ†Õ·Õ¡Õ¶Õ¡ÕµÕ«Õ¶ Õ°Õ²Õ¸Ö‚Õ´ (Õ©Õ²Õ©Õ¡ÕºÕ¡Õ¶Õ¡Õ¯) +Õ„Õ«Õ¡ÖÕ´Õ¡Õ¶ Õ¯Õ¥Õ¿ (Junction) diff --git a/Utils/7-Zip/Lang/id.txt b/Utils/7-Zip/Lang/id.txt new file mode 100644 index 000000000..c4fc1b78b --- /dev/null +++ b/Utils/7-Zip/Lang/id.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 15.14 : 02/01/2016 : Frans Liando +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Indonesian +Bahasa Indonesia +401 +Oke +Batal + + + +&Ya +&Tidak +&Tutup +Bantuan + +&Lanjut +440 +Ya untuk &semua +Tidak untuk s&emua +Henti +Mulai Ulang +Latar Bela&kang +Latar &Depan +&Jeda +Terjeda +Anda yakin ingin batal? +500 +&Berkas +&Edit +Tam&pilan +&Kesukaan +Pera&latan +Ban&tuan +540 +&Buka +Buka Sisi &Dalam +Buka Sisi L&uar +&Tampilkan +&Edit +&Nama Ulang +&Salin Ke... +P&indahkan Ke... +&Hapus +Be&lah Berkas... +Gabun&g Berkas... +P&roperti +K&omentari +Hitung ceksum +Beda +Buat Direktori +Buat Berkas +&Keluar +Tautan +Alternati&f Aliran +600 +Pi&lih Semua +Batal Pilih Semua +Pilih Sebal&iknya +Pilih... +Batal Pilih... +Pilih Berdasarkan Tipe +Batal Pilih Berdasarkan Tipe +700 +Ikon &Besar +Ikon &Kecil +&Daftar +&Rincian +730 +Tidak Berurutan +Tampilan Datar +&2 Panel +Bilah Ala&t +Buka Akar Direktori +Naik Satu Tingkat +Riwayat Direktori... +&Segarkan +Segarkan Otomatis +750 +Bilah Alat Arsip +Bilah Ala&t Standar +Tombol Besar +Perlihatkan Teks Tombol +800 +Tambah direktori ke Favorit sebagai +Markah +900 +&Pilihan... +&Tolok Ukur +960 +Petun&juk... +Mengen&ai 7-Zip... +1003 +Lintasan +Nama +Ekstensi +Direktori +Ukuran +Ukuran dipak +Atribut +Dibuat +Diakses +Dimodifikasi +Padat +Dikomentari +Dienkripsi +Belah Sebelum +Belah Setelah +Kamus + +Tipe +Anti +Metode +SO Host +Sistem Berkas +Pengguna +Grup +Blok +Komentar +Posisi +Awalan Lintasan +Direktori +Berkas +Versi +Volume +Multivolume +Gelinciran +Tautan +Blok +Volume + +64-bit +Big-endian +CPU +Ukuran Fisik +Ukuran Tajuk +Ceksum +Karakteristik +Alamat Maya +ID +Nama Singkat +Aplikasi Pembuat +Ukuran Sektor +Ragam +Tautan Simbolik +Kesalahan +Ukuran Total +Ruang Kosong +Ukuran Gugus +Label +Nama Lokal +Penyelenggara +Keamanan NT +Alternatif ALiran +Aux +Dihapus +Apakah Pohon + + +Tipe Kesalahan +Kesalahan +Kesalahan +Peringatan +Peringatan +Aliran +Alternatif Aliran +Ukuran Alternatif Aliran +Ukuran Maya +Ukuran Terbongkar +Jumlah Ukuran Fisik +Indeks Volume +SubTipe +Komentar Singkat +Laman Kode + + + +Ukuran Ujung +Ukuran Rintisan Tertanam +Tautan +Tautan Keras +iNode + +Hanya-baca +2100 +Pilihan +Bahasa +Bahasa: +Editor +&Editor: +Be&da: +2200 +Sistem +Asosiasikan 7-Zip dengan: +Semua pengguna +2301 +Integrasikan 7-Zip ke cangkang menu konteks +Menu konteks dikaskade +Butir-butir menu konteks: +Ikon-ikon di menu konteks +2320 + + +Buka arsip +Ekstrak berkas... +Tambah ke arsip... +Uji arsip +Ekstrak di sini +Ekstrak ke {0} +Tambah ke {0} +Mampat dan surat kawat... +Mampat ke {0} dan surat kawat +2400 +Direktori +&Direktori kerja +Direktori temporer &sistem +S&ekarang +&Ditentukan: +Hanya untuk kandar yang dapat dilepas +Tetapkan lokasi untuk berkas arsip sementara. +2500 +Pengaturan +Perlihatkan butir ".." +Perlihatkan ikon asli berkas +Perlihatkan menu sistem +&Memilih baris penuh +Perlihatkan &garis kisi-kisi +Klik-tunggal untuk membuka +Ragam pemilihan &alternatif +Gunakan halaman memori &besar +2900 +Mengenai 7-Zip +7-Zip merupakan perangkat lunak gratis.\n\nTerjemahan oleh Frans Liando. +3000 +Sistem tidak bisa mengalokasikan jumlah memori yang diperlukan +Tidak ada kesalahan +{0} objek terpilih +Tidak bisa membuat Direktori '{0}' +Operasi pembaruan tidaklah didukung untuk arsip ini. +Tidak bisa membuka berkas arsip '{0}' +Tidak bisa membuka arsip terenkripsi '{0}'. Salah kata sandi? +Tipe arsip tidak didukung +Berkas {0} telah ada +Berkas '{0}' telah termodifikasi.\nApakah Anda ingin perbarui berkas dalam arsip? +Tidak bisa perbarui berkas\n'{0}' +Tidak bisa memulai editor. +Berkas ini tampaknya seperti virus (pada nama berkas berisi spasi yang panjang). +Pengoperasian tidak bisa dipanggil dari direktori yang berlintasan panjang. +Anda harus pilih satu berkas +Anda harus pilih satu berkas atau lebih +Terlalu banyak butir +Tidak bisa buka berkas sebagai arsip {0} +Berkas dibuka sebagai arsip {0} +Berkas dibuka dengan gelinciran +3300 +Mengekstrak +Pemampatan +Pengujian +Membuka... +Memindai... +Memindah +3320 +Menambah +Memperbarui +Menganalisa +Mereplikasi +Pak ulang +Melewatkan +Menghapus +Membuat tajuk +3400 +Ekstrak +E&kstrak ke: +Tetapkan lokasi untuk berkas yang diekstrak. +3410 +Ragam lintasan: +Nama lengkap lintasan +Tidak ada nama lintasan +Nama lintasan absolut +Nama lintasan relatif +3420 +Ragam tulis timpa: +Konfirmasikan sebelum tulis timpa +Tulis timpa tanpa konfirmasi +Lewati berkas yang ada +Penamaan ulang automatis +Penamaan ulang automatis terhadap berkas yang ada +3430 +Hilangkan duplikasi direktori akar +Pulihkan keamanan berkas +3500 +Konfirmasi Penggantian Berkas +Direktori tujuan telah berisi berkas yang terproses. +Maukah Anda mengganti berkas yang ada +dengan yang satu ini? +{0} bita +Nama &Ulang Automatis +3700 +Metode mampat untuk '{0}' tidak didukung. +Kesalahan data di '{0}'. Berkas ini rusak. +CRC gagal di '{0}'. Berkas ini rusak. +Kesalahan data di berkas terenkripsi '{0}'. Salah kata sandi? +CRC gagal di berkas terenkripsi '{0}'. Salah kata sandi? +3710 +Salah kata sandi? +3721 +Metode mampat tidak didukung +Kesalahan data +CRC gagal +Data tidak tersedia +Akhir data tidak terduga +Terdapat suatu data setelah akhir muatan data +Bukan arsip +Kesalahan Tajuk +Salah kata sandi +3763 +Awal arsip belum tersedia +Awal arsip belum dikonfirmasi + + + +Fitur tidak didukung +3800 +Masukkan kata sandi +Masukkan kata sandi: +Konfirmasi kata sandi: +&Perlihatkan kata sandi +Kata sandi tidak cocok +Gunakanlah hanya huruf bahasa Indonesia, nomor dan karakter khusus (!, #, $, ...) untuk kata sandi +Kata sandi terlalu panjang +Kata sandi +3900 +Waktu terpakai: +Sisa waktu: +Jumlah ukuran: +Kecepatan: +Terproses: +Rasio mampat: +Kesalahan: +Arsip: +4000 +Tambah ke arsip +&Arsip: +Ragam perbar&u: +&Format arsip: +&Level mampat: +&Metode mampat: +Ukuran &kamus: +Ukuran ka&ta: +Ukuran blok padat: +Jumlah CPU: +&Parameter: +Pilihan +Buat arsip SF&X +Kompres berkas bersama +Enkripsi +Metode enkripsi: +Enkripsi &nama berkas +Pemakaian memori untuk Pemampatan: +Pemakaian memori untuk Pengawamampatan: +Hapus berkas setelah dimampatkan +4040 +Simpan tautan simbolik +Simpan tautan keras +Simpan alternatif aliran data +Simpan keamanan berkas +4050 +Simpan +Tercepat +Cepat +Normal +Maksimum +Ultra +4060 +Tambah dan ganti berkas +Perbarui dan tambah berkas +Segarkan ulang berkas yang ada +Sinkronisasikan berkas +4070 +Ramban +Semua Berkas +Non-padat +Padat +6000 +Salin +Pindah +Salin ke: +Pindah ke: +Menyalin... +Memindah... +Penamaan Ulang... +Pilih direktori tujuan. +Pengoperasian tidak didukung untuk direktori demikian. +Kesalahan Penamaan Ulang Berkas atau Direktori +Konfirmasi Salin Berkas +Anda yakin ingin menyalin berkas ke arsip? +6100 +Konfirmasi Hapus Berkas +Konfirmasi Hapus Direktori +Konfirmasi Hapus Berkas-berkas +Anda yakin ingin hapus '{0}'? +Anda yakin ingin hapus direktori '{0}' dan semua isinya? +Anda yakin ingin hapus {0} butir? +Menghapus... +Kesalahan Penghapusan Berkas atau Direktori +Sistem tidak bisa memindah suatu berkas yang berlintasan panjang ke Recycle Bin +6300 +Buat Direktori +Buat Berkas +Nama Direktori: +Nama berkas: +Direktori Baru +Berkas Baru +Kesalahan Membuat Direktori +Kesalahan Pembuatan Berkas +6400 +Komentar +&Komentari: +Pilih +Tidak Memilih +Masker: +6600 +Properti +Riwayat Direktori +Pesan Diagnosa +Pesan +7100 +Komputer +Jaringan +Dokumen +Sistem +7200 +Tambah +Ekstrak +Uji +Salin +Pindah +Hapus +Info +7300 +Belah Berkas +&Belah ke: +Belah ke &volume, bita: +Membelah... +Konfirmasi Membelah +Anda yakin ingin membelah berkas menjadi {0} volume? +Ukuran volume seharusnya lebih kecil dari pada ukuran asli berkas +Ukuran volume salah +Tentukan ukuran volume: {0} bita.\nAnda Yakin ingin membelah arsip menjadi volume demikian? +7400 +Gabung Berkas +&Gabung ke: +Menggabung... +Pilih bagian pertama saja dari berkas belahan +Tidak bisa mendeteksi berkas sebagai bagian berkas belahan +Tidak bisa menemukan lebih dari satu bagian berkas belahan +7500 +Hitung ceksum... +Informasi ceksum +Ceksum CRC untuk data: +Ceksum CRC untuk data dan nama: +7600 +Tolok Ukur +Pemakaian memori: +Pemampatan +Pengawamampatan +Penilaian +Jumlah Penilaian +Sekarang +Hasil +Pemakaian CPU +Penilaian/Pemakaian +Lulus: +7700 +Tautan +Tautan +Tautan dari: +Tautan ke: +7710 +Tipe Tautan +Tautan Keras +Berkas Tautan Simbolik +Direktori Tautan Simbolik +Cabang Direktori diff --git a/Utils/7-Zip/Lang/io.txt b/Utils/7-Zip/Lang/io.txt new file mode 100644 index 000000000..ccc4f6c91 --- /dev/null +++ b/Utils/7-Zip/Lang/io.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.08 : iZoom +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Ido +Ido +401 +B&one +Abandonar + + + +&Yes +&No +&Klozez +Helpo + +&Durez +440 +Yes por &omni +No por o&mni +Haltez +Ristartez +&Fono +&Avanajo +&Pauzo +Pauzita +Kad vu ya volas abortar? +500 +&Dosiero +&Redakto +&Aspekto +&Favoraji +&Utensili +&Helpo +540 +&Apertigar +Apertigar int&erne +Apertigar e&xter +&Vidigar +&Redaktar +Ch&anjar nomo +Ko&piar aden... +Transp&ozar aden... +E&facar +F&endar dosiero... +Komb&inar dosieri... +In&heraji +Ko&mentar + + +Krear &dosieruyo +Krear dos&iero +E&kirar +600 +Merk&ar omno +Des&merkar omno +&Inversigar merko +Merkar... +Desmerkar... +Merkar segun tipo +Desmerkar segun tipo +700 +&Granda ikoneti +&Mikra ikoneti +&Listo +&Tabelo +730 +&Nearanjite + +&2 paneli +Utens&ila paneli +Apertigar radika dosieruyo +Ad-supre ye un nivelo +Dosieruya historio... +R&inovigar +750 +Utensila panelo di arkivo +Norma utensila panelo +Granda ikoneti +Videbla butontexto +800 +&Adjuntar dosieruyo ad la favorata quale +Lektomerkajo +900 +&Ajusti... +&Experienco dil rapideso +960 +&Konteno... +&Pri 7-Zip... +1003 +Dosiervoyo +Nomo +Dosiernoma sufixo +Dosieruyo +Grandeso +Enarkiva grandeso +Atributi +Kreita +Acesita +Chanjita +Solida +Komentita +Chifrita +Fendita ante +Fendita pos +Vortaro +CRC +Tipo +Kontre +Metodo +Operacala sistemo +Dosiersistemo +Uzero +Grupo +Bloko +Komenturo +Poziciono + + + + + + + + + + + + + + + + + + + + + + + + + +Eroro +Tota kapacivo +Vakanta +Faskogrando +Etiketo +Lokala nomo +Provizanto +2100 +Ajusti +Linguo +Linguo: +Redaktilo +&Redaktilo: + +2200 +Sistemo +Asociar 7-Zip-o kun dosieru: +2301 +Pozar 7-Zip'o en kuntexta menuo di shelo +Kaskada kuntexta menuo +Elementi di kuntexta menuo: +2320 + + +Apertar +Extraktar dosieri... +Adjuntar ad arkivo... +Verifikar arkivo +Extraktez hike +Extraktez aden {0} +Adjuntar ad {0} +Enarkivigar ed sendar elk-posto... +Enarkivigar aden {0} e sendar elk-posto... +2400 +Dosieruyi +&Laborala dosieruyo +&Sistemala provizora dosieruyo +&Nuna +&Definez: +&Uzar nur por deprenebla datumportili +Definar loko por provizora arkiva dosieri. +2500 +Ajusti +Montrar ".."-elemento +Montrar reala dosier-ikoneti +Montrar sistemala menuo +Merkar &tota lineo +Montrar &streki separanta + + + +2900 +Pri progamo... +7-Zip esas gratuita programo. Tamen, vu povas mantenar developado di 7-Zip per enregistrigesar. +3000 + +Erori ne es trovita +{0} objekt(o|i) merkita +Krear dosieruyo '{0}' neposiblesis +Rinovigo ne suportesas por ica arkivo. + + + + +Dosiero '{0}' chanjesis.\nKa vu volas rinovigar lu enarkive? +Rinovigo dil dosiero\n'{0}' faliis +Startigo dil redaktilo. + + + + +Tro multa objekti +3300 +Extrakto +Kompreso +Probado +Aperto... + +3400 +&Extraktar +E&xtraktar aden: +Definez loko por dosieri extraktenda. +3410 +Dosiervoyi +&Absoluta dosiervoyi +&Sen dosiervoyi +3420 +Remplasala skribmodo +&Kun konfirmo +&Sen konfirmo +&Omisar existanta dosieri +Automata nomchanjo +Automata nomchanjo de existanta dosieri +3500 +Konfirmo di nomchanjo +Dosieruyo ja kontenas operacata dosiero. +Kad remplasor esanta dosiero +per la ica? +{0} bayti* +&Automata nomchanjo. +3700 +Kompresmetodo ne esas suportata por dosiero '{0}'. +Datumeroro en '{0}'. Dosiero es fushita. +CRC-eroro en '{0}'. Dosiero es fushita. + + +3800 +Pasovorto +Sugestez pasovorto: + +&Montrar pasovorto + + + +&Pasovorto +3900 +Pasinta tempo: +Restanta tempo: +Grandeso: +Rapideso: + + +Erori: + +4000 +Adjuntar aden arkivo +&Arkivo: +R&emplasomodo: +A&rkiva formato: +Kompreso&grado +&Kompresometodo: +&Vortarograndeso: +Vo&rtograndeso: + + +&Parametri: +Ajustaji +Krear SF&X-arkivo + + + +Chifrar dosier&nomi +Memoruzo por kompresar: +Memoruzo por extraktar: +4050 +Sen kompresar +Maxim rapide +Rapide +Normala kompreso +Maxim granda kompreso +Extreme +4060 +Adjuntar e remplasar dosieri +Rinovigar e adjuntar dosieri +Rifreshigar existanta dosieri +Sinkronizar dosieri +4070 +Inspektar +Omna dosieri + + +6000 +Kopiar +Transpozar +Kopiez aden: +Transpozez aden: +Kopio... +Transpozo... +Nomchanjo... + +Operaco ne suportesas. +Eroro dum nomchanjo di dosiero o dosieruyo + + +6100 +Konfirmo dil efaco di dosiero +Konfirmo dil efaco di dosieruyo +Konfirmo dil efaco di dosieraro +Ka vu ya volas efacar '{0}'? +Ka vu ya volas efacar dosieruyo "{0}" e omna lua kontenaji? +Ka vu ya volas efacar ita {0} objekti? +Efaco... +Eroro dum efacar di dosiero o dosieruyo + +6300 +Krear dosieruyo +Krear dosiero +Dosieruynomo: +Dosiernomo: +Nova dosieruyo +Nova dosiero +Eroro dum dosieruykreo +Eroro dum dosierkreo +6400 +Komento +&Komento: +Merkar +Desmerkar +Masko: +6600 + +Dosieruyhistorio +Diagnozala mesaji +Mesajo +7100 +Komputilo +Reto + +Sistemo +7200 +Adjuntar +Extraktar +Verifikar +Kopiar +Transpozar +Efacar +Informo +7300 +Fendar dosiero +&Fendez aden: +&Fendar por volumini, bayti: +Fendo... + + + + + +7400 +Kombinar dosieri +&Kombinar aden: +Kombino... + + + +7500 + + + + +7600 +Experienco dil rapideso +Memoruzo: +Kompresado +Extraktado +Aprecuro +Tota aprecuro +Kuranta +Rezulta + + +Pasi: diff --git a/Utils/7-Zip/Lang/is.txt b/Utils/7-Zip/Lang/is.txt new file mode 100644 index 000000000..f324c6e94 --- /dev/null +++ b/Utils/7-Zip/Lang/is.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 15.14 : 2016-04-23 : Stefán Örvar Sigmundsson +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Icelandic +Ãslenska +401 +à lagi +Viðhætta + + + +&Já +&Nei +&Loka +Hjálp + +&Halda áfram +440 +&Já við öllu +&Nei við öllu +Stöðva +Endurræsa +&Bakgrunnur +&Forgrunnur +&Gera hlé +à hléi +Ert þú viss um að þú viljir viðhætta? +500 +&Skrá +&Breyta +S&koða +&Uppáhald +&Verkfæri +&Hjálp +540 +&Opna +Opna að &innanverðu +Opna að &utanverðu +S&koða +&Breyta +&Endurnefna +&Afrita í… +&Færa í… +E&yða +K&ljúfa skrá +S&ameina skrár +E&iginleikar +&Gera athugasemd +Reikna samtölu +Mismunur +Skapa &möppu +Skapa &skrá +&Hætta +&Tengill +&Víxlstraumar +600 +&Velja allt +&Afvelja allt +&Umsnúa vali +&Velja +&Afvelja +&Velja eftir tegund +&Afvelja eftir tegund +700 +&Stórar táknmyndir +S&máar táknmyndir +&Listi +Sm&áatriði +730 +Óflokkað +&Flatsýn +&2 spjöld +&Verkfærastikur +&Opna rótarmöppu +&Upp um eitt stig +M&öppusaga +&Endurglæða +S&jálfendurglæðun +750 +Safnverkfærastika +Stöðluð verkfærastika +Stórir takkar +Sýna takkatexta +800 +&Viðbæta möppu í uppáhald sem +Bókamerki +900 +&Valmöguleikar +&Afkastaprófa +960 +&Efnisyfirlit +&Um 7-Zip +1003 +Slóð +Nafn +Framlenging +Mappa +Stærð +Þjöppuð stærð +Eiginleikar +Skapað +Aðkomið +Dagsetning +Þétt +Athugasemd +Dulkóðað +Kljúfa fyrir +Kljúfa eftir +Orðasafn + +Tegund +And +Aðferð +Stýrikerfi hýsils +Skráakerfi +Notandi +Hópur +Bálkur +Athugasemd +Staðsetning +Slóðarforskeyti +Möppur +Skrár +Útgáfa +Bindi +Fjölbinda +Afsetning +Tenglar +Bálkar +Bindi + +64-bita +Háenda +Gjörvi +Efnisleg stærð +Höfðastærð +Samtala +Einkenni +Sýndarvistfang +Auðkenni +Stutt nafn +Skaparahugbúnaður +Geirastærð +Hamur +Mjúktengill +Villa +Heildarstærð +Laust pláss +Klasastærð +Merki +Staðarnafn +Veitandi +NT-öryggi +Víxlstraumur +Aðstoðar- +Eytt +Er tré + + +Villutegund +Villur +Villur +Viðvaranir +Viðvörun +Straumar +Víxlstraumar +Víxlstraumastærð +Sýndarstærð +Afþjöppuð stærð +Efnisleg heildarstærð +Bindaskrá +Undirtegund +Stutt athugasemd +Kóðasíða + + + +Halastærð +Innfallin stubbastærð +Tengill +Harðtengill +Skráhnútur + +Einungis lesanlegt +2100 +Valmöguleikar +Tungumál +Tungumál: +Ritill +&Ritill: +&Mismunur: +2200 +Kerfi +Tengja 7-Zip við: +Allir notendur +2301 +&Innleiða 7-Zip í samhengisvalmynd skeljar +&Þrepaskipt samhengisvalmynd +Samhengisvalmyndaratriði: +&Táknmyndir í samhengisvalmynd +2320 + + +Opna safn +Afþjappa skrár +Viðbæta í safn +Prófa safn +Afþjappa hér +Afþjappa í „{0}“ +Viðbæta í „{0}“ +Þjappa og senda í rafpósti +Þjappa í „{0}“ og senda í rafpósti +2400 +Möppur +Vinnslumappa +&Tímabundin mappa kerfis +&Núverandi +Til&greind: +N&ota einungis fyrir fjarlægjanleg drif +Tilgreina staðsetningu fyrir tímabundnar safnskrár. +2500 +Stillingar +Sýna „..“ &atriði +Sýna &raunverulegar skráartáknmyndir +Sýna &kerfisvalmynd +&Fullraðaval +Sýna &töflulínur +&Einsmella til að opna atriði +Annars konar &valhamur +Nota stórar &minnissíður +2900 +Um 7-Zip +7-Zip er frjáls hugbúnaður +3000 +Kerfið getur ekki ráðstafað nauðsynlega magninu af minninu +Það eru engar villur +„{0}“ hlutir valdir +Getur ekki skapað möppuna „{0}“ +Uppfærsluaðgerðir eru ekki studdar fyrir þetta safn. +Getur ekki opnað skrána „{0}“ sem safn +Getur ekki opnað dulkóðaða safnið „{0}“. Rangt aðgangsorð? +Óstudd safntegund +Skráin „{0}“ er nú þegar til +Skránni „{0}“ var breytt.\nVilt þú uppfæra hana í safninu? +Getur ekki uppfært skrána\n„{0}“ +Getur ekki ræst ritilinn. +Skráin lítur út eins og veira (skráarnafnið inniheldur löng bil). +Aðgerðina er ekki hægt að kalla í frá möppu sem hefur langa slóð. +Þú verður að velja eina skrá +Þú verður að velja eina eða fleiri skrár +Of mörg atriði +Getur ekki opnað skrána sem „{0}“-safn +Skráin er opin sem „{0}“-safn +Safnið er opið með afsetningu +3300 +Afþjappar +Þjappar +Prófar +Opnar +Skimar +Fjarlægir +3320 +Viðbætir +Uppfærir +Greinir +Endurtekur +Endurpakkar +Sleppir +Eyðir +Skapar höfuð +3400 +Afþjappa +Afþjappa í: +Tilgreindu staðsetningu fyrir afþjöppuðu skrárnar. +3410 +Slóðarhamur: +Full slóðarnöfn +Engin slóðarnöfn +Algild slóðarnöfn +Afstæð slóðarnöfn +3420 +Yfirritunarhamur: +Spyrja áður en yfirritað er +Yfirrita án kvaðningar +Sleppa gildandi skrám +Sjálfendurnefnun +Sjálfendurnefnun gildandi skráa +3430 +Útrýma afritinu af rótarmöppunni +Endurheimta skráaröryggi +3500 +Staðfesta skráaryfirritun +Ãfangastaðsmappan inniheldur nú þegar meðhöndluðu skrána. +Vilt þú yfirrita gildandi skrána +með þessari? +{0} bæti +Sjálfendurnefnun +3700 +Óstudd þjöppunaraðferð fyrir „{0}“. +Gagnavilla í „{0}“. Skráin er brotin +CRC mistókst í „{0}“. Skráin er brotin. +Gagnavilla í dulkóðuðu „{0}“. Rangt aðgangsorð? +CRC mistókst í dulkóðuðu skránni „{0}“. Rangt aðgangsorð? +3710 +Rangt aðgangsorð? +3721 +Óstudd þjöppunaraðferð +Gagnavilla +CRC mistókst +Ótiltæk gögn +Óvæntur endir gagna +Það eru gögn eftir endanum á aðalgögnunum +Er ekki safn +Höfðavillur +Rangt aðgangsorð +3763 +Ótiltækt upphaf safns +Óstaðfest upphaf safns + + + +Óstuddur eiginleiki +3800 +Ritaðu aðgangsorðið +Ritaðu aðgangsorðið: +Endurritaðu aðgangsorðið: +Sýna aðgangsorðið +Aðgangsorðin samsvarast ekki +Notaðu einungis enska stafi, tölur og sérstök rittákn (!, #, $, o.s.frv.) í aðgangsorðunum +Aðgangsorðið er of langt +Aðgangsorð +3900 +Liðinn tími: +Tími eftir: +Heildarstærð: +Hraði: +Meðhöndlað: +Þjöppunarhlutfall: +Villur: +Söfn: +4000 +Viðbæta safni +Safn: +Uppfærsluhamur: +Safnsnið: +Þjöppunarsnið: +Þjöppunaraðferð: +Orðasafnsstærð: +Orðastærð: +Þéttbálkstærð: +Fjöldi gjörvaþráða: +Færibreytur: +Valmöguleikar +Skapa SFX-safn +Þjappa sameiginlegum skrám +Dulkóðun +Dulkóðunaraðferð: +Dulkóða skráarnöfn +Minnisnotkun fyrir þjöppun: +Minnisnotkun fyrir afþjöppun: +Eyða skrám eftir þjöppun +4040 +Geyma mjúktengla +Geyma harðtengla +Geyma víxlgagnastrauma +Geyma skráaröryggi +4050 +Geyma +Hraðast +Hratt +Venjulegt +Hámarks +Öfga- +4060 +Viðbæta og yfirrita skrár +Uppfæra og viðbæta skrám +Hressa við gildandi skrám +Samstilla skrár +4070 +Vafra +Allar skrár +Óþétt +Þétt +6000 +Afrita +Færa +Afrita í: +Færa í: +Afritar +Færir +Endurnefnir +Veldu móttökumöppu. +Aðferðin er ekki stutt fyrir þessa möppu. +Villa við endurnefnun skráar eða möppu +Staðfesta skráarafritun +Ert þú viss um að þú viljir afrita skrárnar í safnið +6100 +Staðfesta skráareyðingu +Staðfesta möppueyðingu +Staðfesta fjölskráaeyðingu +Ert þú viss um að þú viljir eyða „{0}“? +Ert þú viss um að þú viljir eyða möppunni „{0}“ og öllu innihaldinu hennar? +Ert þú viss um að þú viljir eyða þessum {0} atriðum? +Eyðir +Villa við eyðingu skráar eða möppu +Kerfið getur ekki fært skrá með langri slóð í Recycle Bin +6300 +Skapa möppu +Skapa skrá +Möppunafn: +Skráarnafn: +Ný mappa +Ný skrá +Villa við sköpun möppunnar +Villa við sköpun skráarinnar +6400 +Athugasemd +Athugasemd: +Velja +Afvelja +Mát: +6600 +Eiginleikar +Möppusaga +Greiningarskilaboð +Skilaboð +7100 +Tölva +Net +Skjöl +Kerfi +7200 +Viðbæta +Afþjappa +Prófa +Afrita +Færa +Eyða +Upplýsingar +7300 +Kljúfa skrá +Kljúfa í: +Kljúfa í bindi, bæti: +Klýfur +Staðfesta klofning +Ert þú viss um að þú viljir kjúfa skrána í {0} bindi? +Bindisstærð verður að vera minni en stærðin á upprunalegu skránni +Röng bindisstærð +Tilgreindu bindisstærð: {0} bæti.\nErt þú viss um að þú viljir kljúfa safnið í slík bindi? +7400 +Sameina skrár +Sameina í: +Sameinar +Veldu einungis fyrsta hlutann af klofningsskránni +Get ekki greint skrána sem hluta af klofningsskránni +Get ekki fundið meira en einn hluta af klofningsskránni +7500 +Reiknar samtölu +Samtöluupplýsingar +CRC-samtala fyrir gögn: +CRC-samtala fyrir gögn og nöfn: +7600 +Afkastapróf +Minnisnotkun: +Þjöppun +Afþjöppun +Niðurstaða +Heildarniðurstaða +Núverandi +Útkoma +Gjörvanotkun +Niðurstaða / Notkun +Yfirferðir: +7700 +Tengill +Tengill +Tengill frá: +Tengill til: +7710 +Tengiltegund +Harðtengill +Skráarmjúktengill +Skráasafnsmjúktengill +Skráasafnstenging \ No newline at end of file diff --git a/Utils/7-Zip/Lang/it.txt b/Utils/7-Zip/Lang/it.txt new file mode 100644 index 000000000..ee4e3b1cf --- /dev/null +++ b/Utils/7-Zip/Lang/it.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 4.07 : Leandro Spagnol +; : Vincenzo Reale (some corrections) +; 15.05 : 2015-06-17 : TJL73 : http://tjl73.altervista.org/ +; +; +; +; +; +; +; +; +0 +7-Zip +Italian +Italiano +401 +OK +Annulla + + + +&Sì +&No +&Chiudi +Aiuto + +&Riprendi +440 +Sì per &tutti +No per t&utti +Ferma +Riavvia +&In background +&In primo piano +&Pausa +In pausa +Sei sicuro di voler annullare? +500 +&File +&Modifica +&Visualizza +&Preferiti +&Strumenti +&Aiuto +540 +&Apri +Apri in &7-Zip File Manager +Apri in E&xplorer +&Visualizza +Apri con l'&editor predefinito +Rino&mina +&Copia in... +&Sposta in... +&Elimina +&Dividi il file... +&Unisci i file... +&Proprietà +Comme&nto... +Calcola chec&ksum +Comparazione differenze (Diff) +Crea cartella +Crea file +E&sci +Collegamento +&Alternate Data Streams +600 +&Seleziona tutto +&Deseleziona tutto +In&verti selezione +Seleziona... +Deseleziona... +Seleziona per tipo +Deseleziona per tipo +700 +Icone &grandi +Icone &piccole +&Elenco +&Dettagli +730 +Nessun ordine +Vista non strutturata +Interfaccia a &2 pannelli +Barre degli &strumenti +Apri cartella principale +Livello superiore +Cronologia... +&Aggiorna +Aggiornamento automatico +750 +Barra archivio +Barra standard +Icone grandi +Mostra etichette di testo +800 +&Aggiungi la cartella ai Preferiti come +Collegamento +900 +&Opzioni... +&Benchmark +960 +&Guida... +&Informazioni su 7-Zip... +1003 +Percorso +Nome +Estensione +Cartella +Dimensione +Dimensione compressa +Attributi +Creato +Ultimo accesso +Ultima modifica +Solido +Commentato +Cifrato +Dividi prima +Dividi dopo +Dizionario + +Tipo +Estensione +Metodo +OS destinatario +File system +Utente +Gruppo +Blocco +Commento +Posizione +Percorso completo +Cartelle +File +Versione +Unità +Unità multiple +Offset +Collegamenti +Blocchi +Unità + +64-bit +Big-endian +CPU +Dimensione fisica +Dimensione intestazioni +Checksum +Caratteristiche +Indirizzo virtuale +ID +Nome breve +Applicativo origine +Dimensione settore +Modalità +Collegamento simbolico +Errore +Capacità +Disponibili +Dimensione dei cluster +Etichetta +Nome locale +Rete +Sicurezza NT +Flusso alternato +Ausiliario +Eliminato +Ad albero + + +Tipo di errori +Errori +Errori +Avvertimenti +Avvertimento +Flussi +Flussi alternati +Dimensione dei flussi alternati +Dimensione virtuale +Dimensione decompressione +Dimensione fisica totale +Indice del volume +Sottotipo +Commento breve +Pagina dei codici + + + +Dimensione della coda +Dimensione della matrice integrato +Collegamento +Collegamento statico +iNode + +Sola lettura +2100 +Opzioni +Lingua +Lingua: +Editor +&Editor predefinito: +Comparatore &differenze (Diff): +2200 +Sistema +Associa 7-Zip a: +Tutti gli utenti +2301 +Integra 7-Zip nel menu contestuale della shell +Menu contestuale a cascata +Elementi del menu contestuale: +Icone nel menu contestuale +2320 + + +Apri +Estrai i file... +Aggiungi all'archivio... +Verifica l'archivio +Estrai qui +Estrai in {0} +Aggiungi a {0} +Comprimi ed invia per email... +Comprimi in {0} ed invia per email +2400 +Cartelle +Cartella di lavoro +Cartella &TEMP di sistema +&Corrente +&Specificata: +&Utilizza solo per dischi rimovibili +Specifica una cartella per i file temporanei. +2500 +Impostazioni +Mostra l'elemento ".." +Mostra le icone dei file +Mostra le icone di sistema +Selezione a &riga intera +Mostra &griglia +Click singolo per aprire una voce +Modalità di selezione &alternativa +Utilizza pagine &larghe di memoria +2900 +Informazioni +7-Zip è un software libero.\n\nLocalizzazione italiana a cura di:\nTJL73 +3000 +Non è possibile allocare la quantità di memoria richiesta +Nessun errore. +Oggetti selezionati: {0} +Impossibile creare la cartella '{0}' +Non è possibile effettuare aggiornamenti su questo archivio. +Impossibile aprire il file '{0}' come archivio. +Impossibile aprire l'archivio cifrato '{0}'. Password errata? +Archivio non supportato +Il file {0} è già presente +Il file '{0}' è stato modificato.\nVuoi aggiornare l'archivio? +Impossibile aggiornare il file\n'{0}' +Impossibile avviare l'editor. +Il file sembra essere un virus (contiene molti spazi nel nome). +L'operazione non può essere richiamata da una cartella con percorso lungo. +Devi selezionare un file +Devi selezionare almeno un file +Troppi elementi +Impossibile aprire il file come archivio {0} +Il file è aperto come archivio {0} +L'archivio è aperto con offset +3300 +Estrazione in corso +Compressione in corso +Verifica archivio +Apertura in corso... +Scansione... +Rimozione +3320 +Aggiunta +Aggiornamento +Analisi +Replica +Ripacchettizzazione +Salto +Eliminazione +Creazione intestazione +3400 +Estrai +E&strai in: +Specifica una cartella in cui estrarre i file. +3410 +Struttura delle cartelle: +Percorsi completi +Nessun percorso +Percorsi assoluti +Percorsi relativi +3420 +Sovrascrittura: +Chiedi prima di sovrascrivere +Sovrascrivi senza chiedere +Non sovrascrivere i file esistenti +Rinomina automaticamente +Rinomina autom. i file esistenti +3430 +Elimina la duplicazione della radice +Ripristina sicurezza dei file +3500 +Conferma la sovrascrittura del file +File già esistente nella cartella di destinazione. +Vuoi sostituire il file esistente +con questo? +{0} byte +&Rinomina automaticamente +3700 +Metodo di compressione non supportato per '{0}'. +Errore nei dati in '{0}'. Il file è danneggiato. +CRC non corretto in '{0}'. Il file è danneggiato. +Errore nel file cifrato '{0}'. Password errata? +CRC errato nel file cifrato '{0}'. Password errata? +3710 +Password errata? +3721 +Metodo di compressione non supportata +Errore dei dati +CRC errato +Dati non disponibili +Fine dei dati inattesa +Sono presenti dati oltre la fine del blocco utile +Non è un archivio +Errore intestazioni +Password errata +3763 +Inizio dell'archivio non disponibile +Inizio dell'archivio non confermato + + + +Funzionalità non supportata +3800 +Inserisci password +&Inserisci password: +&Reinserisci password: +Mostra pass&word +Password differenti +Per la password, utilizzare solo lettere ASCII, numeri e caratteri speciali (!, #, $, ...) +La password è troppo lunga +Password +3900 +Tempo trascorso: +Tempo rimanente: +Dimensione totale: +Velocità: +Elaborato: +Rapporto compressione: +Errori: +Archivi: +4000 +Aggiungi all'archivio +Nome &archivio: +Modalità a&ggiornamento: +&Formato dell'archivio: +&Livello di compressione: +&Metodo di compressione: +Dimensione &Dizionario: +Dimensioni &Parola: +Dimensione del &blocco solido: +N&umero di flussi (thread) CPU: +Parametri &opzionali: +Opzioni +Crea archivio auto-&estraente +Comprimi file condivisi +Cifratura +Metodo &cifratura: +Cifra anche il &nome dei file +Quantità memoria per compressione: +Quantità memoria per decompressione: +Elimina i file dopo la compressione +4040 +Memorizza collegamenti simbolici +Memorizza collegamenti statici +Memorizza flussi dati alternati +Memorizza sicurezza dei file +4050 +Nessuna +Velocissima +Veloce +Normale +Massima +Ultra +4060 +Aggiungi e sostituisci i file +Aggiorna e aggiungi i file +Aggiorna i file esistenti +Sincronizza i file +4070 +Sfoglia +Tutti i file +Non-solido +Solido +6000 +Copia +Sposta +Copia in: +Sposta in: +Copia in corso... +Spostamento in corso... +Rinomina in corso... +Selezionare la cartella di destinazione. +Operazione non supportata per questa cartella. +Errore nella rinomina del file o cartella +Conferma copia +Sei sicuro di voler copiare questi file nell'archivio +6100 +Conferma l'eliminazione del file +Conferma l'eliminazione della cartella +Conferma l'eliminazione di più elementi +Sei certo di voler eliminare '{0}'? +Sei certo di voler eliminare la cartella '{0}' e tutto il suo contenuto? +Sei certo di voler eliminare questi {0} elementi? +Eliminazione in corso... +Errore nell'eliminazione del file o della cartella +Impossibile spostare un file con percorso lungo nel Cestino +6300 +Crea cartella +Crea file +Nome cartella: +Nome file: +Nuova cartella +Nuovo file +Errore nella creazione della cartella +Errore nella creazione del file +6400 +Commento +&Commento: +Seleziona +Deseleziona +Filtro: +6600 +Proprietà +Cronologia +Messaggi di diagnostica +Messaggio +7100 +Computer +Rete +Documenti +Sistema +7200 +Aggiungi +Estrai +Verifica +Copia +Sposta +Elimina +Proprietà +7300 +Dividi file +&Dividi in: +Di&vidi in più file (dimensione in byte): +Dividi in... +Conferma divisione +Sicuro di voler dividere l'archivio in {0} porzioni? +La dimensione di ciascuna porzione deve essere più piccola della dimensione totale dell'archivio originale +Dimensione non corretta +Dimensione specificata: {0} byte.\nSicuro di voler dividere l'archivio in questo modo? +7400 +Unisci i file +&Unisci in: +Unisci... +Seleziona solo la prima parte del file diviso +Impossibile riconoscere il file come archivio diviso +Impossibile trovare più di una parte dell'archivio diviso +7500 +Calcolo del checksum... +Informazioni sul checksum +CRC checksum sui dati: +CRC checksum su dati e nomi: +7600 +Benchmark +Utilizzo memoria: +Compressione in corso +Decompressione in corso +Valutazione +Valutazione totale +Attuale +Risultante +Utilizzo CPU +Stima / Utilizzo +Passaggi: +7700 +Collegamento +Collega +Origine collegamento: +Destinazione collegamento: +7710 +Tipo di collegamento +Collegamento statico +Collegamento simbolico al file +Collegamento simbolico alla cartella +Giunzione cartella diff --git a/Utils/7-Zip/Lang/ja.txt b/Utils/7-Zip/Lang/ja.txt new file mode 100644 index 000000000..c475e03a0 --- /dev/null +++ b/Utils/7-Zip/Lang/ja.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; : Komuro, Mick, 2chBBS-software +; : Crus Mitsuaki +; 9.07 : Stepanushkin Dmitry +; 9.23 : 2011-06-22 : Stepanushkin Dmitry, nabeshin +; 9.33 : 2014-06-17 : Stepanushkin Dmitry +; 15.00 : 2015-04-30 : Stepanushkin Dmitry +; +; +; +; +; +0 +7-Zip +Japanese +日本語 +401 +OK +キャンセル + + + +ã¯ã„(&Y) +ã„ã„ãˆ(&N) +é–‰ã˜ã‚‹(&C) +ヘルプ + +続行(&C) +440 +ã™ã¹ã¦ã« ã¯ã„(&A) +ã™ã¹ã¦ã« ã„ã„ãˆ(&L) +åœæ­¢ +å†é–‹ +ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰(&B) +フォアグラウンド(&F) +ä¸€æ™‚åœæ­¢(&P) +ä¸€æ™‚åœæ­¢ +本当ã«åœ§ç¸®ã‚’å–りやã‚ã¾ã™ã‹ï¼Ÿ +500 +ファイル(&F) +編集(&E) +表示(&V) +ãŠæ°—ã«å…¥ã‚Š(&A) +ツール(&T) +ヘルプ(&H) +540 +é–‹ã(&O) +7-Zipã§é–²è¦§(&I) +関連付ã‘ã§é–‹ã(&U) +表示(&V) +編集(&E) +åå‰ã®å¤‰æ›´(&M) +コピー(&C)... +移動(&M)... +削除(&D) +ファイル分割(&S)... +ファイルçµåˆ(&B)... +プロパティ(&R) +コメント(&N) +ãƒã‚§ãƒƒã‚¯ã‚µãƒ ã®è¨ˆç®— +比較 +ãƒ•ã‚©ãƒ«ãƒ€ä½œæˆ +ãƒ•ã‚¡ã‚¤ãƒ«ä½œæˆ +é–‰ã˜ã‚‹(&X) +ãƒªãƒ³ã‚¯ä½œæˆ +代替データストリーム(&A) +600 +å…¨ã¦é¸æŠž(&A) +å…¨ã¦é¸æŠžè§£é™¤ +åè»¢é¸æŠž(&I) +é¸æŠž... +é¸æŠžè§£é™¤... +åŒä¸€å½¢å¼é¸æŠž +åŒä¸€å½¢å¼é¸æŠžè§£é™¤ +700 +大ãã„アイコン(&G) +å°ã•ã„アイコン(&M) +一覧(&L) +詳細(&D) +730 +ä¸¦ã¹æ›¿ãˆè§£é™¤ +フラットビュー +&2åˆ†å‰²ç”»é¢ +ツールãƒãƒ¼(&T) +ルートフォルダを開ã +1ã¤ä¸Šã®éšŽå±¤ã¸ +フォルダ履歴... +æœ€æ–°ã®æƒ…å ±ã«æ›´æ–°(&R) +自動更新 +750 +書庫ツールãƒãƒ¼ +標準ツールãƒãƒ¼ +大ããªãƒœã‚¿ãƒ³ +ボタンã®ãƒ†ã‚­ã‚¹ãƒˆè¡¨ç¤º +800 +ãƒ•ã‚©ãƒ«ãƒ€ã‚’ãŠæ°—ã«å…¥ã‚Šã«è¿½åŠ (&A) +ブックマーク +900 +オプション(&O)... +ベンãƒãƒžãƒ¼ã‚¯(&B) +960 +ヘルプã®è¡¨ç¤º(&C)... +7-Zipã«ã¤ã„ã¦(&A)... +1003 +パス +åå‰ +æ‹¡å¼µå­ +フォルダ +サイズ +圧縮後サイズ +属性 +ä½œæˆæ—¥æ™‚ +アクセス日時 +更新日時 +ソリッド +コメント済㿠+æš—å·åŒ– +åˆ†å‰²å‰ +分割後 +辞書 + +種類 +逆 +圧縮方法 +ホストOS +ファイルシステム +ユーザー +グループ +ブロック +コメント +ãƒã‚¸ã‚·ãƒ§ãƒ³ +パスプレフィックス +フォルダ数 +ファイル数 +ãƒãƒ¼ã‚¸ãƒ§ãƒ³ +ボリューム +多é‡ãƒœãƒªãƒ¥ãƒ¼ãƒ æ›¸åº« +オフセット +リンク数 +使用ブロック数 +ボリューム数 + +64ビット +ビッグエンディアン +CPU +物ç†ã‚µã‚¤ã‚º +ヘッダーサイズ +ãƒã‚§ãƒƒã‚¯ã‚µãƒ  +特性 +仮想アドレス +ID +çœç•¥å +作æˆã‚¢ãƒ—リケーション +セクターサイズ +モード +リンク +エラー +åˆè¨ˆã‚µã‚¤ã‚º +空ã領域 +クラスタサイズ +ラベル +ローカルå +プロãƒã‚¤ãƒ€ +NTセキュリティ +代替データストリーム +補助 +削除済㿠+ã¯ãƒ„リー + + +エラー種類 +エラー +エラー +警告 +警告 +ストリーム +代替データストリーム +代替データストリームサイズ +仮想サイズ +è§£å‡å¾Œã‚µã‚¤ã‚º +物ç†ã‚µã‚¤ã‚ºåˆè¨ˆ +ボリュームインデックス +亜類型 +コメント +コードページ + + + +テイルã®ã‚µã‚¤ã‚º +組ã¿è¾¼ã¿ã‚¹ã‚¿ãƒ–ã®ã‚µã‚¤ã‚º +リンク +ãƒãƒ¼ãƒ‰ãƒªãƒ³ã‚¯ +iノード + +読ã¿å–り専用 +2100 +オプション +言語 +言語設定: +外部ツール +編集(&E): +比較(diff)(&D): +2200 +システム +7-Zipã«é–¢é€£ä»˜ã‘るファイル: +å…¨ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ +2301 +シェルコンテキスト(å³ã‚¯ãƒªãƒƒã‚¯ï¼‰ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã«7-Zipを登録 +7-Zipをサブメニュー化ã™ã‚‹ +メニュー項目: +コンテキストメニューã«ã‚¢ã‚¤ã‚³ãƒ³ã‚’表示 +2320 +<フォルダ> +<書庫> +é–‹ã +展開... +圧縮... +書庫をテスト +ã“ã“ã«å±•é–‹ +{0}ã«å±•é–‹ +{0}ã«åœ§ç¸® +圧縮ã—ã¦é›»å­ãƒ¡ãƒ¼ãƒ«é€ä¿¡... +{0}ã«åœ§ç¸®ã—ã¦é›»å­ãƒ¡ãƒ¼ãƒ«é€ä¿¡ +2400 +フォルダ +作業フォルダ(&W) +システム一時フォルダ(&S) +カレントフォルダ(&C) +フォルダ指定(&P): +リムーãƒãƒ–ルドライブã®ã¿ä½¿ç”¨ã™ã‚‹ +一時ファイルã®ãŸã‚ã®å ´æ‰€ã‚’指定ã—ã¦ãã ã•ã„ +2500 +設定 +'..'を表示ã™ã‚‹ +å„ファイルã®å®Ÿéš›ã®ã‚¢ã‚¤ã‚³ãƒ³ã‚’表示ã™ã‚‹ +システム(エクスプローラ)ã®ãƒ¡ãƒ‹ãƒ¥ãƒ¼ã‚‚表示ã™ã‚‹ +行å˜ä½ï¼ˆåˆ—アイテムを一括)ã§é¸æŠžã™ã‚‹(&F) +グリッド線を表示ã™ã‚‹(&G) +シングルクリックã§é–‹ã +カーソル移動ã§é¸æŠžãŒè‡ªå‹•解除ã•れãªã„モード(&A) +大ããªãƒ¡ãƒ¢ãƒªãƒšãƒ¼ã‚¸ã‚’使用ã™ã‚‹(&L) +2900 +7-Zipã«ã¤ã„㦠+7-Zipã¯ãƒ•リーソフトウェアã§ã™ +3000 +è¦æ±‚ã•れãŸé‡ã®ãƒ¡ãƒ¢ãƒªã‚’割り当ã¦ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ +正常ã§ã™ +{0}個ã®ã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆã‚’é¸æŠž +'{0}'フォルダãŒä½œæˆã§ãã¾ã›ã‚“ +ã“ã®æ›¸åº«ã¯æ›´æ–°æ©Ÿèƒ½ãŒã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ +ファイル'{0}'ã¯æ›¸åº«ã¨ã—ã¦é–‹ãã“ã¨ãŒã§ãã¾ã›ã‚“ +æš—å·åŒ–ã•ã‚ŒãŸæ›¸åº«'{0}'ã‚’é–‹ãã“ã¨ãŒã§ãã¾ã›ã‚“。パスワードãŒé–“é•ã£ã¦ã„ã¾ã›ã‚“ã‹ï¼Ÿ +æœªå¯¾å¿œã®æ›¸åº«å½¢å¼ã§ã™ +{0}ãƒ•ã‚¡ã‚¤ãƒ«ã¯æ—¢ã«å­˜åœ¨ã—ã¦ã„ã¾ã™ +'{0}'ファイルãŒå¤‰æ›´ã•れã¾ã—ãŸã€‚\n書庫を更新ã—ã¾ã™ã‹ï¼Ÿ +ファイルを更新ã§ãã¾ã›ã‚“。\n'{0}' +エディタを起動ã§ãã¾ã›ã‚“。 +ã“ã®ãƒ•ァイルã¯ã€ã‚¦ã‚¤ãƒ«ã‚¹ã®ã‚ˆã†ã«è¦‹ãˆã¾ã™ï¼ˆãƒ•ァイルåã«å¤§é‡ã®ã‚¹ãƒšãƒ¼ã‚¹ã‚’å«ã‚“ã§ã„る)。 +パスãŒé•·ã„フォルダã§ã¯ã“ã®æ“作を実行ã§ãã¾ã›ã‚“。 +1ã¤ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ãã ã•ã„ +1ã¤ä»¥ä¸Šã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ãã ã•ã„ +アイテムãŒå¤šéŽãŽã¾ã™ +ファイルを{0}書庫ã¨ã—ã¦é–‹ãã“ã¨ãŒã§ãã¾ã›ã‚“ +ファイルãŒ{0}書庫ã¨ã—ã¦é–‹ã‹ã‚Œã¦ã„ã¾ã™ +書庫ãŒã‚ªãƒ•セットを使用ã—ã¦é–‹ã‹ã‚Œã¦ã„ã¾ã™ +3300 +展開中 +圧縮中 +テスト中 +é–‹ã„ã¦ã„ã¾ã™... +スキャン中... +削除中 +3320 +追加中 +更新中 +è§£æžä¸­ +複製中 +å†åœ§ç¸®ä¸­ +スキップ中 +削除中 +ヘッダーã®ä½œæˆä¸­ +3400 +展開 +展開先(&X): +展開先指定 +3410 +パスå出力方法: +フルパス +パスãªã— +絶対パス +相対パス +3420 +ä¸Šæ›¸ãæ–¹æ³• +上書ãã™ã‚‹ã¨ãã¯ç¢ºèªã™ã‚‹ +常ã«ä¸Šæ›¸ã +ファイルãŒå­˜åœ¨ã™ã‚‹ã¨ãã¯ã‚¹ã‚­ãƒƒãƒ— +自動的ã«ãƒªãƒãƒ¼ãƒ  +ファイルãŒå­˜åœ¨ã™ã‚‹ã¨ãã¯è‡ªå‹•リãƒãƒ¼ãƒ  +3430 +ルートフォルダーã®é‡è¤‡ã‚’å›žé¿ +ファイルã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£å±žæ€§ã‚’復元 +3500 +ファイル上書ãç¢ºèª +出力先ã®ãƒ•ォルダã«ã¯æ—¢ã«ä»¥ä¸‹ã®åŒã˜ãƒ•ァイルãŒå­˜åœ¨ã—ã¾ã™ +ç¾åœ¨ã®ãƒ•ァイル +ã«æ¬¡ã®æ–°ã—ã„ファイルを上書ãã—ã¾ã™ã‹ï¼Ÿ +{0}ãƒã‚¤ãƒˆ +自動的ã«ãƒªãƒãƒ¼ãƒ (&U) +3700 +'{0}'ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ãªã„圧縮方å¼ã§ã™ +'{0}'ã§ãƒ‡ãƒ¼ã‚¿ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ファイルã¯å£Šã‚Œã¦ã„ã¾ã™ +'{0}'ã®CRCãŒé•ã„ã¾ã™ã€‚ファイルã¯å£Šã‚Œã¦ã„ã¾ã™ +æš—å·åŒ–ã•れãŸãƒ•ァイル'{0}'ã§ãƒ‡ãƒ¼ã‚¿ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚パスワードãŒé–“é•ã£ã¦ã„ã¾ã›ã‚“ã‹ï¼Ÿ +æš—å·åŒ–ã•れãŸãƒ•ァイル'{0}'ã®CRCãŒé•ã„ã¾ã™ã€‚パスワードãŒé–“é•ã£ã¦ã„ã¾ã›ã‚“ã‹ï¼Ÿ +3710 +パスワードãŒé–“é•ã£ã¦ã„ã¾ã›ã‚“ã‹ï¼Ÿ +3721 +éžå¯¾å¿œåœ§ç¸®æ–¹æ³• +データエラー +CRCãŒé•ã„ã¾ã™ +データå–得失敗 +データä¸è¶³ +データã®ãƒšã‚¤ãƒ­ãƒ¼ãƒ‰å¾Œã«ãƒ‡ãƒ¼ã‚¿ãŒå­˜åœ¨ã—ã¾ã™ +書庫ã§ã¯ã‚りã¾ã›ã‚“ +ヘッダーエラー +パスワードãŒé–“é•ã£ã¦ã„ã¾ã™ +3763 +書庫先頭ã®å–得失敗 +ç„¡åŠ¹ãªæ›¸åº«å…ˆé ­ + + + +éžå¯¾å¿œæ©Ÿèƒ½ +3800 +パスワード入力 +パスワード入力: +パスワードå†å…¥åŠ›ï¼š +パスワードを表示ã™ã‚‹(&S) +パスワードãŒä¸€è‡´ã—ã¾ã›ã‚“ +パスワードã«ã¯åŠè§’英数記å·(!, #, $, ...)ã®ã¿ã‚’使用ã—ã¦ãã ã•ã„。 +パスワードãŒã‚ã¾ã‚Šã«é•·éŽãŽã¾ã™ +パスワード +3900 +çµŒéŽæ™‚間: +残り時間: +サイズåˆè¨ˆï¼š +速度: +å‡¦ç†æ¸ˆã¿ï¼š +圧縮率: +エラー: +書庫数: +4000 +ファイル圧縮 +圧縮先(&A): +更新方法(&U): +書庫形å¼(&F): +圧縮レベル(&L): +圧縮方å¼(&M): +辞書サイズ(&D): +ワードサイズ(&W): +ソリッドブロックサイズ: +CPUスレッド数: +パラメータ(&P): +オプション +自己展開書庫作æˆ(&X) +共有(編集中ã®ï¼‰ãƒ•ァイルも圧縮 +æš—å·åŒ– +æš—å·åŒ–æ–¹å¼ï¼š +ファイルåã‚’æš—å·åŒ–(&N) +圧縮ã«å¿…è¦ãªãƒ¡ãƒ¢ãƒªï¼š +展開ã«å¿…è¦ãªãƒ¡ãƒ¢ãƒªï¼š +圧縮後ã«å…ƒã®ãƒ•ァイルを削除 +4040 +シンボリックリンクをä¿å­˜ +ãƒãƒ¼ãƒ‰ãƒªãƒ³ã‚¯ã‚’ä¿å­˜ +代替データストリームをä¿å­˜ +ファイルã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£å±žæ€§ã‚’ä¿å­˜ +4050 +無圧縮 +最速 +高速 +標準 +最高 +超圧縮 +4060 +ã™ã¹ã¦ã®ãƒ•ァイル上書ã +ãƒ•ã‚¡ã‚¤ãƒ«è¿½åŠ ã¨æ›´æ–° +変更ã—ãŸãƒ•ァイルã®ã¿æ›´æ–° +ãƒ•ã‚¡ã‚¤ãƒ«ã‚’åŒæœŸã•ã›ã‚‹ +4070 +閲覧 +ã™ã¹ã¦ã®ãƒ•ァイル +ソリッドãªã— +ç„¡åˆ¶é™ +6000 +コピー +移動 +フォルダã¸ã‚³ãƒ”ー: +フォルダã¸ç§»å‹•: +コピーã—ã¦ã„ã¾ã™... +移動ã—ã¦ã„ã¾ã™... +リãƒãƒ¼ãƒ ã—ã¦ã„ã¾ã™... +対象ã®ãƒ•ã‚©ãƒ«ãƒ€ã‚’é¸æŠžã—ã¦ãã ã•ã„。 +ã“ã®ãƒ•ォルダã§ã¯ã€ãã®æ“作ã¯ã‚µãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“。 +ファイルã¾ãŸã¯ãƒ•ォルダã®ãƒªãƒãƒ¼ãƒ ã‚¨ãƒ©ãƒ¼ +ファイルコピーã®ç¢ºèª +本当ã«ãƒ•ァイルを書庫ã«è¿½åŠ ã—ã¾ã™ã‹ï¼Ÿ +6100 +ファイル削除ã®ç¢ºèª +フォルダ削除ã®ç¢ºèª +複数ファイル削除ã®ç¢ºèª +'{0}'を本当ã«å‰Šé™¤ã—ã¾ã™ã‹ï¼Ÿ +'{0}'フォルダã¨ãã®ä¸­èº«ã®ã™ã¹ã¦ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ +ã“れらã®{0}個ã®é …目を本当ã«å‰Šé™¤ã—ã¾ã™ã‹ï¼Ÿ +削除中... +ファイルã¾ãŸã¯ãƒ•ォルダã®å‰Šé™¤ã‚¨ãƒ©ãƒ¼ +ファイルã®ãƒ‘スãŒé•·ã™ãŽã‚‹ãŸã‚ã€ãƒ•ァイルをã”ã¿ç®±ã«ç§»å‹•ã§ãã¾ã›ã‚“ +6300 +ãƒ•ã‚©ãƒ«ãƒ€ä½œæˆ +ãƒ•ã‚¡ã‚¤ãƒ«ä½œæˆ +フォルダå: +ファイルå: +æ–°ã—ã„フォルダ +æ–°ã—ã„ファイル +フォルダ作æˆã‚¨ãƒ©ãƒ¼ +ファイル作æˆã‚¨ãƒ©ãƒ¼ +6400 +コメント +コメント(&C): +é¸æŠž +é¸æŠžè§£é™¤ +マスク: +6600 +プロパティ +フォルダ履歴 +è¨ºæ–­çµæžœ +メッセージ +7100 +コンピュータ +ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ +ドキュメント +システム +7200 +追加 +展開 +テスト +コピー +移動 +削除 +情報 +7300 +ファイル分割 +分割先(&S): +書庫をサイズã§åˆ†å‰²(&V): +分割中... +分割ã®ç¢ºèª +{0}個ã«ãƒ•ァイルを分割ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ +分割後ã®ã‚µã‚¤ã‚ºã¯å…ƒã®ãƒ•ァイルサイズよりå°ã•ã„サイズを指定ã—ã¦ãã ã•ã„ +䏿­£ãªãƒœãƒªãƒ¥ãƒ¼ãƒ ã‚µã‚¤ã‚º +é¸æŠžã•れãŸãƒœãƒªãƒ¥ãƒ¼ãƒ ã‚µã‚¤ã‚ºï¼š{0}ãƒã‚¤ãƒˆ\nã“ã®ã‚µã‚¤ã‚ºã«æ›¸åº«ã‚’分割ã—ã¾ã™ã‹ï¼Ÿ +7400 +ファイルçµåˆ +çµåˆå…ˆ(&C): +çµåˆä¸­... +分割ファイルã®å…ˆé ­ã®ãƒ•ァイルã ã‘é¸æŠžã—ã¦ãã ã•ã„ +分割ファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ +分割ファイルã®ä¸€éƒ¨ã—ã‹è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ +7500 +ãƒã‚§ãƒƒã‚¯ã‚µãƒ è¨ˆç®—中... +ãƒã‚§ãƒƒã‚¯ã‚µãƒ æƒ…å ± +データã®CRCãƒã‚§ãƒƒã‚¯ã‚µãƒ ï¼š +データã¨åå‰ã®CRCãƒã‚§ãƒƒã‚¯ã‚µãƒ ï¼š +7600 +ベンãƒãƒžãƒ¼ã‚¯ +å¿…è¦ãƒ¡ãƒ¢ãƒªï¼š +圧縮中 +展開中 +評価 +ç·åˆè©•価 +ç¾åœ¨ +çµæžœ +CPU使用率 +評価 / 使用率 +テスト回数: +7700 +リンク +ãƒªãƒ³ã‚¯ä½œæˆ +リンク元: +リンク先: +7710 +リンク種類 +ãƒãƒ¼ãƒ‰ãƒªãƒ³ã‚¯ +ファイルã®ã‚·ãƒ³ãƒœãƒªãƒƒã‚¯ãƒªãƒ³ã‚¯ +ディレクトリã®ã‚·ãƒ³ãƒœãƒªãƒƒã‚¯ãƒªãƒ³ã‚¯ +ディレクトリã®ã‚¸ãƒ£ãƒ³ã‚¯ã‚·ãƒ§ãƒ³ diff --git a/Utils/7-Zip/Lang/ka.txt b/Utils/7-Zip/Lang/ka.txt new file mode 100644 index 000000000..a920e7318 --- /dev/null +++ b/Utils/7-Zip/Lang/ka.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.23 : 2011-09-25 : Translated by Giorgi Maghlakelidze, original translation by Dimitri Gogelia, +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Georgian +ქáƒáƒ áƒ—ული +401 +OK +გáƒáƒ£áƒ¥áƒ›áƒ”ბრ+ + + +&დიáƒáƒ® +&áƒáƒ áƒ +&დáƒáƒ®áƒ£áƒ áƒ•რ+დáƒáƒ®áƒ›áƒáƒ áƒ”ბრ+ +&გáƒáƒ’რძელებრ+440 +დიáƒáƒ® &ყველáƒáƒ¡áƒáƒ—ვის +áƒáƒ áƒ ყვე&ლáƒáƒ¡áƒáƒ—ვის +შეწყვეტრ+ხელáƒáƒ®áƒšáƒ +&ფáƒáƒœáƒ£áƒ áƒáƒ“ +&წინრპლáƒáƒœáƒ–ე +&შეჩერებრ+&შეჩერებული +ნáƒáƒ›áƒ“ვილáƒáƒ“ გსურთ მáƒáƒ¥áƒ›áƒ”დების შეწყვეტáƒ? +500 +&ფáƒáƒ˜áƒšáƒ˜ +&დáƒáƒ›áƒ£áƒ¨áƒáƒ•ებრ+&ხედი +რ&ჩეულები +&ხელსáƒáƒ¬áƒ§áƒáƒ”ბი +&დáƒáƒ®áƒ›áƒáƒ áƒ”ბრ+540 +&გáƒáƒ®áƒ¡áƒœáƒ +გáƒáƒ®áƒ¡áƒœáƒ &შიგნით +გáƒáƒ®áƒ¡áƒœáƒ გáƒ&რეთ +დáƒ&თვáƒáƒšáƒ˜áƒ”რებრ+დáƒ&მუშáƒáƒ•ებრ+გáƒáƒ“áƒ&რქმევრ+&áƒáƒ¡áƒšáƒ˜áƒ¡ მáƒáƒ—áƒáƒ•სებáƒ... +&გáƒáƒ“áƒáƒ¢áƒáƒœáƒ... +&წáƒáƒ¨áƒšáƒ +&ფáƒáƒ˜áƒšáƒ˜áƒ¡ დáƒáƒ§áƒáƒ¤áƒ... +&ფáƒáƒ˜áƒšáƒ”ბის გáƒáƒ”რთიáƒáƒœáƒ”ბáƒ... +თ&ვისებები +კáƒáƒ›áƒ”ნ&ტáƒáƒ áƒ˜ +სáƒáƒ™áƒáƒœáƒ¢áƒ áƒáƒšáƒ ჯáƒáƒ›áƒ˜áƒ¡ დáƒáƒ—ვლრ+Diff +სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ის შექმნრ+ფáƒáƒ˜áƒšáƒ˜áƒ¡ შექმნრ+გ&áƒáƒ›áƒáƒ¡áƒ•ლრ+600 +&ყველáƒáƒ¤áƒ áƒ˜áƒ¡ მáƒáƒœáƒ˜áƒ¨áƒ•ნრ+მáƒáƒœáƒ˜áƒ¨áƒ•ნის გáƒáƒ£áƒ¥áƒ›áƒ”ბრ+მáƒáƒœáƒ˜áƒ¨áƒ•ნის შებრუნებრ+მáƒáƒ˜áƒœáƒ˜áƒ¨áƒœáƒáƒ¡... +მáƒáƒ˜áƒ®áƒ¡áƒœáƒáƒ¡ მáƒáƒœáƒ˜áƒ¨áƒ•ნáƒ... +მáƒáƒœáƒ˜áƒ¨áƒ•ნრტიპის მიხედვით +მáƒáƒœáƒ˜áƒ¨áƒ•ნის მáƒáƒ®áƒ¡áƒœáƒ ტიპის მიხედვით +700 +&დიდი ხáƒáƒ¢áƒ£áƒšáƒ”ბი +&პáƒáƒ¢áƒáƒ áƒ ხáƒáƒ¢áƒ£áƒšáƒ”ბი +&სირ+&დáƒáƒ¬áƒ•რილებით +730 +დáƒáƒ£áƒšáƒáƒ’ებელი +ბრტყელი ხედი +&2 პáƒáƒœáƒ”ლი +&ხელსáƒáƒ¬áƒ§áƒáƒ—რზáƒáƒšáƒ˜ +ძირეული სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ის გáƒáƒ®áƒ¡áƒœáƒ +სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“იდáƒáƒœ გáƒáƒ¡áƒ•ლრ+სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ეების ისტáƒáƒ áƒ˜áƒ... +&გáƒáƒœáƒáƒ®áƒšáƒ”ბრ+750 +დáƒáƒáƒ áƒ¥áƒ˜áƒ•ების ზáƒáƒšáƒ˜ +ძირითáƒáƒ“ი ზáƒáƒšáƒ˜ +დიდი ღილáƒáƒ™áƒ”ბი +წáƒáƒ áƒ¬áƒ”რები ღილáƒáƒ™áƒ”ბზე +800 +&სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ის რჩეულებში დáƒáƒ›áƒáƒ¢áƒ”ბრრáƒáƒ’áƒáƒ áƒª +სáƒáƒœáƒ˜áƒ¨áƒœáƒ” +900 +&გáƒáƒ›áƒáƒ áƒ—ვáƒ... +&წáƒáƒ áƒ›áƒáƒ“áƒáƒ‘ის შემáƒáƒ¬áƒ›áƒ”ბრ+960 +სáƒáƒ áƒ©áƒ”ვი... +7-Zip-ის შესáƒáƒ®áƒ”ბ... +1003 +მდებáƒáƒ áƒ”áƒáƒ‘რ+სáƒáƒ®áƒ”ლი +გáƒáƒ¤áƒáƒ áƒ—áƒáƒ”ბრ+სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ე +ზáƒáƒ›áƒ +შეკუმშულის ზáƒáƒ›áƒ +áƒáƒ¢áƒ áƒ˜áƒ‘უტები +შექმნილირ+გáƒáƒ®áƒ¡áƒœáƒ˜áƒšáƒ˜áƒ +შეცვლილირ+უწყვეტი +კáƒáƒ›áƒ”ნტáƒáƒ áƒ˜ +დáƒáƒ¨áƒ˜áƒ¤áƒ áƒ£áƒšáƒ˜áƒ +დáƒáƒ§áƒáƒ¤áƒ სáƒáƒœáƒáƒ› +დáƒáƒ§áƒáƒ¤áƒ შემდეგ +ლექსიკáƒáƒœáƒ˜ +CRC +ტიპი +áƒáƒœáƒ¢áƒ˜ +მეთáƒáƒ“ი +სისტემრ+ფáƒáƒ˜áƒšáƒ£áƒ áƒ˜ სისტემრ+მáƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბელი +ჯგუფი +ბლáƒáƒ™áƒ˜ +კáƒáƒ›áƒ”ნტáƒáƒ áƒ˜ +მდებáƒáƒ áƒ”áƒáƒ‘რ+მდებáƒáƒ áƒ”áƒáƒ‘ის თáƒáƒ•სáƒáƒ áƒ—ი +სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ეები +ფáƒáƒ˜áƒšáƒ”ბი +ვერსირ+ტáƒáƒ›áƒ˜ +მრáƒáƒ•áƒáƒšáƒ¢áƒáƒ›áƒ˜áƒáƒœáƒ˜ +წáƒáƒœáƒáƒªáƒ•ლებრ+ბმულები +ბლáƒáƒ™áƒ”ბი +ტáƒáƒ›áƒ”ბი + +64 ბიტი +Big-endian +CPU +ფიზიკური ზáƒáƒ›áƒ +სáƒáƒ—áƒáƒ£áƒ áƒ”ბის ზáƒáƒ›áƒ +სáƒáƒ™áƒáƒœáƒ¢áƒ áƒáƒšáƒ ჯáƒáƒ›áƒ˜ +თვისებები +ვირტუáƒáƒšáƒ£áƒ áƒ˜ მისáƒáƒ›áƒáƒ áƒ—ი +ID +მáƒáƒ™áƒšáƒ” სáƒáƒ®áƒ”ლი +შემქმნელი პრáƒáƒ’რáƒáƒ›áƒ +სექტáƒáƒ áƒ˜áƒ¡ ზáƒáƒ›áƒ +რეჟიმი +ბმული +შეცდáƒáƒ›áƒ +სრული მáƒáƒªáƒ£áƒšáƒáƒ‘რ+თáƒáƒ•ისუფáƒáƒšáƒ˜ სივრცე +კლáƒáƒ¡áƒ¢áƒ”რის ზáƒáƒ›áƒ +წáƒáƒ áƒ¬áƒ”რრ+áƒáƒ“გილáƒáƒ‘რივი სáƒáƒ®áƒ”ლი +მáƒáƒ›áƒ¬áƒáƒ“ებელი +2100 +გáƒáƒ›áƒáƒ áƒ—ვრ+ენები +ენáƒ: +რედáƒáƒ¥áƒ¢áƒáƒ áƒ˜ +რედáƒáƒ¥áƒ¢áƒáƒ áƒ˜: +&Diff: +2200 +სისტემრ+áƒáƒ¡áƒáƒªáƒ˜áƒ áƒ”ბრ7-Zip-თáƒáƒœ: +2301 +7-Zip-ის გáƒáƒ áƒ¡áƒ˜áƒ¡ კáƒáƒœáƒ¢áƒ”ქსტურ მენიუში ჩáƒáƒ“გმრ+კáƒáƒ¡áƒ™áƒáƒ“ური კáƒáƒœáƒ¢áƒ”ქსტური მენიუ +კáƒáƒœáƒ¢áƒ”ქსტური მენიუს შემáƒáƒ“გენლáƒáƒ‘áƒ: +2320 +<სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ე> +<áƒáƒ áƒ¥áƒ˜áƒ•ი> +áƒáƒ áƒ¥áƒ˜áƒ•ის გáƒáƒ®áƒ¡áƒœáƒ +ფáƒáƒ˜áƒšáƒ”ბის áƒáƒ›áƒáƒ¦áƒ”ბáƒ... +áƒáƒ áƒ¥áƒ˜áƒ•ში ჩáƒáƒ›áƒáƒ¢áƒ”ბáƒ... +áƒáƒ áƒ¥áƒ˜áƒ•ის შემáƒáƒ¬áƒ›áƒ”ბრ+áƒáƒ›áƒáƒ¦áƒ”ბრáƒáƒ¥ +áƒáƒ›áƒáƒ¦áƒ”ბრ{0}-ში +{0}-ში ჩáƒáƒ›áƒáƒ¢áƒ”ბრ+შეკუმშვრდრელფáƒáƒ¡áƒ¢áƒ˜áƒ— გáƒáƒ’ზáƒáƒ•ნáƒ... +{0}-ში შეკუმშვრდრელფáƒáƒ¡áƒ¢áƒ˜áƒ— გáƒáƒ’ზáƒáƒ•ნრ+2400 +სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ეები +&მუშáƒáƒáƒ‘ის სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ე +&სისტემური დრáƒáƒ”ბითი სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ე +&მიმდინáƒáƒ áƒ” +&მითითებული: +გáƒáƒ›áƒáƒ˜áƒ§áƒ”ნებრცვლáƒáƒ“ი მეხსიერების მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ებისთვის +მიუთითეთ მდებáƒáƒ áƒ”áƒáƒ‘რდრáƒáƒ”ბითი áƒáƒ áƒ¥áƒ˜áƒ•ებისáƒáƒ—ვის. +2500 +გáƒáƒ›áƒáƒ áƒ—ვრ+".." ელემენტის ჩვენებრ+ფáƒáƒ˜áƒšáƒ—რნáƒáƒ›áƒ“ვილი ხáƒáƒ¢áƒ£áƒšáƒ”ბის ჩვენებრ+სისტემური მენიუს ჩვენებრ+კურსáƒáƒ áƒ˜ &მთელ სტრიქáƒáƒœáƒ–ე +ჩვენებრ&ცხრილის სáƒáƒ®áƒ˜áƒ— +ელემენტთრგáƒáƒ®áƒ¡áƒœáƒ ერთი წკáƒáƒžáƒ˜áƒ— +მáƒáƒœáƒ˜áƒ¨áƒœáƒ•ის &áƒáƒšáƒ¢áƒ”რნáƒáƒ¢áƒ˜áƒ£áƒšáƒ˜ რეჟიმი +&დიდი მეხსიერების ბლáƒáƒ™áƒ”ბის გáƒáƒ›áƒáƒ§áƒ”ნებრ+2900 +7-Zip-ის შესáƒáƒ®áƒ”ბ +7-Zip áƒáƒ áƒ˜áƒ¡ თáƒáƒ•ისუფლáƒáƒ“ გáƒáƒ•რცელებáƒáƒ“ი პრáƒáƒ’რáƒáƒ›áƒ£áƒšáƒ˜ უზრუნველყáƒáƒ¤áƒ. +3000 +შეუძლებელირსáƒáƒ­áƒ˜áƒ áƒ ზáƒáƒ›áƒ˜áƒ¡ მეხსიერების გáƒáƒ›áƒáƒ§áƒáƒ¤áƒ +შეცდáƒáƒ›áƒ”ბი áƒáƒ  მáƒáƒ˜áƒ«áƒ”ბნრ+მáƒáƒœáƒ˜áƒ¨áƒœáƒ£áƒšáƒ˜áƒ {0} áƒáƒ‘იექტი +ვერ მáƒáƒ®áƒ”რხდრ'{0}' სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ეს შექმნრ+áƒáƒ› ტიპის áƒáƒ áƒ¥áƒ˜áƒ•ისáƒáƒ—ვის ცვლილების áƒáƒžáƒ”რáƒáƒªáƒ˜áƒ ხელმიუწვდáƒáƒ›áƒ”ლიáƒ. +'{0}' ფáƒáƒ˜áƒšáƒ˜áƒ¡ áƒáƒ áƒ¥áƒ˜áƒ•áƒáƒ“ გáƒáƒ®áƒ¡áƒœáƒ ვერ მáƒáƒ®áƒ”რხდრ+'{0}' დáƒáƒ¨áƒ˜áƒ¤áƒ áƒ£áƒšáƒ˜ áƒáƒ áƒ¥áƒ˜áƒ•ის გáƒáƒ®áƒ¡áƒœáƒ ვერ მáƒáƒ®áƒ”რხდáƒ. áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜ პáƒáƒ áƒáƒšáƒ˜? +áƒáƒ áƒ¥áƒ˜áƒ•თრáƒáƒ› სáƒáƒ®áƒ”áƒáƒ‘ის გáƒáƒ®áƒ¡áƒœáƒ შეუძლებელირ+ფáƒáƒ˜áƒšáƒ˜ '{0}' უკვე áƒáƒ áƒ¡áƒ”ბáƒáƒ‘ს +ფáƒáƒ˜áƒšáƒ˜ '{0}' შეიცვáƒáƒšáƒ.\nგნებáƒáƒ•თ მისი áƒáƒ áƒ¥áƒ˜áƒ•ში გáƒáƒœáƒáƒ®áƒšáƒ”ბáƒ? +შეუძლებელირ\n'{0}'-ის გáƒáƒœáƒáƒ®áƒšáƒ”ბრ+შეუძლებელირრედáƒáƒ¥áƒ¢áƒáƒ áƒ˜áƒ¡ გáƒáƒ¨áƒ•ებáƒ. +ფáƒáƒ˜áƒšáƒ˜ შესáƒáƒ«áƒšáƒáƒ áƒáƒ¦áƒ›áƒáƒ©áƒœáƒ“ეს ვირუსი (სáƒáƒ®áƒ”ლი შეიცáƒáƒ•ს ძáƒáƒšáƒ˜áƒáƒœ ბევრ თáƒáƒ•მáƒáƒ§áƒ áƒ˜áƒš ჰáƒáƒ áƒ¡). +შეუძლებელირგრძელ-სáƒáƒ®áƒ”ლიáƒáƒœáƒ˜ სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“იდáƒáƒœ ქმედების შესრულებრ+უნდრáƒáƒ˜áƒ áƒ©áƒ˜áƒáƒ— ერთი ფáƒáƒ˜áƒšáƒ˜ +უნდრáƒáƒ˜áƒ áƒ©áƒ˜áƒáƒ— ერთი áƒáƒœ მეტი ფáƒáƒ˜áƒšáƒ˜ +მეტისმეტáƒáƒ“ ბევრი ელემენტი +3300 +მიმდინáƒáƒ áƒ”áƒáƒ‘ს áƒáƒ›áƒáƒ¦áƒ”ბრ+მიმდინáƒáƒ áƒ”áƒáƒ‘ს შეკუმშვრ+მიმდინáƒáƒ áƒ”áƒáƒ‘ს შემáƒáƒ¬áƒ›áƒ”ბრ+მიმდინáƒáƒ áƒ”áƒáƒ‘ს გáƒáƒ®áƒ¡áƒœáƒ... +მიმდინáƒáƒ áƒ”áƒáƒ‘ს áƒáƒ›áƒáƒ™áƒ˜áƒ—ხვáƒ... +3400 +áƒáƒ›áƒáƒ¦áƒ”ბრ+áƒ&მáƒáƒ¦áƒ”ბáƒ: +მიუთითეთ áƒáƒ“გილი áƒáƒ›áƒáƒ¡áƒáƒ¦áƒ”ბი ფáƒáƒ˜áƒšáƒ”ბისáƒáƒ—ვის. +3410 +მდებáƒáƒ áƒ”áƒáƒ‘რ+სრული მდებáƒáƒ áƒ”áƒáƒ‘რ+მდებáƒáƒ áƒ”áƒáƒ‘ის გáƒáƒ áƒ”შე +3420 +ზედგáƒáƒ“áƒáƒ¬áƒ”რრ+ზედგáƒáƒ“áƒáƒ¬áƒ”რის დáƒáƒ¡áƒ¢áƒ£áƒ áƒ˜ +ზედგáƒáƒ“áƒáƒ¬áƒ”რრდáƒáƒ¡áƒ áƒ£áƒ áƒ˜áƒ¡ გáƒáƒ áƒ”შე +áƒáƒ áƒ¡áƒ”ბული ფáƒáƒ˜áƒšáƒ”ბის გáƒáƒ›áƒáƒ¢áƒáƒ•ებრ+áƒáƒ•ტáƒ-გáƒáƒ“áƒáƒ áƒ¥áƒ›áƒ”ვრ+áƒáƒ áƒ¡áƒ”ბული ფáƒáƒ˜áƒšáƒ”ბის áƒáƒ•ტáƒ-გáƒáƒ“áƒáƒ áƒ¥áƒ›áƒ”ვრ+3500 +ფáƒáƒ˜áƒšáƒ˜áƒ¡ ზედგáƒáƒ“áƒáƒ¬áƒ”რის დáƒáƒ¡áƒ¢áƒ£áƒ áƒ˜ +სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ე უკვე შეიცáƒáƒ•ს დáƒáƒ›áƒ£áƒ¨áƒáƒ•ებულ ფáƒáƒ˜áƒšáƒ¡. +გსურთ ჩáƒáƒáƒœáƒáƒªáƒ•ლáƒáƒ— áƒáƒ áƒ¡áƒ”ბული ფáƒáƒ˜áƒšáƒ˜ +áƒáƒ®áƒáƒšáƒ˜ ფáƒáƒ˜áƒšáƒ˜áƒ—? +{0} ბáƒáƒ˜áƒ¢áƒ˜ +áƒ&ვტáƒ-გáƒáƒ“áƒáƒ áƒ¥áƒ›áƒ”ვრ+3700 +შეკუმშვის შეუთáƒáƒ•სებáƒáƒ“ი მეთáƒáƒ“ი '{0}'-თვის. +მáƒáƒœáƒáƒªáƒ”მების შეცდáƒáƒ›áƒ '{0}'-ში. ფáƒáƒ˜áƒšáƒ˜ დáƒáƒ–იáƒáƒœáƒ”ბულიáƒ. +CRC-ის შეცდáƒáƒ›áƒ '{0}'-ში. ფáƒáƒ˜áƒšáƒ˜ დáƒáƒ–იáƒáƒœáƒ”ბულიáƒ. +მáƒáƒœáƒáƒªáƒ”მების შეცდáƒáƒ›áƒ დáƒáƒ¨áƒ˜áƒ¤áƒ áƒ£áƒš ფáƒáƒ˜áƒšáƒ¨áƒ˜ '{0}'. áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜ პáƒáƒ áƒáƒšáƒ˜? +CRC ჩáƒáƒ˜áƒ¨áƒáƒšáƒ დáƒáƒ¨áƒ˜áƒ¤áƒ áƒ£áƒš ფáƒáƒ˜áƒšáƒ¨áƒ˜ '{0}'. áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜ პáƒáƒ áƒáƒšáƒ˜? +3800 +პáƒáƒ áƒáƒšáƒ˜áƒ¡ შეყვáƒáƒœáƒ +შეიყვáƒáƒœáƒ”თ პáƒáƒ áƒáƒšáƒ˜: +პáƒáƒ áƒáƒšáƒ˜ ხელáƒáƒ®áƒšáƒ: +პáƒáƒ áƒáƒšáƒ˜áƒ¡ &ჩვენებრ+პáƒáƒ áƒáƒšáƒ”ბი áƒáƒ  დáƒáƒ”მთხვრ+პáƒáƒ áƒáƒšáƒáƒ“ შეიყვáƒáƒœáƒ”თ მხáƒáƒšáƒáƒ“ ლáƒáƒ—ინური áƒáƒ¡áƒáƒ”ბი, ციფრები დრგáƒáƒœáƒ¡áƒáƒ™áƒ£áƒ—ერებული სიმბáƒáƒšáƒáƒ”ბი (!, #, $, ...) +პáƒáƒ áƒáƒšáƒ˜ მეტისმეტáƒáƒ“ გრძელირ+პáƒáƒ áƒáƒšáƒ˜ +3900 +გáƒáƒ¡áƒ£áƒšáƒ˜ დრáƒ: +დáƒáƒ áƒ©áƒ”ნილი დრáƒ: +ჯáƒáƒ›áƒ£áƒ áƒ˜ ზáƒáƒ›áƒ: +სიჩქáƒáƒ áƒ”: +დáƒáƒ›áƒ£áƒ¨áƒáƒ•ებული: +შეკუმშვის დáƒáƒœáƒ”: +შეცდáƒáƒ›áƒ: +áƒáƒ áƒ¥áƒ˜áƒ•ები: +4000 +áƒáƒ áƒ¥áƒ˜áƒ•ში ჩáƒáƒ›áƒáƒ¢áƒ”ბრ+&áƒáƒ áƒ¥áƒ˜áƒ•ი: +&გáƒáƒœáƒáƒ®áƒšáƒ”ბის რეჟიმი: +áƒáƒ áƒ¥áƒ˜áƒ•ის &ფáƒáƒ áƒ›áƒáƒ¢áƒ˜: +შეკუმშვის &დáƒáƒœáƒ”: +შეკუმშვის &მეთáƒáƒ“ი: +&ლექსიკáƒáƒœáƒ˜áƒ¡ ზáƒáƒ›áƒ: +&სიტყვის ზáƒáƒ›áƒ: +უწყვეტი ბლáƒáƒ™áƒ˜áƒ¡ ზáƒáƒ›áƒ: +CPU ნáƒáƒ™áƒáƒ“ების áƒáƒ“ენáƒáƒ‘áƒ: +&პáƒáƒ áƒáƒ›áƒ”ტრები: +დáƒáƒ›áƒáƒ¢áƒ”ბითი +შეიქმნáƒáƒ¡ SF&X áƒáƒ áƒ¥áƒ˜áƒ•ი +გáƒáƒ–იáƒáƒ áƒ”ბული ფáƒáƒ˜áƒšáƒ”ბის შეკუმშვრ+დáƒáƒ¨áƒ˜áƒ¤áƒ áƒ•რ+დáƒáƒ¨áƒ˜áƒ¤áƒ áƒ•ის მეთáƒáƒ“ი: +ფáƒáƒ˜áƒšáƒ—რ&სáƒáƒ®áƒ”ლების დáƒáƒ¨áƒ˜áƒ¤áƒ áƒ•რ+მეხსიერებრშეკუმშვისáƒáƒ—ვის: +მეხსიერებრáƒáƒ›áƒáƒ¦áƒ”ბისáƒáƒ—ვის: +4050 +შეკუმშვის გáƒáƒ áƒ”შე +უსწრáƒáƒ¤áƒ”სი +სწრáƒáƒ¤áƒ˜ +ჩვეულებრივი +მáƒáƒ¦áƒáƒšáƒ˜ +უმáƒáƒ¦áƒšáƒ”სი +4060 +ფáƒáƒ˜áƒšáƒ—რდáƒáƒ›áƒáƒ¢áƒ”ბრდრშეცვლრ+ფáƒáƒ˜áƒšáƒ—რგáƒáƒœáƒáƒ®áƒšáƒ”ბრდრდáƒáƒ›áƒáƒ¢áƒ”ბრ+ფáƒáƒ˜áƒšáƒ—რგáƒáƒœáƒáƒ®áƒšáƒ”ბრ+ფáƒáƒ˜áƒšáƒ—რსინქრáƒáƒœáƒ˜áƒ–ებრ+4070 +დáƒáƒ—ვáƒáƒšáƒ˜áƒ”რებრ+ყველრფáƒáƒ˜áƒšáƒ˜ +წყვეტილი +უწყვეტი +6000 +áƒáƒ¡áƒšáƒ˜áƒ¡ áƒáƒ¦áƒ”ბრ+გáƒáƒ“áƒáƒ¢áƒáƒœáƒ +áƒáƒ¡áƒšáƒ˜áƒ¡ მáƒáƒ—áƒáƒ•სებáƒ: +გáƒáƒ“áƒáƒ¢áƒáƒœáƒ: +áƒáƒ¡áƒšáƒ˜áƒ¡ áƒáƒ¦áƒ”ბáƒ... +გáƒáƒ“áƒáƒ¢áƒáƒœáƒ... +გáƒáƒ“áƒáƒ áƒ¥áƒ›áƒ”ვáƒ... +áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ დáƒáƒœáƒ˜áƒ¨áƒœáƒ£áƒšáƒ”ბის სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ე. +ქმედებრშუთáƒáƒ•სებელირმიმდინáƒáƒ áƒ” სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ესთáƒáƒœ. +ფáƒáƒ˜áƒšáƒ˜áƒ¡ áƒáƒœ სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ის გáƒáƒ“áƒáƒ áƒ¥áƒ›áƒ”ვის შეცდáƒáƒ›áƒ +ფáƒáƒ˜áƒšáƒ˜áƒ¡ áƒáƒ¡áƒšáƒ˜áƒ¡ შექმნის დáƒáƒ¡áƒ¢áƒ£áƒ áƒ˜ +ნáƒáƒ“მვილáƒáƒ“ გსურთ ფáƒáƒ˜áƒšáƒ”ბის áƒáƒ áƒ¥áƒ˜áƒ•ში ჩáƒáƒ›áƒáƒ¢áƒ”ბრ+6100 +ფáƒáƒ˜áƒšáƒ˜áƒ¡ წáƒáƒ¨áƒšáƒ˜áƒ¡ თáƒáƒœáƒ®áƒ›áƒáƒ‘რ+სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ის წáƒáƒ¨áƒšáƒ˜áƒ¡ თáƒáƒœáƒ®áƒ›áƒáƒ‘რ+რáƒáƒ›áƒ“ენიმე ფáƒáƒ˜áƒšáƒ˜áƒ¡ წáƒáƒ¨áƒšáƒ˜áƒ¡ თáƒáƒœáƒ®áƒ›áƒáƒ‘რ+დáƒáƒ áƒ¬áƒ›áƒ£áƒœáƒ”ბული ხáƒáƒ áƒ—, რáƒáƒ› გინდáƒáƒ— წáƒáƒ¨áƒáƒšáƒáƒ— '{0}'? +დáƒáƒ áƒ¬áƒ›áƒ£áƒœáƒ”ბული ხáƒáƒ áƒ—, რáƒáƒ› გინდáƒáƒ— წáƒáƒ¨áƒáƒšáƒáƒ— '{0}' სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ე დრმთელი მისი შიგთáƒáƒ•სი? +დáƒáƒ áƒ¬áƒ›áƒ£áƒœáƒ”ბული ხáƒáƒ áƒ—, რáƒáƒ› გინდáƒáƒ— წáƒáƒ¨áƒáƒšáƒáƒ— {0} ელემენტები? +იშლებáƒ... +ფáƒáƒ˜áƒšáƒ˜áƒ¡ áƒáƒœ სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ის წáƒáƒ¨áƒšáƒ˜áƒ¡ შეცდáƒáƒ›áƒ +სისტემáƒáƒ¡ áƒáƒ  შეუძლირგáƒáƒ“áƒáƒ˜áƒ¢áƒáƒœáƒáƒ¡ სáƒáƒœáƒáƒ’ვე ყუთში ფáƒáƒ˜áƒšáƒ˜ მეტისმეტáƒáƒ“ გრძელი მისáƒáƒ›áƒáƒ áƒ—ით. +6300 +სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ის შექმნრ+ფáƒáƒ˜áƒšáƒ˜áƒ¡ შექმნრ+სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ის სáƒáƒ®áƒ”ლი: +ფáƒáƒ˜áƒšáƒ˜áƒ¡ სáƒáƒ®áƒ”ლი: +áƒáƒ®áƒáƒšáƒ˜ სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ე +áƒáƒ®áƒáƒšáƒ˜ ფáƒáƒ˜áƒšáƒ˜ +შეცდáƒáƒ›áƒ სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ის შექმნისáƒáƒ¡ +შეცდáƒáƒ›áƒ ფáƒáƒ˜áƒšáƒ˜áƒ¡ შექმნისáƒáƒ¡ +6400 +კáƒáƒ›áƒ”ნტáƒáƒ áƒ˜ +&კáƒáƒ›áƒ”ნტáƒáƒ áƒ˜: +მáƒáƒœáƒ˜áƒ¨áƒ•ნრ+მáƒáƒœáƒ˜áƒ¨áƒ•ნის გáƒáƒ£áƒ¥áƒ›áƒ”ბრ+ნიღáƒáƒ‘ი: +6600 +თვისებები +სáƒáƒ¥áƒáƒ¦áƒáƒšáƒ“ეთრისტáƒáƒ áƒ˜áƒ +დიáƒáƒ’ნáƒáƒ¡áƒ¢áƒ˜áƒ™áƒ£áƒ áƒ˜ შეტყáƒáƒ‘ინებრ+შეტყáƒáƒ‘ინებრ+7100 +კáƒáƒ›áƒžáƒ˜áƒ£áƒ¢áƒ”რი +ქსელი +დáƒáƒ™áƒ£áƒ›áƒ”ნტები +სისტემრ+7200 +დáƒáƒ›áƒáƒ¢áƒ”ბრ+áƒáƒ›áƒáƒ¦áƒ”ბრ+შემáƒáƒ¬áƒ›áƒ”ბრ+áƒáƒ¡áƒšáƒ˜ +გáƒáƒ“áƒáƒ¢áƒáƒœáƒ +წáƒáƒ¨áƒšáƒ +ცნáƒáƒ‘ები +7300 +ფáƒáƒ˜áƒšáƒ˜áƒ¡ დáƒáƒ§áƒáƒ¤áƒ +&დáƒáƒ˜áƒ§áƒáƒ¤áƒ: +დáƒáƒ˜áƒ§áƒáƒ¡ &ტáƒáƒ›áƒ”ბáƒáƒ“, ზáƒáƒ›áƒ˜áƒ— (ბáƒáƒ˜áƒ¢áƒ”ბში): +დáƒáƒ§áƒáƒ¤áƒ... +დáƒáƒ§áƒáƒ¤áƒ˜áƒ¡ დáƒáƒ¡áƒ¢áƒ£áƒ áƒ˜ +ნáƒáƒ›áƒ“ვილáƒáƒ“ გსურთ ფáƒáƒ˜áƒšáƒ˜áƒ¡ დáƒáƒ§áƒáƒ¤áƒ {0} ნáƒáƒ¬áƒ˜áƒšáƒáƒ“? +ნáƒáƒ¬áƒ˜áƒšáƒ˜áƒ¡ ზáƒáƒ›áƒ უნდრიყáƒáƒ¡ სáƒáƒ¬áƒ§áƒ˜áƒ¡áƒ˜ ფáƒáƒ˜áƒšáƒ˜áƒ¡ ზáƒáƒ›áƒáƒ–ე ნáƒáƒ™áƒšáƒ”ბი +ნáƒáƒ¬áƒ˜áƒšáƒ˜áƒ¡ ზáƒáƒ›áƒ მიუღებელირ+მითითებული ნáƒáƒ¬áƒ˜áƒšáƒ˜áƒ¡ ზáƒáƒ›áƒ: {0} ბáƒáƒ˜áƒ¢áƒ˜.\nნáƒáƒ›áƒ“ვილáƒáƒ“ გსურთ áƒáƒ¡áƒ”თი ზáƒáƒ›áƒ˜áƒ¡ ნáƒáƒ¬áƒ˜áƒšáƒ”ბáƒáƒ¡ დáƒáƒ§áƒáƒ— ფáƒáƒ˜áƒšáƒ˜? +7400 +ფáƒáƒ˜áƒšáƒ”ბის შეერთებრ+&შეერთებáƒ: +შეერთებáƒ... +მáƒáƒœáƒ˜áƒ¨áƒœáƒ”თ დáƒáƒ§áƒáƒ¤áƒ˜áƒšáƒ˜ ფáƒáƒ˜áƒšáƒ˜áƒ¡ მხáƒáƒšáƒáƒ“ პირველი ნáƒáƒ¬áƒ˜áƒšáƒ˜ +რáƒáƒ’áƒáƒ áƒª ჩáƒáƒœáƒ¡, ფáƒáƒ˜áƒšáƒ˜ áƒáƒ  წáƒáƒ áƒ›áƒáƒáƒ“გენს დáƒáƒ§áƒáƒ¤áƒ˜áƒšáƒ˜ ფáƒáƒ˜áƒšáƒ˜áƒ¡ ნáƒáƒ¬áƒ˜áƒšáƒ¡ +ვერ მáƒáƒ˜áƒ–ებნრდáƒáƒ§áƒáƒ¤áƒ˜áƒšáƒ˜ ფáƒáƒ˜áƒšáƒ˜áƒ¡ ერთზე მეტი ნáƒáƒ¬áƒ˜áƒšáƒ˜ +7500 +სáƒáƒ™áƒáƒœáƒ¢áƒ áƒáƒšáƒ ჯáƒáƒ›áƒ˜áƒ¡ დáƒáƒ—ვლáƒ... +სáƒáƒ™áƒáƒœáƒ¢áƒ áƒšáƒ ჯáƒáƒ›áƒ˜áƒ¡ შესáƒáƒ®áƒ”ბ +CRC ჯáƒáƒ›áƒ˜ მáƒáƒœáƒáƒªáƒ”მთáƒáƒ—ვის: +CRC ჯáƒáƒ›áƒ˜ სáƒáƒ®áƒ”ლთრდრმáƒáƒœáƒáƒªáƒ”მთáƒáƒ—ვის: +7600 +წáƒáƒ áƒ›áƒáƒ“áƒáƒ‘ის შემáƒáƒ¬áƒ›áƒ”ბრ+გáƒáƒ›áƒáƒ§áƒ”ნებული მეხსიერებáƒ: +შეკუმშვრ+გáƒáƒ¨áƒšáƒ +შეფáƒáƒ¡áƒ”ბრ+სáƒáƒ”რთრშეფáƒáƒ¡áƒ”ბრ+მიმდინáƒáƒ áƒ” +შემáƒáƒ¯áƒáƒ›áƒ”ბელი +CPU დáƒáƒ¢áƒ•ირთვრ+შეფáƒáƒ¡./დáƒáƒ¢áƒ•ირთვრ+გáƒáƒ¢áƒáƒ áƒ”ბáƒ: diff --git a/Utils/7-Zip/Lang/kaa.txt b/Utils/7-Zip/Lang/kaa.txt new file mode 100644 index 000000000..0ac118e38 --- /dev/null +++ b/Utils/7-Zip/Lang/kaa.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.07 : Atabek Murtazaev +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Karakalpak - Latin +Qaraqalpaqsha - Latın +401 +OK +Biykar etiw + + + +&Awa +&Yaq +&Jabıw +Ja'rdem + +&Dawam etiw +440 +&Barlıg'ına awa +Ba&rlıg'ına yaq +Toqtatıw +Qaytadan baslaw +&Artqı fong'a +Aldıng'ı &fong'a +&Pauza +Pauza qılıng'an +Anıq biykar etiwdi qa'leysizbe? +500 +&Fayl +&Du'zetiw +&Ko'rinis +&Saylandılar +A's&baplar +&Ja'rdem +540 +&Ashıw +&İshinde ashıw +&Sırtında ashıw +&Ko'riw +&Du'zetiw +Atın o'&zgertiw +Bul jerge &nusqasın alıw... +Bul jerge ko'shiriw... +O'shiriw +&Fayldı bo'liw... +Fayllardı &biriktiriw... +Sazlawla&r +Kom&mentariy... +Qadag'alaw summası +Diff +Papka jaratıw +Fayl jaratıw +Sh&ıg'ıw +600 +Barlıg'ın &saylaw +Saylawdı alıp taslaw +Saylawdı &teris awdarıw +Saylaw... +Saylawdı alıp taslaw... +Tu'ri boyınsha saylaw +Tu'ri boyınsha saylawdı alıp taslaw +700 +U'&lken ikonalar +Kishi &ikonalar +&Dizim +&Keste +730 +Ta'rtipsiz +Tegis ko'rinis +&2 panel +&A'sbaplar paneli +Derek papkasın ashıw +Bir da'reje joqarıg'a ko'teriliw +Papkalar tariyxı... +&Jan'alaw +750 +Arxivator knopkalar paneli +Standart knopkalar paneli +U'lken knopkalar +Knopkalar tekstin ko'rsetiw +800 +&Papkanı saylandılarg'a qosıw +Belgi +900 +&Sazlawlar... +&O'nimlilikti tekseriw +960 +&Mazmunı... +&7-Zip haqqında... +1003 +Jol +Atı +Ken'eytpe +Papka +Ko'lemi +Qısılg'andag'ı ko'lemi +Attributları +Jaratılg'an +Ashılg'an +O'zgertilgen +U'ziliksiz +Kommentariy berilgen +Shifrlengen +deyin bo'lingen +keyin bo'lingen +So'zlik +CRC +Tu'ri +Anti +Usıl +Basqarıwshı OS +Fayl sisteması +Paydalanıwshı +Topar +Blok +Kommentariy +Ornı +Jol prefiksi +Papkalar +Fayllar +Versiya +Tom +Ko'p tomlı +Jıljıw +Siltewler +Bloklar +Tomlar + +64-bit +Big-endian +Protsessor +Fizikalıq ko'lemi +Baslama ko'lemi +Qadag'alaw summası +Xarakteristika +Virtual adresi +ID +Qısqa atı +Jaratıwshı bag'darlama +Sektor ko'lemi +Rejim +Siltew +Qa'te +Ulıwma ko'lem +Bos orın +Klaster ko'lemi +Belgi +Jergilikli atı +Provayder +2100 +Sazlawlar +Til +Til: +Redaktor +&Redaktor: +&Diff: +2200 +Sistema +To'mendegi fayllardı 7-Zip penen baylanıstırıw: +2301 +7-Zip ti qabıq kontekst menyuine qosıw +Kaskadlı kontekst menyu +Kontekst menyu elementleri: +2320 + + +Arxivti ashıw +Fayllardı shıg'arıp alıw... +Arxivke qosıw... +Arxivti sınaw +Usı jerge shıg'arıw +Mına jerge shıg'arıw: {0} +Mınag'an qosıw: {0} +Qısıw ha'm email arqalı jiberiw... +Mınag'an qısıw: {0} ha'm email arqalı jiberiw +2400 +Papkalar +&İslewshi papka +&Sistemanın' waqtınshalıq papkası +Ha'zirgi &papka +&Ko'rsetilgen: +Tek alınbalı tasıg'ıshlar ushın paydalanıw +Waqtınshalıq arxiv fayllardın' ornın ko'rsetin'. +2500 +Sazlawlar +".." elementti ko'rsetiw +Fayldın' haqıyqıy ikonaların ko'rsetiw +Sistema menyuin ko'rsetiw +&Tolıq joldı saylaw +Tor &sızıqların ko'rsetiw +Elementti bir basıwdan ashıw +Saylawdın' &alternativ usılı +U'lken &yad betlerin paydalanıw +2900 +7-Zip haqqında +7-Zip bul biypul bag'darlama +3000 +Sistema kerekli bolg'an yad mug'darın ajırata almadı +Qa'te tabılmadı +{0} obekt saylang'an +'{0}' papkasın jaratıw iske aspadı +Bul arxiv ushın jan'alaw operatsiyaları qollanılmaydı. +'{0}' faylın arxiv sıpatında ashıw iske aspadı +Shifrlang'an '{0}' arxivin ashıw iske aspadı. Parol qa'te emespe? +Qollanbaytug'ın arxiv tu'ri +{0} faylı a'lle qashan bar (jaratılg'an) +'{0}' faylı o'zgertilgen.\nOnı arxiv ishinde jan'alawdı qa'leysizbe? +Mına fayldı jan'alaw iske aspadı\n'{0}' +Redaktordı ashıw iske aspadı. +Fayl virusqa uqsaydı (fayl atında uzın bos orınlar qollanılg'an). +Operatsiya uzun jollı papkadan ju'klene almaydı. +Siz bir fayl saylawın'ız kerek +Siz bir yamasa onnan ko'p fayllardı saylawın'ız kerek +Elementler sanı dım ko'p +3300 +Shıg'arılmaqta +Qısılmaqta +Sınaw +Ashılmaqta... +Skanerlenbekte... +3400 +Shıg'arıw +&Bul jerge shıg'arıw: +Shıg'arılatug'ın fayllar ushın orın ko'rsetin'. +3410 +Jol usılı +Tolıq jol atları +Jolsız +3420 +U'stinen jazıw usılı +U'stine jazıwdan aldın soraw +Soramastan u'stine jazıw +Aldınnan bar fayllardı o'tkizip jiberiw +Avtomat ta'rizde qayta at beriw +Aldınnan bar fayllarg'a avtomat ta'rizde qayta at beriw +3500 +Fayldın' u'stinen jazıwdı tastıyqlaw +Tayınlang'an papka a'lle qashan islengen fayldı o'z ishine alg'an. +Siz bar fayldı +mına fayl menen almastırıwdı qa'leysizbe? +{0} bayt +A&vtomat ta'rizde qayta at beriw +3700 +'{0}' faylı ushın tanıs bolmag'an qısıw usılı. +'{0}' faylında mag'lıwmat qa'tesi tabıldı. Fayl buzılg'an. +'{0}' faylında CRC qa'tesi tabıldı. Fayl buzılg'an. +'{0}' shifrlang'an faylında mag'lıwmat qa'tesi tabıldı. Parol qa'te emespe? +'{0}' shifrlang'an faylında CRC qa'tesi tabıldı. Parol qa'te emespe? +3800 +Paroldi kiritiw +Paroldi kiritin': +Paroldi qayta kiritin': +Paroldi &ko'rsetiw +Paroller sa'ykes kelmedi +Parol ushın tek latın a'lipbesi ha'riplerin, sanlar ha'm arnawlı simvollardı (!, #, $, ...) paydalanın' +Parol dım uzın +Parol +3900 +O'tken waqıt: +Qalg'an waqıt: +Tolıq ko'lem: +Tezlik: +İslengen ko'lem: +Qısıw da'rejesi: +Qa'teler: +Arxivler: +4000 +Arxivke qosıw +&Arxiv: +&Jan'alaw usılı: +Arxiv &formatı: +Qısıw &da'rejesi: +Qısıw &usılı: +&So'zlik ko'lemi: +So'z &ko'lemi: +Blok ko'lemi: +CPU ag'ımlar sanı: +&Parametrler: +Sazlawlar +SF&X arxivin jaratıw +Jazıw ushın ashılg'an fayllardı qısıw +Shifrlaw +Shifrlaw usılı: +Fayl a&tların shifrlaw +Qısıwg'a arnalg'an yad: +Ajıratıwg'a arnalg'an yad: +4050 +Qısıwsız +En' tez +Tez +Qa'dimgi +En' joqarı +Ultra +4060 +Fayllardı qosıw ha'm almastırıw +Fayllardı jan'alaw ha'm qosıw +Bar fayllardı jan'alaw +Fayllardı sinxronlastırıw +4070 +Belgilew +Barlıq fayllar +Fayl ko'lemi boyınsha +U'ziliksiz +6000 +Nusqasın alıw +Ko'shiriw +Mına papkag'a nusqasın alıw: +Mına papkag'a ko'shiriw: +Nusqa alınbaqta... +Ko'shirilmekte... +Qayta at berilmekte... +Papkanı saylan'. +Ko'rsetilgen operatsiya bul papka ushın qollanılmaydı. +Fayl yaki papkag'a qayta at beriwde qa'te ju'z berdi +Fayldın' nusqasın alıwdı tastıyqlan' +Siz bul fayllardı anıq arxivke ko'shiriwdi qa'leysizbe +6100 +Fayl o'shiriliwin tastıyqlaw +Papka o'shiriliwin tastıyqlaw +Fayllar toparının' o'shiriliwin tastıyqlaw +'{0}' degendi anıq o'shiriwdi qa'leysizbe? +'{0}' papkası ha'm onın' ishindegilerdi anıq o'shiriwdi qa'leysizbe? +Bul obektlerdi ({0} dana) anıq o'shiriwdi qa'leysizbe? +O'shirilmekte... +Fayl yaki papkanı o'shiriwde qa'te ju'z berdi +Sistema uzın jollı fayllardı sebetke ko'shire almaydı +6300 +Papka jaratıw +Fayl jaratıw +Papka atı: +Fayl atı: +Jan'a papka +Jan'a fayl +Papkanı jaratıwda qa'te ju'z berdi +Fayldı jaratıwda qa'te ju'z berdi +6400 +Kommentariy +&Kommentariy: +Saylaw +Saylawdı alıp taslaw +Maska: +6600 +Sazlawlar +Papkalar tariyxı +Diagnostik xabarlar +Xabar +7100 +Kompyuter +Tarmaq +Hu'jjetler +Sistema +7200 +Qosıw +Shıg'arıw +Sınaw +Nusqa alıw +Ko'shiriw +O'shiriw +Mag'lıwmat +7300 +Fayldı bo'liw +&Mınag'an bo'liw: +Tomlarg'a &bo'liw (baytlarda): +Bo'linbekte... +Bo'liwdi tastıyqlan' +Siz fayldı {0} bo'lekke anıq bo'liwdi qa'leysizbe? +Bo'lek ko'lemi original fayl ko'leminen kishi bolıwı kerek +Tom ko'lemi natuwrı berilgen +Tomnın' ko'rsetilgen ko'lemi: {0} bayt.\nArxivti bunday tomlarg'a anıq bo'liwdi qa'leysizbe? +7400 +Fayllardı biriktiriw +&Mınag'an biriktiriw: +Biriktirilmekte... +Bo'lingen fayldın' tek birinshi bo'legin saylaw kerek +Fayl bo'lingen fayldın' bo'legi retinde tanılmadı +Bo'lingen fayldın' birden ko'p bo'legi tabılmadı +7500 +Qadag'alaw summasın esaplaw... +Qadag'alaw summası mag'lıwmatı +Mag'lıwmatlar ushın CRC qadag'alaw summası: +Mag'lıwmatlar ha'm atamalar ushın CRC qadag'alaw summası: +7600 +O'nimlilikti tekseriw +Yad ko'lemi: +Qısıw +Ajıratıw +Reyting +Ulıwma reyting +Ha'zirgi +Ja'mi +Awırlıq +Reyting / Awır. +O'tkenler: diff --git a/Utils/7-Zip/Lang/kk.txt b/Utils/7-Zip/Lang/kk.txt new file mode 100644 index 000000000..0db20e07a --- /dev/null +++ b/Utils/7-Zip/Lang/kk.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.07 : Arslan Beisenov, Arman Beisenov +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Kazakh +Қазақша +401 +Жарайды +Болдырмау + + + +&Иә +&Жоқ +&Жабу +Ðнықтама + +&ЖалғаÑтыру +440 +Бәріне &Ð¸Ñ +Бәріне &жоқ +Тоқтату +Қайта Ñ–Ñке қоÑу +&Фонды +&Ðлдыңғы жоÑпарға +&Ò®Ñ–Ð·Ñ–Ð»Ñ +ҮзіліÑте +ОперациÑны тоқтату керек пе? +500 +&Файл +&Өңдеу +&ÐšÓ©Ñ€Ñ–Ð½Ñ–Ñ +&Таңдаулы +С&ÐµÑ€Ð²Ð¸Ñ +&Ðнықтама +540 +&Ðшу +Ішінен &ашу +Сыртынан ашу +Қарау +&Өңдеу +Қайта атау +&Көшіру... +&Жылжыту... +&Жою +Файлды бөлшектеу... +Файлды біріктіру... +Сипаттар +Комме&нтарий... +Бақылау ÑомаÑÑ‹ +Diff +&Қалта жаÑау +Файл жаÑау +Шығу +600 +Бәрін ерекшелеу +Ерекшелеуді алаÑтау +&Ерекшелеуді көрÑету +Ерекшелеу... +Ерекшедеужі алаÑтау... +Түрі б-ша ерекшелеу +Түрі б-ша ерекшелеуді алаÑтау +700 +&Үлкен таңбаша +&Кішк. таңбаша +Тізім +&КеÑте +730 +СұрыптауÑыз +Жалпақ режим +&2 тақта +&Құралдар тақтаÑÑ‹ +Ðегізгі қалтады ашу +Бір деңгей жоғары өту +Қалта тарихы... +Жаңарту +750 +Мұрағаттауыш батырма тақтаÑÑ‹ +Батырманың кәдімгі тақтаÑÑ‹ +Үлкен батырмалар +Батырмада жазулар +800 +Қалтаны таңдаулыға баÑқаша Ò¯Ñтеу +Таңдаулы +900 +Баптау... +Өнімді теÑтілеу +960 +&БаÑты... +Бағдарламада... +1003 +Жол +Ðты +Кеңейтімі +Қалта +Көлем +Сығылған +Ðтрибут +ЖаÑалған +Ðшылған +Өзгерген +ҮзіліÑÑіз +Комментарий +Шифрланған +дейін бөлінген +кейін бөлінген +Сөздік +CRC +Түрі +Ðнти +ТәÑіл +Жүйе +Файлдық жүйе +Пайдаланушы +Топ +Блок +Комментарий +Орны +Жолы +Қалта +Файл +ÐÒ±Ñқа +Том +Көп томды +Жылжу +Сілтеме +Блок +Том + +64-bit +Big-endian +ПроцеÑÑор +Физикалық көлемі +Тақырып көлемі +Бақылау ÑомаÑÑ‹ +ХарактериÑтика +Виртуальды мекен-жай +ID +ҚыÑқа аты +ЖаÑаушы +Сектор көлемі +Режим +Сілтеме +Қателік +Сыйымы +Ð‘Ð¾Ñ +КлаÑтер көлемі +Белгі +Жергілікті аты +Провайдер +2100 +Баптау +Тіл +Тіл: +Өңдегіш +&Өңдегіш: +&Diff: +2200 +Жүйе +7-Zip файлмен аÑÑорциÑлау: +2301 +Мұқабаның мәтінміндің мәзіріне 7-Zip қою +КаÑкадты мәтінмәндік мәзір +Мәтінміндік мәзірдің Ñлементтері: +2320 +<Қалта> +<Мұрағат> +Мұрағат ашу +Бумадан шешу +Мұрағатқа Ò¯Ñтеу... +ТеÑтілеу +Мұнда шешу +{0} дегенде шешу +{0} дегенге Ò¯Ñтеу +Сығып Ñ-поштамен жіберу... +{0} дегенде Ñығып, Ñ-поштамен жіберу +2400 +Қалта +&Ð–Ò±Ð¼Ñ‹Ñ Ò›Ð°Ð»Ñ‚Ð°ÑÑ‹ +&Жүйелік уақытша қалта +&Ðғымдық +&Сұрау: +Тек алмалы таÑығыштарға ғана пайдалану +Уақытша мұрағаттардың орнын нұÑқаңыз. +2500 +Баптау +".." Ñлементті көрÑету +Файлдың шынайы таңбашаÑын көрÑету +Жүйелік мәзірді көрÑету +Барлық жолаққа меңзер +Бөлгіштерді көрÑету +Бір шерткеннен ашу +Белгілеудің альтернативті режимі +Жадтың үлкен беттерін пайдалану +2900 +7-Zip туралы +7-Zip - тегін таратылатын бағдарлама. +3000 +Ð‘Ð¾Ñ Ð¶Ð°Ð´ жоқ +Қателік табылған жоқ +Ерекшеленген ныÑан: {0} +'{0}' қалтаÑын жаÑау мүмкін ÐµÐ¼ÐµÑ +Көтермейтін мұрағат үшін Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ó©Ð·Ð³ÐµÑ€Ñ‚Ñ–Ð»ÐµÐ´Ñ–. +'{0}' файлды мұрағат ретінде ашу мүмкін ÐµÐ¼ÐµÑ +Шифрланған '{0}' мұрағатты ашу мүмкін емеÑ. КілтÑөз Ð´Ò±Ñ€Ñ‹Ñ ÐµÐ¼ÐµÑ Ð¿Ðµ? +Көтермейтін мұрағат түрі +{0} деген файл бар +'{0}' файлы өзгерді.\nОны мұрағатта жаңарту керек пе? +\n'{0}' файлды жаңарту мүмкін ÐµÐ¼ÐµÑ +Өңдегішті ашу мүмкін ÐµÐ¼ÐµÑ +Файл вируÑқа Ò±Ò›Ñайды (файл аты ұзақ жолды мәÑелені құрайды). +ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ò›Ð°Ð»Ñ‚Ð°Ò“Ð° жолдың ұзақтығынан орындалмайды +Сізге бір файл таңдау керек +Сізге бір не бірнеше файл таңдау керек +Өте көп Ñлемент +3300 +Шешу +КомпреÑÑÐ¸Ñ +ТеÑтілеу +Ðшылу... +Сканерлеу... +3400 +Шығару +&Мұнда шешу: +Шығарылатын файл орнын нұÑқаңыз. +3410 +Жолдар +&Толық жол +&ЖолÑыз +3420 +Қайта жазу +&РаÑтаумен +&РаÑтауÑыз +Өткізіп &жіберу +Ðвтоматты қайта атау. +Бар файлды автом. қайта атау +3500 +Файлды ауыÑтыру раÑтау +Қалтада Ñ–Ñ Ð¶Ò¯Ñ€Ð³Ñ–Ð·Ñ–Ð»ÐµÑ‚Ñ–Ð½ файл бар. +Файлды ауыÑтыру +келеÑÑ– файлмен ба? +{0} байт +Ðвтоматты қайта атау +3700 +'{0}' файлы үшін Ñығу әдіÑін көтермейді. +'{0}' мәліметінде қателік. Файл зақымдалған. +'{0}' CRC қателік. Файл зақымдалған. +Шифрланған '{0}' файлында мәліміт қате. КілтÑөз Ð´Ò±Ñ€Ñ‹Ñ ÐµÐ¼ÐµÑ Ð¿Ðµ? +Шифрланған '{0}' файлында CRС қате. КілтÑөз Ð´Ò±Ñ€Ñ‹Ñ ÐµÐ¼ÐµÑ Ð¿Ðµ? +3800 +КілтÑөзді енгізу +&КілтÑөзді енгізіңіз: +&КілтÑөзді қайталаңыз: +&КілтÑөзді көрÑету +КілтÑөздер ÑÓ™Ð¹ÐºÐµÑ ÐµÐ¼ÐµÑ +КілтÑөзге тек ағылшын әліпбиін пайдаланыңыз, Ñандар және арнайы нышандар (!, #, $, ...) +КілтÑөз өте ұзақ +&КілтÑөз +3900 +Өтті: +Қалды: +Барлығы: +Жылд.: +Көлем: +Сығу дәрежеÑÑ–: +Қате: +Мұрағат: +4000 +Мұрағатқа Ò¯Ñтеу +&Мұрағат: +&Өзгерту режимі: +&Мұрағат пішімі: +&Сығу деңгейі: +&Сығу тәÑілі: +Сөздік &көлемі: +Сөз &көлемі: +Блог көлемі: +Ðғым Ñаны: +&Параметр: +&Баптау +SF&X-мұрағат жаÑау +Ðшылған файд жазу үшін Ñығу +Шифрлау +Шифрлау тәÑілі: +&Файлдың атын шифрлау +Бууға арналған көлем: +Шешуге арналған көлем: +4050 +СығуÑыз +Жылдам +Шапшаң +Кәдімгі +Ең жоғарғы +Ультра +4060 +Ò®Ñтеу, ауыÑтыру +Жаңарту, Ò¯Ñтеу +Жаңату +ТеңеÑтіру +4070 +Сапыру +Барлық файл +Файл көлемі б-ша +ҮзіліÑÑіз +6000 +Көшіру +Жылжыту +Мұнда көшіру: +Мұнда жылжыту: +Көшіру... +Жылжыту... +Ðтын өзгерту... +Қалтаны нұÑқаңыз +Бұл қалтаға Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ ÐºÓ©Ñ‚ÐµÑ€Ð¼ÐµÐ¹Ð´Ñ–. +Файл не қалтаның атын өзгерту мүмкін ÐµÐ¼ÐµÑ +Файлды көшіруді раÑтау +Мына файлдарды мұрағатқа көшіру керек пе +6100 +Файлды жоюды раÑтау +Қалтаны жоюды раÑтау +Бірнеше файлды жоюды раÑтау +"{0}" дегенді жою керек пе? +"{0}" қалтаÑын және оның ішіндегілерін жою керек пе? +({0} дана) ныÑандарды жою керек пе? +Жойылу... +Файл не қалтаны жою қате +Ұзақ жолды файлдарды Ñебетке жоюды жүйе көтермейді +6300 +Қалта жаÑау +Файл жаÑау +Қалта аты: +Файл аты: +Жаңа қалта +Жаңа файл +Қалтаны жаÑау қате +Файлды жаÑау кезінде қателік болды +6400 +Комментарий +&Комментарий: +Ерекшелеу +ÐлаÑтау +МаÑка: +6600 +Сипаттар +Қалта тарихы +Хабарлама +Хабарлама +7100 +Компьютер +Желі +Құжаттар +Жүйе +7200 +Ò®Ñтеу +Шығару +ТеÑтілеу +Көшіру +Жылжыту +Жою +Ðқпарат +7300 +Файлды бөлшектеу +&Бөлшектеу: +Томға бөлшектеу (байтқа): +Бөлшектеу... +Бөлшектеуді раÑтау +Файлд {0} бөлікке бөлшектеу керек пе? +Том көлемі файлдың ÐºÑ–Ñ€Ñ–Ñ ÐºÓ©Ð»ÐµÐ¼Ñ–Ð½ÐµÐ½ аз болу керек +Томның көлемін енгізу қате +Томның орнатылған көлемі: {0} байт.\nМұрағатты томға бөлшектеу керек пе? +7400 +Файлдарды біріктіру +&Мұнда біріктіру: +Біріктіру... +Бөлшектенген файлдың бірінші бөлігін таңдау керек +Бөлшектенген файлды тану мүмкін ÐµÐ¼ÐµÑ +Бөлшектенген файлдың бөліктерін табу мүмкін ÐµÐ¼ÐµÑ +7500 +Бақылау ÑомаÑын еÑептеу... +Бақылау ÑомаÑÑ‹ +Мәлімет үшін CRC бақылау ÑомаÑÑ‹: +Мәлімет және атау үшін CRC бақылау ÑомаÑÑ‹: +7600 +Өнімділікке теÑтілеу +Жад көлемі: +Буу +Шешу +Рейтинг +Жалпы рейтинг +Ðғымдық +ÐәтижеÑÑ– +Ðуырлық +Рейтинг / ЖеңіÑ. +Өтулер: diff --git a/Utils/7-Zip/Lang/ko.txt b/Utils/7-Zip/Lang/ko.txt new file mode 100644 index 000000000..5e9644d58 --- /dev/null +++ b/Utils/7-Zip/Lang/ko.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; : ZannyLim (임재형) +; : bzImage +; 4.52 : Hyeong il Kim (kurt Sawyer) +; 9.07 : Dong-yoon Han (한ë™ìœ¤) +; 15.12 : Winterscenery (Ji-yong BAE) +; 16.02 : Add translation and Modify Typos by Winterscenery (Ji-yong BAE) +; +; +; +; +; +0 +7-Zip +Korean +한국어 +401 +í™•ì¸ +취소 + + + +예(&Y) +아니오(&N) +닫기(&C) +ë„ì›€ë§ + +계ì†(&C) +440 +ëª¨ë‘ ì˜ˆ(&A) +ëª¨ë‘ ì•„ë‹ˆì˜¤(&L) +중지 +다시 시작 +ë‚®ì€ ìˆœìœ„ë¡œ(&B) +ìš°ì„  순위로(&F) +ì¼ì‹œì •ì§€(&P) +ì¼ì‹œì •ì§€ ë¨ +ì •ë§ë¡œ 취소하시겠습니까? +500 +파ì¼(&F) +편집(&E) +보기(&V) +ì¦ê²¨ì°¾ê¸°(&A) +ë„구(&T) +ë„움ë§(&H) +540 +열기(&O) +ë‚´ë¶€ 열기(&I) +외부 열기(&U) +íŒŒì¼ ë·°ì–´(&V) +편집(&E) +새 ì´ë¦„(&M) +복사(&C)... +ì´ë™(&M)... +ì‚­ì œ(&D) +íŒŒì¼ ë‚˜ëˆ„ê¸°(&S)... +íŒŒì¼ í•©ì¹˜ê¸°(&B)... +ì†ì„±(&R) +설명(&N)... +ì²´í¬ì„¬ 계산 +íŒŒì¼ ë¹„êµ +í´ë” 만들기 +íŒŒì¼ ë§Œë“¤ê¸° +ë내기(&X) +ì—°ê²° +대체 ë°ì´í„° 스트림(&A) +600 +ëª¨ë‘ ì„ íƒ(&A) +ëª¨ë‘ ì„ íƒ ì·¨ì†Œ +ì„ íƒ í•­ëª© 반전(&I) +ì„ íƒ... +ì„ íƒ ì·¨ì†Œ... +íŒŒì¼ í˜•ì‹ìœ¼ë¡œ ì„ íƒ +íŒŒì¼ í˜•ì‹ìœ¼ë¡œ ì„ íƒ ì·¨ì†Œ +700 +í° ì•„ì´ì½˜(&G) +ìž‘ì€ ì•„ì´ì½˜(&M) +목ë¡(&L) +ìžì„¸ížˆ(&S) +730 +ì •ë ¬ 안함 +íŽ¼ì³ ë³´ê¸° +2 패ë„(&2) +ë„구 모ìŒ(&T) +최ìƒìœ„ í´ë” 열기 +한단계 위로 +í´ë” 히스토리... +새로 고침(&R) +ìžë™ 새로 고침 +750 +ì••ì¶• ë„구 ëª¨ìŒ +표준 ë„구 ëª¨ìŒ +í° ë²„íŠ¼ +버튼 í…스트 보기 +800 +ì¦ê²¨ì°¾ê¸°ì— 추가(&A) +ë¶ë§ˆí¬ +900 +옵션(&P)... +벤치마í¬(&B) +960 +ë„ì›€ë§ í•­ëª©(&C)... +7-Zip ì •ë³´(&A)... +1003 +경로 +ì´ë¦„ +í™•ìž¥ìž +í´ë” +í¬ê¸° +ì••ì¶•ëœ í¬ê¸° +ì†ì„± +만든 ë‚ ì§œ +액세스한 ë‚ ì§œ +수정한 ë‚ ì§œ +솔리드 +설명 +암호화 +나누기 ì´ì „ +나누기 후 +사전 +CRC +í˜•ì‹ +안티 +ì••ì¶• ë°©ì‹ +ìƒì„±í•œ OS +íŒŒì¼ ì‹œìŠ¤í…œ +ì‚¬ìš©ìž +그룹 +블럭 +설명 +위치 +경로 ì ‘ë‘ +í´ë” +íŒŒì¼ +버전 +볼륨 +다중볼륨 +오프셋 +ì—°ê²° +ë¸”ë¡ +볼륨 + +64-bit +Big-endian +CPU +ë¬¼ë¦¬ì  í¬ê¸° +í•´ë” í¬ê¸° +ì²´í¬ì„¬ +특성 +ê°€ìƒ ì£¼ì†Œ +ID +ì§§ì€ ì´ë¦„ +ìƒì„± ì‘용프로그램 +섹터 í¬ê¸° +모드 +기호 ì—°ê²° +오류 +ì „ì²´ í¬ê¸° +여유 공간 +í´ëŸ¬ìŠ¤í„° í¬ê¸° +ë¼ë²¨ +로컬 ì´ë¦„ +ì œê³µìž +NT 보안 +대체 ë°ì´í„° 스트림 +ë³´ì¡° +ì‚­ì œ +Is Tree + + +오류 유형 +오류 +오류 +경고 +경고 +스트림 +대체 ë°ì´í„° 스트림 +대체 ë°ì´í„° 스트림 í¬ê¸° +ê°€ìƒ í¬ê¸° +ì••ì¶• í•´ì œ 후 í¬ê¸° +용량 실제 í¬ê¸° +볼륨 ì¸ë±ìФ +ë³´ì¡°í˜•ì‹ +ì£¼ì„ +코드 페ì´ì§€ + + + +í…Œì¼ í¬ê¸° +임베디드 ìŠ¤í… í¬ê¸° +ì—°ê²° +하드 ì—°ê²° +i노드 + +ì½ê¸° ì „ìš© +2100 +옵션 +언어 +언어: +편집기 +íŒŒì¼ íŽ¸ì§‘ê¸°(&E): +íŒŒì¼ ë¹„êµ(&D): +2200 +시스템 +7-Zip 으로 ì—°ê²°: +모든 ì‚¬ìš©ìž +2301 +7-Zip íƒìƒ‰ê¸° 메뉴 사용 +íƒìƒ‰ê¸° 메뉴 ê³„ë‹¨ì‹ ë³´ê¸° +íƒìƒ‰ê¸° 메뉴 항목: +íƒìƒ‰ê¸° 메뉴 ì•„ì´ì½˜ 보기 +2320 +<í´ë”> +<압축파ì¼> +ì••ì¶•íŒŒì¼ ì—´ê¸° +ì••ì¶• 풀기... +압축파ì¼ì— 추가... +ì••ì¶•íŒŒì¼ í…ŒìŠ¤íŠ¸ +ì—¬ê¸°ì— ì••ì¶• 풀기 +{0}ì— í’€ê¸° +{0}ì— ì¶”ê°€ +압축해서 ì´ë©”ì¼ ë³´ë‚´ê¸° +{0}로 압축해서 ì´ë©”ì¼ ë³´ë‚´ê¸° +2400 +í´ë” +작업 í´ë”(&W) +시스템 임시 í´ë”(&S) +현재 í´ë”(&C) +지정 í´ë”(&S): +ì´ë™ì‹ 드ë¼ì´ë¸Œì—서만 사용 +ì••ì¶•ì— ê´€ê³„ëœ íŒŒì¼ì´ 임시ì ìœ¼ë¡œ 사용할 위치 지정. +2500 +설정 +ìƒìœ„ í´ë” ".." 항목 보기 +실제 íŒŒì¼ ì•„ì´ì½˜ 보기 +시스템 메뉴 보기 +í–‰ ì „ì²´ ì„ íƒ(&F) +눈금선 보기(&G) +한 번 í´ë¦­ìœ¼ë¡œ 항목 열기 +ìš°ì„  ì„ íƒì  ì„ íƒ ëª¨ë“œ +í° ë©”ëª¨ë¦¬ 페ì´ì§€ 사용 +2900 +7-Zip ì •ë³´ +7-Zip ì€ ë¬´ë£Œ 소프트웨어입니다. +3000 +ì‹œìŠ¤í…œì´ í•„ìš”í•œ ì–‘ì˜ ë©”ëª¨ë¦¬ë¥¼ 할당할 수 ì—†ìŒ +오류 ì—†ìŒ +{0} í•­ëª©ì´ ì„ íƒë¨ +'{0}' í´ë”를 ìƒì„±í•  수 ì—†ìŒ +ì—…ë°ì´íЏ ìž‘ì—…ì´ ì´ ì••ì¶•íŒŒì¼ì—서는 ì§€ì›ë˜ì§€ 않습니다. +íŒŒì¼ '{0}'ì„(를) 압축파ì¼ë¡œ ì—´ 수 ì—†ìŒ +ì•”í˜¸í™”ëœ ì••ì¶•íŒŒì¼ '{0}'ì„(를) ì—´ 수 없습니다. 잘못 ìž…ë ¥ëœ ì•”í˜¸? +ì§€ì›ë˜ì§€ 않는 ì••ì¶•íŒŒì¼ ìœ í˜• +{0} 파ì¼ì€ ì´ë¯¸ 존재함 +íŒŒì¼ '{0}'ì´ ìˆ˜ì •ë˜ì—ˆìŠµë‹ˆë‹¤.\n압축파ì¼ì— ì—…ë°ì´íЏ 하시겠습니까? +'{0}' 파ì¼ì„ ì—…ë°ì´íЏ í•  수 없습니다. +편집기를 시작할 수 없습니다. +해당 파ì¼ì´ ë°”ì´ëŸ¬ìФ 같습니다 (íŒŒì¼ ì´ë¦„ì— ê¸¸ë‹¤ëž€ ê³µë°±ì´ ë“¤ì–´ìžˆìŒ). +긴 ê²½ë¡œë¡œëœ í´ë”ì—서 해당 ìž‘ì—…ì„ í˜¸ì¶œí•  수 없습니다. +반드시 한 ê°œì˜ íŒŒì¼ì„ ì„ íƒí•´ì•¼í•¨ +반드시 한 ê°œ ì´ìƒì˜ 파ì¼ì„ ì„ íƒí•´ì•¼ 함 +í•­ëª©ì´ ë„ˆë¬´ ë§ŽìŒ +{0} 압축파ì¼ë¡œ 파ì¼ì„ ì—´ 수 없습니다 +{0} 압축파ì¼ë¡œ 파ì¼ì´ 열리고 있습니다 +ì••ì¶•íŒŒì¼ ì˜¤í”„ì…‹ì„ ì‚¬ìš©í•˜ì—¬ ì—´ë ¤ 있습니다 +3300 +ì••ì¶• 푸는 중 +압축하는 중 +검사 중 +여는 중... +검색 중... +ì‚­ì œ 중 +3320 +추가 중 +ì—…ë°ì´íЏ 중 +ë¶„ì„ ì¤‘ +ë®ì–´ 쓰는 중 +다시 ì••ì¶• 중 +건너뛰는 중 +ì‚­ì œ 중 +í—¤ë” ìž‘ì„± 중 +3400 +ì••ì¶• 풀기 +ì••ì¶• 풀기(&X): +ì••ì¶• 풀린 파ì¼ì˜ 위치를 지정합니다. +3410 +경로 모드 +ì „ì²´ 경로명 +경로명 ì—†ìŒ +절대 경로명 +ìƒëŒ€ 경로명 +3420 +ë®ì–´ì“°ê¸° 모드 +ë®ì–´ì“°ê¸° ì „ì— í™•ì¸ +물어보지 않고 ë®ì–´ì“°ê¸° +존재하는 íŒŒì¼ ê±´ë„ˆë›°ê¸° +ìžë™ìœ¼ë¡œ ì´ë¦„ 바꾸기 +존재하는 íŒŒì¼ ì´ë¦„ 바꾸기 +3430 +최ìƒìœ„ í´ë”ì˜ ì¤‘ë³µì„ ë°©ì§€ +íŒŒì¼ ë³´ì•ˆ ì†ì„±ì„ ë³µì› +3500 +íŒŒì¼ ë®ì–´ì“°ê¸° í™•ì¸ +ëŒ€ìƒ í´ë”ì— ì´ë¯¸ 파ì¼ì´ 존재합니다. +존재하는 파ì¼ì„ +ì´ê²ƒìœ¼ë¡œ ë®ì–´ì“°ê¸° 하시겠습니까? +{0} ë°”ì´íЏ +ìžë™ìœ¼ë¡œ ì´ë¦„ 바꾸기(&U) +3700 +'{0}'ì€ ì§€ì›í•˜ì§€ 않는 ì••ì¶• ë°©ì‹ìž…니다. +'{0}'ì— ë°ì´í„° 오류가 있습니다. 파ì¼ì´ ì†ìƒë˜ì—ˆìŠµë‹ˆë‹¤. +'{0}'ì˜ CRC 검사를 실패했습니다. 파ì¼ì´ ì†ìƒë˜ì—ˆìŠµë‹ˆë‹¤. +암호화 íŒŒì¼ '{0}'ì— ë°ì´í„° 오류가 있습니다. 암호가 틀리나요? +암호화 íŒŒì¼ '{0}'ì˜ CRC 검사를 실패했습니다. 암호가 틀리나요? +3710 +잘못 ìž…ë ¥ëœ ì•”í˜¸? +3721 +ì§€ì›í•˜ì§€ 않는 ì••ì¶• 방법 +ë°ì´í„° 오류 +CRCê°€ 다릅니다 +사용할 수없는 ë°ì´í„° +ë°ì´í„°ì˜ 예기치 ì•Šì€ ì¢…ë£Œ +페ì´ë¡œë“œ ë°ì´í„°ì˜ 종료 í›„ì— ì¼ë¶€ ë°ì´í„°ê°€ìžˆë‹¤. +ì••ì¶•ë˜ì§€ 않습니다. +í—¤ë” ì˜¤ë¥˜ +암호가 잘못ë˜ì—ˆìŠµë‹ˆë‹¤. +3763 +사용할 수 없는 ì‹œìž‘ì˜ ì••ì¶•íŒŒì¼ +확ì¸ë˜ì§€ 않는 ì‹œìž‘ì˜ ì••ì¶•íŒŒì¼ + + + +ì§€ì›í•˜ì§€ 않는 기능 +3800 +암호 ìž…ë ¥ +암호 ìž…ë ¥: +암호 다시 ìž…ë ¥: +암호 보기(&S) +암호가 ì¼ì¹˜í•˜ì§€ ì•ŠìŒ +암호로는 ì˜ë¬¸ìž, ìˆ«ìž ê·¸ë¦¬ê³  특수 ë¬¸ìž (!, #, $, ...)ë§Œ 사용 +암호가 너무 ê¹ë‹ˆë‹¤. +암호 +3900 +경과 시간: +ë‚¨ì€ ì‹œê°„: +ì „ì²´ í¬ê¸°: +ì†ë„: +처리ë¨: +ì••ì¶• 효율: +오류: +압축파ì¼: +4000 +압축파ì¼ì— 추가 +압축파ì¼(&A): +ì—…ë°ì´íЏ 모드(&U): +ì••ì¶•íŒŒì¼ í˜•ì‹(&F): +ì••ì¶• 레벨(&L): +ì••ì¶• ë°©ì‹(&M): +사전 í¬ê¸°(&D): +단어(word) í¬ê¸°(&W): +솔리드 ë¸”ë¡ í¬ê¸°: +CPU 스레드 수: +매개변수(&P): +옵션 +ìžë™(SFX) ì••ì¶•íŒŒì¼ ìƒì„±(&X) +공유하고있는 íŒŒì¼ ì••ì¶• +암호화 +암호화 ë°©ì‹: +íŒŒì¼ ì´ë¦„ 암호화(&N) +압축시 사용 메모리: +ì••ì¶• 풀기시 사용 메모리: +ì••ì¶• 후 ì›ë³¸ 파ì¼ì„ ì‚­ì œ +4040 +심볼릭 ì—°ê²°ì„ ì €ìž¥ +하드 ì—°ê²°ì„ ì €ìž¥ +대체 ë°ì´í„° ìŠ¤íŠ¸ë¦¼ì„ ì €ìž¥ +íŒŒì¼ ë³´ì•ˆ ì†ì„±ì„ 저장 +4050 +저장 +가장 빠름 +빠름 +보통 +최고 +가장 ëŠë¦¼ +4060 +파ì¼ì„ 추가하고 ë®ì–´ì“°ê¸° +파ì¼ì„ ì—…ë°ì´íŠ¸í•˜ê³  추가 +존재하는 파ì¼ë§Œ 새롭게 하기 +ì••ì¶•íŒŒì¼ ë‚´ìš©ì„ ë™ê¸°í™” +4070 +찾아보기 +모든 íŒŒì¼ +솔리드 사용 않함 +솔리드 +6000 +복사 +ì´ë™ +í´ë”로 복사: +í´ë”로 ì´ë™: +복사 중... +ì´ë™ 중... +ì´ë¦„ 바꾸는 중... +ëŒ€ìƒ í´ë”를 ì„ íƒí•˜ì„¸ìš”. +ì§€ì›ë˜ì§€ 않는 작업입니다. +íŒŒì¼ ë˜ëŠ” í´ë” ì´ë¦„ 바꾸기 실패 +íŒŒì¼ ë³µì‚¬ í™•ì¸ +파ì¼ì„ 압축파ì¼ë¡œ 복사 하시겠습니까? +6100 +íŒŒì¼ ì‚­ì œ í™•ì¸ +í´ë” ì‚­ì œ í™•ì¸ +여러 íŒŒì¼ ì§€ìš°ê¸° í™•ì¸ +'{0}'ì„(를) 삭제하시겠습니까? +í´ë” '{0}'와 ê·¸ 모든 ë‚´ìš©ì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ? +ì´ {0} í•­ëª©ë“¤ì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ? +ì‚­ì œ 중... +íŒŒì¼ ë˜ëŠ” í´ë” ì‚­ì œ 실패 +ì‹œìŠ¤í…œì´ ê¸´ ê²½ë¡œì˜ íŒŒì¼ì„ 휴지통으로 ì´ë™í•  수 ì—†ìŒ +6300 +í´ë” 만들기 +íŒŒì¼ ë§Œë“¤ê¸° +í´ë” ì´ë¦„: +íŒŒì¼ ì´ë¦„: +새 í´ë” +새 íŒŒì¼ +í´ë” 만들기 오류 +íŒŒì¼ ë§Œë“¤ê¸° 오류 +6400 +설명 +설명(&C): +ì„ íƒ +ì„ íƒ ì·¨ì†Œ +마스í¬: +6600 +ì†ì„± +í´ë” 히스토리 +진단 메시지 +메시지 +7100 +컴퓨터 +ë„¤íŠ¸ì›Œí¬ +문서 +시스템 +7200 +추가 +ì••ì¶• 풀기 +테스트 +복사 +ì´ë™ +ì‚­ì œ +ì†ì„± +7300 +íŒŒì¼ ë¶„í• í•˜ê¸° +분할하기(&S): +볼륨 나누기, ë°”ì´íЏ(&V): +분할하는 중... +ë¶„í•  í™•ì¸ +ì •ë§ {0} 볼륨들로 ë¶„í•  하시겠습니까? +볼륨 í¬ê¸°ê°€ ì›ë³¸ 파ì¼ë³´ë‹¤ 작아야만 합니다. +볼륨 í¬ê¸°ê°€ ë¶€ì ì ˆí•©ë‹ˆë‹¤. +ì§€ì •ëœ ë³¼ë¥¨ í¬ê¸°: {0} ë°”ì´íЏ.\nì´ ë³¼ë¥¨ í¬ê¸°ë¡œ ë¶„í•  하시겠습니까? +7400 +íŒŒì¼ í•©ì¹˜ê¸° +합치기(&B): +íŒŒì¼ í•©ì¹˜ëŠ” 중... +첫번째 파ì¼ë§Œ ì„ íƒí•˜ì‹œì˜¤ +분할한 파ì¼ì˜ 한 부분으로 ì¸ì‹í•  수 ì—†ìŒ +ë¶„í•  파ì¼ì˜ 한 부분 ì´ìƒì„ ì°¾ì„ ìˆ˜ ì—†ìŒ +7500 +ì²´í¬ì„¬ 계산중... +ì²´í¬ì„¬ ì •ë³´ +ë°ì´í„° CRC ì²´í¬ì„¬: +ë°ì´í„°ì™€ ì´ë¦„ CRC ì²´í¬ì„¬: +7600 +ë²¤ì¹˜ë§ˆí¬ +메모리 사용량: +ì••ì¶• 중 +ì••ì¶• 푸는 중 +í‰ê°€ +ì „ì²´ í‰ê°€ +현재 +ê²°ê³¼ +CPU 사용량 +í‰ê°€ / 사용량 +통과: +7700 +ì—°ê²° +ì—°ê²° 만들기 +ì›ë³¸: +ì—°ê²°: +7710 +ì—°ê²° 유형 +하드 ì—°ê²° +파ì¼ì˜ 심볼릭 ì—°ê²° +ë””ë ‰í† ë¦¬ì˜ ì‹¬ë³¼ë¦­ ì—°ê²° +디렉토리 ì ‘í•© diff --git a/Utils/7-Zip/Lang/ku-ckb.txt b/Utils/7-Zip/Lang/ku-ckb.txt new file mode 100644 index 000000000..a6b52cea5 --- /dev/null +++ b/Utils/7-Zip/Lang/ku-ckb.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; : Ara Bakhtiar +; 4.66 : Ara Qadir : http://www.chawg.org +; +; +; +; +; +; +; +; +; +0 +7-Zip +Kurdish - Sorani +کوردی +401 +باشە +پاشگەزبوونەوە + + + +&بەڵێ +&نەخێر +&داخستن +یارمەتی + +&بەردەوامبوون +440 +بەڵێ بۆ &هەموو +نەخێر بۆ Ù‡Û•&موو +وەستاندن +دەستپێکردنەوە +&پاشەبنەما +Ù¾&ێشەبنەما +&ڕاگرتن +ڕاگیردرا +دڵنیایت Ù„Û• پاشگەزبوونەوە؟ +500 +&ÙØ§ÛŒÙ„ +&دەستکاری +&بینین +د&ڵخوازەکان +&ئامڕازەکان +&یارمەتی +540 +&کردنەوە +کردنەوە Ù„Û•&ناو خۆدا +کردنەوە Ù„Û• &دەرەوە +&بینین +&دەستکاری +&ناوگۆڕین +&لەبەرگرتنەوە بۆ... +&گواستنەوە بۆ... +&سڕینەوە +&لەتکردنی Ù¾Û•Ú•Ú¯Û•... +&Ù¾ÛŽÚ©Û•ÙˆÛ• لکاندنی پەڕگەکان... +&تایبەتمەندییەکان +Ù„&ێدوان +ژماردنی checksum + +دروستکردنی بوخچە +دروستکردنی Ù¾Û•Ú•Ú¯Û• +دەر&چوون +600 +هەمووی دیاری بکە +هەموو دیاریکراوەکان لاببە +%پێچەوانەکردنەوەی دیاریکراو +دیاری بکە... +دیاری Ù…Û•Ú©Û•... +بە Ù¾ÛŽÛŒ جۆر دیاری بکە +لابردنی دیاریکراوەکان بەپێ جۆر +700 +ئایکۆنی &گەورە +ئایکۆنی &بچوک +&لیست +&وردەکاری +730 +ڕیزنەکراو +بینین بەشێوەی Ùلات +&2 پانێڵ +&جێ ئامڕاز +کردنەوەی بوخچەی Ú•Û•Ú¯ +یەک ئاست بۆ سەرەوە +مێژووی بوخچەکان... +&بووژاندنەوە +750 +جێ ئامڕازی ئەرشیڤ +جی ئامڕازی بنچینە +دوگمەی گەورە +پیشاندانی دەقی دوگمەکان +800 +&زیادکردنی بوخچە بۆ دڵخوازەکان ÙˆÛ•Ú© +دڵخوازی +900 +&هەڵبژاردنەکان +&نیشانەی پێوان +960 +&ناوەڕۆکەکان... +&دەربارەی 7-Zip... +1003 +Ú•ÛŽÚ•Û•Ùˆ +ناو +پاشگر +بوخچە +قەبارە +قەبارەی پێچراو +تایبەتمەندی +دروستکراوە +بینراوە +دەستکاریکراوە +Ú•Û•Ù‚ +لێدوانی لەسەر دراوە +پارێزراو +لەتکردن Ù¾ÛŽØ´ +لەتکردن پاش +Ùەرهەنگ +CRC +جۆر +دژ +ڕێباز +سیستەمی خانەخوێ +سیستەمی Ù¾Û•Ú•Ú¯Û• +بەکارهێنەر +گوروپ +بلۆک +لێدوان +جێگە +Ù€ÛŒ Ú•ÛŽÚ•Û•Ùˆ Prefix +بوخچەکان +پەڕگەکان +وەشان +قەبارەکان +ÙØ±Û• قەبارە +Offset +بەستەرەکان +بلۆکەکان +قەبارەکان + +٦٤-بت +Big-endian +CPU +قەبارەی Ùیزیکی +قەبارەی سەر +کۆپشکنین +تایبەتمەندی +ناونیشانی ڕاستی + + + + + + +Ù‡Û•ÚµÛ• +سەرجەمی قەبارە +بۆشایی بەتاڵ +قەبارەی Ú©Û†Ù…Û•ÚµÛ•Ú©Û• +نیشان +ناوی ناوخۆیی +دابینکەر +2100 +ەەڵبژاردنەکان +زمان +زمان: +دەستکاریکەر +&دەستکاریکەر: + +2200 +سیستەم +7-Zip پەیوەست بکە بە: +2301 +7-Zip بکە Ù†ÛŽÙˆ لیستەی مێنیوەوە +مێنیو بکە بە یەک هاوپۆل +کەرەستەکانی Ù†ÛŽÙˆ مێنیو: +2320 +<بوخچە> +<ئەرشیڤ> +کردنەوەی ئەشیڤ +دەرکێشانی پەڕگەکان +زیادکردن بۆ ئەرشیڤ +تاقیکردنەوەی ئەرشیڤ +لێرە دەریبکێشە +دەرکێشان بۆ {0} +زیادکردنی بۆ {0} +پەستاندن Ùˆ پەیامی ئەلیکترۆنی... +بیپەستێنە بۆ {0} Ùˆ بە پەیامی ئەلیکترۆنی بینێرە +2400 +بوخچەکان +بوخچەی &کارکردن +بوخچەی کاتی &سیستەم +&ئێستایی +&دیاریکراو: +تەنها بۆ ئەو وەگەڕخەرانە بەکاری بهێنە Ú©Û• توانای لابردنیان هەیە (removable disk) +شوێنێک دیاری بکە بۆ Ù¾Û•Ú•Ú¯Û• کاتییەکانی ئەرشیڤ. +2500 +ڕێکخستەکان +شتەکانی ".." پیشانبدە +ئایکۆنی ڕاستەقینەی پەڕگەکان پیشانبدە +مێنیوی سیستەم پیشانبدە +دیاریکردنی هەموو &ڕیزەکە +پیشاندانی Ù‡ÛŽÚµÛ• &grid ـەکان + +جۆری دیاریکردنی &جێگرەوە/ ئەڵتەرناتیڤ +Ù¾Û•Ú•Û•ÛŒ بیرگەی &گەورە بەکارببە +2900 +7-Zip دەربارەی +7-Zip پڕۆگرامێکی خۆڕایە. Ù„Û•Ú¯Û•Úµ ئەوەشدا، ئەتوانی پاڵپشت بیت Ù„Û• پەرەپێدانی 7-Zip. +3000 +سیستەم ناتوانێت بڕی پێویست Ù„Û• بیرگە دابین بکات +هیچ هەڵەیەک نییە +{0} شت دیاریکراوە +ناتوانرێ بوخچەی '{0}' دروستبکرێت. +کارپێکردنی نوێکاری پاڵپشت نەکراوە بۆ ئەم ئەرشیڤە. +ناتوانرێت Ù¾Û•Ú•Ú¯Û•ÛŒ '{0}' بکرێتەوە ÙˆÛ•Ú© ئەرشیڤ. +ناتوانرێت ئەرشیڤی پارێزراوی '{0}' بکرێتەوە. ئایا تێپەڕەوشە هەڵەیە؟ +جۆری ئەرشیڤ پاڵپشتی نەکراوە +Ù¾Û•Ú•Ú¯Û•ÛŒ {0} پێشتر ەەیە +Ù¾Û•Ú•Ú¯Û•ÛŒ '{0}' دەستکاریکراوە. ئەتەوێت Ù„Û• ئەرشیڤەکەدا نوێی بکەیتەوە؟ +ناتوانرێ Ù¾Û•Ú•Ú¯Û•ÛŒ \n'{0}' نوێبکرێتەوە +ناتوانرێ دەستکاریکەر دەستپێبکرێت. +Ù¾Û•Ú•Ú¯Û•Ú©Û• Ù„Û• ڤایرۆس دەچێت (ناوی Ù¾Û•Ú•Ú¯Û•Ú©Û• بۆشایی زۆری تیادایە). +ناتوانرێت ئەو کردارە بانگبکرێت Ù„Û• بوخچەیەکەوە Ú©Û• Ú•ÛŽÚ•Û•ÙˆÛŽÚ©ÛŒ درێژی ەەیە. +پێویستە پەڕگەیەک دیاریبکەیت +پێویستە پەڕگەیەک یان زیاتر دیاریبکەیت +شتی(item) زۆر Ù‡Û•Ù† +3300 +دەری دەکێشێ +دەی پەستێنێ... +تاقیدەکاتەوە +دەیکاتەوە... +دەی پشکنێت... +3400 +دەرکێشان +&دەرکێشان بۆ: +شوێنێک دیاری بکە بۆ Ù¾Û•Ú•Ú¯Û• دەرکێشراوەکان +3410 +جۆری Ú•ÛŽÚ•Û•Ùˆ +ناوی Ú•ÛŽÚ•Û•ÙˆÛŒ تەواوەتی +ناوی Ú•ÛŽÚ•Û•Ùˆ نییە +3420 +جۆری بەسەردا نووسینەوە +بپرسە Ù¾ÛŽØ´ بەسەردا نووسینەوە +بەسەردا بنووسەوە بەبێ ڕەزامەندی +Ù¾Û•Ú•Ú¯Û• هەبووەکان بپەڕێنە +خۆکار ناوگۆڕین +خۆکار ناوگۆڕینی Ù¾Û•Ú•Ú¯Û• هەبووەکان +3500 +دڵنیابە Ù„Û• جێگرتنەوەی Ù¾Û•Ú•Ú¯Û• +بوخچەی مەبەست پەڕگەیەکی تیادایە بە هەمان ناو. +ئەتەوێت Ù¾Û•Ú•Ú¯Û• هەبووەکان جێبگیردرێتەوە؟ +Ù„Û•Ú¯Û•Úµ ئەم دانەیە؟ +{0} بایت +ناوگۆڕینی &خۆکار +3700 +ڕێبازێکی پاڵپشتی نەکراوی پەستاندن بۆ '{0}'. +زانیاری '{0}' هەڵەیە. Ù¾Û•Ú•Ú¯Û• تێکشکاوە. +CRC سەرکەوتوو نەبوو. Ù¾Û•Ú•Ú¯Û• تێکشکاوە. +زانیاری هەڵەیە Ù„Û• Ù¾Û•Ú•Ú¯Û•ÛŒ پارێزراودا '{0}'. ئایا تێپەڕەوشە هەڵەیە؟ +CRC هەڵەیە Ù„Û• Ù¾Û•Ú•Ú¯Û•ÛŒ پارێزراودا '{0}'. ئایا تێپەڕەوشە هەڵەیە؟ +3800 +نووسینی تێپەڕەوشە: +تێپەڕەوشە بنووسە: +تێپەڕەوشە بنووسەوە: +&پیشاندانی تێپەڕەوشە +تێپەڕەوشەکان ÙˆÛ•Ú© یەک نین +تەنها پیتە ئینگلیزییەکان، ژمارە Ùˆ نووسە تیبەتییەکان ($ØŒ %ØŒ #) بۆ تێپەڕەوشە بەکاربهێنە +تێپەڕەوشە زۆر درێژە +تێپەڕەوشە +3900 +Ú©. دەستپێکردوو: +کاتی ماوە: +سەرجەمی قەبارە: +خێرایی: +جێبەجێکراوە: +Ú•ÛŽÚ˜Û•ÛŒ پەستاندن: +هەڵەکان: +ئەرشیڤەکان: +4000 +زیادکردن بۆ ئەرشیڤ +&ئەرشیڤ: +&جۆری نوێکاری: +&Ùۆڕماتی ئەرشیڤ: +&ئاستی پەستاندن: +&ڕێبازی پەستاندن: +قەبارەی &Ùەرەەنگ: +قەبارەی &وشە: +قەبارەی بلۆکی Ú•Û•Ù‚: +ژمارەی دەزووەکانی CPU: +&هاوکۆڵکەکان: +هەڵبژاردنەکان +دروستکردنی ئەرشیڤی SF&X +پەستاندنی Ù¾Û•Ú•Ú¯Û• ئاڵوگۆڕکراوەکان +پاراستن +ڕێبازی پاراستن +&ناوی پەڕگەکان بپارێزە (encrypt) +Ú•ÛŽÚ˜Û•ÛŒ بەکارهێنانی بیرگە بۆ پەستاندان: +Ú•ÛŽÚ˜Û•ÛŒ بەکارهێنانی بیرگە بۆ کردنەوەی پەستێنراو: +4050 +پاشەکەوت +خێراترین +خێرا +ئاسایی +زۆرترین +سەروو +4060 +زیادکردن Ùˆ جێگرتنەوەی پەڕگەکان +نوێکردنەوە Ùˆ زیادکردنی Ù¾Û•Ú•Ú¯Û• +بووژاندنەوەی Ù¾Û•Ú•Ú¯Û• هەبووەکان +ـکردنی پەڕگەکان Synchronize +4070 +هەڵدەوە/ Browse +هەموو پەڕگەکان +نا-Ú•Û•Ù‚ +Ú•Û•Ù‚ +6000 +لەبەرگرتنەوە +گواستنەوە +لەبەرگتنەوە بۆ: +گواستنەوە بۆ: +لەبەری دەگرێتەوە... +دەیگوازێتەوە بۆ... +ناوی دەگۆڕێ... +بوخچەی مەبەست دیاری بکە. +ئەو کارە پاڵپشتی نەکراوە +Ù‡Û•ÚµÛ• هەیە Ù„Û• ناو گۆڕینی Ù¾Û•Ú•Ú¯Û• یان بوخچەکان. +دڵنیابە Ù„Û• لەبەرگرتنەوەی Ù¾Û•Ú•Ú¯Û• +دڵنیایت Ù„Û• لەبەرگرتنەوەی پەڕگەکان بۆ ئەرشیڤ؟ +6100 +دڵنیابە Ù„Û• سڕینەوەی Ù¾Û•Ú•Ú¯Û• +دڵنیابە Ù„Û• سڕینەوەی بوخچە +دڵنیابە Ù„Û• سڕینەوەی هەموو پەڕگەکان +دڵنیایت Ù„Û• سڕینەوەی '{0}'ØŸ +دڵنیایت Ù„Û• سڕینەوەی بوخچەی '{0}' Ùˆ هەموو ناوەڕۆکەکانی؟ +دڵنیایت Ù„Û• سڕینەوەی ئەم {0} شتە؟ +دەی سڕێتەوە... +Ù‡Û•ÚµÛ• هەیە Ù„Û• سڕینەوەی پەڕگەکان یا بوخچەکان +سیستەم ناتوانێت پەڕگەیەک بگوێزێتەوە بۆ تەنەکەی خۆڵ بە Ú•ÛŽÚ•Û•ÙˆÛŒ درێژەوە +6300 +دروستکردنی بوخچە +دروستکردنی Ù¾Û•Ú•Ú¯Û• +ناوی بوخچە: +ناوی Ù¾Û•Ú•Ú¯Û•: +بەخچەی نوێ +Ù¾Û•Ú•Ú¯Û•ÛŒ نوێ +Ù‡Û•ÚµÛ• هەیە Ù„Û• دروستکردنی بوخچە +Ù‡Û•ÚµÛ• هەیە Ù„Û• دروستکردنی پەڕگەدا +6400 +لێدوان +&لێدوان: +دیاری بکە +دیاری Ù…Û•Ú©Û• +دەمامک: +6600 +تایبەتمەندییەکان +مێژووی بوخچەکان +پەیامی لێکۆڵینەوە/ پشکنین +پەیام +7100 +کۆمپیوتەر +ڕایەڵە +بەڵگەنامەکان +سیستەم +7200 +زیادکردن +دەرکێشان +تاقیکردنەوە +لەبەرگرتنەوە +گواستنەوە +سڕینەوە +زانیاری +7300 +لەتکردنی Ù¾Û•Ú•Ú¯Û• +&لەتی بکە بۆ: +لەتی بکە بۆ &قەبارە Ùˆ بایت:‌ +لەتی دەکات... +دڵنیابە Ù„Û• لەتکردن +دڵنیایت Ù„Û• لەتکردنی Ù¾Û•Ú•Ú¯Û•ÛŒ {0} بۆ دوو قەبارە؟ +پێویستە قەبارەی هەر یەک Ù„Û• لەتەکان بچوکتربێت Ù„Û• قەبارەی Ù¾Û•Ú•Ú¯Û• لەتکراوەکە +قەبارەی لەتکراوەکە هەڵەیە +قەبارەی دیاریکراوی لەتکراوەکە: {0} بایت.\nدڵنیایت Ù„Û• لەتکردنی ئەرشیڤەکە بۆ ئەو قەبارانە؟ +7400 +پێکەوەلکاندنی پەڕگەکان +%Ù¾ÛŽÚ©Û•ÙˆÛ•ÛŒ بلکێنە بۆ: +Ù¾ÛŽÚ©Û•ÙˆÛ•ÛŒ دەلکێنێت... +تەنها Ù¾Û•Ú•Ú¯Û•ÛŒ یەکەم دیاری بکە +ناتوانێت Ù¾Û•Ú•Ú¯Û• بدۆزێتەوە ÙˆÛ•Ú© بەشێک Ù„Û• Ù¾Û•Ú•Ú¯Û• لەتکراوەکە +ناتوانێت Ù„Û• بەشێک زیاتر بدۆزێتەوە Ù„Û• Ù¾Û•Ú•Ú¯Û• لەتکراوەکە +7500 +ژماردنی کۆپشکنین... +زانیاری کۆپشکنین +کۆپشکنینی CRC بۆ دراوە: +کۆپشکنینی CRC بۆ دراوە Ùˆ ناوەکان: +7600 +نیشانەی پێوان +Ú•ÛŽÚ˜Û•ÛŒ بەکارهێنراوی بیرگە: +پەستاندن +کردنەوەی پەستێنراو +هەڵسەنگاندن +سەرجەمی هەڵسەنگاندن +ئێستایی +ئەنجام +CPU Ú•ÛŽÚ˜Û•ÛŒ بەکارهێنراوی +Ú•ÛŽÚ˜Û•ÛŒ بەکارەێنراو / هەڵسەنگاندن +دەرچوونەکان: diff --git a/Utils/7-Zip/Lang/ku.txt b/Utils/7-Zip/Lang/ku.txt new file mode 100644 index 000000000..e5fb38f53 --- /dev/null +++ b/Utils/7-Zip/Lang/ku.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.37 : Rizoyê Xerzî +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Kurdish +Kurdî +401 +Temam +Betal + + + +&Erê +&Na +Bi&gire +Alîkarî + +Bi&domîne +440 +&Ji Bo Hemûyî Erê +Ji &Bo Hemûyî Na +Raweste +Dîsa Destpêke +L%i PiÅŸt +Li &Pêş +&Rawestîne +Rawestiya - +Ma bila betal bibe? +500 +&Dosya +&Bipergalîne +&Nîşan Bide +Bi&jare +&Amûr +A&lîkarî +540 +&Veke +&Di Panelê De Veke +Di &Paceyê De Veke +&Nîşan Bide +&Sererast bike +&Navekî Nû Bidê +&Ji Ber Bigire +B&ar Bike +Jê B&ibe +Parçe Bi&ke... +Bike &Yek... +&Taybetî +Da&xuyanî +checksum heseb bike + +Pe&ldankeke Nû +Do&siyeke Nû +De&rkeve +600 +&Hemûyî hilbijêre +He&mû hilijartinê rake +Be&revajî wê hilbijêre +&Hilbijêre... +Hilbijarti&nê Rake... +Vî cureyî hilbijêre +Hilbijartina cure rake +700 +&Dawêrên Mezin +D&awêrên Biçûk +&Lîste +&Hûragahî +730 +Bê Dor +xuyakirina sade +&2 Panelan veke +Da&rikê amûran +Peldanka Kok Veke +Astekê Berjor +Dîroka Peldankê... +&Nû Bike +750 +Darikê arşîvê +Darikê standart +BiÅŸkojkên mezin +Bila nivîsa biÅŸkojkan bixuye +800 +Pe&ldanka derbasdar veke +Cih +900 +&Vebijêrk... +&Pîvana Çalakbûnê +960 +&Naverok... +D&er barê 7-Zip de... +1003 +Rê +Nav +Cure +Peldank +Mezinahî +Mezinahiya di arşîvê de +Taybetiyên xweser +Çêbûyî +GihaÅŸtî +Guhertî +HiÅŸk +Daxuyankirî +Şîfrekirî +Parçeyê borî +Parçeyê piÅŸtre +Ferheng +CRC +Cure +Dij +Awayê ÅŸidandinê +Platform +Pergala Dosiyê +Bikarhêner +Kom +Stûn +Daxuyanî +Cih +Path Prefix + + + + + + + + + + + + + + + + + + + + + + + + +Çewtî +Hemû Mezinahî +Cihê Vala +Mezinahiya telpikî +Etîket +Navê Herêmî +Derfetkar +2100 +Vebijêrk +Ziman +Ziman: +Per&galker +Pergalkerê De&qê: + +2200 +Pergal +Bi 7-Zip re têkildar bike: +2301 +Bila di pêşekên naverokê de 7-Zip bixuye +Pêşeka naverokê ya sûlavî +Hêmanên peÅŸeka naverokê: +2320 + + +Arşîvê veke +Dosiyan derxe... +Bike Arşîv... +Arşîvê bihêçîne +Valake vir +Peldanka {0} derxe +Wekî {0} bike arşîv +BiÅŸidîne û bişîne... +Wekî {0} biÅŸidîne û bişîne +2400 +Peldank +Peldanka &Xebatê +&Peldanka TEMP a Pergalê +P&eldanka Derbasdar +Pe&ldanka Navborî: +Bi tenê ji bo ajokarên derketî bi kar bîne +Ji bo dosiyên demdemî yên arşîvê cih belî bike. +2500 +Mîheng +Bila hêmana ".." xuya bike +Bila dawêrên dosiyên rastî xuya bikin +Bila pêşeka pergalê xuya bike +Bila &hemû rêzikê bibore +Bila &xêzên tabloyê xuya bike + +&Kipa hilbijartina alternatîf +bîra berfireh bikar bîne +2900 +Der barê 7-Zip de +7-Zip nivîsbariyeke azad e. Lê, tu dikarî wekî tomarkirin desteka pêşxistina 7-zip bibî. +3000 + +Çewtî nîn e. +{0} heb hêman hilbijartî ne +Peldanka '{0}' nayê çêkirin +Tu nikarî vê arşîvê rojane bikî. + + + + +Dosiya '{0}' hatiye guhartin.\nMa bila di arşîvê bête rojanekirin? +Dosiya '{0}' nehate rojanekirin +Pergalkerê deqê nehate destpêkirin. + + + + +Hêman zêde ne +3300 +Tên derxistin +tên ÅŸidandin +tê hêçandin +vedibe... +Tê raguhestin +3400 +Derxe +&Cihê Dê Derkevê: +Ji bo dosya derkevinê cihekî belî bike. +3410 +Navê Rê +Navê tevahî yên rê +Bila navên rê tune bin +3420 +Dosiyên heyî +Ji bo li ser binivîse bipirse +Bê pirs li ser binivîse +Derxistin +Navekî nû li yên di arşîvê de bike +Navekî nû li yên heyî bike +3500 +Rewla Lisernivîsandinê +Di dosiya armanckirî de bi vî navî dosiyek heye. Bila li ser bête nivîsîn? +Dosiya heyî: +Dosiya tê derxistin: +{0} bayt +Na&vekî nû lê bike +3700 +Ji bo '{0}' awayê ÅŸidandinê nehate nasîn. +'{0}' xerabe ye. (Çewtiya daneyê) +'{0}' xerabe ye. (Çewtiya CRC) + + +3800 +Têketina Şîfreyê +Şîfreyê binivîse: + +Bi&la şîfre bixuye + + + +Şîfre +3900 +Dema borî: +Dema mayî: +Mezinahî: +Lez: + + +Çewt: + +4000 +Bike Arşîv +&Arşîv: +A&wayê rojanekirinê: +Awa&yê arşîvê: +A&sta ÅŸidandinê: +Awayê ÅŸi&dandinê: +Me&zinahiya ferhengê: +M&ezinahiya bêjeyê: + + +&Parametre: +Vebijêrk +Bila ew bixwe derxe (SFX) + + + +Navên dosiyê bike şîfre +Bikaranîna bîrê (Åžidandin): +Bikaranîna bîrê (Vekirin): +4050 +Bêyî ÅŸidandin +Leztirîn +Bi Lez +Asayî +Herî Pir +Ultra +4060 +Dosiyan têxê, yên heyî derxe +Dosiyan têxê, yên kevin rojane bike +Bi tenê yên kevin rojane bike +Dosiyan bike wekî hev +4070 +Bibîne +Hemû dosya + + +6000 +Ji Ber Bigire +Bar Bike +Cihê Dê Were Jibergirtin: +Cihê Dê Were Barkirin: +tê jibergirtin... +tê barkirin... +navekî nû tê lêkirin... + +Kirin ne pêkan e. +Çewtiya Navlêkirinê +Erêkirina Jibergirtinê +Ma bila dosî ji bo arşîvê bên jibergirtin +6100 +Erêkirina jêbirina dosiyê +Erêkirina jêbirina peldankê +Erêkirina jêbirina gelek dosiyan +Ma bila dosiya '{0}' bête jêbirin? +Ma bila peldanka '{0}' û yên tê de bên jêbirin? +Ma bila hêmana {0} bête jêbirin? +tê jêbirin... +Çewtiya Jêbirinê + +6300 +Peldankeke nû +Dosiyeke Nû +Navê peldankê: +Navê Dosiyê: +Peldankeke Nû +Dosiyeke Nû +Çewtiya Çêkirina peldankê +Çewtiya çêkirina Dosiyê +6400 +Daxuyanî +&Daxuyanî: +Hilbijêre +Hilbijartinê rake +Derbirîna hilbijartinê: +6600 + +Rabirdûya Peldankê +Peyamên haydariyê +Peyam +7100 +Komputer +Tor + +Pergal +7200 +Bike Arşîv +Derxe +Bihêçîne +Ji Ber Bigire +Bar Bike +Jê Bibe +Agahî +7300 +Bike Parçe +Di &vê peldankê de parçe bike: +Wekî Bayt/&cilt bike parçe: +tê parçekirin... + + + + + +7400 +Bike Yek +Di vê &peldankê de bike yek: +tê yekirin... + + + +7500 +Hesabê sererastkirî... +Agahiyên hesabê sererast +CRC hesabê sererast bo data: +CRC hesabê sererast bo data û nav: +7600 +Çalakiya komputerê +Bikaranîna birê: +Åžidandin +Vekirin +Puan +bi guloverî puanan +Carî +Encam + + +Serkeftî: diff --git a/Utils/7-Zip/Lang/ky.txt b/Utils/7-Zip/Lang/ky.txt new file mode 100644 index 000000000..3fff8815b --- /dev/null +++ b/Utils/7-Zip/Lang/ky.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.20 : Kalil uulu Bolot +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Kyrgyz +Кыргызча +401 +OK +Ðйнуу + + + +&Ооба +&Жок +&Жабуу +Жардам + +&Улантуу +440 +Ооба &баарына +Жок б&аарына ÑÐ¼ÐµÑ +Токто +Кайта иштетүү +&Фоном +&Ðлдыңкы планга +&Тыныгуу +Тыныгууда +Сиз чын Ñле операциÑны токтоткуңуз келип жатабы? +500 +&Файл +&Оңдоо +&Кейп +&Тандалган +С&ÐµÑ€Ð²Ð¸Ñ +&Жардам +540 +&Ðчуу +Ичи&нен ачуу +Сырты&нан ачуу +Көрүү +&Редактирлөө +Ðтын& өзгөртүү +&Буга көчүрүү... +&Буга ордун которуу... +&Өчүрүү +Фа&лды бөлүштүрүү... +Ф&айлдарды кошуу... +КаÑ&иети +Комме&нтарий +Контролдук жыйынтык +Diff +&Баштык жаратуу +Фай&л жаратуу +Ч&ыгуу +600 +Баарын б&өлүү +Бөлүнгөндү алуу +&Бөлүүнү к&аратуу +Бөлүү... +Бөлүнгөндү алуу... +Түзү менен бөлүү +Түзү менен бөлүнгөндү алуу +700 +&Чоң белгилер +&Кичине белгилер +Тиз&ме +&Таблица +730 +ИргөөÑүз +Түз түзүлүш +&2 Панель +&ÐÑпап панелдери +Түпкү баштыкты ачуу +Бир деңгÑÑлге өйдө өтүү +Баштыктардын тарыхы... +Ж&аңыртуу +750 +Ðрхиватордун баÑкычтарынын панели +Стандарттык баÑкыч панели +Чоң баÑкычтар +БаÑкычтагы жазуулар +800 +Кантип тандалганга& баштыкты кошуу +Чөп кат +900 +Оңдоп-түздөмөлөр... +Өндүрүмдүүлүктү теÑтирлөө +960 +&Мазмуну... +Программа &жөнүндө... +1003 +Жол +ÐÑ‚ +Кенейтүү +Баштык +Көлөм +КыÑылган +Ðтрибуттар +Жаралган +Ðчылган +Өзгөртүлгөн +ҮзгүлтүкÑүз +Комментарий +Шифрланган +Чейин бөлүнгөн +Кийин Бөлүнгөн +Сөздүк +CRC +Түр +Ðнти +Ðмал +СиÑтема +Файлдык ÑиÑтема +Колдонуучу +Топ +Блок +Комментарий +ЭÑлеген ахывал +Жол +Баштыктар +Файлдар +ВерÑиÑÑÑ‹ +Том +Көптомдуу +Жылдыруу +Сүрүштүрүү +Блоктор +Томдор + +64-bit +Big-endian +ПроцеÑÑор +Физикалык көлөм +Баш аттардын көлөмү +Текшерүү жыйынтык +Мүнөздөмөлөр +Виртуалдык дарек +ID +КыÑка ат +Жараткан +Сектордун көлөмү +Түзүлүш +Сүрүштүрүү +Ката +Сыйдыргычтык +Бош +Размер клаÑтера +Белги +Локалдык ат +Провайдер +2100 +Оңдоп-түздөмөлөр +Тил +Тил: +Редактор +&Редактор: +&Diff: +2200 +СиÑтема +7-Zip менен файлдарды бириктирүү: +2301 +7-Zipти менюнун контекÑттик кыртышына жайгаштыруу +КаÑкадттык контекÑттик меню +КонтекÑттик менюнун Ñлементтери: +2320 +<Баштык> +<Ðрхив> +Ðрхивди ачуу +Таңгагын чечүү +Ðрхивке кошуу... +ТеÑтирлөө +Мында таңгагын чечүү +Буга таңгагын чечүү {0} +Буга кошуу {0} +КыÑып жана аны email менен жиберүү... +Буга кыÑып {0} жана email менен жиберүү +2400 +Баштыктар +&Иштөө баштык +&Убактылуу ÑиÑтемалык баштык +&Учурдагы +&Тапшыруу: +Котормолуу алып жүрүүчүлөргө гана колдонуу +Убактылуу архивтерге турган ордун көрÑөтүңүз. +2500 +Оңдоп-түздөмөлөр +".." Ñлементин көрÑөтүү +Файлдардын реалдуу иконкаларын көрÑөтүү +СиÑтемалык менюну көрÑөтүү +КурÑор жалпы Ñапка +Бөлгүчтөрдү көрÑөтүү +Бир баÑуу менен ачуу +Белгинин алтернативалык режими +ЭÑтин чоң барактарын колдонуу +2900 +7-Zip программа жөнүндө +7-Zip Ñркин таратылуучу программа.Бирок Ñиз 7-Zipтин иштетүүÑүн колдогуңуз келÑе,Ñиз программаны каттатып койÑоңуз болот. +3000 +Эркин ÑÑке тутуу жетишÑиз +Ката табылган жок +{0} ÐерÑелер бөлүнгөн +'{0}' баштыкты жаратуу ишке ашкан жок +Бул архив үчүн өзгөртүү операциÑлары колдолбойт. +'{0}' архивтей кылып ачуу ишке ашкан жок +Шифрленген архивди ачуу ишке ашкан жок '{0}'. КупуÑ-белги туура ÑмеÑ? +Ðрхивдин колдоо кылынбаган түрлөрү +{0} деген файл бар +'{0}' файлы өзгөртүлгөн.\nСиз аны архивде жаңырткыңыз келип жатабы? +\n'{0}' файлын жаңыртуу ишке ашкан жок +Редакторду иштетүү, ишке ашкан жок +Файл вируÑка окшош (файлдын аты ырааттуу бош жерди камтыйт). +Баштыктын ичинен Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð°Ñ‚ÐºÐ°Ñ€Ñ‹Ð»Ð±Ð°Ð¹Ñ‚, жолдун узактыгынан. +Сиз бир фалды бөлүшүңүз керек +Сиз бир нече файлды бөлүшүңүз керек +Өтө көп Ñлементтерди камтыйт +3300 +Таңгагын чечүү +КомпреÑÑиÑлоо +ТеÑтирлөө +Ðчуу... +Сканирлөө... +3400 +Чыгаруу +& Буга таңгагын чечүү: +Чыгарып жаткан файлдар үчүн ордун көрÑөтүңүз. +3410 +Жолдор +То&лук жолдор +&ЖолÑуз +3420 +Кайта жазуу +&Далилдөө менен +Д&алилдөөÑүз +Өткө&Ñ€Ò¯Ò¯ +Ðвтоматтык түрдө атын өзгөртүү. +Бар файлдардын аттарын автоматтык түрдө өзгөртүү. +3500 +Файлды алмаштырууну далилдөө +Баштык иштеп чыгарылган файлды камтыйт. +Бар файлдарды алмаштыруу +кийинки файл менен? +{0} байт +Ðвтоматтык түрдө атын өзгөртүү. +3700 +'{0}' файлдагы колдобоочу кыÑуу амалы. +'{0}' берилүүÑүндөгү ката.файл бузук. +'{0}' CRCте ката.файл бузук. +'{0}' шифрленген файлда ката. Туура ÑÐ¼ÐµÑ ÐºÑƒÐ¿ÑƒÑ-белги? +'{0}' шифрленген файл үчүн CRCте ката. Туура ÑÐ¼ÐµÑ ÐºÑƒÐ¿ÑƒÑ-белги? +3800 +КупуÑ-белгини киргизүү +&КупуÑ-белгини киргизиңиз: +&КупуÑ-белгини кайталаңыз: +&КупуÑ-белгини көрÑөтүү +КупуÑ-белгилер бири-бирине туура келбейт +КупуÑ-белги үчүн латын алфавитиндеги Ñимволдорду гана колдонуңуз,Ñандарды жана атайын (!, #, $, ...) Ñимволдорун +КупуÑ-белги өтө узун +&КупуÑ-белги +3900 +Өттү: +Калды: +Баары: +Ылдамдык: +Көлөм: +КыÑуунун даражаÑÑ‹: +Каталар: +Ðрхивтер: +4000 +Ðрхивке кошуу +&Ðрхив: +&Өзгөртүүнүн режими: +&Ðрхивдин форматы: +&КыÑуунун деңгÑÑли: +&КыÑуунун амалы: +Сөздүктүн &көлөмү: +Сөздүн к&өлөмү: +Блоктун көлөмү: +Ðгымдардын Ñаны: +&Параметрлер: +&Оңдоп-түздөмөлөр +SF&X-архивди жаратуу +Файлдарды жаздыруу үчүн ачууну кыÑуу +Шифрлөө +Шифрлөөнүн амалы: +&Файлдардын атын шифрлөө +Салып бекитүү үчүн ÑÑтин көлөмү: +Таңгагын чечүү үчүн ÑÑтин көломү: +4050 +КыÑууÑуз +Ылдамдуу +Бат +Кадимкидей +ЭÑÒ£ жогорку +Ультра +4060 +Кошуп жана алмаштыруу +Жаңыртып жана кошуу +Жаңыртуу +Калптандыруу +4070 +Барактоо +Баардык файлдар +Файлдын көлөмүндөй +ҮзгүлтүкÑүз +6000 +Көчүрүү +Которуштуруу +Буга көчүрүү: +Буга которуштуруу: +Көчүрүү... +Ордун которуу... +Ðтын өзгөртүү... +Баштыкты көрÑөтүңүз. +ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð±ÑƒÐ» баштык үчүн колдолбойт. +Баштыктын же файлдын атын өзгөртүүдө ката +Файлдарды көчүрүүнү аныктоо +Сиз бул файлдарды архивке көчүрүүнү чын Ñле каалап жатаÑызбы +6100 +Файлдын өчүрүүÑүн аныктоо +Баштыктын өчүрүүÑүн аныктоо +Файлдын тобун өчүрүүÑүн аныктоо +Сиз "{0}" өчүрүүнү чын Ñле каалап жатаÑызбы? +Сиз "{0}" баштыктын ичиндегилери менен өчүрүүнү чын Ñле каалап жатаÑызбы? +({0} даана.) нерÑелерди өчүрүүнү чын Ñле каалап жатаÑызбы? +Өчүрүү... +Баштыкты же файлды өчүрүүдө ката +СиÑтема узун жолдуу файлдарды өчүрүүбаштыкка өчүрүү операциÑÑын колдобойт +6300 +Баштык жаратуу +Файл жаратуу +Баштыктын аты: +Файлдын аты: +Жаңы баштык +Жаңы файл +Баштык жаратуудан ката +Файл жаратуудан ката +6400 +Комментарий +&Комментарий: +Бөлүү +Бөлүнгөндү алып Ñалуу +МаÑка: +6600 +КаÑиет +Баштыктардын тарыхы +Билдирүү +Билдирүү +7100 +Компьютер +Желе +Иш кагаздар +СиÑтема +7200 +Кошуу +Чыгаруу +ТеÑтирлөө +Көчүрүү +Ордун которуу +Өчүрүү +Маалымат +7300 +Файлды бөлүштүрүү +&Буга бөлүштүрүү: +(байт ) көлөмү менен томдорго бөлүштүрүү: +Бөлүштүрүү... +Бөлүштүрүүнү аныктоо +Сиз файлды {0} бөлүккө бөлгүңүз келип жатабы? +Томдун көлөмү баштапкы файлдын көлөмүнөн кичине болушу керек +Томдордун көлөмүн берүүдө ката +Отургузулган томдун көлөмү: {0} байт.\nСиз чын Ñле архивди ушундай томдорго бөлгүңүз келип жатабы? +7400 +Файлдарды бириктирүү +&Буга бириктирүү: +Биригүү... +Бөлүштүрүлгөн файлдын биринчи бөлүгүн гана бөлүү керек +Бөлүштүрүлгөн файлды таану ишке ашкан жок +Бөлүштүрүлгөн файлдын бир бөлүгүн дагы табуу ишке ашкан жок +7500 +Текшерүүнүн жыйынтыгын ÑÑептөө... +Текшерүүнүн жыйынтыгы +Маалымат үчүн CRC текшерүү жыйынтыгы: +Маалымат жана аттар үчүн CRC текшерүү жыйынтыгы: +7600 +Өндүрүмдүүлүктү теÑтирлөө +ЭÑтин көлөмү: +Салып бекитүү +Таңгагын чечүү +Рейтинг +Жалпы рейтинг +Учурдагы +Жыйынтыгы +Жүк +Рейтинг / Жүк. +Өтүүлөр: diff --git a/Utils/7-Zip/Lang/lij.txt b/Utils/7-Zip/Lang/lij.txt new file mode 100644 index 000000000..bf31dacee --- /dev/null +++ b/Utils/7-Zip/Lang/lij.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.07 : GENOVES.com.ar +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Ligurian +Zeneize +401 +D'acòrdio +Anulla + + + +&Sci +&No +Sæ&ra +Agiutto + +&Continoa +440 +Sci pe &Tutti +No pe T&utti +Ferma +Inandia torna +Into &sfondo +&In primmo cian +&Paoza +In paoza +Ti ê seguo de voei anulâ? +500 +&Archivio +&Modifica +&Vixoalizza +&Preferii +&Strumenti +A&giutto +540 +&Arvi +Arvi into Manezatô d'archivi 7-Zip +Arvi inte Explorer +&Vixoalizza +&Modifica +Ri&nomina +&Còpia inte... +&Spòsta inte... +Scancel&la +&Dividi l'archivio... +&Unisci i archivi... +P&ropietæ +Comen&ta... +Calcola somma de contròllo +Dif +Crea cartella +Crea archivio +Sc&iòrti +600 +Seleçionn-a &tutto +Deseleçionn-a tutto +In&verti seleçion +Seleçionn-a... +Deseleçionn-a... +Seleçionn-a pe tipo +Deseleçionn-a pe tipo +700 +Figue &grende +Figue picinn-e +&Listin +&Dæti +730 +Nisciun ordine +Vista ciatta +&2 barcoîn +Bare di &Strumenti +Arvi cartella prinçipâ +Livello supeiô +Cronologia... +&Agiorna +750 +Bara di strumenti Archivio +Bara di strumenti Normali +Figue grende +Mostra etichette de tèsto +800 +&Azonzi a cartella a-i Preferii comme +Colegamento +900 +&Ã’psioin... +&Ponto de riferimento +960 +&Goidda... +&Informaçioin in sce de 7-Zip... +1003 +Percorso +Nomme +Estension +Cartella +Mezua +Mezua Conpressa +Atributi +Creou +Urtimo acesso +Urtima modifica +Sòlido +Comentou +Criptou +Dividi primma +Dividi dòppo +Diçionaio +CRC +Tipo +Anti +Metodo +OS destinaçion +Archivio do scistemma +Utente +Gruppo +Blòcco +Comento +Poxiçion +Prefisso da destinaçion +Cartelle +Archivi +Verscion +Volumme +Multivolumme +Offset +Colegamenti +Blòcchi +Volummi + +64-bit +Big-endian +CPU +Mezua fixica +Mezua de titoli +Somma de contròllo +Carateristiche +Indirisso Virtoale +ID +Nomme curto +Aplicaçion de creaçion +Mezua da seçion +Mòddo +Colegamento +Erô +Capaçitæ totale +Spaçio disponibile +Mezua de particole (cluster) +Etichetta +Nomme locale +Proveditô +2100 +Ã’psioin +Lengoa +Lengoa: +Editô +&Editô: +&Dif: +2200 +Scistemma +Asòccia 7-Zip a: +2301 +Integra 7-Zip into menù contestoale +Menù contestoale a cascaa +Elementi do menù contestoale: +2320 + + +Arvi +Estranni i archivi... +Azonzi a l'archivio... +Contròlla l'archivio +Estranni chi. +Estranni inte {0} +Azonzi a {0} +Conprimmi e invia pe email... +Conprimmi in {0} e invia pe email +2400 +Cartelle +Cartella de tra&vaggio +Cartella &Temp de Scistemma +&Corente +&Specificâ: +Deuvia solo pe dischi estraibili +Specifica unna cartella pe-i archivi tenporanni. +2500 +Inpostaçioin +Mostra l'elemento ".." +Mostra e icöne di archivi +Mostra e icöne do scistemma +Seleçionn-a a &riga intrega +Mostra &grixella +Sciacâ unna vòtta sola pe arvî un elemento +&Mòddo de seleçion alternativo +Deuvia grende &pagine de memöia +2900 +Dæti +7-Zip o l'é un programa libero e de badda. +3000 +O scistemma o no peu separâ a quantitæ de memöia necesaia +Nisciun erô +{0} ògetti seleçionæ +Inposcibile creâ a cartella '{0}' +No l'é poscibile efetoâ agiornamenti in sce quest'archivio. +Inposcibile arvî l'archivio '{0}' comme conpilaçion +Inposcibile arvî a conpilaçion criptâ '{0}'. Scoretta a paròlla d'ordine? +Tipo de conpilaçion no consentia +L'archivio {0} za o l'existe +L'archivio '{0}' o l'é stæto modificou.\nTi veu agiornâ l'archivio? +Inposcibile agiornâ l'archivio\n'{0}' +Inposcibile inandiâ l'editô. +L'archivio o pâ d'ese un virus (o seu nomme o gh'à di spaççi longhi). +L'òperaçion a no peu conpletase da unna cartella da percorso longo. +Ti devi seleçionâ un archivio +Ti devi seleçionâ ùn ò ciù archivi +Tròppi elementi +3300 +Estraçion in corso... +Conprescion in corso... +Contròllo in corso... +Avertua in corso... +Controlemmo... +3400 +Estranni +&Estranni inte: +Specifica unna cartella dove estrae i archivi. +3410 +Strutua de cartelle +Percorsci conpleti +Nisciun percorso +3420 +Sorvescritua +Domanda primma de sorvescrive +Sorvescrivi sensa domandâ +No sorvescrive i archivi existenti +Rinomina outomaticamente +Rinomina outomaticamente i archivi existenti +3500 +Conferma a sostitoçion de l'archivio +A cartella destinaçion a contegne za l'archivio fæto. +Ti voriesci sostitoî l'archivio +con questo? +{0} byte +Rinomina &outomaticamente +3700 +Metodo de conprescion no consentio pe '{0}'. +Erô di dæti inte '{0}'. L'archivio o l'é danezou. +CRC no coretto inte '{0}'. L'archivio o l'é danezou. +Erô di dæti inte l'archivio criptou '{0}'. Scoretta a paròlla d'ordine? +CRC falio inte l'archivio criptou '{0}'. Scoretta a paròlla d'ordine? +3800 +Introduxi paròlla d'ordine +Introduxi paròlla d'ordine: +Ripeti a paròlla d'ordine: +&Mostra paròlla d'ordine +E paròlle d'ordine no coincidan +Deuvia solo segni de l'ingleize, numeri e carateri speciali (!, #, $, ...) pe-a paròlla d'ordine +Paròlla d'ordine tròppo longa +Paròlla d'ordine +3900 +Tenpo trascorso: +Tenpo rimanente: +Mezua: +Velocitæ: +Procesou: +Tascia de conprescion: +Eroî: +Conpilaçion: +4000 +Azonzi a l'archivio +&Archivio: +Modalitæ d'a&giornamento: +&Formato de l'archivio: +&Livello de conprescion: +&Metodo de conprescion: +Mezua &Diçionaio: +Mezue da Pa&ròlla: +Mezua do blòcco sòlido: +Numero de thread CPU: +&Parametri: +Ã’psioin +Crea archivio outo-estraente +Conprimmi archivi condivixi +Critografia +Mòddo de criptâ: +Cripta o &nomme di archivi +Utilizzo da memöia pe conprescion: +Utilizzo da memöia pe deconprescion: +4050 +Nisciunn-a +Velociscima +Veloce +Normale +Mascima +Ultra +4060 +Azonzi e sostitoisci i archivi +Agiorna e azonzi i archivi +Agiorna i archivi existenti +Sincronizza i archivi +4070 +Sfeuggia +Tutti i archivi +No-sòlido +Sòlido +6000 +Còpia +Spòsta +Còpia inte: +Spòsta verso: +Còpia in corso... +Spostamento in corso... +Rinominaçion in corso... +Çerni cartella de destinaçion. +L'òperaçion a no l'é consentia. +Erô into rinominâ l'archivio ò a cartella +Conferma a còpia d'archivi +Ti ê seguo de voei copiâ di archivi a unna conpilaçion? +6100 +Conferma l'eliminaçion de l'archivio +Conferma l'eliminaçion da cartella +Conferma l'eliminaçion de ciù elementi +Ti ê seguo de voei eliminâ '{0}'? +Ti ê seguo de voei eliminâ a cartella '{0}' e tutto o seu contegnuo? +Ti ê seguo de voei eliminâ questi {0} elementi? +Eliminaçion in corso... +Erô inte l'eliminaçion de l'archivio ò da cartella +O scistemma o no peu caciâ via un archivio da percorso longo +6300 +Crea Cartella +Crea archivio +Nomme da cartella: +Nomme d'archivio: +Neuva cartella +Neuvo archivio +Erô inta creaçion da cartella +Erô inta creaçion de l'archivio +6400 +Comento +&Comento: +Seleçionn-a +Deseleçionn-a +Filtro: +6600 +Propietæ +Cronologia +Mesaggi diagnòstichi +Mesaggio +7100 +Calcolatô +Ræ +Documenti +Scistemma +7200 +Azonzi +Estranni +Contròlla +Còpia +Spòsta +Cancella +Propietæ +7300 +Dividi Archivio +&Dividi inte: +Di&vidi in ciù archivi, mezua in byte: +Dividimmo... +Conferma a divixon +Ti ê seguo de voei separâ l'archivio inte {0} volummi? +A mezua de volumme a deve ese ciù picinn-a de quella de l'òriginale +Scoretta a mezua do volumme +Mezua de volumme specificâ: {0} byte.\nTi ê seguo de voei separâ l'archivio inte quelli volummi? +7400 +Unisci i archivi +&Unisci inte: +Unimmo... +Seleçionn-a solo a primma parte de l'archivio divizo +No se gh'atreuva un archivio comme parte d'un archivio divizo +No se gh'atreuva ciù d'unna parte d'un archivio divizo +7500 +Calcolemmo a somma de contròllo... +Dæti da somma de contròllo +Somma de contròllo CRC pe dæti: +Somma de contròllo CRC pe dæti e nommi: +7600 +Ponto de riferimento +Utilizzo da memöia: +Conprescion in corso +Deconprescion in corso +Valutaçion +Valutaçion totale +Atoale +Derivou +Utilizzo de CPU +Rating / Utilizzo +Pasaggi: diff --git a/Utils/7-Zip/Lang/lt.txt b/Utils/7-Zip/Lang/lt.txt new file mode 100644 index 000000000..7f8fb71e9 --- /dev/null +++ b/Utils/7-Zip/Lang/lt.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 2.30 : Marius Navickas : http://www.teisininkas.lt/ivairus/7-zip: +; 4.57 : Domas Jokubauskis +; 15.05 : Vaidas777 (terminų Å¡altinis: www.raÅ¡tija.lt) +; +; +; +; +; +; +; +; +0 +7-Zip +Lithuanian +Lietuvių +401 +Gerai +AtÅ¡aukti + + + +&Taip +&Ne +&Uždaryti +Elektroninis žinynas + +&TÄ™sti +440 +T&aip Visiems +Ne v&isiems +Sustabdyti +IÅ¡ naujo +&Fone +&Pirminis procesas +&Laikinai sustabdyti +Laikinai sustabdyta +Ar jÅ«s esate tikri, kad norite atÅ¡aukti? +500 +&Failas +K&eisti +&Rodyti +MÄ—gi&amiausi +Ä®ran&kiai +&Elektroninis žinynas +540 +&Atverti +Atverti v&iduje +Atverti iÅ¡&orÄ—je +&Rodyti +K&eisti +Pervadi&nti +&Kopijuoti į... +&Perkelti į... +Å alin&ti +&Skaidyti failÄ…... +Jungti &failus... +Savy&bÄ—s +Kome&ntuoti +SkaiÄiuoti kontrolinÄ™ sumÄ… +Sulyginti +Sukurti aplankÄ… +Sukurti failÄ… +IÅ¡ei&ti +Nuoroda +&AlternatyvÅ«s srautai +600 +PažymÄ—ti &viskÄ… +NužymÄ—ti viskÄ… +Atv&irkÅ¡tinis žymÄ—jimas +Parinkti... +AtžymÄ—ti... +Pasirinkti pagal tipÄ… +AtžymÄ—ti pagal tipÄ… +700 +Did&elÄ—s piktogramos +&Mažos piktogramos +&SÄ…raÅ¡as +&IÅ¡samiai +730 +Nerūšiuotos +Nepaisyti aplankų +&2 skydeliai +&Mygtukų juostos +Atverti Å¡akninį aplankÄ… +Lygiu aukÅ¡Äiau +Aplankų istorija... +&Atnaujinti +AutomatiÅ¡kai atnaujinti +750 +Archyvo mygtukų juosta +StandartinÄ— mygtukų juosta +Dideli mygtukai +Rodyti užraÅ¡us ant mygtukų +800 +&PridÄ—ti aplankÄ… prie mÄ—giamiausiųjų kaip +MÄ—giamiausias +900 +&Nuostatos... +&Spartos bandymas +960 +&Turinys... +&Apie 7-Zip... +1003 +Kelias +Pavadinimas +PlÄ—tinys +Aplankas +Dydis +Dydis archyve +Atributai +Sukurta +Naudota +Pakeista +Vientisas +Komentaras +Å ifruotas +Perskyra prieÅ¡ +Perskyra paskui +Žodynas + +Tipas +Anti +BÅ«das +Kompiuterio OS +Bylų sistema +Naudotojas +GrupÄ— +Blokas +Komentaras +Pozicija +Kelio prieÅ¡dÄ—lis +Aplankai +Failai +Versija +Tomas +Daugiatomis +Poslinkis +Nuorodos +Blokai +Tomai + +64-bit +Mažėjantys baitai +Procesorius +Fizinis dysis +AntraÅ¡tÄ—s dydis +KontrolinÄ— suma +Charakteristikos +Virtualus adresas +ID +Trumpas pavadinimas +KÅ«rÄ—jo programa +Sektoriaus dydis +Moda +SimbolinÄ— nuoroda +Klaida +Visas dydis +Laisva vieta +Blokinio dydis +ŽymÄ— +Vietinis pavadinimas +TeikÄ—jas +NT saugumas +Alternatyvus srautas +Pagalbinis +IÅ¡trintas +Medis + + +Klaidos tipas +Klaidos +Klaidos +PerspÄ—jimai +PerspÄ—jimas +Srautai +AlternatyvÅ«s srautai +Alternatyvių srautų dydis +Virtualus dydis +IÅ¡pakuoto dydis +Visas fizinis dydis +Tomo numeris +Subtipas +Trumpas komentaras +Kodų lentelÄ— + + + +Pabaigos dydis +Ä®terpto bloko dydis +Nuoroda +Patvarioji nuoroda +iNode + +Tik skaityti +2100 +Nustatymai +Kalba +Kalba: +Redaktorius +R&edaktorius: +&Sulyginimas: +2200 +Sistema +Susieti 7-Zip su: +Visi vartotojai +2301 +Integruoti 7-Zip į kontekstinį meniu +Pakopinis kontekstinis meniu +Kontekstinio meniu įraÅ¡ai: +Piktogramos kontekstiniame meniu +2320 + + +Atverti archyvÄ… +IÅ¡skleisti failus... +Ä®traukti į archyvÄ…... +Patikrinti archyvÄ… +IÅ¡skleisti Äia +IÅ¡skleisti į {0} +Ä®traukti į {0} +Suglaudinti ir iÅ¡siųsti el. paÅ¡tu... +Suglaudinti į {0} ir iÅ¡siųsti el. paÅ¡tu +2400 +Aplankai +&Darbinis aplankas +&Sisteminis laikinas aplankas +D&abartinis +Nurodyta&s: +Naudoti tik iÅ¡imamiems diskams +Nurodyti vietÄ… laikiniems archyviniams failams. +2500 +Nustatos +Rodyti „..“ failų sÄ…raÅ¡e +Rodyti tikras failų piktogramas +Rodyti sisteminį meniu +&ŽymÄ—ti visÄ… eilutÄ™ +Rodyti tinklelio linija&s +Atverti vienu spustelÄ—jimu +&Kitoks žymÄ—jimo bÅ«das +Naudoti didžiu&lius atmintinÄ—s puslapius +2900 +Apie 7-Zip +7-Zip yra nemokama programa. +3000 +Sistema begali skirti reikalingo atminties kiekio +Klaidų nerasta +Pasirinkta {0} objektų: +Negalima sukurti aplanko „{0}“ +Å io archyvo negalima atnaujinti. +Negalima atverti „{0}“ failo kaip archyvo +Negalima atverti Å¡ifruoto archyvo „{0}“. Neteisingas slaptažodis? +Nepalaikomas archyvo tipas +Failas pavadinimu {0} yra +Failas „{0}“ buvo pakeistas.\nAr norite jį atnaujinti archyve? +Negalima atnaujinti failo\n„{0}“ +Negalima paleisti redaktoriaus. +Å is failas panaÅ¡us į virusÄ… (failo pavadinime yra pasikartojanÄių tarpų) +Å is veiksmas negali bÅ«ti vykdomas iÅ¡ aplanko, kuris turi ilgÄ… keliÄ… +JÅ«s privalote pažymÄ—ti vienÄ… failÄ… +JÅ«s privalote pažymÄ—ti bent vienÄ… failÄ… +Per daug elementų +Negalima atverti failo kaip {0} archyvo +Failas yra atvertas kaip {0} archyvas +Archyvas yra atvertas su ofsetu +3300 +IÅ¡skleidžiama +Glaudinama +Tikrinama +Atidaroma... +Skenuojama... +Å alinama +3320 +Glaudinama +Atnaujinama +Analizuojama +Dubliuojama +Glaudinama iÅ¡ naujo +Praleidžiama +Å alinama +Kuriama antraÅ¡tÄ— +3400 +IÅ¡skleisti +IÅ¡&skleisti į: +Nurodyti vietÄ… iÅ¡skleidžiamiesiems failams. +3410 +Kelio moda: +Pilni keliai +Jokių kelių +AbsoliutÅ«s keliai +Santykiniai keliai +3420 +PerraÅ¡ymo režimas +Paklausti prieÅ¡ perraÅ¡ant +PerraÅ¡yti neįspÄ—jant +Praleisti esanÄius failus +AutomatiÅ¡kai pervadinti +AutomatiÅ¡kai pervadinti esanÄius failus +3430 +PaÅ¡alinti Å¡akninio aplanko pasikartojimÄ… +Atstatyti failų saugumo nuostatas +3500 +Failų pakeitimo patvirtinimas +Paskirtame aplanke jau yra apdorojamas failas. +Ar norÄ—tumÄ—te pakeisti esanÄiÄ… failÄ… +Å¡iuo failu? +{0} baitų +A&utomatinis pervadinimas +3700 +Nepalaikomas suglaudinimo metodas failui „{0}“. +Duomenų klaida „{0}“. Failas pažeistas. +CRC klaida „{0}“. Failas pažeistas. +Duomenų klaida Å¡ifruotame faile „{0}“. Neteisingas slaptažodis? +CRC neatitikimas Å¡ifruotame faile „{0}“. Neteisingas slaptažodis? +3710 +Neteisingas slaptažodis? +3721 +Nepalaikomas suglaudinimo metodas +Duomenų klaida +CRC neatitikimas +Nepasiekiami duomenys +NetikÄ—ta duomenų pabaiga +Yra papildomi duomenys po naudingų duomenų +Tai nÄ—ra archyvas +Klaida antraÅ¡tÄ—se +Klaidingas slaptažodis +3763 +Nepasiekiama archyvo pradžia +Nepatvirtinta archyvo pradžia + + + +Nepalaikoma ypatybÄ— +3800 +Slaptažodžio įvedimas +Ä®veskite slaptažodį: +Pakartokite slaptažodį: +&Rodyti slaptažodį +Slaptažodžiai nesutampa +Slaptažodžiui naudokite tik lotyniÅ¡kas raides, numerius bei specialiuosius simbolius (!, #, $, ...) +Slaptažodis yra per ilgas +Slaptažodis +3900 +PraÄ—jÄ™s laikas: +LikÄ™s laikas: +Dydis: +Sparta: +Apdorota: +Suglaudinimo laipsnis: +Klaidos: +Archyvai: +4000 +Ä®traukti į archyvÄ… +&Archyvas: +Atna&ujinimo režimas: +Archyvo &formatas: +Suglaudinimo &lygis: +Glaudini&mo bÅ«das: +Žo&dyno dydis: +Ž&odžio dydis: +Vientiso bloko dydis: +CPU gijų skaiÄius: +&Parametrai: +Nuostatos +Sukurti iÅ¡&sipakuojantį archyvÄ… +Glaudinti atvertus įraÅ¡ymui failus +Å ifravimas +Å ifravimo metodas: +Už&Å¡ifruoti failų pavadinimus +AtmintinÄ—s naudojimas suglaudinimui: +AtmintinÄ—s naudojimas iÅ¡skleidimui: +IÅ¡trinti failus suglaudinus +4040 +Ä®siminti simbolines nuorodas +Ä®siminti patvariasias nuorodas +Ä®siminti alternatyvius duomenų srautus +Ä®siminti failų saugumo nuostatas +4050 +Mažiausias +Greitesnis +Greitas +Normalus +Didžiausias +Smarkiausias +4060 +Ä®traukti ir pakeisti failus +Atnaujinti ir įtraukti failus +Atnaujinti esanÄius failus +Sinchronizuoti failus +4070 +NarÅ¡yti +Visus failus +Ne vientisas +Vientisas archyvas +6000 +Kopijuoti +Perkelti +Kopijuoti į: +Perkelti į: +Kopijuojama... +Perkeliama... +Pervadinama... +Pasirinkite paskirties aplankÄ…. +Å iam aplankui veiksmas nepalaikomas. +Klaida pervadinant failÄ… ar aplankÄ… +Failų kopijavimo patvirtinimas +Ar jÅ«s esate įsitikinÄ™, jog norite kopijuoti failus į archyvÄ…? +6100 +Patvirtinkite failo Å¡alinimÄ… +Patvirtinkite aplanko Å¡alinimÄ… +Patvirtinkite kelių failų Å¡alinimÄ… +Ar esate įsitikinÄ™, jog norite paÅ¡alinti „{0}“? +Ar esate įsitikinÄ™, jog norite paÅ¡alinti „{0}“ aplankÄ… ir visÄ… jo turinį? +Ar esate įsitikinÄ™, jog norite paÅ¡alinti Å¡iuos {0} elementus? +Å alinama... +Klaida trinant failÄ… ar aplankÄ… +Sistema negali perkelti failo į Å¡iukÅ¡linÄ™, kadangi per ilgas kelias +6300 +Sukurti aplankÄ… +Sukurti failÄ… +Aplanko pavadinimas: +Failo pavadinimas: +Naujas aplankas +Naujas failas +Klaida kuriant aplankÄ… +Klaida kuriant failÄ… +6400 +Komentaras +&Komentaras: +PažymÄ—ti +Panaikinti žymÄ—jimÄ… +Å ablonas: +6600 +Nuostatos +Aplankų istorija +Diagnostiniai praneÅ¡imai +PraneÅ¡imas +7100 +Kompiuteris +Tinklas +Dokumentai +Sistema +7200 +Glaudinti +IÅ¡skleisti +Bandyti +Kopijuoti +Perkelti +Å alinti +Informacija +7300 +Skaidyti failÄ… +&Skaidyti į: +Skaidyti į dalis bai&tais: +Skaidoma... +Skaidymo patvirtinimas +Ar jÅ«s esate įsitikinÄ™, jog norite failÄ… skaidyti į {0} dalis? +Dalies dydis privalo bÅ«ti mažesnis už dalijamo failo dydį +Neteisingas dalies dydis +Nurodytas dalies dydis: {0} baitai.\nAr jÅ«s esate įsitikinÄ™, jog norite archyvÄ… skaidyti į tokias dalis? +7400 +Sujungti failus +&Sujungti į: +Sujungiami failai... +PažymÄ—kite tik pirmÄ… suskaidyto archyvo failÄ… +Nepavyko atpažinti suskaldyto failo +Nepavyko surasti daugiau nei vienos suskaldymo failo dalies +7500 +SkaiÄiuojama kontrolinÄ— suma... +KontrolinÄ—s sumos informacija +Duomenų CRC kontrolinÄ— suma: +Duomenų ir failų pavadinimų CRC kontrolinÄ— suma: +7600 +Spartos bandymas +AtmintinÄ—s naudojimas: +Suglaudinama +IÅ¡skleidžiama +Ä®vertis +Galutinis įvertis +Dabartinis +Galutinis +CPU naudojimas +Ä®vertis/naudojimas +Kartai: +7700 +Nuoroda +Sujungti +Å altinis: +Tikslas: +7710 +Nuorodos tipas +Patvarioji nuoroda +Failo simbolinÄ— nuoroda +Aplanko simbolinÄ— nuoroda +Sujungimo taÅ¡kas (Junction) diff --git a/Utils/7-Zip/Lang/lv.txt b/Utils/7-Zip/Lang/lv.txt new file mode 100644 index 000000000..85e4a1555 --- /dev/null +++ b/Utils/7-Zip/Lang/lv.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.10 : Armands RadzuÅ¡ka +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Latvian +LatvieÅ¡u +401 +&Labi +&Atcelt + + + +&JÄ +&NÄ“ +Aiz&vÄ“rt +&RokasgrÄmata + +&TurpinÄt +440 +JÄ &visiem +NÄ“ v&isiem +Stop +PÄrstartÄ“t +&FonÄ +&PriekÅ¡plÄnÄ +Pa&uze +PauzÄ“ts +Vai piekrÄ«tat pÄrtraukt Å¡o darbÄ«bu? +500 +&Fails +&LaboÅ¡ana +&Izskats +Ie&cienÄ«tÄs +&RÄ«ki +&PalÄ«dzÄ«ba +540 +&AtvÄ“rt +AtvÄ“rt &iekÅ¡pusÄ“ +AtvÄ“rt Ärp&usÄ“ +Ap&skate +&Labot +PÄ&rdÄ“vÄ“t +&KopÄ“t uz... +PÄr&vietot uz... +&DzÄ“st +&SadalÄ«t failu... +Ap&vienot failus... +Īpašī&bas +&PiezÄ«mes + + +Izveidot &mapi +Izveidot &failu +&Beigt +600 +IezÄ«mÄ“t &visu +Atcelt vis&u +I&nvertÄ“t iezÄ«mÄ“jumu +Ie&zÄ«mÄ“t... +&Atcelt... +I&ezÄ«mÄ“t pÄ“c tipa +A&tcelt pÄ“c tipa +700 +&Lielas ikonas +&Mazas ikonas +&Saraksts +SÄ«&kÄk +730 +&Nešķirot + +&2 paneļi +&RÄ«ku joslas +&AtvÄ“rt saknes mapi +LÄ«meni &uz augÅ¡u +Mapju &vÄ“sture... +&PÄrlasÄ«t +750 +ArhÄ«va rÄ«ku josla +Standarta rÄ«ku josla +Lielas pogas +ParÄdÄ«t pogu tekstu +800 +&Pievienot mapi iecienÄ«tajÄm kÄ +IecienÄ«tÄs +900 +&UzstÄdÄ«jumi... +&DarbspÄ“jas pÄrbaude +960 +&RokasgrÄmatas saturs... +&Par 7-Zip... +1003 +Ceļš +Nosaukums +PaplaÅ¡inÄjums +Mape +Lielums +Saspiests +AtribÅ«ti +Izveidots +AtvÄ“rts +PÄrveidots +BlÄ«vs +PiezÄ«mes +Å ifrÄ“ts +DalÄ«ts pirms +DalÄ«ts pÄ“c +VÄrdnÄ«ca +CRC +Veids +Anti +Paņēmiens +SistÄ“ma +Failu sistÄ“ma +LietotÄjs +Grupa +Bloks +PiezÄ«mes +PozÄ«cija + + + + + + + + + + + + + + + + + + + + + + + + + +Kļūda +Tilpums +BrÄ«vÄ vieta +KlÄstera lielums +Nosaukums +LokÄlais nosaukums +Provaiders +2100 +UzstÄdÄ«jumi +Valoda +Valoda: +LaboÅ¡ana +&LaboÅ¡anas programma: + +2200 +SistÄ“ma +PiesaistÄ«t 7-Zip pie: +2301 +&IntegrÄ“t 7-Zip Windows vides kontekstizvÄ“lÄ“ +&KaskÄdveida kontekstizvÄ“le +KontekstizvÄ“les elementi: +2320 + + +AtvÄ“rt arhÄ«vu +Izvilkt failus... +Ielikt arhÄ«vÄ... +PÄrbaudÄ«t arhÄ«vu +Izvilkt Å¡eit +Izvilkt {0} mapÄ“ +Ielikt {0} +Saspiest, sÅ«tÄ«t pa e-pastu... +Saspiest {0}, sÅ«tÄ«t pa e-pastu +2400 +Mapes +&Darba mape +&SistÄ“mas pagaidfailu mape +&TekoÅ¡Ä +&Noteikt: +&Izmantot tikai mainÄmajiem nesÄ“jiem +NorÄdiet vietu arhÄ«vu pagaidfailiem. +2500 +UzstÄdÄ«jumi +ParÄdÄ«t ".." &vienÄ«bu +ParÄdÄ«t Ä«stÄs &failu ikonas +ParÄdÄ«t &sistÄ“mas izvÄ“li +&Pilnas rindas iezÄ«mēšana +ParÄdÄ«t at&daloÅ¡Äs lÄ«nijas + + + +2900 +Par 7-Zip +7-Zip ir bezmaksas programma, tomÄ“r, reÄ£istrÄ“joties jÅ«s varat 7-Zip izstrÄdÄÅ¡anu atbalstÄ«t. +3000 + +Kļūdu nav. +IezÄ«mÄ“ts(i) {0} objekts(i) +Mapi '{0}' neizdevÄs izveidot. +Å is arhÄ«vs neatbalsta pÄrveidoÅ¡anu. + + + + +Fails '{0}' ir pÄrveidots.\nVai vÄ“laties to atjauninÄt arÄ« arhÄ«vÄ? +Failu nav iespÄ“jams uzlabot\n'{0}' +Nav iespÄ“jams palaist laboÅ¡anas programmu. + + + + +Par daudz vienÄ«bu. +3300 +IzvilkÅ¡ana +saspieÅ¡ana +PÄrbaude +AtvÄ“rÅ¡ana... + +3400 +IzvilkÅ¡ana +&Izvilkt uz: +NorÄdiet vietu izvelkamiem failiem. +3410 +Ceļi +Pilni &ceļu nosaukumi +&Bez ceļu nosaukumiem +3420 +PÄrrakstīšana +&JautÄt pirms pÄrrakstīšanas +PÄrra&kstÄ«t bez jautÄÅ¡anas +I&zlaist esoÅ¡os failus +A&utopÄrdÄ“vēšana +AutomÄtiski pÄrdÄ“vÄ“t esoÅ¡os failu +3500 +Apstipriniet faila aizvietoÅ¡anu +MÄ“rÄ·a mape jau satur apstrÄdÄjamo failu. +Vai vÄ“laties aizvietot esoÅ¡o failu +ar Å¡o? +{0} baiti +AutopÄrdÄ“vēšana +3700 +NeatbalstÄ«ts saspieÅ¡anas paņēmiens '{0}' failam. +Datu kļūda '{0}'. Fails ir bojÄts. +CRC kļūda '{0}'. Fails ir bojÄts. + + +3800 +Paroles ievadīšana +Ievadiet paroli: + +Par&ole redzama + + + +&Parole +3900 +PagÄjuÅ¡ais laiks: +AtlikuÅ¡ais laiks: +Lielums: +Ä€trums: + + +Kļūdas: + +4000 +PievienoÅ¡ana arhÄ«vam +Ar&hÄ«vs: +PÄr&veidoÅ¡anas režīms: +ArhÄ«va &formÄts: +&SaspieÅ¡anas lÄ«menis: +&SaspieÅ¡anas paņēmiens: +&VÄrdnÄ«cas lielums: +VÄr&da lielums: + + +Parame&tri: +&UzstÄdÄ«jumi +Izveidot SF&X arhÄ«vu + + + +Å ifrÄ“t failu &nosaukumus +Atmiņa arhivēšanai: +Atmiņa atarhivēšanai: +4050 +UzkrÄÅ¡ana nesaspiežot +Ä€trÄkais +Ä€trais +Parastais +MaksimÄlÄ saspieÅ¡ana +Ultra +4060 +Pievienot un aizvietot failus +AtjauninÄt un pievienot failus +AtjauninÄt esoÅ¡os failus +SinhronizÄ“t failus +4070 +PÄrlÅ«koÅ¡ana +Visi faili + + +6000 +KopÄ“t +PÄrvietot +KopÄ“t uz: +PÄrvietot uz: +Kopēšana... +PÄrvietoÅ¡ana... +PÄrdÄ“vēšana... + +DarbÄ«ba nav atbalstÄ«ta. +Faila vai mapes pÄrdÄ“vēšanas kļūda + + +6100 +Faila dzēšanas apstiprinÄjums +Mapes dzēšanas apstiprinÄjums +VairÄku failu dzēšanas apstiprinÄjums +Vai piekrÄ«tat '{0}' dzēšanai? +Vai piekrÄ«tat mapes '{0}' un visa tÄs satura dzēšanai? +Vai piekrÄ«tat Å¡o {0} vienÄ«bu dzēšanai? +Dzēšana... +Faila vai mapes dzēšanas kļūda + +6300 +Mapes izveidoÅ¡ana +Faila izveidoÅ¡ana +Mapes nosaukums: +Faila nosaukums: +Jauna mape +Jauns fails +Mapes izveidoÅ¡anas kļūda +Faila izveidoÅ¡anas kļūda +6400 +PiezÄ«mes +&PiezÄ«mes: +IezÄ«mēšana +IezÄ«mÄ“juma atcelÅ¡ana +Å ablons: +6600 + +Mapju vÄ“sture +Diagnostikas paziņojumi +Paziņojums +7100 +Dators +TÄ«kls + +SistÄ“ma +7200 +Ievietot +Izvilkt +PÄrbaude +KopÄ“t +PÄrvietot +DzÄ“st +Info +7300 +SadalÄ«t failu +&DalÄ«t uz: +&DalÄ«t sÄ“jumos, baitos: +Dalīšana... + + + + + +7400 +Apvienot failus +&Apvienot uz: +ApvienoÅ¡ana... + + + +7500 + + + + +7600 +DarpspÄ“jÄ«bas pÄrbaude +Atmiņas pielietojums: +Arhivēšana +Atarhivēšana +VÄ“rtÄ“jums +KopÄ“jais vÄ“rtÄ“jums +PatreizÄ“jais +RezultÄ“joÅ¡ais + + +IzdoÅ¡anÄs: diff --git a/Utils/7-Zip/Lang/mk.txt b/Utils/7-Zip/Lang/mk.txt new file mode 100644 index 000000000..f2d7a0b92 --- /dev/null +++ b/Utils/7-Zip/Lang/mk.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.09 : Gabriel Stojanoski +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Macedonian +МакедонÑки +401 +Ок +Откажи + + + +&Да +&Ðе +&Затвори +Помош + +П&родолжи +440 +Да &Сите +Ðе Ð&иедна +Стоп +РеÑтартирај +По&задина +&Ðапред +&Пауза +Паузирано +Дали Ñигурно Ñакате да откажете? +500 +&Датотека +&Уреди +&Изглед +&Омилени +&Ðлатки +&Помош +540 +&Отвори +Отвори &Внатре +Отвори &Ðадвор +&Поглед +&Уреди +&Преименувај +&Копирај Во... +&ПремеÑти Во... +&Избриши +&Подели датотека... +Ком&бинирај датотеки... +&КарактериÑтики +К&оментар + + +Креирај Директориум +Креирај Датотека +&Излези +600 +Селектирај &Се +ДеÑелектирај Се +&Обратна Селекција +Селектирај... +ДеÑелектирај... +Селектирај по Тип +ДеÑелектирај по Тип +700 +&Големи Икони +&Мали Икони +&ЛиÑта +&Детали +730 +Ðеподредени + +&2 Панели +&Ðлатници +Отвори го оÑновниот директориум +Едно Ðиво Ðагоре +ИÑторија на Директориумите... +&Обнови +750 +Ðлатница на Ðрхивата +Стандардна Ðлатница +Големи Копчиња +ТекÑÑ‚ Ðа Копчињата +800 +&Додади го директориумот во Омилени +Забелешки +900 +&Опции... +&ТеÑÑ‚ +960 +&Содржина... +&За 7-Zip... +1003 +Патека +Име +ЕкÑтензија +Директориум +Големина +Пакувана Големина +Ðтрибути +Креирано +ПриÑтапено +Променето +ЦелоÑна +ОбјаÑнета +Енкриптирана +Подели Пред +Подели ПоÑле +Речник +CRC +Тип +Ðнти +Метод +Оперативен СиÑтем +Датотечен СиÑтем +КориÑник +Група +Блок +Коментар +Позиција + + + + + + + + + + + + + + + + + + + + + + + + + +Грешка +Вкупна Големина +Слободен ПроÑтор +Голем на КлаÑтерите +Етикета +Име +Провајдер +2100 +Опции +Јазик +Јазик: +Едитор +&Едитор: + +2200 +СиÑтем +Придружи го 7-Zip Ñо: +2301 +Интегрирај го 7-Zip во додатно мени +КаÑкадно додатно мени +Во додатното мени: +2320 +<Директориум> +<Ðрхива> +Отвори архива +Отпакувај датотеки... +Додади во Ðрхива... +ТеÑтирај архива +Отпакувај овде +Отпакувај во {0} +Додади во {0} +КомпреÑирај и прати на e-mail... +КомпреÑирај во {0} и иÑпрати +2400 +Директориуми +&Работен директориум +&СиÑтемÑки привремен директориум +&Моментален +&Одреди: +Само за преноÑни уреди +Одреди локација за привремените архивÑки датотеки. +2500 +ПодеÑувања +Прикажи ".." предмет +Прикажи виÑтинÑки икони +Прикажи ÑиÑтемÑко мени +&Селектирај &цел ред +Прикажи &мрежа + + + +2900 +За 7-Zip +7-Zip е беÑплатен архивер. Меѓутоа, вие можете да дадете поддршка на понатамошното развивање на 7-Zip Ñо вашето региÑтрирање. +3000 + +Ðема грешки +{0} објект(и) избрано +Ðе е можно креирање на директориумот '{0}' +Оваа операција не е поддржана за овој тип на архива. + + + + +Датотеката '{0}' е модифицирана.\nДали Ñакате да ја обновите во архивата? +Ðе е можно обновување на датотеката\n'{0}' +Грешка при Ñтартување на Едиторот. + + + + +Премногу објекти +3300 +Отпакување +КомпреÑирање +ТеÑтирање +Отворање... + +3400 +Отпакувај +Отпакувај &во: +Одреди локација за отпакуваните датотеки. +3410 +Патеки +ЦелоÑна патека +Без патека +3420 +Презапишување +Прашај пред да презапишеш +Презапиши без прашување +ИзоÑтави поÑтоечки датотеки +ÐвтоматÑки преименувај +ÐвтоматÑки преименувај поÑтоечки датотеки +3500 +Потврди Замена на Датотеки +Конечниот директориум веќе ја Ñодржи датотеката. +Дали Ñакате да ја замените поÑтоечката датотека +Ñо оваа? +{0} бајти +&ÐвтоматÑки преименувај +3700 +Ðеподдржан метод за компреÑија за '{0}'. +Грешка во податоците во '{0}'. Датотеката е оштетена. +CRC грешка во '{0}'. Датотеката е оштетена. + + +3800 +ВнеÑи лозинка +ВнеÑи лозинка: + +&Прикажи ја лозинката + + + +Лозинка +3900 +Поминато време: +ПреоÑтанато време: +Големина: +Брзина: + + +Грешки: + +4000 +Додади во архива +&Ðрхива: +&Додавање: +&Формат на Ðрхива: +&Ðиво на компреÑија: +&Метод на компреÑија: +Големина на &речникот: +Големина на &зборот: + + +&Параметри: +Опции +Креирај SFX ар&хива + + + +&Енкриптирај ги имињата +ИÑкориÑÑ‚ мемор за компреÑ: +ИÑкориÑÑ‚ мемор за декомпреÑ: +4050 +Спакувај +Ðајбрзо +Брзо +Ðормално +МакÑимално +Ултра +4060 +Додади и замени датотеки +Обнови и додади датотеки +Обнови ги поÑтоечките датотеки +Синхронизирај ги датотеките +4070 +Барај +Сите Датотеки + + +6000 +Копирај +ПремеÑти +Копирај Во: +ПремеÑти Во: +Копирање... +ПремеÑтување... +Преименување... + +ПоÑтапката не е поддржана. +Грешка при преименувањето на Датотеката или Директориумот + + +6100 +Потврдете го Бришењето на Датотеката +Потврдете го Бришењето на Директориумот +Потврдете го бришењето на повеќе датотеки +Дали Ñте Ñигурни за бришењето на '{0}'? +Дали Ñте Ñигурни за бришењето на директориумот '{0}' и неговата Ñодржина? +Дали Ñте Ñигурни за бришењето на овие {0} датотеки? +Бришење... +Грешка при бришењето на Датотеката или Директориумот + +6300 +Креирај Директориум +Креирај датотека +Име на Директориумот: +Име на Датотеката: +Ðов Директориум +Ðова датотека +Грешка при креирањето на Директориумот +Грешка при креирањето на датотеката +6400 +Коментар +&Коментар: +Селектирај +ДеÑелектитај +МаÑкирај: +6600 + +ИÑторија на Директориумите +Дијагноза +Порака +7100 +Компјутер +Мрежа + +СиÑтем +7200 +Додади +Отпакувај +ТеÑтирај +Копирај +ПремеÑти +Избриши +Инфо +7300 +Подели датотека +&Подели на: +&Подели на делови, бајти: +Делење... + + + + + +7400 +СоÑтави Датотеки +&СоÑтави на: +СоÑтавување... + + + +7500 + + + + +7600 +ТеÑÑ‚ +ИÑкориÑÑ‚ Меморија: +КомпреÑирање +ДекомпреÑирање +Проценка +Вкупна Проценка +Моментално +Резултат + + +Поминато: diff --git a/Utils/7-Zip/Lang/mn.txt b/Utils/7-Zip/Lang/mn.txt new file mode 100644 index 000000000..9ab889ebf --- /dev/null +++ b/Utils/7-Zip/Lang/mn.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 3.12 : Bayar +; : Bayarsaikhan +; +; +; +; +; +; +; +; +; +0 +7-Zip +Mongolian +Монгол Ñ…Ñл +401 +За +Болих + + + +&Тийм +&Үгүй +&Хаах +ТуÑламж + +&ҮргÑлжлүүл +440 +Бүг&д тийм +Бүгд Ò¯&гүй +Ð—Ð¾Ð³Ñ +Дахин ачаалла +&ÐÑ€ талд +&Өмнө тал +&Түр Ð·Ð¾Ð³Ñ +Түр зогÑлоо +Та үнÑÑ…ÑÑÑ€ болих гÑж байна уу? +500 +&Файл +&ЗаÑах +&ҮзүүлÑÑ… +Д&уртай зүйл +&Ð¥ÑÑ€ÑгÑлүүд +&ТуÑламж +540 +&ÐÑÑÑ… +Дотор &нÑÑÑ… +Гадна &нÑÑÑ… +&Харах +&ЗаÑварлах +ÐÑÑ€ Ñ&олих +...Ñ€Ò¯Ò¯ &Хуулах... +...руу &Зөөх... +&УÑтгах +&Файл хуваах... +Файлуудыг нÑгтгÑÑ…... +&МÑдÑÑлÑл үзÑÑ… +Тай&лбар + + +Ð¥Ð°Ð²Ñ‚Ð°Ñ Ò¯Ò¯ÑгÑÑ… +Файл Ò¯Ò¯ÑгÑÑ… +Га&рах +600 +Бүгдийг Ñо&нгох +Сонгохоо болих +&Сонголтоо Ñргүүл +Сонгох... +Сонгохгүй... +Төрлөөр нь Ñонгох +Төрлөөр нь Ñонгохгүй +700 +Том& дүрÑүүд +Жи&жиг дүрÑүүд +&ЖагÑаалт +&ДÑлгÑÑ€Ñнгүй +730 +Ðнгилаагүй + +&2 Ñамбарууд +&Багажны Ñамбар +Гол хавтÑыг нÑÑÑ… +ÐÑг төвшин дÑÑш +ХавтаÑны түүх... +&СÑргÑÑ +750 +Aрхив багажны Ñамбар +Стандарт багажны Ñамбар +Том товчлуурууд +Товчлуурын текÑтийг үзүүл +800 +&ХавтÑыг дуртайдаа нÑмÑÑ… Ò¯Ò¯ +Хавчуурга +900 +&Сонголтууд... +&БÑнчмарк +960 +&Ðгуулгууд... +&7-Зип-ийн тухай... +1003 +Зам +ÐÑÑ€ +Өргөтгөл +Ð¥Ð°Ð²Ñ‚Ð°Ñ +Ð¥ÑмжÑÑ +БагцалÑан Ñ…ÑмжÑÑ +Чанар +Ò®Ò¯ÑгÑÑÑн +ХандÑан +ӨөрчилÑөн +Баталгаатай +Тайлбар хийгдÑÑн +ÐууцлагдÑан +Өмнө хувааÑан +Дараа хувааÑан +Толь бичиг +CRC +Төрөл +ЭÑÑ€Ñг +Ðрга +ХоÑÑ‚ ҮС +Файл ÑиÑтем +Ð¥ÑÑ€ÑглÑгч +БүлÑг +Түгжих +Тайлбар +Байрлал + + + + + + + + + + + + + + + + + + + + + + + + + +Ðлдаа +Ðийт Ñ…ÑмжÑÑ +ХооÑон зай +КлаÑтерын зай +ÐÑÑ€ +Локаль ÐÑÑ€ +Хангагч +2100 +Сонголтууд +Ð¥Ñл +Ð¥Ñл: +ЗаÑварлагч +&ЗаÑварлагч: + +2200 +СиÑтем +7-Зипийг дараахтай нÑгтгÑÑ…: +2301 +7-Зип-ийг шелл контекÑÑ‚ цÑÑÑ‚Ñй нийлүүлÑÑ… +КонтекÑÑ‚ цÑÑийг цувуулан харуул +КонтекÑÑ‚ цÑÑийн төрлүүд: +2320 +<ХавтаÑ> + +Ðрхив нÑÑÑ… +Файлуудыг задал... +Ðрхивт нÑм... +Ðрхивийн шалгалт +Энд задал +{0} руу задал +{0} Ñ€Ò¯Ò¯ нÑм +Шахах ба Шуудандах... +{0} Ñ€Ò¯Ò¯ шахах ба Шууданд +2400 +ХавтÑууд +&Ðжлын Ñ…Ð°Ð²Ñ‚Ð°Ñ +&СиÑтем\завÑрын Ñ…Ð°Ð²Ñ‚Ð°Ñ +&Одоогийн +&ТодорхойлÑон: +Зөвхөн зөөврийн төхөөрөмж Ñ…ÑÑ€ÑглÑÑ… +ЗавÑрын архив файл Ò¯Ò¯ÑгÑÑ… байрлалыг тодорхойл. +2500 +Тохируулгууд +Зүйл ".." үзүүлÑÑ… +ЖинхÑÐ½Ñ Ñ„Ð°Ð¹Ð» дүрÑийг харуулах +СиÑтемийн цÑÑ Ñ…Ð°Ñ€ÑƒÑƒÐ» +&БүтÑн мөр Ñонгох +&Мөрийн дундах Ð·ÑƒÑ€Ð°Ð°Ñ Ñ…Ð°Ñ€ÑƒÑƒÐ»Ð°Ñ… + +&СайжруулÑан Сонгох горим +&Том Ñанах ойн Ñ…ÑƒÑƒÐ´Ð°Ñ Ñ…ÑÑ€ÑглÑÑ… +2900 +7-Zip-ийн тухай +7-Zip бол үнÑгүй програм. ГÑлÑÑ Ñ‡ та 7-Зип-д бүртгүүлÑн хөгжүүлÑгчдийг дÑмжиж болно. БүртгүүлÑÑн Ñ…ÑÑ€ÑглÑгчид техникийн дÑмжлÑг авах болно. +3000 + +Ðлдаа алга байна +{0} зүйл(үүд) ÑонгогдÑон байна +Ð¥Ð°Ð²Ñ‚Ð°Ñ Ò¯Ò¯ÑгÑж чадахгүй '{0}' +ДÑмжигдÑÑгүй архивийг задлах шинÑчлÑлт хийх. + + + + +Файл '{0}' өөрчлөгдлөө.\nТа архивт шинÑчлÑлт хиймÑÑÑ€ байна уу? +Файлыг шинÑчилж чадÑангүй\n'{0} +ЗаÑварлагчийг нÑÑж чадÑангүй. + + + + +ДÑндүү их юм байна +3300 +Задалж байна... +Шахаж байна +Шалгаж байна +ÐÑÑж байна... + +3400 +Задал +З&адлах газар: +ЗадалÑан файлуудын байрлалыг тодорхойл. +3410 +Замын горим +БүтÑн замын нÑÑ€ +Параметр алга +3420 +Давхарлаж бичих горим +Давхарлаж бичхÑÑÑÑÑ Ó©Ð¼Ð½Ó© аÑуух +ÐÑуултгүй давхарлаж бичих +Байгаа файлыг алгаÑах +Ðвтоматаар нÑÑ€ Ñолих +Байвал нÑрийг нь Ñолих +3500 +Файл дахин байрлуулахыг батлах +Ð¥Ð°Ð²Ñ‚Ð°Ñ Ñ„Ð°Ð¹Ð»Ñ‹Ð³ агуулÑан байна. +Байгаа файлуудыг дахин байрлуулах уу +ЭнүүнтÑй юу? +{0} битүүд +A&втомат нÑÑ€ Ñолих +3700 +Дараах файлд шахалтын арга дÑмжигдÑÑгүй байна '{0}'. +'{0}'өгөгдлийн алдаа. Файл ÑвдÑÑ€ÑÑн байна. +'{0}'CRC бүтÑÑнгүй. Файл ÑвдÑÑ€ÑÑн байна. + + +3800 +Ðууц үгÑÑ Ð¾Ñ€ÑƒÑƒÐ»Ð°Ñ… +Ðууц үгÑÑ Ð¾Ñ€ÑƒÑƒÐ»Ð°Ñ…: + +&Ðууц үг харуулах + + + +Ðууц үг +3900 +ӨнгөрÑөн хугацаа: +ҮлдÑÑн хугацаа: +Ð¥ÑмжÑÑ: +Хурд: + + +Ðлдаанууд: + +4000 +Ðрхивт нÑм +&Aрхив: +&ШинÑчлÑÑ… горим: +Ðрхив &өргөтгөл: +Шахалтын &төвшин: +Шахах &арга: +&Толь бичгийн Ñ…ÑмжÑÑ: +&Үгийн Ñ…ÑмжÑÑ: + + +&Параметерүүд: +Сонголтууд +SF&X архив Ò¯Ò¯ÑгÑÑ… + + + +&Файлын нÑрүүдийг нууцал +Шахаж байгаа Ñанах ойн Ñ…ÑÑ€ÑглÑÑ: +Задалж байгаа Ñанах ойн Ñ…ÑÑ€ÑглÑÑ: +4050 +Хадгалах +ДÑÑд хурданаар +Хурдан +Энгийн +ДÑÑд Ñ…ÑмжÑÑгÑÑÑ€ +Ултра +4060 +ÐÑмÑÑ… ба дахин байрлуулах +ШинÑчлÑÑ… ба дахин байрлуулах +Байгаа файлуудыг дахин унших +Файлуудыг тааруулах +4070 +ÐÑгжих +Бүх файлууд + + +6000 +Хуулах +Зөөх +хуулахдаа: +Зөөхдөө: +Хуулж байна... +Зөөж байна... +ÐÑÑ€ Ñолиж байна... + +ҮйлдÑл дÑмжигдÑÑгүй байна. +Файл болон хавтÑын нÑрийг Ñолиход алдаатай +Файл хуулахыг магадлах +Та үнÑÑ…ÑÑÑ€ файлуудыг архив руу хуулахыг Ñ…Ò¯ÑÑж байна уу +6100 +Файл уÑтгахыг батал +Ð¥Ð°Ð²Ñ‚Ð°Ñ ÑƒÑтгахыг батал +Олон файл уÑтгахыг батал +Та'{0}'-ийг уÑтгах гÑж байна уу? +'{0}' Ñ…Ð°Ð²Ñ‚Ð°Ñ Ð±Ð¾Ð»Ð¾Ð½ бүх агуулгыг уÑтгах гÑж байна уу? +{0} зүйлүүдийг уÑтгах гÑж байна уу? +УÑтгаж байна... +Файл болон хавтÑыг уÑтгахад алдаатай + +6300 +Ð¥Ð°Ð²Ñ‚Ð°Ñ Ò¯Ò¯ÑгÑÑ… +Файл Ò¯Ò¯ÑгÑÑ… +Ð¥Ð°Ð²Ñ‚Ð°Ñ Ð½ÑÑ€: +Файл нÑÑ€: +Ð¨Ð¸Ð½Ñ Ñ…Ð°Ð²Ñ‚Ð°Ñ +Ð¨Ð¸Ð½Ñ Ñ„Ð°Ð¹Ð» +Ð¥Ð°Ð²Ñ‚Ð°Ñ Ò¯Ò¯ÑгÑÑ…Ñд алдаа гарав +Файл Ò¯Ò¯ÑгÑÑ…Ñд алдаа гарав +6400 +Тайлбар +&Тайлбар: +Сонгох +Сонгохгүй +MaÑк: +6600 + +ХавтÑын түүх +ШинжилгÑÑний Ð·ÑƒÑ€Ð²Ð°Ñ +Ð—ÑƒÑ€Ð²Ð°Ñ +7100 +Компьютер +СүлжÑÑ + +СиÑтем +7200 +ÐÑм +Задал +Шалгах +Хуулах +Зөөх +УÑтгах +Шинж... +7300 +Хуваах файл: +&Файл хуваах газар: +ЭзлÑхүүн Ñ€Ò¯Ò¯ хуваах, битүүд: +Хувааж байна... + + + + + +7400 +ÐÑгтгÑÑ… файлууд: +&ÐÑгтгÑÑ… газар: +ÐÑгтгÑж байна... + + + +7500 + + + + +7600 +БÑнчмарк +Санах ойн Ñ…ÑÑ€ÑглÑÑ: +Шахаж байна +Задалж байна +ҮнÑлж байна +Ðийт үнÑлгÑÑ +Одоогийн +Хариуг гаргаж байна + + +ӨнгөрÑөн: diff --git a/Utils/7-Zip/Lang/mng.txt b/Utils/7-Zip/Lang/mng.txt new file mode 100644 index 000000000..f0c0859df --- /dev/null +++ b/Utils/7-Zip/Lang/mng.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 7-Zip 9.20 +; Saqirilatu Mongolqileb +; QQ:136087084 Email:saqirilatu@126.com +; Mongol soft QQ bulug â… : 39338772 â…¡:38803882 +; Toli Mongolian IME +; http://hi.baidu.com/saqirilatuu/item/9438213716f316ebe7bb7a8d +;last updated: 2014-1-1 +; +; +; +; +0 +7-Zip +Mongolian (Unicode) +ᠮᠤᠩᠭᠤᠯ ᠬᠡᠯᠡ +401 +ᠲᠡᠭᠡ +ᠤᠰᠠᠳᠬᠠᠬᠤ + + + +ᠲᠡᠭᠡ (&Y) +ᠪᠣᠯᠢ (&N) +ᠬᠠᠭᠠᠬᠤ (&C) +ᠬᠠᠪᠰᠤᠷᠤᠮᠵᠢ + +ᠵᠠᠯᠭᠠᠭᠠᠳ (&C) +440 +ᠪᠦᠭᠦᠳᠡ ᠲᠡᠭᠡ (&A) +ᠪᠦᠭᠦᠳᠡ ᠪᠣᠯᠢ (&L) +ᠵᠣᠭᠰᠣᠭᠠᠬᠤ +ᠳᠠᠬᠢᠨ ᠡᠬᠢᠯᠡᠬᠦ +á  á ·á ¤ ᠲᠠᠯ᠎ᠠ (&B) +ᠡᠮᠦᠨ᠎ᠡ ᠲᠠᠯ᠎ᠠ (&F) +ᠵᠣᠭᠰᠣᠭᠠᠬᠤ (&P) +ᠨᠢᠭᠡᠨᠲᠡ ᠵᠣᠭᠰᠣᠪᠠ +ᠲᠠ ᠦᠨᠡᠬᠡᠷ ᠤᠰᠠᠳᠬᠠᠬᠤ ᠦᠦ ︖ +500 +ᠹᠠᠶᠢᠯ (&F) +ᠨᠠᠶᠢᠷᠠᠭᠤᠯᠬᠤ (&E) +ᠦᠵᠡᠬᠦ (&V) +ᠬᠠᠳᠠᠭᠠᠯᠠᠬᠤ (&A) +ᠪᠠᠭᠠᠵᠢ (&T) +ᠬᠠᠪᠰᠤᠷᠤᠮᠵᠢ (&H) +540 +ᠨᠡᠭᠡᠭᠡᠬᠦ (&O) +ᠣᠳᠣᠬᠢ ᠴᠣᠩᠬᠣ  ᠶᠢ ᠨᠡᠭᠡᠭᠡᠬᠦ (&I) +ᠰᠢᠨ᠎ᠡ ᠴᠣᠩᠬᠣ ᠪᠠᠶᠢᠭᠤᠯᠤᠭᠠᠳ ᠨᠡᠭᠡᠭᠡᠬᠦ (&U) +ᠵᠢᠷᠤᠭ ᠦᠵᠡᠬᠦ (&V) +ᠨᠠᠶᠢᠷᠠᠭᠤᠯᠬᠤ (&E) +ᠲᠠᠬᠢᠨ ᠨᠡᠷᠡᠯᠡᠬᠦ (&M) +ᠬᠣᠣᠰᠯᠠᠬᠤ (&C) +ᠰᠢᠯᠵᠢᠭᠦᠯᠬᠦ (&M) +ᠤᠰᠠᠳᠬᠠᠬᠤ (&D) +ᠹᠠᠶᠢᠯ ᠬᠤᠪᠢᠶᠠᠬᠤ (&S)... +ᠹᠠᠶᠢᠯ  ᠢ ᠨᠡᠶᠢᠯᠡᠭᠦᠯᠬᠦ (&B)... +ᠰᠢᠨᠵᠢ ᠴᠢᠨᠠᠷ (&R) +ᠲᠠᠶᠢᠯᠪᠤᠷᠢ (&N) +ᠹᠠᠶᠢᠯ ᠰᠢᠯᠭᠠᠬᠤ +ᠹᠠᠶᠢᠯ ᠠᠳᠠᠯᠢᠳᠬᠠᠬᠤ +ᠰᠢᠨ᠎ᠡ ᠬᠠᠪᠲᠠᠰᠤ ᠪᠠᠶᠢᠭᠤᠯᠬᠤ +ᠹᠠᠶᠢᠯ ᠪᠠᠶᠢᠭᠤᠯᠬᠤ +ᠭᠠᠷᠬᠤ (&X) +600 +ᠪᠦᠬᠦᠨ  ᠢ ᠰᠣᠩᠭᠣᠬᠤ (&A) +ᠪᠦᠬᠦᠨ  ᠢ ᠤᠰᠠᠳᠬᠠᠬᠤ +á ¡á °á ¡á ·á ­á ¦ ᠰᠣᠩᠭᠣᠬᠤ (&I) +ᠰᠣᠩᠭᠣᠬᠤ ... +ᠤᠰᠠᠳᠬᠠᠬᠤ ᠪᠠᠨ ᠰᠣᠩᠭᠣᠬᠤ ... +ᠠᠳᠠᠯᠢ ᠬᠡᠯᠪᠡᠷᠢ  ᠶᠢᠨ ᠹᠠᠶᠢᠯ  ᠢ ᠰᠣᠩᠭᠣᠬᠤ +ᠠᠳᠠᠯᠢ ᠬᠡᠯᠪᠡᠷᠢ  ᠶᠢᠨ ᠹᠠᠶᠢᠯ  ᠢ ᠤᠰᠠᠳᠬᠠᠬᠤ +700 +ᠶᠡᠬᠡ ᠵᠢᠷᠤᠭ (&G) +ᠪᠠᠭ᠎ᠠ ᠵᠢᠷᠤᠭ (&M) +ᠵᠢᠭᠰᠠᠭᠠᠬᠤ (&L) +ᠨᠠᠷᠢᠨ ᠠᠭᠤᠯᠭ᠎ᠠ (&D) +730 +ᠮᠥᠷᠯᠡᠬᠦ ᠦᠭᠡᠢ +ᠲᠡᠭᠰᠢ ᠬᠡᠪ +ᠬᠣᠣᠰ ᠨᠢᠭᠤᠷ (&2) +ᠪᠠᠭᠠᠵᠢ  ᠶᠢᠨ ᠰᠠᠮᠪᠠᠷ᠎ᠠ (&T) +ᠦᠨᠳᠦᠰᠦ ᠬᠠᠪᠲᠠᠰᠤ ᠨᠡᠭᠡᠭᠡᠬᠦ +ᠳᠡᠭᠡᠭᠰᠢ +ᠬᠠᠪᠲᠠᠰᠤᠨ â€á £ ᠲᠡᠦᠬᠡ ... +ᠰᠢᠨᠡᠳᠬᠡᠬᠦ (&R) +750 +ᠳᠠᠩᠰᠠᠨ â€á £ ᠪᠠᠭᠠᠵᠢ  ᠶᠢᠨ ᠰᠠᠮᠪᠠᠷ᠎ᠠ +ᠪᠠᠷᠢᠮᠵᠢᠶ᠎ᠠ ᠪᠠᠭᠠᠵᠢ  ᠶᠢᠨ ᠰᠠᠮᠪᠠᠷ᠎ᠠ +ᠶᠡᠬᠡ ᠳᠠᠷᠤᠪᠴᠢ +ᠳᠠᠷᠤᠪᠴᠢ  ᠶᠢᠨ ᠦᠰᠦᠭ ᠢᠯᠡᠷᠡᠬᠦ +800 +ᠬᠠᠳᠠᠭᠠᠯᠠᠮᠵᠢ ᠲᠠᠭᠠᠨ ᠨᠡᠮᠡᠬᠦ (&A) +ᠱᠣᠰᠢᠭ᠎ᠠ +900 +ᠰᠣᠩᠭᠣᠯᠲᠠ (&O) +ᠥᠭᠭᠦᠭᠳᠡᠯ ᠰᠢᠯᠭᠠᠯᠲᠠ (&B) +960 +ᠬᠠᠪᠰᠤᠷᠤᠮᠵᠢ ᠦᠵᠡᠬᠦ (&C) +7-Zip â€á £ ᠲᠤᠬᠠᠢ (&A) +1003 +ᠵᠠᠮ +ᠨᠡᠷᠡᠶᠢᠳᠦᠯ +ᠥᠷᠭᠡᠳᠭᠡᠭᠰᠡᠨ ᠨᠡᠷ᠎ᠡ +ᠬᠠᠪᠲᠠᠰᠤ +ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ +ᠠᠪᠴᠢᠶᠠᠭᠰᠠᠨ â€á £ ᠳᠠᠷᠠᠭᠠᠬᠢ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ +ᠰᠢᠨᠵᠢ ᠴᠢᠨᠠᠷ +ᠪᠠᠶᠢᠭᠤᠯᠤᠭᠰᠠᠨ á ´á  á ­ +ᠦᠵᠡᠭᠰᠡᠨ á ´á  á ­ +ᠵᠠᠰᠠᠭᠰᠠᠨ á ´á  á ­ +ᠬᠠᠳᠠᠭᠤᠷ +ᠲᠠᠶᠢᠯᠪᠤᠷᠢ +ᠪᠠᠲᠤᠵᠢᠭᠤᠯᠬᠤ +ᠡᠮᠦᠨ᠎ᠡ ᠨᠢ ᠬᠤᠪᠢᠶᠠᠬᠤ +ᠰᠡᠭᠦᠯᠡᠷ ᠨᠢ ᠬᠤᠪᠢᠶᠠᠬᠤ +ᠲᠣᠯᠢ ᠪᠢᠴᠢᠭ  ᠦᠨ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ +CRC +ᠲᠥᠷᠥᠯ +ᠠᠷᠢᠯᠭᠠᠬᠤ ᠵᠦᠢᠯ +ᠲᠣᠭᠠᠴᠠᠬᠤ á  á ·á ­á Žá   +ᠭᠣᠣᠯ ᠠᠵᠢᠯᠯᠠᠬᠤ ᠰᠢᠰᠲ᠋ᠧᠮ +ᠹᠠᠶᠢᠯ  ᠤᠨ ᠰᠢᠰᠲ᠋ᠧᠮ +ᠬᠡᠷᠡᠭᠯᠡᠭᠴᠢ +ᠪᠦᠯᠦᠭ +ᠥᠰᠦᠭ  ᠦᠨ ᠬᠡᠰᠡᠭ +ᠲᠠᠶᠢᠯᠪᠤᠷᠢ +ᠣᠷᠣᠨ ᠲᠣᠭᠲᠠᠭᠠᠬᠤ +ᠵᠠᠮ  ᠤᠨ ᠡᠮᠦᠨ᠎ᠡ ᠬᠡᠰᠡᠭ +ᠬᠠᠪᠲᠠᠰᠤ +ᠹᠠᠶᠢᠯ +ᠬᠡᠪ +ᠡᠪᠬᠡᠮᠡᠯ +ᠣᠯᠠᠨ ᠡᠪᠬᠡᠮᠡᠯ ᠠᠪᠴᠢᠭᠤᠯᠬᠤ +ᠬᠠᠵᠠᠭᠠᠢ ᠰᠢᠯᠵᠢᠭᠦᠯᠬᠦ +ᠵᠠᠯᠭᠠᠬᠤ +ᠥᠰᠦᠭ  ᠦᠨ ᠬᠡᠰᠡᠭ +ᠡᠪᠬᠡᠮᠡᠯ ᠬᠤᠪᠢᠶᠠᠬᠤ + +64 ᠣᠷᠣᠨ +ᠶᠡᠬᠡ ᠦᠰᠦᠭ  ᠦᠨ ᠬᠡᠰᠡᠭ  ᠦᠨ ᠳᠠᠷᠠᠭᠠᠯᠠᠯ +CPU +ᠹᠢᠽᠢᠺ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ +ᠹᠠᠶᠢᠯ  ᠤᠨ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ +ᠨᠡᠶᠢᠯᠡᠪᠦᠷᠢ  ᠶᠢ ᠰᠢᠯᠭᠠᠬᠤ +ᠣᠨᠴᠠᠯᠢᠭ +ᠬᠡᠶᠢᠰᠪᠦᠷᠢ ᠬᠠᠶ᠋ᠢᠭ +ID +ᠵᠢᠭᠠᠬᠠᠨ ᠹᠠᠶᠢᠯ  ᠤᠨ ᠨᠡᠷ᠎ᠡ +ᠫᠷᠦᠭᠷᠡᠮ ᠪᠠᠶᠢᠭᠤᠯᠬᠤ +ᠳᠡᠪᠢᠭᠦᠷ  ᠦᠨ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ +ᠬᠡᠪ ᠵᠠᠭᠪᠤᠷ +ᠵᠠᠯᠭᠠᠬᠤ +ᠪᠤᠷᠤᠭᠤ +ᠶᠡᠷᠤᠩᠬᠡᠢ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ +ᠬᠡᠷᠡᠭᠯᠡᠭᠰᠡᠨ ᠣᠷᠣᠨ ᠵᠠᠢ +ᠪᠠᠭᠴᠠ  ᠶᠢᠨ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ +ᠡᠪᠬᠡᠮᠡᠯ  ᠦᠨ ᠲᠡᠮᠳᠡᠭ +ᠲᠤᠰ ᠭᠠᠵᠠᠷ  ᠤᠨ ᠨᠡᠷᠡᠶᠢᠳᠦᠯ +ᠬᠠᠩᠭᠠᠭᠴᠢ +2100 +ᠰᠣᠩᠭᠣᠯᠲᠠ +ᠬᠡᠯᠡ +ᠬᠡᠯᠡ ᠰᠣᠩᠭᠣᠬᠤ᠄ +ᠨᠠᠶᠢᠷᠠᠭᠤᠯᠤᠭᠴᠢ +ᠲᠣᠭᠲᠠᠭᠰᠠᠨ ᠨᠠᠶᠢᠷᠠᠭᠤᠯᠤᠭᠴᠢ (&E): +ᠲᠣᠭᠲᠠᠭᠰᠠᠨ ᠹᠠᠶᠢᠯ  ᠢ ᠠᠳᠠᠯᠢᠳᠬᠠᠬᠤ (&D): +2200 +ᠰᠢᠰᠲ᠋ᠧᠮ +7-Zip ᠪᠠᠷ ᠹᠠᠶᠢᠯ  ᠤᠨ ᠲᠥᠷᠥᠯ  ᠢ ᠬᠣᠯᠪᠣᠭᠳᠠᠭᠤᠯᠬᠤ á „ +2301 +7-Zip á ¶á ‹á ¢ ᠪᠠᠷᠠᠭᠤᠨ ᠳᠠᠷᠤᠪᠴᠢ ᠳ᠋ᠥ ᠨᠡᠮᠡᠬᠦ +ᠪᠠᠷᠠᠭᠤᠨ ᠳᠠᠷᠤᠪᠴᠢ ᠳ᠋ᠥ ᠳᠠᠪᠬᠤᠴᠠᠭᠤᠯᠬᠤ +ᠪᠠᠷᠠᠭᠤᠨ ᠳᠠᠷᠤᠪᠴᠢ ᠳ᠋ᠥ ᠢᠯᠡᠷᠡᠬᠦ ᠺᠣᠳ᠋  ᠢ ᠰᠣᠩᠭᠣᠬᠤ +2320 +<ᠬᠠᠪᠲᠠᠰᠤ > +< ᠡᠪᠬᠡᠮᠡᠯ > +ᠠᠪᠴᠢᠮᠠᠯ  ᠢ ᠨᠡᠭᠡᠭᠡᠬᠦ +ᠮᠠᠲ᠋ᠧᠷᠢᠶᠠᠯ  ᠢ á ­á  á ·á ­á  á ¨ ᠠᠪᠬᠤ ... +ᠠᠪᠴᠢᠮᠠᠯ ᠳ᠋ᠥ ᠨᠡᠮᠡᠬᠦ ... +ᠠᠪᠴᠢᠮᠠᠯ  ᠢ ᠰᠢᠯᠭᠠᠬᠤ +ᠳᠣᠣᠷᠠᠬᠢ á ­á  á ·á ´á  á ­ ᠳ᠋ᠥ á ­á  á ·á ­á  á ¨ ᠠᠪᠬᠤ +{0}ᠤᠷᠤᠭᠤ ᠵᠠᠳᠠᠯᠬᠤ +{0} ᠳ᠋ᠥ ᠨᠡᠮᠡᠬᠦ +ᠠᠪᠴᠢᠶᠠᠭᠠᠳ ᠢᠮᠸᠯ ᠶᠠᠪᠤᠭᠤᠯᠬᠤ ... +ᠠᠪᠴᠢᠶᠠᠭᠠᠳ {0}á ¶á ‹á ¢ ᠢᠯᠡᠭᠡᠬᠦ +2400 +ᠬᠠᠪᠲᠠᠰᠤ +ᠠᠵᠢᠯ  ᠤᠨ ᠬᠠᠪᠲᠠᠰᠤ (&W) +ᠰᠢᠰᠲ᠋ᠧᠮ \ ᠵᠠᠰᠠᠪᠤᠷᠢ  ᠶᠢᠨ ᠬᠠᠪᠲᠠᠰᠤ (&S) +ᠣᠳᠣ  ᠶᠢᠨ ᠬᠠᠪᠲᠠᠰᠤ (&C) +ᠲᠣᠭᠲᠠᠮᠠᠯ ᠪᠠᠶᠢᠷᠢ (&S) +ᠵᠥᠪᠬᠡᠨ ᠵᠥᠭᠡᠭᠡᠪᠦᠷᠢ  ᠶᠢᠨ ᠲᠥᠬᠥᠭᠡᠷᠦᠮᠵᠢ ᠳ᠋ᠥ ᠬᠡᠷᠡᠭᠯᠡᠬᠦ +ᠲᠦᠷ á ´á  á ­  ᠤᠨ ᠠᠪᠴᠢᠮᠠᠯ ᠹᠠᠶᠢᠯ ᠡᠭᠦᠰᠬᠡᠬᠦ ᠪᠠᠶᠢᠷᠢᠯᠠᠯ  ᠢ ᠲᠣᠳᠣᠷᠬᠠᠶᠢᠯᠠᠯ +2500 +ᠢᠯᠡᠷᠡᠬᠦ +ᠢᠯᠡᠷᠡᠬᠦ “..†ᠵᠦᠢᠯ ( ᠳᠡᠭᠡᠭᠰᠢ ᠬᠣᠣᠰ ᠲᠣᠪᠴᠢᠳᠠᠭᠠᠳ ) +ᠦᠨᠡᠨ ᠵᠢᠷᠤᠭ ᠨᠢ ᠢᠯᠡᠷᠡᠬᠦ +ᠰᠢᠰᠲ᠋ᠧᠮ  ᠦᠨ ᠲᠣᠪᠶᠣᠭ ᠨᠢ ᠢᠯᠡᠷᠡᠬᠦ +ᠪᠦᠬᠦ ᠮᠥᠷ  ᠢ ᠰᠣᠩᠭᠣᠬᠤ (&F) +ᠰᠦᠯᠵᠢᠶᠡᠨ ᠤᠲᠠᠰᠤ ᠢᠯᠡᠷᠡᠬᠦ (&G) +ᠲᠣᠪᠴᠢᠳᠠᠭᠠᠳ ᠨᠡᠭᠡᠭᠡᠬᠦ +7-Zip ᠤᠯᠠᠮᠵᠢᠯᠠᠯᠲᠤ ᠬᠡᠪ  ᠢ ᠰᠣᠩᠭᠣᠬᠤ (&A) +ᠶᠡᠬᠡ á ·á  á ®  ᠤᠨ ᠨᠢᠭᠤᠷ  ᠢ ᠬᠡᠷᠡᠭᠯᠡᠬᠦ (&L) +2900 +7-Zip  ᠶᠢᠨ ᠲᠤᠬᠠᠢ +7-Zip ᠪᠣᠯ ᠲᠥᠯᠥᠪᠦᠷᠢ ᠦᠭᠡᠢ ᠰᠣᠹᠲ á ‚ ᠲᠠ ᠬᠠᠨᠳᠢᠪ  ᠤᠨ ᠬᠡᠯᠪᠡᠷᠢ ᠪᠠᠷ 7 -zip á ¶á ‹á ¢ ᠳᠡᠮᠵᠢᠵᠦ ᠪᠣᠯᠣᠨ᠎ᠠ á ‚ ᠮᠣᠩᠭᠣᠯ ᠰᠣᠹᠲ  ᠤᠨ ᠪᠦᠯᠦᠭ ᠨᠢᠭᠡ 39338772 ᠬᠣᠶᠠᠷ 38803882 ᠵᠢᠨ ᠰᠠᠴᠤᠷᠠᠯᠲᠤ ᠮᠣᠩᠭᠣᠯᠴᠢᠯᠠᠪᠠ á ‚ QQ 136087084 Email: saqirilatu@126.com +3000 +ᠰᠢᠰᠲ᠋ᠧᠮ á ·á  á ®  ᠢ ᠨᠢ ᠬᠤᠪᠢᠶᠠᠵᠤ ᠳᠡᠶᠢᠯᠬᠦ ᠦᠭᠡᠢ +ᠪᠤᠷᠤᠭᠤ ᠦᠭᠡᠢ +{0}  ᠢ ᠰᠣᠩᠭᠣᠬᠤ +{0}†ᠬᠠᠪᠲᠠᠰᠤ ᠪᠠᠶᠢᠭᠤᠯᠵᠤ ᠳᠡᠶᠢᠯᠬᠦ ᠦᠭᠡᠢ +ᠲᠣᠰ ᠠᠪᠴᠢᠮᠠᠯ  ᠤᠨ ᠰᠢᠨᠡᠳᠬᠡᠬᠦ  ᠶᠢ ᠳᠡᠮᠵᠢᠬᠦ ᠦᠭᠡᠢ +ᠠᠪᠴᠢᠮᠠᠯ ᠨᠡᠭᠡᠭᠡᠵᠦ ᠳᠡᠶᠢᠯᠬᠦ ᠦᠭᠡᠢ {0}†+ᠪᠠᠲᠤ ᠠᠪᠴᠢᠮᠠᠯ ᠨᠡᠭᠡᠭᠡᠵᠦ ᠳᠡᠶᠢᠯᠬᠦ ᠦᠭᠡᠢ “{0}†ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠨᠢ ᠪᠤᠷᠤᠭᠤ +ᠳᠡᠮᠵᠢᠬᠦ ᠦᠭᠡᠢ ᠠᠪᠴᠢᠮᠠᠯ  ᠤᠨ ᠬᠡᠯᠪᠡᠷᠢ +{0} ᠹᠠᠶᠢᠯ ᠨᠢᠭᠡᠨᠲᠡ ᠪᠠᠶᠢᠨ᠎ᠠ +ᠹᠠᠶᠢᠯ“{0}â€á ¥á ­á ¡á ·á ¡á ´á ¢á ¯á ¡á ­á ³á ¡á ªá ¡ \nᠲᠠ ᠠᠪᠴᠢᠮᠠᠯ  ᠳᠠᠬᠢ ᠹᠠᠶᠢᠯ  ᠢᠶᠠᠨ ᠰᠢᠨᠡᠳᠬᠡᠬᠦ ᠦᠦ ? +ᠹᠠᠶᠢᠯ  ᠢ ᠰᠢᠨᠡᠳᠬᠡᠵᠦ ᠳᠡᠶᠢᠯᠦᠭᠰᠡᠨ ᠦᠭᠡᠢ \n“{0}â€á ²á ¤á ° ᠠᠪᠴᠢᠮᠠᠯ ᠪᠤᠷᠤᠭᠤ á ­á  á ·á ´á  á ¢ +ᠭᠠᠳᠠᠨᠠᠬᠢ ᠨᠠᠶᠢᠷᠠᠭᠤᠯᠭ᠎ᠠ  ᠶᠢ ᠠᠵᠢᠯᠯᠠᠭᠤᠯᠵᠤ ᠳᠡᠶᠢᠯᠬᠦ ᠦᠭᠡᠢ +ᠲᠤᠰ ᠹᠠᠶᠢᠯ ᠬᠣᠣᠷ ᠲᠠᠢ ( ᠹᠠᠶᠢᠯ  ᠤᠨ ᠨᠡᠷ᠎ᠡ ᠳ᠋ᠥ ᠣᠯᠠᠨ ᠬᠣᠭᠣᠰᠤᠨ ᠵᠠᠢ ᠪᠠᠶᠢᠨ᠎ᠠ )。 +ᠬᠡᠲᠦᠷᠬᠡᠢ ᠤᠷᠲᠤ á ‚ ᠠᠵᠢᠯᠯᠠᠭᠤᠯᠵᠤ ᠳᠡᠶᠢᠯᠬᠦ ᠦᠭᠡᠢ +ᠲᠠ ᠵᠢᠭᠠᠪᠠᠯ ᠨᠢᠭᠡ ᠹᠠᠶᠢᠯ ᠰᠣᠩᠭᠣᠨ᠎ᠠ +ᠲᠠ ᠬᠠᠮᠤᠭ ᠪᠠᠭ᠎ᠠ  ᠳᠠᠭᠠᠨ ᠨᠢᠭᠡ ᠹᠠᠶᠢᠯ ᠰᠣᠩᠭᠣᠨ᠎ᠠ +ᠮᠠᠲ᠋ᠧᠷᠢᠶᠠᠯ ᠬᠡᠲᠦᠷᠬᠡᠢ ᠣᠯᠠᠨ +3300 +á ¶á  á ­ á ­á  á ·á ­á  á ¨ ᠠᠪᠴᠤ ᠪᠠᠶᠢᠨ᠎ᠠ .... +ᠠᠪᠴᠢᠶᠠᠬᠤ +ᠰᠢᠯᠭᠠᠬᠤ +á ¶á ¡á ¬ ᠨᠡᠭᠡᠭᠡᠵᠦ ᠪᠠᠶᠢᠨ᠎ᠠ ... +ᠬᠠᠶᠢᠵᠤ ᠪᠠᠶᠢᠨ᠎ᠠ ... +3400 +á ­á  á ·á ­á  á ¨ ᠠᠪᠬᠤ +(&X)  ᠠᠴᠠ á ­á  á ·á ­á  á ¨ ᠠᠪᠬᠤ : +ᠮᠠᠲ᠋ᠧᠷᠢᠶᠠᠯ  ᠢ á ­á  á ·á ­á  á ¨ ᠠᠪᠤᠭᠰᠠᠨ ᠬᠠᠪᠲᠠᠰᠤ  ᠶᠢᠨ ᠪᠠᠶᠢᠷᠢ +3410 +ᠵᠠᠮ  ᠤᠨ ᠬᠡᠯᠪᠡᠷᠢ +ᠪᠦᠷᠢᠨ ᠵᠠᠮ +ᠵᠠᠮ ᠦᠭᠡᠢ +3420 +ᠰᠣᠯᠢᠬᠤ ᠬᠡᠯᠪᠡᠷᠢ +ᠰᠣᠯᠢᠬᠤ  ᠠᠴᠠ ᠡᠮᠦᠨ᠎ᠡ ᠠᠰᠠᠭᠤᠨ᠎ᠠ +ᠰᠠᠨᠠᠭᠤᠯᠬᠤ ᠦᠭᠡᠢ ᠰᠢᠭᠤᠳ ᠰᠣᠯᠢᠬᠤ +ᠪᠠᠶᠢᠬᠤ ᠹᠠᠶᠢᠯ  ᠢ ᠥᠰᠦᠷᠬᠡᠶᠢᠯᠨᠨ ᠭᠠᠷᠬᠤ +ᠠᠦ᠋ᠲ᠋ᠣ᠋ ᠵᠢᠨᠷ ᠨᠡᠷᠡᠯᠡᠬᠦ +ᠣᠳᠣᠬᠢ ᠹᠠᠶᠢᠯ  ᠢᠶᠠᠨ ᠨᠡᠷᠡᠯᠡᠬᠦ +3500 +ᠹᠠᠶᠢᠯ  ᠢ ᠰᠣᠯᠢᠨ᠎ᠠ +ᠲᠣᠰ ᠬᠠᠪᠲᠠᠰᠤ ᠳ᠋ᠥ ᠠᠳᠠᠯᠢ ᠨᠡᠷᠡᠶᠢᠳᠦᠯ ᠲᠠᠢ ᠹᠠᠶᠢᠯ ᠪᠠᠶᠢᠨ᠎ᠠ +ᠣᠳᠣ ᠪᠠᠶᠢᠬᠤ ᠹᠠᠶᠢᠯ  ᠢᠶᠠᠨ +ᠰᠣᠯᠢᠬᠤ +{0}ᠦᠰᠦᠭ  ᠦᠨ ᠰᠢᠷᠬᠡᠭ +ᠠᠦ᠋ᠲ᠋ᠣ᠋ ᠰᠢᠨ᠎ᠡ ᠨᠡᠷᠡᠯᠡᠬᠦ (&U) +3700 +ᠳᠡᠮᠵᠢᠬᠦ ᠦᠭᠡᠢ ᠠᠪᠴᠢᠮᠠᠯ ᠲᠤᠭᠠᠴᠢᠯᠳᠠ “{0}â€ã€‚ +ᠲᠣᠭ᠎ᠠ ᠪᠠᠷᠢᠮᠲᠠ “{0}†ᠪᠤᠷᠤᠭᠤ á ­á  á ·á ´á  á ¢ á ‚ ᠹᠠᠶᠢᠯ ᠡᠪᠳᠡᠷᠡᠭᠰᠡᠨ á ¡ +CRC ᠰᠢᠯᠭᠠᠭᠠᠳ“{0}â€á ¢á ¯á  á ­á ³á  á µá  á ¢ á ‚ ᠹᠠᠶᠢᠯ ᠡᠪᠳᠡᠷᠡᠭᠰᠡᠨ á ¡ +ᠪᠠᠲᠤ ᠹᠠᠶᠢᠯ “{0}† ᠤᠨ ᠲᠣᠭ᠎ᠠ ᠪᠠᠷᠢᠮᠲᠠ ᠨᠢ ᠪᠤᠷᠤᠭᠤ á ‚ ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠨᠢ ᠪᠤᠷᠤᠭᠤ +ᠪᠠᠲᠤ ᠹᠠᠶᠢᠯ “{0}†CRC  ᠵᠢᠨ ᠰᠢᠯᠭᠠᠭᠰᠠᠨ ᠲᠣᠭ᠎ᠠ ᠪᠠᠷᠢᠮᠲᠠ ᠨᠢ ᠪᠤᠷᠤᠭᠤ á ‚ ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠨᠢ ᠪᠤᠷᠤᠭᠤ +3800 +ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠣᠷᠣᠭᠤᠯᠬᠤ +ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠣᠷᠣᠭᠤᠯᠬᠤ á „ +ᠳᠠᠬᠢᠨ ᠣᠷᠣᠭᠤᠯᠬᠤ +ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠢᠯᠡᠷᠡᠬᠦ (&S) +ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠨᠢ ᠪᠤᠷᠤᠭᠤ +ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠨᠢ ᠵᠥᠪᠬᠡᠨ á  á ©á ­á á ¯á ¢ ᠬᠡᠯᠡ ᠪᠣᠯᠣᠨ ᠲᠣᠭ᠎ᠠ ᠬᠢᠭᠡᠳ ᠣᠨᠴᠠᠭᠠᠢ ᠲᠡᠮᠳᠡᠭ (!ã€#ã€$...) +ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ ᠨᠢ ᠬᠡᠲᠦᠷᠬᠡᠢ ᠤᠷᠲᠤ +ᠨᠢᠭᠤᠴᠠ ᠨᠣᠮᠧᠷ +3900 +ᠬᠡᠷᠡᠭᠰᠡᠭᠰᠡᠨ á ´á  á ­ á „ +ᠦᠯᠡᠳᠡᠭᠰᠡᠨ á ´á  á ­ á „ +ᠶᠡᠷᠤᠩᠬᠡᠢ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ +ᠬᠤᠷᠳᠤᠴᠠ á „ +ᠨᠢᠭᠡᠨᠲᠡ ᠰᠢᠢᠳᠪᠦᠷᠢᠯᠡᠭᠰᠡᠨ ᠨᠢ á „ +ᠠᠪᠴᠢᠶᠠᠭᠰᠠᠨ ᠬᠡᠮᠵᠢᠶ᠎ᠡ á „ +ᠪᠤᠷᠤᠭᠤ á ­á  á ·á ´á  á ¢ á „ +ᠠᠪᠴᠢᠮᠠᠯ á „ +4000 +ᠠᠪᠴᠢᠮᠠᠯ ᠳ᠋ᠥ ᠨᠡᠮᠡᠬᠦ +ᠠᠪᠴᠢᠮᠠᠯ (&A): +ᠰᠢᠨᠡᠳᠬᠡᠬᠦ ᠬᠡᠯᠪᠡᠷᠢ (&U): +ᠠᠪᠴᠢᠮᠠᠯ  ᠤᠨ ᠬᠡᠯᠪᠡᠷᠢ (&F): +ᠠᠪᠴᠢᠶᠠᠬᠤ ᠳᠡᠰ (&L): +ᠠᠪᠴᠢᠶᠠᠬᠤ á  á ·á ­á Žá   (&M): +ᠲᠣᠯᠢ  ᠶᠢᠨ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ (&D): +ᠳᠠᠩ ᠦᠭᠡᠰ  ᠦᠨ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ (&W): +ᠲᠣᠭ᠎ᠠ ᠪᠠᠷᠢᠮᠲᠠ  ᠶᠢᠨ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ +CPU ᠬᠡᠷᠡᠭᠯᠡᠭᠰᠡᠨ ᠬᠡᠮᠵᠢᠶ᠎ᠡ +ᠲᠣᠭ᠎ᠠ ᠪᠠᠷᠢᠮᠲᠠ (&P): +ᠰᠣᠩᠭᠣᠯᠲᠠ +ᠥᠪᠡᠷᠲᠡᠭᠡᠨ ᠠᠴᠢᠶᠠᠯᠠᠭᠴᠢ ᠫᠷᠦᠭᠷᠡᠮ ᠪᠠᠶᠢᠭᠤᠯᠬᠤ (&X) +ᠠᠪᠴᠢᠮᠠᠯ ᠹᠠᠶᠢᠯ  ᠢᠶᠠᠨ ᠬᠤᠪᠢᠶᠠᠯᠴᠠᠬᠤ +ᠪᠠᠲᠤᠵᠢᠭᠤᠯᠬᠤ +ᠪᠠᠲᠤᠵᠢᠭᠤᠯᠬᠤ ᠲᠣᠭᠠᠴᠠᠬᠤ á  á ·á ­á Žá   +ᠪᠠᠲᠤ ᠹᠠᠶᠢᠯ  ᠤᠨ ᠨᠡᠷᠡᠶᠢᠳᠦᠯ (&N) +ᠠᠪᠴᠢᠶᠠᠯᠠᠬᠤ ᠳ᠋ᠥ ᠬᠡᠷᠡᠭᠯᠡᠬᠦ á ·á  á ® +ᠵᠠᠳᠠᠯᠬᠤ ᠳ᠋ᠥ ᠬᠡᠷᠡᠭᠯᠡᠬᠦ á ·á  á ® +4050 +ᠵᠥᠪᠬᠡᠨ ᠬᠠᠳᠠᠭᠠᠯᠠᠬᠤ +ᠣᠨᠴᠠ ᠬᠤᠷᠳᠤᠨ ᠠᠪᠴᠢᠶᠠᠬᠤ +ᠬᠤᠷᠳᠤᠨ ᠠᠪᠴᠢᠶᠠᠬᠤ +ᠪᠠᠷᠢᠮᠵᠢᠶ᠎ᠠ ᠠᠪᠴᠢᠶᠠᠬᠤ +ᠬᠠᠮᠤᠭ ᠶᠡᠬᠡ ᠠᠪᠴᠢᠶᠠᠬᠤ +ᠴᠢᠨᠠᠷᠯᠠᠩᠭᠤᠢ ᠠᠪᠴᠢᠶᠠᠬᠤ +4060 +ᠹᠠᠶᠢᠯ ᠨᠡᠮᠡᠭᠡᠳ ᠰᠣᠯᠢᠬᠤ +ᠹᠠᠶᠢᠯ  ᠢ ᠰᠢᠨᠡᠳᠬᠡᠭᠡᠳ ᠨᠡᠮᠡᠬᠦ +ᠪᠠᠶᠢᠬᠤ ᠹᠠᠶᠢᠯ  ᠢ ᠰᠢᠨᠡᠳᠬᠡᠬᠦ +ᠠᠪᠴᠢᠮᠠᠯ  ᠤᠨ ᠠᠭᠤᠯᠭ᠎ᠠ  ᠶᠢ ᠵᠡᠷᠭᠡᠴᠡᠭᠦᠯᠬᠦ +4070 +ᠦᠵᠡᠬᠦ ... +ᠪᠦᠬᠦ ᠹᠠᠶᠢᠯ +ᠲᠡᠭᠡ +ᠪᠤᠯᠢ +6000 +ᠬᠣᠣᠰᠯᠠᠬᠤ +ᠰᠢᠯᠵᠢᠭᠦᠯᠬᠦ +ᠬᠣᠣᠰᠯᠠᠭᠤᠯᠤᠨ á „ +ᠰᠢᠯᠵᠢᠭᠦᠯᠦᠨ +ᠬᠣᠣᠰᠯᠠᠵᠤ ᠪᠠᠶᠢᠨ᠎ᠠ ... +ᠰᠢᠯᠵᠢᠭᠦᠯᠵᠦ ᠪᠠᠶᠢᠨ᠎ᠠ ... +á ¶á  á ­ ᠰᠢᠨ᠎ᠡ ᠨᠡᠷᠡᠶᠢᠳᠴᠦ ᠪᠠᠶᠢᠨ᠎ᠠ ... +ᠬᠠᠪᠲᠠᠰᠤ ᠰᠣᠩᠭᠣᠬᠤ +ᠣᠳᠣᠬᠢ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ  ᠶᠢ ᠳᠡᠮᠵᠢᠬᠦ ᠦᠭᠡᠢ +ᠹᠠᠶᠢᠯ ᠪᠠ ᠬᠠᠪᠲᠠᠰᠤ  ᠶᠢ ᠲᠠᠬᠢᠨ ᠨᠡᠷᠡᠶᠢᠳᠴᠦ ᠳᠡᠶᠢᠯᠬᠦ ᠦᠭᠡᠢ +ᠹᠠᠶᠢᠯ  ᠢ ᠬᠣᠣᠰᠯᠠᠬᠤ ᠦᠦ ︖ +ᠹᠠᠶᠢᠯ  ᠢ ᠠᠪᠴᠢᠮᠠᠯ ᠳ᠋ᠥ ᠬᠣᠣᠰᠯᠠᠬᠤ ᠦᠦ ︖ +6100 +ᠹᠠᠶᠢᠯ ᠤᠰᠠᠳᠬᠠᠬᠤ +ᠬᠠᠪᠲᠠᠰᠤ  ᠶᠢ ᠤᠰᠠᠳᠬᠠᠬᠤ +ᠣᠯᠠᠨ ᠹᠠᠶᠢᠯ  ᠢ ᠤᠰᠠᠳᠬᠠᠬᠤ +ᠦᠨᠡᠬᠡᠷ“{0}â€â€¯á ¶á ¢ ᠤᠰᠠᠳᠬᠠᠬᠤ ᠦᠦ ? +ᠦᠨᠡᠬᠡᠷ“{0}â€á ¬á  á ªá ²á  á °á ¤  ᠶᠢᠨ ᠪᠦᠬᠦ ᠠᠭᠤᠯᠭ᠎ᠠ  ᠶᠢ ᠤᠰᠠᠳᠬᠠᠬᠤ ᠦᠦ ? +ᠦᠨᠡᠬᠡᠷ {0} ᠵᠢ ᠤᠰᠠᠳᠬᠠᠬᠤ ᠦᠦ ? +ᠤᠰᠠᠳᠬᠠᠵᠤ ᠪᠠᠶᠢᠨ᠎ᠠ ... +ᠹᠠᠶᠢᠯ ᠪᠠ ᠬᠠᠪᠲᠠᠰᠤ  ᠶᠢ ᠤᠰᠠᠳᠬᠠᠵᠤ ᠳᠡᠶᠢᠯᠬᠦ ᠦᠭᠡᠢ +ᠰᠢᠰᠲ᠋ᠧᠮ ᠬᠡᠲᠦᠷᠬᠡᠢ ᠤᠷᠲᠤ ᠵᠠᠮ ᠲᠠᠢ ᠹᠠᠶᠢᠯ  ᠢ ᠬᠣᠭ ᠬᠤᠷᠢᠶᠠᠭᠴᠢ ᠳ᠋ᠥ ᠰᠢᠯᠵᠢᠭᠦᠯᠵᠦ ᠳᠡᠶᠢᠯᠬᠦ ᠦᠭᠡᠢ +6300 +ᠰᠢᠨ᠎ᠡ ᠬᠠᠪᠲᠠᠰᠤ ᠪᠠᠶᠢᠭᠤᠯᠬᠤ +ᠰᠢᠨ᠎ᠡ ᠹᠠᠶᠢᠯ ᠪᠠᠶᠢᠭᠤᠯᠬᠤ +ᠬᠠᠪᠲᠠᠰᠤ  ᠶᠢᠨ ᠨᠡᠷᠡᠶᠢᠳᠦᠯ á „ +ᠹᠠᠶᠢᠯ  ᠤᠨ ᠨᠡᠷ᠎ᠡ +ᠰᠢᠨ᠎ᠡ ᠬᠠᠪᠲᠠᠰᠤ ᠪᠠᠶᠢᠭᠤᠯᠬᠤ +ᠰᠢᠨ᠎ᠡ ᠹᠠᠶᠢᠯ ᠪᠠᠶᠢᠭᠤᠯᠬᠤ .txt +ᠬᠠᠪᠲᠠᠰᠤ ᠪᠠᠶᠢᠭᠤᠯᠵᠤ ᠳᠡᠶᠢᠯᠦᠭᠰᠡᠨ ᠦᠭᠡᠢ +ᠰᠢᠨ᠎ᠡ ᠹᠠᠶᠢᠯ ᠪᠠᠶᠢᠭᠤᠯᠵᠤ ᠳᠡᠶᠢᠯᠦᠭᠰᠡᠨ ᠦᠭᠡᠢ +6400 +ᠲᠠᠶᠢᠯᠪᠤᠷᠢ +ᠲᠠᠶᠢᠯᠪᠤᠷᠢ (&C): +ᠰᠣᠩᠭᠣᠬᠤ +ᠰᠣᠩᠭᠣᠭᠰᠠᠨ  ᠢᠶᠠᠨ ᠤᠰᠠᠳᠬᠠᠬᠤ +ᠨᠣᠮᠧᠷ á „ +6600 +ᠰᠢᠨᠵᠢ ᠴᠢᠨᠠᠷ +ᠬᠠᠪᠲᠠᠰᠤ  ᠶᠢᠨ ᠲᠡᠦᠬᠡ +ᠣᠨᠣᠰᠢᠯᠠᠭᠰᠠᠨ ᠰᠤᠷᠠᠭ +ᠰᠤᠷᠠᠭ +7100 +ᠮᠢᠨᠤ ᠺᠣᠮᠫᠢᠦ᠋ᠲ᠋ᠧᠷ +ᠰᠦᠯᠵᠢᠶᠡᠨ â€á £ ᠬᠠᠨᠢ +ᠮᠢᠨᠤ ᠮᠠᠲ᠋ᠧᠷᠢᠶᠠᠯ +ᠰᠢᠰᠲ᠋ᠧᠮ +7200 +ᠨᠡᠮᠡᠬᠦ +á ­á  á ·á ­á  á ¨ ᠠᠪᠬᠤ +ᠰᠢᠯᠭᠠᠬᠤ +ᠬᠣᠣᠰᠯᠠᠬᠤ +ᠰᠢᠯᠵᠢᠭᠦᠯᠬᠦ +ᠤᠰᠠᠳᠬᠠᠬᠤ +ᠰᠤᠷᠠᠭ +7300 +ᠹᠠᠶᠢᠯ ᠰᠢᠯᠭᠠᠬᠤ +ᠹᠠᠶᠢᠯ  ᠢ (&S) ᠵᠦᠭ ᠰᠠᠯᠭᠠᠬᠤ᠄ +ᠡᠪᠬᠡᠮᠡᠯ ᠬᠤᠪᠢᠶᠠᠬᠤ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ (&V): +ᠰᠠᠯᠭᠠᠵᠤ ᠪᠠᠶᠢᠨ᠎ᠠ ... +ᠰᠠᠯᠭᠠᠬᠤ ᠦᠦ +ᠲᠠ ᠹᠠᠶᠢᠯ  ᠢᠶᠠᠨ {0} ᠬᠡᠰᠡᠭ ᠬᠤᠪᠢᠶᠠᠬᠤ ᠦᠦ ︖ +ᠡᠪᠬᠡᠮᠡᠯ ᠬᠤᠪᠢᠶᠠᠭᠰᠠᠨ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ ᠨᠢ ᠤᠭ ᠹᠠᠶᠢᠯ  ᠠᠴᠠ ᠪᠠᠭ᠎ᠠ ᠪᠠᠶᠢᠬᠤ ᠬᠡᠷᠡᠭᠲᠡᠢ +ᠡᠪᠬᠡᠮᠡᠯ ᠬᠤᠪᠢᠶᠠᠬᠤ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ ᠨᠢ ᠪᠤᠷᠤᠭᠤ +ᠡᠪᠬᠡᠮᠡᠯ ᠬᠤᠪᠢᠶᠠᠬᠤ ᠶᠡᠬᠡ ᠪᠠᠭ᠎ᠠ  ᠶᠢ ᠲᠣᠭᠲᠠᠭᠠᠬᠤ {0} ᠦᠰᠦᠭ  ᠦᠨ ᠰᠢᠷᠬᠡᠭ \nᠲᠠ ᠳᠣᠣᠷᠠᠬᠢ ᠹᠠᠶᠢᠯ  ᠢ ᠬᠤᠪᠢᠶᠠᠬᠤ ᠦᠦ ? +7400 +ᠹᠠᠶᠢᠯ ᠨᠡᠶᠢᠯᠡᠭᠦᠯᠬᠦ +ᠹᠠᠶᠢᠯ  ᠢ (&S) ᠳ᠋ᠥ ᠨᠡᠶᠢᠯᠡᠭᠦᠯᠬᠦ +ᠨᠡᠶᠢᠯᠡᠭᠦᠯᠵᠦ ᠪᠠᠶᠢᠨ᠎ᠠ ... +ᠬᠤᠪᠢᠷᠢ ᠡᠪᠬᠡᠮᠡᠯ  ᠦᠨ ᠲᠦᠷᠦᠭᠦᠦ ᠹᠠᠶᠢᠯ  ᠢ ᠰᠣᠩᠭᠣᠭᠠᠷᠠᠢ +ᠬᠤᠪᠢᠷᠢ ᠡᠪᠬᠡᠮᠡᠯ  ᠢ ᠲᠠᠨᠢᠬᠤ ᠦᠭᠡᠢ +ᠪᠤᠰᠤᠳ ᠬᠤᠪᠢᠷᠢ ᠡᠪᠬᠡᠮᠡᠯ ᠬᠠᠶᠢᠵᠤ ᠣᠯᠣᠭᠰᠠᠨ ᠦᠭᠡᠢ +7500 +ᠰᠢᠯᠭᠠᠵᠤ ᠪᠠᠶᠢᠨ᠎ᠠ ... +ᠰᠢᠯᠭᠠᠭᠰᠠᠨ ᠰᠤᠷᠠᠭ +CRC ᠲᠣᠭ᠎ᠠ ᠪᠠᠷᠢᠮᠲᠠ ᠰᠢᠯᠭᠠᠬᠤ +CRC ᠲᠣᠭ᠎ᠠ ᠪᠠᠷᠢᠮᠲᠠ ᠪᠠ ᠹᠠᠶᠢᠯ  ᠤᠨ ᠨᠡᠷ᠎ᠡ  ᠶᠢ ᠰᠢᠯᠭᠠᠬᠤ : +7600 +ᠥᠭᠭᠦᠭᠳᠡᠯ ᠰᠢᠯᠭᠠᠯᠲᠠ +á ·á  á ® ᠬᠡᠷᠡᠭᠯᠡᠯᠲᠡ á „ +ᠠᠪᠴᠢᠶᠠᠬᠤ +ᠵᠠᠳᠠᠯᠬᠤ +ᠣᠨᠣᠭ᠎ᠠ ᠥᠭᠬᠦ +ᠶᠡᠷᠤᠩᠬᠡᠢ ᠣᠨᠣᠭ᠎ᠠ +ᠣᠳᠣᠬᠢ +ᠦᠷ᠎ᠡ ᠳ᠋ᠦᠩ +CPUᠬᠡᠷᠡᠭᠯᠡᠭᠰᠡᠨ ᠬᠡᠮᠵᠢᠶ᠎ᠡ +ᠬᠡᠷᠡᠭᠯᠡᠭᠴᠢ  ᠶᠢᠨ ᠣᠨᠣᠭ᠎ᠠ +ᠪᠠᠲᠤᠯᠠᠭᠳᠠᠭᠰᠠᠨ á ¡ á „ diff --git a/Utils/7-Zip/Lang/mng2.txt b/Utils/7-Zip/Lang/mng2.txt new file mode 100644 index 000000000..ad99c235e --- /dev/null +++ b/Utils/7-Zip/Lang/mng2.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 7-Zip 9.20 +; Saqirilatu Mongolqileb +; QQ:136087084 Email:saqirilatu@126.com +; Mongol soft QQ bulug â… : 39338772 â…¡:38803882 +; Toli Mongolian IME +; http://hi.baidu.com/saqirilatuu/item/9438213716f316ebe7bb7a8d +;last updated: 2013-12-11 +; Update and Spelling corrected Bayarsaikhan +; +; +; +0 +7-Zip +Mongolian (MenkCode) +î—«î—–î— î—¥î— +401 +î—¹î—î—¥î—î— +î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ + + + +î—¹î—î—¥î—î— (&Y) +î—œî——î—±î— (&N) +î—£î—î—šî—šî—î—î—î—“ (&C) +î—£î—î—žî—µî—–î—¿î—–î—­î—½î— + +î—¼î—î—î—šî—šî—î—–î—Œ (&C) +440 + î—¹î—î—¥î—î— (&A) + î—œî——î—±î— (&L) +î—î—î—î—î—î—“ +î—¹î— î— +î—‹î—î—¿î—“ î—¹î—î—°î—Ž (&B) + î—¹î—î—° (&F) +î—î—î—î—î—“ (&P) +î—î— î—î—î—î— + î—‹î—–î—‘î—šî—î—¥î— î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ î—–î—“ ? +500 +î—î—‘î—‘î—° (&F) +î—˜î—î—‘î—‘î—¿î—î—šî—šî—–î—±î—î—î—“ (&E) +î—‹î—–î—‘î—½î— (&V) +î—£î—î—ºî—î—šî—šî—î—±î—î—î—î—“ (&A) +î—›î—î—šî—šî—î—½î— (&T) +î—£î—î—žî—µî—–î—¿î—–î—­î—½î— (&H) +540 +î—˜î—î—¥î—î—¥î— (&O) + î—î—ªî—î—î—“ î—¼î— î—˜î—î—¥î—î—¥î— (&I) + î—î—ªî—î—î—“ î—›î—î—î—–î—Œ î—˜î—î—¥î—î—¥î— (&U) + î—‹î—–î—‘î—½î— (&V) +î—˜î—î—‘î—‘î—¿î—î—šî—šî—–î—±î—î—î—“ (&E) +î—¹î— î—˜î—î—¿î—î—±î— (&M) +î—î—î—î—“ (&C) + (&M) +î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ (&D) +î—î—‘î—‘î—° î—î—î—î—“ (&S)... +î—î—‘î—‘î—° î— î—˜î—î—‘î—‘î—±î— (&B)... +î—î—½î— î—»î—‘î—šî— (&R) +î—¹î—î—‘î—‘î—±î—œî——î—¿î— (&N) +î—î—‘î—‘î—° î—î—î—î—“ +î—î—‘î—‘î—° î—‹î—î—ºî—î—î—î—î—î—î—î—“ + î—£î—î—žî—ºî— î—›î—î—‘î—‘î—šî—šî—–î—±î—î—î—“ +î—î—‘î—‘î—° î—›î—î—‘î—‘î—šî—šî—–î—±î—î—î—“ +î—¤î—î—¿î—î—î—“ (&X) +600 + î— î—³î—–î—î—î—î—“ (&A) + î— î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ +î—‹î—µî— î—î—î—î—“ (&I) +î—î—î—î—“ ... +î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ î—›î—î—Œ î—î—î—î—“ ... +î—‹î—î—ºî—î—±î— î—¥î—î—î—¿î— î—‘î—‘î—Œ î—î—‘î—‘î—° î— î—³î—–î—î—î—î—“ +î—‹î—î—ºî—î—±î— î—¥î—î—î—¿î— î—‘î—‘î—Œ î—î—‘î—‘î—° î— î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ +700 +î—¾î—î—¥î—î— î—¼î—‘î—¿î—–î—§ (&G) +î—›î—  (&M) +î—î—î—µî—î—šî—šî—î—î—î—“ (&L) +î—˜î—î—¿î—‘î—Œ î—‹î— (&D) +730 +î—«î—–î—‘î—¿î—±î— î—‹î—–î—‘î—¥î—î— +î—¹î—î—ªî—µî— î—¥î—î— +  (&2) +î—›î—î—šî—šî—î—½î— î—‘î—‘î—Œ î—³î—î—­î—›î— (&T) +î—‹î—–î—‘î— î—£î—î—žî—ºî— î—˜î—î—¥î—î—¥î— +î—¹î—î—¥î—î—ªî—µî— +î—£î—î—žî—ºî— î—“ î—¹î—î—–î—¥î—î— ... +î—î—–î—î—¥î— (&R) +750 +î—¹î—î—î—î—Œ î—“ î—›î—î—šî—šî—î—½î— î—‘î—‘î—Œ î—³î—î—­î—›î— +î—›î—î—î—Ž î—›î—î—šî—šî—î—½î— î—‘î—‘î—Œ î—³î—î—­î—›î— +î—¾î—î—¥î—î— î—¹î—î—¿î—–î—žî—»î— +î—¹î—î—¿î—–î—žî—»î— î—‘î—‘î—Œ î—‹î—–î—‘î—µî—–î—‘î— î—‹î—‘î—±î—î—¿î— +800 +î—£î—î—ºî—î—šî—šî—î—±î—î—­î—½î— î—¹î—î—šî—šî—î—Œ î—˜î—î—­î— (&A) + +900 +î— (&O) +î—î—° î— (&B) +960 +î—£î—î—žî—µî—–î—¿î—–î—­î—½î— î—‹î—–î—‘î—½î— (&C) +7-Zip î—“ î—î—î—î— (&A) +1003 +î—¼î—î—¬ +î—˜î—î—¿î— +î—‹î—–î—‘î—¿î—¥î—î—–î—î—¥î—î—î—Œ î—˜î— +î—£î—î—žî—ºî— +î—¾î—î—¥î—î— î—›î— +î—‹î—î—î—î—î—µî—î—Œ î—“ î—¹î—î—¿î—î—šî—šî— î—¾î—î—¥î—î— î—›î— +î—î—½î— î—»î—‘î—šî— +î—›î—î—î—î—µî—î—Œ î—»î—î—§ +î—‹î—–î—‘î—½î—î—î—Œ î—»î—î—§ +î—¼î—î—µî—î—î—î—µî—î—Œ î—»î—î—§ +î—£î—î—ºî— +î—¹î—î—‘î—‘î—±î—œî——î—¿î— +î—›î—î—î—î—“ + î—˜î— î—£î—–î—›î—‘î—¾î—î—î—î—“ +î—³î—î— î—˜î— î—£î—–î—›î—‘î—¾î—î—î—î—“ +î—¹î—–î—±î— î—›î—‘î—»î—‘î—‘î— î—–î—Œ î—¾î—î—¥î—î— î—›î— +CRC + +î—‹î—î—î—î—î—“  +î—î—»î—î—î—î—“ î—‹î— + î—‹î—î—î—î—î—“ î˜î—¬ +î—î—‘î—‘î—° î—–î—Œ î˜î—¬ +î—¥î—î—¿î—î—î—ªî—»î— +î—œî——î—‘î—±î—–î—‘î— +î—‹î—–î—‘î—µî—–î—‘î— î—–î—Œ î—¥î—î—µî—î—‘î— +î—¹î—î—‘î—‘î—±î—œî——î—¿î— +î—‹î—–î—¿î—–î—Œ î—î—î—ºî—î—šî—šî—î—î—î—“ +î—¼î—î—¬ î—–î—Œ  î—¥î—î—µî—î—‘î— +î—£î—î—žî—ºî— +î—î—‘î—‘î—° +î—¥î—î— +î—‹î—žî—¥î—î—­î—î—° +î—‹î—–î—±î—î—Œ î—‹î—žî—¥î—î—­î—î—° î—‹î—î—î—î—“ +î—£î—î—½î—î—šî—šî—î— î—³î—‘î—±î—½î—‘î—¦î——î—±î—¦î—• +î—¼î—î—î—î—î—“ +î—‹î—–î—‘î—µî—–î—‘î— î—–î—Œ î—¥î—î—µî—î—‘î— +î—‹î—žî—¥î—î—­î—î—° î—î—î—î—“ + +64 î—‹î—–î—¿î—–î—Œ +î—¾î—î—¥î—î— î—‹î—–î—‘î—µî—–î—‘î— î—–î—Œ î—¥î—î—µî—î—‘î— î—–î—Œ î—¹î—î—¿î—î—šî—šî—î—±î—î—° +CPU + î—¾î—î—¥î—î— î—›î— +î—î—‘î—‘î—° î—–î—Œ î—¾î—î—¥î—î— î—›î— +î—˜î—î—‘î—‘î—±î—î—œî——î—¿î— î— î—³î—‘î—±î—šî—šî—î—î—î—“ +î—‹î—–î—î—»î— +î—¥î—î—‘î—‘î—µî—œî——î—¿î— î—£î— +ID +î—î—î—î—î—Œ î—î—‘î—‘î—° î—–î—Œ î—˜î— +î—¢î˜î—¿î—–î—î—¬ î—›î—î—‘î—‘î—šî—šî—–î—±î—î—î—“ +î—¹î— î—–î—Œ î—¾î—î—¥î—î— î—›î— +î—¥î—î— î—¼î—î—î— +î—¼î—î—î—î—î—“ + +î—¾î—î—¿î—–î—î—î— î—¾î—î—¥î—î— î—›î— +î—¥î—î—¿î—î—î—î—Œ î—‹î—–î—¿î—–î—Œ î—¼î—î— +î—›î—î—î— î—‘î—‘î—Œ î—¾î—î—¥î—î— î—›î— +î—‹î—žî—¥î—î—­î—î—° î—–î—Œ î—¹î—î—­î—ºî—î—‘î— + î—¤î—î—½î— î—–î—Œ î—˜î—î—¿î— +î—£î—î—î—î—î—î—»î— +2100 +î— +î—¥î— +î—¥î— î—î—î—î—“ : +î—˜î—î—‘î—‘î—¿î—î—î—î—»î— +î—î—î—ºî—î—î—î—µî—î—Œ î—˜î—î—‘î—‘î—¿î—î—î—î—»î— (&E): +î—î—î—ºî—î—î—î—µî—î—Œ î—î—‘î—‘î—° î— î—‹î—î—ºî—î—î—î—î—î—î—î—“ (&D): +2200 +î˜î—¬ +7-Zip î—›î— î—î—‘î—‘î—° î—–î—Œ  î— î—£î—–î—±î—œî——î—î—î—ºî—î—šî—šî—–î—±î—î—î—“ : +2301 +7-Zip î—¼î— î—›î—î—¿î—î—šî—šî—–î—Œ î—¹î—î—¿î—–î—žî—»î— î—ºî—“ î—˜î—î—­î— +î—›î—î—¿î—î—šî—šî—–î—Œ î—¹î—î—¿î—–î—žî—»î— î—ºî—“ î—¹î—î—žî—î—î—–î—»î—î—šî—šî—–î—±î—î—î—“ +î—›î—î—¿î—î—šî—šî—–î—Œ î—¹î—î—¿î—–î—žî—»î— î—ºî—“ î—‹î—‘î—±î—î—¿î—  î— î—³î—–î—î—î—î—“ +2320 +<î—£î—î—žî—ºî— > +< î—‹î—žî—¥î—î—­î—î—° > +î—‹î—î—î—° î— î—˜î—î—¥î—î—¥î— +î—«î—î—¹î˜î—¿î—‘î—¾î—î—° î— î—¤î—î—¿î—šî—šî—î—Œ î—‹î—î—žî—î—î—“ ... +î—‹î—î—î—°  î—˜î—î—­î— ... +î—‹î—î—î—° î— î—³î—‘î—±î—šî—šî—î—î—î—“ +î— î—¤î—î—¿î—»î—î—§  î—¤î—î—¿î—šî—šî—î—Œ î—‹î—î—žî—î—î—“ +{0}î—‹î—–î—¿î—–î—šî—šî—“ î—¼î—î—ºî—î—±î—î—î—“ +{0}  î—˜î—î—­î— +î—‹î—î—î—šî—šî—î—–î—Œ î—‹î—‘î—­î˜î—° î—¾î—î—î—î—“ ... +î—‹î—î—î—šî—šî—î—–î—Œ {0} î—¼î— î—‹î—‘î—±î—î—¥î— +2400 +î—£î—î—žî—ºî— +î—‹î— î—–î—Œ î—£î—î—žî—ºî— (&W) +î˜î—¬ \î—¼î—î—µî—î—œî——î—¿î— î—‘î—‘î—Œ î—£î—î—žî—ºî—(&S) + î—‘î—‘î—Œ î—£î—î—žî—ºî— (&C) +î—î—î—ºî—î—­î—î—° î—›î—î—‘î—‘î—¿î— (&S) +î—î—Œ î—î—¥î—î—œî——î—¿î— î—‘î—‘î—Œ î—î—¿î—–î—­î—½î— î—ºî—“ î—¥î—î—¿î—î— + î—»î—î—§ î—–î—Œ î—‹î—î—î—° î—î—‘î—‘î—° î—î—¥î— î—›î—î—‘î—‘î—¿î—‘î—±î—î—° î— î—¹î—–î—ºî—–î—¿î—î—î—î—‘î—‘î—° +2500 +î—‹î—‘î—±î—î—¿î— +î—‹î—‘î—±î—î—¿î— “..â€î—¼î—–î—‘î—‘î—° (î—¹î—î—¥î—î—ªî—µî— î—£î—–î—–î—´ î—î—šî—šî—î—–î—Œ ) +î—‹î—–î—‘î—šî—î—Œ  î—˜î— î—‹î—‘î—±î—î—¿î— +î˜î—¬ î—–î—Œ  î—˜î— î—‹î—‘î—±î—î—¿î— +  î— î—³î—–î—î—î—î—“ (&F) +î—î—Œ î—‹î—–î—ºî— î—‹î—‘î—±î—î—¿î— (&G) +î—î—šî—šî—î—–î—Œ î—˜î—î—¥î—î—¥î— +7-Zip î—‹î—–î—±î—î— î—¥î—î— î— î—³î—–î—î—î—î—“ (&A) +î—¾î—î—¥î—î— î—¿î—î—¬ î—–î—Œ  î— î—¥î—î—¿î—î— (&L) +2900 +7-Zip  î—î—î—î— +7-Zip  î—¹î—–î—‘î—±î—–î—œî——î—¿î— î—‹î—–î—‘î—¥î—î— î—³î—–î˜…î—¹î—’   î—£î—î—î—ºî—‘î— î—–î—Œ î—¥î—î—î—¿î— î—›î— 7-Zip î—¼î— î—¹î—  î—«î—–î—  î—‘î—‘î—Œ î—œî——î—‘î—±î—–î—‘î— â… 39338772 â…¡ 38803882  î—³î—î— î—«î—–î—î—î—›î—î— î—£î—î—î—î—î—“ î—‹î— QQ 136087084 Email: saqirilatu@126.com +3000 +î˜î—¬ î—¿î—î—¬ î— î—˜î— î—£î—–î—›î—‘î—¾î— î—¹î— î—‹î—–î—‘î—¥î—î— + î—‹î—–î—‘î—¥î—î— +{0} î—¼î— î—³î—–î—î—î—î—“ +{0}†î—î—žî—ºî— î— î—¹î— î—‹î—–î—‘î—¥î—î— + î—‹î—î—î—° î—–î—Œ î—î—–î—î—¥î— î—¼î— î—¹î— î—‹î—–î—‘î—¥î—î— +î—‹î—î—î—° î—˜î—î—¥î—î—¥î— î—¹î— î—‹î—–î—‘î—¥î—î—“{0}†+î—›î— î—‹î—î—î—° î— î—˜î—î—¥î—î—¥î— î—¹î— î—‹î—–î—‘î—¥î—î—“{0}â€î—˜î—‘ î˜î˜€ î—˜î— î—œî——î—¿î—–î—šî—šî—“ ? +î—¹î— î—‹î—–î—‘î—¥î—î— î—‹î—î—î—° î—–î—Œ î—¥î—î—î—¿î— +{0} î—î—‘î—‘î—° î—î— î—›î— +î—î—‘î—‘î—° “{0}â€î—‹î—–î—‘î—¥î—î—¿î—î—î—î—›î—î— \n î—‹î—î—î—° î—ºî— î—î—‘î—‘î—° î—î—Œ î—î—–î—î—¥î— î—–î—“ ? +î—î—‘î—‘î—° î— î—³î—‘î—šî—î—–î—î—¥î— î—¹î—î—î—Œ î—‹î—–î—‘î—¥î—î— \n“{0}â€ï¼Œî—¹î—–î—´ î—‹î—î—î—°  î—¤î—î—¿î—»î—î— +î—¤î—î—ºî—î—šî— î—˜î—î—‘î—‘î—¿î— î—¼î— î—‹î—î— î—¹î— î—‹î—–î—‘î—¥î—î— + î—î—‘î—‘î—°  î—¹î—î— ( î—î—‘î—‘î—° î—–î—Œ î—˜î—  î—‹î—–î—±î—î—Œ  î—¼î—î— î—›î— )。 +î—¥î—î—î— î—‹î—–î—¿î—ºî—“ î—‹î—î— î—¹î— î—‹î—–î—‘î—¥î—î— + î—î—›î—î—° î—î— î˜‚î—î—‘î—‘î—° î— + î—£î—î—­î—–î—§ î—›î— î—¹î—î—šî—šî—î—Œ î—î— î˜‚î—î—‘î—‘î—° î— +î—«î—î—¹î˜î—¿î—‘î—¾î—î—° î—¥î—î—î— î—‹î—–î—±î—î—Œ +3300 +î—¾î—î—§ î—¤î—î—¿î—šî—šî—î—Œ î—‹î— î—›î— .... +î—‹î—î—î—î—î—“ +î—î—î—î—“ +î—¾î—î—§ î—˜î—î—¥î—î—¥î— î—›î— ... +î—£î— î—›î— ... +3400 +î—¤î—î—¿î—šî—šî—î—Œ î—‹î—î—žî—î—î—“ +(&X)  î—¤î—î—¿î—šî—šî—î—Œ î—‹î—î—žî—î—: +î—«î—î—¹î˜î—¿î—‘î—¾î—î—° î— î—¤î—î—¿î—šî—šî—î—Œ î—‹î—î—î—î—µî—î—Œ î—£î—î—žî—ºî— î—–î—Œ î—›î—î—‘î—‘î—¿î— +3410 +î—¼î—î—¬ î—–î—Œ î—¥î—î—î—¿î— + î—¼î—î—¬ +î—¼î—î—¬ î—‹î—–î—‘î—¥î—î— +3420 +î—î—î—“ î—¥î—î—î—¿î— +î—î—î—“   î—‹î—î—µî— +î—³î—î—šî—î—šî—šî—–î—±î—î—î—“ î—‹î—–î—‘î—¥î—î— î—³î—‘î—šî—šî—–î—–î—Œ î—î—î—“ +î—›î—î—‘î—‘î—î—î—“ î—î—‘î—‘î—° î— î—‹î—–î—‘î—µî—–î—¿î—¥î—î—‘î—‘î—±î—î—Œ î—¤î—î—¿î—î—î—“ +î—‹î— î— î—˜î—î—¿î—î—±î— + î—î—‘î—‘î—° î—î—Œ î—˜î—î—¿î—î—±î— +3500 +î—î—‘î—‘î—° î— î—³î—–î—±î—‘î—™î—Ž + î—£î—î—žî—ºî—  î—‹î—î—ºî—î—±î— î—˜î—î—¿î— î—¹î—î— î˜‚î—î—‘î—‘î—° î—›î— + î—›î—î—‘î—‘î—î—î—“ î—î—‘î—‘î—° î—î—Œ +î—î—î—“ +{0} î—‹î—–î—‘î—µî—–î—‘î— î—–î—Œ î—î—‘î— +î—‹î— î—î—Œ  î—˜î—î—¿î—î—±î— (&U) +3700 +î—¹î— î—‹î—–î—‘î—¥î—î— î—‹î—î—î—° î— “{0}â€ã€‚ + î—›î— “{0}â€î—œî——î—¿î—–î—šî—šî—“ î—¤î—î—¿î—»î—î— ï¼Œî˜‚î—î—‘î—‘î—° î—‹î—žî—ºî—î—¿î—î—î—Œ î—‹î— +CRC î—î—šî—šî—î—–î—Œ “{0}â€î—‹î—‘î—±î—î—î—î—ºî—î—½î—î—,î—î—‘î—‘î—° î—‹î—žî—ºî—î—¿î—î—î—Œ î—‹î— +î—›î— î—î—‘î—‘î—° “{0}â€î—–î—Œ  î—›î— î—˜î— î—œî——î—¿î—–î—šî—šî—“ , î˜î˜€ î—˜î— î—œî——î—¿î—–î—šî—šî—“ ? +î—›î— î—î—‘î—‘î—° “{0}â€CRC î—‘î—‘î—Œ î—î—î—î—µî—î—Œ  î—›î— î—˜î— î—œî——î—¿î—–î—šî—šî—“ , î˜î˜€ î—˜î— î—œî——î—¿î—–î—šî—šî—“ ? +3800 + î˜î˜€ î—î—Œ î—‹î—–î—¿î—–î—šî—šî—–î—±î—î—î—“ + î˜î˜€ î—î—Œ î—‹î—–î—¿î—–î—šî—šî—–î—±î—î—î—“ : +î—¹î— î—‹î—–î—¿î—–î—šî—šî—–î—±î—î—î—“ : + î˜î˜€ î—‹î—‘î—±î—î—¿î— (&S) + î˜î˜€ î—˜î— î—œî——î—¿î—–î—šî—šî—“ + î˜î˜€ î—˜î— î—¼î—–î—‘î—žî—¥î—î—Œ î—‹î—î—î—ªî—¥î—²î— î—¥î—   î—î—–î—Œ î—‹î—–î—î—»î—î—šî—šî—î— î—¹î—î—­î—ºî—î—‘î— (!ã€#ã€$...) + î˜î˜€ î—¥î—î—î— î—‹î—–î—¿î—ºî—“ + î˜î˜€ +3900 +î—¥î—î—¿î—î—î—î—Œ î—»î—î—§ : +î—‹î—–î—‘î—±î—î—ºî—î—î—Œ î—»î—î—§ : +î—¾î—î—¿î—–î—î—î— î—¾î—î—¥î—î— î—›î— : + : +î—î— î—î—î—î—Œ î—˜î— ï¼š +î—‹î—î—î—î—î—µî—î—Œ î—¥î—î—î—Ž : + î—¤î—î—¿î—»î—î—: +î—‹î—î—: +4000 +î—‹î—î—î—°  î—˜î—î—­î— +î—‹î—î—î—° (&A): +î—î—–î—î—¥î— î—¥î—î—î—¿î— (&U): +î—‹î—î—î—° î—–î—Œ î—¥î—î—î—¿î— (&F): +î—‹î—î—î—î—î—“ î—¹î—î—´ (&L): +î—‹î—î—î—î—î—“ î—‹î— (&M): +î—¹î—–î—±î— î—‘î—‘î—Œ î—¾î—î—¥î—î— î—›î— (&D): +î—¹î—î—î—‘î— î—‹î—–î—‘î—¥î—î—´ î—–î—Œ î—¾î—î—¥î—î— î—›î— (&W): + î—›î— î—‘î—‘î—Œ î—¾î—î—¥î—î— î—›î— : +CPU î—¥î—î—î—Ž : + î—›î— (&P): +î— +î—‹î—–î—‘î—›î—î—¿î—ºî—î—¥î—î—Œ î—‹î—î—î—±î—î—î—î—»î— î—¢î˜î—¿î—–î—î—¬ î—›î—î—‘î—‘î—šî—šî—–î—±î—î—î—“ (&X) +î—‹î—î—î—­î—î—° î—î—‘î—‘î—° î—î—Œ î—î—î—î—î—“ +î—›î—î—î—î—“ +î—›î—î—î—î—“ î—î—»î—î—î—î—“ î—‹î— : +î—›î— î—î—‘î—‘î—° î—–î—Œ î—˜î—î—¿î— (&N) +î—‹î—î—î—±î—î—î—î—“  î—¥î—î—¿î—î— î—¿î—î—¬ : +î—¼î—î—ºî—î—±î—î—î—“  î—¥î—î—¿î—î— î—¿î—: +4050 +î—î—Œ î—£î—î—ºî—î—šî—šî—î—±î—î—î—î—“ +î—‹î—–î—  î—‹î—î—î—î—î—“ + î—‹î—î—î—î—î—“ +î—›î—î—î—Ž î—‹î—î—î—î—î—“ +î—£î—î—­î—–î—§ î—¾î—î—¥î—î— î—‹î—î—î—î—î—“ +î—î—¿î—±î—î—î—ªî—šî—šî—–î— î—‹î—î—î—î—î—“ +4060 +î—î—‘î—‘î—° î—˜î—î—­î—î—¥î—î—–î—Œ î—î—î—“ +î—î—‘î—‘î—° î— î—³î—‘î—šî—î—–î—î—¥î—î—¥î—î—–î—Œ î—˜î—î—­î— +î—›î—î—‘î—‘î—î—î—“ î—î—‘î—‘î—° î— î—³î—‘î—šî—î—–î—î—¥î— +î—‹î—î—î—° î—–î—Œ î—‹î— î—¼î— î—¼î—î—¿î—¥î—î—»î— +4070 +î—‹î—–î—‘î—½î— ... + î—î—‘î—‘î—° +î—¹î—î—¥î—î— +î—œî——î—±î— +6000 +î—î—î—î—“ + +î—: +: +î— î—›î— ... + î—›î— ... +î—¾î—î—§  î—˜î—î—¿î—î—‘î—‘î—–î— î—›î— ... +î—£î—î—žî—ºî— î—î—î—î—“ + î—‹î—î— î—¼î— î—¹î— î—‹î—–î—‘î—¥î—î— +î—î—‘î—‘î—° î—›î—î— î—£î—î—žî—ºî— î— î—¹î— î—˜î—î—¿î—î—‘î—‘î—–î— î—¹î— î—‹î—–î—‘î—¥î—î— +î—î—‘î—‘î—° î— î—£î—–î—–î—µî—±î—î—î—î—“ î—–î—“ +î—î—‘î—‘î—° î— î—‹î—î—î—°  î—î—î—î—“ +6100 +î—î—‘î—‘î—° î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ +î—£î—î—žî—ºî— î— î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ +î—‹î—–î—±î—î—Œ î—î—‘î—‘î—° î— î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ +î—‹î—–î—‘î—šî—î—¥î—“{0}â€î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ î—–î—“ ? +î—‹î—–î—‘î—šî—î—¥î—“{0}â€î—£î—î—žî—ºî— î—‘î—‘î—Œ  î—‹î— î—¼î— î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ î—–î—“ ? +î—‹î—–î—‘î—šî—î—¥î— {0} î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ ? +î—‹î—–î—µî—î—–î—î—î—î— î—›î— ... +î—î—‘î—‘î—° î—›î—î— î—£î—î—žî—ºî— î—¼î— î—‹î—–î—µî—î—–î—î—î—î— î—¹î— î—‹î—–î—‘î—¥î—î— . +î˜î—¬ î—¥î—î—î— î—‹î—–î—¿î—ºî—“ î—¼î—î—¬ î—¹î—î— î˜‚î—î—‘î—‘î—° î— î—£î—–î—§ î—î—î—î—»î— î—ºî—“  î—¹î— î—‹î—–î—‘î—¥î—î— +6300 + î—£î—î—žî—ºî— î—›î—î—‘î—‘î—šî—šî—–î—±î—î—î—“ + î—î—‘î—‘î—° î—›î—î—‘î—‘î—šî—šî—–î—±î—î—î—“ +î—£î—î—žî—ºî— î—‘î—‘î—Œ î—˜î—î—¿î— : +î—î—‘î—‘î—° î—–î—Œ î—˜î—: + î—£î—î—žî—ºî— î—›î—î—‘î—‘î—šî—šî—–î—±î—î—î—“ + î—î—‘î—‘î—° î—›î—î—‘î—‘î—šî—šî—–î—±î—î—î—“ .txt +î—£î—î—žî—ºî— î—›î— î—¹î—î—î—Œ î—‹î—–î—‘î—¥î—î— + î—î—‘î—‘î—° î—›î— î—¹î—î—î—Œ î—‹î—–î—‘î—¥î—î— +6400 +î—¹î—î—‘î—‘î—±î—œî——î—¿î— +î—¹î—î—‘î—‘î—±î—œî——î—¿î— (&C): +î—î—î—î—“ +î—î—î—î—µî—î—Œ î—î—Œ î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ +î˜î˜€ï¼š +6600 +î—î—½î— î—»î—‘î—šî— +î—£î—î—žî—ºî— î—–î—Œ î—¹î—î—–î—¥î—î— +î—î—î—î—µî—î—Œ î—î—§ +î—î—§ +7100 +î—«î—‘î—šî—“ î˜î˜€ +î—î—Œ î—“ î—£î—î—šî— +î—«î—‘î—šî—“ î—«î—î—¹î˜î—¿î—‘î—¾î—î—° +î˜î—¬ +7200 +î—˜î—î—­î— +î—¤î—î—¿î—šî—šî—î—Œ î—‹î—î—žî—î—î—“ +î—î—î—î—“ +î—î—î—î—“ + +î—‹î—–î—µî—î—–î—î—î—î—î—î—î—“ +î—î—§ +7300 +î—î—‘î—‘î—° î—³î—î—î—î—î—“ +î—î—‘î—‘î—° î— (&S) î—¼î—–î—‘î—‘î— î—³î—î—î—î—î—“ : +î—‹î—žî—¥î—î—­î—î—° î—î—î—î—“ î—¾î—î—¥î—î— î—›î— (&V): +î—³î—î— î—›î— ... +î—³î—î—î—î—î—“ î—–î—“ + î—î—‘î—‘î—° î—î—Œ {0} î—¥î—î—µî—î—‘î— î—£î—–î—›î—‘î—¾î—î—î—î—“ î—–î—“ ? +î—‹î—žî—¥î—î—­î—î—° î—î—î—î—µî—î—Œ î—¾î—î—¥î—î— î—›î— î—˜î— î—‹î—–î—§ î—î—‘î—‘î—°  î—›î— î—›î—î—‘î—‘î—î—î—“ î—¥î—î—¿î—î—î— +î—‹î—žî—¥î—î—­î—î—° î—î—î—î—“ î—¾î—î—¥î—î— î—›î— î—˜î— î—œî——î—¿î—–î—šî—šî—“ +î—‹î—žî—¥î—î—­î—î—° î—î—î—î—“ î—¾î—î—¥î—î— î—›î— î— î—¹î—–î—î—î—ºî—î—šî—šî—î—î—î—“ :{0} î—‹î—–î—‘î—µî—–î—‘î— î—–î—Œ î—î—‘î— ã€‚\n î—¹î—î—¿î—î—šî—šî— î—î—‘î—‘î—° î— î—£î—–î—›î—‘î—¾î—î—î—î—“ î—–î—“ ? +7400 +î—î—‘î—‘î—° î—˜î—î—‘î—‘î—±î— +î—î—‘î—‘î—° î— (&S)  î—˜î—î—‘î—‘î—±î—: +î—˜î—î—‘î—‘î—±î— î—›î— ... +î—£î—–î—›î—‘î—¿î— î—‹î—žî—¥î—î—­î—î—° î—–î—Œ  î—î—‘î—‘î—° î— î—³î—–î—î—î—¿î—î— +î—£î—–î—›î—‘î—¿î— î—‹î—žî—¥î—î—­î—î—° î— î—¹î—î—šî—‘î—î—î—“ î—‹î—–î—‘î—¥î—î— + î—£î—–î—›î—‘î—¿î— î—‹î—žî—¥î—î—­î—î—° î—£î— î—î—î—µî—î—Œ î—‹î—–î—‘î—¥î—î— +7500 +î— î—›î— ... +î—î—î—î—µî—î—Œ î—î—§ +CRC  î—›î— î—î—î—: +CRC  î—›î— î—›î—î— î˜‚î—î—‘î—‘î—° î—–î—Œ î—˜î— î—î—î—: +7600 +î—î—° î— +î—¿î—î—¬ î—¥î—î—¿î—î— : +î—‹î—î—î—î—î—“ +î—¼î—î—ºî—î—±î—î—î—“ +  +î—¾î—î—¿î—–î—î—î— î—‹î—–î—šî—–î—¨î—Ž + + î—î—‘î— +CPU î—¥î—î—¿î—î—î—î—Œ î—¥î—î—î—Ž +î—¥î—î—¿î—î—î—ªî—»î— î—‘î—‘î—Œ  +î—›î—î—î—î—î—ºî—î—î—î—µî—î—Œ î—‹î— ï¼š diff --git a/Utils/7-Zip/Lang/mr.txt b/Utils/7-Zip/Lang/mr.txt new file mode 100644 index 000000000..0a4e5eb9e --- /dev/null +++ b/Utils/7-Zip/Lang/mr.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.42 : अनà¥à¤µà¤¾à¤¦ सà¥à¤¬à¥‹à¤§ गायकवाड (Subodh Gaikwad) +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Marathi +मराठी +401 +ठीक +रदà¥à¤¦ + + + +&हो +&नाही +&बंद +मदत + +&सà¥à¤°à¥ +440 +&सरà¥à¤µ ला हो +&सरà¥à¤µ ला नाही +थांबा +पà¥à¤¨à¥à¤¹à¤¾ सà¥à¤°à¥ करा +&मागे लपवा +&समोर आणा +&विशà¥à¤°à¤¾à¤® +विशà¥à¤°à¤¾à¤® +तà¥à¤®à¥à¤¹à¤¾à¤²à¤¾ रदà¥à¤¦ करणà¥à¤¯à¤¾à¤¬à¤¾à¤¬à¤¤ खातà¥à¤°à¥€ आहे का? +500 +&फ़ाइल +&संपादन +&दरà¥à¤¶à¤¨ +आ&वडते +&अवजार +&मदत +540 +&उघडा +&अंदर उघडा +&बाहेर उघडा +&दृशà¥à¤¯ +&संपादक +नाव बदल +&पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¥€... +&हलवा... +&मिटवा +&फ़ाइल तà¥à¤•डे करा... +फ़ाइल जोडा... +लकà¥à¤·à¤£à¤‚ +पà¥à¤°à¤¤à¤¿à¤•à¥à¤°à¤¿à¤¯à¤¾ + + +फ़ोलà¥à¤¡à¤° तयार करा +फ़ाइल तयार करा +गमन +600 +सरà¥à¤µ निवडा +सरà¥à¤µà¤¾à¤‚ना अनिवडित करा +&निवड उलटी करा +निवडा... +अनिवडा... +पà¥à¤°à¤•ारेदà¥à¤µà¤¾à¤°à¤¾ निवडा +पà¥à¤°à¤•ारेदà¥à¤µà¤¾à¤°à¥‡ अनिवडा +700 +मोठे Icons +लहान Icons +&सà¥à¤šà¥€ +&माहिती +730 +अवà¥à¤¯à¤µà¤¸à¥à¤¥à¤¿à¤¤ +Flat दृशà¥à¤¯ +&२ फ़लक +&अवजार कपà¥à¤ªà¤¾s +Root फ़ोलà¥à¤¡à¤° उघडा +à¤à¤• वरती चढा +फ़ोलà¥à¤¡à¤° इतिहास... +&टवटवीत करा +750 +दफ़तर अवजार कपà¥à¤ªà¤¾ +पà¥à¤°à¤®à¤¾à¤£ अवजार कपà¥à¤ªà¤¾ +मोठे कळ +कळ शबà¥à¤¦ दाखवा +800 +&फ़ोलà¥à¤¡à¤° आवडते मधà¥à¤¯à¥‡ टाका... +पृषà¥à¤  +900 +&परà¥à¤¯à¤¾à¤¯... +&Benchmark +960 +&माहिती... +7-Zip बदà¥à¤¦à¤²... +1003 +मारà¥à¤— +नाव +शेपà¥à¤Ÿ +फ़ोलà¥à¤¡à¤° +आकार +दबलेला आकार +गà¥à¤£à¤§à¤°à¥à¤® +तयार +वापर +बदल +ठोस +भाषà¥à¤¯ +बंधिसà¥à¤¤ +या पà¥à¤°à¥à¤µ तà¥à¤•डे करा +या नंतर तà¥à¤•डे करा +शबà¥à¤¦à¤¾à¤µà¤²à¥€ +CRC +पà¥à¤°à¤•ार +विरà¥à¤¦à¥à¤§ +पदà¥à¤§à¤¤ +यजमान आजà¥à¤žà¤¾à¤µà¤²à¥€ +फ़ाइल पà¥à¤°à¤£à¤¾à¤²à¥€ +उपयोगकरà¥à¤¤à¤¾ +गट +गठà¥à¤ à¤¾ +पà¥à¤°à¤¤à¤¿à¤•à¥à¤°à¤¿à¤¯à¤¾ +सà¥à¤¥à¤¿à¤¤à¥€ +मारà¥à¤—ाची सà¥à¤°à¥à¤µà¤¾à¤¤ + + + + + + + + + + + + + + + + + + + + + + + + +चà¥à¤• +à¤à¤•ूण आकार +खाली जागा +कà¥à¤²à¤¸à¥à¤Ÿà¤° आकार +शिरà¥à¤·à¤• +लोकल नाव +देणारा +2100 +परà¥à¤¯à¤¾à¤¯ +भाषा +भाषा: +संपादक +&संपादक: + +2200 +पà¥à¤°à¤£à¤¾à¤²à¥€ +7-Zip संबधित करा: +2301 +7-Zip ला shell context मेनà¥à¤¶à¥€ जोडा +Cascaded context menu +Context मेनॠवसà¥à¤¤à¥‚: +2320 + + +दफ़तर उघडा +फ़ाइलà¥à¤¸ बाहेर काढा... +दफ़तरात टाका... +दफ़तर तपासा +येथे बाहेर काढा +बाहेर {0} +{0} येथे टाका +दाबा आणि इमेल करा... +{0} येथे दाबा आणि इमेल करा +2400 +फ़ोलà¥à¤¡à¤° +&चलित फ़ोलà¥à¤¡à¤° +&पà¥à¤°à¤£à¤¾à¤²à¥€à¤šà¥‡ तातà¥à¤ªà¥à¤°à¤¤à¥‡ फ़ोलà¥à¤¡à¤° +&सधà¥à¤¯à¤¾ +&नमà¥à¤¦: +फकà¥à¤¤ काढता येणायà¥à¤°à¤¾ डà¥à¤°à¥‰à¤ˆà¤µà¥à¤¹à¤•रता वापरा +तातà¥à¤ªà¥à¤°à¤¤à¥à¤¯à¤¾ दफ़तर करिता मारà¥à¤— दरà¥à¤¶à¤µà¤¾. +2500 +सà¥à¤¥à¤¿à¤¤à¥€ +वसà¥à¤¤à¥‚ ".." दाखवा +फ़ाइलचे खरे icon दाखवा +पà¥à¤°à¤£à¤¾à¤²à¥€à¤šà¥‡ मेनॠदाखवा +&सरà¥à¤µ ओळ निवडा +&grid रेघा दाखवा + +&अतिरिकà¥à¤¤ निवड पदà¥à¤§à¤¤à¥€ +मोठे सà¥à¤®à¤°à¤£à¤¶à¤•à¥à¤¤à¥€ पृषà¥à¤  वापरा +2900 +7-Zip बदà¥à¤¦à¤² माहिती +7-Zip हे मोफ़त सॉफ़à¥à¤Ÿà¤µà¥‡à¤…र आहे. तरिही, तà¥à¤®à¥à¤¹à¥€ नोंद करà¥à¤¨ याचà¥à¤¯à¤¾ पà¥à¤°à¤—तीला सहायà¥à¤¯ करू शकता. +3000 + +येथे à¤à¤•ही चूक नाही +निवडलेलà¥à¤¯à¤¾ वसà¥à¤¤à¥‚{0} +'{0}' फ़ोलà¥à¤¡à¤° तयार होऊ शकले नाही +या दफ़तरासाठी नà¥à¤¤à¤¨à¥€à¤•रण शकà¥à¤¯ नाही. + + + + +'{0}' ही फ़ाइल बदलली आहे.\nतà¥à¤®à¥à¤¹à¤¾à¤²à¤¾ हे दफ़तरात नà¥à¤¤à¤¨ करायचे आहे का? +फ़ाइल नà¥à¤¤à¤¨ करता येत नाही\n'{0}' +संपादक सà¥à¤°à¥ होत नाही. + + + + +खूपच जासà¥à¤¤ वसà¥à¤¤à¥‚ +3300 +बाहेर +दाब +तपासणी +उघडत आहे... +बारकाईने पाहत आहे... +3400 +बाहेर +&बाहेर: +फ़ाइल बाहेर काढणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ मारà¥à¤— नमूद करा. +3410 +मारà¥à¤— रित +पूरà¥à¤£ पतà¥à¤¤à¤¾ +पतà¥à¤¤à¤¾ नाही +3420 +Overwrite रीत +overwrite करणà¥à¤¯à¤¾à¤ªà¥à¤°à¥à¤µà¥€ विचारा +Overwrite न विचारता करा +असà¥à¤¤à¤¿à¤¤à¥à¤µà¤¾à¤¤ असलेलà¥à¤¯à¤¾ फ़ाइल सोडा +सà¥à¤µà¤¯à¤‚चलित नाव बदलवा +असà¥à¤¤à¤¿à¤¤à¥à¤µà¤¾à¤¤ असलेलà¥à¤¯à¤¾ फ़ाइलचे आपोआप नाव बदलवा +3500 +फ़ाइल बदलवीणे खातà¥à¤°à¥€ +तà¥à¤¯à¤¾ फ़ोलà¥à¤¡à¤°à¤®à¤§à¥à¤¯à¥‡ अगोदरच कारà¥à¤¯à¤¾à¤¨à¥à¤µà¥€à¤¤ फ़ाइल आहे. +असà¥à¤¤à¤¿à¤¤à¥à¤µà¤¾à¤¤ असलेली फ़ाइल बदलवायची आहे का? +यानी? +{0} बाइटà¥à¤¸ +सà¥à¤µà¤¯à¤‚चलित नाव बदलवा +3700 +असहायà¥à¤¯à¤• दाब पदà¥à¤§à¤¤'{0}'. +डेटा चूक'{0}'. फ़ाइल तà¥à¤Ÿà¤²à¥‡à¤²à¥€ आहे. +CRC अयशसà¥à¤µà¥€ '{0}'. फ़ाइल तà¥à¤Ÿà¤²à¥‡à¤²à¥€ आहे. + + +3800 +परवलिचा शबà¥à¤¦ टाका +परवलिचा शबà¥à¤¦ टाका: + +परवलिचा शबà¥à¤¦ दाखवा + + + +परवलिचा शबà¥à¤¦ +3900 +à¤à¤¾à¤²à¥‡à¤²à¤¾ वेळ: +निघà¥à¤¨ गेलेला वॆळ: +आकार: +वेग: + + +चूक: + +4000 +दफ़तरात टाका +&दफ़तर: +&नà¥à¤¤à¤¨à¥€à¤•रन रित: +दफ़तर &पà¥à¤°à¤•ार: +दाब &level: +दाब &पदà¥à¤§à¤¤: +&शबà¥à¤¦à¥à¤•ोश आकार: +&शबà¥à¤¦ आकार: + + +&Parameters: +परà¥à¤¯à¤¾à¤¯ +SF&X दफ़तर तयार करा + + + +फ़ाइल &नाव बंधिसà¥à¤¤ करा +दाबणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ सà¥à¤®à¤°à¤£à¤¶à¤•à¥à¤¤à¥€à¤šà¤¾ वापर: +पà¥à¤°à¤¸à¤°à¤£ पावणà¥à¤¯à¤¾à¤¸à¤¾à¤ à¥€ सà¥à¤®à¤°à¤£à¤¶à¤•à¥à¤¤à¥€à¤šà¤¾ वापर: +4050 +साठा +अतिशय वेगवान +वेगवान +साधारण +जासà¥à¤¤à¥€à¤¤ जासà¥à¤¤ +à¤à¤•दमच +4060 +फ़ाइल टाका आणि ठेवा +फ़ाइल टाका आणि नà¥à¤¤à¤¨ करा +असà¥à¤¤à¤¿à¤¤à¥à¤µà¤¾à¤¤à¥€à¤² फ़ाइल ताजे करा +Synchronize फ़ाइल +4070 +बà¥à¤°à¤¾à¤‰à¤ +सरà¥à¤µ फ़ाइल + + +6000 +पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¥€ +हलवा +पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¥€: +हलवा: +पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¥€... +हलवलà¥à¤¯à¤¾ जात आहे... +नविन नाव दिलà¥à¤¯à¤¾ जात आहे... + +कà¥à¤°à¤¿à¤¯à¤¾ करता येणार नाही. +फ़ाइल किंवा फ़ोलà¥à¤¡à¤°à¤²à¤¾ नविन नाव देता येत नाही आहे +फ़ाइलची पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¥€ करणà¥à¤¯à¤¾à¤¸ तà¥à¤®à¤šà¥€ खातà¥à¤°à¥€ आहे का +दफ़à¥à¤¤à¤°à¤¾à¤¤ फ़ाइलची पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¥€ करणà¥à¤¯à¤¾à¤¸ तà¥à¤®à¤šà¥€ खातà¥à¤°à¥€ आहे का +6100 +फ़ाइल मिटवायची खातà¥à¤°à¥€ +फ़ोलà¥à¤¡à¤° मिटवायची खातà¥à¤°à¥€ +अनेक फ़ाइल मिटवायची खातà¥à¤°à¥€ +तà¥à¤®à¥à¤¹à¤¾à¤²à¤¾ '{0}' मिटवायची खातà¥à¤°à¥€ आहे का? +तà¥à¤®à¥à¤¹à¤¾à¤²à¤¾ '{0}' फ़ोलà¥à¤¡à¤° आणि तà¥à¤¯à¤¾à¤¤à¥€à¤² सरà¥à¤µ वसà¥à¤¤à¥ मिटवायची खातà¥à¤°à¥€ आहे का? +तà¥à¤®à¥à¤¹à¤¾à¤²à¤¾ {0} वसà¥à¤¤à¥ मिटवायची खातà¥à¤°à¥€ आहे का? +मिटत आहे... +फ़ाइल किंवा फ़ोलà¥à¤¡à¤° मिटवता येत नाही आहे + +6300 +फ़ॊलà¥à¤¡à¤° तयार करा +फ़ाइल तयार करा +फ़ोलà¥à¤¡à¤° नाव: +फ़ाइलचे नाव: +नविन फ़ॊलà¥à¤¡à¤° +नविन फ़ाइल +फ़ोलà¥à¤¡à¤° तयार करता येत नाही आहे +फ़ाइल तयार करता येत नाही आहे +6400 +पà¥à¤°à¤¤à¤¿à¤•à¥à¤°à¤¿à¤¯à¤¾ +&पà¥à¤°à¤¤à¤¿à¤•à¥à¤°à¤¿à¤¯à¤¾: +निवडा +निवड रदà¥à¤¦ +मà¥à¤–वटा: +6600 + +फ़ोलà¥à¤¡à¤°à¤šà¤¾ इतिहास +उपचार संदेश +संदेश +7100 +संगणक +नेटवरà¥à¤• + +पà¥à¤°à¤£à¤¾à¤²à¥€ +7200 +टाका +बाहेर +तपासा +पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¥€ +हलवा +मिटवा +माहिती +7300 +फ़ाइलचे तà¥à¤•डे करा +&येथे तà¥à¤•डे: +तà¥à¤•डे, बाइटà¥à¤¸: +तà¥à¤•डे होत आहे... + + + + + +7400 +फ़ाइल जोडा +&येथे फ़ाइल जोडा: +फ़ाइल जà¥à¤¡à¤¤ आहे... + + + +7500 +Checksum मोजत आहे... +Checksum माहिती +डेटाकरिता CRC checksum : +नाव आणि डेटाकरिता CRC checksum : +7600 +Benchmark +सà¥à¤®à¤°à¤£à¤¶à¤•à¥à¤¤à¥€ वापर: +दाबत आहे +पà¥à¤°à¤¸à¤°à¤£ होत आहे +कà¥à¤°à¤®à¤¾à¤‚कन +à¤à¤•à¥à¤£ कà¥à¤°à¤®à¤¾à¤‚कन +सधà¥à¤¯à¤¾ +परिणाम + + +Passes: diff --git a/Utils/7-Zip/Lang/ms.txt b/Utils/7-Zip/Lang/ms.txt new file mode 100644 index 000000000..19285ca43 --- /dev/null +++ b/Utils/7-Zip/Lang/ms.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.30 : Khairul Ridhwan Bin Omar +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Malay +Bahasa Melayu +401 +OK +Batal + + + +&Ya +&Tidak +&Tutup +Bantuan + +&Teruskan +440 +Ya untuk Semua +Tidak untuk Semua +Henti +Mula Semula +&Latar belakang +&Latar depan +&Berehat +Berehat +Anda yakin untuk membatalkannya? +500 +&Fail +&Edit +&Paparan +K&egemaran +&Alat +&Bantuan +540 +&Buka +Buka di D&alam +Buka di L&uar +&Paparan +&Edit +Nam&akan semula +&Salin ke... +&Pindahkan ke... +Hapus +&Bahagi/belah Fail... +Gab&ung Fail... +P&roperti +Kom&en + + +Buat Folder +Buat Fail +K&eluar +600 +Pilih &Semua +Jangan Pilih Semua +&Sonsangkan Pilihan +Pilih... +Tidak Memilih... +Pilih Berdasarkan Jenis +Tidak Memilih Berdasarkan Jenis +700 +Ikon B&esar +Ikon K&ecil +&Senarai +&Butiran +730 +Tidak Tersusun + +&2 Panel +&Toolbar +Buka Root Folder +Ke atas Satu Aras +Folder Sejarah... +&Segarkan Semula +750 +Toolbar Arkib +Toolbar Standard +Bebutang Besar +Perlihatkan Teks Bebutang +800 +&Tambah folder pada Kegemaran sebagai +Penanda Buku +900 +&Opsyen... +&Tanda Aras +960 +&Kandungan... +&Perihal 7-Zip... +1003 +Bahagian +Nama +Sambungan +Folder +Saiz +Saiz Paket +Atribut +Dibuat +Diakses +Diubah Suai +Solid +Komen +Terenkripsi +Terpisah Sebelum +Terpisah Selepas +Kamus +CRC +Jenis +Anti +Kaedah +Sistem Operasi +Sistem Fail +Pengguna +Kumpulan +Blok +Komen +Posisi + + + + + + + + + + + + + + + + + + + + + + + + + +Ralat +Saiz Keseluruhan +Ruang Kosong +Saiz Kluster +Label +Nama Tempatan +Penyedia +2100 +Opsyen +Bahasa +Bahasa: +Editor +&Editor: + +2200 +Sistem +Kongsikan 7-Zip dengan: +2301 +Integrasikan 7-Zip ke shell konteks menu +Cascaded konteks menu +Item pada konteks menu: +2320 + + +Buka arkib +Ekstrak fail... +Tambahkan ke arkib... +Uji arkib +Ekstrak di sini +Ekstrak ke {0} +Tambahkan ke {0} +Padatkan dan kirimkan melalui email... +Padatkan ke {0} dan kirimkan melalui email +2400 +Folder +&Folder kerja +&Folder sementara sistem +&Sekarang +&Ditentukan: +Hanya untuk pemacu mudah alih +Tentukan lokasi untuk arkib fail sementara. +2500 +Seting +Perlihatkan ".." item +Perlihatkan ikon asli dari fail +Perlihatkan menu sistem +&Pilih barisan penuh +Perlihatkan garisan grid + +&Mod Pilihan Alternatif +Gunakan muka surat memori yang &besar +2900 +Perihal 7-Zip +7-Zip adalah perisian percuma. Sokong pembangunan 7-Zip dengan melakukan pendaftaran. +3000 + +Tidak ada ralat +{0} buah objek telah terpilih +Tidak dapat membuat folder '{0}' +Tidak menyokong pengemaskinian untuk arkib ini. + + + + +Fail '{0}' telah terubah suai.\nApakah anda ingin mengemaskininya pada arkib? +Tidak dapat mengemaskini fail\n'{0}' +Tidak dapat membuka editor. + + + + +Terlalu banyak item +3300 +Sedang mengekstrak +Memampatkan +Pengujian +Membuka... + +3400 +Ekstrak +Ekstrak ke: +Tentukan lokasi untuk pengekstrakan fail. +3410 +Mod laluan +Laluan nama penuh +Tidak pakai nama laluan +3420 +Mod tulis semula +Tanya sebelum menulis semula +Tulis semula tanpa perlu diberitahu +Abaikan fail yang ada +Namakan semula automatik +Namakan automatik fail yang ada +3500 +Pastikan penggantian fail +Folder tujuan telah berisi fail yang telah terproses. +Mahukah anda menggantikan fail yang ada +dengan yang ini? +{0} baits +N&amakan semula Automatik +3700 +Kaedah pemampatan untuk '{0}' tidak disokong. +Data ralat di '{0}'. Fail ini rosak. +CRC gagal di '{0}'. Fail ini rosak. + + +3800 +Masukkan kata laluan +Masukkan kata laluan: + +&Perlihatkan kata laluan + + + +Kata laluan +3900 +Telah berlalu: +Selesai dalam: +Saiz: +Kecepatan: + + +Ralat: + +4000 +Tambahkan ke arkib +&Arkib: +&Mod kemaskini: +Format arkib: +Aras &mampatan: +Kaedah mampatan: +&Saiz kamus: +&Saiz perkataan: + + +&Parameter: +Opsyen +Buat arkib SF&X + + + +Enkripsi nama &fail +Penggunaan memori untuk Memampatkan: +Penggunaan memori untuk Menyah-mampatkan: +4050 +Untuk Penyimpanan +Lebih cepat +Cepat +Normal +Maksimum +Ultra +4060 +Tambah dan gantikan fail +Kemaskini dan tambahkan fail +Perbaharui fail yang ada +Menyesuaikan fail +4070 +Selusur... +Semua Fail + + +6000 +Salin +Pindah +Salin ke: +Pindah ke: +Sedang menyalin... +Sedang memindah... +Namakan semula... + +Operasi tidak disokong. +Ralat, ketika namakan semula Fail atau Folder +Pasti salinkan fail +Anda yakin untuk menyalinkan fail kepada arkib +6100 +Pasti penghapusan fail +Pasti penghapusan folder +Pasti penghapusan fail-fail +Anda yakin untuk menghapus '{0}'? +Anda yakin untuk menghapus folder '{0}' dan semua isi kandungannya? +Anda yakin untuk menghapus item {0}? +Penghapusan... +Ralat ketika menghapuskan Fail atau Folder + +6300 +Buat Folder +Buat Fail +Nama Folder: +Nama Fail: +Folder Baru +Fail Baru +Ralat, tidak dapat Membuat Folder +Ralat, tidak dapat Membuat Fail +6400 +Komen +&Komen: +Pilih +Tidak Memilih +Topeng: +6600 + +Folder Sejarah +Mesej diagnostik +Mesej +7100 +Komputer +Rangkaian + +Sistem +7200 +Tambah +Ekstrak +Uji +Salin +Pindah +Hapus +Maklumat +7300 +Pisahkan Fail +&Pisahkan ke: +Bahagi/belah ke &nilai, baits: +Pembelahan ... + + + + + +7400 +Gabungan Fail +&Gabung ke: +Penggabungan ... + + + +7500 + + + + +7600 +Tanda Aras +Penggunaan memori: +Pemampatan +Penyah-mampatan +Rating +Total Rating +Sekarang +Keputusan + + +Lulus: diff --git a/Utils/7-Zip/Lang/nb.txt b/Utils/7-Zip/Lang/nb.txt new file mode 100644 index 000000000..c90522a41 --- /dev/null +++ b/Utils/7-Zip/Lang/nb.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.45 : Jostein Christoffer Andersen +; : Kjetil Hjartnes +; : Robert Grønning +; +; +; +; +; +; +; +; +0 +7-Zip +Norwegian Bokmal +Norsk BokmÃ¥l +401 +OK +Avbryt + + + +&Ja +&Nei +&Lukk +Hjelp + +&Fortsett +440 +Ja til &alt +Nei til a< +Stopp +Start pÃ¥ nytt +&Bakgrunn +&Forgrunn +&Stopp +Stoppet +Vil du avbryte? +500 +&Fil +&Rediger +&Vis +&Bokmerker +Verk&tøy +&Hjelp +540 +&Ã…pne +Ã…pne &internt +Ã…pne &eksternt +&Vis +&Rediger +Gi nytt &navn +&Kopier til … +&Flytt til … +S&lett +&Del opp arkiv … +&Sett sammen arkiv … +E&genskaper +&Kommentar … +Beregn sjekksum + +Ny &mappe … +Ny f&il … +&Avslutt +600 +Merk &alle +Merk i&ngen +Merk &omvendt +Merk … +Merk &ikke … +Merk &valgt type +Merk i&kke valgt type +700 +&Store ikoner +S&mÃ¥ ikoner +&Liste +&Detaljer +730 +Usortert +&Flat visning +&To felt +&Verktøylinjer +Rotmappe +GÃ¥ opp et nivÃ¥ +Mappelogg … +&Oppdater +750 +Arkivverktøylinje +Standardverktøylinje +Store knapper +Knappetekst +800 +&Bokmerk denne mappen som +Bokmerke +900 +&Innstillinger … +&Yteprøve … +960 +&Innhold +&Om 7-Zip +1003 +Plassering +Navn +Filetternavn +Mappe +Størrelse +Komprimert størrelse +Attributter +Opprettet +Ã…pnet +Endret +Kompakt +Kommentert +Kryptert +Oppdeling før +Oppdeling etter +Ordbok +CRC +Type +Anti +Metode +Vert-OS +Filsystem +Bruker +Gruppe +Blokk +Kommentar +Posisjon +Stiprefiks + + + + + + + + + + + + + + + + + + + + + + + + +Feil +Total plass +Ledig plass +Sektorgruppestørrelse +Etikett +Lokalt navn +Forsyner +2100 +Innstillinger +SprÃ¥k +SprÃ¥k: +Redigering +&Redigeringsprogram: + +2200 +System +Assosier 7-Zip med: +2301 +Integrer 7-Zip i programmenyen +Forgrenet programmeny +Valg i programmenyen: +2320 + + +Ã…pne arkiv +Pakk ut … +Legg til arkiv … +Test arkiv +Pakk ut internt +Pakk ut til {0} +Legg til {0} +Komprimer og send med e-post … +Komprimer til {0} og send med e-post +2400 +Mapper +&Arbeidsmappe +Systemets &midlertidige mappe +&NÃ¥værende +&Egendefinert: +Kun for flyttbare stasjoner +Angi plassering for midlertidige filer. +2500 +Innstillinger +Vis element for Ã¥ gÃ¥ opp et &nivÃ¥ +Vis egentlige fil&ikoner +Vis system&meny +Merk &hele rader +Vis &rutenett + +&Alternativ merking +Bruk &store minnesider +2900 +Om 7-Zip +7-Zip er gratis programvare, men du kan støtte utviklingen av 7-Zip ved Ã¥ registrere deg. +3000 + +Fant ingen feil. +{0} element(er) merket +Klarte ikke opprette mappen «{0}» +Oppdateringsfunksjoner støttes ikke for dette arkivet. +Kan ikke Ã¥pne filen «{0}» som arkiv +Kan ikke Ã¥pne det krypterte arkivet «{0}». Sjekk at du har riktig passord. + + +Filen «{0}» har blitt endret.\nVil du oppdatere den i arkivet? +Klarte ikke oppdatere filen\n«{0}» +Klarte ikke starte redigeringsprogram. + + + + +For mange elementer +3300 +Pakker ut +Komprimerer +Testing +Ã…pner … +Skanner … +3400 +Pakk ut +&Pakk ut til: +Angi plassering for filer som skal pakkes ut. +3410 +Filstier +Fullstendige filstier +Ingen filstier +3420 +Overskrivelse +Bekreft før overskrivelse +Overskriv uten bekreftelse +Hopp over filer som finnes allerede +Navngi nye filer automatisk +Navngi filer som finnes automatisk +3500 +Bekreft filoverskrivelse +Den behandlede filen finnes i mÃ¥lmappen allerede. +Vil du overskrive filen +med denne? +{0} byte +Navngi a&utomatisk +3700 +Komprimeringsmetoden støttes ikke for «{0}». +Datafeil i «{0}». Filen er ødelagt. +CRC-feil i «{0}». Filen er ødelagt. +Datafeil i den krypterte filen «{0}». Sjekk at du har riktig passord. +CRC feilet i den krypterte filen «{0}». Sjekk at du har riktig passord. +3800 +Angi passord +Angi passord: +Bekreft passord: +&Vis passord +Passordene er ikke like +Du kan bare bruke engelske bokstaver, tall eller spesialtegn (!, #, $, …) i passordet +Passordet er for langt +Passord +3900 +Tidsforbruk: +Gjenværende tid: +Størrelse: +Hastighet: + + +Feilet: + +4000 +Legg til arkiv +Filn&avn: +&Oppdateringsmetode: +&Format: +Komprimerings&nivÃ¥: +Komprimerings&metode: +Ord&bokstørrelse: +&Ordstørrelse: +Solid blokk størrelse: +Antall CPU trÃ¥der: +&Parametre: +&Innstillinger +Selvutpakkende arkiv («SF&X») + +Kryptering +Krypteringsmetode: +Kr&ypter filnavn +Minnebruk ved komprimering: +Minnebruk ved dekomprimering: +4050 +Ukomprimert +Raskest +Rask +Normal +Maksimum +Ultra +4060 +Legg til og overskriv filer +Oppdater og legg til filer +Oppdater filer +Synkroniser filer +4070 +Bla gjennom +Alle filer +Ikkje-solid +Solid +6000 +Kopier +Flytt +Kopier til: +Flytt til: +Kopierer … +Flytter … +Navngir … +Velg mÃ¥lmappe. +Operasjonen støttes ikke. +Det oppstod en feil da filen eller mappen skulle navngis +Bekreft at fil skal kopieres +Vil du kopiere filene til arkivet +6100 +Bekreft at fil skal slettes +Bekreft at mappe skal slettes +Bekreft at flere filer skal slettes +Vil du slette «{0}»? +Vil du slette mappen «{0}» med alt innhold? +Vil du slette disse {0} elementene? +Sletter … +Det oppstod en feil da filen eller mappen skulle slettes + +6300 +Ny mappe +Ny fil +Mappenavn: +Filnavn: +Ny mappe +Ny fil +Det oppstod en feil da mappen skulle opprettes +Det oppstod en feil da filen skulle opprettes +6400 +kommentar +&Kommentar: +Merk +Merk ikke +Filter: +6600 + +Mappelogg +Diagnosemeldinger +Melding +7100 +Datamaskin +Nettverk + +System +7200 +Legg til +Pakk ut +Prøv +Kopier +Flytt +Slett +Egenskaper +7300 +Del opp arkiv +&Del opp som: +&Del opp til flere delarkiv i størrelsen: +Deler opp … +Bekreft deling +Er du sikker pÃ¥ at du vil dele arkivet i {0} delarkiv? +Delarkivene mÃ¥ være mindre enn originalarkivet +Ugyldig delarkivstørrelse +Valgt delarkivstørrelse: {0} byte.\nEr du sikker pÃ¥ at du vil dele arkivet med denne størrelsen? +7400 +Sett sammen arkiv +&Sett sammen som: +Setter sammen … +Velg bare det første delarkivet + + +7500 +Beregner sjekksum … +Sjekksuminformasjon +CRC-sjekksum for data: +CRC-sjekksum for data og filnavn: +7600 +Yteprøve +Minnebruk: +Komprimering +Dekomprimering +Ytelse +Samlet ytelse +NÃ¥værende +Resultat +CPU bruk +Ytelse / Bruk +BestÃ¥tt: diff --git a/Utils/7-Zip/Lang/ne.txt b/Utils/7-Zip/Lang/ne.txt new file mode 100644 index 000000000..7c3f29cb8 --- /dev/null +++ b/Utils/7-Zip/Lang/ne.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.37 : Shiva Pokharel, Mahesh Subedi +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Nepali +नेपाली +401 +ठीक छ +रदà¥à¤¦ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ + + + +&हो +&होइन +&बनà¥à¤¦ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +मदà¥à¤¦à¤¤ + +&जारी राखà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +440 +&सबैलाई हो +&कसैलाई होइन +रोकà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +पà¥à¤¨: सà¥à¤°à¥ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +&पृषà¥à¤ à¤­à¥à¤®à¤¿ +&अगà¥à¤°à¤­à¥à¤®à¤¿ +&पज गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +पज गरिà¤à¤•ो +तपाईठरदà¥à¤¦ गरà¥à¤¨ यकिन हà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤› ? +500 +&फाइल +&समà¥à¤ªà¤¾à¤¦à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +&हेरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +&रà¥à¤šà¤¾à¤‡à¤à¤•ो +&उपकरण +&मदà¥à¤¦à¤¤ +540 +&खोलà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +भितà¥à¤°à¤ªà¤Ÿà¥à¤Ÿà¤¿ खोलà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +बाहरिपटà¥à¤Ÿà¤¿ खोलà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +&हेरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +&समà¥à¤ªà¤¾à¤¦à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +पà¥à¤¨: नामकरण गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +&यसमा पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ बनाउनà¥à¤¹à¥‹à¤¸à¥... +&यसमा सारà¥à¤¨à¥à¤¹à¥‹à¤¸à¥... +&मेटà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +&फाइल विभाजन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥... +फाइलहरू संयोजन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥... +&गà¥à¤£ +टिपà¥à¤ªà¤£à¥€ +checksum गणना गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ + +फोलà¥à¤¡à¤° सिरà¥à¤œà¤¨à¤¾ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +फाइल सिरà¥à¤œà¤¨à¤¾ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +निसà¥à¤•नà¥à¤¹à¥‹à¤¸à¥ +600 +सबै चयन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +सबै मेटà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +&चयन उलà¥à¤Ÿà¤¾à¤‰à¤¨à¥à¤¹à¥‹à¤¸à¥ +चयन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥... +चयन हटाउनà¥à¤¹à¥‹à¤¸à¥... +पà¥à¤°à¤•ार अनà¥à¤¸à¤¾à¤° चयन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +पà¥à¤°à¤•ार अनà¥à¤¸à¤¾à¤° चयन हटाउनà¥à¤¹à¥‹à¤¸à¥ +700 +ठूलो पà¥à¤°à¤¤à¤¿à¤®à¤¾ +सानो पà¥à¤°à¤¤à¤¿à¤®à¤¾ +&सूची +&वरà¥à¤£à¤¨ +730 +कà¥à¤°à¤®à¤¬à¤¦à¥à¤§ नगरिà¤à¤•ो +फà¥à¤²à¥à¤¯à¤¾à¤Ÿ दृशà¥à¤¯ +&२ पà¥à¤¯à¤¾à¤¨à¤² +&उपकरणपटà¥à¤Ÿà¥€ +पà¥à¤°à¤®à¥‚ल फोलà¥à¤¡à¤° खोलà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +à¤à¤• सà¥à¤¤à¤° माथि +फोलà¥à¤¡à¤°à¤•ो इतिहार... +&ताजा गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +750 +सङà¥à¤—à¥à¤°à¤¹ उपकरणपटà¥à¤Ÿà¥€ +मानक उपकरणपटà¥à¤Ÿà¥€ +ठूलो बटन +बटनको पाठ देखाउनà¥à¤¹à¥‹à¤¸à¥ +800 +&यस रूपमा रूचाइà¤à¤•ोमा फोलà¥à¤¡à¤° थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +पà¥à¤¸à¥à¤¤à¤•चिनो +900 +&विकलà¥à¤ª... +&बेञà¥à¤šà¤®à¤¾à¤°à¥à¤• +960 +&सामगà¥à¤°à¥€... +&7-जिपका बारेमा... +1003 +मारà¥à¤— +नाम +विसà¥à¤¤à¤¾à¤° +फोलà¥à¤¡à¤° +साइज +पà¥à¤¯à¤¾à¤• गरिà¤à¤•ो साइज +विशेषता +सिरà¥à¤œà¤¿à¤¤ +पहà¥à¤à¤š +परिमारà¥à¤œà¤¿à¤¤ +ठोस +टिपà¥à¤ªà¤£à¥€ +गà¥à¤ªà¥à¤¤à¤¿à¤•ृत +यस पहिले विभाजन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +यस पछि विभाजन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +शबà¥à¤¦à¤•ोश +CRC +पà¥à¤°à¤•ार +बिरोधि +विधि +होसà¥à¤Ÿ OS +फाइल पà¥à¤°à¤£à¤¾à¤²à¥€ +पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾ +समूह +रोक +टिपà¥à¤ªà¤£à¥€ +सà¥à¤¥à¤¾à¤¨ +मारà¥à¤— पà¥à¤°à¤¤à¥à¤¯à¤¯ + + + + + + + + + + + + + + + + + + + + + + + + +तà¥à¤°à¥à¤Ÿà¤¿ +जमà¥à¤®à¤¾ साइज +सà¥à¤µà¤¤à¤¨à¥à¤¤à¥à¤° रिकà¥à¤¤à¤¸à¥à¤¥à¤¾à¤¨ +समूह साइज +लेबà¥à¤² +सà¥à¤¥à¤¾à¤¨à¤¿à¤¯ नाम +पà¥à¤°à¤¦à¤¾à¤¯à¤• +2100 +विकलà¥à¤ª +भाषा +भाषा: +समà¥à¤ªà¤¾à¤¦à¤• +&समà¥à¤ªà¤¾à¤¦à¤•: + +2200 +पà¥à¤°à¤£à¤¾à¤²à¥€ +यससà¤à¤— 7-जिप समà¥à¤¬à¤¨à¥à¤§à¤¿à¤¤: +2301 +शेल पà¥à¤°à¤¸à¤™à¥à¤— मेनà¥à¤®à¤¾ 7-जिप लाई समà¥à¤®à¤¿à¤²à¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +सोपानी पà¥à¤°à¤¸à¤™à¥à¤— मेनॠ+पà¥à¤°à¤¸à¤™à¥à¤— मेनॠवसà¥à¤¤à¥: +2320 + + +सङà¥à¤—à¥à¤°à¤¹ खोलà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +फाइलहरू निकालà¥à¤¨à¥à¤¹à¥‹à¤¸à¥... +सङà¥à¤—à¥à¤°à¤¹à¤®à¤¾ थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥... +सङà¥à¤—à¥à¤°à¤¹ जाà¤à¤šà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +यहाठनिकालà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +{0} मा निकालà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +{0} मा थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +सङà¥à¤•à¥à¤šà¤¨ गरेर इमेल गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥... +{0} मा सङà¥à¤•à¥à¤šà¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ र इमेल गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +2400 +फोलà¥à¤¡à¤° +&कारà¥à¤¯ फोलà¥à¤¡à¤° +&पà¥à¤°à¤£à¤¾à¤²à¥€ टेमà¥à¤ª(असà¥à¤¥à¤¾à¤¯à¥€) फोलà¥à¤¡à¤° +&चालू +&निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ: +हटाउन योगà¥à¤¯ डà¥à¤°à¤¾à¤‡à¤­à¤¹à¤°à¥‚का लागि मातà¥à¤° पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +असà¥à¤¥à¤¾à¤¯à¥€ सङà¥à¤—à¥à¤°à¤¹ फाइलका लागि सà¥à¤¥à¤¾à¤¨ तोकà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ । +2500 +सेटिङ +".." वसà¥à¤¤à¥ देखाउनà¥à¤¹à¥‹à¤¸à¥ +वासà¥à¤¤à¤µà¤¿à¤• फाइल पà¥à¤°à¤¤à¤¿à¤®à¤¾ देखाउनà¥à¤¹à¥‹à¤¸à¥ +पà¥à¤°à¤£à¤¾à¤²à¥€ मेनॠदेखाउनà¥à¤¹à¥‹à¤¸à¥ +&पूरा पङà¥à¤•à¥à¤¤à¤¿ चयन +गà¥à¤°à¤¿à¤¡ रेखा देखाउनà¥à¤¹à¥‹à¤¸à¥ + +&वैकलà¥à¤ªà¤¿à¤• चयन मोड +ठूलो सà¥à¤®à¥ƒà¤¤à¤¿ पृषà¥à¤  पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +2900 +7-जिपका बारेमा +7-जिप निशà¥à¤²à¥à¤• सफà¥à¤Ÿà¤µà¥‡à¤¯à¤° हो। यदà¥à¤¯à¤ªà¥€,तपाईà¤à¤²à¥‡ दरà¥à¤¤à¤¾ गरेर 7-जिपलाई सहयोग गरà¥à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤› । +3000 + +तà¥à¤¯à¤¹à¤¾à¤ तà¥à¤°à¥à¤Ÿà¤¿ छैन +{0} वसà¥à¤¤à¥(हरू) चयन गरियो +'{0}'फोलà¥à¤¡à¤° सिरà¥à¤œà¤¨à¤¾ गरà¥à¤¨ सकिदैन +यो सङà¥à¤—à¥à¤°à¤¹à¤•ा लागि अदà¥à¤¯à¤¾à¤µà¤§à¤¿à¤• सञà¥à¤šà¤¾à¤²à¤¨ समरà¥à¤¥à¤¨ गरà¥à¤¦à¥ˆà¤¨ + + + + +'{0}'फाइल परिमारà¥à¤œà¤¨ गरिà¤à¤•ो छ ।\nतपाईà¤à¤²à¥‡ यसलाई सङà¥à¤—à¥à¤°à¤¹à¤®à¤¾ अदà¥à¤¯à¤¾à¤µà¤§à¤¿à¤• गरà¥à¤¨ चाहनà¥à¤¹à¥à¤¨à¥à¤› ? +फाइल अदà¥à¤¯à¤¾à¤µà¤§à¤¿à¤• गरà¥à¤¨ सकिदैन\n'{0}' +समà¥à¤ªà¤¾à¤¦à¤• सà¥à¤°à¥ गरà¥à¤¨ सकिदैन + + + + +धेरै वसà¥à¤¤à¥ +3300 +निकालà¥à¤¦à¥ˆà¤› +सङà¥à¤•à¥à¤šà¤¨ +परीकà¥à¤·à¤£ +खोलà¥à¤¦à¥ˆà¤›... +सà¥à¤•à¥à¤¯à¤¾à¤¨à¤¿à¤™... +3400 +निकालà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +यसलाई निकालà¥à¤¨à¥à¤¹à¥‹à¤¸à¥: +निकालिà¤à¤•ो फाइलका लागि सà¥à¤¥à¤¾à¤¨ निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +3410 +मारà¥à¤— मोड +पूरा मारà¥à¤—नाम +मारà¥à¤—नामहरू छैन +3420 +अधिलेखन मोड +अधिलेखन गरà¥à¤¨à¥ अगाडि सोधà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +पà¥à¤°à¥‹à¤®à¥à¤Ÿà¤¬à¤¿à¤¨à¤¾ अधिलेखन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +अवसà¥à¤¥à¤¿à¤¤ फाइलहरू फडà¥à¤•ाउनà¥à¤¹à¥‹à¤¸à¥ +सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ पà¥à¤¨: नामकरण +सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ पà¥à¤¨: नामकरण अवसà¥à¤¥à¤¿à¤¤ फाइलहरू +3500 +फाइल पà¥à¤°à¤¤à¤¿à¤¸à¥à¤¥à¤¾à¤ªà¤¨ यकिन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +गनà¥à¤¤à¤µà¥à¤¯ फोलà¥à¤¡à¤°à¤²à¥‡ पहिले नै पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ गरिà¤à¤•ो फाइल समावेश गरà¥à¤¦à¤› +अवसà¥à¤¥à¤¿à¤¤ फाइलमा पà¥à¤°à¤¤à¤¿à¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨ चाहनà¥à¤¹à¥à¤¨à¥à¤› +योसà¤à¤—? +{0} बाइट +सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ पà¥à¤¨: नामकरण +3700 +'{0}'का लागि असमरà¥à¤¥à¤¿à¤¤ सङà¥à¤•à¥à¤šà¤¨ विधि +'{0}'लगत तà¥à¤°à¥à¤Ÿà¤¿ । फाइल बिगà¥à¤°à¥‡à¤•ो छ +'{0}' मा CRC असफल । फाइल बिगà¥à¤°à¥‡à¤•ो छ + + +3800 +पासवरà¥à¤¡ पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +पासवरà¥à¤¡ पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥: + +&पासवरà¥à¤¡ देखाउनà¥à¤¹à¥‹à¤¸à¥ + + + +पासवरà¥à¤¡ +3900 +वà¥à¤¯à¤¤à¥€à¤¤ समय: +पà¥à¤¨: नामकरण समय: +साइज: +गति: + + +तà¥à¤°à¥à¤Ÿà¤¿: + +4000 +सङà¥à¤—à¥à¤°à¤¹à¤®à¤¾ थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +&सङà¥à¤—à¥à¤°à¤¹: +&अदà¥à¤¯à¤¾à¤µà¤§à¤¿à¤• मोड: +सङà¥à¤—à¥à¤°à¤¹ ढाà¤à¤šà¤¾: +सङà¥à¤•à¥à¤šà¤¨ सà¥à¤¤à¤°: +सङà¥à¤•à¥à¤šà¤¨ विधि: +&शबà¥à¤¦à¤•ोश साइज: +&शबà¥à¤¦ साइज: + + +&परिमिति: +विकलà¥à¤ª +SF&X सङà¥à¤—à¥à¤°à¤¹ सिरà¥à¤œà¤¨à¤¾ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ + + + +फाइलनाम गà¥à¤ªà¥à¤¤à¤¿à¤•रण गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +सङà¥à¤•à¥à¤šà¤¨à¤•ा लागि सà¥à¤®à¥ƒà¤¤à¤¿ पà¥à¤°à¤¯à¥‹à¤—: +असङà¥à¤•à¥à¤šà¤¨à¤•ा लागि सà¥à¤®à¥ƒà¤¤à¤¿ पà¥à¤°à¤¯à¥‹à¤—: +4050 +भणà¥à¤¡à¤¾à¤° गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +सब भनà¥à¤¦à¤¾ छिटो +छिटो +साधारण +अधिकतम +अतà¥à¤¯à¤¨à¥à¤¤ +4060 +फाइल थपेर पà¥à¤°à¤¤à¤¿à¤¸à¥à¤¥à¤¾à¤ªà¤¨ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +फाइलहरू अदà¥à¤¯à¤¾à¤µà¤§à¤¿à¤• गरेर थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +अवसà¥à¤¥à¤¿à¤¤ फाइल ताजा गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +फाइल समकà¥à¤°à¤®à¤£ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +4070 +बà¥à¤°à¤¾à¤‰à¤œ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +सबै फाइल + + +6000 +पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +सारà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +यसमा पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥: +यसमा सारà¥à¤¨à¥à¤¹à¥‹à¤¸à¥: +पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ गरà¥à¤¦à¥ˆà¤›... +सारà¥à¤¦à¥ˆà¤›... +पà¥à¤¨: नामकरण... + +सञà¥à¤šà¤¾à¤²à¤¨ समरà¥à¤¥à¤¨ गरà¥à¤¦à¥ˆà¤¨ +फाइल वा फोलà¥à¤¡à¤° पà¥à¤¨: नामकरण गरà¥à¤¦à¤¾ तà¥à¤°à¥à¤Ÿà¤¿ +फाइल पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ गरà¥à¤¨ यकिन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +तपाईठफाइलहरू सङà¥à¤—à¥à¤°à¤¹à¤®à¤¾ पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ गरà¥à¤¨ निशà¥à¤šà¤¿à¤¤ हà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤› +6100 +फाइल मेटà¥à¤¨ यकिन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +फोलà¥à¤¡ मेटà¥à¤¨ यकिन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +बहà¥à¤µà¤¿à¤§ फाइल मेटà¥à¤¨ यकिन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +तपाईठ'{0}'मेटà¥à¤¨ निशà¥à¤šà¤¿à¤¤ हà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤› ? +तपाईठ'{0}' फोलà¥à¤¡à¤° यसको सबै सामगà¥à¤°à¥€ मेटà¥à¤¨ निशà¥à¤šà¤¿à¤¤ हà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤› ? +तपाईठ{0} वसà¥à¤¤à¥à¤¹à¤°à¥‚ मेटà¥à¤¨ निशà¥à¤šà¤¿à¤¤ हà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤› ? +मेटà¥à¤¦à¥ˆà¤›... +फाइल वा फोलà¥à¤¡à¤° मेटà¥à¤¦à¤¾ तà¥à¤°à¥à¤Ÿà¤¿ + +6300 +फोलà¥à¤¡à¤° सिरà¥à¤œà¤¨à¤¾ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +फाइल सिरà¥à¤œà¤¨à¤¾ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +फोलà¥à¤¡à¤° नाम: +फाइल नाम: +नयाठफोलà¥à¤¡à¤° +नयाठफाइल +फोलà¥à¤¡à¤° सिरà¥à¤œà¤¨à¤¾ गरà¥à¤¦à¤¾ तà¥à¤°à¥à¤Ÿà¤¿ +फाइल सिरà¥à¤œà¤¨à¤¾ गरà¥à¤¦à¤¾ तà¥à¤°à¥à¤Ÿà¤¿ +6400 +टिपà¥à¤ªà¤£à¥€ +&टिपà¥à¤ªà¤£à¥€: +चयन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +चयन हटाउनà¥à¤¹à¥‹à¤¸à¥ +मासà¥à¤•: +6600 + +फोलà¥à¤¡à¤° इतिहार +निदानातà¥à¤®à¤• सनà¥à¤¦à¥‡à¤¶ +सनà¥à¤¦à¥‡à¤¶ +7100 +कमà¥à¤ªà¥à¤¯à¥à¤Ÿà¤° +सञà¥à¤œà¤¾à¤² + +पà¥à¤°à¤£à¤¾à¤²à¥€ +7200 +थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +निकालà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +परीकà¥à¤·à¤£ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +सारà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +मेटà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +सूचना +7300 +फाइल विभाजन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +&यसमा विभाजन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥: +भोलà¥à¤¯à¥à¤®, बाइटमा विभाजन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥: +विभाजन गरà¥à¤¦à¥ˆà¤›... + + + + + +7400 +फाइल संयोजन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +&यसमा संयोजन गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥: +संयोजन गरà¥à¤¦à¥ˆà¤›... + + + +7500 +Checksum गणना गरà¥à¤¦à¥ˆà¤›... +Checksum सूचना +लगतका लागि CRC checksum: +लगत र नामहरूका लागि CRC checksum: +7600 +बेञà¥à¤šà¤®à¤¾à¤°à¥à¤• +सà¥à¤®à¥ƒà¤¤à¤¿ उपयोग: +सङà¥à¤•à¥à¤šà¤¨ +असङà¥à¤•à¥à¤šà¤¨ +दर +जमà¥à¤®à¤¾ दर +हालको +नतिजा + + +पास: diff --git a/Utils/7-Zip/Lang/nl.txt b/Utils/7-Zip/Lang/nl.txt new file mode 100644 index 000000000..c91ecdffa --- /dev/null +++ b/Utils/7-Zip/Lang/nl.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; : Bert van Velsen +; 4.26 : Jeroen van der Weijde. +; : Harm Hilvers +; 9.07 : Jeroen Tulp +; 15.00 : Jeroen Tulp +; +; +; +; +; +; +0 +7-Zip +Dutch +Nederlands +401 +OK +Annuleren + + + +&Ja +&Nee +A&fsluiten +Help + +&Hervatten +440 +Ja op &alles +Nee op a&lles +Stop +Herstarten +&Achtergrond +&Voorgrond +&Pauzeren +Gepauzeerd +Weet u zeker dat u wilt annuleren? +500 +&Bestand +Be&werken +Bee&ld +&Favorieten +E&xtra +&Help +540 +&Openen +Open B&innen +Open B&uiten +Bee&ld +&Bewerken +&Hernoemen +&Kopiëren naar... +&Verplaatsen naar... +Verwij&deren +Bestand &opsplitsen... +Bestanden &samenvoegen... +&Eigenschappen +O&pmerking +&Checksum berekenen +Ver&gelijken +Nieuwe &map +&Nieuw bestand +&Sluiten +Koppe&ling +&Alternate Streams +600 +&Alles selecteren +A&lles de-selecteren +Selectie &omkeren +&Selecteer... +&De-selecteer... +Selecteer op &type +De-selecteer op t&ype +700 +&Grote pictogrammen +&Kleine pictogrammen +&Lijst +&Details +730 +&Ongesorteerd +&Platte weergave +&2 Panelen +&Werkbalken +&Root map openen +Één &niveau omhoog +&Mappen Geschiedenis... +&Verversen +&Automatisch verversen +750 +&Archief werkbalk +&Standaard werkbalk +Grote kn&oppen +Knop&tekst weergeven +800 +&Map toevoegen aan Favorieten +Favoriet maken +900 +&Opties... +&Benchmark +960 +&Inhoud... +&Over 7-Zip... +1003 +Pad +Naam +Extensie +Map +Grootte +Ingepakte grootte +Kenmerken +Aangemaakt +Laatst geopend +Gewijzigd +Compact +Commentaar +Gecodeerd +Gesplitst voor +Gesplitst na +Woordenboek + +Type +Anti +Methode +Gastheer OS +Bestandssysteem +Gebruiker +Groep +Blok +Commentaar +Positie +Pad Prefix +Mappen +Bestanden +Versie +Volume +Multivolume +Offset +Koppelingen +Blokken +Volumes + +64-bit +Big-endian +CPU +Fysieke grootte +Kop grootte +Checksum +Karakteristieken +Virtueel Adres +ID +Korte Naam +Aanmaak Applicatie +Sector Grootte +Modus +Symbolische Kpoppeling +Fout +Capaciteit +Beschikbaar +Clustergrootte +Label +Lokale naam +Provider +NT Beveiliging +Alternate Stream +Aux +Verwijderd +Is Structuur + + +Error Type +Errors +Errors +Waarschuwingen +Waarschuwing +Streams +Alternate Streams +Alternate Streams Grootte +Virtuele Grootte +Uitgepakte Grootte +Totale Fysieke Grootte +Volume Index +SubType +Kort Commentaar +Tekstcodering + + + +Tail Grootte +Embedded Stub Grootte +Koppeling +Harde Koppeling +iNode + +Alleen-lezen +2100 +Opties +Taal +Taal: +Editor +&Editor: +&Vergelijken: +2200 +Systeem +Associeer 7-Zip met: +All users +2301 +7-Zip in het contextmenu integreren. +Trapsgewijs contextmenu +Contextmenu items: +Pictogrammen in contexmenu +2320 + + +Open archief +Bestanden uitpakken... +Toevoegen aan archief... +Testen archief +Uitpakken (hier) +Uitpakken naar {0} +Toevoegen aan {0} +Comprimeer en verstuur... +Comprimeer naar {0} en verstuur +2400 +Mappen +&Werkmap +&Tijdelijke systeemmap +&Huidige +&Gespecificeerd: +Alleen voor verwisselbare schijven gebruiken. +Specificeer een locatie voor tijdelijke archiefbestanden. +2500 +Instellingen +Toon ".." &item +Toon &echte bestandspictogrammen +Toon &systeem contextmenu +&Selecteer gehele rij +Toon &rasterlijnen +Enkele klik om een item te openen +&Alternatieve selectiemodus +&Gebruik grote geheugenpagina's +2900 +Over 7-Zip +7-Zip is gratis software. Echter, u kunt de ontwikkeling van 7-Zip ondersteunen door u te registreren. +3000 +Het systeem kan de benodigde hoeveelheid geheugen niet alloceren +Er zijn geen fouten. +{0} item(s) geselecteerd +Kan map '{0}' niet aanmaken. +Bijwerkfuncties niet ondersteund voor dit archief. +Kan bestand '{0}' niet openen als archief. +Kan het gecodeerde archief '{0}' niet openen. Verkeerd wachtwoord? +Niet ondersteund archief type +Bestand {0} bestaat reeds +Bestand '{0}' is gewijzigd.\nWilt u het bijwerken in het archief? +Kan bestand\n'{0}' niet bijwerken. +Kan de editor niet starten. +Het bestand lijkt op een virus (het bevat veel opeenvolgende spaties in de naam). +De actie kan niet worden uitgevoerd vanuit een folder met een dermate lang pad. +U moet 1 bestand selecteren +U moet 1 of meerdere bestanden selecteren +Te veel items +Kan het bestand niet openen als {0} archief +Het bestand is geopend als {0} archief +Het archiefis geopend met een offset +3300 +Uitpakken +Comprimeren +Testen +Openen... +Scannen... +Verwijderen +3320 +Toevoegen +Bijwerken +Analyseren +Repliceren +Opnieuw inpakken +Overslaan +Verwijderen +Kop aanmaken +3400 +&Uitpakken +U&itpakken naar: +Specificeer een locatie voor de uitgepakte bestanden. +3410 +Padmethode +Volledige padnamen +Geen padnamen +Absolute padnamen +Relatieve padnamen +3420 +Overschrijfmethode +Vraag voor overschrijven +Overschrijven zonder bevestiging +Bestaande bestanden overslaan +Automatisch hernoemen +Automatisch hernoemen van bestaande bestanden +3430 +Verwijder duplicaat van de hoofdmap +Herstellen bestandsbeveiliging +3500 +Bevestig vervangen bestand +Doelmap bevat reeds het verwerkte bestand. +Wilt u het bestaande bestand vervangen +door dit bestand? +{0} bytes +A&utomatisch hernoemen +3700 +Niet ondersteunde compressiemethode voor '{0}'. +Gegevensfout in '{0}'. Bestand is beschadigd. +CRC mislukt in '{0}'. Bestand is beschadigd. +Gegevensfout in het gecodeerde bestand '{0}'. Verkeerd wachtwoord? +CRC mislukt in het gecodeerde bestand '{0}'. Verkeerd wachtwoord? +3710 +Verkeerd wachtwoord? +3721 +Niet ondersteunde compresssiemethode +Gegevens fout +CRC mislukt +Data niet beschikbaar +Data eindigd onverwachts +Er is nog wat data na het einde van de payload data +Is geen archief +Kop Error +Verkeerd wachtwoord +3763 +Start van archief is niet beschikbaar +Start van archief is niet bevestigd + + + +Niet ondersteunde functie +3800 +Wachtwoord ingeven +Wachtwoord &ingeven: +Wachtwoord bevestigen: +Wachtwoord &tonen +Wachtwoorden komen niet overeen. +Gebruik alleen alfanumerieke en speciale karakters (!, #, $, ...) voor het wachtwoord +Wachtwoord is te lang. +&Wachtwoord +3900 +Verstreken tijd: +Overgebleven tijd: +Grootte: +Snelheid: +Verwerkt: +Compressie verhouding: +Fouten: +Archieven: +4000 +Toevoegen aan archief +&Archief: +&Bijwerkmethode: +Archief &formaat: +Compressie&niveau: +Compressie&methode: +&Woordenboekgrootte: +W&oordgrootte: +Compacte b&lokgrootte: +Aantal CP&U-threads: +&Parameters: +Opties +SF&X archief maken +Comprimeer &gedeelde bestanden +Codering +Cod&eermethode: +Codee&r bestandsnamen +Geheugengebruik bij het inpakken: +Geheugengebruik bij het uitpakken: +Verwijder bestanden na inpakken +4040 +Symboische koppelingen opslaan +Harde koppelingen opslaan +Alternate data streams opslaan +Bestandsbeveiliging opslaan +4050 +Opslaan +Snelst +Snel +Normaal +Maximum +Ultra +4060 +Bestanden toevoegen en vervangen +Bestanden bijwerken en toevoegen +Bestaande bestanden opfrissen +Bestanden synchroniseren +4070 +Bladeren +Alle bestanden +Niet compact +Compact +6000 +Kopiëren +Verplaatsen +Kopiëren naar: +Verplaatsen naar: +Bezig met kopiëren... +Bezig met verplaatsen... +Bezig met hernoemen... +Selecteer een doelmap. +Functie wordt niet ondersteund. +Fout bij het hernoemen van een bestand of map +Bevestig kopiëren van bestand +Weet u zeker dat u deze bestanden naar het archief wilt kopiëren +6100 +Verwijdering bestand bevestigen +Verwijdering map bevestigen +Verwijdering van meerdere bestanden bevestigen +Weet u zeker dat u '{0}' wilt verwijderen? +Weet u zeker dat u de map '{0}' en alle onderliggende items wilt verwijderen? +Weet u zeker dat u deze {0} items wilt verwijderen? +Bezig met verwijderen... +Fout bij het verwijderen van een bestand of map +Het systeem kan een bestand met een lang pad niet verplaatsen naar de Prullenbak +6300 +Map maken +Bestand maken +Naam van de map: +Bestandsnaam: +Nieuwe map +Nieuw bestand +Fout bij het maken van de map +Fout bij het maken van het bestand. +6400 +Opmerking +&Opmerking: +Selecteren +De-selecteren +Masker: +6600 +Eigenschappen +Mappen Geschiedenis +Diagnostische berichten +Bericht +7100 +Computer +Netwerk +Documenten +Systeem +7200 +Toevoegen +Uitpakken +Testen +Kopiëren +Verplaatsen +Verwijderen +Info +7300 +Opsplitsen bestand +&Opsplitsen naar: +Opsplitsen in &volumes (grootte in bytes): +Bezig met opsplitsen... +Bevestigen opsplitsen +Weet u zeker dat u het bestand wilt opsplitsen in {0} volumes? +De volumegrootte moet kleiner zijn dan de grootte van het oorspronkelijke bestand. +Verkeerde volumegrootte +Gespecificeerde volumegrootte: {0} bytes.\nWeet u zeker dat u het archief zo wilt splitsen? +7400 +Bestanden samenvoegen +&Samenvoegen naar: +Bezig met samenvoegen... +Selecteer alleen het eerste bestand. +Kan het bestand niet herkennen als onderdeel van een gesplitst bestand +Kan niet meer dan 1 deel van een gesplitst bestand +7500 +Bezig met checksum berekenen... +Checksum informatie +CRC checksum voor gegevens: +CRC checksum voor gegevens en namen: +7600 +Benchmark +Geheugengebruik: +Inpakken +Uitpakken +Waarde +Totale waarde +Huidig +Resultaat +CPU-gebruik +Waarde / gebruik +Doorgangen: +7700 +Koppeling +Koppeling +Koppeling van: +Koppeling naar: +7710 +Koppeling Type +Harde Koppeling +Bestand Symbolische Koppeling +Map Symbolische Koppeling +Mapsplitsing diff --git a/Utils/7-Zip/Lang/nn.txt b/Utils/7-Zip/Lang/nn.txt new file mode 100644 index 000000000..bcd7fc8ab --- /dev/null +++ b/Utils/7-Zip/Lang/nn.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.45 : Robert Grønning +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Norwegian Nynorsk +Norsk Nynorsk +401 +OK +Avbryt + + + +&Ja +&Nei +&Lukke +Hjelp + +&Hald fram +440 +Ja til &alt +N&ei til alt +Stopp +Start pÃ¥ nytt +&Bakgrunn +&Forgrunn +&Pause +Sett pÃ¥ pause +Er du sikker pÃ¥ du vil avbryte? +500 +&Fil +&Redigere +&Vis +F&avorittar +Verk&tøy +&Hjelp +540 +&Opna +Opna &Inni +Opna &Utanfor +&Vis +&Redigere +Endra &namn +&Kopiere til... +&Flytt til... +&Slett +&Del opp fil... +Set saman filer... +&Eigenskapar +Ko&mmentar +Rekna ut kontrollnummer + +Opprett mappe +Opprett fil +&Avslutta +600 +&Merk alle +Fjern alle markeringar +&Omvendt markering +Marker... +Fjern markering... +Merk etter type +Fjern markering etter type +700 +S&tore ikon +S&mÃ¥ ikon +&Lista +&Detaljar +730 +Assortert +Flat vising +&2 felt +&Verktøylinjer +Opna kjeldemappa +Opp eit nivÃ¥ +Mappelogg... +&Oppdatere +750 +Arkiv verktøylinje +Standard verktøylinjer +Store knappar +Vis knappetekst +800 +&Legg mappe til i favorittar som +Bokmerke +900 +&Val... +&Yting test +960 +&Innhold... +&Om 7-Zip... +1003 +Bane +Namn +Fil etternamn +Mappe +Størrelse +Komprimert Størrelse +Eigenskapar +Oppretta +Opna +Endra +Solid +Kommentert +Kryptert +Delt før +Delt etter +Ordbok +CRC +Type +Anti +Metode +Vert OS +Filsystem +Brukar +Gruppe +Blokkering +Kommentar +Posisjon +Bane prefiks + + + + + + + + + + + + + + + + + + + + + + + + +Feil +Total størrelse +Ledig plass +Klyngje størrelse +Etikett +Lokalt namn +Leverandør +2100 +Val +SprÃ¥k +SprÃ¥k: +Redigeringsprogram +&Redigeringsprogram: + +2200 +System +Forbind 7-Zip med: +2301 +Legg inn 7-Zip i programmenyen +Forgreina programmeny +Programmeny val: +2320 + + +Opna arkiv +Pakk ut filer... +Legg til i arkiv... +Test arkiv +Pakk ut her +Pakk ut til {0} +Legg til i {0} +Komprimere og send som epost... +Komprimere til {0} og send som epost +2400 +Mapper +&Arbeidsmappe +&Midlertidig mappe +&Noverande +&Eigendefinert: +Berre for flyttbare stasjonar +Oppgje plassering for midlertidige arkiv filer. +2500 +Innstillingar +Vis ".." element +Vis dei ordentlege fil ikona +Vis system meny +&Merk heile rader +Vis &rutenett + +&Alternativ markerings modus +Bruk &store minnesider +2900 +Om 7-Zip +7-Zip er fri programvare. Du kan støtta utviklinga av 7-Zip ved Ã¥ registrere deg. +3000 + +Ingen feil +{0} objekt(ar) valt +Kan ikkje oppretta mappe '{0}' +Dette arkivet manglar støtte for Ã¥ kunne oppdaterast. +Kan ikkje opna fila '{0}' som eit arkiv +Kan ikkje opna det krypterte arkivet '{0}'. Feil passord? + + +Fila '{0}' blei endra.\nVil du oppdatere den i arkivet? +Kan ikkje oppdatere fil\n'{0}' +Kan ikkje starta redigeringsprogram. + + + + +For mange gonger +3300 +Pakkar ut +Komprimerer +Testing +Opnar... +Undersøkjer... +3400 +Pakk ut +Pakk ut &til: +Vel ei mappe for ut-pakka filer. +3410 +Bane modus +Fulstendig banenamn +Ingen banenamn +3420 +Overskriving modus +Bekrefta før overskriving +Skriv over utan bekrefting +Hopp over eksisterande filer +Endra filnamn automatisk +Endra filnamn automatisk for eksisterande filer +3500 +Bekrefta overskriving av fil +MÃ¥lmappa inneheld allereie ei behandla fil. +Vil du overskriva den eksisterande fila +med denne? +{0} byte +&Skift filnamn automatisk +3700 +Kompresjonsmetoden er ikkje støtta for '{0}'. +Data feil i '{0}'. Fila er øydelagt. +CRC feila pÃ¥ '{0}'. Fila er øydelagt. +Data feil i den krypterte fila '{0}'. Feil passord? +CRC feila i den krypterte fila '{0}'. Feil passord? +3800 +Skriv inn passord +Skriv inn passord: +Skriv inn passordet pÃ¥ nytt: +&Vis passord +Passorda er ikkje like +Bruk berre Engelske bokstavar, tal og spesielle teikn (!, #, $, ...) i passordet +Passordet er for langt +Passord +3900 +Tid brukt: +Tid gjenstÃ¥r: +Størrelse: +Fart: + + +Feil: + +4000 +Legg til i arkiv +&Arkiv: +&Oppdaterings modus: +Arkiv &format: +Kompresjons &nivÃ¥: +Kompresjons &metode: +O&rdbok størrelse: +Or&d størrelse: +Solid blokk størrelse: +Anntal CPU trÃ¥der: +&Parameter: +Val +Opprett SF&X arkiv + +Krypter +Krypter metode: +&Krypter filnamn +Minnebruk ved kompresjon: +Minnebruk ved ut-pakking: +4050 +Lagre +Raskast +Rask +Normal +Maksimum +Ekstrem +4060 +Legg til og skriv over filer +Oppdatere og legg til filer +Frisk opp eksisterande filer +Synkroniser filer +4070 +Bla igjennom +Alle filer +Ikkje-solid +Solid +6000 +Kopiere +Flytt +Kopiere til: +Flytt til: +Kopierer... +Flyttar... +Endrar namn... +Vel mÃ¥lmappe. +Støttar ikkje handlinga. +Feil ved endring av namn pÃ¥ fil eller mappe +Godkjenne filkopiering +Er du sikker pÃ¥ at du vil kopiere filer til arkiv +6100 +Godkjenne sletting av fil +Godkjenne sletting av mappe +Godkjenne sletting av fleire filer +Er du sikker pÃ¥ at du vil sletta '{0}'? +Er du sikker pÃ¥ at du vil sletta mappa '{0}' og alt innhold i den? +Er du sikker pÃ¥ at du vil sletta desse {0} elementa? +Slettar... +Feil ved sletting av fil eller mappe + +6300 +Opprett mappe +Opprett fil +Mappe namn: +Filnamn: +Ny mappe +Ny fil +Feil ved oppretting av mappe +Feil ved oppretting av fil +6400 +Kommentar +&Kommentar: +Marker +Fjern markering +Maske: +6600 + +Mappe logg +Diagnose meldingar +Melding +7100 +Datamaskin +Nettverk + +System +7200 +Legg til +Pakk ut +Test +Kopiere +Flytt +Slett +Informasjon +7300 +Del opp fil +&Del opp til: +Splitt opp i deler, byte: +Delar opp... +Godkjenne oppdeling +Er du sikker pÃ¥ at du vil dele opp fila i {0} delar? +Størrelsen pÃ¥ delane mÃ¥ vera mindre enn størrelsen pÃ¥ originalfila +Feil del-størrelse +Oppgitt del-størrelse: {0} byte.\nEr du sikker pÃ¥ du vil dele arkivet opp i slike deler? +7400 +SlÃ¥ saman filer +&SlÃ¥ saman til: +SlÃ¥r saman... +Berre vel den første fila + + +7500 +Reknar ut kontrollnummer... +Informasjon om kontrollnummer +CRC kontrollnummer for data: +CRC kontrollnummer for data og namn: +7600 +Yting test +Minnebruk: +Kompresjon +Ut-pakking +Yting +Total yting +Noverande +Resultat +CPU bruk +Yting / Bruk +Gonger: diff --git a/Utils/7-Zip/Lang/pa-in.txt b/Utils/7-Zip/Lang/pa-in.txt new file mode 100644 index 000000000..8633ed4fb --- /dev/null +++ b/Utils/7-Zip/Lang/pa-in.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.53 : Gurmeet Singh Kochar +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Punjabi, Indian +ਪੰਜਾਬੀ +401 +ਠੀਕ ਹੈ +ਰੱਦ ਕਰੋ + + + +ਹਾਂ (&Y) +ਨਹੀਂ (&N) +ਬੰਦ ਕਰੋ (&C) +ਮੱਦਦ + +ਜਾਰੀ ਕਰੋ (&C) +440 +ਸਾਰਿਆਂ ਲਈ ਹਾਂ (&A) +ਸਾਰਿਆਂ ਲਈ ਨਹੀਂ (&l) +ਰà©à¨•à©‹ +ਮà©à©œ ਚਾਲੂ ਕਰੋ +ਬੈਕਗਰਾਉਂਡ (&B) +ਫੋਰਗਰਾਉਂਡ (&F) +ਪੋਜ਼ (&P) +ਪੋਜ਼ ਹੋਇਆ +ਕੀ ਤà©à¨¸à©€à¨‚ ਨਿਸ਼ਚਿੱਤ ਹੀ ਰੱਦ ਕਰਨਾ ਚਾਹà©à©°à¨¦à©‡ ਹੋ? +500 +ਫਾਇਲ (&F) +ਸੋਧ (&E) +ਵੇਖੋ (&V) +ਪਸੰਦੀਦਾ (&a) +ਸੰਧ (&T) +ਮੱਦਦ (&H) +540 +ਖੋਲੋ (&O) +ਅੰਦਰ ਖੋਲੋ (&I) +ਬਾਹਰ ਖੋਲੋ (&u) +ਵਿਖਾਓ (&V) +ਸੋਧ ਕਰੋ (&E) +ਨਾਂ ਬਦਲੋ (&m) +ਨਵੇਂ ਟਿਕਾਣੇ ਤੇ ਨਕਲ ਉਤਾਰੋ (&C)... +ਨਵੇਂ ਟਿਕਾਣੇ ਤੇ ਭੇਜੋ (&M)... +ਹਟਾਓ (&D) +ਫਾਇਲ ਹਿੱਸਿਆਂ ਵਿੱਚ ਵੰਡੋ (&S)... +ਫਾਇਲ ਦੇ ਹਿੱਸੇ ਜੋੜੋ (&b)... +ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ (&r) +ਟਿੱਪਣੀ (&n) +ਚੈਕਸੱਮ ਗਣਨਾ ਕਰੋ + +ਫੋਲਡਰ ਬਣਾਓ +ਫਾਇਲ ਬਣਾਓ +ਬਾਹਰ ਨਿਕਲੋ (&x) +600 +ਸਭ ਚà©à¨£à©‹ (&A) +ਸਭ ਚੋਣ ਰੱਦ ਕਰੋ +ਉਲਟ ਚੋਣ ਕਰੋ (&I) +ਚà©à¨£à©‹... +ਚੋਣ ਰੱਦ ਕਰੋ... +ਕਿਸਮ ਨਾਲ ਚà©à¨£à©‹ ਕਰੋ +ਕਿਸਮ ਨਾਲ ਚੋਣ ਰੱਦ ਕਰੋ +700 +ਵੱਡੇ ਆਈਕਾਨ (&g) +ਛੋਟੇ ਆਈਕਾਨ (&m) +ਸੂਚੀ (&L) +ਵੇਰਵੇ ਸਹਿਤ (&D) +730 +ਨਾ ਕà©à¨°à¨®-ਬੱਧ +ਫਲੈਟ ਦà©à¨°à¨¿à¨¸à¨¼ +&2 ਪੈਨਲ +ਟੂਲਬਾਰ (&T) +ਰੂਟ ਫੋਲਡਰ ਖੋਲੋ +ਇੱਕ ਪੱਧਰ ਉੱਤੇ +ਫੋਲਡਰ ਅਤੀਤ... +ਤਾਜ਼ਾ ਕਰੋ(&R) +750 +ਆਕਾਈਵ ਟੂਲਬਾਰ +ਸਧਾਰਨ ਟੂਲਬਾਰ +ਵੱਡੇ ਬਟਨ +ਬਟਨ ਟੈਕਸਟ ਵਿਖਾਓ +800 +ਫੋਲਡਰ ਪਸੰਦੀਦਾ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ (&A) +ਬà©à©±à¨•ਮਾਰਕ +900 +ਚੋਣਾਂ (&O)... +ਬੈਂਚਮਾਰਕ (&B) +960 +ਵਿਸ਼ਾ ਸੂਚੀ (&C)... +7-ਜ਼ਿੱਪ ਬਾਰੇ (&A)... +1003 +ਮਾਰਗ +ਨਾਂ +à¨à¨•ਸਟੈਂਸ਼ਨ +ਫੋਲਡਰ +ਸਾਈਜ਼ +ਪੈਕਡ ਸਾਈਜ਼ +ਲੱਛਨ +ਬਣਤਰ ਸਮਾਂ +ਪਹà©à©°à¨š ਸਮਾਂ +ਸੋਧ ਸਮਾਂ +ਠੋਸ +ਟਿੱਪਣੀ +à¨à¨¨à¨•à©à¨°à¨¿à¨ªà¨Ÿà¨¡ +Split Before +Split After +ਡਿਕਸ਼ਨਰੀ +ਸੀ-ਆਰ-ਸੀ (CRC) +ਕਿਸਮ +à¨à¨‚ਟੀ (Anti) +ਢੰਗ +ਮੇਜ਼ਬਾਨ ਔ-à¨à©±à¨¸ +ਫਾਇਲ ਸਿਸਟਮ +ਯੂਜ਼ਰ +ਸਮੂਹ +ਬਲੋਕ +ਟਿੱਪਣੀ +ਸਥਿੱਤੀ +ਮਾਰਗ ਅਗੇਤਰ +ਫੋਲਡਰ +ਫਾਇਲਾਂ +ਵਰਜਨ +ਵੋਲà©à©±à¨® +ਮਲਟੀਵੋਲà©à©±à¨® +ਔਫ਼ਸੈਟ +ਲਿੰਕ +ਬਲੋਕ +ਵੋਲà©à©±à¨® + + + + + + + + + + + + + + + +ਸਮੱਸਿਆ +ਕà©à©±à¨² ਸਾਈਜ਼ +ਖ਼ਾਲੀ ਥਾਂ +ਕਲੱਸਟਰ ਸਾਈਜ਼ +ਲੇਬਲ +ਸਥਾਨਕ ਨਾਂ +ਉਪਲੱਬਧ ਕਰਤਾ +2100 +ਚੋਣਾਂ +ਭਾਸ਼ਾ +ਭਾਸ਼ਾ: +à¨à¨¡à©€à¨Ÿà¨° +ਟੈਕਸਟ à¨à¨¡à©€à¨Ÿà¨° (&E): + +2200 +ਸਿਸਟਮ +7-ਜ਼ਿੱਪ ਨਾਲ ਹੇਠਾਂ ਦਿੱਤੇ ਫਾਇਲ à¨à¨•ਸਟੈਂਸ਼ਨ ਜੋੜੋ: +2301 +ਸ਼ੈੱਲ ਕੰਨਟੈਕਸਟ ਮੇਨੂੰ ਨਾਲ 7-ਜ਼ਿੱਪ ਨੂੰ à¨à¨•ੀਕਿਰਤ ਕਰੋ +ਕੈਸਕੇਡਡ ਕੰਨਟੈਕਸਟ ਮੇਨੂੰ +ਕੰਨਟੈਕਸਟ ਮੇਨੂੰ ਆਈਟਮਾਂ: +2320 +<ਫੋਲਡਰ> +<ਆਕਾਈਵ> +ਆਕਾਈਵ ਖੋਲੋ +ਫਾਇਲਾਂ ਕੱਡੋ... +ਆਕਾਈਵ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ... +ਆਕਾਈਵ ਪਰਖੋ +ਫਾਇਲਾਂ ਇੱਥੇ ਕੱਡੋ +{0} ਵਿੱਚ ਕੱਡੋ +{0} ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ +ਨਪੀੜੋ ਅਤੇ ਈਮੇਲ ਕਰੋ... +{0} ਵਿੱਚ ਨਪੀੜੋ ਅਤੇ ਈਮੇਲ ਕਰੋ +2400 +ਫੋਲਡਰ +ਵਰਕਿੰਗ ਫੋਲਡਰ (&W) +ਸਿਸਟਮ ਆਰਜ਼ੀ (temp) ਫੋਲਡਰ (&S) +ਇਸ ਸਮੇਂ ਚà©à¨£à¨¿à¨† (&C) +ਹੇਠਾਂ ਦਿੱਤਾ ਗਿਆ (&S): +ਸਿਰਫ਼ ਹਟਾਈ ਜਾ ਸੱਕਨ ਵਾਲੀਆਂ ਡਰਾਈਵ ਲਈ ਵਰਤੋਂ ਕਰੋ +ਆਰਜ਼ੀ ਆਕਾਈਵ ਫਾਇਲਾਂ ਲਈ ਟਿਕਾਣਾ ਦੱਸੋ। +2500 +ਸੈਟਿੰਗ +".." ਆਈਟਮ ਵਿਖਾਓ +ਅਸਲੀ ਫਾਇਲ ਆਈਕਾਨ ਵਿਖਾਓ +ਸਿਸਟਮ ਮੇਨੂੰ ਵਿਖਾਓ +ਪੂਰੀ ਕਤਾਰ ਚà©à¨£à©‹ (&F) +ਗà©à¨°à¨¿à¨¡ ਲਾਈਨਾਂ ਵਿਖਾਓ (&g) + +ਵਿਕਲਪਕ ਚà©à¨£à¨¾à¨“ ਢੰਗ (&A) +ਵੱਡੇ ਮੈਮੋਰੀ ਪੇਜ ਵਰਤੋ (&l) +2900 +7-ਜ਼ਿੱਪ ਬਾਰੇ +7-ਜ਼ਿੱਪ ਇੱਕ ਮà©à¨«à¨¼à¨¤ ਸਾਫ਼ਟਵੇਅਰ ਹੈ। ਪਰ ਫੇਰ ਵੀ, ਤà©à¨¸à©€à¨‚ ਰਜਿਸਟਰ ਕਰਕੇ 7-ਜ਼ਿੱਪ ਦੇ ਵਿਕਾਸ ਵਿੱਚ ਸਮਰਥਨ ਪਾ ਸੱਕਦੇ ਹੋ।\n\nਪੰਜਾਬੀ ਵਿੱਚ ਅਨà©à¨µà¨¾à¨¦ ਕੀਤਾ (Translation Done By):\nGurmeet Singh Kochar (ਗà©à¨°à¨®à©€à¨¤ ਸਿੰਘ ਕੋਚਰ)\n +3000 + +ਕੋਈ ਸਮੱਸਿਆਵਾਂ ਨਹੀਂ ਹਨ +ਚà©à¨£à©‡ ਪਦਾਰਥ: {0} +'{0}' ਫੋਲਡਰ ਨਹੀਂ ਬਣਾਇਆ ਜਾ ਸੱਕਿਆ +ਅੱਪਡੇਟ ਔਪਰੇਸ਼ਨ ਇਸ ਆਕਾਈਵ ਲਈ ਸਹਿਯੋਗੀ ਨਹੀਂ ਹਨ। +'{0}' ਫਾਇਲ ਨੂੰ ਆਕਾਈਵ ਤਰà©à¨¹à¨¾à¨‚ ਨਹੀਂ ਖੋਲਿਆ ਜਾ ਸੱਕਿਆ +'{0}' à¨à¨¨à¨•à©à¨°à¨¿à¨ªà¨Ÿà¨¡ ਆਕਾਈਵ ਨਹੀਂ ਖੋਲਿਆ ਜਾ ਸੱਕਿਆ। ਗਲ਼ਤ ਪਾਸਵਰਡ? + + +'{0}' ਫਾਇਲ ਸੋਧ ਦਿੱਤੀ ਗਈ ਹੈ।\nਕੀ ਤà©à¨¸à©€à¨‚ ਉਸਨੂੰ ਆਕਾਈਵ ਵਿੱਚ ਅੱਪਡੇਟ ਕਰਨਾ ਚਾਹà©à©°à¨¦à©‡ ਹੋ? +ਫਾਇਲ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸੱਕੀ\n'{0}' +à¨à¨¡à©€à¨Ÿà¨° ਚਾਲੂ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸੱਕਿਆ। + + + + +ਬਹà©à©±à¨¤ ਸਾਰੀਆਂ ਆਈਟਮਾਂ +3300 +ਕੱਡੀਆਂ ਜਾ ਰਹੀਆਂ ਹਨ +ਨਪੀੜਨ ਕਾਰਜ ਚੱਲ ਰਿਹਾ ਹੈ +ਪਰਖ ਚੱਲ ਰਹੀ ਹੈ +ਖੋਲੀ ਜਾ ਰਹੀ ਹੈ... +ਸਕੈਨ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ... +3400 +ਕੱਡੋ +ਇੱਥੇ ਕੱਡੋ (&x): +ਕੱਡੀਆਂ ਜਾਉਣ ਵਾਲੀਆਂ ਫਾਇਲਾਂ ਲਈ ਟਿਕਾਣਾ ਦੱਸੋ। +3410 +ਮਾਰਗ ਢੰਗ +ਪੂਰੇ ਮਾਰਗ ਨਾਂ +ਕੋਈ ਮਾਰਗ ਨਾਂ ਨਹੀਂ +3420 +ਉਪਰੀਲੇਖਨ ਢੰਗ +ਉਪਰੀਲੇਖਨ ਤੋਂ ਪਹਿਲਾਂ ਤਸਦੀਕ +ਬਿਨà©à¨¹à¨¾à¨‚ ਤਸਦੀਕ ਉਪਰੀਲੇਖਨ +ਮੌਜੂਦਾ ਫਾਇਲਾਂ ਨਾਂ ਕੱਡੋ +ਆਪੇ ਨਾਂ ਬਦਲ ਦਿਓ +ਮੌਜੂਦਾ ਫਾਇਲਾਂ ਦਾ ਆਪੇ ਨਾਂ ਬਦਲ ਦਿਓ +3500 +ਫਾਇਲ ਬਦਲਨ ਦੀ ਤਸਦੀਕ +ਕਾਰਵਾਈ ਕੀਤੀ ਜਾਉਂਦੀ ਫਾਇਲ ਨਿਯਤ ਫੋਲਡਰ ਵਿੱਚ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ। +ਕੀ ਤà©à¨¸à©€à¨‚ ਮੌਜੂਦਾ ਫਾਇਲ ਨੂੰ +ਇਸ ਫਾਇਲ ਨਾਲ ਬਦਲਨਾ ਚਾਹੋਗੇ? +{0} ਬਾਈਟ +ਆਪੇ ਨਾਂ ਬਦਲੀ ਕਰੋ (&u) +3700 +'{0}' ਲਈ ਨਪੀੜਨ ਢੰਗ ਸਹਿਯੋਗੀ ਨਹੀਂ। +'{0}' ਵਿੱਚ ਡਾਟਾ ਸਮੱਸਿਆ। ਫਾਇਲ ਟà©à©±à¨Ÿà©€ ਹੋਈ ਹੈ। +'{0}' ਵਿੱਚ ਸੀ-ਆਰ-ਸੀ ਅਸਫ਼ਲ ਰਿਹਾ। ਫਾਇਲ ਟà©à©±à¨Ÿà©€ ਹੋਈ ਹੈ। +'{0}' à¨à¨¨à¨•à©à¨°à¨¿à¨ªà¨Ÿà¨¡ ਫਾਇਲ ਵਿੱਚ ਡਾਟਾ ਸਮੱਸਿਆ। ਗਲ਼ਤ ਪਾਸਵਰਡ? +'{0}' à¨à¨¨à¨•à©à¨°à¨¿à¨ªà¨Ÿà¨¡ ਫਾਇਲ ਵਿੱਚ ਸੀ-ਆਰ-ਸੀ ਅਸਫ਼ਲ ਰਿਹਾ। ਗਲ਼ਤ ਪਾਸਵਰਡ? +3800 +ਪਾਸਵਰਡ ਭਰੋ +ਪਾਸਵਰਡ ਭਰੋ: +ਪਾਸਵਰਡ ਮà©à©œ ਭਰੋ: +ਪਾਸਵਰਡ ਵਿਖਾਓ (&S) +ਪਾਸਵਰਡ ਮੇਲ ਨਹੀਂ ਖਾਂਦੇ +ਪਾਸਵਰਡ ਲਈ ਸਿਰਫ਼ ਅੰਗà©à¨°à©‡à¨œà¨¼à©€ ਅੱਖਰ, ਅੰਕ, ਅਤੇ ਖ਼ਾਸ ਅੱਖਰਾਂ (!, #, $, ...) ਦੀ ਹੀ ਵਰਤੋਂ ਕਰੋ +ਪਾਸਵਰਡ ਬਹà©à©±à¨¤ ਲੰਬਾ ਹੈ +ਪਾਸਵਰਡ +3900 +ਬੀਤਿਆ ਸਮਾਂ: +ਰਹਿੰਦਾ ਸਮਾਂ: +ਕà©à©±à¨² ਸਾਈਜ਼: +ਗਤੀ: +ਨਿਬੇੜੀਆਂ ਬਾਈਟ: +ਨਪੀੜਨ ਅਨà©à¨ªà¨¾à¨¤: +ਸਮੱਸਿਆਵਾਂ: +ਆਕਾਈਵਾਂ: +4000 +ਆਕਾਈਵ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ +ਆਕਾਈਵ (&A): +ਅੱਪਡੇਟ ਢੰਗ (&U): +ਆਕਾਈਵ ਫੌਰਮੈਟ (&f): +ਨਪੀੜਨ ਪੱਧਰ (&l): +ਨਪੀੜਨ ਢੰਗ (&m): +ਡਿਕਸ਼ਨਰੀ ਸਾਈਜ਼ (&D): +ਵਰਡ ਸਾਈਜ਼(&W): +ਠੋਸ ਬਲੋਕ ਸਾਈਜ਼: +ਸੀ-ਪੀ-ਯੂ ਥਰੈੱਡ ਗਿਣਤੀ: +ਪੈਰਾਮੀਟਰ (&P): +ਚੋਣਾਂ +SF&X ਆਕਾਈਵ ਬਣਾਓ +ਵਰਤੀਆਂ ਜਾਉਂਦੀਆਂ ਫਾਇਲਾਂ ਨੂੰ ਵੀ ਨਪੀੜੋ +à¨à¨¨à¨•à©à¨°à¨¿à¨ªà¨¸à¨¼à¨¨ +à¨à¨¨à¨•à©à¨°à¨¿à¨ªà¨¸à¨¼à¨¨ ਢੰਗ: +ਫਾਇਲਾਂ ਦੇ ਨਾਂ à¨à¨¨à¨•à©à¨°à¨¿à¨ªà¨Ÿ ਕਰੋ (&n) +ਨਪੀੜਨ ਲਈ ਮੈਮੋਰੀ ਦੀ ਵਰਤੋਂ: +ਆਕਾਈਵ ਖੋਲਨ ਲਈ ਮੈਮੋਰੀ ਦੀ ਵਰਤੋਂ: +4050 +ਸਿਰਫ਼ ਇਕੱਤਰਤਾ +ਬਹà©à©±à¨¤ ਤੇਜ਼ +ਤੇਜ਼ +ਆਮ +ਵੱਧੋਂ ਵੱਧ +ਸਭ ਤੋਂ ਵੱਧ +4060 +ਫਾਇਲਾਂ ਸ਼ਾਮਲ ਕਰੋ ਅਤੇ ਬਦਲੋ +ਫਾਇਲਾਂ ਸ਼ਾਮਲ ਅਤੇ ਅੱਪਡੇਟ ਕਰੋ +ਮੌਜੂਦਾ ਫਾਇਲਾਂ ਤਾਜ਼ਾ ਕਰੋ +ਫਾਇਲਾਂ ਸਮਕਾਲਵਰਤੀ ਕਰੋ +4070 +ਬਰਾਊਜ਼ +ਸਾਰੀਆਂ ਫਾਇਲਾਂ +ਨਾ-ਠੋਸ +ਠੋਸ +6000 +ਨਵੇਂ ਟਿਕਾਣੇ ਤੇ ਨਕਲ ਉਤਾਰੋ +ਨਵੇਂ ਟਿਕਾਣੇ ਤੇ ਭੇਜੋ +ਹੇਠਾਂ ਦਿੱਤੇ ਟਿਕਾਣੇ ਤੇ ਨਕਲ ਉਤਾਰੋ: +ਹੇਠਾਂ ਦਿੱਤੇ ਟਿਕਾਣੇ ਤੇ ਭੇਜੋ: +ਨਕਲ ਉਤਾਰੀ ਜਾ ਰਹੀ ਹੈ... +ਭੇਜਿਆ ਜਾ ਰਿਹਾ ਹੈ... +ਨਾਂ ਬਦਲਿਆ ਜਾ ਰਿਹਾ ਹੈ... +ਨਿਯਤ ਫੋਲਡਰ ਚà©à¨£à©‹ +ਕਾਰਵਾਈ ਸਹਿਯੋਗੀ ਨਹੀਂ ਹੈ। +ਫਾਇਲ ਜਾਂ ਫੋਲਡਰ ਦਾ ਨਾਂ ਬਦਲਣ ਵਿੱਚ ਸਮੱਸਿਆ +ਫਾਇਲ ਦੀ ਨਕਲ ਉਤਾਰਣ ਦੀ ਤਸਦੀਕ +ਕੀ ਤà©à¨¸à©€à¨‚ ਨਿਸ਼ਚਿੱਤ ਫਾਇਲਾਂ ਦੀ ਆਕਾਈਵ ਵਿੱਚ ਨਕਲ ਉਤਾਰਨਾ ਚਾਹà©à©°à¨¦à©‡ ਹੋ +6100 +ਫਾਇਲ ਹਟਾਉਣ ਦੀ ਤਸਦੀਕ +ਫੋਲਡਰ ਹਟਾਉਣ ਦੀ ਤਸਦੀਕ +ਬਹà©-ਫਾਈਲਾਂ ਹਟਾਉਣ ਦੀ ਤਸਦੀਕ +'{0}' ਨੂੰ ਹਟਾਉਣਾ ਚਾਹà©à©°à¨¦à©‡ ਹੋ? +ਕੀ ਤà©à¨¸à©€à¨‚ ਨਿਸ਼ਚਿੱਤ ਫੋਲਡਰ '{0}' ਅਤੇ ਉਸਦੇ ਵਿੱਚਲੀਆਂ ਆਈਟਮਾਂ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹà©à©°à¨¦à©‡ ਹੋ? +ਕੀ ਤà©à¨¸à©€à¨‚ ਨਿਸ਼ਚਿੱਤ ਇਨà©à¨¹à¨¾à¨‚ {0} ਆਈਟਮਾਂ ਨੂੰ ਹਟਾਉਣਾ ਚਾਹà©à©°à¨¦à©‡ ਹੋ? +ਹਟਾਉਣ ਦੀ ਕਾਰਵਾਈ ਚੱਲ ਰਹੀ ਹੈ... +ਫਾਇਲ ਜਾਂ ਫੋਲਡਰ ਹਟਾਉਣ ਵਿੱਚ ਸਮੱਸਿਆ + +6300 +ਫੋਲਡਰ ਬਣਾਓ +ਫਾਇਲ ਬਣਾਓ +ਫੋਲਡਰ ਨਾਂ: +ਫਾਇਲ ਨਾਂ: +ਨਵਾਂ ਫੋਲਡਰ +ਨਵੀਂ ਫਾਇਲ +ਫੋਲਡਰ ਬਨਾਉਣ ਵਿੱਚ ਸਮੱਸਿਆ +ਫਾਇਲ ਬਨਾਉਣ ਵਿੱਚ ਸਮੱਸਿਆ +6400 +ਟਿੱਪਣੀ +ਟਿੱਪਣੀ (&C): +ਚà©à¨£à©‹ +ਚੋਣ ਰੱਦ ਕਰੋ +ਮਾਸਕ: +6600 +ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ +ਫੋਲਡਰ ਅਤੀਤ +ਡਾਈਗਨੋਸਟਿੱਕ ਸੰਦੇਸ਼ +ਸੰਦੇਸ਼ +7100 +ਕੰਪਿਊਟਰ +ਨੈੱਟਵਰਕ + +ਸਿਸਟਮ +7200 +ਸ਼ਾਮਲ ਕਰੋ +ਕੱਡੋ +ਪਰਖ ਕਰੋ +ਨਕਲ ਉਤਾਰੋ +ਨਵੇਂ ਟਿਕਾਣੇ ਤੇ ਭੇਜੋ +ਹਟਾਓ +ਜਾਣਕਾਰੀ +7300 +ਫਾਇਲ ਹਿੱਸਿਆਂ ਵਿੱਚ ਵੰਡੋ +ਹੇਠਾਂ ਦਿੱਤੇ ਟਿਕਾਣੇ ਉੱਤੇ ਹਿੱਸੇ ਕਰੋ (&S): +ਵੋਲà©à©±à¨®à¨¾à¨‚ ਵਿੱਚ ਵੰਡੋ, ਬਾਈਟ (&v): +ਫਾਇਲ ਹਿੱਸਿਆਂ ਵਿੱਚ ਵੰਡੀ ਜਾ ਰਹੀ ਹੈ... +ਹਿੱਸੇ ਕਰਨ ਦੀ ਤਸਦੀਕ +ਕੀ ਤà©à¨¸à©€à¨‚ ਨਿਸ਼ਚਿੱਤ ਫਾਇਲ ਦੇ {0} ਵੋਲà©à©±à¨®à¨¾à¨‚ ਵਿੱਚ ਹਿੱਸੇ ਕਰਨਾ ਚਾਹà©à©°à¨¦à©‡ ਹੋ? +ਵੋਲà©à©±à¨® ਸਾਈਜ਼ ਅਸਲੀ ਫਾਇਲ ਦੇ ਸਾਈਜ਼ ਤੋਂ ਛੋਟਾ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ +ਵੋਲà©à©±à¨® ਸਾਈਜ਼ ਗਲ਼ਤ ਹੈ +ਦਿੱਤਾ ਗਿਆ ਵੋਲà©à©±à¨® ਸਾਈਜ਼: {0} ਬਾਈਟ।\nਕੀ ਤà©à¨¸à©€à¨‚ ਨਿਸ਼ਚਿੱਤ ਆਕਾਈਵ ਨੂੰ ਦਿੱਤੇ ਗਠਵੋਲà©à©±à¨®à¨¾à¨‚ ਵਿੱਚ ਵੰਡਣਾ ਚਾਹà©à©°à¨¦à©‡ ਹੋ? +7400 +ਫਾਇਲ ਦੇ ਹਿੱਸੇ ਜੋੜੋ +ਹੇਠਾਂ ਦਿੱਤੇ ਟਿਕਾਣੇ ਉੱਤੇ ਹਿੱਸੇ ਜੋੜੋ(&C): +ਹਿੱਸੇ ਜੋੜੇ ਜਾ ਰਹੇ ਹਨ... +ਸਿਰਫ਼ ਪਹਿਲੀ ਫਾਇਲ ਚà©à¨£à©‹ + + +7500 +ਚੈਕਸੱਮ ਗਣਨਾ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ... +ਚੈਕਸੱਮ ਜਾਣਕਾਰੀ +ਡਾਟਾ ਲਈ ਸੀ-ਆਰ-ਸੀ ਚੈਕਸੱਮ: +ਡਾਟਾ ਅਤੇ ਨਾਮਾਂ ਲਈ ਸੀ-ਆਰ-ਸੀ ਚੈਕਸੱਮ: +7600 +ਬੈਂਚਮਾਰਕ +ਮੈਮੋਰੀ ਵਰਤੋਂ: +ਨਪੀੜਨ ਕਾਰਜ +ਖੋਲਣ ਕਾਰਜ +ਦਰਜ਼ਾ +ਕà©à©±à¨² ਦਰਜ਼ਾ +ਇਸ ਸਮੇਂ +ਰੀਸੱਲਟਿੰਗ +ਸੀ-ਪੀ-ਯੂ ਵਰਤੋਂ +ਦਰਜ਼ਾ / ਵਰਤੋਂ +ਪਾਸ: diff --git a/Utils/7-Zip/Lang/pl.txt b/Utils/7-Zip/Lang/pl.txt new file mode 100644 index 000000000..e8d20b880 --- /dev/null +++ b/Utils/7-Zip/Lang/pl.txt @@ -0,0 +1,477 @@ +;!@Lang2@!UTF-8! +; : cienislaw +; : pixel +; 9.07 : F1xat +; 9.33 : Åukasz Maria P. Pastuszczak +; +; +; +; +; +; +; +0 +7-Zip +Polish +Polski +401 +OK +Anuluj + + + +&Tak +&Nie +&Zamknij +Pomoc + +&Kontynuuj +440 +Ta&k na wszystkie +Ni&e na wszystkie +Zatrzymaj +Ponów +&TÅ‚o +&Pierwszy plan +&Wstrzymaj +Wstrzymano +Czy na pewno chcesz anulować? +500 +&Plik +&Edycja +&Widok +&Ulubione +&NarzÄ™dzia +Pomo&c +540 +&Otwórz +Otwórz &wewnÄ…trz +Otwórz na &zewnÄ…trz +Pod&glÄ…d +&Edytuj +ZmieÅ„ &nazwÄ™ +Kopiuj &do... +&PrzenieÅ› do... +&UsuÅ„ +Podzie&l plik... +Złą&cz pliki... +WÅ‚&aÅ›ciwoÅ›ci +Ko&mentarz +Oblicz sumÄ™ kontrolnÄ… +Diff +Utwórz &folder +U&twórz plik +Za&koÅ„cz +Dow&iÄ…zanie +600 +Z&aznacz wszystko +&Odznacz wszystko +Odwróć &zaznaczenie +Zaznacz... +Odznacz... +Zaznacz wedÅ‚ug typu +Odznacz wedÅ‚ug typu +700 +&Duże ikony +&MaÅ‚e ikony +&Lista +&Szczegóły +730 +Nieposortowane +Widok pÅ‚aski +&2 panele +&Paski narzÄ™dzi +Otwórz folder główny +Do góry o jeden poziom +Historia folderów... +&OdÅ›wież +Automatyczne odÅ›wieżanie +750 +Pasek archiwum +Pasek standardowy +Duże przyciski +Pokaż etykiety tekstowe +800 +&Dodaj folder do ulubionych jako +ZakÅ‚adka +900 +&Opcje... +&Test wydajnoÅ›ci +960 +&Zawartość +7-Zip - i&nformacje +1003 +Åšcieżka +Nazwa +Rozszerzenie +Folder +Rozmiar +Rozmiar po spakowaniu +Atrybuty +Utworzony +Ostatnio otwarty +Zmodyfikowany +CiÄ…gÅ‚y +Z komentarzem +Zaszyfrowany +Podzielony przed +Podzielony po +SÅ‚ownik + +Typ +Anty +Metoda +Pochodzenie +System plików +Użytkownik +Grupa +Blok +Komentarz +Pozycja +Prefiks Å›cieżki +Foldery +Pliki +Wersja +Wolumin +Wielowoluminowy +PrzesuniÄ™cie +DowiÄ…zania +Bloki +Woluminy + +64-bitowy +Big-endian +Procesor +Rozmiar fizyczny +Rozmiar nagłówków +Suma kontrolna +Charakterystyki +Adres wirtualny +Numer seryjny woluminu +Krótka nazwa +Generator +Rozmiar sektora +Tryb +DowiÄ…zanie symboliczne +Błąd +CaÅ‚kowity rozmiar +Wolne miejsce +Rozmiar klastra +Etykieta +Nazwa lokalna +Dostawca +Zabezpieczenia NT +Alternatywny strumieÅ„ + +UsuniÄ™ty +Drzewo + + +Rodzaj błędu +Błędy +Błędy +Ostrzeżenia +Ostrzeżenie +Strumienie +Alternatywne strumienie +Rozmiar alternatywnych strumieni +Rozmiar wirtualny +Rozmiar po rozpakowaniu +CaÅ‚kowity rozmiar fizyczny +Numer woluminu +Podtyp +Krótki komentarz +Strona kodowa + + + + + +DowiÄ…zanie +DowiÄ…zanie twarde +I-wÄ™zeÅ‚ +2100 +Opcje +JÄ™zyk +JÄ™zyk: +Edytor +&Edytor: +&Diff: +2200 +System +Skojarz program 7-Zip z: +2301 +Zintegruj program 7-Zip z menu kontekstowym powÅ‚oki +Kaskadowe menu kontekstowe +Elementy menu kontekstowego: +Ikony w menu kontekstowym +2320 + + +Otwórz archiwum +Wypakuj pliki... +Dodaj do archiwum... +Testuj archiwum +Wypakuj tutaj +Wypakuj do {0} +Dodaj do {0} +Skompresuj i wyÅ›lij e-mailem... +Skompresuj do {0} i wyÅ›lij e-mailem +2400 +Foldery +Folder roboczy +&Systemowy folder tymczasowy +&Bieżący +&Wskazany: +Użyj tylko dla dysków wymiennych +Wskaż lokalizacjÄ™ dla tymczasowych plików archiwów. +2500 +Ustawienia +Pokaż element „..†+Pokaż prawdziwe ikony plików +Pokaż menu systemowe +Zaznaczaj &caÅ‚y rzÄ…d +&Pokaż linie siatki +Pojedyncze klikniÄ™cie otwiera element +&Alternatywny tryb zaznaczania +&Użyj dużych stron pamiÄ™ci +2900 +7-Zip - informacje +7-Zip jest programem darmowym. +3000 +System nie może przydzielić wymaganej iloÅ›ci pamiÄ™ci +Nie wykryto błędów +Zaznaczono {0} obiekt(ów) +Nie można utworzyć folderu „{0}†+Operacje aktualizacji nie sÄ… obsÅ‚ugiwane dla tego archiwum. +Nie można otworzyć pliku „{0}†jako archiwum +Nie można otworzyć zaszyfrowanego archiwum „{0}â€. NieprawidÅ‚owe hasÅ‚o? +NieobsÅ‚ugiwany typ archiwum +Plik {0} już istnieje +Plik „{0}†zostaÅ‚ zmodyfikowany.\nCzy chcesz zaktualizować go w archiwum? +Nie można zaktualizować pliku\n„{0}†+Nie można uruchomić edytora. +Plik wyglÄ…da na wirusa (nazwa pliku zawiera dÅ‚ugi ciÄ…g spacji). +Operacja nie może być wywoÅ‚ana z folderu, który ma dÅ‚ugÄ… Å›cieżkÄ™. +Musisz zaznaczyć jeden plik +Musisz zaznaczyć jeden lub wiÄ™cej plików +Zbyt dużo elementów +3300 +Wypakowywanie +Kompresowanie +Testowanie +Otwieranie... +Skanowanie... +3400 +Wypakuj +&Wypakuj do: +Wskaż lokalizacjÄ™ dla wypakowanych plików. +3410 +Tryb Å›cieżek: +PeÅ‚ne Å›cieżki +Bez Å›cieżek +BezwzglÄ™dne Å›cieżki +WzglÄ™dne Å›cieżki +3420 +Tryb nadpisywania: +Monituj przed nadpisaniem +Nadpisuj bez monitowania +PomiÅ„ istniejÄ…ce pliki +Automatycznie zmieÅ„ nazwy +Automatycznie zmieÅ„ nazwy istniejÄ…cych plików +3430 +Wyeliminuj podwojenie folderu głównego +Przywróć zabezpieczenia plików +3500 +Potwierdź zamianÄ™ pliku +Folder docelowy zawiera już przetwarzany plik. +Czy chcesz zamienić istniejÄ…cy plik +na nastÄ™pujÄ…cy? +{0} bajtów +Automatycznie &zmieÅ„ nazwy +3700 +NieobsÅ‚ugiwana metoda kompresji pliku „{0}â€. +Błąd danych w „{0}â€. Plik jest uszkodzony. +CRC nie powiodÅ‚a siÄ™ dla „{0}â€. Plik jest uszkodzony. +Błąd danych w zaszyfrowanym pliku „{0}â€. NieprawidÅ‚owe hasÅ‚o? +CRC nie powiodÅ‚a siÄ™ dla zaszyfrowanego pliku „{0}â€. NieprawidÅ‚owe hasÅ‚o? +3710 +NieprawidÅ‚owe hasÅ‚o? +3721 +NieobsÅ‚ugiwana metoda kompresji +Błąd danych +CRC nie powiodÅ‚a siÄ™ +NiedostÄ™pne dane +Nieoczekiwany koniec danych +Pewne dane znajdujÄ… siÄ™ za koÅ„cem bloku użytecznych danych +Nie rozpoznano archiwum +Błąd nagłówków +3763 +NiedostÄ™pny poczÄ…tek archiwum +Niepotwierdzony poczÄ…tek archiwum + + + +NieobsÅ‚ugiwana funkcja +3800 +Wprowadź hasÅ‚o +Wprowadź hasÅ‚o: +Wprowadź ponownie hasÅ‚o: +Pokaż &hasÅ‚o +HasÅ‚a nie zgadzajÄ… siÄ™ +W haÅ›le używaj tylko liter alfabetu angielskiego, cyfr i znaków specjalnych (!, #, $, ...) +HasÅ‚o jest zbyt dÅ‚ugie +HasÅ‚o +3900 +UpÅ‚ynęło czasu: +PozostaÅ‚o czasu: +CaÅ‚kowity rozmiar: +Szybkość: +Przetworzono: +Współczynnik kompresji: +Błędy: +Archiwów: +4000 +Dodaj do archiwum +&Archiwum: +&Tryb aktualizacji: +&Format archiwum: +StopieÅ„ &kompresji: +&Metoda kompresji: +&Rozmiar sÅ‚ownika: +Rozmiar &sÅ‚owa: +Rozmiar bloku ciÄ…gÅ‚ego: +Liczba wÄ…tków: +&Parametry: +Opcje +&Utwórz archiwum SFX +Kompresuj pliki współdzielone +Szyfrowanie +Metoda szyfrowania: +&Zaszyfruj nazwy plików +Użycie pamiÄ™ci dla kompresji: +Użycie pamiÄ™ci dla dekompresji: +UsuÅ„ pliki po skompresowaniu +4040 +Zachowaj dowiÄ…zania symboliczne +Zachowaj dowiÄ…zania twarde +Zachowaj alternatywne strumienie danych +Zachowaj zabezpieczenia plików +4050 +Bez kompresji +Najszybsza +Szybka +Normalna +Maksymalna +Ultra +4060 +Dodaj i zamieÅ„ pliki +Aktualizuj i dodaj pliki +OdÅ›wież istniejÄ…ce pliki +Synchronizuj pliki +4070 +PrzeglÄ…daj +Wszystkie pliki +NieciÄ…gÅ‚y +CiÄ…gÅ‚y +6000 +Kopiuj +PrzenieÅ› +Kopiuj do: +PrzenieÅ› do: +Kopiowanie... +Przenoszenie... +Zmienianie nazwy... +Wybierz folder docelowy. +Operacja nie jest obsÅ‚ugiwana dla tego folderu. +Błąd podczas zmiany nazwy pliku lub folderu +Potwierdź kopiowanie plików +Czy na pewno chcesz skopiować pliki do archiwum +6100 +Potwierdź usuniÄ™cie pliku +Potwierdź usuniÄ™cie folderu +Potwierdź usuniÄ™cie wielu plików +Czy na pewno chcesz usunąć plik „{0}â€? +Czy na pewno chcesz usunąć folder „{0}†i całą zawartość? +Elementów: {0} - czy na pewno chcesz je usunąć? +Usuwanie... +Błąd podczas usuwania pliku lub folderu +System nie może przenieść pliku o dÅ‚ugiej Å›cieżce do Kosza +6300 +Utwórz folder +Utwórz plik +Nazwa folderu: +Nazwa pliku: +Nowy folder +Nowy plik +Błąd podczas tworzenia folderu +Błąd podczas tworzenia pliku +6400 +- komentarz +&Komentarz: +Zaznacz +Odznacz +Maska: +6600 +WÅ‚aÅ›ciwoÅ›ci +Historia folderów +Komunikaty diagnostyczne +Komunikat +7100 +Komputer +Sieć +Dokumenty +System +7200 +Dodaj +Wypakuj +Testuj +Kopiuj +PrzenieÅ› +UsuÅ„ +Informacje +7300 +Podziel plik +&Podziel do: +Rozmiar &woluminów (bajty): +Dzielenie... +Potwierdź podziaÅ‚ +Czy na pewno chcesz podzielić plik na {0} woluminów? +Rozmiar woluminu musi być mniejszy od rozmiaru oryginalnego pliku +NieprawidÅ‚owy rozmiar woluminu +OkreÅ›lony rozmiar woluminu: {0} bajtów.\nCzy na pewno chcesz podzielić archiwum na takie woluminy? +7400 +Złącz pliki +&Złącz do: +ÅÄ…czenie... +Zaznacz tylko pierwszÄ… część podzielonego pliku +To nie jest poprawna część podzielonego pliku +Nie można odnaleźć co najmniej jednej części podzielonego pliku +7500 +Obliczanie sumy kontrolnej... +Informacja o sumie kontrolnej +Suma kontrolna CRC dla danych: +Suma kontrolna CRC dla danych i nazw: +7600 +Test wydajnoÅ›ci +Użycie pamiÄ™ci: +Kompresja +Dekompresja +Ocena +CaÅ‚kowita ocena +Aktualnie +Wynik +Użycie CPU +Ocena / Użycie +Przebiegi: +7700 +DowiÄ…zanie +Dowiąż +Element źródÅ‚owy: +Element docelowy: +7710 +Rodzaj dowiÄ…zania +DowiÄ…zanie twarde +DowiÄ…zanie symboliczne pliku +DowiÄ…zanie symboliczne katalogu +Połączenie katalogów diff --git a/Utils/7-Zip/Lang/ps.txt b/Utils/7-Zip/Lang/ps.txt new file mode 100644 index 000000000..baef904d0 --- /dev/null +++ b/Utils/7-Zip/Lang/ps.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.53 : 2007-12-26 : Pathanisation Project : pathanisation.pakhtosoft.com +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Pashto +پښتو +401 +Ù‡ÙˆÚ©Û +بندول + + + +هو& +نه& +بندول& +مرسته + +پرمختلل& +440 +ټولو ته هو& +Ù¼&ولو ته نه +تمول +بياپÛلول +شاليد& +پاسليد& +Úنډول& +Ú…Ù†Ú‰ÛØ¯Ù„ÛŒ +په ډاډمنه توګه غواړئ Ú†Û Ø¨Ù†Ø¯ ÙŠÛ Ú©Ú“Ø¦ØŸ +500 +دوتنه& +سمون& +ليد& +Ø®&واپوري +توکي& +مرسته& +540 +پرانيستل& +دننه& پرانيستل +بهر Ù¾&رانيستل +ليد& +سمون& +بي&انومول +...ته Ù„Ù…ÛØ³Ù„& +...ته خوÚول& +ړنګول& +...دوتنه چول& +...Ø¯ÙˆØªÙ†Û ÙŠÙˆÚ&ايول +ÚØ§Ù†ØªÙŠØ§ÙˆÛ +څرګند&ون +Ú†Ûکسم Ø´Ù…ÛØ±Ù„ + +Ù¾ÙˆÚšÛ Ø¬ÙˆÚ“ÙˆÙ„ +دوتنه جوړول +Ùˆ&تون +600 +ټول ټاکل& +ټول ناټاکل +ټاکنه نسکورول& +...ټاکل +...ناټاکل +پر ډول ټاکل +پر ډول ناټاکل +700 +لو&ÛŒ انÚورنونه +Ùˆ&اړه انÚورنونه +Ù„Ú“& +خبرتياوÛ& +730 +ناڼلي +پوړ ليد +۲†چوکاټه& +توکپټÛ& +ÙˆÙ„Û Ù¾ÙˆÚšÛ Ù¾Ø±Ø§Ù†ÙŠØ³ØªÙ„ +يو Ú©Ú†Ù‡ برول +...پوښيو مخينه +تاندول& +750 +ارشيو توکپټه +کره توکپټه +Ù„ÙˆÛŒÛ ØªÚ¼Û +د تڼيو Ù„ÙŠÚ©Ù†Û ÚšÙˆØ¯Ù„ +800 +Ù¾ÙˆÚšÛ Ø®ÙˆØ§Ù¾ÙˆØ±Ùˆ ته زياتول Ù„Ú©Ù‡& +Ù†ÚšÙ‡ +900 +...غوراوي& +بنچمارک& +960 +...منÚپانګه& +...Û·â€-زÛÙ¾ په اړه& +1003 +يونلور +نوم +شاتاړی +Ù¾ÙˆÚšÛ +Ú©Ú† +بنډل شوی Ú©Ú† +Ú…Ø§Ù†ØªÙŠØ§ÙˆÛ +جوړشوی +رسی +بدلون +Ú©Ù„Ú© +څرګندون +کوډييز +چول Ù…Ø®Ú©ÚšÛ +چول وروسته +ويÛپانګه +CRC +ډول +مخال٠+Ù„Ûله +کوربه چغ +دوتنه غونډال +کارن +ډله +غونډ +څرګندون +ÚØ§ÛŒ +يونلور مختاړی +Ù¾ÙˆÚšÛ +Ø¯ÙˆØªÙ†Û +نسخه +ډکون +ګڼډکون +Ø§ÙØ³ÛÙ¼ +Ù¾Ûوندونه +غوڼدونه +ډکونونه + + + + + + + + + + + + + + + +ØªÛØ±ÙˆØªÙ†Ù‡ +بشپړ Ú©Ú† +Ù¾Ø§ØªÛ ØªØ´Ù‡ +Úومبک Ú©Ú† +Ù†ÚšÚ©Ù‡ +ÚØ§ÛŒÙŠ Ù†ÙˆÙ… +برابروونى +2100 +غوراوي +ژبه +:ژبه +سمونګر +:سمونګر& + +2200 +غونډال +:له Û·â€-زÛÙ¾ سره ملول +2301 +Û·â€-زÛÙ¾ Ø³ÙŠÙ¾Û ØªÚ“Ø§Ùˆ ØºÙˆØ±Ù†Û Ú©ÚšÛ Ø²ÙŠØ§ØªÙˆÙ„ +Úړبهيزه تړاو ØºÙˆØ±Ù†Û +:تړاو ØºÙˆØ±Ù†Û ØªÙˆÚ©ÙŠ +2320 +<پوښÛ> +<ارشيو> +ارشيو پرانيستل +...Ø¯ÙˆØªÙ†Û ÙˆÙŠØ³ØªÙ„ +...ارشيو ته زياتول +ارشيو ازمويل +دلته ويستل +ته ويستل {0}†+ته زياتول {0} +...Ø²ÛØ±Ù„ او برÛÚšÙ„ +ته زياتول او برÛÚšÙ„ {0} +2400 +Ù¾ÙˆÚšÛ +&کارنه Ù¾ÙˆÚšÛ +&لنډمهاله غونډال Ù¾ÙˆÚšÛ +&Ø§ÙˆØ³Ù†Û +&Ú…Ø§Ù†Ú«Ú“Û +ÙŠÙˆØ§Ø²Û Ù„Ù‡ Ù„ÛØ±Ûدونکو چليÚونو لپاره کارول +.د لنډمهاله ارشيو دوتنو لپاره ÚØ§ÛŒ وټاکئ +2500 +Ø§Ù…Ø³ØªÙ†Û +توکي ښودل ".." +د دوتنو ريښتيني انÚورنونه ښودل +غونډال ØºÙˆØ±Ù†Û ÚšÙˆØ¯Ù„ +ټول کيل ټاکل& +Ú©Ø±ÚšÛ ÚšÙˆØ¯Ù„ + +انډوليز Ù¼Ø§Ú©Ù†Û Ø§Ú©Ø±& +لوی ياد مخونه کارول& +2900 +Û·â€-زÛÙ¾ په اړه +.دا يو وړيا ساوتری دی. خو، په نومکښلو سره د ساوتري د پرمختګ ملاتړ کولی شئ +3000 + +Ù‡ÛÚ… ØªÛØ±ÙˆØªÙ†Ù‡ نشته +ټاکل شوي څيزونه {0} +Ù¾ÙˆÚšÛ Ø¬ÙˆÚ“ÙˆÙ„ÛŒ نه شي '{0}' +.اوسمهاله چلښتونه Ø¯Û Ø§Ø±Ø´ÙŠÙˆ لپاره منلي نه دي +'{0}' دوتنه Ù„Ú©Ù‡ د ارشيو نه شي پرانيستلی +کوډييز ارشيو پرانيستلی نه شي '{0}'. ناسمه ØªÛØ±Ù†ÙˆÙŠÛØŸ + + +.'{0}' دوتنه Ú©ÚšÛ Ø¨Ø¯Ù„ÙˆÙ† راغلی\nپه ارشيو Ú©ÚšÛ ÙŠÛ Ø§ÙˆØ³Ù…Ù‡Ø§Ù„ÙˆÙ„ غواړئ؟ +'{0}'\nدوتنه اوسمهالولی نه شي +سمونګر Ù¾Ûلولی نه شي + + + + +Ú‰ÛØ± زيات توکي +3300 +وباسي +Ø²ÛØ±Ù„ Ú©ÙŠÚ–ÙŠ +ازموينه +...پرانيستل Ú©ÙŠÚ–ÙŠ +...Úیريږي +3400 +ويستل +:ته Ùˆ&يستل +.د ويستلو دوتنو لپاره يو ÚØ§ÛŒ وټاکئ +3410 +يونلور اکر +بشپړ يونلورنومونه +Ù‡ÛÚ… يونلورنومونه +3420 +سرليکلو اکر +سرليکلو نه Ù…Ø®Ú©ÚšÛ Ù¾ÙˆÚšØªÙ„ +Ø¨Û Ù„Ù‡ Ù¾Ø§Ø±Ù„ÙŠÚ©Û Ø³Ø±Ù„ÙŠÚ©Ù„ +شته Ø¯ÙˆØªÙ†Û Ù¾Ø±Ûښودل +خپله بيانومول +شته Ø¯ÙˆØªÙ†Û Ø®Ù¾Ù„Ù‡ بيانومول +3500 +دوتنه ÚØ§Ûناستی باورييل +.موخه Ù¾ÙˆÚšÛ Ø¯Ù…Ø®Ù‡ Ø¨Ù‡ÙŠØ±Ù„Û Ø¯ÙˆØªÙ†Û Ù„Ø±ÙŠ +غواړئ Ú†Û Ø´ØªÙ‡ دوتنه ÚØ§ÛÙ†Ø§Ø³ØªÛ Ú©Ú“Ø¦ +له Ø¯Û Ø³Ø±Ù‡ØŸ +باÛټه {0} +خپله ب&يانومول +3700 +.لپاره Ù†Ø§Ù…Ù†Ù„Û Ø²ÛØ±Ù†Û Ù„Ûله '{0}' +.Ú©ÚšÛ Ø§ÙˆÙ…ØªÙˆÚ© ستونزه '{0}' دوتنه ماته ده +.Ú©ÚšÛ Ø³Ø±Ø³ Ù¾Ø§ØªÛ Ø±Ø§ØºÛŒ '{0}' دوتنه ماته ده +Ú©ÙˆÚ‰ÙŠÙŠØ²Û Ø¯ÙˆØªÙ†Û '{0}' Ú©ÚšÛ Ø§ÙˆÙ…ØªÙˆÚ© ستونزه. ناسمه ØªÛØ±Ù†ÙˆÙŠÛØŸ +په Ú©ÙˆÚ‰ÙŠÙŠØ²Û Ø¯ÙˆØªÙ†Û '{0}' Ú©ÚšÛ Ø³Ø±Ø³ Ù¾Ø§ØªÛ Ø±Ø§ØºÛŒ. ناسمه ØªÛØ±Ù†ÙˆÙŠÛØŸ +3800 +ØªÛØ±Ù†ÙˆÙŠÛ وليکئ +:ØªÛØ±Ù†ÙˆÙŠÛ وليکئ +:ØªÛØ±Ù†ÙˆÙŠÛ بيا وليکئ +ØªÛØ±Ù†ÙˆÙŠÛ ښودل& +ØªÛØ±Ù†ÙˆÙŠÛ سمون نه خوري +ØªÛØ±Ù†ÙˆÙŠÛ لپاره ÙŠÙˆØ§Ø²Û Ø§Ù†Ú«Ø±ÙŠØ²ÙŠ توري، Ø´Ù…ÛØ±Û او ÚØ§Ù†Ú«Ú“ÙŠ Ù„ÙˆÚšÛ (!, #, $, ...) وکاروئ +ØªÛØ±Ù†ÙˆÙŠÛ Ú‰ÛØ±Ù‡ اوږده ده +ØªÛØ±Ù†ÙˆÙŠÛ +3900 +:ØªÛØ± مهال +:Ù¾Ø§ØªÛ Ù…Ù‡Ø§Ù„ +:بشپړ Ú©Ú† +:چټکتیا +:بهيرلی +:Ø²ÛØ±Ù„Ùˆ نسبت +:ØªÛØ±ÙˆØªÙ†Ù‡ +:ارشيونه +4000 +ارشيو ته زياتول +:ارشيو& +:اوسمهاليز اکر& +:ارشيو بڼه& +:Ø²ÛØ±Ù†Û &Ú©Ú†Ù‡ +:Ø²ÛØ±Ù†Û &Ù„Ûله +:ويÛÙ¾Ø§Ù†Ú«Û Ú©Ú†Ù‡& +:ÙˆÙŠÛ Ú©Ú†& +:Ú©Ù„Ú© غونډ Ú©Ú† +:د مبي مزيو Ø´Ù…ÛØ± +:ارزښتمني& +غوراوي +ارشيو جوړول SF&X +ÙˆÙ†Ú‰ÙˆÙ„Û Ø¯ÙˆØªÙ†Û Ø²ÛØ±Ù„ +کوډییزونه +:Ú©ÙˆÚ‰ÛŒÛŒØ²ÙˆÙ†Û Ù„Ûله +دوتنه &نومونه کوډييزول +:Ø²ÛØ±Ù„Ùˆ لپاره ياد کارونه +:Ù†Ø§Ø²ÛØ±Ù„Ùˆ لپاره ياد کارونه +4050 +Ø²ÛØ±Ù…Ù„ +Ú‰ÛØ± Ú†Ù¼Ú© +Ú†Ù¼Ú© +ليوی +زيات +Ú‰ÛØ± زيات Ú†Ù¼Ú© +4060 +Ø¯ÙˆØªÙ†Û Ø²ÙŠØ§ØªÙˆÙ„ او ÚØ§Ûناستول +Ø¯ÙˆØªÙ†Û Ø§ÙˆØ³Ù…Ù‡Ø§Ù„ÙˆÙ„ او زياتول +شته Ø¯ÙˆØªÙ†Û ØªØ§Ù†Ø¯ÙˆÙ„ +Ø¯ÙˆØªÙ†Û Ù‡Ù…Ù…Ù‡Ø§Ù„ÙˆÙ„ +4070 +لټول +Ù¼ÙˆÙ„Û Ø¯ÙˆØªÙ†Û +نا-Ú©Ù„Ú© +Ú©Ù„Ú© +6000 +Ù„Ù…ÛØ³Ù„ +خوÚول +:ته Ù„Ù…ÛØ³Ù„ +:ته خوÚول +...لميسل Ú©ÙŠÚ–ÙŠ +...خوÚÙŠÚ–ÙŠ +بيانوميږي +.موخه Ù¾ÙˆÚšÛ ÙˆÙ¼Ø§Ú©Ø¦ +.چلښت منلی نه دی +Ø¯ÙˆØªÙ†Û ÙŠØ§ Ù¾ÙˆÚšÛ Ø¨ÙŠØ§Ù†ÙˆÙ…ÙˆÙ„Ùˆ ستونزه +Ø¯ÙˆØªÙ†Û Ù„Ù…ÛØ³Ù„ باورييل +په ډاډمنه توګه Ø¯ÙˆØªÙ†Û Ø§Ø±Ø´ÙŠÙˆ ته Ù„Ù…ÛØ³Ù„ غواړئ؟ +6100 +Ø¯ÙˆØªÙ†Û Ú“Ù†Ú«ÙˆÙ†Ù‡ باورييل +Ù¾ÙˆÚšÛ Ú“Ù†Ú«ÙˆÙ†Ù‡ باورييل +Ú«Ú¼Ùˆ دوتنو ړنګونه باورييل +په ډاډمنه توګه '{0}' ړنګول غواړئ؟ +په ډاډمنه توګه '{0}' Ù¾ÙˆÚšÛ Ø§Ùˆ د Ø¯Û Ù¼ÙˆÙ„Ù‡ منÚپانګه ړنګول غواړئ؟ +په ډاډمنه توګه دا {0} توکي ړنګول غواړئ؟ +...Ú“Ù†Ú«ÙŠÚ–ÙŠ +Ø¯ÙˆØªÙ†Û ÙŠØ§ Ù¾ÙˆÚšÛ Ú“Ù†Ú«ÙˆÙ„Ùˆ ستونزه + +6300 +Ù¾ÙˆÚšÛ Ø¬ÙˆÚ“ÙˆÙ„ +دوتنه جوړول +:Ù¾ÙˆÚšÛ Ù†ÙˆÙ… +:دوتنه نوم +Ù†ÙˆÛ Ù¾ÙˆÚšÛ +Ù†ÙˆÛ Ø¯ÙˆØªÙ†Ù‡ +Ù¾ÙˆÚšÛ Ø¬ÙˆÚ“ÙˆÙ„Ùˆ ستونزه +Ø¯ÙˆØªÙ†Û Ø¬ÙˆÚ“ÙˆÙ„Ùˆ ستونزه +6400 +څرګندون +:څرګندون& +ټاکل +ناټاکل +:ÙˆØ±Ø¨ÙˆØ²Û +6600 +ÚØ§Ù†ØªÙŠØ§ÙˆÛ +پوښيو مخينه +Ø±Ù†Ú Ù†ÙˆÙ…ÛØ±Ù†Û استوزه +استوزه +7100 +سولګر +جال + +غونډال +7200 +زياتول +ويستل +ازمويل +Ù„Ù…ÛØ³Ù„ +خوÚول +ړنګول +Ø®Ø¨Ø±ØªÙŠØ§ÙˆÛ +7300 +دوتنه چول +:ته چول& +:ډکونونو، باÛټونو ته چول& +...چول Ú©ÙŠÚ–ÙŠ +چونه باورييل +په ډاډمنه توګه دوتنه په {0} ډکونونو ÙˆÛØ´Ù„ غواړئ؟ +ډکون Ú©Ú† بايد د Ø¯ÙˆØªÙ†Û Ø§Ø± Ú©Ú† نه ÙˆÚ“ÙˆÚ©ÛŒ وي +ناسم ډکون Ú©Ú† +.باÛټه {0} :ټاکلی ډکون Ú©Ú†\nپه ډاډمنه توګه غواړئ Ú†Û Ø§Ø±Ø´ÙŠÙˆ په Ø¯Ø§Ø³Û Ú‰Ú©ÙˆÙ†ÙˆÙ†Ùˆ وويشئ؟ +7400 +Ø¯ÙˆØªÙ†Û ÙŠÙˆÚØ§ÙŠÙˆÙ„ +:ته ÙŠÙˆÚØ§ÙŠÙˆÙ„& +...ÙŠÙˆÚØ§ÙŠÚ–ÙŠ +ÙŠÙˆØ§Ø²Û Ù„Ù…Ú“Û Ø¯ÙˆØªÙ†Ù‡ ټاکل + + +7500 +...Ú†Ûکسم Ø´Ù…ÛØ±ÙŠÚ–ÙŠ +Ú†Ûکسم Ø®Ø¨Ø±ØªÙŠØ§ÙˆÛ +:Ú†Ûکسم CRC اومتوک لپاره +:Ú†Ûکسم CRC اومتوک او نومونو لپاره +7600 +بنچمارک +:ياد کارونه +Ø²ÛØ±ÙŠÚ–ÙŠ +Ù†Ø§Ø²ÛØ±ÙŠÚ–ÙŠ +کچونه +بشپړه کچونه +اوسنی +پايليز +مبي کارونه +کچونه / کارونه +:تيريږي diff --git a/Utils/7-Zip/Lang/pt-br.txt b/Utils/7-Zip/Lang/pt-br.txt new file mode 100644 index 000000000..a44134242 --- /dev/null +++ b/Utils/7-Zip/Lang/pt-br.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; : Francisco Jr +; 4.37 : Fabricio Biazzotto +; 15.07 : Atualizado por Felipe +; +; +; +; +; +; +; +; +0 +7-Zip +Portuguese Brazilian +Português Brasileiro +401 +OK +Cancelar + + + +&Sim +&Não +&Fechar +Ajuda + +&Continuar +440 +Sim pra &Todos +Não pra T&odos +Parar +Reiniciar +&Em 2º plano +&Em 1º plano +&Pausar +Pausado +Você tem certeza que você quer cancelar? +500 +&Arquivo +&Editar +&Visualizar +F&avoritos +&Ferramentas +&Ajuda +540 +&Abrir +Abrir &por Dentro +Abrir p&or Fora +&Visualizar +&Editar +Re&nomear +&Copiar Para... +&Mover Para... +&Apagar +&Dividir arquivo... +Com&binar arquivos... +P&ropriedades +Comen&tário +Calcular checksum +Diff +Criar Pasta +Criar Arquivo +S&air +Link +&Correntes Alternantes +600 +Selecionar &Tudo +Desmarcar Tudo +&Inverter Seleção +Selecionar... +Desmarcar... +Selecionar por Tipo +Desfazer seleção por Tipo +700 +Ãco&nes Grandes +Ãc&ones Pequenos +&Lista +&Detalhes +730 +Desorganizado +Visualização Plana +&2 Painéis +&Barra de Ferramentas +Abrir a Pasta Raiz +Um Nível Acima +Histórico das Pastas... +&Atualizar +Auto-Atualizar +750 +Barra de Ferramentas do Arquivo Compactado +Barra de Ferramentas Padrão +Botões Grandes +Mostrar o Texto dos Botões +800 +&Adicionar a pasta aos Favoritos como +Favorito +900 +&Opções... +&Benchmark +960 +&Conteúdo... +&Sobre o 7-Zip... +1003 +Caminho +Nome +Extensão +Pasta +Tamanho +Tamanho Compactado +Atributos +Criado +Acessado +Modificado +Sólido +Comentado +Criptografado +Dividir Antes +Dividir Depois +Dicionário + +Tipo +Anti +Método +Sistema Operacional Hospedeiro +Sistema de Arquivos +Usuário +Grupo +Bloco +Comentário +Posição +Prefixo do Caminho +Pastas +Arquivos +Versão +Volume +Multivolume +Offset +Links +Blocos +Volumes + +64 bits +Big-endian +CPU +Tamanho Físico +Tamanho dos Cabeçalhos +Checksum +Características +Endereço Virtual +ID +Nome Curto +Aplicativo Criador +Tamanho do Setor +Modo +Link Simbólico +Erro +Tamanho Total +Espaço Livre +Tamanho do Cluster +Rótulo +Nome Local +Provedor +Segurança da NT +Corrente Alternante +Aux +Apagado +É Ãrvore + + +Tipo de Erro +Erros +Erros +Avisos +Aviso +Correntes +Correntes Alternantes +Tamanho das Correntes Alternantes +Tamanho Virtual +Tempo Descompactado +Total do Tamanho Físico +Ãndice do Volume +Sub-Tipo +Comentário Curto +Página do Código + + + +Tamanho da Cauda +Tamanho do Toco Embutido +Link +Link Rígido +iNode + +Somente-Leitura +2100 +Opções +Idioma +Idioma: +Editor +&Editor: +&Diff: +2200 +Sistema +Associar o 7-Zip com: +Todos os usuários +2301 +Integrar o 7-Zip ao menu de contexto do shell +Menu de contexto em cascata +Itens do menu de contexto: +Ãcones no menu de contexto +2320 + + +Abrir arquivo compactado +Extrair arquivos... +Adicionar ao arquivo compactado... +Testar arquivo compactado +Extrair Aqui +Extrair para {0} +Adicionar para {0} +Comprimir e enviar por email... +Comprimir para {0} e enviar por email +2400 +Pastas +&Pasta de trabalho +&Pasta temporária do sistema +&Atual +&Especificada: +Usar só pra drives removíveis +Especifique um local pros arquivos compactados temporários. +2500 +Configurações +Mostrar o item ".." +Mostrar os ícones reais dos arquivos +Mostrar o menu do sistema +&Selecionar a linha inteira +Mostrar as &linhas de grade +Clique único pra abrir um item +&Modo de seleção alternativo +Usar &grandes páginas de memória +2900 +Sobre o 7-Zip +7-Zip é um software grátis +3000 +O sistema não pôde alocar a quantia requerida de memória +Não há erros +{0} objeto(s) selecionado(s) +Não pôde criar a pasta '{0}' +Operações de atualização não são suportadas por este arquivo compactado. +Não pôde abrir o arquivo '{0}' como arquivo compactado +Não pôde abrir o arquivo compactado encriptado '{0}'. Senha errada? +Tipo de arquivo compactado não suportado +O arquivo {0} já existe +O arquivo '{0}' foi modificado.\nVocê quer atualizá-lo no arquivo compactado? +Não pôde atualizar o arquivo\n'{0}' +Não pôde iniciar o editor. +O arquivo parece um vírus (o nome do arquivo contém espaços longos no nome). +A operação não pode ser chamada de uma pasta que tem um caminho longo. +Você deve selecionar um arquivo +Você deve selecionar um ou mais arquivos +Itens demais +Não pôde abrir o arquivo como {0} arquivo compactado +O arquivo está aberto como {0} arquivo compactado +O arquivo compactado está aberto com o offset +3300 +Extraindo +Comprimindo +Testando +Abrindo... +Escaneando... +Removendo +3320 +Adicionando +Atualizando +Analisando +Replicando +Re-compactando +Ignorando +Apagando +Criando cabeçalho +3400 +Extrair +E&xtrair para: +Especifique um local pros arquivos extraídos. +3410 +Modo do caminho: +Nomes dos caminhos completos +Sem nomes de caminhos +Nomes dos caminhos absolutos +Nomes dos caminhos relativos +3420 +Modo de sobrescrição: +Perguntar antes de sobrescrever +Sobrescrever sem alertar +Ignorar os arquivos existentes +Auto-renomear +Auto-renomear os arquivos existentes +3430 +Eliminar duplicação da pasta raiz +Restaurar a segurança do arquivo +3500 +Confirmar a Substituição dos Arquivos +A pasta destino já contém o arquivo processado. +Você gostaria de substituir o arquivo existente +por este? +{0} bytes +A&uto-Renomear +3700 +Método de compressão não suportado por '{0}'. +Erro nos dados de '{0}'. O arquivo está danificado. +O CRC falhou em '{0}'. O arquivo está danificado. +Erros nos dados do arquivo encriptado '{0}'. Senha errada? +O CRC falhou no arquivo encriptado '{0}'. Senha errada? +3710 +Senha errada? +3721 +Método de compressão não suportado +Erro dos dados +O CRC falhou +Dados indisponíveis +Fim inesperado dos dados +Há alguns dados após o fim da carga dos dados +Não é arquivo compactado +Erro dos Cabeçalhos +Senha errada +3763 +Início indisponível do arquivo compactado +Início não confirmado do arquivo compactado + + + +Função não suportada +3800 +Inserir senha +Inserir senha: +Re-inserir a senha: +&Mostrar senha +As senhas não combinam +Usar apenas letras em Inglês, números e caracteres especiais (!, #, $, ...) para a senha +A senha é muito longa +Senha +3900 +Tempo decorrido: +Tempo restante: +Tamanho total: +Velocidade: +Processados: +Taxa de compressão: +Erros: +Arquivos: +4000 +Adicionar ao arquivo compactado +&Arquivo compactado: +&Modo de atualização: +Formato do &arquivo compactado: +Nível da &compressão: +Método de &compressão: +&Tamanho do dicionário: +&Tamanho da palavra: +Tamanho do bloco sólido: +Número de threads da CPU: +&Parâmetros: +Opções +Criar ar&quivo compactado SFX +Comprimir arquivos compartilhados +Encriptação +Método de encriptação: +Criptografar os &nomes dos arquivos +Uso de memória pra Compressão: +Uso de memória para Descompressão: +Apagar arquivos após a compressão +4040 +Armazenar links simbólicos +Armazenar links rígidos +Armazenar correntes de dados alternantes +Armazenar segurança do arquivo +4050 +Armazenar +Mais rápida +Rápida +Normal +Máximo +Ultra +4060 +Adicionar e substituir arquivos +Atualizar e adicionar arquivos +Atualizar arquivos existentes +Sincronizar arquivos +4070 +Navegar +Todos os Arquivos +Não-sólido +Sólido +6000 +Copiar +Mover +Copiar para: +Mover para: +Copiando... +Movendo... +Renomeando... +Selecionar a pasta destino. +A operação não é suportada por esta pasta. +Erro ao Renomear o Arquivo ou Pasta +Confirmar a Cópia do Arquivo +Você tem certeza que você quer copiar os arquivos pra dentro do arquivo compactado? +6100 +Confirmar a Exclusão do Arquivo +Confirmar a Exclusão da Pasta +Confirmar a Exclusão de Múltiplos Arquivos +Você tem certeza que você quer apagar '{0}'? +Você tem certeza que você quer apagar a pasta '{0}' e todo o conteúdo dela? +Você tem certeza que você quer apagar estes {0} itens? +Apagando... +Erro ao Apagar o Arquivo ou Pasta +O sistema não pode mover um arquivo com caminho longo para o Recycle Bin +6300 +Criar Pasta +Criar Arquivo +Nome da pasta: +Nome do Arquivo: +Nova Pasta +Novo Arquivo +Erro ao Criar a Pasta +Erro ao Criar o Arquivo +6400 +Comentário +&Comentário: +Selecionar +Desmarcar +Máscara: +6600 +Propriedades +Histórico das Pastas +Mensagens de diagnóstico +Mensagem +7100 +Computador +Rede +Documentos +Sistema +7200 +Adicionar +Extrair +Testar +Copiar +Mover +Apagar +Info +7300 +Dividir Arquivo +&Dividir para: +Dividir em &volumes, bytes: +Dividindo... +Confirmar a Divisão +Você tem certeza que você quer dividir o arquivo em {0} volumes? +O tamanho do volume deve ser menor do que o tamanho do arquivo original +Tamanho do volume incorreto +Tamanho do volume especificado: {0} bytes.\nVocê tem certeza que você quer dividir o arquivo compactado em tais volumes? +7400 +Combinar Arquivos +&Combinar em: +Combinando... +Selecionar só a primeira parte do arquivo dividido +Não pôde detectar o arquivo como parte do arquivo dividido +Não pôde achar mais do que uma parte do arquivo dividido +7500 +Calculando checksum... +Informação do checksum +Checksum do CRC pros dados: +Checksum do CRC pros dados e nomes: +7600 +Benchmark +Uso da memória: +Comprimindo +Descomprimindo +Avaliação +Total da Avaliação +Atual +Resultando +Uso da CPU +Avaliação / Uso +Passos: +7700 +Link +Link +Link de: +Link para: +7710 +Tipo de Link +Link Rígido +Link Simbólico do Arquivo +Link Simbólico do Diretório +Junção do Diretório diff --git a/Utils/7-Zip/Lang/pt.txt b/Utils/7-Zip/Lang/pt.txt new file mode 100644 index 000000000..6a378478b --- /dev/null +++ b/Utils/7-Zip/Lang/pt.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; : Carlos Macao +; : João Alves +; : João Frade (100 NOME TR) +; 4.46 : Rui Costa +; 9.17 : Sérgio Marques +; 15.00 : Rui Aguiar +; +; +; +; +; +0 +7-Zip +Portuguese Portugal +Português +401 +OK +Cancelar + + + +&Sim +&Não +&Fechar +Ajuda + +&Continuar +440 +Sim p/ &Todos +Não p/ T&odos +Parar +Reiniciar +&Segundo plano +P&rimeiro plano +&Pausar +Em pausa +Quer mesmo cancelar? +500 +&Ficheiro +&Editar +&Ver +F&avoritos +Ferramen&tas +&Ajuda +540 +&Abrir +Abrir &dentro +Abrir &fora +&Ver +&Editar +Mudar& o nome +&Copiar para... +&Mover para... +&Eliminar +&Separar ficheiro... +Com&binar ficheiros... +P&ropriedades +Come&ntário +Calcular o checksum +Diff +Criar pasta +Criar ficheiro +&Sair +Link +&Alternar Fluxo +600 +Seleccionar &Tudo +Desmarcar tudo +&Inverter selecção +Seleccionar... +Desmarcar... +Seleccionar por tipo +Desmarcar por tipo +700 +Ãcones &grandes +Ãcones &pequenos +&Lista +&Detalhes +730 +Sem ordem +Vista plana +&2 painéis +&Barras de ferramentas +Abrir pasta root +Subir um nível +Histórico de pastas... +&Actualizar +Auto Actualizar +750 +Barra de ferramentas do arquivo +Barra de ferramentas pré-definida +Botões grandes +Mostrar a legenda dos botões +800 +&Adicionar a pasta aos favoritos como +Marcador +900 +&Opções... +&Desempenho +960 +&Conteúdo... +&Acerca do 7-Zip... +1003 +Caminho +Nome +Extensão +pasta +Tamanho +Tamanho comprimido +Atributos +Criado +Acedido +Modificado +sólido +Comentado +Encriptado +Separar antes +Separar depois +Dicionário + +Tipo +Anti +Método +SO anfitrião +Sistema de ficheiros +Utilizador +Grupo +Bloco +Comentário +Posição +Prefixo do destino +pastas +ficheiros +Versão +Volume +Multivolume +Não definido +Ligações +Blocos +Volumes + +64-bit +Big-endian +CPU +Tamanho físico +Tamanho dos cabeçalhos +Soma de verificação +Características +Endereço virtual +ID +Abreviatura +Criador da aplicação +Tamanho do sector +Modo +Ligação +Erro +Tamanho total +Espaço livre +Tamanho do sector +Etiqueta +Nome local +Fornecedor +Segurança NT +Alternar Fluxo +Aux +Excluído +É Ãrvore + + +Tipo de Erro +Erros +Erros +Avisos +Aviso +Fluxo +Alternar Fluxo +Alternate Tamanho de Fluxos +Tamanho Virtual +Tamanho Descompactado +Tamanho Físico Total +Ãndice de Volume +SubTipo +Breve Comentário +Página de Código + + + +Tamanho Tail +Tamanho Stub Incorporado +Link +Link do Disco Rígido +iNode + +Só de leitura +2100 +Opções +Idioma +Idioma: +Editor +&Editor: +&Diff: +2200 +Sistema +Associar o 7-Zip com: +Todos os utilizadores +2301 +Integrar o 7-Zip no menu de contexto +Menu de contexto em cascata +Itens do menu de contexto: +Icons no menu de contexto +2320 + + +Abrir arquivo +Extrair ficheiros... +Adicionar ao arquivo... +Testar arquivo +Extrair para aqui +Extrair para {0} +Adicionar a {0} +Comprimir e enviar por e-mail... +Comprimir para {0} e enviar por e-mail +2400 +pastas +&Pasta de trabalho +Pasta &temporária +&Actual +&Especificada: +Utilizar só para discos amovíveis +Especifique a pasta para os ficheiros temporários. +2500 +Definições +Mostrar item ".." +Mostrar os ícones reais dos ficheiros +Mostrar o menu do sistema +Selecção de linha &completa +Mostrar as linhas da &grelha +Clique uma vez para abrir um item +Modo de seleçção &alternativo +Utilizar páginas de &memória grandes +2900 +Acerca do 7-Zip +O 7-Zip é um programa gratuito.\n\nO 7-Zip foi traduzido por: Rui Aguiar\n\trui_a_aguiar@hotmail.com +3000 +O sistema não consegue alocar a memória necessária +Não existem erros +{0} objecto(s) seleccionado(s) +Não é possível criar a pasta '{0}' +Este tipo de arquivo não permite actualizações. +Não é possível abrir o ficheiro '{0}' como arquivo +Não é possível abrir o arquivo encriptado '{0}'. Palavra-passe errada? +O arquivo não é suportado +Já existe o ficheiro {0} +O ficheiro '{0}' foi modificado.\nQuer actualizá-lo no arquivo? +Não foi possível actualizar o ficheiro\n'{0}' +Não foi possível iniciar o editor. +O ficheiro parece ser um vírus (o nome do ficheiro contém muitos espaços em branco). +A operação não pode ser invocada a partir de uma pasta com uma longa localização. +Tem que seleccionar um ficheiro +Tem que seleccionar um ou mais ficheiros +Demasiados itens +Não é possível abrir o ficheiro como arquivo {0} +Ficheiro aberto como arquivo {0} +Arquivo aberto com offset +3300 +A extrair... +A comprimir +A testar... +A abrir... +A pesquisar... +Removendo +3320 +A adicionar... +A actualizar... +A analisar... +Replicando +A recomprimir... +A ignorar... +A eliminar +Criando cabeçalho +3400 +Extrair +E&xtrair para: +Especifique o destino para os ficheiros extraídos. +3410 +Modo de nome de pasta +Nome de pastas completo +Sem nome de pastas +Caminhos absolutos +Caminhos relativos +3420 +Modo de sobrescrever +Perguntar antes de substituir +Sem confirmação +Ignorar ficheiros existentes +Mudar o nome automaticamente. +Mudar o nome automomaticamente os ficheiros existentes +3430 +Eliminar a duplicação da pasta de raiz +Restaurar segurança de ficheiros +3500 +Confirmar substituição de ficheiro +A pasta já possui um ficheiro com o mesmo nome. +Deseja substituir o ficheiro existente +por este? +{0} bytes +Mudar o nome a&utomaticamente +3700 +O método de compressão é inválido para '{0}'. +Erro de dados em '{0}'. O arquivo está danificado. +CRC falhou em '{0}'. O arquivo está danificado. +Erro de dados no ficheiro encriptado '{0}'. Palavra-passe errada? +CRC falhou no ficheiro encriptado '{0}'. Palavra-passe errada? +3710 +Palavra-passe errada? +3721 +Método de compressão não suportado +Erro nos dados +O CRC falhou +Dados indisponíveis +Fim inesperado nos dados +Existem alguns dados após o final dos dados de carga útil +Não é um arquivo +Erro nos Cabeçalhos +Palavra-passe errada +3763 +Início de arquivo indisponível +Início de arquivo não confirmado + + + +Funcionalidade não suportada +3800 +Insira a palavra-passe +Introduza a palavra-passe: +Reintroduza a palavra-passe: +&Mostrar palavra-passe +As palavras-passe não coincidem +Para a palavra-passe, utilize apenas letras inglesas, números e os caracteres especiais (!, #, $, ...) +A palavra-passe é muito comprida +Palavra-passe +3900 +Tempo decorrido: +Tempo restante: +Tamanho: +Velocidade: +Processado: +Rácio de compressão: +Erros: +Arquivos: +4000 +Adicionar ao arquivo +&Arquivo: +&Modo de actualização: +&Formato do arquivo: +Níve&l de compressão: +Método de &compressão: +Tamanho do &dicionário: +&Tamanho da &palavra: +Tamanho dos blocos sólidos: +Nº de processos do CPU: +&Parâmetros: +Opções +Criar arquivo SF&X +Comprimir ficheiros partilhados +Encriptação +Método de encriptação: +Encriptar &nomes de ficheiros +Utilização de memória para compressão: +Utilização de memória para descompressão: +Excluir ficheiros após compressão +4040 +Armazenar links simbólicos +Armazenar links do disco rígido +Armazenar fluxo de dados alternados +Armazenar segurança de ficheiros +4050 +Guardar +Muito rápido +Rápido +Normal +Máxima +Ultra +4060 +Adicionar e substituir ficheiros +Actualizar e adicionar ficheiros +Actualizar ficheiros +Sincronizar ficheiros +4070 +Procurar +Todos os ficheiros +Não sólido +sólido +6000 +Copiar +Mover +Copiar para: +Mover para: +A copiar... +A mover... +A mudar o nome... +Seleccione a pasta de destino. +Operação não suportada. +Erro ao mudar o nome do ficheiro ou pasta +Confirmar a cópia dos ficheiros +Quer mesmo copiar os ficheiros para o arquivo? +6100 +Confirmar a eliminação do ficheiro +Confirmar a eliminação da pasta +Confirmar a eliminação de múltiplos ficheiros +Quer mesmo eliminar o '{0}'? +Quer mesmo eliminar a pasta '{0}' e todo o seu conteúdo? +Quer mesmo eliminar os itens {0}? +A eliminar... +Erro ao eliminar o ficheiro ou pasta +O sistema não consegue mover para a reciclagem um ficheiro com uma localização longa +6300 +Criar pasta +Criar ficheiro +Nome da pasta: +Nome do ficheiro: +Nova Pasta +Novo ficheiro +Erro ao criar a pasta +Erro ao criar ficheiro +6400 +Comentário +&Comentário: +Seleccionar +Desseleccionar +Máscara: +6600 +Propriedades +Histórico de pastas +Mensagens de diagnóstico +Mensagem +7100 +Computador +Rede +Documentos +Sistema +7200 +Adicionar +Extrair +Testar +Copiar +Mover +Eliminar +Info +7300 +Separar ficheiro +&Separar para: +Separar por &volumes, bytes: +A separar... +Confirmar separação +Quer mesmo separar o ficheiro em {0} volumes? +O tamanho do volume tem de ser inferior ao tamanho do ficheiro original +Tamanho do volume incorrecto +Tamanho do volume especificado: {0} bytes.\nQuer mesmo separar o arquivo nestes volumes? +7400 +Combinar ficheiros +&Combinar para: +A combinar... +Seleccione apenas o primeiro ficheiro +Não foi possivel apagar o ficheiro como parte do ficheiro dividido +Não foi possível encontrar mais do que uma parte do ficheiro dividido +7500 +A calcular o checksum... +Informações do checksum +CRC checksum para dados: +CRC checksum para dados e nome: +7600 +Desempenho +Utilização de Memória: +A comprimir +A descomprimir +Desempenho +Desempenho Total +Actual +Resultante +Utilização CPU +Desemp. / Utiliza. +Passagens: +7700 +Link +Link +Link de: +Link para: +7710 +Link Tipo +Link do Disco Rígido +Link do Ficheiro Simbólico +Link do Directório Simbólico +Directório de Junção \ No newline at end of file diff --git a/Utils/7-Zip/Lang/ro.txt b/Utils/7-Zip/Lang/ro.txt new file mode 100644 index 000000000..0498a8e95 --- /dev/null +++ b/Utils/7-Zip/Lang/ro.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.59 : Lucian Nan : http://www.prizeeinternational.com +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Romanian +Română +401 +Bine +Anulare + + + +&Da +&Nu +&ÃŽnchide +Ajutor + +&Continuă +440 +Da, pe &toate +N&ici unul +OpreÅŸte +Restartează +ÃŽn &fundal +La &suprafaţă +&Pauză +ÃŽn pauză +EÅŸti sigur că vrei să anulezi? +500 +&FiÅŸier +&Editează +&Vizualizează +F&avorite +&Unelte +&Ajutor +540 +&Deschide +Deschide î&n +Deschide în &afară +&Vizualizeză +&Editează +&RedenumeÅŸte +&Copiază la... +&Mută la... +Åžter&ge +ÃŽmparte &fiÅŸierul... +&UneÅŸte fiÅŸierele... +&Proprietăţi +Comen&tariu +Calculează suma de verificare + +Crează director +Crează fiÅŸier +&IeÅŸire +600 +&Selectează tot +&Deselectează tot +&Inversează selecÅ£ia +Selectează... +Deselectează... +Selectează după tip +Deselectează după tip +700 +IconiÅ£e m&ari +IconiÅ£e m&ici +&Listă +&Detalii +730 +Nesortat +Vedere plană +&2 panouri +Bare de &unelte +Deschide directorul rădăcină +Un nivel mai sus +Istoria directoarelor... +&ÃŽmprospătează +750 +Bara de arhivare +Bara de unelte standard +Butoane mari +Arată textul butoanelor +800 +&Adaugă directorul în Favorite ca +Semn de carte +900 +&OpÅ£iuni... +&Banc de încercare +960 +&ConÅ£inut... +&Despre 7-Zip... +1003 +Calea +Nume +Extensie +Director +Mărime +Mărimea pachetului +Atribute +Creată +Accesată +Modificată +Solidă +Comentat +Criptat +ÃŽmparte înainte +ÃŽmparte după +DicÅ£ionar +CRC +Tip +Anti +Metoda +SO gazdă +FiÅŸier de sistem +Utilizator +Grup +Blochează +Comentariu +PoziÅ£ia +Prefixul destinaÅ£iei +Directoare +FiÅŸiere +Versiunea +Volum +Multivolume +Ofset +Legături +Blocuri +Volume + +64-bit +Big-endian +CPU +Mărime fizică +Mărimea antetelor +Checksum +Caracteristici +Adresa virtuală + + + + + + +Eroare +Mărimea totală +SpaÅ£iu liber +Mărimea grupului +Etichetă +Nume local +Distribuitor +2100 +OpÅ£iuni +Limba +Limba: +Editor +&Editor: + +2200 +Sistem +Asociază 7-Zip cu: +2301 +Integrează 7-Zip în contextul meniului shell +Contextul meniului în cascadă +Obiectele meniului: +2320 + + +Deschide arhiva +Dezarhivează fiÅŸierele... +Adaugă într-o arhivă... +Testează arhiva +Dezarhivează aici +Dezarhivează în {0} +Adaugă în {0} +Arhivează ÅŸi trimite email... +Arhivează în {0} ÅŸi trimite email +2400 +Directoare +&Directorul de lucru +Directorul &tempotar al sistemului +&Actual +&Specificat: +Utilizează numai pentru discurile detaÅŸabile +Specifică o destinaÅ£ie pentru arhivele temporare. +2500 +Setări +Arată ".." obiect +Arată iconiÅ£ele reale ale fiÅŸierului +Arată meniul sistemului +Selectează &tot rândul +Arată liniile de ghidare + +Mod de selectare &alternativă +Utilizează pagini &mari de memorie +2900 +Despre 7-Zip +7-Zip este un program gratuit. Oricum, poÅ£i contribui la dezvoltarea 7-Zip înregistrându-te. +3000 +Sistemul nu poate aloca memoria necesară +Nu sunt erori +{0} obiect(e) selectat(e) +Nu pot crea directorul '{0}' +OperaÅ£iile de actualizare nu sunt suportate pentru această arhivă. +Nu pot deschide fiÅŸierul '{0}' ca arhivă +Nu pot deschide arhiva criptată '{0}'. Parola greÅŸită? +Tip de arhivă nesuportat +FiÈ™ierul {0} există deja +FiÅŸierul '{0}' a fost modificat.\nDoreÅŸti să îl actualizez în arhivă? +Nu pot actualiza fiÅŸierul\n'{0}' +Nu pot porni editorul. +The file looks like a virus (the file name contains long spaces in name). +OperaÈ›ia nu poate fi apelată dintr-un director cu cale lungă. +Trebuie să alegi un fiÈ™ier +Trebuie să alegi unul sau mai multe fiÈ™iere +Prea multe obiecte +3300 +Dezarhivez +Arhivare +Testez +Deschid... +Citesc... +3400 +Dezarhivează +Dezarhivează în: +Specifică o destinaÅ£ie pentru fiÅŸierele dezarhivate. +3410 +Modul destinaÅ£ie +Numele întreg al destinaÅ£iei +Fără locaÅ£ie +3420 +Modul de înlocuire +ÃŽntreabă înainte de a înlocui +ÃŽnlocuieÅŸte fară a întreba +Sări peste fiÅŸierele existente +Auto redenumire +Auto redenumeÅŸte fiÅŸierele existente +3500 +Aprobă înlocuirea fiÅŸierului +FiÅŸierul există deja în directorul destinaÅ£ie. +DoriÅ£i să înlocuiÅ£i fiÅŸierul existent +cu acesta? +{0} octeÅ£i +A&uto redenumire +3700 +Metodă de arhivare nesuportată pentru '{0}'. +Eroare de date la '{0}'. FiÅŸierul este corupt. +Verificarea CRC a eÅŸuat pentru '{0}'. FiÅŸierul este corupt. +Erori de date la fiÅŸierul criptat '{0}'. Parolă greÅŸită? +Verificarea CRC a eÅŸuat pentru fiÅŸierul criptat '{0}'. Parolă greÅŸită? +3800 +Introdu parola +Introdu parola: +Reintrodu parola: +&Arată parola +Parolele nu sunt identice +UtilizaÅ£i numai litere, cifre ÅŸi caracterele speciale (!, #, $, ...) pentru parolă +Parola este prea lungă +Parola +3900 +Timp trecut: +Timp rămas: +Mărimea: +Viteza: +Procesat: +Rata de comprimare: +Erori: +Arhive: +4000 +Adaugă într-o arhivă +&Arhivează: +Modul de a&ctualizare: +&Formatul arhivei: +&Nivel de arhivare: +&Metoda de arhivare: +Mărimea &dicÅ£ionarului: +Mărimea &cuvântului: +Mărimea blocului solid: +Numărul de procesoare utilizate: +&Parametri: +OpÅ£iuni +Crează arhivă SF&X +Arhivează fiÅŸierele partajate +Criptare +Metoda de criptare: +Criptează &numele fiÅŸierului +Memorie utilizată pentru arhivare: +Memorie utilizată pentru dezarhivare: +4050 +Stochează +Imediată +Rapidă +Normală +Maximă +Ultra +4060 +Adaugă ÅŸi înlocuieÅŸte fiÅŸierele +Actualizează ÅŸi adaugă fiÅŸierele +Actualizează fiÅŸierele existente +Sincronizează fiÅŸierele +4070 +Caută +Toate fiÅŸierele +NEsolid +Solid +6000 +Copiază +Mută +Copiază la: +Mută la: +Copiez... +Mut... +Redenumesc... +Alege directorul destinaÅ£ie. +OperaÅ£ia nu este suportată. +Eroare la redenumirea fiÅŸierului sau directorului +Aprobă copierea fiÅŸierului +EÅŸti sigur că vrei să copiezi fiÅŸierele în arhivă +6100 +Aprobă ÅŸtergerea fiÅŸierului +Aprobă ÅŸtergerea directorului +Aprobă ÅŸtergerea mai multor fiÅŸiere +EÅŸti sigur că vrei să ÅŸtergi '{0}'? +EÅŸti sigur că vrei să ÅŸtergi directorul '{0}' ÅŸi tot conÅ£inutul lui? +EÅŸti sigur că vrei să ÅŸtergi aceste {0} obiecte? +Åžterg... +Eroare la ÅŸtergerea fiÅŸierului sau directorului +Sistemul nu poate muta un fiÅŸier cu cale lungă la CoÅŸul de gunoi +6300 +Crează director +Crează fiÅŸier +Numele directorului: +Numele fiÅŸierului: +Director nou +FiÅŸier nou +Eroare la crearea directorului +Eroare la crearea fiÅŸierului +6400 +Comentariu +&Comentariu: +Selectează +Deselectează +Masca: +6600 +Proprietăţi +Istoria directoarelor +Mesaje de diagnosticare +Mesaj +7100 +Computer +ReÅ£ea +Documente +Sistem +7200 +Arhivează +Dezarhivează +Testează +Copiază +Mută +Åžterge +Info +7300 +ÃŽmparte fiÅŸierul +ÃŽ&mparte în: +ÃŽmparte în &volume, octeÅ£i: +ÃŽmpart... +Confirmă împărÅ£irea +EÅŸti sigur că vrei să imparÅ£i arhiva în {0} volume (părÅ£i)? +Dimensiunea volumului trebuie să fie mai mică decât dimensiunea fiÅŸierului original +Dimensiunea volumului este incorectă +Dimensiunea volumului specificat: {0} octeÅ£i.\nEÅŸti sigur că vrei să împarÅ£i arhiva în mai multe volume (părÅ£i)? +7400 +UneÅŸte fiÅŸierele +&UneÅŸte în: +Unesc... +Alege doar prima parte din fiÅŸierul împărÈ›it +Nu pot detecta fiÈ™ierul ca parte a fiÈ™ierului împărÈ›it +Nu găsesc mai mult de o parte din fiÈ™ierul împărÈ›it +7500 +Calculez suma de control... +InformaÅ£ii despre suma de control +Suma de control CRC pentru conÅ£inut: +Suma de control CRC pentru conÅ£inut ÅŸi nume: +7600 +Banc de încercare +Memorie utilizată: +Arhivez +Dezarhivez +Rata +Rata totală +Actual +Rezultate +Utilizarea procesorului +Rata / Utilizare +Trecute: diff --git a/Utils/7-Zip/Lang/ru.txt b/Utils/7-Zip/Lang/ru.txt new file mode 100644 index 000000000..7e5cf4960 --- /dev/null +++ b/Utils/7-Zip/Lang/ru.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 15.10 : 2015-10-31 : Igor Pavlov +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Russian +РуÑÑкий +401 +OK +Отмена + + + +&Да +&Ðет +&Закрыть +Помощь + +&Продолжить +440 +Да Ð´Ð»Ñ &вÑех +Ðет Ð´Ð»Ñ Ð²&Ñех +Стоп +ПерезапуÑк +&Фоном +&Ðа передний план +&Пауза +Ðа паузе +Ð’Ñ‹ дейÑтвительно хотите прервать операцию? +500 +&Файл +&Правка +&Вид +&Избранное +С&ÐµÑ€Ð²Ð¸Ñ +&Справка +540 +&Открыть +Открыть &внутри +Открыть Ñнару&жи +ПроÑмотр +&Редактировать +Переи&меновать +&Копировать в... +&ПеремеÑтить в... +&Удалить +Ра&збить файл... +О&бъединить файлы... +Сво&йÑтва +Комме&нтарий... +ÐšÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ñумма +Сравнить +&Создать Папку +Созд&ать Файл +Ð’&ыход +СÑылка +&Ðльтернативные Потоки +600 +Выделить в&Ñе +Убрать выделение +&Обратить в&ыделение +Выделить... +Убрать выделение... +Выделить по типу +Убрать выделение по типу +700 +&Крупные значки +&Мелкие значки +СпиÑ&ок +&Таблица +730 +Без Ñортировки +ПлоÑкий режим +&2 Панели +&Панели инÑтрументов +Открыть корневую папку +Переход на один уровень вверх +ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¿Ð°Ð¿Ð¾Ðº... +О&бновить +Ðвтообновление +750 +Панель кнопок архиватора +Ð¡Ñ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð°Ñ Ð¿Ð°Ð½ÐµÐ»ÑŒ кнопок +Большие кнопки +ÐадпиÑи на кнопках +800 +Добавить папку в &избранное как +Закладка +900 +ÐаÑтройки... +ТеÑтирование производительноÑти +960 +&Оглавление... +О &программе... +1003 +Путь +Ð˜Ð¼Ñ +РаÑширение +Папка +Размер +Сжатый +Ðтрибуты +Создан +Открыт +Изменен +Ðепрерывный +Комментарий +Зашифрован +Разбит До +Разбит ПоÑле +Словарь + +Тип +Ðнти +Метод +СиÑтема +Ð¤Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ Ð¡Ð¸Ñтема +Пользователь +Группа +Блок +Комментарий +ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ +Путь +Папок +Файлов +ВерÑÐ¸Ñ +Том +Многотомный +Смещение +СÑылок +Блоков +Томов + + + +ПроцеÑÑор +ФизичеÑкий Размер +Размер Заголовков +ÐšÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ð¡ÑƒÐ¼Ð¼Ð° +ХарактериÑтики +Виртуальный ÐÐ´Ñ€ÐµÑ + +Короткое Ð˜Ð¼Ñ +Создатель +Размер Сектора +Режим +Ð¡Ð¸Ð¼Ð²Ð¾Ð»ÑŒÐ½Ð°Ñ Ð¡Ñылка +Ошибка +ЕмкоÑть +Свободно +Размер клаÑтера +Метка +Локальное Ð¸Ð¼Ñ +Провайдер +NT БезопаÑноÑть +Ðльтернативный Поток + +Удаленный +Дерево + + +Тип Ошибки +Ошибки +Ошибки +ÐŸÑ€ÐµÐ´ÑƒÐ¿Ñ€ÐµÐ¶Ð´ÐµÐ½Ð¸Ñ +Предупреждение +Потоки +Ðльтернативные Потоки +Размер Ðльтернативных потоков +Виртуальный Размер +РаÑпакованный Размер +Общий ФизичеÑкий Размер +Ðомер Тома +Подтип +Короткий Комментарий +ÐšÐ¾Ð´Ð¾Ð²Ð°Ñ Ð¡Ñ‚Ñ€Ð°Ð½Ð¸Ñ†Ð° + + + +Размер ОÑтатка +Размер Ð’Ñтроенного Блока +СÑылка +ЖеÑÑ‚ÐºÐ°Ñ Ð¡Ñылка +iNode + +Только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ +2100 +ÐаÑтройки +Язык +Язык: +Редактор +&Редактор: +&Программа ÑравнениÑ: +2200 +СиÑтема +ÐÑÑоциировать 7-Zip Ñ Ñ„Ð°Ð¹Ð»Ð°Ð¼Ð¸: +Ð’Ñе пользователи +2301 +Ð’Ñтроить 7-Zip в контекÑтное меню оболочки +КаÑкадное контекÑтное меню +Элементы контекÑтного меню: +Иконки в контекÑтном меню +2320 +<Папка> +<Ðрхив> +Открыть архив +РаÑпаковать +Добавить к архиву... +ТеÑтировать +РаÑпаковать здеÑÑŒ +РаÑпаковать в {0} +Добавить к {0} +Сжать и отправить по email... +Сжать в {0} и отправить по email +2400 +Папки +&Ð Ð°Ð±Ð¾Ñ‡Ð°Ñ Ð¿Ð°Ð¿ÐºÐ° +&СиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð¿Ð°Ð¿ÐºÐ° +&Ð¢ÐµÐºÑƒÑ‰Ð°Ñ +&Задать: +ИÑпользовать только Ð´Ð»Ñ Ñменных ноÑителей +Укажите положение Ð´Ð»Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð½Ñ‹Ñ… архивов. +2500 +ÐаÑтройки +Показывать Ñлемент ".." +Показывать реальные иконки файлов +Показывать ÑиÑтемное меню +КурÑор на вÑÑŽ Ñтроку +Показывать разделители +Открывать одним щелчком +Ðльтернативный режим пометки +ИÑпользовать большие Ñтраницы памÑти +2900 +О программе 7-Zip +7-Zip ÑвлÑетÑÑ Ñвободно раÑпроÑтранÑемой программой. +3000 +ÐедоÑтаточно Ñвободной памÑти +Ошибок не найдено +Выделено объектов: {0} +Ðе удалоÑÑŒ Ñоздать папку '{0}' +Операции Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ðµ поддерживаютÑÑ Ð´Ð»Ñ Ñтого архива. +Ðе удалоÑÑŒ открыть файл '{0}' как архив +Ðе удалоÑÑŒ открыть зашифрованный архив '{0}'. Ðеверный пароль? +Ðеподдерживаемый тип архива +Файл {0} уже ÑущеÑтвует +Файл '{0}' был изменен.\nÐ’Ñ‹ хотите обновить его в архиве? +Ðе удалоÑÑŒ обновить файл\n'{0}' +Ðе удалоÑÑŒ запуÑтить редактор +Файл похож на Ð²Ð¸Ñ€ÑƒÑ (Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° Ñодержит длинную поÑледовательноÑть пробелов). +ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð½Ðµ может быть иÑполнена из папки, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¸Ð¼ÐµÐµÑ‚ длинный путь. +Ð’Ñ‹ должны выделить один файл +Ð’Ñ‹ должны выделить один или неÑколько файлов +Слишком много Ñлементов +Ðе удалоÑÑŒ открыть файл как {0} архив +Файл открыт как {0} архив +Ðрхив открыт Ñо Ñмещением +3300 +РаÑпаковка +Упаковка +ТеÑтирование +Открытие... +Сканирование... +Удаление +3320 +Добавление +Обновление +Ðнализ +Копирование +Перепаковка +ПропуÑк +Удаление +Создание заголовков +3400 +Извлечь +&РаÑпаковать в: +Укажите положение Ð´Ð»Ñ Ð¸Ð·Ð²Ð»ÐµÐºÐ°ÐµÐ¼Ñ‹Ñ… файлов. +3410 +Пути к файлам: +Полные пути +Без путей +ÐбÑолютные пути +ОтноÑительные пути +3420 +ПерезапиÑÑŒ: +С подтверждением +Без Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ +ПропуÑкать +Переименовать автоматичеÑки +Переименовать ÑущеÑтвующие +3430 +УÑтранить дублирование корневой папки +УÑтанавливать права доÑтупа +3500 +Подтверждение замены файла +Папка уже Ñодержит обрабатываемый файл. +Заменить ÑущеÑтвующий файл +Ñледующим файлом? +{0} байтов +Переименовать автом. +3700 +Ðеподдерживаемый метод ÑÐ¶Ð°Ñ‚Ð¸Ñ Ð´Ð»Ñ Ñ„Ð°Ð¹Ð»Ð° '{0}'. +Ошибка в данных в '{0}'. Файл иÑпорчен. +Ошибка CRC в '{0}'. Файл иÑпорчен. +Ошибка в данных зашифрованного файла '{0}'. Ðеверный пароль? +Ошибка CRC Ð´Ð»Ñ Ð·Ð°ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð½Ð¾Ð³Ð¾ файла '{0}'. Ðеверный пароль? +3710 +Ðеверный пароль? +3721 +Ðеподдерживаемый метод ÑÐ¶Ð°Ñ‚Ð¸Ñ +Ошибка в данных +Ошибка CRC +ÐедоÑтупные данные +Ðеожиданный конец данных +ЕÑть данные поÑле конца блока полезных данных +Ðе ÑвлÑетÑÑ Ð°Ñ€Ñ…Ð¸Ð²Ð¾Ð¼ +Ошибка в заголовках +Ðеверный пароль +3763 +ÐедоÑтупно начало архива +Ðеподтвержденное начало архива + + + +ÐÐµÐ¿Ð¾Ð´Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°ÐµÐ¼Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¾Ð½Ð°Ð»ÑŒÐ½Ð¾Ñть +3800 +Ввод Ð¿Ð°Ñ€Ð¾Ð»Ñ +&Введите пароль: +Повторите пароль: +&Показать пароль +Пароли не Ñовпадают +Ð”Ð»Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð¸Ñпользуйте только Ñимволы латинÑкого алфавита, цифры и Ñпециальные Ñимволы (!, #, $, ...) +Пароль Ñлишком длинный +&Пароль +3900 +Прошло: +ОÑталоÑÑŒ: +Ð’Ñего: +СкороÑть: +Размер: +Степень ÑжатиÑ: +Ошибок: +Ðрхивов: +4000 +Добавить к архиву +&Ðрхив: +&Режим изменениÑ: +&Формат архива: +&Уровень ÑжатиÑ: +&Метод ÑжатиÑ: +Размер &ÑловарÑ: +Размер Ñ&лова: +Размер блока: +ЧиÑло потоков: +&Параметры: +&Опции +Создать SF&X-архив +Сжимать открытые Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи файлы +Шифрование +Метод шифрованиÑ: +&Шифровать имена файлов +Объем памÑти Ð´Ð»Ñ ÑƒÐ¿Ð°ÐºÐ¾Ð²ÐºÐ¸: +Объем памÑти Ð´Ð»Ñ Ñ€Ð°Ñпаковки: +УдалÑть файлы поÑле ÑÐ¶Ð°Ñ‚Ð¸Ñ +4040 +СохранÑть Ñимвольные ÑÑылки +СохранÑть жеÑткие ÑÑылки +СохранÑть альтернативные потоки +СохранÑть права доÑтупа +4050 +Без ÑÐ¶Ð°Ñ‚Ð¸Ñ +СкороÑтной +БыÑтрый +Ðормальный +МакÑимальный +Ультра +4060 +Добавить и заменить +Обновить и добавить +Обновить +Синхронизировать +4070 +ПролиÑтать +Ð’Ñе файлы +По размеру файла +Ðепрерывный +6000 +Копировать +ПеремеÑтить +Копировать в: +ПеремеÑтить в: +Копирование... +Перемещение... +Переименование... +Укажите папку. +ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð½Ðµ поддерживаетÑÑ Ð´Ð»Ñ Ñтой папки. +Ошибка при переименовании файла или папки +Подтверждение ÐºÐ¾Ð¿Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² +Ð’Ñ‹ дейÑтвительно хотите Ñкопировать Ñти файлы в архив +6100 +Подтверждение ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð° +Подтверждение ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¿Ð°Ð¿ÐºÐ¸ +Подтверждение ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ файлов +Ð’Ñ‹ дейÑтвительно хотите удалить "{0}"? +Ð’Ñ‹ дейÑтвительно хотите удалить папку "{0}" и вÑе ее Ñодержимое? +Ð’Ñ‹ дейÑтвительно хотите удалить Ñти объекты ({0} шт.)? +Удаление... +Ошибка при удалении файла или папки +СиÑтема не поддерживает операцию ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð² Ñ Ð´Ð»Ð¸Ð½Ð½Ñ‹Ð¼Ð¸ путÑми в корзину +6300 +Создать папку +Создать файл +Ð˜Ð¼Ñ Ð¿Ð°Ð¿ÐºÐ¸: +Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°: +ÐÐ¾Ð²Ð°Ñ Ð¿Ð°Ð¿ÐºÐ° +Ðовый файл +Ошибка при Ñоздании папки +Ошибка при Ñоздании файла +6400 +Комментарий +&Комментарий: +Выделить +Убрать выделение +МаÑка: +6600 +СвойÑтва +ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð¿Ð°Ð¿Ð¾Ðº +Ð¡Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ +Сообщение +7100 +Компьютер +Сеть +Документы +СиÑтема +7200 +Добавить +Извлечь +ТеÑтировать +Копировать +ПеремеÑтить +Удалить +Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ +7300 +Разбить файл +&Разбить в: +Разбить на &тома размером (в байтах): +Разбиение... +Подтверждение Ñ€Ð°Ð·Ð±Ð¸ÐµÐ½Ð¸Ñ +Ð’Ñ‹ дейÑтвительно хотите разбить файл на {0} чаÑтей? +Размер тома должен быть меньше размера иÑходного файла +Ошибка в поле Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ð½Ð¸Ñ Ñ€Ð°Ð·Ð¼ÐµÑ€Ð° томов +УÑтановленный размер тома: {0} байтов.\nÐ’Ñ‹ дейÑтвительно хотите разбить архив на такие тома? +7400 +Объединить файлы +&Объединить в: +Объединение... +Ðеобходимо выделить только первую чаÑть разбитого файла +Ðе удалоÑÑŒ раÑпознать разбитый файл +Ðе удалоÑÑŒ найти более одной чаÑти разбитого файла +7500 +ВычиÑление контрольной Ñуммы... +ÐšÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ñумма +ÐšÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ñумма CRC Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ…: +ÐšÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð°Ñ Ñумма CRC Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… и имен: +7600 +ТеÑтирование производительноÑти +Объем памÑти: +Упаковка +РаÑпаковка +Рейтинг +Общий рейтинг +Текущий +Итоговый +Ðагрузка +Рейтинг / Ðагр. +Проходов: +7700 +СÑылка +СвÑзать +ИÑточник: +Цель: +7710 +Тип ÑÑылки +ЖеÑÑ‚ÐºÐ°Ñ ÑÑылка +Ð¡Ð¸Ð¼Ð²Ð¾Ð»ÑŒÐ½Ð°Ñ ÑÑылка (Файл) +Ð¡Ð¸Ð¼Ð²Ð¾Ð»ÑŒÐ½Ð°Ñ ÑÑылка (Папка) +Точка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (Junction) diff --git a/Utils/7-Zip/Lang/sa.txt b/Utils/7-Zip/Lang/sa.txt new file mode 100644 index 000000000..c919de2e0 --- /dev/null +++ b/Utils/7-Zip/Lang/sa.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.07 : Vinayy Sharrma, अनà¥à¤µà¤¾à¤¦à¤‚ विनय शरà¥à¤®à¤¾ संसà¥à¤•ृतमॠगरà¥à¤µ कà¥à¤°à¥, जय हिनà¥à¤¦à¤‚ ! जय संसà¥à¤•ृतमà¥! à¤à¤¤à¤¤à¥ साधारण अनà¥à¤µà¤¾à¤¦à¤‚ असà¥à¤¤à¤¿ +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Sanskrit, Indian, हिनà¥à¤¦à¥à¤¸à¥à¤¤à¤¾à¤¨à¤‚ +संसà¥à¤•ृत +401 +ठीक असà¥à¤¤à¤¿ +रदà¥à¤¦ + + + +&हाठ+&ना +&बंद कà¥à¤°à¥ +मददं + +&जारी रखे +440 +&सरà¥à¤µà¤¸à¥à¤¯ हाठ+&सरà¥à¤µà¤¸à¥à¤¯ ना +रूको +पà¥à¤¨à¤ƒ शà¥à¤°à¥ कà¥à¤°à¥ +&पॄषà¥à¤ à¥à¤­à¥‚मि +&अगà¥à¤°à¤­à¥‚मि(डेसà¥à¤•à¥à¤Ÿà¥‹à¤ª) +&विशà¥à¤°à¤¾à¤® +विशà¥à¤°à¤¾à¤®à¤¿à¤¤à¤‚ +तà¥à¤µà¤® रदà¥à¤¦ करना चाहते हो. तà¥à¤®à¥à¤¹à¥‡à¤‚ यकीन असà¥à¤¤à¤¿ कà¥à¤¯à¤¾? +500 +&फ़ाइलमॠ+&संपादनमॠ+&दरà¥à¤¶à¤¨à¤®à¥ +&मनपसंदमॠ+&औजारमॠ+&मददमॠ+540 +&अनावृतं +&अंदर अनावृतं +&बहिः अनावृतं +&दृशà¥à¤¯à¤®à¥ +&संपादनमॠ+&पà¥à¤¨: नामकरणमॠ+&में नकल बनाये... +&में ले जायें... +&मिटायें +&फ़ाइलसà¥à¤¯ विभाजनं कà¥à¤°à¥... +&फ़ाइलसà¥à¤¯ संयोजनं कà¥à¤°à¥... +&संपतà¥à¤¤à¤¿à¤¯à¤¾à¤ वा गà¥à¤£à¤‚ +&टिपà¥à¤ªà¤£à¥€ +&जाà¤à¤š योगसà¥à¤¯ गणनां कà¥à¤°à¥ +&अनà¥à¤¤à¤° +&फोलà¥à¤¡à¤°à¤‚ निरà¥à¤®à¤¾à¤£à¤®à¥ कà¥à¤°à¥ +&फ़ाइलं निरà¥à¤®à¤¾à¤£à¤®à¥ कà¥à¤°à¥ +&निरà¥à¤—मन +600 +&सरà¥à¤µ चयनं कà¥à¤°à¥ +&सरà¥à¤µ अचयनितं कà¥à¤°à¥ +&चयन उलटा कà¥à¤°à¥ +चयनं कà¥à¤°à¥... +अचयनं कà¥à¤°à¥... +पà¥à¤°à¤•ार दà¥à¤µà¤¾à¤°à¤¾ चयनमॠ+पà¥à¤°à¤•ार दà¥à¤µà¤¾à¤°à¤¾ अचयनमॠ+700 +विशाल पà¥à¤°à¤¤à¥€à¤•ं +लघॠपà¥à¤°à¤¤à¥€à¤•ं +&सूची +&वरà¥à¤£à¤¨à¤‚ +730 +अवितरितमॠ+चौड़ा दृशà¥à¤¯ +&२ फ़लक +&औजार पटà¥à¤Ÿà¥€à¤¯à¤¾à¤ +मूल फोलà¥à¤¡à¤°à¤‚ अनावृतं +à¤à¤• सà¥à¤¤à¤° उरà¥à¤§à¥à¤µ चà¥à¥‡ +फ़ोलà¥à¤¡à¤°à¥‹ का इतिहास... +&ताजा कà¥à¤°à¥ +750 +संगà¥à¤°à¤¹à¤®à¥ उपकरणपटà¥à¤Ÿà¥€ +मानक औजार पटà¥à¤Ÿà¥€ +विशाल खटके(बटन) +खटके(बटन) के शबà¥à¤¦ दिखायें +800 +&फोलà¥à¤¡à¤°à¤‚ मनपसंद में ऎसे जोड़े... +पà¥à¤¸à¥à¤¤à¤šà¤¿à¤¨à¥à¤¹à¤®à¥ +900 +&विकलà¥à¤ªà¤®à¥... +&बेञà¥à¤šà¤®à¤¾à¤°à¥à¤•मà¥(पà¥à¤°à¤¾à¤®à¤¾à¤£à¤¿à¤• तà¥à¤²à¤¨à¤¾) +960 +&सामगà¥à¤°à¥€... +7-जिप विषय... +1003 +मारà¥à¤— +नाम +विसà¥à¤¤à¤¾à¤°à¤‚ +फोलà¥à¤¡à¤°à¤‚ +आकारं +कà¥à¤² आकारं +विशेषता वा गà¥à¤£à¤§à¤°à¥à¤® +सरà¥à¤œà¤¿à¤¤à¤‚ +चालितमॠ+परिवरà¥à¤§à¤¿à¤¤à¤‚ +ठोस +टिपà¥à¤ªà¤£à¥€ +गà¥à¤ªà¥à¤¤à¤¿à¤•ृतमॠ+के पूरà¥à¤µ विभाजनं(टà¥à¤•डे) कà¥à¤°à¥ +के पशà¥à¤šà¤¾à¤¤ विभाजनं(टà¥à¤•डे) कà¥à¤°à¥ +शबà¥à¤¦à¤•ोशं +सीआरसी +पà¥à¤°à¤•ारं +विरोधी +पदà¥à¤§à¤¤à¤¿ +ओपरेटिंग सिसà¥à¤Ÿà¤® +फ़ाइलं पà¥à¤°à¤£à¤¾à¤²à¥€ +पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾ +समूहमॠ+रोक वा टà¥à¤•ड़े +पà¥à¤°à¤¤à¤¿à¤•à¥à¤°à¤¿à¤¯à¤¾ +सà¥à¤¥à¤¾à¤¨à¤‚ +मारà¥à¤— पà¥à¤°à¤¤à¥à¤¯à¤¯à¤‚ +फोलà¥à¤¡à¤°à¥à¤¸ +फाइलà¥à¤¸ +संसà¥à¤•रणमॠ+जतà¥à¤¥à¤¾ +अनेक जतà¥à¤¥à¥‡ +ओफसेट +कडियाठ+टà¥à¤•ड़े +जतà¥à¤¥à¥‡ + +64-बिट +विशाल-à¤à¤¨à¥à¤¡à¤¿à¤¯à¤¨ +सीपीयू +भौतिक आकारं +शीरà¥à¤·à¤•ाः आकारं +जाà¤à¤šà¤¯à¥‹à¤— +चरितà¥à¤°à¤¤à¤¾à¤Žà¤‚ +आभासी पता +आईडी +संकà¥à¤·à¤¿à¤ªà¥à¤¤à¤ƒ नामं +सरà¥à¤œà¤• अनà¥à¤ªà¥à¤°à¤¯à¥‹à¤—ं +सेकà¥à¤Ÿà¤°à¤¸à¥à¤¯ आकारं +सà¥à¤¥à¤¿à¤¤à¤¿ +कड़ी +तà¥à¤°à¥à¤Ÿà¤¿ +कà¥à¤² आकारं +सà¥à¤µà¤¤à¤¨à¥à¤¤à¥à¤° रिकà¥à¤¤à¤¸à¥à¤¥à¤¾à¤¨à¤‚(खाली जगह) +कà¥à¤²à¤¸à¥à¤Ÿà¤°(समूह) आकारं +धà¥à¤¯à¤¾à¤¨à¤¾à¤•रà¥à¤·à¤•ं(लेबलमà¥) +सà¥à¤¥à¤¾à¤¨à¤¿à¤¯ नाममॠ+पà¥à¤°à¤¦à¤¾à¤¯à¤•मॠ+2100 +विकलà¥à¤ªà¤®à¥ +भाषा +भाषा: +संपादकमॠ+&संपादक: +&अनà¥à¤¤à¤°: +2200 +पà¥à¤°à¤£à¤¾à¤²à¥€ वा तंतà¥à¤°à¤®à¥ +संबधित कà¥à¤°à¥ 7-जिप के साथ: +2301 +7-जिपसà¥à¤¯ शेल(कवच) पà¥à¤°à¤¸à¤‚ग मेनॠमें जोडें +सोपानीकृत(केसà¥à¤•ेडेड) पà¥à¤°à¤¸à¤‚ग मेनॠ+पà¥à¤°à¤¸à¤‚ग(कोनà¥à¤Ÿà¥‡à¤•à¥à¤¸à¥à¤Ÿ) मेनॠवसà¥à¤¤à¥à¤à¤: +2320 +<फोलà¥à¤¡à¤°à¤‚> +<संगà¥à¤°à¤¹à¤®à¥(आरà¥à¤šà¤¿à¤µ)> +संगà¥à¤°à¤¹à¤®à¥ अनावृतं +फ़ाइलà¥à¤¸ बहिः निकाले... +संगà¥à¤°à¤¹à¤®à¥ में जोड़े... +संगà¥à¤°à¤¹à¤®à¤¸à¥à¤¯ जाà¤à¤š कà¥à¤°à¥ +यहीं बहिः निकाले +{0} में बहिः निकाले +{0} में जोड़े +संकà¥à¤šà¤¨à¤‚ à¤à¤µà¤®à¥ ईमेलमॠकà¥à¤°à¥... +{0} में दबायें à¤à¤µà¤®à¥ ईमेलं कà¥à¤°à¥ +2400 +फ़ोलà¥à¤¡à¤°à¥à¤¸ +&कारà¥à¤¯à¤°à¤¤ फोलà¥à¤¡à¤°à¤‚ +&पà¥à¤°à¤£à¤¾à¤²à¤¯à¤¾à¤¸à¥à¤¯ असà¥à¤¥à¤¾à¤¯à¥€(टेमà¥à¤ªà¤°à¤°à¥€) फोलà¥à¤¡à¤°à¤‚ +&चालू +&निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ: +मातà¥à¤° हटाने योगà¥à¤¯(रिमूवेबल) डà¥à¤°à¤¾à¤ˆà¤µà¤¾à¤¯ ही पà¥à¤°à¤¯à¥‹à¤—ं कà¥à¤°à¥ +असà¥à¤¥à¤¾à¤¯à¥€ संगà¥à¤°à¤¹à¤®à¥ फाइलाय सà¥à¤¥à¤¾à¤¨à¤‚ निरà¥à¤¦à¤¿à¤·à¥à¤Ÿà¤‚ कà¥à¤°à¥(बतायें). +2500 +वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤à¤ +दिखाओ ".."वसà¥à¤¤à¥ +वासà¥à¤¤à¤µà¤¿à¤• फ़ाइल पà¥à¤°à¤¤à¤¿à¤®à¤¾à¤¯à¥‡à¤‚ दिखाओ +तंतà¥à¤°à¤¸à¥à¤¯ मेनॠदिखाओ +&पूरà¥à¤£ पनà¥à¤•à¥à¤¤à¤¿à¤¸à¥à¤¯ चयनं +&गà¥à¤°à¤¿à¤¡(जाल) रेखा दिखाओ +वसà¥à¤¤à¥ अनावृताय à¤à¤•ं ही(सिंगल)-कà¥à¤²à¤¿à¤•मॠ+&वैकलà¥à¤ªà¤¿à¤• चयनं सà¥à¤¥à¤¿à¤¤à¤¿ +&विशालॠसà¥à¤®à¥ƒà¤¤à¤¿ पृषà¥à¤ à¤¸à¥à¤¯ पà¥à¤°à¤¯à¥‹à¤—ं कà¥à¤°à¥ +2900 +7-जिप विषय +7-जिपं à¤à¤¤à¤¤ निःशà¥à¤²à¥à¤• सॉफ़à¥à¤Ÿà¤µà¥‡à¤¯à¤° असà¥à¤¤à¤¿. तथापि, भवानॠपंजीकृतं(रजिसà¥à¤Ÿà¤°à¥à¤¡) होकर७-ज़िपसà¥à¤¯ विकास में सहयोग कर सकते असà¥à¤¤à¤¿. +3000 +तंतà¥à¤°à¤®à¥ आवशà¥à¤¯à¤• मातà¥à¤°à¤¾ में मेमोरी(सà¥à¤®à¥ƒà¤¤à¤¿) वितरितं ना कर सकता असà¥à¤¤à¤¿ +इनमे कोई भी तà¥à¤°à¥à¤Ÿà¤¿ ना असà¥à¤¤à¤¿ +{0} चयनित वसà¥à¤¤à¥(à¤à¤) +'{0}' फोलà¥à¤¡à¤°à¤‚ सरà¥à¤œà¤¿à¤¤ ना कर सकता +à¤à¤¤à¤¤à¥ संगà¥à¤°à¤¹à¤®à¤¸à¥à¤¯ अदà¥à¤¯à¤¤à¤¨à¥€à¤•ृत संचालन समरà¥à¤¥à¤¿à¤¤ ना असà¥à¤¤à¤¿. +'{0}' फाइल को संगà¥à¤°à¤¹à¤®à¥ के रूप में ना खोल सकता +'{0}' गà¥à¤ªà¥à¤¤à¤¿à¤•ृतमॠसंगà¥à¤°à¤¹à¤®à¥ को ना खोल सकता. मिथà¥à¤¯à¤¾ कूटशबà¥à¤¦à¤®à¥? +असमरà¥à¤¥à¤¿à¤¤ संगà¥à¤°à¤¹à¤®à¥ पà¥à¤°à¤•ारं +फाइलं {0} पहले से मौजूद असà¥à¤¤à¤¿ +'{0}' फ़ाइल परिवरà¥à¤§à¤¿à¤¤à¤‚ हà¥à¤ˆ असà¥à¤¤à¤¿.\nकà¥à¤¯à¤¾ तà¥à¤® संगà¥à¤°à¤¹à¤®à¥ में इसे अदà¥à¤¯à¤¤à¤¨à¥€à¤•ृतं करना चाहते हो? +फ़ाइल को अदà¥à¤¯à¤¤à¤¨à¥€à¤•ृतं ना कर सकता\n'{0}' +संपादक को शà¥à¤°à¥‚ ना कर सकता. +यह फाइलं à¤à¤• विषाणà¥(वायरस) जैसी लगती असà¥à¤¤à¤¿(फाइलं नाम लंबी खाली जगह नाम में रखता असà¥à¤¤à¤¿). +जिस फोलà¥à¤¡à¤°à¤‚ का लंबा मारà¥à¤— असà¥à¤¤à¤¿ उससे सञà¥à¤šà¤¾à¤²à¤¨à¤‚ कà¥à¤°à¤¿à¤¯à¤¾ ना बà¥à¤²à¤¾à¤ˆ जा सकती. +तà¥à¤®à¥à¤¹à¥‡ à¤à¤• फाइलं का चयन तो करना ही होगा +तà¥à¤®à¥à¤¹à¥‡ à¤à¤• वा अधिक फाइलों को चà¥à¤¨à¤¨à¤¾ ही होगा +अतà¥à¤¯à¤§à¤¿à¤• वसà¥à¤¤à¥à¤à¤ +3300 +बहिः निकाल रहा असà¥à¤¤à¤¿ +संकà¥à¤šà¤¨à¤‚ कर रहा असà¥à¤¤à¤¿ +परीकà¥à¤·à¤£à¤®à¥ +अनावृतं कर रहा असà¥à¤¤à¤¿... +तलाशी(सà¥à¤•ैनिंग) कर रहा असà¥à¤¤à¤¿... +3400 +बहिः निकाले +&बहिः निकाले: +बहिः निकाली हà¥à¤ˆ फ़ाइलों के लिये सà¥à¤¥à¤¾à¤¨à¤‚ निरà¥à¤¦à¤¿à¤·à¥à¤Ÿà¤‚ कà¥à¤°à¥. +3410 +मारà¥à¤— सà¥à¤¥à¤¿à¤¤à¤¿ +पूरà¥à¤£ मारà¥à¤—नामं +कोई मारà¥à¤— नामं ना असà¥à¤¤à¤¿ +3420 +अधिलेखन रीत +अधिलेखन करने से पहले पृचà¥à¤›à¤¾à¤ƒ +बिना पृचà¥à¤›à¤¾à¤ƒ अधिलेखनं(पà¥à¤°à¤¾à¤¨à¥‡ को मिटाना) कà¥à¤°à¥ +पहले से मौजूद फ़ाइलस को छोड़े +सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ पà¥à¤¨: नामकरणं +पहले से मौजूद फ़ाइलस का सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤à¤‚(ओटोमेटिक) पà¥à¤¨: नामकरणं कà¥à¤°à¥ +3500 +फ़ाइलं पà¥à¤°à¤¤à¤¿à¤¸à¥à¤¥à¤¾à¤ªà¤¨à¤‚ को पकà¥à¤•ा कà¥à¤°à¥ +गनà¥à¤¤à¤µà¥à¤¯ फोलà¥à¤¡à¤°à¤‚ में पहले से ही पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ हà¥à¤ˆ फ़ाइलं असà¥à¤¤à¤¿. +कà¥à¤¯à¤¾ भवानॠपहले से मौजूद फ़ाइल को बदलना पसंद करेंगे? +इसके साथ? +{0} बाइटà¥à¤¸ +सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ पà¥à¤¨: नामकरणमॠ+3700 +'{0}' के लिठअसहायक दबाने की पदà¥à¤§à¤¤à¤¿. +डेटा तà¥à¤°à¥à¤Ÿà¤¿'{0}' में. फ़ाइलं टूटी हà¥à¤ˆ असà¥à¤¤à¤¿. +'{0}' में सीआरसी असफल. फ़ाइलं टूटी हà¥à¤ˆ असà¥à¤¤à¤¿. +'{0}' गà¥à¤ªà¥à¤¤à¤¿à¤•ृतमà¥(à¤à¤¨à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿà¥‡à¤¡) फाइलं में डेटा तà¥à¤°à¥à¤Ÿà¤¿. मिथà¥à¤¯à¤¾ कूटशबà¥à¤¦à¤®à¥? +'{0}'गà¥à¤ªà¥à¤¤à¤¿à¤•ृतमà¥(à¤à¤¨à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿà¥‡à¤¡) फाइलं में सीआरसी असफल. मिथà¥à¤¯à¤¾ कूटशबà¥à¤¦à¤®à¥? +3800 +कूटशबà¥à¤¦à¤®à¥(पासवरà¥à¤¡) डाले +कूटशबà¥à¤¦à¤®à¥(पासवरà¥à¤¡) डाले: +कूटशबà¥à¤¦à¤®à¥ पà¥à¤¨à¤ƒ डाले: +&कूटशबà¥à¤¦à¤®à¥(पासवरà¥à¤¡) दिखाओ +कूटशबà¥à¤¦à¤®à¥ सहेजे हà¥à¤ से अलग असà¥à¤¤à¤¿ +कूटशबà¥à¤¦à¤®à¥ के लिये मातà¥à¤° इंगà¥à¤²à¤¿à¤¶ वरà¥à¤£à¤®à¤¾à¤²à¤¾, अंकाः और विशेष अकà¥à¤·à¤°à¥‹à¤‚ (!, #, $, ...) का ही उपयोग कà¥à¤°à¥ +कूटशबà¥à¤¦à¤®à¥ अतà¥à¤¯à¤§à¤¿à¤• विशाल असà¥à¤¤à¤¿ +कूटशबà¥à¤¦à¤®à¥(पासवरà¥à¤¡) +3900 +वà¥à¤¯à¤¤à¥€à¤¤ समय: +शेष बचा समय: +कà¥à¤² आकारं: +गति: +पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ किया हà¥à¤†: +दबाने(आकारं छोटा करने) का अनà¥à¤ªà¤¾à¤¤à¤‚: +तà¥à¤°à¥à¤Ÿà¤¿à¤¯à¤¾à¤: +संगà¥à¤°à¤¹à¤®à¥: +4000 +संगà¥à¤°à¤¹à¤®à¥ में जोड़े +&संगà¥à¤°à¤¹à¤®à¥: +&अदà¥à¤¯à¤¤à¤¨à¥€à¤•रणं सà¥à¤¥à¤¿à¤¤à¤¿(मोड): +संगà¥à¤°à¤¹à¤®à¥ &ढाà¤à¤šà¤¾: +&संकà¥à¤šà¤¨à¤®à¥ सà¥à¤¤à¤°: +&संकà¥à¤šà¤¨à¤®à¥ विधि: +&शबà¥à¤¦à¤•ोशमॠआकारं: +&शबà¥à¤¦ आकारं: +ठोस टà¥à¤•डे का आकारं: +सीपीयू सूतà¥à¤° संखà¥à¤¯à¤¾: +&परिमाप: +विकलà¥à¤ª +&à¤à¤¸à¤à¥žà¤à¤•à¥à¤¸(SFX) संगà¥à¤°à¤¹à¤®à¥ निरà¥à¤®à¤¾à¤£à¤®à¥ कà¥à¤°à¥ +साà¤à¥€ फाइलें संकà¥à¤šà¤¿à¤¤à¤‚ कà¥à¤°à¥ +गà¥à¤ªà¥à¤¤à¤¿à¤•रणमॠ+गà¥à¤ªà¥à¤¤à¤¿à¤•रणमॠपदà¥à¤§à¤¤à¤¿: +फ़ाइल &नाम गà¥à¤ªà¥à¤¤à¤¿à¤•रणमॠकà¥à¤°à¥ +संकà¥à¤šà¤¨à¤¸à¥à¤¯ सà¥à¤®à¥ƒà¤¤à¤¿ पà¥à¤°à¤¯à¥‹à¤—: +पà¥à¤°à¤¸à¤¾à¤°à¤£à¤¸à¥à¤¯ सà¥à¤®à¥ƒà¤¤à¤¿ पà¥à¤°à¤¯à¥‹à¤—: +4050 +भणà¥à¤¡à¤¾à¤°à¤£à¤®à¥ +सरà¥à¤µà¤¾à¤§à¤¿à¤• तेज +तेज +साधारणमॠ+अधिकतम +अतà¥à¤¯à¤¨à¥à¤¤ +4060 +फ़ाइलें जोड़े à¤à¤µà¤®à¥ पà¥à¤°à¤¤à¤¿à¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ कà¥à¤°à¥ +फ़ाइले अदà¥à¤¯à¤¤à¤¨à¥€à¤•ृतं कà¥à¤°à¥ à¤à¤µà¤®à¥ जोड़े +अवसà¥à¤¥à¤¿à¤¤à¤‚ फ़ाइलें ताजा कà¥à¤°à¥ +फाइलें समकà¥à¤°à¤®à¤£(सिंकà¥à¤°à¥‹à¤¨à¤¾à¤ˆà¥›) कà¥à¤°à¥ +4070 +बà¥à¤°à¤¾à¤‰à¤œà¤®à¥ वा घूमे +सरà¥à¤µà¤¾à¤ƒ फ़ाइलें +अ-ठोसं +ठोसं +6000 +नकल +ले जायें +में नकल: +में ले जायें: +नकल... +ले जा रहा असà¥à¤¤à¤¿... +पà¥à¤¨: नामकरणं... +गनà¥à¤¤à¤µà¥à¤¯ फोलà¥à¤¡à¤°à¤‚ चयनित कà¥à¤°à¥. +à¤à¤¤à¤¤à¥ फोलà¥à¤¡à¤°à¤‚ के लिये यह सञà¥à¤šà¤¾à¤²à¤¨à¤‚ कà¥à¤°à¤¿à¤¯à¤¾ समरà¥à¤¥à¤¿à¤¤à¤‚ ना असà¥à¤¤à¤¿. +फ़ाइल वा फोलà¥à¤¡à¤°à¤‚ के पà¥à¤¨: नामकरणं में तà¥à¤°à¥à¤Ÿà¤¿ +फ़ाइल की नकल करना पकà¥à¤•ा कà¥à¤°à¥ +तà¥à¤® संगà¥à¤°à¤¹à¤®à¥ में फाइलं की पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¤¿ करना चाहते हो कà¥à¤¯à¤¾ तà¥à¤®à¥à¤¹à¥‡ यकीन असà¥à¤¤à¤¿ +6100 +फ़ाइल मिटाये यह पकà¥à¤•ा कà¥à¤°à¥ +फोलà¥à¤¡à¤°à¤‚ मिटायें पकà¥à¤•ा कà¥à¤°à¥ +अनेक फ़ाइल मिटायें पकà¥à¤•ा कà¥à¤°à¥ +कà¥à¤¯à¤¾ तà¥à¤®à¥à¤¹à¥‡ यकीन असà¥à¤¤à¤¿ कि तà¥à¤® मिटाना चाहते हो '{0}'? +कà¥à¤¯à¤¾ तà¥à¤®à¥à¤¹à¥‡ यकीन असà¥à¤¤à¤¿ कि तà¥à¤® फोलà¥à¤¡à¤°à¤‚ मिटाना चाहते हो '{0}' और इसकी सरà¥à¤µà¤¾à¤ƒ सामगà¥à¤°à¥€ भी? +कà¥à¤¯à¤¾ तà¥à¤®à¥à¤¹à¥‡ यकीन असà¥à¤¤à¤¿ कि तà¥à¤® मिटाना चाहते हो इन {0} वसà¥à¤¤à¥à¤“ं को? +मिटा रहा असà¥à¤¤à¤¿... +फ़ाइलं किंवा फोलà¥à¤¡à¤°à¤‚ मिटाने में तà¥à¤°à¥à¤Ÿà¤¿ +तंतà¥à¤° लंबे मारà¥à¤— वाली फाइलं को पà¥à¤¨à¤ƒà¤šà¤•à¥à¤°à¤£ पेटी(रिसाईकल बिन) में ना ले जा सकता असà¥à¤¤à¤¿. +6300 +फ़ॊलà¥à¤¡à¤° निरà¥à¤®à¤¾à¤£à¤®à¥ कà¥à¤°à¥ +फ़ाइल निरà¥à¤®à¤¾à¤£à¤®à¥ कà¥à¤°à¥ +फोलà¥à¤¡à¤°à¤‚ नाम: +फ़ाइल नाम: +नवीन फ़ॊलà¥à¤¡à¤° +नवीन फ़ाइलं +फोलà¥à¤¡à¤°à¤‚ निरà¥à¤®à¤¾à¤£à¤®à¥ करने में तà¥à¤°à¥à¤Ÿà¤¿ +फ़ाइल निरà¥à¤®à¤¾à¤£à¤®à¥ करने में तà¥à¤°à¥à¤Ÿà¤¿ +6400 +टिपà¥à¤ªà¤£à¥€ +&टिपà¥à¤ªà¤£à¥€: +चयनमॠ+चयन रदà¥à¤¦ +मà¥à¤–ौटा: +6600 +गà¥à¤£à¤®à¥ वा संपतà¥à¤¤à¤¿à¤¯à¤¾à¤ +फ़ोलà¥à¤¡à¤°à¥‹à¤‚ का इतिहास +निदानातà¥à¤®à¤•ं संदेश +संदेशं +7100 +संगणकमॠ+सञà¥à¤œà¤¾à¤²à¤®à¥ +दसà¥à¤¤à¤¾à¤µà¥‡à¤œà¤®à¥ +पà¥à¤°à¤£à¤¾à¤²à¥€ +7200 +जोड़े +बहिः निकाले +परीकà¥à¤·à¤£à¤®à¥ +नकल +ले जायें +मिटायें +सूचना +7300 +फ़ाइलसà¥à¤¯ विभाजनं कà¥à¤°à¥ +&में विभाजनं: +जतà¥à¤¥à¥‹à¤‚ में विभाजनं, बाइटà¥à¤¸: +विभाजनं कर रहा असà¥à¤¤à¤¿... +विभाजनं करना पकà¥à¤•ा कà¥à¤°à¥ +कà¥à¤¯à¤¾ तà¥à¤®à¥à¤¹à¥‡ यकीन असà¥à¤¤à¤¿ कि तà¥à¤® फाइलं को {0} जतà¥à¤¥à¥‹à¤‚ में विभाजित करना चाहते हो? +मूल फाइलं के आकारं की तà¥à¤²à¤¨à¤¾ में जतà¥à¤¥à¥‡ का आकारं छोटा ही होना चाहिठ+जतà¥à¤¥à¥‡ का आकारं मिथà¥à¤¯à¤¾ असà¥à¤¤à¤¿ +निरà¥à¤¦à¥‡à¤¶à¤¿à¤¤ जतà¥à¤¥à¤¾ आकारं: {0} बाइटस.\n भवानॠसंगà¥à¤°à¤¹à¤®à¥ को ऎसे जतà¥à¤¥à¥‹à¤‚ में विभाजितं करना चाहते असà¥à¤¤à¤¿, कà¥à¤¯à¤¾ आपको यकीन असà¥à¤¤à¤¿? +7400 +फ़ाइले संयोजितं कà¥à¤°à¥ +&में संयोजनं कà¥à¤°à¥: +संयोजनं हो रहा असà¥à¤¤à¤¿... +विभाजितं फाइल का मातà¥à¤° पà¥à¤°à¤¥à¤® भागं ही चयनितं कà¥à¤°à¥ +फाइलं को विभाजित फाइलं के भाग के रूप में पहचान ना सकता +विभाजित फाइलं का à¤à¤• से जà¥à¤¯à¤¾à¤¦à¤¾ भाग ना ढूà¤à¤¢ सकता +7500 +जाà¤à¤šà¤¯à¥‹à¤—सà¥à¤¯(चेकसम) गणनां कर रहा असà¥à¤¤à¤¿... +जाà¤à¤šà¤¯à¥‹à¤—(चेकसम) माहिती +सीआरसी जाà¤à¤šà¤¯à¥‹à¤—(चेकसम) आà¤à¤•ड़ों के लिये : +सीआरसी जाà¤à¤šà¤¯à¥‹à¤—(चेकसम) आà¤à¤•ड़ों और नामों के लिये : +7600 +(कसौटी चिनà¥à¤¹à¤‚)बेञà¥à¤šà¤®à¤¾à¤°à¥à¤•मॠ+सà¥à¤®à¥ƒà¤¤à¤¿ उपयोग: +संकà¥à¤šà¤¨à¤‚ कर रहा असà¥à¤¤à¤¿ +पà¥à¤°à¤¸à¤¾à¤°à¤£à¤‚ हो रहा असà¥à¤¤à¤¿ +कà¥à¤°à¤®à¤¾à¤‚कन +कà¥à¤² कà¥à¤°à¤®à¤¾à¤‚कन +वरà¥à¤¤à¤®à¤¾à¤¨à¤®à¥ +परिणाममॠ+सीपीयू उपयोग +कà¥à¤°à¤®à¤¾à¤‚कन / उपयोग +पास: diff --git a/Utils/7-Zip/Lang/si.txt b/Utils/7-Zip/Lang/si.txt new file mode 100644 index 000000000..5a4900181 --- /dev/null +++ b/Utils/7-Zip/Lang/si.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.59 : සුපුන් බුධà·à¶¢à·“à·€ (Supun Budhajeewa) +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Sinhala +සිංහල +401 +හරි +à¶‘à¶´à· + + + +ඔවු (&Y) +à¶±à·à·„à· (&N) +වසන්න (&C) +උදව් + +à¶±à·à·€à¶­ අරඹන්න (&C) +440 +සියල්ලටම ඔවු (&A) +සියල්ලටම à¶±à·à·„à·(&L) +නවතන්න +à¶´à·Šâ€à¶»à¶­à·Šâ€à¶ºà·à¶»à¶¸à·Šà¶· කරන්න +පසුබිමින් (&B) +පෙරබිමින් (&F) +මදකට නවතන්න (&P) +මදකට නවත෠ඇත +ඉවත්වීමට à¶…à·€à·à·Šâ€à¶º à¶¶à·€ විà·à·Šà·€à·à·ƒ ද ? +500 +ගොනුව (&F) +à·ƒà·à¶šà·ƒà·”ම් (&E) +දසුන (&V) +à¶šà·à¶¸à¶­à·’ම (&A) +මෙවලම් (&T) +උදව් (&H) +540 +විවෘත කරන්න (&O) +මෙහිම විවෘත කරන්න (&I) +පිටතින් විවෘත කරන්න (&U) +දසුන (&V) +සංස්කරණය කරන්න (&E) +à¶±à·à·€à¶­ නම් කරන්න (&M) +වෙනත් à¶­à·à¶±à¶šà¶§ à¶´à·’à¶§à¶´à¶­à·Š කරන්න (&C)... +වෙනත් à¶­à·à¶±à¶šà¶§ ගෙනයâ€à¶±à·Šà¶± (&M)... +මකන්න (&D) +ගොනුව බෙදන්න (&S)... +ගොනු à¶‘à¶šà¶­à·” කරන්න (&B)... +වත්කම් (&R) +à¶§à·“à¶šà· (&N) +checksum ගණනය කරන්න + +à¶¶à·„à·à¶½à·”මක් තනන්න +ගොනුවක් තනන්න +ඉවත් වන්න (&X) +600 +සියල්ල à¶­à·à¶»à¶±à·Šà¶± (&A) +à¶­à·à¶»à¶± ලද සියල්ල ඉන් ඉවත් කරන්න +අනෙක් à¶´à·à¶­à·Šà¶­à¶§ à¶­à·à¶»à¶±à·Šà¶± (&I) +à¶­à·à¶»à¶±à·Šà¶±... +තේරීම ඉවත් කරන්න... +වර්ගයෙන් à¶­à·à¶»à¶±à·Šà¶± +වර්ගයෙන් තේරීම ඉවත් කරන්න +700 +විà·à·à¶½ මූර්ති (&G) +කුඩ෠මූර්ති (&M) +ලයිස්තුව (&L) +සවිස්තර (&D) +730 +අසුරන නොලද +à¶´à·à¶­à¶½à·’ දසුන +à¶´à·à¶±à¶½ &2 +මෙවලම් තීරු (&T) +à¶´à·Šâ€à¶»à¶°à·à¶± à¶¶à·„à·à¶½à·”ම විවෘත කරන්න +ඉහළට à¶‘à¶šà·Š ස්ථරයක් +à¶¶à·„à·à¶½à·”ම් අතීතය... +à¶´à·Šâ€à¶»à¶­à·’පූරණය කරන්න(&R) +750 +à·„à·à¶šà·’ළුම් මෙවලම් තීරුව +සම්මත මෙවලම් තීරුව +විà·à·à¶½ බොත්තම් +බොත්තම් පෙළ දක්වන්න +800 +à¶¶à·„à·à¶½à·”ම à¶šà·à¶¸à¶­à·’ම à¶…à¶‚à¶œ වලට à¶‘à¶šà¶­à·” කරන අයුර (&A) +පොත් සලකුණ - à¶…à¶‚à¶š +900 +විකල්ප (&O)... +&Benchmark +960 +අන්තර්ගතය (&C)... +7-Zip පිළිබඳව (&A)... +1003 +මංපෙත +නම +දිගුව +à¶¶à·„à·à¶½à·”ම +විà·à·à¶½à¶­à·Šà·€à¶º +à·„à·à¶šà·’ළුමෙන් පසු විà·à·à¶½à¶­à·Šà·€à¶º +ලක්ෂණ +නිර්මà·à¶«à¶º à¶šà·… දිනය +à¶´à·Šâ€à¶»à·€à·šà·Â à·€à·– දිනය +අළුත් à¶šà·… දිනය +සවි +ටීක෠කොට ඇත +සුරක්ෂිතය +Split Before +Split After +Dictionary +CRC +වර්ගය +Anti +à¶šà·Šâ€à¶»à¶¸à¶º +à¶°à·à¶»à¶š මෙහෙයුම් පද්ධතිය +ගොනු පද්ධතිය +පරිà·à·’ලක +සමූහය +Block +à¶§à·“à¶šà·à·€ +පිහිටීම +Path Prefix +à¶¶à·„à·à¶½à·”ම් +ගොනු +සංස්කරණය +Volume +Multivolume +Offset +සබà·à¶³à·“න් +Blocks +Volumes + +à¶¶à·’à¶§à·Š-64 +Big-endian +මධ්â€à¶ºà¶¸ à·ƒà·à¶šà·ƒà·”ම් පද්ධතිය (CPU) +à¶·à·žà¶­à·’à¶š විà·à·à¶½à¶­à·Šà·€à¶º +à·à·“ර්ෂ විà·à·à¶½à¶­à·Šà·€à¶º +Checksum +මුහුණුවර +Virtual Address + + + + + + +දà·à·‚යක් +සම්පූර්ණ විà·à·à¶½à¶­à·Šà·€à¶º +නිදහස් ඉඩ +Cluster විà·à·à¶½à¶­à·Šà·€à¶º +නම් à¶´à¶­ +Local Name +සපයන්න෠+2100 +විකල්ප +à¶·à·à·‚à·à·€ +à¶·à·à·‚à·à·€: +සංස්කà·à¶»à¶šà¶º +සංස්කà·à¶»à¶šà¶º (&E): + +2200 +පද්ධතිය +7-Zip සමග හවුල් කරන්න: +2301 +Shell context මෙනුව වෙත 7-Zip අන්තර්ගත කරන්න +Context menu à¶…à¶‚à¶œ à¶‘à¶šà·Š අංගයක් යටතේ සඳහන් කරන්නâ€â€â€ +Context මෙනු à¶…à¶‚à¶œ: +2320 +<à¶¶à·„à·à¶½à·”ම> +<à·„à·à¶šà·’ළුම> +à·„à·à¶šà·’ළුම විවෘත කරන්න +ගොනු ලිහන්න... +à·„à·à¶šà·’ළුමකට ඇතුල් කරන්න... +à·„à·à¶šà·’ළුම පරීක්ෂ෠කරන්න +මෙතà·à¶±à¶§ ලිහන්න +{0} වෙත ලිහන්න +{0} වෙත à¶‘à¶šà·Š කරන්න +හකුළ෠විදුලි à¶­à·à¶´à·à¶½à·Š කරන්න... +{0} වෙත හකුළ෠විදුලි à¶­à·à¶´à·à¶½à·Š කරන්න +2400 +à¶¶à·„à·à¶½à·”ම් +සක්â€à¶»à·“ය à¶¶à·„à·à¶½à·”ම් (&W) +පද්ධති à¶­à·à·€à¶šà·à¶½à·’à¶š දෑ (Temp) à¶¶à·„à·à¶½à·”ම (&S) +වත්මන් (&C) +à¶­à·à¶»à¶±à·Šà¶± (&S): +ඉවත් කළ හà·à¶šà·’ à¶°à·à·€à¶š සඳහ෠පමණක් à¶·à·à·€à·’ත෠කරන්න +à¶­à·à·€à¶šà·à¶½à·’à¶š à·„à·à¶šà·’ළුම් ගොනු සඳහ෠ස්ථà·à¶±à¶ºà¶šà·Š දෙන්න. +2500 +සෙටිංග්ස් +".." අංගය පෙන්වන්න +ගොනු වල සත්â€à¶ºÂ à¶¸à·–ර්තිය පෙන්වන්න +පද්ධති මෙනුව පෙන්වන්න +සම්පූර්ණ තීරු තේරීම (&F) +වගු රේභ෠පෙන්වන්න (&G) + +වෛකල්පිත තේරීම් ස්වභà·à·€à¶º (&A) +විà·à·à¶½ මතක à¶´à·’à¶§à·” à¶·à·à·€à·’ත෠කරන්න (&L) +2900 +7-Zip පිළිබඳව +7-Zip නිදහස් මෘදුකà·à¶‚ගයක් වුවත්, ලියà·à¶´à¶¯à·’à¶‚à¶ à·’ වීම මගින් 7-Zip à·„à·’ à·€à·à¶©à·’ දියුණුව සඳහ෠ඔබට දà·à¶ºà¶š විය à·„à·à¶šà·’ය.\n\nමෙම සිංහල à¶·à·à·‚෠පරිවර්තනය G.S.N. සුපුන් බුධà·à¶¢à·“à·€ (budhajeewa@gmail.com) විසින් සිදු කරන ලදී. +3000 +à¶…à·€à·à·Šâ€à¶º මතක à¶´à·Šâ€à¶»à¶¸à·à¶«à¶º පද්ධතියට වෙන් à¶šà¶» ගත නොහà·à¶š +දà·à·‚ නොමà·à¶­ +වස්තු {0}à¶šà·Š à¶­à·à¶»à· ඇත +'{0}' à¶¶à·„à·à¶½à·”ම à¶­à·à¶±à·’ය නොහà·à¶š +මෙම à·„à·à¶šà·’ළුම සඳහ෠යà·à·€à¶­à·Šà¶šà·à¶½ කිරීම් සහයà·à¶œà¶º නොදක්වයි. +'{0}' ගොනුව à·„à·à¶šà·’ළුමක් ලෙස විවෘත කළ නොහà·à¶š +සුරක්ෂිත '{0}' à·„à·à¶šà·’ළුම විවෘත කළ නොහà·à¶š. මුරපදය à·€à·à¶»à¶¯à·’ ද ? +සහයà·à¶œà·“ නොවන à·„à·à¶šà·’ළුම් à¶šà·Šâ€à¶»à¶¸à¶ºà¶šà·Š +{0} ගොනුව දà·à¶±à¶§à¶¸à¶­à·Š ඇත +'{0}' ගොනුව නව්â€à¶º කෙරිනි.\nà·„à·à¶šà·’ළුමෙහි එය යà·à·€à¶­à·Šà¶šà·à¶½ කිරීමට ඔබට à¶…à·€à·à·Šâ€à¶º ද ? +ගොනුව යà·à·€à¶­à·Šà¶šà·à¶½ à¶šà¶½ නොහà·à¶š\n'{0}' +සංස්කà·à¶»à¶šà¶º විවෘත කළ නොහà·à¶š. +ගොනුව වෛරසයක් à·€à·à¶±à·’ය (ගොනු à¶±à·à¶¸à¶ºà·š දිගු හිස් à¶­à·à¶±à·Š ඇත). +දිගු මංපෙතක් ඇති à¶¶à·„à·à¶½à·”මකින් à¶šà·Šâ€à¶»à·’යà·à¶šà·à¶»à·“ත්වය à¶šà·à¶³à·€à·’ය නොහà·à¶š. +à¶‘à¶šà·Š ගොනුවක් පමණක් තේරිය යුතුය. +ගොනු à¶‘à¶šà¶šà·Š à·„à· à·€à·à¶©à·’ ගණනක් තේරිය යුතුය. +à¶…à¶‚à¶œ ඉත෠අධිකය +3300 +ලිහමින් පවතී†+හකුළමින් පවතී +පරීක්ෂ෠කිරීම +විවෘත කරමින් පවතී... +සුපිරික්සමින් පවතී... +3400 +ලිහන්න +ලිහීමට à¶­à·à¶±à¶šà·Š (&X) : +ලිහන ලද ගොනු සඳහ෠ස්ථà·à¶±à¶ºà¶šà·Š à¶­à·à¶»à¶±à·Šà¶±. +3410 +මංපෙත් ස්වභà·à·€à¶º +සම්පූර්ණ මංපෙත් නම් +මංපෙත් නම් à¶…à·€à·à·Šâ€à¶º à¶±à·à¶­ +3420 +උඩින් ලිවීමේ ස්වභà·à·€à¶º +උඩින් ලියන්නට පෙර විමසන්න +විමසීමෙන් තොරව උඩින් ලියන්න +දà·à¶±à¶§à¶¸à¶­à·Š ඇති ගොනු මගහරින්න +ගොනු ස්වයංක්â€à¶»à·’යව à¶´à·Šâ€à¶»à¶­à·’නම් කරන්න +දà·à¶±à¶§à¶¸à¶­à·Š ඇති ගොනු†ප්â€à¶»à¶­à·’නම් කරන්න +3500 +ගොනු à¶´à·Šâ€à¶»à¶­à·’ස්ථà·à¶´à¶±à¶º තහවුරු à¶šà¶» à¶œà·à¶±à·“ම +ගමනà·à¶±à·Šà¶­ à¶¶à·„à·à¶½à·”මේ දà·à¶±à¶§à¶¸à¶­à·Š නිර්මිත ගොනුවක් ඇත. +දà·à¶±à¶§à¶¸ පවතින à¶´à·„à¶­ ගොනුව, +මෙය සමග à¶´à·Šâ€à¶»à¶­à·’ස්ථà·à¶´à¶±à¶º කරන්න ද ? +{0} බයිට (Bytes) +ස්වයංක්â€à¶»à·’යව à¶´à·Šâ€à¶»à¶­à·’නම් කරන්න (&U) +3700 +'{0}' සඳහ෠සහයà·à¶œà¶º නොදක්වන à·„à·à¶šà·’ළුම් à¶šà·Šâ€à¶»à¶¸à¶ºà¶šà·Š. +'{0}' à·„à·’ දත්ත දà·à·‚යකි. ගොනුව බිඳී ඇත. +'{0}' à·„à·’ CRC අසමත් විනි. ගොනුව බිඳී ඇත. +සුරක්ෂිත '{0}' ගොනුවේ දත්ත දà·à·‚යකි. à·€à·à¶»à¶¯à·’ මුරපදයක් ද ? +සුරක්ෂිත '{0}' ගොනුවේ CRC අසමත් විනි. à·€à·à¶»à¶¯à·’ මුරපදයක් ද ? +3800 +මුරපදය ඇතුල් කරන්න +මුරපදය ඇතුල් කරන්න: +මුරපදය à¶±à·à·€à¶­ ඇතුල් කරන්න: +මුරපදය පෙන්වන්න (&S) +මුරපද නොගà·à¶½à¶´à·š +මුරපද සඳහ෠ඉංග්â€à¶»à·’සි අකුරු, ඉලක්කම් සහ විà·à·šà·‚à·’à¶­ සංකේත පමණක් à¶·à·à·€à·’ත෠කරන්න (!, #, $, ...) +මුරපදය දිග à·€à·à¶©à·’ය +මුරපදය +3900 +ගතවූ à¶šà·à¶½à¶º: +ඉතිරි à¶šà·à¶½à¶º: +සම්පූර්ණ විà·à·à¶½à¶­à·Šà·€à¶º: +වේගය: +à¶šà·Šâ€à¶»à·’යà·à·€à¶½à·’යට à¶·à·à¶¢à¶±à¶º වූ à¶´à·Šâ€à¶»à¶¸à·à¶«à¶º: +à·„à·à¶šà·’ළුම් අනුපà·à¶­à¶º: +දà·à·‚: +à·„à·à¶šà·’ළුම්: +4000 +à·„à·à¶šà·’ළුමකට à¶‘à¶šà·Š කරන්න +à·„à·à¶šà·’ළුම (&A): +යà·à·€à¶­à·Šà¶šà·à¶½ කිරීමේ ස්වභà·à·€à¶º (&U): +à·„à·à¶šà·’ළුම් රටà·à·€ (&F): +à·„à·à¶šà·’ළුම් මට්ටම (&L): +à·„à·à¶šà·’ළුම් à¶šà·Šâ€à¶»à¶¸à¶º (&M): +&Dictionary size: +&Word size: +Solid block size: +Number of CPU threads: +à¶´à¶»à·à¶¸à·’à¶­à·’ (&P): +විකල්ප +SF&X à·„à·à¶šà·’ළුමක් තනන්න +හවුල්කà·à¶» ගොනු ද හකුළන්න +සුරà·à¶šà·”ම් +සුරà·à¶šà·”ම් à¶šà·Šâ€à¶»à¶¸à¶º: +ගොනු à¶±à·à¶¸ සුරකින්න (&S) +à·„à·à¶šà·’ලීම සඳහ෠මතක à¶·à·à·€à·’තය: +ලිහීම à¶šà·Šâ€à¶»à·’යà·à·€ සඳහ෠මතක à¶·à·à·€à·’තය: +4050 +à¶­à·à¶±à·Šà¶´à¶­à·Š කරන්න +ඉත෠වේගවත් +වේගවත් +à·ƒà·à¶¸à·à¶±à·Šâ€à¶º +උපරිම +à¶…à¶°à·’ à·„à·à¶šà·’ළුම්†+4060 +ගොනු à¶‘à¶šà·Š à¶šà¶» à¶´à·Šâ€à¶»à¶­à·’ස්ථà·à¶´à¶±à¶º කරන්න +ගොනු යà·à·€à¶­à·Šà¶šà·à¶½ à¶šà¶» à¶‘à¶šà·Š කරන්න +දà·à¶±à¶§à¶¸à¶­à·Š ඇති ගොනු à¶±à·à·€à·”ම් කරන්න +ගොනු සමකà·à¶½à·“ කරන්න +4070 +සොයන්න +සියළු ගොනු +සවි-නොමà·à¶­à·’ +සවි +6000 +à¶´à·’à¶§à¶´à¶­à·Š කරන්න +ගෙනයන්න +වෙනත් à¶­à·à¶±à¶šà¶§ à¶´à·’à¶§à¶´à¶­à·Š කරන්න: +වෙනත් à¶­à·à¶±à¶šà¶§ ගෙනයන්න: +à¶´à·’à¶§à¶´à¶­à·Š කරමින් පවතී... +ගෙනයමින් පවතී... +à¶´à·Šâ€à¶»à¶­à·’නම් කරමින් පවතී... +ගමනà·à¶±à·Šà¶­ à¶¶à·„à·à¶½à·”ම à¶­à·à¶»à¶±à·Šà¶±. +à¶šà·Šâ€à¶»à·’යà·à·€ à¶šà¶½ නොහà·à¶š. +ගොනුව à·„à· à¶¶à·„à·à¶½à·”ම මà·à¶šà·“මේදී දà·à·‚යක් මතු විය +ගොනු à¶´à·’à¶§à¶´à¶­à·Š කිරීම තහවුරු කිරීම +ගොâ€à¶±à·”à·€ à·„à·à¶šà·’ළුම වෙත à¶´à·’à¶§à¶´à¶­à·Š කළ යුතු à¶¶à·€ ඔබට විà·à·Šà·€à·à·ƒ ද +6100 +ගොනු මà·à¶šà·“ම තහවුරු කිරීම +à¶¶à·„à·à¶½à·”ම් මà·à¶šà·“ම තහවුරු කිරීම +à¶¶à·„à·” ගොනු මà·à¶šà·“ම තහවුරු කිරීම +ඔබට '{0}' මà·à¶šà·“මට à¶…à·€à·à·Šâ€à¶ºÂ à¶¶à·€ විà·à·Šà·€à·à·ƒ ද ? +ඔබට '{0}' à¶¶à·„à·à¶½à·”ම à·„à· à¶‘à·„à·’ අන්තර්ගතය මà·à¶šà·“මට à¶…à·€à·à·Šâ€à¶ºÂ à¶¶à·€ විà·à·Šà·€à·à·ƒ ද ? +ඔබට {0} යන වස්තු මà·à¶šà·“මට à¶…à·€à·à·Šâ€à¶ºÂ à¶¶à·€ විà·à·Šà·€à·à·ƒ ද ? +මකමින් පවතී... +ගොනුව à·„à· à¶¶à·„à·à¶½à·”ම මà·à¶šà·“මේ දී දà·à·‚යක් මතු විය +දිගු මංපෙතක් ඇති ගොනුවක් කුණු බඳුනට යà·à·€à·“ම පද්ධතියට à¶šà¶½ නොහà·à¶š. +6300 +à¶¶à·„à·à¶½à·”මක් තනන්න +ගොනුවක් තනන්න +à¶¶à·„à·à¶½à·”ම් à¶±à·à¶¸à¶º: +ගොනු à¶±à·à¶¸à¶º: +නව à¶¶à·„à·à¶½à·”මක් +නව ගොනුවක් +à¶¶à·„à·à¶½à·”ම à¶­à·à¶±à·“මේදී දà·à·‚යක් මතුවිය +ගොනුව à¶­à·à¶±à·“මේදී දà·à·‚යක් මතුවිය +6400 +à¶§à·“à¶šà·à·€ +à¶§à·“à¶šà·à·€ (&C): +à¶­à·à¶»à¶±à·Šà¶± +තේරීම ඉවත් කරන්න +ගොනුවේ à·„à· à¶¶à·„à·à¶½à·”මේ නම හ෠කොටසක්: +6600 +වත්කම් +à¶¶à·„à·à¶½à·”ම් අතීතය +විනිà·à·Šà¶ à·“ය පණිවුඩ +පණිවිඩය +7100 +පරිගණකය +à¶¢à·à¶½à¶º +ලියකියවිලි +පද්ධතිය +7200 +හකුළන්න +ලිහන්න +පරීක්ෂ෠කරන්න +à¶´à·’à¶§à¶´à¶­à·Š කරන්න +ගෙන යන්න +මකන්න +තොරතුරු +7300 +ගොනුව බෙදන්න +ගොනුව බෙද෠කොටස් මෙහි à¶­à·à¶±à·Šà¶´à¶­à·Š කරන්න (&S): +කොටස් වලට බෙදන්න, බයිට (bytes): +ගොනුව බෙදමින් පවතී... +ගොනුව බෙදීම තහවුරු කරන්න +ගොනුව කොටස් {0} à¶šà¶§ බෙදීමට à¶…à·€à·à·Šâ€à¶º à¶¶à·€ ඔබට විà·à·Šà·€à·à·ƒ ද ? +බෙදූ කොටසක විà·à·à¶½à¶­à·Šà·€à¶º, මුල් ගොනුවේ විà·à·à¶½à¶­à·Šà·€à¶ºà¶§ වඩ෠අඩු විය යුතුය +à·€à·à¶»à¶¯à·’ කොටස් විà·à·à¶½à¶­à·Šà·€à¶ºà¶šà·’ +සඳහන් කරන ලද කොටස් විà·à·à¶½à¶­à·Šà·€à¶º බයිට {0} à¶šà·’.\nà·„à·à¶šà·’ළුම මෙවන් කොටස් වලට බෙදියයුතු à¶¶à·€ විà·à·Šà·€à·à·ƒ ද ? +7400 +බෙදූ ගොනු à¶‘à¶šà·Š කරන්න +බෙදූ ගොනු à¶‘à¶šà·Š à¶šà¶» මෙහි à¶­à·à¶±à¶´à¶­à·Š කරන්න (&C): +බෙදූ ගොන à¶‘à¶šà·Š කරමින් පවතී... +පළමු ගොනුව පමණක් à¶­à·à¶»à¶±à·Šà¶± +ගොනුව, බෙදන ලද ගොනුවක කොටසක් ලෙස හඳුනà·à¶œà¶­ නොහà·à¶š. +බෙදන ලද ගොනුවේ කොටස් වලින් à¶‘à¶šà¶šà¶§ වඩ෠සොයà·à¶œà¶­ නොහà·à¶š. +7500 +Checksum calculating... +Checksum information +CRC checksum for data: +CRC checksum for data and names: +7600 +Benchmark +මතක à¶·à·à·€à·’තය: +à·„à·à¶šà·’ළුම් +ලිහුම් +ඇගයුම +සම්පූර්ණ ඇගයුම +වත්මන් +à¶´à·Šâ€à¶»à¶­à·’ඵලිත +CPU à¶·à·à·€à·’à¶­à·à·€ +ඇගයුම / à¶·à·à·€à·’à¶­à·à·€â€ +Passes: diff --git a/Utils/7-Zip/Lang/sk.txt b/Utils/7-Zip/Lang/sk.txt new file mode 100644 index 000000000..9715a09c8 --- /dev/null +++ b/Utils/7-Zip/Lang/sk.txt @@ -0,0 +1,477 @@ +;!@Lang2@!UTF-8! +; : Tomas Tomasek +; 9.07 : Pavel DeveÄka +; 9.38 beta : 2015-01-11 : Roman Horváth +; +; +; +; +; +; +; +; +0 +7-Zip +Slovak +SlovenÄina +401 +OK +ZruÅ¡iÅ¥ + + + +&Ãno +&Nie +&ZavrieÅ¥ +Pomocník + +Po&kraÄovaÅ¥ +440 +Ãno na &vÅ¡etko +Nie na vÅ¡&etko +ZastaviÅ¥ +ReÅ¡tartovaÅ¥ +&Pozadie +P&opredie +Po&zastaviÅ¥ +Pozastavené +Ste si istý, že chcete akciu zruÅ¡iÅ¥? +500 +&Súbor +&UpraviÅ¥ +&ZobraziÅ¥ +&Obľúbené +&Nástroje +&Pomocník +540 +&OtvoriÅ¥ +O&tvoriÅ¥ vnútri +Ot&voriÅ¥ externe +&ZobraziÅ¥ +&UpraviÅ¥ +&PremenovaÅ¥ +&KopírovaÅ¥ do... +P&resunúť do... +O&dstrániÅ¥ +Ro&zdeliÅ¥ súbor... +ZlúÄ&iÅ¥ súbory... +V&lastnosti +Ko&mentár +VypoÄítaÅ¥ kontrolný súÄet +Rozdiel (Diff) +VytvoriÅ¥ prieÄinok +VytvoriÅ¥ súbor +Uko&nÄiÅ¥ +Odkaz... +600 +OznaÄiÅ¥ vÅ¡etko +OdznaÄiÅ¥ vÅ¡etko +InvertovaÅ¥ oznaÄenie +OznaÄiÅ¥... +OdznaÄiÅ¥... +OznaÄiÅ¥ podľa typu +OdznaÄiÅ¥ podľa typu +700 +&Veľké ikony +&Malé ikony +&Zoznam +&Podrobnosti +730 +NetriediÅ¥ +Plochý vzhľad +&2 Panely +P&anely nástrojov +OtvoriÅ¥ koreňový prieÄinok +O úroveň vyššie +História prieÄinkov... +&ObnoviÅ¥ +Automatické obnovenie +750 +Archív +Å tandard +Veľké ikony +Textový popis pod ikonami +800 +PridaÅ¥ prieÄinok medzi obľúbené ako +Záložka +900 +N&astavenia... +&Skúšobný test +960 +&Obsah pomocníka... +O p&rograme 7-Zip +1003 +Cesta +Meno +Prípona +PrieÄinok +VeľkosÅ¥ +VeľkosÅ¥ po kompresii +Atribúty +Vytvorený +Sprístupnený +Zmenený +Jednoliaty +Komentovaný +ZaÅ¡ifrovaný +Rozdelený predtým +Rozdelený potom +Slovník +CRC +Typ +Anti +Metóda +Hostiteľský OS +Súborový systém +Používateľ +Skupina +Blok +Komentár +Pozícia +Predpona cesty +PrieÄinky +Súbory +Verzia +Zväzok +Multizväzok +Ofset +Odkazy +Bloky +Zväzky + +64-bitov +Big-endian +CPU +Fyzická veľkosÅ¥ +VeľkosÅ¥ hlaviÄiek +Kontrolný súÄet +Charakteristiky +Virtuálna adresa +ID +Skrátený názov +Aplikácia +VeľkosÅ¥ sektora +Režim +Symbolický odkaz +Chyba +Celková veľkosÅ¥ +Voľné miesto +VeľkosÅ¥ klastra +Menovka +Lokálny názov +Prevádzkovateľ +BezpeÄnosÅ¥ NT +Alternatívny prúd +Aux +Vymazané +Je strom + + +Typ chyby +Chyby +Chyby +Varovania +Varovanie +Prúdy +Alternatívne prúdy +VeľkosÅ¥ alternatívneho prúdu +Virtuálna veľkosÅ¥ +VeľkosÅ¥ po rozbalení +Celková fyzická veľkosÅ¥ +Index zväzku +Podtyp +Krátky komentár +Kódová stránka + + + +VeľkosÅ¥ zakonÄenia +VeľkosÅ¥ vloženého ústrižku +Odkaz +Pevný odkaz +iNode +2100 +Nastavenia +Jazyk +Jazyk: +Editor +Editor: +VyhľadávaÄ rozdielov (Diff): +2200 +Systém +AsociovaÅ¥ 7-Zip s vybranými typmi súborov: +2301 +IntegrovaÅ¥ 7-Zip do kontextovej ponuky +Kaskádová kontextová ponuka +Položky kontextovej ponuky: +Ikony v kontextovej ponuke +2320 + + +OtvoriÅ¥ archív +RozbaliÅ¥ súbory... +PridaÅ¥ do archívu... +OtestovaÅ¥ archív +RozbaliÅ¥ tu +RozbaliÅ¥ do {0} +PridaÅ¥ do {0} +SkomprimovaÅ¥ a poslaÅ¥ emailom... +SkomprimovaÅ¥ do {0} a poslaÅ¥ emailom +2400 +PrieÄinky +&Pracovný prieÄinok +&Systémový prieÄinok pre doÄasné súbory +&Aktuálny prieÄinok +&Manuálne vybraný prieÄinok: +Po&užiÅ¥ len pre vymeniteľné jednotky +Vyberte prieÄinok pre doÄasné súbory. +2500 +Nastavenia +UkázaÅ¥ položku ".." +UkázaÅ¥ skutoÄné ikony súborov +UkázaÅ¥ systémovú ponuku +OznaÄiÅ¥ celý riadok +ZobraziÅ¥ Äiary mriežky +OtvoriÅ¥ položku jednoduchým kliknutím +Alternatívny režim výberu +Použitie veľkých stránok pamäti +2900 +O programe 7-Zip +7-Zip je voľne šíriteľný softvér +3000 +Systém nemôže alokovaÅ¥ požadované množstvo pamäte +V archíve sa nenaÅ¡li žiadne chyby +{0} vybraných objektov +Nie je možné vytvoriÅ¥ prieÄinok '{0}'. +Operácie aktualizácie nie sú pre tento archív podporované. +Súbor '{0}' nie je možné otvoriÅ¥ ako archív +Nie je možné otvoriÅ¥ Å¡ifrovaný archív '{0}'. Nesprávne heslo? +Nepodporovaný typ archívu +Súbor {0} už existuje. +Súbor '{0}' bol zmenený.\nChcete ho aktualizovaÅ¥ v archíve? +Nie je možné aktualizovaÅ¥ súbor\n'{0}' +Nie je možné spustiÅ¥ editor. +Súbor vyzerá ako vírus. (Meno súboru obsahuje veľa medzier.) +Operácia nemôže byÅ¥ vykonaná v prieÄinku, ktorý má dlhú cestu. +Musíte vybraÅ¥ jeden súbor +Musíte vybraÅ¥ jeden alebo viac súborov +Priveľa položiek +3300 +Rozbaľovanie +Komprimovanie +Výsledok testovania +Otváranie... +Prehľadávanie... +3400 +RozbaliÅ¥ +RozbaliÅ¥ do: +Å pecifikujte prieÄinok pre rozbalené súbory. +3410 +Nastavenie názvov ciest: +Plné názvy ciest +Žiadne názvy ciest +Absolútne názvy ciest +Relatívne názvy ciest +3420 +Nastavenie prepísania: +SpýtaÅ¥ sa pred prepísaním +PrepísaÅ¥ bez výzvy +PreskoÄiÅ¥ existujúce súbory +Automaticky premenovaÅ¥ +Automaticky premenovaÅ¥ existujúce súbory +3430 +EliminovaÅ¥ duplikáty koreňového prieÄinka +ObnoviÅ¥ práva súboru +3500 +Potvrdenie nahradenia súboru +Cieľový prieÄinok už obsahuje rozbaľovaný súbor. +Chcete nahradiÅ¥ existujúci súbor +týmto súborom? +{0} bajtov +&Automaticky premenovaÅ¥ +3700 +Nepodporovaná kompresná metóda pre '{0}'. +Chybné údaje v '{0}'. Súbor je poÅ¡kodený. +CRC zlyhalo v '{0}'. Súbor je poÅ¡kodený. +Chybné údaje v Å¡ifrovanom súbore '{0}'. Nesprávne heslo? +CRC zlyhalo v Å¡ifrovanom súbore '{0}'. Nesprávne heslo? +3710 +Nesprávne heslo? +3721 +Nepodporovaná kompresná metóda +Chybné údaje +CRC zlyhalo +Údaje sú nedostupné +NeoÄakávaný koniec údajov +Za zužitkovateľnými údajmi sa nachádzajú ÄalÅ¡ie údaje +Nie je to archív +Chybné hlaviÄky +3763 +ZaÄiatok archívu je nedostupný +ZaÄiatok archívu nie je potvrdený + + + +Nepodporovaná funkcia +3800 +Zadajte heslo +Zadajte heslo: +Zopakujte heslo: +UkázaÅ¥ heslo +Heslá nie sú zhodné +Používajte len písmená bez diakritiky, Äísla a Å¡peciálne znaky (!, #, $, ...) +Heslo je príliÅ¡ dlhé +Heslo +3900 +Uplynutý Äas: +Zostávajúci Äas: +VeľkosÅ¥: +RýchlosÅ¥: +Spracované: +Úroveň kompresie: +Chyby: +Archívy: +4000 +PridaÅ¥ do archívu +&Archív: +&Nastavenie aktualizácie súborov v existujúcich archívoch: +&Formát archívu: +Ú&roveň kompresie: +&Kompresná metóda: +&VeľkosÅ¥ slovníka: +V&eľkosÅ¥ slova: +Veľk&osÅ¥ jednoliateho bloku: +PoÄe&t CPU vlákien: +&Parametre: +Možnosti +VytvoriÅ¥ samorozbaľovací archív +KomprimovaÅ¥ zdieľané súbory +Å ifrovanie +Metóda Å¡ifrovania: +ZaÅ¡ifrovaÅ¥ mená súborov +Pamäť potrebná na kompresiu: +Pamäť potrebná na dekompresiu: +VymazaÅ¥ súbory po kompresii +4040 +UložiÅ¥ symbolické odkazy +UložiÅ¥ pevné odkazy +UložiÅ¥ alternatívne údajové prúdy +UložiÅ¥ práva súborov +4050 +Bez kompresie +NajrýchlejÅ¡ia +Rýchla +Normálna +Maximálna +Ultra +4060 +PridaÅ¥ a nahradiÅ¥ súbory +AktualizovaÅ¥ a pridaÅ¥ súbory +AktualizovaÅ¥ existujúce súbory +SynchronizovaÅ¥ súbory +4070 +PrehľadávaÅ¥ +VÅ¡etky súbory +Nejednoliaty +Jednoliaty +6000 +KopírovaÅ¥ +Presunúť +KopírovaÅ¥ do: +Presunúť do: +Kopírovanie... +Presúvanie... +Premenovanie... +Vyberte cieľový prieÄinok. +Operácia nie je podporovaná pre tento prieÄinok. +Chyba pri premenovaní súboru alebo prieÄinka +PotvrdiÅ¥ kopírovanie súboru/súborov +Ste si istý, že chcete kopírovaÅ¥ súbor/súbory do archívu +6100 +Potvrdenie odstránenia súboru +Potvrdenie odstránenia prieÄinka +Potvrdenie odstránenia viacerých položiek +Ste si istý, že chcete odstrániÅ¥ súbor '{0}'? +Ste si istý, že chcete odstrániÅ¥ prieÄinok '{0}' a celý jeho obsah? +Ste si istý, že chcete odstrániÅ¥ týchto {0} položiek? +Odstraňovanie... +Chyba pri odstraňovaní súboru alebo prieÄinka +Systém nemôže presunúť do koÅ¡a súbor s dlhou cestou +6300 +VytvoriÅ¥ prieÄinok +VytvoriÅ¥ súbor +Meno prieÄinka: +Meno súboru: +Nový prieÄinok +Nový súbor +Chyba pri vytváraní prieÄinka +Chyba pri vytváraní súboru +6400 +Komentár +&Komentár: +OznaÄiÅ¥ +OdznaÄiÅ¥ +Maska: +6600 +Vlastnosti +História prieÄinkov +Diagnostické správy +Správa +7100 +PoÄítaÄ +SieÅ¥ +Dokumenty +Systém +7200 +PridaÅ¥ +RozbaliÅ¥ +OtestovaÅ¥ +KopírovaÅ¥ +Presunúť +OdstrániÅ¥ +Informácie +7300 +RozdeliÅ¥ súbor +&RozdeliÅ¥ na: +Roz&deliÅ¥ na zväzky bajtov: +Rozdeľovanie... +PotvrdiÅ¥ rozdeľovanie. +Ste si istý, že chcete rozdeliÅ¥ súbor na {0} zväzkov? +VeľkosÅ¥ zväzku musí byÅ¥ menÅ¡ia než veľkosÅ¥ pôvodného súboru. +Nesprávna veľkosÅ¥ zväzku +UrÄená veľkosÅ¥ zväzku: {0} bajtov.\nSte si istý, že chcete rozdeliÅ¥ archív na také zväzky? +7400 +ZlúÄiÅ¥ súbory +&ZlúÄiÅ¥ do: +ZluÄovanie... +Vyberte len prvý súbor. +Nie je možné rozlíšiÅ¥ súbor ako súÄasÅ¥ rozdeleného súboru +Nie je možné nájsÅ¥ viac než jednu ÄasÅ¥ rozdeleného súboru +7500 +VýpoÄet kontrolného súÄtu... +Informácie o kontrolnom súÄte +CRC súÄet pre údaje: +CRC súÄet pre údaje a názvy: +7600 +Skúšobný test +Použitá pamäť: +Komprimovanie +Rozbaľovanie +Hodnotenie +Celkové hodnotenie +Aktuálne +Výsledné +Využitie CPU +Hodn. / Využitie +Testov bez chýb: +7700 +Odkaz +Odkaz +Odkaz z: +Odkaz do: +7710 +Typ odkazu +Pevný odkaz +Súbor symbolického odkazu +PrieÄinok symbolického odkazu +Prekríženie prieÄinkov diff --git a/Utils/7-Zip/Lang/sl.txt b/Utils/7-Zip/Lang/sl.txt new file mode 100644 index 000000000..2c3214a86 --- /dev/null +++ b/Utils/7-Zip/Lang/sl.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; : tomazek +; 4.55 : miles +; +; +; +; +; +; +; +; +; +0 +7-Zip +Slovenian +Slovenski +401 +V redu +PrekliÄi + + + +&Da +&Ne +&Zapri +PomoÄ + +&Nadaljuj +440 +Da za &vse +Ne za v&se +Zaustavi +Ponovno zaženi +&Ozadje +O&spredje +&ZaÄasno zaustavi +Zaustavljen +Ste prepriÄani, da želite preklicati? +500 +&Datoteka +&Urejanje +&Pogled +Pr&iljubljene +O&rodja +Po&moÄ +540 +&Odpri +Odpri &znotraj +Odpri zu&naj +P&rikaz +&Urejanje +Prei&menuj +&Kopiraj ... +&Premakni ... +Iz&briÅ¡i +&Razdeli datoteko ... +&Združi datoteke ... +L&astnosti +Opomb&e +IzraÄunaj preskusno vsoto + +Ustvari mapo +Ustvari datoteko +&Izhod +600 +Izberi &vse +Razveljavi izbiro vseh +&Preobrni izbor +Izberi ... +Razveljavi izbiro ... +Izberi glede na vrsto +Razveljavi izbiro glede na vrsto +700 +&Velike ikone +&Majhne ikone +&Seznam +&Podrobnosti +730 +NerazvrÅ¡Äeno +SploÅ¡en pogled +&Dve podokni +&Orodne vrstice +Odpri korensko mapo +Nadrejena raven +Zgodovina mape ... +&Osveži +750 +Orodna vrstica arhiva +Navadna orodna vrstica +Veliki gumbi +Pokaži besedilo gumbov +800 +&Dodaj mapo med Priljubljene kot +Zaznamek +900 +&Možnosti ... +Me&ritev +960 +&Vsebina pomoÄi ... +&O programu 7-Zip ... +1003 +Pot +Ime +Pripona +Mapa +Velikost +Stisnjena velikost +Atributi +Izdelano +Uporabljeno +Spremenjeno +Trdno +Komentirano +Å ifrirano +Razdeli pred +Razdeli po +Slovar +CRC +Vrsta +Anti +Metoda +Gostiteljski OS +DatoteÄni sistem +Uporabnik +Skupina +Blok +Opomba +Položaj +Predpona poti +Mape +Datoteke +RazliÄica +Nosilec +VeÄ nosilcev +Zamik +Povezave +Bloki +Nosilcev + + + + + + + + + + + + + + + +Napaka +Skupna velikost +Prostega prostora +Velikost gruÄe +Oznaka +Krajevno ime +Ponudnik +2100 +Možnosti +Jezik +Jezik: +Urejevalnik +&Urejevalnik: + +2200 +Sistem +Poveži 7-Zip z: +2301 +Integracija 7-Zip v kontekstni meni lupine +Kaskadni kontekstni meni +Izbire kontekstnega menija: +2320 + + +Odpri arhiv +RazÅ¡iri datoteke ... +Stisni v arhiv ... +Preizkusi arhiv +RazÅ¡iri semkaj +RazÅ¡iri v {0} +Dodaj v {0} +Stisni in poÅ¡lji ... +Stisni v {0} in poÅ¡lji po e-poÅ¡ti +2400 +Mape +&Delovna mapa +&Sistemska zaÄasna mapa +&Trenutna mapa +&Navedeno: +Uporabi le za izmenljive pogone +Navedite mesto za zaÄasne arhivske datoteke. +2500 +Nastavitve +Pokaži postavko ".." +Pokaži prave ikone datotek +Pokaži sistemski meni +&Izbor celotne vrstice +Pokaži &mrežne Ärte + +&Alternativni naÄin izbiranja +Uporabi &velike spominske strani +2900 +O programu 7-Zip +7-Zip je brezplaÄen. Njegov razvoj podprete s tem, da se registrirate. +3000 + +Brez napak +Izbran(ih) {0} objekt(ov) +Mape '{0}' ni mogoÄe ustvariti +Za ta arhiv operacije osveževanja niso podprte. +Datoteke '{0}' ni mogoÄe odpreti kot arhiv +Å ifriranega arhiva '{0}' ni mogoÄe odpreti. Je morda geslo napaÄno? + + +Datoteka '{0}' je bila spremenjena.\nJo želite osvežiti v arhivu? +Datoteke ni mogoÄe osvežiti\n'{0}' +Urejevalnika ni mogoÄe pognati. + + + + +PreveÄ izbir +3300 +RazÅ¡irjanje +Stiskanje +PreizkuÅ¡anje +Odpiranje ... +Pregledovanje ... +3400 +RazÅ¡iri +R&azÅ¡iri v: +DoloÄite mesto razÅ¡irjanja datotek. +3410 +Poti +Polne poti +Brez poti +3420 +Prepisovalni naÄin +Zahtevaj potrditev +PrepiÅ¡i brez potrditve +PreskoÄi obstojeÄe datoteke +Samodejno preimenuj +Samodejno preimenuj obstojeÄe datoteke +3500 +Potrditev zamenjave datoteke +Ciljna mapa že vsebuje obdelovano datoteko. +Želite zamenjati obstojeÄo datoteko +s to datoteko? +{0} bajtov +Samodejno &preimenuj +3700 +Nepodprta metoda stiskanja za '{0}'. +Podatkovna napaka v '{0}'. Datoteka je poÅ¡kodovana. +Napaka CRC v '{0}'. Datoteka je poÅ¡kodovana. +Podatkovna napaka v Å¡ifrirani datoteki '{0}'. Je geslo pravilno? +Napaka CRC v Å¡ifrirani datoteki '{0}'. Je geslo pravilno? +3800 +Vnos gesla +Vnesite geslo: +Ponovno vnesite geslo: +&Pokaži geslo +Gesli se ne ujemata +Za geslo uporabite le Ärke, Å¡tevilke in posebne znake angleÅ¡ke abecede (!, #, $, ...) +Geslo je predolgo +Geslo +3900 +PreteÄeni Äas: +Preostali Äas: +Velikost: +Hitrost: +Obdelano: +Razmerje stiskanja: +Napake: +Arhivi: +4000 +Dodaj v arhiv +&Arhiv: +&NaÄin osveževanja: +&Vrsta arhiva: +Raven &stiskanja: +&Metoda stiskanja: +&Velikost slovarja: +Velikost &besede: +Velikost trdnega bloka: +Å tevilo niti CPE: +&Parametri: +Možnosti +Izdelaj arhiv SF&X +Stisni skupne datoteke +Å ifriranje +Metoda Å¡ifriranja: +Å ifriraj &imena datotek +Poraba pomnilnika za stiskanje: +Poraba pomnilnika za razÅ¡irjanje: +4050 +Brez stiskanja +NajhitrejÅ¡e +Hitro +ObiÄajno +NajveÄje stiskanje +Ultra +4060 +Dodaj in zamenjaj datoteke +Osveži in dodaj datoteke +Osveži obstojeÄe datoteke +Sinhroniziraj datoteke +4070 +Prebrskaj +Vse datoteke +Ne-trdno +Trdno +6000 +Kopiraj +Premakni +Kopiraj v: +Premakni v: +Kopiranje ... +Premikanje ... +Preimenovanje ... +Izberite ciljno mapo. +Operacija ni podprta. +Napaka pri preimenovanju datoteke ali mape +Potrditev kopiranja datoteke +Ste prepriÄani, da želite kopirati datoteke v arhiv? +6100 +Potrditev brisanja datoteke +Potrditev brisanja mape +Potrditev brisanja veÄ datotek +Ste prepriÄani, da želite zbrisati '{0}'? +Ste prepriÄani, da želite zbrisati mapo '{0}' in celotno njeno vsebino? +Ste prepriÄani, da želite zbrisati teh {0} postavk? +Brisanje ... +Napaka pri brisanju datoteke ali mape + +6300 +Izdelava mape +Ustvarjanje datoteke +Ime mape: +Ime datoteke: +Nova mapa +Nova datoteka +Napaka pri ustvarjanju mape +Napaka pri ustvarjanju datoteke +6400 +Opomba +&Opomba: +Izberi +Razveljavi izbiro +Maska: +6600 +Properties +Zgodovina map +DiagnostiÄna sporoÄila +SporoÄilo +7100 +RaÄunalnik +Omrežje + +Sistem +7200 +Dodaj +RazÅ¡iri +Preizkusi +Kopiraj +Premakni +IzbriÅ¡i +Informacije +7300 +Razdeli datoteko +&Razdeli na: +Razdeli na &nosilce velikosti (v bajtih): +Razdeljevanje ... +Potrditev razdelitve +Ste prepriÄani, da želite razdeliti datoteko na {0} nosilcev? +Velikost nosilca mora biti manjÅ¡a kot velikost izvorne datoteke +Neveljavna velikost nosilca +Navedena velikost nosilca: {0} bajtov.\nSte prepriÄani, da želite razdeliti arhiv na takÅ¡ne nosilce? +7400 +Združi datoteke +&Združi v: +Združevanje ... +Izberite samo prvo datoteko + + +7500 +IzraÄun preskusne vsote ... +Podatki o preskusni vsoti +Preskusna vsota za podatke: +Preskusna vsota za podatke in imena: +7600 +Meritev +Poraba pomnilnika: +Stiskanje +RazÅ¡irjanje +Ocena +Skupna ocena +Trenutno +KonÄno +Uporaba CPE +Ocena / uporaba +Prehodi: diff --git a/Utils/7-Zip/Lang/sq.txt b/Utils/7-Zip/Lang/sq.txt new file mode 100644 index 000000000..024a2b8f5 --- /dev/null +++ b/Utils/7-Zip/Lang/sq.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.37 : Mikel Hasko +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Albanian +Shqip +401 +Në rregull +Anulim + + + +&Po +&Jo +&Mbyll +Ndihmë + +&Vazhdim +440 +Po për të gjith&a +Jo për të gjit&ha +Ndalo +Rinis +Në &sfond +Në pla&n të parë +&Pushim +Në pushim +Jeni të sigurt se dëshironi ta anuloni? +500 +&Skedari +&Redaktimi +&Pamja +&Të parapëlqyerit +&Veglat +&Ndihma +540 +&Hap +Hap përbre&nda +Hap përjas&hta +&Pamja +&Redakto +Ri&emërto +&Kopjo tek... +&Zhvendos tek... +&Fshi +N&daj skedarin... +Kom&bino skedarët... +&Vetitë +Ko&menti +Llogarit shumën e verifikimit + +Krijo një dosje +Krijo një skedar +&Dil +600 +S&elekto të gjithë +Çse&lekto të gjithë +Anasill selekti&min +Selekto... +Çselekto... +Selekto sipas tipit +Çselekto sipas tipit +700 +Ikona të &mëdha +Ikona të &vogla +&Listë +&Detaje +730 +&Të parenditur +Pamje e rrafshtë +&2 panele +&Shiritat e veglave +Hap dosjen rrënjë +Një nivel më lartë +Historiku i dosjes... +&Rifresko +750 +Shiriti i veglave i arkivit +Shiriti standard i veglave +Butona të mëdhenj +Shfaq tekstin e butonave +800 +&Shto dosjen tek të parapëlqyerit si +Shënuesi +900 +&Opsionet... +&Etapa +960 +&Përmbajtjet... +&Për 7-Zip... +1003 +Shtegu +Emri +Prapashtesa +Dosja +Madhësia +Madhësia e mbërthimit +Atributet +Krijuar më +Hyrë së fundi më +Modifikuar më +I qëndrueshëm +I komentuar +I shifruar +Ndaj para +Ndaj pas +Fjalori +CRC +Tipi +Anti +Metoda +Pronari i sistemit +Sistemi i skedarit +Përdoruesi +Grupi +Blloku +Komenti +Pozicioni +Prefiksi i shtegut + + + + + + + + + + + + + + + + + + + + + + + + +Gabim +Madhësia totale +Hapësira e lirë +Madhësia e cluster-it +Etiketa +Emri lokal +Kujdestari +2100 +Opsionet +Gjuha +Gjuha: +Redaktuesi +&Redaktuesi: + +2200 +Sistemi +Lidh 7-Zip me: +2301 +Integro 7-Zip në kontekst meny +Kaskado kontekst menynë +Elementë të kontekst menysë: +2320 + + +Hap arkivin +Zbërthe skedarët... +Shto në arkiv... +Testo arkivin +Zbërthe këtu +Zbërthe në {0} +Shto tek {0} +Kompreso dhe dërgo me e-mail... +Kompreso në {0} dhe dërgo me e-mail +2400 +Dosje +&Dosja e punës +Dosja e përkohshme e &sistemit +Dosja &aktuale +I sp&ecifikuar: +Përdor vetëm për njësi të largueshme +Specifikoni një vendndodhje për skedarët e përkohshëm të arkivit. +2500 +Rregullimet +Shfaq &artikullin ".." +Shfaq &ikonat e vërteta të skedarëve +Shfaq &menynë e sistemit +Selekto të tërë &rreshtin +Shfaq &vijat e rrjetit + +Mënyrë alternative &selektimi +Përdor &faqe të mëdha të memories +2900 +Për 7-Zip +7-Zip është softuer falas. Megjithatë, ju mund të përkrahni në zhvillimin e 7-Zip duke e regjistruar atë. +3000 + +S'ka gabime +Selektuar {0} objekt(e) +S'mund të krijojë dosjen '{0}' +Operacionet e azhurnimit për këtë arkiv s'përkrahen. + + + + +Skedari '{0}' u modifikua.\nDoni ta azhurnoni atë edhe në arkiv? +S'mund të azhurnojë skedarin\n'{0}' +S'mund të hap redaktuesin. + + + + +Tepër shumë artikuj +3300 +Duke zbërthyer +Duke kompresuar +Duke testuar +Duke hapur... +Duke skanuar... +3400 +Zbërthe +Z&bërthe në: +Specifikoni një vendndodhje për skedarët e zbërthyer. +3410 +Mënyra e Shtegut +Emra të plotë Shtigjesh +Pa emra shtigjesh +3420 +Mënyra e mbishkrimit +Pyet para se të mbishkruaj +Mbishkruaj pa nxitje +Mbikalo skedarët ekzistues +Riemërto automatikisht +Riemërto auto. skedarët ekzistues +3500 +Konfirmo zëvendësimin e skedarëve +Dosja e destinacionit e përmban një herë skedarin e përpunuar. +Dëshironi ta zëvendësoni skedarin ekzistues +me këtë? +{0} bajt +R&iemërtim automatik +3700 +Metodë e papërkrahshme kompresimi për '{0}'. +Gabim të dhënash në '{0}'. Skedari është i prishur. +CRC dështoi '{0}'. Skedari është i prishur. + + +3800 +Fusni fjalëkalimin +Fusni fjalëkalimin: + +&Trego fjalëkalimin + + + +Fjalëkalimi +3900 +Koha e kaluar: +Koha e mbetur: +Madhësia: +Shpejtësia: + + +Gabime: + +4000 +Shto në arkiv +&Arkivi: +&Mënyra e azhurnimit: +&Formati i arkivit: +&Niveli i kompresimit: +Metoda e kompr&esimit: +Ma&dhësia e fjalorit: +Mad&hësia e fjalës: + + +&Parametrat: +Opsionet +Krijo një arkiv SF&X + + + +Shifro em&rat e skedarëve +Shfrytëzimi i memo. për kompresimin: +Shfrytëzimi i memo. për dekompresimin: +4050 +Ruaj +Më i shpejtë +I shpejtë +Normal +Maksimal +Ultra +4060 +Shto dhe zëvendëso skedarët +Azhurno dhe shto skedarët +Azhurno skedarët ekzistues +Sinkronizo skedarët +4070 +Shfleto +Të gjithë skedarët + + +6000 +Kopjo +Zhvendos +Kopjo tek: +Zhvendos tek: +Duke kopjuar... +Duke zhvendosur... +Duke riemërtuar... + +Operacioni s'përkrahet. +Gabim gjatë riemërtimit të skedarit apo dosjes +Konfirmim për kopjimin e skedarëve +Jeni të sigurt që doni të kopjoni skedarë në arkiv +6100 +Konfirmo fshirjen e skedarit +Konfirmo fshirjen e dosjes +Konfirmo fshirjen e shumëfishtë të skedarëve +Jeni të sigurt që doni të fshini '{0}'? +Jeni të sigurt që doni të fshini dosjen '{0}' dhe tërë përmbajtjen e saj? +Jeni të sigurt që doni t'i fshini këto {0} artikuj? +Duke fshirë... +Gabim gjatë fshirjes së skedarit apo dosjes + +6300 +Krijo një dosje +Krijo një skedar +Emri i dosjes: +Emri i skedarit: +Dosje e re +Skedar i ri +Gabim gjatë krijimit të dosjes +Gabim gjatë krijimit të skedarit +6400 +Komenti +&Komenti: +Selekto +Çselekto +Maska: +6600 + +Historiku i dosjes +Mesazhe diagnostikues +Mesazhi +7100 +Kompjuteri +Rrejti + +Sistemi +7200 +Shto +Zbërthe +Testo +Kopjo +Zhvendos +Fshi +Info +7300 +Ndaj skedarin +&Ndaj në: +Ndaj në &volume (madhësia jepet në bajt): +Duke ndarë... + + + + + +7400 +Kombino skedarët +&Kombino në: +Duke kombinuar... + + + +7500 +Duke llogaritur shumën e verifikimit... +Informacionet e shumës së verifikimit +Shuma e verifikimit CRC për të dhënat: +Shuma e verifikimit CRC për të dhënat dhe emrat: +7600 +Etapa +Shfrytëzimi i memories: +Kompresimi +Dekompresimi +Vlerësimi +Vlerësimi total +Aktualisht +Rezultati + + +Kalime: diff --git a/Utils/7-Zip/Lang/sr-spc.txt b/Utils/7-Zip/Lang/sr-spc.txt new file mode 100644 index 000000000..098317335 --- /dev/null +++ b/Utils/7-Zip/Lang/sr-spc.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; : Lazar +; 9.07 : Ozzii +; +; +; +; +; +; +; +; +; +0 +7-Zip +Serbian - Cyrillic +СрпÑки - ћирилица +401 +У реду +Откажи + + + +Да +Ðе +Затвори +Помоћ + +ÐаÑтави +440 +Да за Ñве +Ðе за Ñве +Стани +Поново +Позадина +Ðа врху +Пауза +Пауза +Да ли Ñте Ñигурни да желите да прекинете? +500 +Датотека +Уређивање +Преглед +Омиљено +Ðлати +Помоћ +540 +Погледај +Отвори Ñа 7-Zip-ом +Отвори Ñа придруженом програмом +Прегледај +Промени +Преименуј +Копирај у... +ПремеÑти у... +Обриши +Подели фајл... +Спој делове... +СвојÑтва +Коментар +Израчунајте проверну величину +разлика +Ðова фаÑцикла +Ðова датотека +Излаз +600 +Изабери Ñве +Поништи избор Ñвега +Обрнути избор +Изабери... +Поништи избор... +Изабери по типу +Поништи избор по типу +700 +Иконе +Ðапоредно Ñлагање +СпиÑак +Детаљи +730 +Без Ñортирања +Раван преглед +2 Прозора +Траке Ñа алаткама +Отвори почетну фаÑциклу +Горе за један ниво +Хронологија... +ОÑвежавање +750 +Рад Ñа архивама +Рад Ñа датотекама +Велика дугмад +Прикажи текÑÑ‚ иÑпод дугмади +800 +Додај +Изабери +900 +Опције... +Benchmark +960 +Помоћ... +О програму... +1003 +Путања +Ðазив +Тип +ФаÑцикла +Величина +Величина у запакованом Ñтању +Ðтрибути +Креирана +ПриÑтупано +Промењено +Солидно +Коментар +Шифровано +Подели пре +Подели поÑле +Речник +ЦРЦ +Тип +Ðнти +Метод +Оперативни ÑиÑтем +СиÑтем датотека +КориÑник +Група +Блок +Коментар +Положај +ÐŸÑ€ÐµÑ„Ð¸ÐºÑ Ð¿ÑƒÑ‚Ð°ÑšÐµ +ФаÑцикле +Датотеке +Верзија +Волумен +ВишеВолумена +ОфÑет +Линкови +Блокови +Волумени + +64-бит +Big-endian +ПроцеÑор +Физичка величина +Величина заглавља +Провера резултата +КарактериÑтике +Виртуелна адреÑа +ИД +Кратко име +Креатор апликације +Величина Ñектора +Ðачин +Линк +Грешка +Укупни капацитет +Слободни проÑтор +Величинa cluster-а +Ðазив +Локално име +Провајдер +2100 +Опције +Језик +Језик: +Промене у датотекама +Програм: +Разлика: +2200 +СиÑтем +7-Zip отвара Ñледеће типове датотека: +2301 +Убаци 7-Zip у ÑиÑтемÑки мени +КаÑкадни ÑиÑтемÑки мени +Ставке ÑиÑтемÑког менија: +2320 + + +Отвори архиву +Издвој датотеке... +Додај у архиву... +ТеÑтирај архиву +Издвој овде +Издвој у {0} +Додај у {0} +Додај и направи e-mail... +Додај у {0} и направи e-mail +2400 +ФаÑцикле +Радна фаÑцикла +КориÑти Windows-ову привремену фаÑциклу +Тренутна +Ðаведена: +КориÑти Ñамо за измењиве медије +Ðаведите локацију за Ñмештање привремених датотека. +2500 +Подешавања +Прикажи ".." +Прикажи праве Ñличице датотека +Прикажи ÑиÑтемÑки мени +Обележи цео ред +Прикажи линије мреже +Један клик за отварање Ñтавке +Ðлтернативни начин за бирање +КориÑти велике меморијÑке блокове +2900 +О програму +7-Zip је беÑплатан програм. +3000 +СиÑтем не може да издвоји потребну количину меморије +Ðије било никаквих грешака +{0} објекат(а) изабрано +Ðе могу да креирам ФаÑциклу '{0}' +Операција оÑвежавања није дозвољена за ову архиву. +Ðе могу да отворим датотеку '(0)' као архива +Ðе могу да отворим шифровану архиву '(0)'. Погрешна лозинка? +Ðеподржан тип архиве +Датотека {0} већ поÑтоји +Датотека '{0}' је измјењена.\nДа ли желите ажурирати архиву? +Ðије могуће ажурирати датотеку\n'{0}' +Ðије могуће започети уређивање. +Датотека изгледа као Ð²Ð¸Ñ€ÑƒÑ (име датотеке Ñадржи пуно размака у имену). +Операција не може бити позвана из фаÑцикле која има дугу путању. +Морате да изаберете неку датотеку +Морате да изаберете једну или више датотека +ИÑувише Ñтавки +3300 +Издвајање +Додајем +ТеÑтирање +Отварање у току... +Скенирање... +3400 +Издвој +Издвој у: +Ðаведи локацију где ће Ñе издвајати датотеке из архива. +3410 +Путање +Пуна путања +Без путање +3420 +Замена +Питај пре него што замениш +Замени без запиткивања +ПреÑкочи поÑтојеће датотеке +ÐутоматÑка промена назива +ÐутоматÑка промена назива поÑтојећих датотека +3500 +Потврди замену датотеке +Циљна фаÑцикла већ Ñадржи датотеку која Ñе тренутно обрађује. +Да ли желите да замените поÑтојећу датотеку +Ñа овом? +{0} бајтова +ÐутоматÑка промена назива +3700 +Ðеподржани метод компреÑије за '{0}'. +Грешка у '{0}'. Датотека је неиÑправана. +CRC грешка у '{0}'. Датотека је неиÑправана. +Грешке у кодирану датотеку '(0)' Погрешна лозинка. +ЦРЦ грешка у шифроване датотеке '(0)' Погрешна лозинка. +3800 +УнеÑите лозинку +УнеÑите лозинку: +Поново унеÑите лозинку: +Прикажи лозинку +Лозинке Ñе не поклапају +КориÑти Ñамо енглеÑка Ñлова, бројева и Ñпецијалних знакова (!, #, $, ...) За лозинке +Лозинка је предугачка +Шифра +3900 +Протекло време: +ПреоÑтало време: +Величина: +Брзина: +Обрађено: +КомпреÑиони одноÑ: +Грешке: +Ðрхиве: +4000 +Додај у архиву +Ðрхива: +Ðадоградња архива: +Формат архиве: +Ðиво компреÑије: +Тип компреÑије: +Dictionary size: +Word size: +Величина чврÑтог блока: +Број низа процеÑора: +Параметри: +Опције +Креирај SFX архиву +КомпреÑија дељене датотека +Шифровање +Метода шифровања: +Шифруј називе датотека +Потребна меморија - компреÑија: +Потребна меморија - декомпреÑија: +4050 +Без компреÑије +Брже +Брзо +Ðормално +МакÑимално +Ðајбрже +4060 +Додај и замени датотеке +ОÑвежи и додај датотеке +Озвежи поÑтојеће датотеке +Синхронизуј датотеке +4070 +Прегледај +Све датотеке +Ðе-чврÑте +ЧврÑто +6000 +Копирај +ИÑеци +Копирај у: +ПремеÑти у: +Копирање у току... +Премештање у току... +Преименовање у току... +Изаберите одредишну фаÑциклу. +Операција није подржана. +Грешка при преименовању датотеке или фаÑцикле +Потврди копирање датотеке +Да ли Ñте Ñигурни да желите да копирате датотеке у архиву +6100 +Потврдите бриÑање датотеке +Потврдите бриÑање фаÑцикле +Потврдите вишеÑтруко бриÑање датотека +ЈеÑте ли Ñигурни да желите да обришете '{0}'? +ЈеÑте ли Ñигурни да желите да обришете фаÑциклу '{0}' и Ñав њен Ñадржај? +ЈеÑте ли Ñигурни да желите да обришете ове {0} податке? +БриÑање у току... +Грешка при бриÑању датотеке или фаÑцикле +СиÑтем не може да премеÑти датотеку Ñа дугом путу у корпу за отпатке +6300 +Креирај фаÑциклу +Креирај датотеку +Име фаÑцикле: +Име датотеке: +Ðова фаÑцикла +Ðова датотека +Грешка при креирању фаÑцикли +Грешка при креирању датотека +6400 +Коментар +Коментар: +Одабери +Поништи избор +МаÑка: +6600 +ОÑобине +Хронологија +ДијагноÑтичке поруке +Поруке +7100 +Рачунар +Мрежа +Документи +СиÑтем +7200 +Додај +Издвој +ТеÑтирај +Копирај +ПремеÑти +Избриши +СвојÑтва +7300 +Подели датотеку +Подели на: +Подели на делове, бајти: +Подела... +Потврда поделе +Да ли Ñте Ñигурни да желите да поделите датотеку на (0) дела? +Величине дела мора бити мањи од величине оригиналне датотеке +ÐеиÑправна величина волумена +Одређена величина волумена: {0} бајтова.\nДа ли Ñте Ñигурни да желите да поделите архиву у тих волумена? +7400 +СаÑтави датотеке +СаÑтави у: +Спајање... +Изаберите Ñамо први део поделе датотеке +Ðе могу да откријем датотеку као део поделе +Ðе може Ñе наћи више од једног дела поделе +7500 +Израчунавање проверне величине... +Информација о проверне величине +ЦРЦ порвера за податак: +ЦРЦ провера за податак и имена: +7600 +Benchmark +Коришћење меморије: +КомпреÑија +ДекомпреÑија +Оцена +Потпуна оцена +Тренутно +Резултат +Употреба процеÑора +Рејтинг/Употреба +Пролази: diff --git a/Utils/7-Zip/Lang/sr-spl.txt b/Utils/7-Zip/Lang/sr-spl.txt new file mode 100644 index 000000000..cf66e8dd8 --- /dev/null +++ b/Utils/7-Zip/Lang/sr-spl.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; : Lazar +; 9.07 : Ozzii +; +; +; +; +; +; +; +; +; +0 +7-Zip +Serbian - Latin +Srpski - latinica +401 +U redu +Otkaži + + + +Da +Ne +Zatvori +Pomoć + +Nastavi +440 +Da za sve +Ne za sve +Stani +Ponovo +Pozadina +Na vrhu +Pauza +Pauza +Da li ste sigurni da želite da prekinete? +500 +Datoteka +UreÄ‘ivanje +Pregled +Omiljeno +Alati +Pomoć +540 +Pogledaj +Otvori sa 7-Zip-om +Otvori sa pridruženom programom +Pregledaj +Promeni +Preimenuj +Kopiraj u... +Premesti u... +ObriÅ¡i +Podeli fajl... +Spoj delove... +Svojstva +Komentar +IzraÄunajte provernu veliÄinu +razlika +Nova fascikla +Nova datoteka +Izlaz +600 +Izaberi sve +PoniÅ¡ti izbor svega +Obrnuti izbor +Izaberi... +PoniÅ¡ti izbor... +Izaberi po tipu +PoniÅ¡ti izbor po tipu +700 +Ikone +Naporedno slaganje +Spisak +Detalji +730 +Bez sortiranja +Ravan pregled +2 Prozora +Trake sa alatkama +Otvori poÄetnu fasciklu +Gore za jedan nivo +Hronologija... +Osvežavanje +750 +Rad sa arhivama +Rad sa datotekama +Velika dugmad +Prikaži tekst ispod dugmadi +800 +Dodaj +Izaberi +900 +Opcije... +Benchmark +960 +Pomoć... +O programu... +1003 +Putanja +Naziv +Tip +Fascikla +VeliÄina +VeliÄina u zapakovanom stanju +Atributi +Kreirana +Pristupano +Promenjeno +Solidno +Komentar +Å ifrovano +Podeli pre +Podeli posle +ReÄnik +CRC +Tip +Anti +Metod +Operativni sistem +Sistem datoteka +Korisnik +Grupa +Blok +Komentar +Položaj +Prefiks putanje +Fascikle +Datoteke +Verzija +Volumen +ViÅ¡eVolumena +Ofset +Linkovi +Blokovi +Volumeni + +64-bit +Big-endian +Procesor +FiziÄka veliÄina +VeliÄina zaglavlja +Provera rezultata +Karakteristike +Virtuelna adresa +ID +Kratko ime +Kreator aplikacije +VeliÄina sektora +NaÄin +Link +GreÅ¡ka +Ukupni kapacitet +Slobodni prostor +VeliÄina cluster-a +Naziv +Lokalno ime +Provajder +2100 +Opcije +Jezik +Jezik: +Promene u datotekama +Program: +Razlika: +2200 +Sistem +7-Zip otvara sledeće tipove datoteka: +2301 +Ubaci 7-Zip u sistemski meni +Kaskadni sistemski meni +Stavke sistemskog menija: +2320 + + +Otvori arhivu +Izdvoj datoteke... +Dodaj u arhivu... +Testiraj arhivu +Izdvoj ovde +Izdvoj u {0} +Dodaj u {0} +Dodaj i napravi e-mail... +Dodaj u {0} i napravi e-mail +2400 +Fascikle +Radna fascikla +Koristi Windows-ovu privremenu fasciklu +Trenutna +Navedena: +Koristi samo za izmenjive medije +Navedite lokaciju za smeÅ¡tanje privremenih datoteka. +2500 +PodeÅ¡avanja +Prikaži ".." +Prikaži prave sliÄice datoteka +Prikaži sistemski meni +Obeleži ceo red +Prikaži linije mreže +Jedan klik za otvaranje stavke +Alternativni naÄin za biranje +Koristi velike memorijske blokove +2900 +O programu +7-Zip je besplatan program. +3000 +Sistem ne može da izdvoji potrebnu koliÄinu memorije +Nije bilo nikakvih greÅ¡aka +{0} objekat(a) izabrano +Ne mogu da kreiram Fasciklu '{0}' +Operacija osvežavanja nije dozvoljena za ovu arhivu. +Ne mogu da otvorim datoteku '(0)' kao arhiva +Ne mogu da otvorim Å¡ifrovanu arhivu '(0)'. PogreÅ¡na lozinka? +Nepodržan tip arhive +Datoteka {0} već postoji +Datoteka '{0}' je izmjenjena.\nDa li želite ažurirati arhivu? +Nije moguće ažurirati datoteku\n'{0}' +Nije moguće zapoÄeti ureÄ‘ivanje. +Datoteka izgleda kao virus (ime datoteke sadrži puno razmaka u imenu). +Operacija ne može biti pozvana iz fascikle koja ima dugu putanju. +Morate da izaberete neku datoteku +Morate da izaberete jednu ili viÅ¡e datoteka +IsuviÅ¡e stavki +3300 +Izdvajanje +Dodajem +Testiranje +Otvaranje u toku... +Skeniranje... +3400 +Izdvoj +Izdvoj u: +Navedi lokaciju gde će se izdvajati datoteke iz arhiva. +3410 +Putanje +Puna putanja +Bez putanje +3420 +Zamena +Pitaj pre nego Å¡to zameniÅ¡ +Zameni bez zapitkivanja +PreskoÄi postojeće datoteke +Automatska promena naziva +Automatska promena naziva postojećih datoteka +3500 +Potvrdi zamenu datoteke +Ciljna fascikla već sadrži datoteku koja se trenutno obraÄ‘uje. +Da li želite da zamenite postojeću datoteku +sa ovom? +{0} bajtova +Automatska promena naziva +3700 +Nepodržani metod kompresije za '{0}'. +GreÅ¡ka u '{0}'. Datoteka je neispravana. +CRC greÅ¡ka u '{0}'. Datoteka je neispravana. +GreÅ¡ke u kodiranu datoteku '(0)' PogreÅ¡na lozinka. +CRC greÅ¡ka u Å¡ifrovane datoteke '(0)' PogreÅ¡na lozinka. +3800 +Unesite lozinku +Unesite lozinku: +Ponovo unesite lozinku: +Prikaži lozinku +Lozinke se ne poklapaju +Koristi samo engleska slova, brojeva i specijalnih znakova (!, #, $, ...) Za lozinke +Lozinka je predugaÄka +Å ifra +3900 +Proteklo vreme: +Preostalo vreme: +VeliÄina: +Brzina: +ObraÄ‘eno: +Kompresioni odnos: +GreÅ¡ke: +Arhive: +4000 +Dodaj u arhivu +Arhiva: +Nadogradnja arhiva: +Format arhive: +Nivo kompresije: +Tip kompresije: +Dictionary size: +Word size: +VeliÄina Ävrstog bloka: +Broj niza procesora: +Parametri: +Opcije +Kreiraj SFX arhivu +Kompresija deljene datoteka +Å ifrovanje +Metoda Å¡ifrovanja: +Å ifruj nazive datoteka +Potrebna memorija - kompresija: +Potrebna memorija - dekompresija: +4050 +Bez kompresije +Brže +Brzo +Normalno +Maksimalno +Najbrže +4060 +Dodaj i zameni datoteke +Osveži i dodaj datoteke +Ozveži postojeće datoteke +Sinhronizuj datoteke +4070 +Pregledaj +Sve datoteke +Ne-Ävrste +ÄŒvrsto +6000 +Kopiraj +Iseci +Kopiraj u: +Premesti u: +Kopiranje u toku... +PremeÅ¡tanje u toku... +Preimenovanje u toku... +Izaberite odrediÅ¡nu fasciklu. +Operacija nije podržana. +GreÅ¡ka pri preimenovanju datoteke ili fascikle +Potvrdi kopiranje datoteke +Da li ste sigurni da želite da kopirate datoteke u arhivu +6100 +Potvrdite brisanje datoteke +Potvrdite brisanje fascikle +Potvrdite viÅ¡estruko brisanje datoteka +Jeste li sigurni da želite da obriÅ¡ete '{0}'? +Jeste li sigurni da želite da obriÅ¡ete fasciklu '{0}' i sav njen sadržaj? +Jeste li sigurni da želite da obriÅ¡ete ove {0} podatke? +Brisanje u toku... +GreÅ¡ka pri brisanju datoteke ili fascikle +Sistem ne može da premesti datoteku sa dugom putu u korpu za otpatke +6300 +Kreiraj fasciklu +Kreiraj datoteku +Ime fascikle: +Ime datoteke: +Nova fascikla +Nova datoteka +GreÅ¡ka pri kreiranju fascikli +GreÅ¡ka pri kreiranju datoteka +6400 +Komentar +Komentar: +Odaberi +PoniÅ¡ti izbor +Maska: +6600 +Osobine +Hronologija +DijagnostiÄke poruke +Poruke +7100 +RaÄunar +Mreža +Dokumenti +Sistem +7200 +Dodaj +Izdvoj +Testiraj +Kopiraj +Premesti +IzbriÅ¡i +Svojstva +7300 +Podeli datoteku +Podeli na: +Podeli na delove, bajti: +Podela... +Potvrda podele +Da li ste sigurni da želite da podelite datoteku na (0) dela? +VeliÄine dela mora biti manji od veliÄine originalne datoteke +Neispravna veliÄina volumena +OdreÄ‘ena veliÄina volumena: {0} bajtova.\nDa li ste sigurni da želite da podelite arhivu u tih volumena? +7400 +Sastavi datoteke +Sastavi u: +Spajanje... +Izaberite samo prvi deo podele datoteke +Ne mogu da otkrijem datoteku kao deo podele +Ne može se naći viÅ¡e od jednog dela podele +7500 +IzraÄunavanje proverne veliÄine... +Informacija o proverne veliÄine +CRC porvera za podatak: +CRC provera za podatak i imena: +7600 +Benchmark +Korišćenje memorije: +Kompresija +Dekompresija +Ocena +Potpuna ocena +Trenutno +Rezultat +Upotreba procesora +Rejting/Upotreba +Prolazi: diff --git a/Utils/7-Zip/Lang/sv.txt b/Utils/7-Zip/Lang/sv.txt new file mode 100644 index 000000000..adaeeb966 --- /dev/null +++ b/Utils/7-Zip/Lang/sv.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; : Andreas M Nilsson, Christoffer Enqvist +; 4.59 : Bernhard Eriksson +; +; +; +; +; +; +; +; +; +0 +7-Zip +Swedish +Svenska +401 +OK +Avbryt + + + +&Ja +&Nej +&Stäng +Hjälp + +F&ortsätt +440 +Ja till &Alla +Nej till A&lla +Stopp +Starta om +&Bakgrunden +&Förgrunden +&Pausa +Pausad +Är du säker pÃ¥ att du vill avbryta? +500 +&Arkiv +&Redigera +&Visa +&Favoriter +Verkt&yg +&Hjälp +540 +&Öppna +Öppna &internt +Öppna &externt +&Visa +&Redigera +&Byt namn +&Kopiera till... +&Flytta till... +&Ta bort +&Dela upp fil... +&Sätt ihop filer... +E&genskaper +Komme&ntera +Beräkna checksumma + +Skapa mapp +Skapa fil +&Avsluta +600 +Markera &alla +Avmarkera alla +&Invertera markering +Markera... +Avmarkera... +Markera efter typ +Avmarkera efter typ +700 +St&ora ikoner +Sm&Ã¥ ikoner +&Lista +&Detaljerad lista +730 +Osorterade +Platt vy +&2 Paneler +&Verktygsfält +Öppna rotmappen +Upp en nivÃ¥ +Mapphistorik... +&Uppdatera +750 +Verktygsfältet Arkiv +Verktygsfältet Standard +Stora Knappar +Visa Knapptext +800 +&Lägg mappen till Favoriter som +Bokmärke +900 +&Alternativ... +&Benchmark +960 +&InnehÃ¥ll... +&Om 7-Zip... +1003 +Sökväg +Namn +Filändelse +Mapp +Storlek +Storlek komprimerad +Attribut +Skapad +Använd +Ändrad +Sammanhängande (Solid) +Kommenterad +Krypterad +Delad före +Delad efter +Ordlista +CRC +Typ +Anti +Metod +Värd OS +Filsystem +Användare +Grupp +Block +Kommentar +Position +Sökvägs prefix +Kataloger +Filer +Version +Volym +Multivolym +Offset +Länkar +Block +Volymer + +64-bitars +Big-endian +CPU +Fysisk storlek +Storlek pÃ¥ header +Checksumma +Karakteristisk +Virtuell adress + + + + + + +Fel +Total Storlek +Ledigt utrymme +Kluster storlek +Etikett +Datornamn +Nätverkstyp +2100 +Alternativ +SprÃ¥k +SprÃ¥k: +Redigerare +&Redigerare: + +2200 +System +Associera med 7-Zip: +2301 +Lägg till 7-Zip som alternativ i Utforskarens högerklicksmenyn +Placera 7-Zip som en undermeny +Objekt i högerklicksmenyn: +2320 + + +Öppna +Packa upp filer... +Lägg till arkiv... +Kontrollera arkivet +Packa upp här +Packa upp till {0} +Lägg till {0} +Komprimera och skicka som e-post... +Komprimera till {0} och skicka som e-post +2400 +Mappar +&Arbetsmapp +&Systemets temp-mapp +A&ktuell +Spe&cificerad: +Använd enbart för &flyttbara enheter +Ange plats där temporära arkivfiler ska sparas. +2500 +Inställningar +Visa ".." objektet +Visa riktiga ikoner, för filer innehÃ¥llande ikoner (el. länkade) +Visa system-menyn +Markera &hel rad +Visa &rutnät + +&Alternativt markeringsläge +Använd &stora minnessidor +2900 +Om 7-Zip +7-Zip är fri programvara. Du kan emellertid stödja den fortsatta utvecklingen av 7-Zip genom att donera. Den aktuella översättningen pÃ¥ svenska är gjord av Wermlandsdata, baserad pÃ¥ arbete utfört av Andreas M Nilsson och Christoffer Enqvist. +3000 +Systemet kan inte allokera den begärda minnesmängden +Inga fel pÃ¥träffades +{0} objekt markerade +Kan inte skapa mapp '{0}' +Uppdatering stöds inte för det här arkivet. +Kan inte öppna filen '{0}' som ett arkiv +Kan inte öppna det krypterade arkivet '{0}'. Fel lösenord? +Arkiv typen stöds ej +Filen {0} existerar redan +Filen '{0}' har blivit ändrad.\nVill du uppdatera den i arkivet? +Kan inte uppdatera filen\n'{0}' +Kan inte starta redigeraren. +Filen verkar vara ett virus (filnamnet innehÃ¥ller 'lÃ¥nga' mellanslag). +Operation kan inte utföras frÃ¥n en katalog med en sÃ¥ lÃ¥ng sökväg. +Du mÃ¥ste välja en fil +Du mÃ¥ste välja en eller flera filer +För mÃ¥nga objekt +3300 +Packar upp +Komprimerar +Kontrollerar +Öppnar... +Skannar... +3400 +Packa upp +&Packa upp till: +Ange sökväg för uppackade filer. +3410 +&Sökvägstyp +Kompletta sökvägar +Inga sökvägar +3420 +&Överskrivning +FrÃ¥ga före överskrivning +Skriv över befintliga filer +Hoppa över befintliga filer +Automatisk omdöpning +Automatisk omdöpning av befintliga filer +3500 +Bekräfta överskrivning av fil +MÃ¥lmappen innehÃ¥ller redan den behandlade filen. +Vill du skriva över den befintliga filen +med den här? +{0} bytes +A&utomatisk omdöpning +3700 +Komprimeringsmetoden stöds inte för '{0}'. +Datafel i '{0}'. Filen är korrupt. +CRC-fel i '{0}'. Filen är korrupt. +Datafel i den krypterade filen '{0}'. Fel lösenord? +CRC-fel i den krypterade filen '{0}'. Fel lösenord? +3800 +Ange lösenord +Ange lösenord: +Upprepa lösenord: +&Visa lösenord +Lösenorden stämmer inte överens. +Använd endast engelska tecken, siffror och special tecken (!, #, $, ...) till lösenord +Lösenordet är för lÃ¥ngt. +&Lösenord +3900 +Förfluten tid: +Ã…terstÃ¥ende tid: +Storlek: +Hastighet: +Bearbetat: +Komprimeringsgrad: +Fel: +Arkiv: +4000 +Lägg till arkiv +&Arkiv: +&Uppdateringsmetod: +Arkiv&format: +Komprimeringsniv&Ã¥: +&Komprimeringsmetod: +Storlek pÃ¥ &ordlista: +Storlek pÃ¥ o&rd: +Solit block storlek: +Antal trÃ¥dar: +&Parametrar: +Alternativ +Skapa sj&älvuppackande arkiv +Komprimera delade filer +Kryptering +Krypteringsmetod: +Kryptera fil&namn +Minne behövt vid komprimering: +Minne behövt vid dekomprimering: +4050 +Okomprimerat +Snabbaste +Snabb +Normal +Maximal +Ultra +4060 +Lägg till och ersätt filer +Lägg till och uppdatera befintliga filer +Uppdatera enbart befintliga filer +Synkronisera filer +4070 +Bläddra +Alla filer +Icke-solit +Solit +6000 +Kopiera +Flytta +Kopiera till: +Flytta till: +Kopierar... +Flyttar... +Döper om... +Välj mÃ¥lmapp. +Funktionen stöds inte. +Ett fel uppstod under omdöpning av fil eller mapp +Bekräfta kopiering av fil +Är du säker pÃ¥ att du vill kopiera filerna till arkivet +6100 +Bekräfta borttagning av fil +Bekräfta borttagning av mapp +Bekräfta borttagning av flera filer +Är du säker pÃ¥ att du vill ta bort '{0}'? +Är du säker pÃ¥ att du vill ta bort mappen '{0}' och allt dess innehÃ¥ll? +Är du säker pÃ¥ att du vill ta bort de här {0} objekten? +Tar bort... +Ett fel uppstod under borttagning av fil eller mapp +Systemet kan inte flytta en fil med s lng skvg till papperskorgen +6300 +Skapa mapp +Skapa fil +Mappnamn: +Filnamn: +Ny mapp +Ny fil +Fel vid skapande av mapp +Fel vid skapande av fil +6400 +Kommentar +&Kommentar: +Markera +Avmarkera +Filter: +6600 +Egenskaper +Mapphistorik +Diagnostiska meddelanden +Meddelande +7100 +Dator +Nätverk +Dokument +System +7200 +Lägg till +Packa upp +Testa +Kopiera +Flytta +Radera +Info +7300 +Dela Upp Fil +&Dela upp till: +Dela upp i &delar (volymer), bytes: +Delar upp... +Bekräfta uppdelning +Är det säkert du vill dela upp filen i {0} volymer? +Volymstorleken mÃ¥ste vara mindre än storleken pÃ¥ originalfilen. +Felaktig volymstorlek. +Specificerad volymstorlek: {0} byte.\nÄr du säker du vill dela arkivet i sÃ¥dana delar? +7400 +Sätt Ihop Filer +&Sätt ihop till: +Sätter ihop... +Markera bara första filen +Kan inte upptäcka att filen är en del av en uppdelad fil +Kan inte hitta mer än en del av en uppdelad fil +7500 +Beräknar checksumma... +Checksumma information +CRC checksumma för data: +CRC checksumma för data och namn: +7600 +Benchmark +Minnesanvändning: +Komprimering +Dekomprimering +Prestanda +Total prestanda +Aktuellt +Resultat +CPU Användning +Rate / Användning +OmgÃ¥ngar: diff --git a/Utils/7-Zip/Lang/ta.txt b/Utils/7-Zip/Lang/ta.txt new file mode 100644 index 000000000..a65c116df --- /dev/null +++ b/Utils/7-Zip/Lang/ta.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 3.13 : Ve Elanjelian : ThamiZha! team : www.thamizha.com +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Tamil +தமிழ௠+401 +சரி +இரதà¯à®¤à¯ + + + +ஆம௠+வேணà¯à®Ÿà®¾à®®à¯ +மூட௠+உதவி + +தொடரவà¯à®®à¯ +440 +அனைதà¯à®¤à®¿à®±à¯à®•à¯à®®à¯ ஆம௠+அனைதà¯à®¤à®¿à®±à¯à®•à¯à®®à¯ இலà¯à®²à¯ˆ +நிறà¯à®¤à¯à®¤à¯ +மீளà¯à®¤à¯à®µà®™à¯à®•௠+பினà¯à®ªà¯à®²à®®à¯ +à®®à¯à®©à¯à®ªà¯à®²à®®à¯ +தறà¯à®•ாலிகமாக நிறà¯à®¤à¯à®¤à¯ +தறà¯à®•ாலிக நிறà¯à®¤à¯à®¤à®²à¯ +உறà¯à®¤à®¿à®¯à®¾à®•வே இரதà¯à®¤à¯ செயà¯à®¯ விரà¯à®®à¯à®ªà¯à®•ிறீரà¯à®•ளா? +500 +கோபà¯à®ªà¯ +பதிபà¯à®ªà¯ +பாரà¯à®µà¯ˆ +விரà¯à®ªà¯à®ªà®™à¯à®•ள௠+கரà¯à®µà®¿à®•ள௠+உதவி +540 +திற +உளà¯à®³à¯‡ திற +வெளியே திற +பாரà¯à®µà¯ˆ +பதிபà¯à®ªà¯ +மாறà¯à®±à¯à®ªà¯à®ªà¯†à®¯à®°à®¿à®Ÿà¯ +இஙà¯à®•௠நகலெடà¯... +இஙà¯à®•௠நகரà¯à®¤à¯à®¤à¯... +அழி + + +தனà¯à®®à¯ˆà®•ள௠+கரà¯à®¤à¯à®¤à¯à®°à¯ˆ + + +கோபà¯à®ªà¯ˆ உரà¯à®µà®¾à®•à¯à®•௠+கோபà¯à®ªà¯ உரà¯à®µà®¾à®•à¯à®•௠+வெளியேற௠+600 +அனைதà¯à®¤à¯à®®à¯ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯ +அனைதà¯à®¤à¯à®®à¯ நீகà¯à®•௠+தெரிவை பà¯à®°à®Ÿà¯à®Ÿà¯ +தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯... +நீகà¯à®•à¯... +வகைபà¯à®ªà®Ÿà®¿ தெரிவà¯à®šà¯†à®¯à¯ +வகைபà¯à®ªà®Ÿà®¿ நீகà¯à®•௠+700 +பெரிய உரà¯à®ªà®¿à®•ள௠+சிறிய உரà¯à®ªà®¿à®•ள௠+படà¯à®Ÿà®¿à®¯à®²à¯ +தகவலà¯à®•ள௠+730 +வரிசைபà¯à®ªà®Ÿà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà®¾à®¤à®¤à¯ + +2 பலகஙà¯à®•ள௠+கரà¯à®µà®¿à®ªà¯à®ªà®Ÿà¯à®Ÿà¯ˆà®•ள௠+வேர௠அடைவைத௠திற +ஒர௠படி மேல௠+அடைவà¯à®•ளின௠வரலாறà¯... +பà¯à®¤à¯à®•à¯à®•ல௠+750 +காபà¯à®ªà®• கரà¯à®µà®¿à®ªà¯à®ªà®Ÿà¯à®Ÿà¯ˆ +பொதà¯à®µà®¾à®© கரà¯à®µà®¿à®ªà¯à®ªà®Ÿà¯à®Ÿà¯ˆ +பெரிய பொதà¯à®¤à®¾à®©à¯à®•ள௠+பொதà¯à®¤à®¾à®©à¯à®•ளின௠உரையைக௠காடà¯à®Ÿà¯ +800 +அடைவை விரà¯à®®à¯à®ªà®¿à®¯à®µà®±à¯à®±à¯à®³à¯ இபà¯à®ªà®Ÿà®¿ இணை +பà¯à®¤à¯à®¤à®•கà¯à®•à¯à®±à®¿ +900 +விரà¯à®ªà¯à®ªà®¤à¯à®¤à¯‡à®°à¯à®µà¯... +மதிபà¯à®ªà¯€à®Ÿà¯à®Ÿà¯ அளவை +960 +பொரà¯à®³à®Ÿà®•à¯à®•à®®à¯... +7-ஜிபà¯à®ªà¯ˆà®ªà¯ பறà¯à®±à®¿... +1003 +பாதை +பெயர௠+நீடà¯à®Ÿà®¿à®ªà¯à®ªà¯ +அடைவ௠+அளவ௠+கடà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿ அளவ௠+பணà¯à®ªà¯à®•à¯à®•ூற௠+உரà¯à®µà®¾à®•à¯à®•பà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +அனà¯à®•பà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +மாறà¯à®±à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +திணà¯à®®à®®à¯ +கரà¯à®¤à¯à®¤à¯à®°à¯ˆà®•à¯à®•பà¯à®ªà®Ÿà¯à®Ÿ +மறைகà¯à®•à¯à®±à®¿à®¯à¯€à®Ÿà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +à®®à¯à®©à¯ பிரி +பின௠பிரி +அகராதி +CRC +வகை +தடà¯à®ªà¯à®ªà¯ +வழி +பà¯à®°à®µà®²à®°à®¿à®©à¯ OS +கோபà¯à®ªà¯ மணà¯à®Ÿà®²à®®à¯ +பயனர௠+கà¯à®´à¯ +கடà¯à®Ÿà®®à¯ +கà¯à®±à®¿à®ªà¯à®ªà¯ + + + + + + + + + + + + + + + + + + + + + + + + + + +தவற௠+மொதà¯à®¤ அளவ௠+காளி இடம௠+கொதà¯à®¤à®£à®¿à®¯à®¿à®©à¯ அளவ௠+விலà¯à®²à¯ˆ +இடதà¯à®¤à¯à®°à®¿ பெயர௠+வழஙà¯à®•à¯à®ªà®µà®°à¯ +2100 +தேரà¯à®µà¯à®•ள௠+மொழி +மொழி: +பதிபà¯à®ªà®¾à®³à®©à¯ +பதிபà¯à®ªà®¾à®³à®©à¯: + +2200 +மணà¯à®Ÿà®²à®®à¯ +7-ஜிபà¯à®ªà¯ˆ இதனà¯à®Ÿà®©à¯ தொடரà¯à®ªà¯à®ªà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯: +2301 +7-ஜிபà¯à®ªà¯ˆ வெறà¯à®±à¯ சூழலà¯à®ªà®Ÿà¯à®Ÿà®¿à®¯à®²à¯à®Ÿà®©à¯ à®’à®°à¯à®™à¯à®•ிணை +விழà¯à®¤à¯Šà®Ÿà®°à¯à®®à¯ சூழல௠படà¯à®Ÿà®¿à®¯à®²à¯ +சூழல௠படà¯à®Ÿà®¿à®¯à®²à¯ உரà¯à®ªà¯à®ªà®Ÿà®¿à®•ளà¯: +2320 +<அடைவà¯> +<காபà¯à®ªà®•à®®à¯> +காபà¯à®ªà®•தà¯à®¤à¯ˆà®¤à¯ திற +கோபà¯à®ªà¯à®•ளை வெளிகà¯à®•ொணரà¯... +காபà¯à®ªà®•தà¯à®¤à®¿à®²à¯ இணை... +காபà¯à®ªà®•தà¯à®¤à¯ˆà®šà¯ சோதனைசெய௠+இஙà¯à®•௠வெளிகà¯à®•ொணர௠+{0}-ல௠வெளிகà¯à®•ொணர௠+{0}-ல௠இணை +இறà¯à®•à¯à®•ி மினà¯à®©à®žà¯à®šà®²à®©à¯à®ªà¯à®ªà¯... +{0}-கà¯à®•௠இறà¯à®•à¯à®•ி அஞà¯à®šà®²à®©à¯à®ªà¯à®ªà¯ +2400 +அடைவà¯à®•ள௠+பணியிலà¯à®³à¯à®³ அடைவ௠+மணà¯à®Ÿà®² தறà¯à®•ாலிக அடைவ௠+நடபà¯à®ªà¯ +கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà¯à®Ÿ: +கழறà¯à®±à¯ இயகà¯à®•ிகளை மடà¯à®Ÿà¯à®®à¯ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +கோபà¯à®ªà¯à®•ளைத௠தறà¯à®•ாலிக காபà¯à®ªà®•பà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯à®®à¯ இடதà¯à®¤à¯ˆà®•௠கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà¯à®•. +2500 +அமைவà¯à®•ள௠+".." உரà¯à®ªà¯à®ªà®Ÿà®¿à®¯à¯ˆà®•௠காடà¯à®Ÿà¯ +கோபà¯à®ªà¯ உரà¯à®ªà®¿à®•ளைக௠காடà¯à®Ÿà¯ +மணà¯à®Ÿà®² படà¯à®Ÿà®¿à®¯à®²à¯ˆà®•௠காடà¯à®Ÿà¯ + + + + + +2900 +7-ஜிபà¯à®ªà¯ˆà®ªà¯ பறà¯à®±à®¿ +தமிழாகà¯à®•ம௠(c) 2004 தமிழா! கà¯à®´à¯ - www.thamizha.com/\n\n7-ஜிப௠ஒர௠பரிநிரல௠ஆகà¯à®®à¯. ஆனாலà¯, நீஙà¯à®•ள௠7-ஜிபà¯à®ªà®¿à®©à¯ மேமà¯à®ªà®¾à®Ÿà¯à®Ÿà¯ˆ ஆதரிகà¯à®• விரà¯à®®à¯à®ªà®¿à®©à®¾à®²à¯, பதிவà¯à®ªà¯†à®±à¯à®±à¯à®™à¯à®•ளà¯. பதிவà¯à®ªà¯†à®±à¯à®± பயனராக, நீஙà¯à®•ள௠தொழிலà¯à®¨à¯à®Ÿà¯à®ª உதவியà¯à®®à¯ பெறலாமà¯. +3000 + +தவறà¯à®•ளேதà¯à®®à®¿à®²à¯à®²à¯ˆ +{0} பொரà¯à®³à¯ தெரிவானத௠+'{0}' அடைவை உரà¯à®µà®¾à®•à¯à®• இயலவிலà¯à®²à¯ˆ +இகà¯à®•ாபà¯à®ªà®•தà¯à®¤à®¿à®²à¯ பà¯à®¤à¯à®ªà¯à®ªà®¿à®•à¯à®•à¯à®®à¯ செயலà¯à®•ளà¯à®•à¯à®•௠ஆதரவிலà¯à®²à¯ˆ. + + + + +'{0}' கோபà¯à®ªà¯ மாறà¯à®±à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯.\nஇதனை காபà¯à®ªà®•தà¯à®¤à®¿à®²à¯ பà¯à®¤à¯à®ªà¯à®ªà®¿à®•à¯à®• வேணà¯à®Ÿà¯à®®à®¾? +பினà¯à®µà®°à¯à®®à¯ கோபà¯à®ªà¯ˆ பà¯à®¤à¯à®ªà¯à®ªà®¿à®•à¯à®• இயலவிலà¯à®²à¯ˆ\n'{0}' +பதிபà¯à®ªà®¾à®³à®©à¯ˆà®¤à¯ தà¯à®µà®•à¯à®• இயலவிலà¯à®²à¯ˆ. + + + + +மிகவà¯à®®à¯ அதிகமான உரà¯à®ªà¯à®ªà®Ÿà®¿à®•ள௠+3300 +வெளிகà¯à®•ொணரபà¯à®ªà®Ÿà¯à®•ினà¯à®±à®¤à¯ +இறà¯à®•à¯à®•பà¯à®ªà®Ÿà¯à®•ினà¯à®±à®¤à¯ +சோதனை... +திறகà¯à®•பà¯à®ªà®Ÿà¯à®•ினà¯à®±à®¤à¯... + +3400 +வெளிகà¯à®•ொணர௠+இஙà¯à®•௠வெளிகà¯à®•ொணரà¯: +வெளிகà¯à®•ொணரà¯à®¨à¯à®¤ கோபà¯à®ªà¯à®•ளà¯à®•à¯à®•ான இடதà¯à®¤à¯ˆà®•௠கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà¯. +3410 +பாதை à®®à¯à®±à¯ˆà®®à¯ˆ +à®®à¯à®´à¯ பாதைபà¯à®ªà¯†à®¯à®°à¯à®•ள௠+பாதைபà¯à®ªà¯†à®¯à®°à¯à®•ளிலà¯à®²à¯ˆ +3420 +மேலெழà¯à®¤à®²à¯ à®®à¯à®±à¯ˆà®®à¯ˆ +மேலெழà¯à®¤à¯à®µà®¤à®±à¯à®•௠மà¯à®©à¯ கேள௠+கேடà¯à®•ாமல௠மேலெழà¯à®¤à¯ +தறà¯à®ªà¯Šà®´à¯à®¤à¯à®³à¯à®³ கோபà¯à®ªà¯à®•ளைத௠தவிர௠+தானாக மாறà¯à®±à¯à®ªà¯à®ªà¯†à®¯à®°à®¿à®Ÿà¯ + +3500 +கோபà¯à®ªà¯ மாறà¯à®±à®¤à¯à®¤à¯ˆ உறà¯à®¤à®¿à®šà¯†à®¯à¯ +சேரிட அடைவ௠à®à®±à¯à®•னவே செயலà¯à®ªà®Ÿà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®Ÿ கோபà¯à®ªà¯ˆà®•௠கொணà¯à®Ÿà¯à®³à¯à®³à®¤à¯. +தறà¯à®ªà¯Šà®´à¯à®¤à¯à®³à¯à®³ கோபà¯à®ªà¯ˆ +இதகà¯à®•ொணà¯à®Ÿà¯ மாறà¯à®± விரà¯à®®à¯à®ªà¯à®•ிறீரà¯à®•ளா? +{0} பைடà¯à®•ள௠+தானாக மாறà¯à®±à¯à®ªà¯à®ªà¯†à®¯à®°à®¿à®Ÿà¯ +3700 +'{0}'-ல௠ஆதரவிலà¯à®²à®¾à®¤ இறà¯à®•à¯à®•ல௠மà¯à®±à¯ˆ. +'{0}'-ல௠தரவà¯à®¤à¯ தவறà¯. கோபà¯à®ªà¯ à®®à¯à®±à®¿à®¨à¯à®¤à¯à®³à¯à®³à®¤à¯. +'{0}'-ல௠CRC தோலà¯à®µà®¿à®¯à¯à®±à¯à®±à®¤à¯. கோபà¯à®ªà¯ à®®à¯à®±à®¿à®¨à¯à®¤à¯à®³à¯à®³à®¤à¯. + + +3800 +கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ உளà¯à®³à®¿à®Ÿà¯à®• +கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ உளà¯à®³à®¿à®Ÿà¯à®•: + +கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ காடà¯à®Ÿà¯ + + + +கடவà¯à®šà¯à®šà¯Šà®²à¯ +3900 +மீதமà¯à®³à¯à®³ நேரமà¯: +மீதமà¯à®³à¯à®³ நேரமà¯: +அளவà¯: +வேகமà¯: + + +தவறà¯à®•ளà¯: + +4000 +காபà¯à®ªà®•தà¯à®¤à®¿à®²à®¿à®£à¯ˆ +காபà¯à®ªà®•à®®à¯: +பà¯à®¤à¯à®ªà®¿à®•à¯à®•à¯à®®à¯ à®®à¯à®±à¯ˆà®®à¯ˆ: +காபà¯à®ªà®• வடிவமà¯: +இறà¯à®•à¯à®• வகை: +இறà¯à®•à¯à®•à¯à®®à¯ வழி: +அகராதி அளவà¯: +வாரà¯à®¤à¯à®¤à¯ˆ அளவà¯: + + +அளபà¯à®°à¯à®•ளà¯: +விரà¯à®ªà¯à®ªà®¤à¯à®¤à¯‡à®°à¯à®µà¯à®•ள௠+SFX காபà¯à®ªà®•ம௠உறà¯à®µà®¾à®•à¯à®•௠+ + + +பெயரை மறைகà¯à®•à¯à®±à®¿à®¯à¯€à®Ÿà®¾à®•à¯à®•௠+இறà¯à®•à¯à®• நினைவக பயனà¯à®ªà®¾à®Ÿà¯: +பெரà¯à®•à¯à®• நினைவக பயனà¯à®ªà®¾à®Ÿà¯: +4050 +தேகà¯à®•௠+அதிவிரைவான +விரைவான +சாதாரண +அதிகமான +சிறபà¯à®ªà®¾à®© +4060 +சேரà¯à®¤à¯à®¤à¯ கோபà¯à®ªà¯à®•ளை மாறà¯à®±à¯ +பà¯à®¤à®¿à®ªà¯à®ªà®¿à®¤à¯à®¤à¯ கோபà¯à®ªà¯à®•ளை சேர௠+உளà¯à®³ கோபà¯à®ªà¯à®•ளைப௠பà¯à®¤à¯à®ªà¯à®ªà®¿ +கோபà¯à®ªà¯à®•ளை ஒதà¯à®¤à®¿à®¯à®•à¯à®•௠+4070 +உலாவ௠+அனைதà¯à®¤à¯ கோபà¯à®ªà¯à®•ளà¯à®®à¯ + + +6000 +நகல௠+நகரà¯à®¤à¯à®¤à¯ +இஙà¯à®•௠நகலெடà¯: +இஙà¯à®•௠நகரà¯à®¤à¯à®¤à¯: +நகலெடà¯à®•à¯à®•பà¯à®ªà®Ÿà¯à®•ிறதà¯... +நகரà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®•ிறதà¯... +பெயரà¯à®®à®¾à®±à¯à®±à®ªà¯à®ªà®Ÿà¯à®•ிறதà¯... + +அதà¯à®¤à®•ைய செயலà¯à®•à¯à®•௠ஆதரவிலà¯à®²à¯ˆ. +கோபà¯à®ªà¯ˆà®¯à¯‹ அடைவையோ பெயரà¯à®®à®¾à®±à¯à®±à¯à®®à¯à®ªà¯‹à®¤à¯ தவற௠+ + +6100 +கோபà¯à®ªà¯ அழிபà¯à®ªà¯ˆ உறà¯à®¤à®¿à®šà¯†à®¯à¯ +அடைவ௠அழிபà¯à®ªà¯ˆ உறà¯à®¤à®¿à®šà¯†à®¯à¯ +பல கோபà¯à®ªà¯ அழிபà¯à®ªà¯ˆ உறà¯à®¤à®¿à®šà¯†à®¯à¯ +'{0}'-஠உறà¯à®¤à®¿à®¯à®¾à®• அழிகà¯à®• விரà¯à®®à¯à®ªà¯à®•ிறீரà¯à®•ளா? +'{0}' அடைவையà¯à®®à¯ அதிலà¯à®³à¯à®³à®µà®±à¯à®±à®¯à¯à®®à¯ உறà¯à®¤à®¿à®¯à®¾à®•வே அழிகà¯à®• விரà¯à®®à¯à®ªà¯à®•ிறீரà¯à®•ளா? +இநà¯à®¤ {0} உரà¯à®ªà¯à®ªà®Ÿà®¿à®•ளை உறà¯à®¤à®¿à®¯à®¾à®• அழிகà¯à®• விரà¯à®®à¯à®ªà¯à®•ிறீரà¯à®•ளா? +அழிகà¯à®•பà¯à®ªà®Ÿà¯à®•ிறதà¯... +கோபà¯à®ªà¯ˆà®¯à¯‹ அடைவையோ அழிகà¯à®•à¯à®®à¯à®ªà¯‹à®¤à¯ தவற௠+ +6300 +அடைவ௠உரà¯à®µà®¾à®•à¯à®•௠+கோபà¯à®ªà¯ உரà¯à®µà®¾à®•à¯à®•௠+அடைவின௠பெயரà¯: +கோபà¯à®ªà®¿à®©à¯ பெயரà¯: +பà¯à®¤à®¿à®¯ அடைவ௠+பà¯à®¤à®¿à®¯ கோபà¯à®ªà¯ +அடைவ௠உரà¯à®µà®¾à®•à¯à®•à¯à®®à¯à®ªà¯‹à®¤à¯ தவற௠+கோபà¯à®ªà¯ உரà¯à®µà®¾à®•à¯à®•ையில௠தவறேறà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +6400 +கரà¯à®¤à¯à®¤à¯à®°à¯ˆ +கரà¯à®¤à¯à®¤à¯à®°à¯ˆ: +தெரிவà¯à®šà¯†à®¯à¯ +நீகà¯à®•௠+à®®à¯à®•à®®à¯à®Ÿà®¿: +6600 + +அடைவà¯à®•ளின௠வரலாற௠+அறிவழிச௠செயà¯à®¤à®¿à®•ள௠+செயà¯à®¤à®¿ +7100 +கணினி +பிணையம௠+ +மணà¯à®Ÿà®²à®®à¯ +7200 +இணை +வெளிகà¯à®•ொணர௠+பரிசோதி +நகல௠+நகரà¯à®¤à¯à®¤à¯ +அழி +தகவல௠+7300 + + +கனவளவà¯à®•ளà¯à®•à¯à®•à¯, பைடà¯à®•ளà¯à®•à¯à®•à¯à®ªà¯ பிரி: + + + + + + +7400 + + + + + + +7500 + + + + +7600 +மதிபà¯à®ªà¯€à®Ÿà¯à®Ÿà¯ அளவை +நினைவக பயனà¯: +இறà¯à®•à¯à®•பà¯à®ªà®Ÿà¯à®•ையில௠+பெரà¯à®•à¯à®•பà¯à®ªà®Ÿà¯à®•ையில௠+பà¯à®³à¯à®³à®¿à®•ள௠+மொதà¯à®¤ பà¯à®³à¯à®³à®¿à®•ள௠+நடபà¯à®ªà¯ +à®®à¯à®Ÿà®¿à®µà®¿à®²à¯ + + +சரியானவை: diff --git a/Utils/7-Zip/Lang/th.txt b/Utils/7-Zip/Lang/th.txt new file mode 100644 index 000000000..96e06cc03 --- /dev/null +++ b/Utils/7-Zip/Lang/th.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.09 : Chayanon Ruamcharoen +; 4.10 : Zafire06 +; 9.13 : Kom10 +; +; +; +; +; +; +; +; +0 +7-Zip +Thai +ไทย +401 +ตà¸à¸¥à¸‡ +ยà¸à¹€à¸¥à¸´à¸ + + + +&ใช่ +&ไม่ +&ออภ+ช่วยเหลือ + +&ดำเนินà¸à¸²à¸£à¸•่อ +440 +ใช่ทั้งหมด +ไม่ทั้งหมด +หยุด +ฟื้นฟู +&ทำงานเป็นพื้นหลัง +&ทำงานเป็นพื้นหน้า +&หยุดชั่วคราว +หยุดชั่วคราว +คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸­à¸§à¹ˆà¸²à¸ˆà¸°à¸¢à¸à¹€à¸¥à¸´à¸ +500 +&à¹à¸Ÿà¹‰à¸¡ +&à¹à¸à¹‰à¹„ข +&มุมมอง +&รายà¸à¸²à¸£à¹‚ปรด +&เครื่องมือ +&ช่วยเหลือ +540 +&เปิด +เปิดในหน้าต่างเดิม +เปิดในหน้าต่างใหม่ +&มุมมอง +&à¹à¸à¹‰à¹„ข +&เปลี่ยนชื่อ +&คัดลอà¸à¹„ปที่... +&วางที่... +&ลบ +&à¹à¸¢à¸à¹„ฟล์... +รวมไฟล์... +&คุณสมบัติ +&หมายเหตุ +คำนวณ checksum +Diff +สร้างโฟลเดอร์ +สร้างไฟล์ +ออภ+600 +เลือà¸à¸—ั้งหมด +ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¹€à¸¥à¸·à¸­à¸à¸—ั้งหมด +&สลับà¸à¸²à¸£à¹€à¸¥à¸·à¸­à¸à¹ƒà¸«à¹‰à¹€à¸›à¹‡à¸™à¸•รงà¸à¸±à¸™à¸‚้าม +เลือà¸... +ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¹€à¸¥à¸·à¸­à¸... +เลือà¸à¸”้วยà¹à¸šà¸šà¸Šà¸™à¸´à¸” +ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¹€à¸¥à¸·à¸­à¸à¸”้วยà¹à¸šà¸šà¸Šà¸™à¸´à¸” +700 +&ไอคอนขนาดใหà¸à¹ˆ +&ไอคอนขนาดเล็ภ+&à¹à¸ªà¸”งเป็นรายà¸à¸²à¸£ +&à¹à¸ªà¸”งà¹à¸šà¸šà¸¥à¸°à¹€à¸­à¸µà¸¢à¸” +730 +ไม่เลือภ+à¹à¸ªà¸”งไฟล์à¹à¸¥à¸°à¹‚ฟลด์เดอร์ทั้งหมด +&à¹à¸ªà¸”ง 2 à¹à¸œà¸‡ +&à¹à¸–บเครื่องมือ +เปิดราà¸à¹‚ฟลเดอร์ +เลื่อนขึ้นหนึ่งระดับ +ประวัติโฟลเดอร์... +&ฟื้นฟู +750 +à¹à¸–บเครื่องมือเอà¸à¸ªà¸²à¸£ +à¹à¸–บเครื่องมือธรรมดา +ปุ่มขนาดใหà¸à¹ˆ +à¹à¸ªà¸”งข้อความบนปุ่ม +800 +&เพิ่มโฟลเดอร์เข้ารายà¸à¸²à¸£à¹‚ปรด +คั่นหน้าที่ +900 +&ตัวเลือà¸... +&เà¸à¸“ฑ์เปรียบเทียบสมรรถนะ +960 +&เนื้อหาà¹à¸¥à¸°à¸”ัชนี... +&เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š 7-Zip... +1003 +ที่ตั้ง +ชื่อ +ชนิด +โฟลเดอร์ +ขนาด +ขนาดเมื่อถูà¸à¸ˆà¸±à¸”เà¸à¹‡à¸š +ลัà¸à¸©à¸“ะประจำ +สร้างเมื่อ +เข้าถึงเมื่อ +ดัดà¹à¸›à¸£à¹€à¸¡à¸·à¹ˆà¸­ +ต่อเนื่อง +หมายเหตุ +à¸à¸²à¸£à¹€à¸‚้ารหัสลับ +à¸à¹ˆà¸­à¸™à¹à¸šà¹ˆà¸‡ +หลังà¹à¸šà¹ˆà¸‡ +ดิคชันนารี +ซีอาร์ซี +à¹à¸šà¸šà¸Šà¸™à¸´à¸” +ต่อต้าน +วิธีà¸à¸²à¸£ +OS ที่ใช้ +ไฟล์ระบบ +ผู้ใช้ +à¸à¸¥à¸¸à¹ˆà¸¡ +บล็อภ+หมายเหตุ +ตำà¹à¸«à¸™à¹ˆà¸‡ +คำนำหน้าที่ตั้ง +โฟลเดอร์ +ไฟล์ +เวอร์ชั่น +วอลลุ่ม +มัลติวอลลุ่ม +ออฟเซ็ท +ลิ้งค์ +บล็อค +Volumes + +64-บิท +Big-endian +CPU +ขนาดทางà¸à¸²à¸¢à¸ à¸²à¸ž +ขนาดเฮดเดอร์ +Checksum +คุณลัà¸à¸©à¸“ะ +Virtual Address +ID +ชื่อย่อ +ผู้สร้างโปรà¹à¸à¸£à¸¡ +ขนาด Sector +โหมด +ลิงค์ +เà¸à¸´à¸”ข้อผิดพลาด +ขนาดทั้งหมด +ช่องว่างที่เหลืออยู่ +ขนาดà¸à¸¥à¸¸à¹ˆà¸¡ +ป้าย +ชื่อเฉพาะ +ผู้ให้บริà¸à¸²à¸£ +2100 +ตัวเลือภ+ภาษา +ภาษา: +บรรณาธิà¸à¸£à¸“์ +&บรรณาธิà¸à¸£à¸“์: +&Diff: +2200 +ระบบ +ทำให้ 7-Zip ทำงานร่วมà¸à¸±à¸š: +2301 +รวบรวมคำสั่ง 7-Zip ไปที่à¹à¸–บเมนูลัด +à¹à¸¢à¸à¹€à¸›à¹‡à¸™à¹à¸–บคำสั่ง 7-Zip +วัตถุที่ปราà¸à¸à¸šà¸™à¹€à¸¡à¸™à¸¹à¸¥à¸±à¸”: +2320 +<โฟลเดอร์> +<เอà¸à¸ªà¸²à¸£> +เปิดเอà¸à¸ªà¸²à¸£ +à¹à¸¢à¸à¹„ฟล์... +เพิ่มเข้าเอà¸à¸ªà¸²à¸£... +ทดสอบเอà¸à¸ªà¸²à¸£ +à¹à¸¢à¸à¹„ฟล์ที่นี่ +à¹à¸¢à¸à¹„ฟล์ไปที่ {0} +เพิ่มเข้า {0} +บีบอัดà¹à¸¥à¹‰à¸§à¸ªà¹ˆà¸‡à¸­à¸µà¹€à¸¡à¸¥à¸¥à¹Œ... +บีบอัดเป็น {0} à¹à¸¥à¹‰à¸§à¸ªà¹ˆà¸‡à¸­à¸µà¹€à¸¡à¸¥à¸¥à¹Œ +2400 +โฟลเดอร์ +&โฟลเดอร์ที่ทำงานอยู่ +&โฟลเดอร์ต่างๆของระบบ +&โฟลเดอร์ปัจจุบัน +&ระบุ: +ใช้สำหรับไดรฟ์à¹à¸šà¸šà¸–อดได้เท่านั้น +ระบุที่ตั้งสำหรับไฟล์เอà¸à¸ªà¸²à¸£à¸Šà¸±à¹ˆà¸§à¸„ราว +2500 +à¸à¸³à¸«à¸™à¸” +à¹à¸ªà¸”งวัตถุ ".." +à¹à¸ªà¸”งไอคอนไฟล์ที่à¹à¸—้จริง +à¹à¸ªà¸”งเมนูระบบ +&เลือà¸à¹€à¸•็มà¹à¸–ว +à¹à¸ªà¸”ง &เส้นà¸à¸£à¸´à¸” +คลิà¸à¸„รั้งเดียวเพื่อเปิดไฟล์ +&โหมดà¸à¸²à¸£à¹€à¸¥à¸·à¸­à¸à¸­à¸·à¹ˆà¸™ +ใช้ &เพจความจำขนาดใหà¸à¹ˆ +2900 +เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š 7-Zip +7-Zip เป็นฟรีà¹à¸§à¸£à¹Œ อย่างไรà¸à¹‡à¸•าม คุณสามารถสนับสนุนà¸à¸²à¸£à¸žà¸±à¸’นาของ 7-Zip ได้โดยà¸à¸²à¸£à¸¥à¸‡à¸—ะเบียน เมื่อคุณเป็นผู้ใช้ที่ลงทะเบียนà¹à¸¥à¹‰à¸§ คุณจะได้รับà¸à¸²à¸£à¸ªà¸™à¸±à¸šà¸ªà¸™à¸¸à¸™à¸—างเทคนิคจาà¸à¹€à¸£à¸² +3000 +ระบบไม่สามารถใช้หน่วยความจำตามที่ระบุได้ +ไม่มีข้อผิดพลาด +{0} วัตถุที่เลือà¸à¹„ว้ +ไม่สามารถสร้างโฟลเดอร์ '{0}' +ปรับปรุงà¸à¸²à¸£à¸—ำงานไม่สนับสนุนสำหรับเอà¸à¸ªà¸²à¸£ +ไม่สามารถเปิดไฟล์ '{0}' เป็นเอà¸à¸ªà¸²à¸£à¹„ด้ +ไม่สามารถเปิดไฟล์ที่เข้ารหัสได้ '{0}' พาสเวิร์ดผิดหรือไม่? +ชนิดของไฟล์บีบอัดไม่รองรับ +ไฟล์ {0} มีอยู่à¹à¸¥à¹‰à¸§ +ไฟล์ '{0}' ได้ถูà¸à¸”ัดà¹à¸›à¸£à¹à¸¥à¹‰à¸§\nคุณต้องà¸à¸²à¸£à¸ˆà¸°à¸›à¸£à¸±à¸šà¸›à¸£à¸¸à¸‡à¹€à¸­à¸à¸ªà¸²à¸£à¸«à¸£à¸·à¸­à¹„ม่ +ไม่สามารถปรับปรุงไฟล์\n'{0}' +ไม่สามารถเปิดตัวà¹à¸à¹‰à¹„ขได้ +ไฟล์อาจเป็นไวรัส (ชื่อไฟล์มีช่องว่างยาว) +ไม่สามารถดำเเนินà¸à¸²à¸£à¹„ด้จาà¸à¹‚ฟลเดอร์ที่มีที่ตั้งยาว +ท่านต้องเลือà¸à¹„ฟล์ +ท่านต้องเลือà¸à¹„ฟล์หนึ่งไฟล์หรือมาà¸à¸§à¹ˆà¸² +มีวัตถุมาà¸à¹€à¸à¸´à¸™à¹„ป +3300 +à¸à¸³à¸¥à¸±à¸‡à¹à¸¢à¸à¹„ฟล์ +à¸à¸³à¸¥à¸±à¸‡à¸šà¸µà¸šà¸­à¸±à¸” +à¸à¸³à¸¥à¸±à¸‡à¸—ดสอบ +à¸à¸³à¸¥à¸±à¸‡à¹€à¸›à¸´à¸”... +à¸à¸³à¸¥à¸±à¸‡à¸ªà¹à¸à¸™... +3400 +à¹à¸¢à¸à¹„ฟล์ +à¹à¸¢à¸à¹„ฟล์ไปที่: +ระบุที่ตั้งสำหรับไฟล์ที่à¹à¸¢à¸à¸­à¸­à¸à¸¡à¸² +3410 +ที่ตั้ง +ชื่อที่ตั้งà¹à¸šà¸šà¹€à¸•็ม +ไม่มีชื่อที่ตั้ง +3420 +à¸à¸²à¸£à¸šà¸±à¸™à¸—ึà¸à¸—ับ +ถามà¸à¹ˆà¸­à¸™à¸¡à¸µà¸à¸²à¸£à¸šà¸±à¸™à¸—ึà¸à¸—ับ +บันทึà¸à¸—ับโดยไม่มีข้อความพร้อมรับ +ข้ามไฟล์ที่มีอยู่ +เปลี่ยนชื่ออัตโนมัติ +เปลี่ยนชื่อไฟล์ที่มีอยู่อัตโนมัติ +3500 +ยืนยันà¸à¸²à¸£à¹à¸—นที่ไฟล์ +โฟลเดอร์ปลายทางมีไฟล์ที่ได้ประมวลผลà¹à¸¥à¹‰à¸§ +คุณต้องà¸à¸²à¸£à¹à¸—นที่ไฟล์ที่มีอยู่หรือไม่ +ด้วย +{0} ไบต์ +เปลี่ยนชื่ออัตโนมัติ +3700 +ไม่รองรับวิธีà¸à¸²à¸£à¸šà¸µà¸šà¸­à¸±à¸”นี้สำหรับ '{0}' +ข้อมูลใน '{0}' ผิดพลาด ไฟล์ชำรุด +ซีอาร์ซีใน '{0}' ไม่สามารถใช้à¸à¸²à¸£à¹„ด้ ไฟล์ชำรุด +ข้อมูลในไฟล์บีบอัด '{0}' ผิดพลาด รหัสผ่านไม่ถูà¸à¸•้อง? + +3800 +ใส่รหัสผ่าน +ใส่รหัสผ่าน: +ใส่รหัสผ่านอีà¸à¸„รั้ง: +&à¹à¸ªà¸”งรหัสผ่าน +รหัสผผ่านไม่ตรง +ตั้งรหัสผ่านด้วยอัà¸à¸©à¸£à¸ à¸²à¸©à¸²à¸­à¸±à¸‡à¸à¸¤à¸© หรืออัà¸à¸‚ระ (!, #, $, ...) +รห้สผ่านยาวเà¸à¸´à¸™à¹„ป +รหัสผ่าน +3900 +ใช้เวลาไปà¹à¸¥à¹‰à¸§: +ต้องใช้เวลาอีà¸: +ขนาด: +ความเร็ว: +ดำเนินà¸à¸²à¸£à¹à¸¥à¹‰à¸§: +อัตราส่วนà¸à¸²à¸£à¸šà¸µà¸šà¸­à¸±à¸”: +ความผิดพลาด: +เอà¸à¸ªà¸²à¸£: +4000 +เพิ่มเข้าเอà¸à¸ªà¸²à¸£ +&เอà¸à¸ªà¸²à¸£: +&à¸à¸²à¸£à¸›à¸£à¸±à¸šà¸›à¸£à¸¸à¸‡: +รูปà¹à¸šà¸šà¸à¸²à¸£à¸šà¸µà¸šà¸­à¸±à¸”ที่ต้องà¸à¸²à¸£à¹ƒà¸Šà¹‰: +อัตราà¸à¸²à¸£à¸šà¸µà¸šà¸­à¸±à¸”: +&วิธีà¸à¸²à¸£à¸šà¸µà¸šà¸­à¸±à¸”: +&ขนาดดิคชันนารี: +&ขนาดอัà¸à¸©à¸£: +ขนาด Solid block: +จำนวน CPU threads: +&พารามิเตอร์: +ตัวเลือภ+สร้างเอà¸à¸ªà¸²à¸£ SFX +บีบอัดà¹à¸Šà¸£à¹Œà¹„ฟล์ +à¸à¸²à¸£à¹€à¸‚้ารหัส +วิธีà¸à¸²à¸£à¹€à¸‚้ารหัส: +สร้างรหัสผ่าน +หน่วยความจำที่ใช้ในà¸à¸²à¸£à¸šà¸µà¸šà¸­à¸±à¸”: +หน่วยความจำที่ใช้ในà¸à¸²à¸£à¹à¸•à¸à¸­à¸­à¸: +4050 +เà¸à¹‡à¸šà¹€à¸‰à¸¢à¹† +เร็วที่สุด +เร็ว +ปà¸à¸•ิ +ดี +ดีที่สุด +4060 +เพิ่มà¹à¸¥à¸°à¹à¸—นที่ไฟล์ +ปรับปรุงà¹à¸¥à¸°à¹€à¸žà¸´à¹ˆà¸¡ +ทำให้ไฟล์ที่มีอยู่ใช้à¸à¸²à¸£à¹„ด้ดีขึ้น +ทำให้ไฟล์ประสานà¸à¸±à¸™ +4070 +ค้นดู +ไฟล์ทั้งหมด +Non-solid +Solid +6000 +คัดลอภ+ย้าย +คัดลอà¸à¹„ปที่: +ย้ายที่: +à¸à¸³à¸¥à¸±à¸‡à¸„ัดลอà¸... +à¸à¸³à¸¥à¸±à¸‡à¸§à¸²à¸‡... +à¸à¸³à¸¥à¸±à¸‡à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸Šà¸·à¹ˆà¸­... +เลือà¸à¹‚ฟลเดอร์ที่ตั้ง +à¸à¸²à¸£à¸›à¸à¸´à¸šà¸±à¸•ิà¸à¸²à¸£à¹„ม่สนับสนุน +เà¸à¸´à¸”ข้อผิดพลาดในà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸Šà¸·à¹ˆà¸­à¹„ฟล์หรือโฟลเดอร์ +ยืนยันà¸à¸²à¸£à¸„ัดลอà¸à¹„ฟล์ +ท่านมั่นใจที่จะคัดลอà¸à¹„ฟล์ไปยังเอà¸à¸ªà¸²à¸£à¸«à¸£à¸·à¸­à¹„ม่ +6100 +ยืนยันà¸à¸²à¸£à¸¥à¸šà¹„ฟล์ +ยืนยันà¸à¸²à¸£à¸¥à¸šà¹‚ฟลเดอร์ +ยืนยันà¸à¸²à¸£à¸¥à¸šà¹„ฟล์à¹à¸šà¸šà¸„วบซ้อน +คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸­à¸§à¹ˆà¸²à¸„ุณต้องà¸à¸²à¸£à¸ˆà¸°à¸¥à¸šà¹„ฟล์ '{0}'? +คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸­à¸§à¹ˆà¸²à¸„ุณต้องà¸à¸²à¸£à¸ˆà¸°à¸¥à¸šà¹‚ฟลเดอร์ '{0}' à¹à¸¥à¸°à¸‚้อมูลของมันทั้งหมด +คุณà¹à¸™à¹ˆà¹ƒà¸ˆà¸«à¸£à¸·à¸­à¸§à¹ˆà¸²à¸ˆà¸°à¸¥à¸šà¸§à¸±à¸•ถุ {0} เหล่านี้ +à¸à¸³à¸¥à¸±à¸‡à¸¥à¸š... +เà¸à¸´à¸”ข้อผิดพลาดในà¸à¸²à¸£à¸¥à¸šà¹„ฟล์หรือโฟลเดอร์ +ระบบไม่สามารถย้ายไฟล์ที่ชื่อที่ตั้งยาวไปยังถังขยะได้ +6300 +สร้างโฟลเดอร์ +สร้างไฟล์ +ชื่อโฟลเดอร์: +ชื่อไฟล์: +โฟลเดอร์ใหม่ +ไฟล์ใหม่ +เà¸à¸´à¸”ข้อผิดพลาดในà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¹‚ฟลเดอร์ +เหิดข้อผิดพลาดในà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¹„ฟล์ +6400 +หมายเหตุ +&หมายเหตุ: +เลือภ+ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¹€à¸¥à¸·à¸­à¸ +ตัวพราง: +6600 +คุณสมบัติ +ประวัติโฟลเดอร์ +ข้อความวินิจฉัย +ข้อความ +7100 +คอมพิวเตอร์ +เครือข่าย +เอà¸à¸ªà¸²à¸£ +ระบบ +7200 +เพิ่มเข้า +à¹à¸¢à¸à¹„ฟล์ +ทดสอบ +คัดลอภ+ย้าย +ลบ +เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š +7300 +à¹à¸¢à¸à¹„ฟล์ +&à¹à¸¢à¸à¹„ปยัง: +ขนาดไฟล์ที่ต้องà¸à¸²à¸£à¹à¸šà¹ˆà¸‡, ไบต์: +à¸à¸³à¸¥à¸±à¸‡à¹à¸¢à¸... +ยืนยันà¸à¸²à¸£à¹à¸¢à¸ +ท่านมั่นใจว่าต้องà¸à¸²à¸£à¹à¸¢à¸à¹„ฟล์เป็น {0} volumes? +ขนาด Volume ต้องเล็à¸à¸à¸§à¹ˆà¸²à¸‚นาดไฟล์ต้นฉบับ +ขนาด volume ไม่ถูà¸à¸•้อง +ระบุขนาด volume: {0} ไบท์.\nท่านมั่นใจว่าต้องà¸à¸²à¸£à¹à¸šà¹ˆà¸‡à¹„ฟล์เป็น volumes ดังà¸à¸¥à¹ˆà¸²à¸§? +7400 +รวมไฟล์ +&รวมไปยัง: +à¸à¸³à¸¥à¸±à¸‡à¸£à¸§à¸¡... +เลือà¸à¹€à¸‰à¸žà¸²à¸°à¸ªà¹ˆà¸§à¸™à¹à¸£à¸à¸‚องไฟล์à¹à¸¢à¸ +ไม่สามารถตรวจพบไฟล์ว่าเป็นส่วนหนึ่งของไฟล์à¹à¸¢à¸ +ไม่สามารถหาไฟล์à¹à¸¢à¸à¹„ด้มาà¸à¸«à¸™à¸¶à¹ˆà¸‡ +7500 +à¸à¸³à¸¥à¸±à¸‡à¸„ำนวณ Checksum... +ข้อมูล Checksum +CRC checksum ของข้อมูล: +CRC checksum ของข้อมูลà¹à¸¥à¸°à¸Šà¸·à¹ˆà¸­: +7600 +เà¸à¸“ฑ์เปรียบเทียบสมรรถนะ +หน่วยความจำที่ใช้: +à¸à¸²à¸£à¸šà¸µà¸šà¸­à¸±à¸” +à¸à¸²à¸£à¸¢à¸à¹€à¸¥à¸´à¸à¸šà¸µà¸šà¸­à¸±à¸” +เà¸à¸“ฑ์ความสามารถ +เà¸à¸“ฑ์ความสามารถทั้งหมด +ปัจจุบัน +ผลà¸à¸²à¸£à¸›à¸£à¸°à¹€à¸¡à¸´à¸™ +à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ CPU +ประสิทธิภาพ /à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ +ข้อความ: diff --git a/Utils/7-Zip/Lang/tr.txt b/Utils/7-Zip/Lang/tr.txt new file mode 100644 index 000000000..c0f3ae379 --- /dev/null +++ b/Utils/7-Zip/Lang/tr.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.07 : 2009-09-22 : X-FoRcE +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Turkish +Türkçe +401 +Tamam +İptal + + + +&Evet +&Hayır +&Kapat +Yardım + +De&vam et +440 +Tümüne E&vet +Tümüne Ha&yır +Dur +Tekrar baÅŸlat +&Arka planda +Ö&nde +&Duraklat +Duraklatıldı - +İptal edilsin mi? +500 +&Dosya +Dü&zenle +Gö&rüntüle +Sı&k Kullanılanlar +&Araçlar +&Yardım +540 +&Aç +Pa&nelde Aç +Pence&rede Aç +&Görüntüle +Dü&zenle +Yeni a&d ver +K&opyala +&Taşı +&Sil +&Parçala... +&BirleÅŸtir... +Öz&ellikler +Açıkla&ma +Toplam checksum hesapla +Fark +Yeni k&lasör +Yeni dos&ya +Çı&k +600 +&Tümünü seç +Tüm seçimi ka&ldır +&Aksini seç +Seç... +Seçimi kaldır... +Bu uzantıyı seç +Uzantılı seçimi kaldır +700 +&Büyük Simgeler +&Küçük Simgeler +&Liste +&Detaylar +730 +Sırasız +Düz Görünüm +&2 Panel aç +&Araç çubukları +Kök Klasörü Aç +Bir Seviye Yukarı +Klasör GeçmiÅŸi... +&Yenile +750 +ArÅŸiv çubuÄŸu +Standart çubuk +Büyük düğmeler +Düğme metinleri görünsün +800 +Geçerli &klasörü ekle +Yer +900 +&Seçenekler... +&Performans ölçümü +960 +İç&indekiler... +7-Zip &Hakkında... +1003 +Yol +Ad +Uzantı +Klasör +Boyut +ArÅŸivde boyutu +Öznitelikler +OluÅŸturma +EriÅŸim +DeÄŸiÅŸtirme +Katı +Açıklanmış +ÅžifrelenmiÅŸ +Önceki parça +Sonraki parça +Sözlük +CRC +Tür +Anti +Sıkıştırma ÅŸekli +İşletim sistemi +Dosya Sistemi +Kullanıcı +Grup +Blok +Açıklama +Konum +Yol Öneki +Klasörler +Dosyalar +Sürüm +Cilt +Çoklu Cilt +Konum +BaÄŸlantılar +Bloklar +Ciltler + +64-bit +Big-endian +İşlemci +Fiziksel Boyut +BaÅŸlık Boyutu +Checksum +Karakteristik +Sanal Adres +ID +Kısa İsim +OluÅŸturan Yazılım +Kesim Boyutu +Biçim +BaÄŸlantı +Hata +Toplam Boyut +BoÅŸ Alan +Küme Boyutu +Etiket +Yerel Ad +SaÄŸlayıcı +2100 +Seçenekler +Dil +Dil: +Düzenleyici +Metin &düzenleyici: +&Fark: +2200 +Sistem +7-Zip ile iliÅŸkilendir: +2301 +İçerik menülerinde 7-Zip görünsün +Kademeli içerik menüsü +İçerik menü öğeleri: +2320 + + +ArÅŸivi aç +Dosyaları çıkart... +ArÅŸivle... +ArÅŸivi sına +Burada çıkart +{0} klasörüne çıkart +{0} olarak arÅŸivle +Sıkıştırıp postala... +{0} olarak sıkıştır ve postala +2400 +Klasörler +Ça&lışma klasörü +&Sistem TEMP klasörü +&Geçerli klasör +&Belirtilen klasör: +Sadece çıkarılabilen sürücüler için kullan +Geçici arÅŸiv dosyaları için bir yer belirleyin. +2500 +Ayarlar +".." öğesi görünsün +Gerçek dosya simgeleri görünsün +Sistem menüsü görünsün +&Tüm satır seçilsin +Tabl&o çizgileri görünsün +Öğeyi açmak için tek tıkla +&Alternatif seçim kipi +GeniÅŸ &bellek sayfaları kullan +2900 +7-Zip hakkında +7-Zip özgür bir yazılımdır. Ancak, kayıt olarak 7-zip geliÅŸtirme faaliyetine destek olabilirsiniz. +3000 +Sistem gerekli belleÄŸi ayarlayamadı +Hata yok. +{0} adet öğe seçili +'{0}' klasörü oluÅŸturulamıyor +Bu arÅŸiv üzerinde güncelleme yapamazsınız. +'{0}' arÅŸiv dosyası olarak açılamıyor. +'{0}' dosyası açılamıyor. Åžifreniz yanlış olabilir mi? +Desteklenmeyen arÅŸiv tipi +Dosya {0} zaten mevcut +'{0}' dosyası deÄŸiÅŸmiÅŸ.\nArÅŸivde güncellensin mi? +'{0}' dosyası güncellenemedi +Metin düzenleyici baÅŸlatılamadı. +Bu dosya virüs gibi görünüyor.(Dosya ismi uzun boÅŸluk içeriyor). +The operation cannot be called from a folder that has a long path. +Bir dosya seçmelisiniz +Bir veya daha fazla dosya seçmelisiniz +Çok fazla öğe +3300 +çıkartılıyor +sıkıştırılıyor +Sınanıyor +açılıyor... +Taranıyor... +3400 +Çıkart +&Çıkartılacak yer: +Çıkartılacak dosyalar için bir yer belirleyin. +3410 +Yol adları +Tam yol adları +Yol adları olmasın +3420 +Olan dosyalar +Üzerine yazmak için sor +Sormadan üzerine yaz +Çıkartma +ArÅŸivdekilere yeni ad ver +Olanlara yeni ad ver +3500 +Üzerine Yazma Durumu +Hedef klasörde bu adla bir dosya var. Üzerine yazılsın mı? +Mevcut dosya: +Çıkartılan dosya: +{0} bayt +&Yeni ad ver +3700 +'{0}' için sıkıştırma ÅŸekli tanınamadı. +'{0}' bozuk. (Veri hatası) +'{0}' bozuk. (CRC hatası) +ÅžifrelenmiÅŸ '{0}' dosyası hatalı. Åžifreniz yanlış olabilir mi? +'{0}' dosyasında CRC hatası. Åžifreniz yanlış olabilir mi? +3800 +Parola GiriÅŸi +Parolayı girin: +Åžifre tekrarı: +Par&ola görünsün +Åžifreler birbiriyle uyuÅŸmuyor. +Åžifre için İngilizce harfler, sayılar ve özel karekterden (!, #, $, ...) kullanabilirsiniz. +Åžifre çok uzun +Parola +3900 +Geçen süre: +Kalan süre: +Boyut: +Hız: +İşlenen: +Sıkıştırma oranı: +Hatalı: +ArÅŸivler: +4000 +ArÅŸivle +&ArÅŸiv: +&Güncelleme ÅŸekli: +ArÅŸiv &biçimi: +Sıkıştırma dü&zeyi: +Sı&kıştırma ÅŸekli: +&Sözlük boyutu: +Ke&lime boyutu: +Aralıksız blok boyutu: +İşlemci iÅŸ parçası sayısı: +&Parametreler: +Seçenekler +Ke&ndi çıkartsın (SFX) +Paylaşılan dosyaları sıkıştır +Åžifreleme +Åžifreleme metodu: +Dosya adlarını ÅŸi&frele +Bellek kullanımı (Sıkıştırma): +Bellek kullanımı (Çözme): +4050 +Sıkıştırmasız +En hızlı +Hızlı +Normal +Maksimum +Ultra +4060 +Dosyaları ekle, olanları çıkart +Dosyaları ekle, eskileri güncelle +Sadece eskileri güncelle +Dosyaları eÅŸitle +4070 +Gözat +Tüm dosyalar +Aralıklı +Aralıksız +6000 +Kopyala +Taşı +Kopyalanacak yer: +Taşınacak yer: +Kopyalanıyor... +Taşınıyor... +Dosya Adı DeÄŸiÅŸtiriliyor... +Hedef klasörü seçiniz. +Bu klasör için istenen iÅŸlem desteklenmiyor. +Dosya veya Klasör Adlandırma Hatası +Kopyalama Onayı +Dosyalar arÅŸive kopyalansın mı +6100 +Dosya Silme Onayı +Klasör Silme Onayı +Birden Fazla Dosya Silme Onayı +'{0}' silinsin mi? +'{0}' klasörü ve içindekiler silinsin mi? +{0} silinsin mi? +Siliniyor... +Dosya veya Klasör Silme Hatası +Dosya yolu uzun olduÄŸundan Geri Dönüşüm Kutusuna taşınamıyor +6300 +Yeni klasör +Yeni dosya +Klasör adı: +Dosya Adı: +Yeni Klasör +Yeni Dosya +Klasör OluÅŸturma Hatası +Dosya OluÅŸturma Hatası +6400 +Açıklama +&Açıklama: +Seç +Seçimi kaldır +Seçim ifadesi: +6600 +Özellikler +Klasör GeçmiÅŸi +Tanılayıcı iletiler +İleti +7100 +Bilgisayar +AÄŸ +Belgeler +Sistem +7200 +ArÅŸivle +Çıkart +Sına +Kopyala +Taşı +Sil +Bilgi +7300 +Parçala +Åžu &klasörde parçala: +Bayt/&cilt olarak parçala: +Parçalanıyor... +Silmeyi onaylayın +Dosyayı {0} parçaya ayırmak istediÄŸinizden emin misiniz? +Parça büyüklüğü, orjinal dosya boyutundan küçük olmalıdır +Yanlış cilt boyutu +Belirtilen cilt boyutu: {0} bayt.\nBu boyutta ciltlere ayırmak istediÄŸinize emin misiniz? +7400 +BirleÅŸtir +Åžu &klasörde birleÅŸtir: +BirleÅŸtiriliyor... +Sadece ilk parçayı seçiniz +Parçalanan dosya tespit edilemedi +Parçalanmış dosyanın bir parçadan fazlası bulunamadı +7500 +Checksum deÄŸeri hesaplanıyor... +Checksum bilgisi +Verinin CRC deÄŸeri: +Verinin CRC deÄŸeri ve isimler: +7600 +Bilgisayar performansı +Bellek kullanımı: +Sıkıştırılıyor +Çözülüyor +Puan +Toplam Puan +Mevcut +Sonuç +İşlemci kullanımı +Puan / Kullanım +BaÅŸarılı: diff --git a/Utils/7-Zip/Lang/tt.txt b/Utils/7-Zip/Lang/tt.txt new file mode 100644 index 000000000..878f6cec0 --- /dev/null +++ b/Utils/7-Zip/Lang/tt.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.16 : Amychok : (Ne laÅ­diÄu ke vi scias multajn lingvojn hontu ke vi ne scias patran lingvon) +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Tatar +Татарча +401 +Ярый +Юкка чыгару + + + +&Әйе +Юк& +&Ябырга +Ярдәм + +&Бару +440 +&БарыÑынга әйе +БарыÑынга юк +Стоп +Яңадан +&Җирлектә +&Ðлга +&Тыныш +Тынышта +Сезгә чынлап киләме операциÑне өзәргә? +500 +&Файл +&Төзәтү +&КыÑфәт +&Сайланма +&Кораллар +&Белешмә +540 +&Ðчырга +&Ðчырга Ñчендә +&Ðчырга тышында +&Карау +&РедакциÑләү +ИÑе&м үзгәрү +&Кабатларга монда... +&Күчәрергә монда... +&Бетерергә +&Ватырга файлны... +Берләштерергә &файлларны... +&Үзлекләр +&Ðңлатма +Тикшерү җыелма +Diff +&ЯÑарга папканы... +&ЯÑарга файлны... +&Чыгу +600 +&Сайларга бөтенеÑене +Сайлануны алырга +&Сайлануны әйләндерергә +Сайларга +Сайлануны алырга +Сайларга төр буенча +Ðлырга Ñайлануны төр буенча +700 +&Зур галәмәтләр +&Вак галәмәтләр +&ИÑемлек +&Җәдвәл +730 +СортлауÑыз +ЯÑÑÑ‹ тарыз +&2 тәрәзә +&Кораллар тактаÑÑ‹ +Ðчырга тамыр папканы +Бер дәрәҗәгә Ó©Ñкәрәк +Элеккеге папкалар... +&Яңартырга +750 +Ðрхивчының кнопкалар тактаÑÑ‹ +Кнопкаларның Ñтандарт тактаÑÑ‹ +Зур кнопкалар +Язмалар кнопкаларда +800 +&Ó¨Ñтәрге папканы Ñайланмага, көбәк: +Китап битбилге +900 +&Көйләр... +&Җитештерүчәнлекне cыналу +960 +&Эчтәлек... +7-Zip &турында... +1003 +Юл +ИÑем +Киңәеш +Папка +Зурлык +КыÑык +Үзенчәлекләр +ЯÑалган +Ðчылган +Үзгәртелгән +Бөтен +Ðңлатма +Шифрланган +Ватылган моңа кадәр +Ðннан Ñоң ватылган +Сүзлек +CRC +Төр +Каршы +ЫÑул +СиÑтема +Файл ÑиÑтема +Кулланучы +төркем +Блок +ТәфÑир +Торыш +Юл +Папка +Файл +Сүрүм +Том +Күп томлы +Күченү +Сылтамалар +Блок +Том + +64-бит +Big-endian +Барыштыручы +Физик Зурлыгы +Сәрләүхәрнең зурлыгы +тикшерү җыелма +Сыйфатламалар +Санал ÐдреÑÑ‹ +ID +КыÑка ИÑеме +ЯÑаучы +Бүлемтекнең Зурлыгы +Тарыз +Сылтама +Ялгыш +Сыемлык +Буш +Күмәк зурлыгы +Тамга +Урындагы иÑеме +Җибәрүче +2100 +Көйләр +Тел +Тел: +Мөхәрир +&Мөхәрир: +&Diff: +2200 +СиÑтема +7-Zip бәйләргә: +2301 +Тыгарга 7-Zip'ны Ñрының контекÑÑ‚ менюга +КаÑкадлы контекÑÑ‚ меню +Баглам меню гонÑырлар: +2320 +<Папка> +<Ðрхив> +Ðрхив ачу +Чишү +Ðрхивка Ó©Ñтәрге +Сынарга +Чишенергә монда +Чишенергә: {0}' папкага +Ó¨Ñтәрге: {0}ка +КыÑып җибәрергә e-mail аша... +КыÑып {0}ка, җибәрергә e-mail аша +2400 +Папкалар +&Эш папка +&СиÑтеманың вакытлы папкаÑÑ‹ +&Ðгымдагы +&КүрÑәтергә: +Файдаланырга алмаш таратучылар өчен генә +Сайлагыз урыны вакытлы архивларга +2500 +Көйләр +КүрÑәтергә гонÑырны".." +КүрÑәтергә чынбарлык файлларның Ñынчыкларны +КүрÑәтергә ÑиÑтем меню +КүрÑәткечне бөтен юлга +КүрÑәтергә бүлгечләрне +Ðчырга чиртү белән +Ðльтернатив билге тарызы +Файдаланырга иÑнең зур битләр белән +2900 +7-Zip программа турында +Программа 7-Zip тараттылыра бушлай +3000 +Буш иÑе җитми +Ялгышлар юк +{0} объект Ñайлаган +Ðрхив ÑÑап булмады: {0} +Бу архив өчен үзгәртү операциÑләр Ñшләмилер. +Файл ачып булмады '{0}' архив кебек +Шифрлы архив ацып булмады '{0}'. Пароль туры килмәде? +Программа бу архив төрә белән Ñшләми +Файл {0} бар инде +Файл '{0}' үзгәргән иде.\nСезгә килә Ñңартырга аны архивта? +Файл Ñңартып булмады\n'{0}' +Мөхәрирне җибәреп булмады. +Файл вируÑка охшаган (файл иÑемендә озын аралар Ñзлеклелеге бар). +ÐžÐ¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð±Ð°ÑˆÐºÐ°Ñ€Ñ‹Ð»Ð³Ð°Ð½Ð³Ð° булдыралмы папкадан озын юл бөлән. +Сез бер файл Ñайларга тиеш +Сез бер Ñ Ð±ÐµÑ€Ð½Ð¸Ñ‡Ó™ файл Ñайларга тиеш +ГонÑырлар артык күп +3300 +Чишү +КыÑу бара +Сынау +Ðчу... +Тарау... +3400 +Чыгарырга +Чишенергә монда: +КүрÑәтегәз чыгара торган файлларга урынны. +3410 +Юллар +&Тулы юллар +ЮлларÑыз +3420 +Ðлмаш +РаÑлау белән +РаÑлауÑыз +Үткәрергә +ИÑем үзгәрү автом. +ИÑем. үзгәр. автом. +3500 +Файл алмаштыруга раÑлау +Эшкертү торган файл папкада бар инде. +Ðлмаштырырга бар файлны +бу файл белән? +{0} байт +ИÑем үзгәрү автом. +3700 +Бу кыÑу Ñ‹Ñулы файл өчен кулланылмы '{0}'. +Ялгыш мәгълүмәтләрдә '{0}'. Файл бозылган. +Ялгыш CRC'да '{0}'. Файл бозылган. +Ялгыш шифрлы файл мәгълүмәтләрдә '{0}'. Пароль дөреÑме? +Ялгыш шифрлы файл CRC'да '{0}'. Пароль дөреÑме? +3800 +Пароль Ñзу +&Языгыз пароль: +&Кабатлагыз парольны: +&КүрÑәтергә пароль +Парольлар тиңÑез +Парольга Ñзыгыз латин әлифбаÑын галәмәтләрне гына, Ñаннар һәм махÑÑƒÑ Ð³Ð°Ð»Ó™Ð¼Ó™Ñ‚Ð»Ó™Ñ€Ð½Ðµ (!, #, $, ...) +Пароль бик озын +Пароль +3900 +Узган: +Калган: +Барлыгы: +Тизлек: +Зурлыгы: +КыÑу катылыгы: +Ялгыш: +Ðрхив: +4000 +Ðрхивка Ó©Ñтәрге +&Ðрхив: +&Үзгәртү тарызы: +Ðрхив форматы: +&КыÑу дәрәҗәÑе: +&КыÑу Ñ‹Ñулы: +&Лөгать зурлыгы: +&Cүз зурлыгы: +Блок зурлыгы: +Ðгымнарның иÑәбе: +&Параметрлар: +ОпциÑләр +ЯÑарга SF&X-архив +КыÑарга Ñздыруга ачкан файлларны +Шифрлау +Шифрлау Ñ‹Ñулы: +&Шифрларга файллар иÑемнәрне +ИÑнең күләме урау өчен: +ИÑнең күләме Чишү өчен: +4050 +КыÑмаÑка +Бик тиз +Тиз +Гадәти +Иң зур +Ультра +4060 +Ó¨Ñтәрге һәм алмаштырырга +Яңартырга һәм Ó©Ñтәрге +Яңартырга +Синхронлаштыру +4070 +Ðктарырга +Бөтен файллар +Файл зурлыгына Ñайларга +ӨзлекÑез +6000 +Кабатларга +Күчәрергә +Кабатларга монда: +Күчәрергә монда: +Кабатлану... +Күчү... +ИÑем үзгәрү... +КүрÑәтегәз папканы. +Бу папка өчен Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ ÐºÑƒÐ»Ð»Ð°Ð½Ñ‹Ð»Ð¼Ñ‹. +Ялгыш файлның\папканың иÑем үзгәрү чакта +Файлларны кабатларга раÑлау +Сезгә чын киләме кабатларга бу файлларны архивка +6100 +Файл бетерүгә раÑлау +Папка бетерүгә раÑлау +Файллар төркемне бетерүгә раÑлау +Сезгә чын киләме бетерергә "{0}"? +Сезгә чын киләме бетерергә папканы "{0}" һәм өчендәге файлларны? +Сезгә чын киләме бетерергә бу объектларны ({0} данә)? +Бетерү... +Ялгыш файлны/папканы бетерүдә +Файллар бетерүне озын юллар белән кәрзингә ÑиÑтема кулланылмы +6300 +ЯÑарга папканы +ЯÑарга файл +Папка иÑеме: +Файл иÑеме: +Яңа папка +Яңа файл +Ялгыш папка ÑÑаганда +Ялгыш файл ÑÑаганда +6400 +Ðңлатма +&Ðңлатма: +Сайларга +Ðлырга Ñайлануны +Үрнәк: +6600 +Үзлекләр +Элеккеге папкалар +Игъланнар +Игълан +7100 +Санак +Челтәр +Кәгазләр +СиÑтема +7200 +Ó¨Ñтәрге +Чыгарырга +Сынарга +Кабатларга +Күчәрергә +Бетерергә +Хәбәр +7300 +Өзергә файлны +&Өзергә монда: +&Ватырга томларга, шундый зурлыгы бөлән, байт: +Өзелү... +Өзелүгә раÑлау +Сезгә чын киләме өзергә файл {0} бүләккә? +Том зурлыгы чыгыш файлдан азрак булырга тиеш +Ялгыш томлар зурлыкны күрÑәтү кырда +Тәгаенле том зурлыгы: {0} байт.\nСезгә чын киләме архив өзергә шундый томларга? +7400 +Берләштерергә файлларны +&Берләштерергә монда: +Берләштерү... +Кирәк Ñайларга өзелгән файлның беренче бүләкне гына +Өзелгән файлны белеп булмады +Өзелгән файлның табылган бер бүләк гәне +7500 +ХиÑаплау тикшерү җыелманы... +Тикшерү җыелма +CRC'ның тикшерү җыелмаÑÑ‹ мәгълүматлар өчән: +CRC'ның тикшерү җыелмаÑÑ‹ мәгълүматлар һәм иÑемнәр өчән: +7600 +Җитештерүчәнлекне cыналу +ИÑнең күләме: +Урау +Чишү +Шөһрәтлелек +Гомуми шөһрәтлелек +Ðгымдагы +Ðәтиҗәле +Куллану +Шөһрәтлелек / Куллану. +Узу: diff --git a/Utils/7-Zip/Lang/ug.txt b/Utils/7-Zip/Lang/ug.txt new file mode 100644 index 000000000..6257c0708 --- /dev/null +++ b/Utils/7-Zip/Lang/ug.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4.59 : Sahran +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Uyghur +ئۇيغۇرچە +401 +جەزملە +ۋاز ÙƒÛ•Ú† + + + +(&Y)ھەئە +ياق(&N) +ياپ(&C) +ياردەم + +داۋاملاشتۇر(&C) +440 +ھەممىسى ھەئە(&A) +ھەممىسى ياق(&L) +توختا +قايتا باشلا +ئارقا سۇپا(&B) +ئالدى سۇپا(&F) +ۋاقىتلىق توختا(&P) +ۋاقىتلىق توختىتىلدى +راستىنلا ۋاز ÙƒÛچەمسىز؟ +500 +ھۆججەت(&F) +تەھرىر(&E) +كۆرۈنۈش(&V) +يىغقۇچ(&A) +قورال(&T) +ياردەم(&H) +540 +ئاچ(&O) +نۆۋەتتىكى كۆزنەكتە ئاچ(&I) +ÙŠÛÚ­Ù‰ كۆزنەكتە ئاچ(&U) +كۆرۈنۈش(&V) +تەھرىر(&E) +ئات ئۆزگەرت(&M) +كۆچۈرۈش ئورنى(&C)… +يۆتكەش ئورنى(&M)… +ئۆچۈر(&D) +ھۆججەت پارچىلا(&S)… +ھۆججەت بىرلەشتۈر(&B)… +خاسلىق(&R) +ئىزاھات(&N) +ھۆججەت دەلىللە + +ÙŠÛÚ­Ù‰ قىسقۇچ +ÙŠÛÚ­Ù‰ ھۆججەت +Ú†Ûكىن(&X) +600 +ھەممىنى تاللا(&A) +ھەممىنى تاللىما +ئەكسىچە تاللا(&I) +تاللا… +ئەكسىچە تاللا… +ئوخشاش تۈردىكى ھۆججەتنى تاللا +ئوخشاش تۈردىكى ھۆججەتنى ئەكسىچە تاللا +700 +Ú†ÙˆÚ­ سىنبەلگە(&G) +كىچىك سىنبەلگە(&M) +تىزىملىك(&L) +تەپسىلىي(&D) +730 +تەرتىپلەنمىگەن +تەكشىلىك كۆرۈنۈش +&2 يۈز +قورال ستونى(&T) +غول قىسقۇچنى ئاچ +يۇقىرىغا +قىسقۇچ تارىخى… +ÙŠÛڭىلا(&R) +750 +پرىس قورال ستونى +ئۆلچەملىك قورال ستونى +Ú†ÙˆÚ­ كۇنۇپكا +كۇنۇپكا Ø®ÛØªÙ‰Ù†Ù‰ كۆرسەت +800 +يىغقۇچقا قوش(&A) +خەتكۈچ +900 +تاللانما(&O)… +ئۆلچەملىك سىناش(&B) +960 +مۇندەرىجە(&C)… +7-Zip (&A)ھەققىدە +1003 +يول +ئاتى +ÙƒÛڭەيتىلگەن ئاتى +قىسقۇچ +چوڭلۇقى +بوغچا چوڭلۇقى +خاسلىق +قۇرغان ۋاقىت +زىيارەت ۋاقتى +ئۆزگەرتكەن ۋاقىت +پۇختا +ئىزاھات +شىÙىرلانغان +ئاۋال پارچىلا +ÙƒÛيىن پارچىلا +لۇغەت +CRC +تىپى +قارشى +ئۇسۇل +ئاساسىي مەشغۇلات سىستÛمىسى +ھۆججەت سىستÛمىسى +ئىشلەتكۈچى +گۇرۇپپا +بۆلەك +ئىزاھات +ئورۇن +يول ئالدى قوشۇلغۇچى +قىسقۇچ +ھۆججەت +نەشرى +ئەن +ÙƒÛ†Ù¾ ئەن +ئورۇن ھالقىش +ئۇلانما +بۆلەك +ئەنلەش + +64-bit +Big-endian +CPU +Ùىزىكىلىق چوڭلۇقى +ھۆججەت باشى چوڭلۇقى +يىغىندا تەكشۈر +ئالاھىدىلىك +Ù…Û•Û‹Ú¾Û‡Ù… Ø¦Ø§Ø¯Ø±ÛØ³ + + + + + + +خاتالىق +ئومۇمى سىغىمى +ئىشلىتىلىشچان بوشلۇق +توپلاشتۇرغۇچ چوڭلۇقى +ئەن +يەرلىك ئاتى +تەمىنلىگۈچى +2100 +تاللانما +تىل +تىل: +تەھرىرلىگۈچ +تەھرىرلىگۈچ(&E): + +2200 +سىستÛما +7-Zip بىلەن باغلانغان ھۆججەت تىپى: +2301 +7-Zip نى ئوڭ كۇنۇپكا تىزىملىكىگە قوش +ئوڭ تىزىملىكنى دەستىلە +ئوڭ تىزىملىكتە كۆرۈنىدىغان تۈرنى تاللاش +2320 +<قىسقۇچ> +<پرىس> +پرىس ئاچ +ھۆججەت يەش… +پرىسقا قوش +پرىس سىنا +مۇشۇ يەرگە ÙŠÛ•Ø´ +{0} غا يەش‪‬ +{0}‬ غا قوش ‪ +پرىس Û‹Û• ئÛلخەت… +‬ غا پرىسلاپ ئÛلخەتتە يوللا +2400 +قىسقۇچ +خىزمەت مۇندەرىجىسى(&W) +(&S)سىستÛما ۋاقىتلىق قىسقۇچ +(&C)نۆۋەتتىكى +(&S)بەلگىلەنگەن قىسقۇچ: +يان دىسكىغىلا ئىشلىتىلىدۇ +پرىس ھۆججىتىنى ÙŠÛØ´Ù‰Ø¯Ù‰ØºØ§Ù† ۋاقىتلىق ئورۇندىن بىرنى بەلگىلەڭ. +2500 +تەڭشەك +كۆرسەت“..â€ØªÛˆØ± +ھەقىقىي ھۆججەت سىنبەلگىسى كۆرسەت +سىستÛما تىزىملىكىنى كۆرسەت +پۈتۈن قۇر تاللا(&F) +Ø³ÛØªÙƒØ§ كۆرسەت(&G) + +شەيئى تاللاش مودىلى(&A) +Ú†ÙˆÚ­ ئەسلەك Ø¨ÛØªÙ‰ ئىشلەت(&L) +2900 +‎7-Zip†ھەققىدە +â€7-Zip â€Ú¾Û•قسىز Ø¯ÛØªØ§Ù„. ئەمما تىزىملىتىش ئارقىلىق ئۇنى ئÛچىشنى قوللىسىڭىز بولىدۇ. +3000 +سىستÛما لازىملىق ئەسلەكنى تەقسىملىيەلمەيدۇ +خاتالىق يوق +{0} تۈر تاللاندى +“{0}†قىسقۇچ قۇرالمايدۇ +بۇ پرىس ÙŠÛڭىلاش مەشغۇلاتىنى قوللىمايدۇ +'{0}' ھۆججەتنى پرىس سۈپىتىدە ئاچالمايدۇ +'{0}' شىÙىرلانغان پرىسنى ئاچالمايدۇ. ئىم خاتا +قوللىمايدىغان پرىس تÛپى +{0} ھۆججەت مەۋجۇد +“{0}â€Ø¦Û†Ø²Ú¯Û•رتىلدى\nپرىس ھۆججىتىدە ÙŠÛڭىلامسىز؟ +“{0}â€Ú¾Û†Ø¬Ø¬Û•تنى ÙŠÛڭىلىيالمىدى\n +تەھرىرلىگۈچنى قوزغىتالمىدى +بۇ ھۆججەت ۋىرۇستەك تۇرىدۇ (ھۆججەت ئاتىدا ÙƒÛ†Ù¾ بوشلۇق بار) +يولى ئۇزۇن قىسقۇچقا بۇ مەشغۇلاتنى ئÛلىپ بارالمايدۇ. +چوقۇم ھۆججەتتىن بىرنى تاللاڭ +چوقۇم بىر ياكى بىر قانچە ھۆججەت تاللاڭ +تۈر بەك ÙƒÛ†Ù¾ +3300 +ÙŠÛØ´Ù‰Û‹Ø§ØªÙ‰Ø¯Û‡ +پرىسلاۋاتىدۇ +سىناۋاتىدۇ +ئÛچىۋاتىدۇ… +ئىزدەۋاتىدۇ… +3400 +ÙŠÛ•Ø´ +ÙŠÛØ´Ù‰Ø´ ئورنى(&X): +ھۆججەت ÙŠÛØ´Ù‰Ø¯Ù‰ØºØ§Ù† جايدىن بىرنى كۆرسىتىڭ +3410 +يول مودÛلى +تولۇق يول ئاتى +يول ئاتى يوق +3420 +قاپلاش مودÛلى +قاپلاشتىن بۇرۇن سورا +ئەسكەرتمەي قاپلا +مەۋجۇد ھۆججەتتىن ئاتلا +ئۆزلۈكىدىن ئاتىنى ئۆزگەرت +مەۋجۇد ھۆججەت ئاتىنى ئۆزگەرت +3500 +ھۆججەت ئالماشتۇرۇشنى جەزملە +بۇ قىسقۇچ ئوخشاش ئاتلىق ھۆججەتتىن بىرنى ئۆز ئىچىگە ئالغان +بۇنىڭغا مەۋجۇد ھۆججەتنى +ئالماشتۇرامسىز؟ +{0} بايت +ئۆزلۈكىدىن ئات ئۆزگەرت(&U) +3700 +{0} قوللىمايدىغان پرىسلاش مودÛلى +“{0}†سانلىق مەلۇمات خاتا. ھۆججەت بۇزۇلغان +“{0}†ئورۇندىكى CRC تەكشۈرۈش مەغلۇپ بولدى، ھۆججەت بۇزۇلغان +»{0}« شىÙىرلانغان ھۆججەت سانلىق مەلۇماتىدا خاتالىق بار، ئىم خاتا. +“{0}†شىÙىرلانغان ھۆججەت CRC سانلىق مەلۇمات دەلىللەشتە خاتالىق بار، ئىم خاتا. +3800 +ئىم كىرگۈزۈڭ +ئىم كىرگۈزۈڭ: +ئىمنى قايتا كىرگۈزۈڭ +ئىم كۆرسەت(&S) +ئىم ماس كەلمىدى +(!ã€#ã€$...)ئىمغا ئىنگلىزچە ھەرپ، سان Û‹Û• ئالاھىدە ھەرپ-بەلگىلەرلا ئىشلىتىلىدۇ +ئىم بەك ئۇزۇن +ئىم +3900 +كەتكەن ۋاقىت: +قالغان ۋاقىت: +ئومۇمىي چوڭلۇقى: +سۈرئىتى: +بىر تەرەپ قىلىندى: +پرىس نىسبىتى: +خاتالىق: +پرىس: +4000 +پرىسقا قوش +پرىس(&A): +ÙŠÛڭىلاش مودÛلى(&U): +پرىسلاش شەكلى(&F): +پرىسلاش دەرىجىسى(&L): +پرىسلاش مودÛلى(&M): +لۇغەت چوڭلۇقى(&D): +سۆز چوڭلۇقى(&W): +مۇقىم سانلىق مەلۇمات چوڭلۇقى: +CPU ئÛقىم سانى : +Ù¾Ø§Ø±Ø§Ù…ÛØªÙ‰Ø±(&P): +تاللانما +ئۆزى ÙŠÛØ´Ù‰Ù„ىدىغان پرىس ياسا(&X) +ھەمبەھىر ھۆججەت پرىسلا +شىÙىرلاش +شىÙىرلاش ئۇسۇلى: +شىÙىرلىق ھۆججەت ئاتى(&N) +پرىسلاشقا ÙƒÛØ±Û•كلىك ئەسلەك: +ÙŠÛØ´Ù‰Ø´ÙƒÛ• ÙƒÛØ±Û•كلىك ئەسلەك: +4050 +ساقلا +ئەڭ ØªÛØ² +ØªÛØ² +نورمال +ئەڭ Ú†ÙˆÚ­ +ئەڭ زور چەكتە +4060 +ھۆججەت قوش Û‹Û• ئالماشتۇر +ھۆججەت ÙŠÛڭىلا Û‹Û• قوش +مەۋجۇد ھۆججەتنى ÙŠÛڭىلا +ھۆججەت قەدەمداشلا +4070 +كۆز يۈگۈرت +ھەممە ھۆججەت +مۇقىمسىز +مۇقىم +6000 +كۆچۈر +يۆتكە +كۆچۈرۈش ئورنى: +يۆتكەش ئورنى: +كۆچۈرۈۋاتىدۇ… +يۆتكەۋاتىدۇ… +ئاتىنى ئۆزگەرتىۋاتىدۇ… +نىشان قىسقۇچ تاللاڭ +نۆۋەتتىكى مەشغۇلاتنى قوللىمايدۇ +ھۆججەت ياكى قىسقۇچ ئاتىنى ئۆزگەرتىش خاتالىقى +ھۆججەت كۆچۈرۈشنى جەزملە +ھۆججەتنى پرىسقا راستىنلا كۆچۈرەمسىز؟ +6100 +ھۆججەت ئۆچۈرۈشنى جەزملە +قىسقۇچ ئۆچۈرۈشنى جەزملە +ÙƒÛ†Ù¾ ھۆججەت ئۆچۈرۈشنى جەزملە +“{0}†راستىنلا ئۆچۈرەمسىز؟ +“{0}†قىسقۇچ Û‹Û• مەزمۇننى راستىنلا ئۆچۈرەمسىز؟ +{0} تۈرنى راستىنلا ئۆچۈرەمسىز؟ +ئۆچۈرۈۋاتىدۇ… +قىسقۇچ ياكى ھۆججەت ئۆچۈرۈش خاتالىقى +سىستÛما يولى ئۇزۇن بولغان ھۆججەتنى ئەخلەتخاناغا يۆتكىيەلمەيدۇ +6300 +قىسقۇچ قۇر +ھۆججەت قۇر +قىسقۇچ ئاتى +ھۆججەت ئاتى +ÙŠÛÚ­Ù‰ قىسقۇچ +ÙŠÛÚ­Ù‰ ھۆججەت +قىسقۇچ قۇرۇش خاتالىقى +ھۆججەت قۇرۇش خاتالىقى +6400 +ئىزاھات +ئىزاھات(&C) +تاللاش +ئەكسىچە تاللا +ماسكا: +6600 +خاسلىق +قىسقۇچ تارىخى +دىئاگنوز ئۇچۇرى +ئۇچۇر +7100 +ÙƒÙˆÙ…Ù¾ÙŠÛ‡ØªÛØ± +تور قوشنا +پۈتۈكلەر +سىستÛما +7200 +قوش +ÙŠÛ•Ø´ +سىنا +كۆچۈر +يۆتكە +ئۆچۈر +ئۇچۇر +7300 +ھۆججەت پارچىلا +پارچىلاش سانى(&S): +پارچە چوڭلۇقى، بايت(&V): +پارچىلاۋاتىدۇ… +پارچىلاشنى جەزملە +ھۆججەتنى {0} پارچىغا بۆلەمسىز؟ +پارچە چوڭلۇقى چوقۇم ئەسلى ھۆججەتتىن كىچىك بولۇشى لازىم +پارچە چوڭلۇقى خاتا +بايت{0} بەلگىلەنگەن پارچە چوڭلۇقى\nنۆۋەتتىكى ھۆججەتنى پارچىلامسىز؟ +7400 +ھۆججەت بىرلەشتۈر +بىرلەشتۈرۈش(&C): +بىرلەشتۈرۈۋاتىدۇ… +بىرىنچى ھۆججەتنىلا تاللا +بۆلەكلەنگەن ھۆججەت پارچىسى ئىكەنلىكىنى بايقىيالمىدى +باشقا ھۆججەت پارچىسىنى بايقىيالمىدى +7500 +تەكشۈرۈۋاتىدۇ… +تەكشۈرۈش ئۇچۇرى +CRC سانلىق مەلۇمات تەكشۈرۈش: +CRC سانلىق مەلۇمات Û‹Û• ھۆججەت ئاتى تەكشۈرۈش: +7600 +ئاساسىي تەكشۈرۈش +ئىشلىتىلگەن ئەسلەك: +پرىسلاۋاتىدۇ +ÙŠÛØ´Ù‰Û‹Ø§ØªÙ‰Ø¯Û‡ +سۈرئىتى +ئوتتۇرىچە سۈرئىتى +نۆۋەتتە +نەتىجە +CPU ئىشلىتىلىشى +ئىشلىتىش سۈرئىتى +يوللاش: diff --git a/Utils/7-Zip/Lang/uk.txt b/Utils/7-Zip/Lang/uk.txt new file mode 100644 index 000000000..885f30389 --- /dev/null +++ b/Utils/7-Zip/Lang/uk.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; : Andrij Ilechko +; : Mokiy Mazaylo +; : Sergiy Gontaruk +; : Misha Padalka +; 15.02 : 2015-05-19 : Yurii Petrashko +; +; +; +; +; +; +0 +7-Zip +Ukrainian +УкраїнÑька +401 +OK +СкаÑувати + + + +&Так +&ÐÑ– +&Закрити +Довідка + +&Продовжити +440 +Так Ð´Ð»Ñ &вÑÑ–Ñ… +ÐÑ– Ð´Ð»Ñ Ð²Ñ&Ñ–Ñ… +Зупинити +ПерезапуÑтити +&Ðа задньому плані +&Ðа передньому плані +&Пауза +Призупинено +Ви впевнені, що бажаєте ÑкаÑувати операцію? +500 +&Файл +&Ð ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ +&ВиглÑд +&Ð£Ð¿Ð¾Ð´Ð¾Ð±Ð°Ð½Ð½Ñ +&ІнÑтрументи +&Допомога +540 +&Відкрити +Відкрити в&Ñередині +Відкрити &зовні +&ПереглÑнути +&Редагувати +Пере&йменувати +&Копіювати до... +Пере&міÑтити до... +Ви&далити +Роз&бити файл... +Об'&єднати файли... +Ð’&лаÑтивоÑті +Комент&ар +ОбчиÑлити контрольну Ñуму +Порівнювач +Створити папку +Створити файл +Ви&хід +ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ +Ðл&ьтернативні потоки +600 +Ви&брати вÑе +ЗнÑти вибір +&Інвертувати вибір +Вибрати... +ЗнÑти вибір... +Вибрати за типом +ЗнÑти вибір за типом +700 +Вели&кі піктограми +&Дрібні піктограми +&СпиÑок +&Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ +730 +Без ÑÐ¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ +ПлоÑкий виглÑд +&2 панелі +&Панелі інÑтрументів +Відкрити кореневу папку +Вище на один рівень +ІÑÑ‚Ð¾Ñ€Ñ–Ñ Ð¿Ð°Ð¿Ð¾Ðº... +&Оновити +ÐÐ²Ñ‚Ð¾Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ +750 +Панель архіву +Стандартна панель +Великі кнопки +ТекÑÑ‚ на кнопках +800 +&Додати папку до вподобань Ñк +Закладка +900 +&ÐалаштуваннÑ... +&ТеÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾Ñті +960 +&ЗміÑÑ‚... +&Про 7-Zip... +1003 +ШлÑÑ… +Ім'Ñ +Ð Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ +Папка +Розмір +Розмір в архіві +Ðтрибути +Створено +Відкрито +Змінено +Ðеперервний +З коментарем +Зашифровано +Розбито до +Розбито піÑÐ»Ñ +Словник + +Тип +Ðнти +Метод +ÐŸÐ¾Ñ…Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ +Файлова ÑиÑтема +КориÑтувач +Група +Блок +Коментар +ÐŸÐ¾Ð·Ð¸Ñ†Ñ–Ñ +ÐŸÑ€ÐµÑ„Ñ–ÐºÑ ÑˆÐ»Ñху +Папок +Файлів +ВерÑÑ–Ñ +Том +Багатотомний +ЗÑув +ПоÑилань +Блоків +ЧаÑтин + + + +ПроцеÑор +Фізичний розмір +Розмір заголовків +Контрольна Ñума +ВлаÑтивоÑті +Віртуальна адреÑа + +Коротке ім'Ñ +Створено програмою +Розмір Ñектора +Режим +ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ +Помилка +Загальний обÑÑг +Вільний проÑтір +Розмір клаÑтеру +Мітка +Локальне ім'Ñ +Провайдер +Безбека NT +Ðльтернативний потік + +Видалено +Дерево + + +Тип помилки +Помилки +Помилки +ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ +ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ +Потоки +Ðльтернативні потоки +Розмір альтернативних потоків +Віртуальний розмір +Розпакований розмір +Загальний фізичний розмір +Ð†Ð½Ð´ÐµÐºÑ Ñ‚Ð¾Ð¼Ñƒ +Підтип +Короткий коментар +Кодова Ñторінка + + + +Розмір залишку +Розмір вбудованої заглушки +ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ +ЖорÑтке поÑÐ¸Ð»Ð°Ð½Ð½Ñ +iNode + +Лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ +2100 +Опції +Мова +Мова: +Ð ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ +&Редактор: +&Порівнювач: +2200 +СиÑтема +ÐÑоціювати 7-Zip з: +УÑÑ– кориÑтувачі +2301 +Інтегрувати 7-Zip до контекÑтного меню оболонки +КаÑкадне контекÑтне меню +Пункти контекÑтного меню: +Піктограми в контекÑтному меню +2320 +<Папка> +<Ðрхів> +Відкрити архів +Видобути файли... +Додати до архіву... +ТеÑтувати архів +Видобути до поточної папки +Видобути до {0} +Додати до {0} +СтиÑнути та надіÑлати... +СтиÑнути до {0} та надіÑлати +2400 +Папки +&Робоча папка +&СиÑтемна тимчаÑова папка +&Поточна +&Задати: +ВикориÑтовувати тільки Ð´Ð»Ñ Ð·Ð¼Ñ–Ð½Ð½Ð¸Ñ… ноÑіїв +Вкажіть Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð¸Ð¼Ñ‡Ð°Ñових архівних файлів. +2500 +ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ +Відображати елемент ".." +Відображати Ñправжні піктограми файлів +Відображати ÑиÑтемне меню +Вибір &цілого Ñ€Ñдка +Відображати лінії &Ñітки +Відкривати об'єкти одним кліком +&Ðльтернативний режим Ð²Ð¸Ð´Ñ–Ð»ÐµÐ½Ð½Ñ +ВикориÑтовувати &великі Ñторінки пам'Ñті +2900 +Про 7-Zip +7-Zip Ñ” вільним програмним забезпеченнÑм +3000 +СиÑтема не може виділити необхідний обÑÑг пам'Ñті +Без помилок +Обрано об'єктів: {0} +Ðе вдаєтьÑÑ Ñтворити папку '{0}' +ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ðµ підтримуєтьÑÑ Ð´Ð»Ñ Ð´Ð°Ð½Ð¾Ð³Ð¾ архіву. +Ðе вдаєтьÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл '{0}' Ñк архів +Ðе вдаєтьÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ зашифрований архів '{0}'. Хибний пароль? +Ðепідтримуватий тип архіву +Файл {0} вже Ñ–Ñнує +Файл '{0}' було змінено.\nБажаєте оновити його в архіві? +Ðеможливо оновити файл\n'{0}' +Ðе вдаєтьÑÑ Ð·Ð°Ð¿ÑƒÑтити редактор. +Файл виглÑдає Ñк Ð²Ñ–Ñ€ÑƒÑ (ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ міÑтить довгу поÑлідовніÑть пробілів). +Операцію не можна викликати з папки, Ñка має довгий шлÑÑ…. +Ви повинні вибрати один файл +Ви повинні вибрати один або декілька файлів +Забагато елементів +Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл Ñк {0} архів +Файл відкрито Ñк {0} архів +Ðрхів відкрито зі зÑувом +3300 +Ð’Ð¸Ð´Ð¾Ð±ÑƒÐ²Ð°Ð½Ð½Ñ +СтиÑÐ½ÐµÐ½Ð½Ñ +ТеÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ +ВідкриттÑ... +СкануваннÑ... +Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ +3320 +Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ +ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ +Ðналіз +Ð ÐµÐ¿Ð»Ñ–ÐºÐ°Ñ†Ñ–Ñ +ÐŸÐµÑ€ÐµÐ¿Ð°ÐºÑƒÐ²Ð°Ð½Ð½Ñ +ПропуÑк +Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ +Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÑ–Ð² +3400 +Видобути +Ð’&идобути до: +Вкажіть Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð²Ð¸Ð´Ð¾Ð±ÑƒÑ‚Ð¸Ñ… файлів. +3410 +Обробка шлÑхів +Повні шлÑхи +Без шлÑхів +ÐбÑолютні шлÑхи +ВідноÑні шлÑхи +3420 +Режим перезапиÑу +Запитувати перед перезапиÑом +ПерезапиÑувати без запиту +ПропуÑкати Ñ–Ñнуючі файли +Ðвтоматично перейменовувати +Ðвтоматично перейменовувати Ñ–Ñнуючі файли +3430 +УÑувати Ð´ÑƒÐ±Ð»ÑŽÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€ÐµÐ½ÐµÐ²Ð¾Ñ— папки +ВідновлÑти дані безпеки файлу +3500 +Підтвердіть заміну файлу +Папка Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð¶Ðµ міÑтить оброблюваний файл. +Бажаєте замінити Ñ–Ñнуючий файл +на такий? +{0} байт +&Ðвтоматично перейменовувати +3700 +Ðепідтривуваний метод ÑтиÑÐ½ÐµÐ½Ð½Ñ Ð´Ð»Ñ '{0}'. +Помилка даних у '{0}'. Файл пошкоджено. +Помилка CRC у '{0}'. Файл пошкоджено. +Помилка даних у зашифрованому файлі '{0}'. Хибний пароль? +Помилка CRC у зашифрованому файлі '{0}'. Хибний пароль? +3710 +Хибний пароль? +3721 +Ðепідтримуваний метод ÑтиÑÐ½ÐµÐ½Ð½Ñ +Помилка даних +Помилка CRC +ÐедоÑтупні дані +Ðеочікуваний кінець даних +ІÑнують деÑкі дані піÑÐ»Ñ Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñних даних +Ðе Ñ” архівом +Помилка заголовків +Ðеправильний пароль +3763 +ÐедоÑтупний початок архіву +Ðепідтверджений початок архіву + + + +Ðепідтримувана Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ +3800 +Уведіть пароль +Уведіть пароль: +Повторіть пароль: +&Відображати пароль +Паролі не Ñпівпадають +Ð”Ð»Ñ Ð¿Ð°Ñ€Ð¾Ð»ÑŽ викориÑтовуйте лише англійÑькі літери, цифри та Ñпеціальні Ñимволи (!, #, $, ...) +Пароль занадто довгий +Пароль +3900 +Минуло чаÑу: +ЗалишилоÑÑ: +Загалом: +ШвидкіÑть: +Оброблено: +Ступінь ÑтиÑненнÑ: +Помилок: +Ðрхівів: +4000 +Додати до архіву +&Ðрхів: +&Режим оновленнÑ: +&Формат архіву: +С&тупінь ÑтиÑненнÑ: +&Метод ÑтиÑканнÑ: +&Розмір Ñловника: +Р&озмір Ñлова: +Розмір блоку: +КількіÑть потоків: +&Параметри: +ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ +&Створити SFX архів +СтиÑкати Ñпільні файли +Ð¨Ð¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ +Метод шифруваннÑ: +Шифрувати &імена файлів +Ðеобхідно пам'Ñті Ð´Ð»Ñ ÑтиÑканнÑ: +Ðеобхідно пам'Ñті Ð´Ð»Ñ Ð²Ð¸Ð´Ð¾Ð±ÑƒÐ²Ð°Ð½Ð½Ñ: +Видалити файли піÑÐ»Ñ ÑтиÑÐ½ÐµÐ½Ð½Ñ +4040 +Зберігати Ñимволічні поÑÐ¸Ð»Ð°Ð½Ð½Ñ +Зберігати жорÑткі поÑÐ¸Ð»Ð°Ð½Ð½Ñ +Зберігати альтернативні потоки даних +Зберігати дані безпеки файлу +4050 +Без ÑтиÑÐ½ÐµÐ½Ð½Ñ +Ðайшвидше +Швидке +Ðормальне +МакÑимальне +Ультра +4060 +Додати та замінити файли +Оновити та замінити файли +Оновити Ñ–Ñнуючі файли +Синхронізувати файли +4070 +ПереглÑнути +УÑÑ– файли +За розміром файлу +Ðеперервний +6000 +Копіювати +ПереміÑтити +Копіювати до: +ПереміÑтити до: +КопіюваннÑ... +ПереміщеннÑ... +ПерейменуваннÑ... +Виберіть папку призначеннÑ. +ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ підтримуєтьÑÑ Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— папки. +Помилка Ð¿ÐµÑ€ÐµÐ¹Ð¼ÐµÐ½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ або папки +Підтвердіть ÐºÐ¾Ð¿Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ +Ви впевнені, що хочете Ñкопіювати файли до архіву +6100 +Підтвердіть Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ +Підтвердіть Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¿Ð°Ð¿ÐºÐ¸ +Підтвердіть Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð´ÐµÐºÑ–Ð»ÑŒÐºÐ¾Ñ… файлів +Ви впевнені, що хочете видалити '{0}'? +Ви впевнені, що хочете видалити папку '{0}' Ñ– веÑÑŒ Ñ—Ñ— вміÑÑ‚? +Ви впевнені, що хочете видалити ці елементи ({0} шт.)? +ВидаленнÑ... +Помилка при видаленні файлу або папки +СиÑтемі не вдалоÑÑ Ð¿ÐµÑ€ÐµÐ¼Ñ–Ñтити файл із довгим шлÑхом до Кошика +6300 +Створити папку +Створити файл +Ім'Ñ Ð¿Ð°Ð¿ÐºÐ¸: +Ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ: +Ðова папка +Ðовий файл +Помилка при Ñтворенні папки +Помилка при Ñтворенні файлу +6400 +Коментар +&Коментар: +Вибрати +ЗнÑти вибір +МаÑка: +6600 +ВлаÑтивоÑті +ІÑÑ‚Ð¾Ñ€Ñ–Ñ Ð¿Ð°Ð¿Ð¾Ðº +ДіагноÑтичні Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ +ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ +7100 +Комп'ютер +Мережа +Документи +СиÑтема +7200 +Додати +Видобути +ТеÑтувати +Копіювати +ПереміÑтити +Видалити +Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ +7300 +Розбити файл +&Розбити до: +Розбити на &томи розміром, байт: +РозбиттÑ... +Підтвердіть Ñ€Ð¾Ð·Ð±Ð¸Ñ‚Ñ‚Ñ +Ви впевнені, що бажаєте розбити архів на {0} томів? +Розмір тому має бути меншим за розмір вихідного файлу +Ðеправильний розмір тому +Задано розмір тому: {0} байт.\nВи впевнені, що бажаєте розбити архів на такі томи? +7400 +Об'єднати файли +&Об'єднати до: +Об'єднаннÑ... +Виберіть тільки першу чаÑтину розбитого файлу +Ðе вдалоÑÑ Ð²Ð¸Ð·Ð½Ð°Ñ‡Ð¸Ñ‚Ð¸ файл, Ñк чаÑтину розбитого файлу +Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ більше однієї чаÑтини розбитого файлу +7500 +ОбчиÑÐ»ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ñ— Ñуми... +Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ контрольну Ñуму +Контрольна Ñума CRC Ð´Ð»Ñ Ð´Ð°Ð½Ð¸Ñ…: +Контрольна Ñума CRC Ð´Ð»Ñ Ð´Ð°Ð½Ð¸Ñ… та імен: +7600 +ТеÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾Ñті +ВикориÑтано пам'Ñті: +СтиÑÐºÐ°Ð½Ð½Ñ +Ð’Ð¸Ð´Ð¾Ð±ÑƒÐ²Ð°Ð½Ð½Ñ +Рейтинг +Загальний рейтинг +Поточні Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ +ПідÑумкові Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ +Завант. ЦП +Рейтинг/Завант. +Проходів: +7700 +ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ +Пов'Ñзати +Джерело: +Мета: +7710 +Тип поÑÐ¸Ð»Ð°Ð½Ð½Ñ +ЖорÑтке поÑÐ¸Ð»Ð°Ð½Ð½Ñ +Символічне поÑÐ¸Ð»Ð°Ð½Ð½Ñ (файл) +Символічне поÑÐ¸Ð»Ð°Ð½Ð½Ñ (каталог) +Точка з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ (каталог) diff --git a/Utils/7-Zip/Lang/uz.txt b/Utils/7-Zip/Lang/uz.txt new file mode 100644 index 000000000..71762c0ad --- /dev/null +++ b/Utils/7-Zip/Lang/uz.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 9.07 : Sherzod Mamatkulov +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Uzbek +O'zbek +401 +OK +Bekor + + + +&Ha +&Yo'q +&Yopish +Yordam + +&Davom et +440 +H&ammasiga ha +Hammasiga y&o'q +To'xta +Qayta boshla +&Orqa fon +Ol&di fon +&Pauza +Pauza qilingan +Haqiqatdan ham bekor qilishni istaysizmi? +500 +&Fayl +&Tahrir +&Ko'rinish +&Xatcho'plar +&Asboblar +&Yordam +540 +&Ochish +&Ichkarida ochish +&Tashqarida ochish +&Ko'rish +Ta&hrirlash +&Qayta nomlash +&Nusxalash... +Ko'chi&rish... +O'chirish +&Faylni bo'laklash... +Fayllarni &birlashtirish... +&Xossalari +&Sharh... +Nazorat summasini hisoblash +Farq +Papka yaratish +Fayl yaratish +&Chiqish +600 +H&ammasini tanla +Hammasini tashla +&Tanlanishni teskarila +Tanlash... +Tashlash... +Turi bo'yicha tanla +Turi bo'yicha tashla +700 +&Yirik ikonlar +&Mitti ikonlar +&Ro'yxat +&Tafsilotlar +730 +Saralanmagan +Tekis ko'rinish +&2 ta panel +&Uskunalar majmuasi +Ildiz papkasini och +Bir bosqich yuqoriga +Papkalar tarixi... +&Qayta och +750 +Arxiv toolbari +Standart toolbar +Yirik tugmalar +Tugmalar matnini ko'rsat +800 +&Papkani ushbu xatcho'pga qo'sh +Xatcho'p +900 +Tanl&ovlar... +&Baholash +960 +Yordam &tarkibi... +&7-Zip haqida... +1003 +Yo'lak +Nomi +Kengaytma +Papka +Hajmi +Siqilgan hajmi +Atributlari +Yaratilgan +Ochilgan +O'zgartirilgan +Yaxlit +Sharhlangan +Tilsimlangan +Bundan oldin bo'laklangan +Bundan keyin bo'laklangan +Lug'at +CRC +Turi +Anti +Uslub +Mezbon OS +Fayl tizimi +Foydalanuvchi +Guruh +Block +Sharh +Joylashuv +Yo'lak prefiksi +Papkalar soni +Fayllar soni +Versiya +Tom +Ko'p-tomli +Offset +Ulanmalar +Bloklar soni +Tomlar soni + +64-bit +Big-endian +Protsessor +Fizik hajmi +Header hajmi +Nazorat summasi +Tavsifi +Virtual adresi +ID +Qisqa nomi +Yaratgan dastur +Sektor hajmi +Uslubi +Ulanma +Xato +Umumiy hajmi +Bo'sh joy +Klaster hajmi +Yorliq +Lokal nomi +Provayder +2100 +Tanlovlar +Til +Til: +Muharrir +&Muharrir: +&Farq: +2200 +Tizim +Ushbu arxivlarni 7-Zip bilan biriktir: +2301 +7-Zipni qobiq kontekst menyusiga qo'sh +Kontekst menyusi pog'onali bo'lsin +Kontekst menyu bandlari: +2320 + + +Arxivni och +Fayllarni ajrat... +Arxivga qo'sh... +Arxivni sina +Shu joyga ajrat +{0}ga ajrat +{0}ga qo'sh +Siq va emailda jo'nat... +{0}ga siq va emailda jo'nat +2400 +Papkalar +&Ish papkasi +Tizim &vaqtinchalik (temp) papkasi +&Joriy papka +&Tayinlangan: +Faqat olinadigan drayvlar uchun ishlat +Vaqtinchalik arxiv fayllari uchun manzilni tayinlang. +2500 +Sharoit +".." qismini ko'rsat +Fayllarning haqiqiy ikonlarini ko'rsat +Tizim menyusini ko'rsat +&To'liq yo'lakni tanlash +&Panjara chiziqlarini ko'rsat +&Faylni ochish uchun bitta klik yetarli +&Muqobil tanlash uslubini qo'lla +&Yirik xotira pageini ishlat +2900 +7-Zip haqida +7-Zip - bepul dasturiy ta'minot. +3000 +Tizim kerakli miqdordagi xotirani band qila olmadi +Hechqanday xatolik aniqlanmadi +{0} ta qism tanlangan +'{0}' papkasini yarata olmadim +Bu arxivga nisbatan yangilash amallari bajarilmaydi. +'{0}' faylini arxiv sifatida ocha olmadim +Tilsimlangan '{0}' arxivni ocha olmadim. Noto'g'ri parol kiritildimi? +Noma'lum arxiv turi +{0} fayli oldindan mavjud +'{0}' fayli o'zgartirildi.\nUni arxiv ichida yangilashni xohlaysizmi? +Ushbu faylni yangilay olmadim\n'{0}' +Muharrirni ocha olmadim. +Bu fayl virusga o'xshaydi (fayl nomida uzun bo'shliq bor). +Bu amalni uzun yo'lakli papkada chaqirib bo'lmaydi. +Bitta faylni tanlashingiz lozim +Bitta yoki undan ortiq faylni tanlashingiz lozim +Qismlar o'ta ko'p +3300 +Ajratilmoqda +Siquv ketmoqda +sinaldi +Ochilmoqda... +Tekshirilmoqda... +3400 +Ajratish +&Buyerga ajrat: +Ajratilgan fayllar uchun manzil tayinlang. +3410 +Yo'lak uslubi +To'liq yo'lak nomlari +Yo'lak nomi yo'q +3420 +Ustidan yozish uslubi +Yozib yuborishdan oldin so'ra +So'ramasdan yozib yubor +Oldindan mavjud fayllarni tashlab ket +Avtomatik tarzda qayta nomla +Mavjud fayllarni avto qayta nomla +3500 +Fayl ustidan yozishni tasdiqlash +Manziliy papkada ishlangan fayl oldindan mavjud. +Mavjud faylni +bunisi bilan almashtirasizmi? +{0} bayt +A&vtomatik tarzda qayta nomla +3700 +'{0}'dagi siquv metodi notanish. +'{0}'da data xatosi yuz berdi. Fayl zararlangan. +'{0}'da CRC amalga oshmadi. Fayl zararlangan. +Tilsimlangan '{0}' faylida data xatosi yuz berdi. Parol noto'g'ri shekilli +Tilsimlangan '{0}' faylida CRC amalga oshmadi. Parol noto'g'ri shekilli +3800 +Parolni kiritish +Parolni kiriting: +Parolni qayta kiriting: +&Parolni ko'rsat +Parollar bir xil emas +Parol uchun faqat lotincha harflar, raqam va maxsus belgilar (!, #, $, ...) ishlating +Parol haddan tashqari uzun +Parol +3900 +O'tgan vaqt: +Qolgan vaqt: +Jami hajmi: +Tezlik: +Ishlandi: +Siquv nisbati: +Xatolar: +Arxivlar: +4000 +Arxivga qo'shish +&Arxiv: +&Yangilash uslubi: +Arxiv &formati: +Siq&uv bosqichi: +&Siquv metodi: +&Lug'at hajmi: +So'z haj&mi: +Yaxlit blok hajmi: +CPU oqimlari soni: +&Parametrlar: +Tanlovlar +SF&X arxiv yarat +Baham ko'rilgan fayllarni ham siq +Tilsimlash +Tilsimlash metodi: +Fayl &nomlarini tilsimla +Siquvda ishlatiladigan xotira: +Ajratuvda ishlatiladigan xotira: +4050 +Saqlash +Eng tez +Tez +O'rtacha +Eng zo'r +Ultra +4060 +Fayllarni qo'sh va almashtir +Fayllarni yangila va qo'sh +Mavjud fayllarni yangila +Fayllarni sinxronla +4070 +Belgila +Barcha fayllar +No-yaxlit +Yaxlit +6000 +Nusxalash +Ko'chirish +Ushbu katalogga nusxala: +Ushbu katalogga ko'chir: +Nusxalanmoqda... +Ko'chirilmoqda... +Qayta nomlanmoqda... +Manziliy papkani tanlang. +Ushbu papkaga nisbatan bu amalni qo'llab bo'lmaydi +Fayl yoki papkani qayta nomlashda xato yuz berdi +Faylni nusxalashni tasdiqlang +Haqiqatdan ham fayllarni arxivga nusxalashni xohlaysizmi +6100 +Faylni o'chirishni tasdiqlang +Papkani o'chirishni tasdiqlang +Ko'plab fayllarni o'chirishni tasdiqlang +Haqiqatdan ham '{0}'ni o'chirib tashlamoqchimisiz? +Haqiqatdan ham '{0}' papkasini butun tarkibi bilan o'chirib tashlamoqchimisiz? +Haqiqatdan ham ushbu {0} ta qismni o'chirib tashlamoqchimisiz? +O'chirilmoqda... +Fayl yoki papkani o'chirishda xato yuz berdi +Tizim uzun yo'lakli faylni Recycle Bin (Korzina)ga ko'chira olmaydi +6300 +Papka yaratish +Fayl yaratish +Papka nomi: +Fayl nomi: +Yangi papka +Yangi fayl +Papka yaratishda xato yuz berdi +Fayl yaratishda xatolik yuz berdi +6400 +sharhi +&Sharh: +Tanlash +Tashlash +Maska: +6600 +Xossalari +Papkalar tarixi +Diagnostik xabarlar +Xabar +7100 +Kompyuter +Tarmoq +Hujjatlar +Tizim +7200 +Qo'sh +Ajrat +Sina +Nusxala +Ko'chir +O'chir +Ma'lumot +7300 +Faylni bo'laklash +&Ushbu katalogga bo'lakla: +&Tomlarga bo'lakla, bayt: +Bo'laklanmoqda... +Bo'laklash tasdig'i +Haqiqatdan ham faylni {0} ta tomga bo'lmoqchimisiz? +Tom hajmi original fayl hajmidan kichikroq bo'lishi lozim +Tom hajmi noto'g'ri +Berilgan tom hajmi: {0} bayt.\nHaqiqatdan ham arxivni shunaqa bo'laklarga bo'lmoqchimisiz? +7400 +Fayllarni birlashtirish +&Ushbu katalogga birlashtir: +Birlashtirilmoqda... +Bo'lak faylning faqat birinchi qismini tanla +Bu fayl bo'lak faylning qismi emas +Bo'lak faylning bittadan ortiq qismi topilmadi +7500 +Nazorat summasi hisoblanmoqda... +Nazorat summasi ma'lumoti +Data uchun CRC checksum: +Data va nomlar uchun CRC checksum: +7600 +Baholash +Xotira ishlatilishi: +Siqish +Ajratish +Reyting +Umumiy reyting +Joriy +Natijaviy +CPU bandligi +Reyting / Ishlatish +O'tganlar: diff --git a/Utils/7-Zip/Lang/va.txt b/Utils/7-Zip/Lang/va.txt new file mode 100644 index 000000000..b7ecf4bd0 --- /dev/null +++ b/Utils/7-Zip/Lang/va.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 4:26 : Tomas Miralles +; 4.44 : Fernando Verdú +; +; +; +; +; +; +; +; +; +0 +7-Zip +Valencian +Valencià +401 +Acceptar +Cancel·lar + + + +&Si +&No +Tan&car +Ajuda + +&Continuar +440 +Si a &tot +No a t&ot +Parar +Reiniciar +Segon pla +Primer pla +&Pausa +Parat +Està segur que vol cancel·lar? +500 +&Arxiu +&Editar +&Visualitzar +Favorits +Ferramentes +Ajuda +540 +&Obrir +Obrir d&ins +Obrir fora +&Visualitzar +&Editar +Renom&enar +&Copiar a... +&Moure a... +&Suprimir +&Separar fitxer... +Com&binar fitxers... +P&ropietats +Come&ntari +Calcular checksum + +Crear directori +Crear fitxer +Eixir +600 +Seleccion&ar-ho tot +Deseleccionar-ho tot +&Invertir selecció +Seleccionar... +No seleccionar... +Seleccionar per tipus +No seleccionar per tipus +700 +Icones g&rans +Icones menudes +&Llista +&Detalls +730 +No ordenat +Vista plana +&2 Taules +&Barres de ferramentes +Obrir directori arrel +Directori pare +Historial de carpetes... +Actualitza&r +750 +Arxiu +Estàndard +Botons grans +Mostrar text dels botons +800 +&Afegir el directori als Favorits com a +Personal +900 +&Opcions... +&Banc de proves +960 +&Contingut... +Sobre 7-Zip... +1003 +Adreça +Nom +Tipus d'arxiu +Directori +Tamany +Tamany comprimit +Atributs +Creat +ültim accés +Última modificació +Compacte +Comentari +Xifrat +Expandit abans +Expandit després +Diccionari +CRC +Tipus +Anti +Mètode +SO d'origen +Sistema de fitxers +Usuari +Grup +Bloc +Comentari +Posició +Prefix de ruta + + + + + + + + + + + + + + + + + + + + + + + + +Error +Tamany total +Espai lliure +Tamany sector +Etiqueta +Nom local +Proveïdor +2100 +Opcions +Idioma +Idioma: +Editor +&Editor: + +2200 +Sistema +Associar 7-Zip amb: +2301 +Integrar 7-Zip dins el menu contextual de Windows +Menu contextual en cascada +Objectes del menu contextual: +2320 + + +Obrir arxiu +Extraure fitxers... +Afegir a l'arxiu... +Comprovar arxiu +Extraure ací +Extraure a {0} +Afegir a {0} +Comprimir i enviar per correu electrònic... +Comprimir a {0} i enviar per correu electrònic +2400 +Directoris +Directori de &treball +Directori temporal del &sistema +Directori a&ctual +E&specificar directori: +Utilitzar només per a discs extraibles +Especificar un directori per als arxius temporals. +2500 +Ajusts +Mostrar l'objecte ".." +Mostrar icones reals dels fitxers +Mostrar el menú del sistema +Seleccionar &tota la la fila +Mostrar &línies de la taula + +Mode de selecció &alternatiu +Utilitzar pàgines de memòria &grans +2900 +Sobre 7-Zip +7-Zip és un programa lliure (GNU LGPL). Pot col·laborar en el desenvolupament del 7-zip registrant-lo, com a usuari registrat podrà rebre suport tècnic. +3000 + +No hi ha errors +{0} objecte(s) seleccionat(s) +No es pot crear el directori '{0}' +Les operacions d'actualització d'este arxiu no estan suportades. +No es pot obrir el fitxer '{0}' com arxiu +No es pot obrir l'arxiu xifrat '{0}'. contrasenya incorrecta? + + +El fitxer '{0}' ha sigut modificat.\nVol actualitzar-lo a l'arxiu? +No es pot actualitzar el fitxer\n'{0}' +No es pot executar l'editor. + + + + +Massa objectes +3300 +Extraent +Comprimint +Provant +Obrint... +Escanejant... +3400 +Extraure +E&xtraure a: +Seleccione destinació per als fitxers extrets. +3410 +Mode d'adreça +Adreça sencera +Sense adreça +3420 +Sobreescriu +Pregunta abans de sobreescriure +Sobreescriu sense confirmació +Conserva arxius ja existents +Reanomena automàticament +Reanomena automàticament arxius ja existents +3500 +Confirmar substitució de fitxers +El directori de destinació conté un fitxer amb el mateix nom. +Vol substituir el fitxer existent +per este altre? +{0} bytes +Renomenar a&utomàticament +3700 +Mètode de compressió no vàlid per a '{0}'. +Error de dades en '{0}'. L'arxiu està corrupte. +CRC ha fallat en '{0}'. L'arxiu està corrupte. +Errors de dades en l'arxiu xifrat '{0}'. Contrasenya incorrecta? +CRC incorrecte en l'arxiu xifrat '{0}'. Contrasenya incorrecta? +3800 +Introduir contrasenya +Introduir contrasenya: +Reintroduir contrasenya: +Mo&strar contrasenya +Les contrasenyes no coincideixen +Useu només lletres de l'alfabet anglès, números i caràcters especials (!, #, $, ...) per a la contrasenya +Contrasenya massa llarga +Contrasenya +3900 +Temps transcorregut: +Temps restant: +Mida: +Velocitat: + + +Errors: + +4000 +Afegir a l'arxiu +&Arxiu: +Mode d'act&ualització: +&Format de l'arxiu: +&Nivell de compressió: +Tipus de co&mpressió: +Tamany &diccionari: +Tamany ¶ula: + + +&Paràmetres: +Opcions +Crear arxiu SF&X + +Xifrat +Métode de xifrat: +Encriptar el nom dels fitxers +Us de memòria Comprimint: +Us de memòria Descomprimint: +4050 +Sense compressió +La més ràpida +Ràpida +Normal +Màxima +Ultra +4060 +Afegir i substituir fitxers +Actualitzar i afegir fitxers +Actualitzar fitxers existents +Sincronitzar fitxers +4070 +Visualitzar +Tots els fitxers + + +6000 +Copiar +Moure +Copiar a: +Moure a: +Copiant... +Movent... +Renomenant... +Seleccioneu carpeta de destinació. +Operació no permesa. +Error renomenant fitxer o carpeta +Confirmar copia de fitxer +Està segur que vol copiar els fitxers a l'arxiu +6100 +Confirmar supressió del fitxer +Confirmar supressió del directori +Confirmar supressió m�ltiple de fitxers +Està segur de voler suprimir '{0}'? +Està segur de voler suprimir la carpeta '{0}' i tot el seu contingut? +Està segur de voler esborrar estos {0} elements? +Suprimint... +Error esborrant fitxer o carpeta + +6300 +Crear carpeta +Crear fitxer +Nom de carpeta: +Nom de fitxer: +Carpeta nova +Fitxer nou +Error creant carpeta +Error creant el fitxer +6400 +Comentari +&Comentari: +Seleccionar +No seleccionar +Màscara: +6600 + +Historial de directoris +Missatges de diagnosi +Missatge +7100 +El meu ordinador +Entorn de xarxa + +Sistema +7200 +Afegir +Extraure +Provar +Copiar +Moure +Esborrar +Info +7300 +Separar fitxer +&Separar a: +Separar en &volums, bytes: +Separant... +Confirma divissió +Esteu segurs que voleu dividir el fitxer en {0} volums? +La mida del volum ha de ser menor que la mida original del fitxer +Mida incorrecta de volum +Mida del volum especificada: {0} bytes.\nEsteu segurs que voleu dividir l'arxiu en volums? +7400 +Combinar fitxers +&Combinar a: +Combinant... +Seleccioneu només el primer fitxer + + +7500 +Calculant checksum... +Informació checksum +CRC checksum per a les dades: +CRC checksum per a dades i noms: +7600 +Banc de proves +Us de memoria: +Comprimint +Descomprimint +Taxa +Taxa total +Actual +Resultant + + +Passades: diff --git a/Utils/7-Zip/Lang/vi.txt b/Utils/7-Zip/Lang/vi.txt new file mode 100644 index 000000000..05def96a0 --- /dev/null +++ b/Utils/7-Zip/Lang/vi.txt @@ -0,0 +1,404 @@ +;!@Lang2@!UTF-8! +; 2.30 : : Tran Hong Ha +; 4.42 : : Le Vu Hoang +; 4.48 : : Nguyen Hong Quan +; 9.07 : 2011-04-12 : Vietnamize Team +; +; +; +; +; +; +; +0 +7-Zip +Vietnamese +Tiếng Việt +401 +Äồng ý +Há»§y bá» + + + +Có +Không +Äóng +Giúp đỡ + +Tiếp tục +440 +Có tất cả +Không tất cả +Dừng +Làm lại +Chạy ná»n +Chế độ ưu tiên +Dừng +Äã dừng +Bạn chắc chắn muốn há»§y bá»? +500 +Tập tin +Biên tập +Xem +Ưa thích +Công cụ +Giúp đỡ +540 +Mở +Mở tại đây +Mở trong cá»­a sổ khác +Xem +Biên tập +Äổi tên +Sao chép đến... +Di chuyển đến... +Xoá +Chia cắt tệp nén... +Nối tệp nén... +Thuá»™c tính +Chú thích +Tính checksum (md5) +So sánh +Tạo thư mục +Tạo tệp nén +Thoát +600 +Chá»n tất cả +Bá» chá»n tất cả +Äảo lá»±a chá»n +Chá»n... +Bá» chá»n... +Chá»n theo loại +Bá» chá»n theo loại +700 +Biểu tượng lá»›n +Biểu tượng nhá» +Danh sách +Chi tiết +730 +Không sắp xếp +Má»i tập tin và thư mục con +2 bảng +Thanh công cụ +Mở thư mục gốc +Lên má»™t cấp +Lịch sá»­ thư mục... +Nạp lại +750 +Thanh công cụ nén +Thanh công cụ chuẩn +Sá»­ dụng nút lá»›n +Hiển thị chữ trên nút +800 +Thêm thư mục vào 'Ưa thích' như là +Äánh dấu +900 +Tùy chá»n... +Äo tốc độ +960 +Ná»™i dung... +Vá» 7-Zip... +1003 +ÄÆ°á»ng dẫn +Tên +Phần mở rá»™ng +Thư mục +Kích cỡ thá»±c +Kích cỡ nén +Thuá»™c tính +Thá»i Ä‘iểm tạo +Thá»i Ä‘iểm truy xuất +Thá»i Ä‘iểm sá»­a đổi +Kiểu nén Solid +Ghi chú +ÄÆ°á»£c mã hoá +Chia nhá» trước +Chia nhá» sau +Từ Ä‘iển +CRC +Loại +Anti +Phương thức nén +Hệ Ä‘iá»u hành +Tệp hệ thống +Ngưá»i dùng +Tập Ä‘oàn +Số thứ tá»± +Chú thích +Vị trí +ÄÆ°á»ng dẫn đầu +Thư mục +Tập tin +Phiên bản +Kích cỡ +Äa kích cỡ +Offset +Liên kết +Tập tin +Kích cỡ + +64-bit +Big-endian +CPU +Kích cỡ lý thuyết +Kích cỡ hiện tại +Checksum +Äặc Ä‘iểm +Äịa chỉ ảo +ID +Tên ngắn +Trình tạo ứng dụng +Kích cỡ vùng +Chế độ +Liên kết +Lá»—i +Tổng dung lượng +Dung lượng trống +Dung lượng cluster +Nhãn +Tên mạng cục bá»™ +Nhà cung cấp +2100 +Các tùy chá»n +Ngôn ngữ hiển thị +Ngôn ngữ: +Biên tập +Trình biên tập: +Trình so sánh: +2200 +Hệ thống +Kết hợp 7-Zip vá»›i: +2301 +Tích hợp 7-Zip vào menu ngữ cảnh +Xếp tầng menu ngữ cảnh +Menu ngữ cảnh: +2320 + + +Mở tệp nén +Giải nén tệp... +Thêm vào tệp nén... +Kiểm tra tệp nén +Giải nén tại đây +Giải nén vào {0} +Thêm vào {0} +Nén và gởi qua email... +Nén thành {0} và gởi qua email +2400 +Thư mục +Thư mục hiện hành +Hệ thống thư mục tạm +Hiện tại +Äặc biệt: +Chỉ sá»­ dụng cho ổ đĩa di động +Xác định vị trí cho các tệp lưu trữ tạm thá»i. +2500 +Thiết lập +Hiển thị mục ".." +Hiển thị biểu tượng thá»±c cá»§a tệp +Hiển thị menu hệ thống +Chá»n cả dòng +Hiển thị lưới +Nhắp chuá»™t để mở +Chế độ chá»n luân phiên +Sá»­ dụng bá»™ nhá»› lá»›n +2900 +Vá» 7-Zip +7-Zip là má»™t phần má»m miá»…n phí. +3000 +Không thể cấp thêm dung lượng RAM yêu cầu +Không xuất hiện lá»—i +{0} đối tượng đã chá»n +Không thể tạo thư mục '{0}' +Tệp nén này không được há»— trợ cập nhật. +Không thể mở '{0}' như là tệp nén +Không thể mở tập tin nén '{0}' bị mã hóa . Mật khẩu sai? +Không há»— trợ tệp nén này +Tệp {0} Ä‘ang tồn tại +Tệp '{0}' đã bị thay đổi.\nBạn có muốn cập nhật nó vào tập tin nén? +Không thể cập nhật tệp\n'{0}' +Không thể khởi động trình biên tập. +Tập tin này có thể là virus (tên tập tin có chứa khoảng cách dài). +Không thể thá»±c hiện vá»›i má»™t thư mục có đưá»ng dẫn quá dài. +Bạn phải chá»n má»™t tập tin +Bạn phải chá»n má»™t hoặc 2 tập tin +Quá nhiá»u tập tin +3300 +Äang giải nén... +Äang nén... +Äang kiểm tra... +Äang mở... +Äang quét... +3400 +Giải nén +Giải nén vào: +Chá»n nÆ¡i để giải nén tệp. +3410 +Chá»n đưá»ng dẫn +ÄÆ°á»ng dẫn đầy đủ +Không có đưá»ng dẫn +3420 +Chế độ ghi đè +Há»i trước khi ghi đè +Ghi đè không cần há»i +Bá» qua tệp đã có +Tá»± động đổi tên +Tá»± động đổi tên tệp đã có +3500 +Xác nhận thay thế +Thư mục đích đã có tập tin đó. +Bạn có muốn thay thế tập tin đã có +bằng tập tin má»›i? +{0} bytes +Tá»± động đổi tên +3700 +Phương thức nén không há»— trợ cho '{0}'. +Lá»—i dữ liệu trong '{0}'. Tệp đã bị há»ng. +Lá»—i chẵn/lẻ (CRC) trong '{0}'. Tệp đã bị há»ng. +Lá»—i dữ liệu trong tệp nén bị mã hóa '{0}'. Mật khẩu sai? +Kiểm tra CRC thất bại trong tệp nén bị mã hóa '{0}'. Mật khẩu sai? +3800 +Vui lòng nhập mật khẩu +Nhập mật khẩu: +Nhập lại mật khẩu: +Xem mật khẩu +Mật khẩu không khá»›p nhau +Chỉ đặt mật khẩu bằng tiếng Anh, số và những kí tá»± đặc biệt (!, #, $, ...) cho mật khẩu +Mật khẩu quá dài +Mật khẩu +3900 +Thá»i gian đã qua: +Thá»i gian còn lại: +Tổng kích cỡ: +Tốc độ: +Äã nén: +Tá»· lệ nén: +Lá»—i: +Tệp nén: +4000 +Thêm vào tệp nén +Tệp nén: +Chế độ cập nhật: +Dạng tệp nén: +Mức độ nén: +Phương thức nén: +Kích cỡ thư mục: +Kích cỡ văn bản: +Kích cỡ nén Solid: +Số luồng xá»­ lý CPU: +Tham số: +Tùy chá»n +Tạo trình tá»± giải nén tệp +Nén tập tin chia sẻ +Mã hóa +Phương thức mã hóa: +Mã hoá tên tệp +Bá»™ nhá»› dùng cho việc nén: +Bá»™ nhá»› dùng cho việc giải nén: +4050 +Lưu trữ +Nhanh nhất +Nhanh +Bình thưá»ng +Tối Ä‘a +Siêu nhanh +4060 +Thêm và thay thế +Thêm và cập nhật +Cập nhật tập tin đã có +Äồng bá»™ +4070 +Duyệt +Má»i tập tin +Không nén dạng Solid +Solid +6000 +Sao chép +Di chuyển +Sao chép đến: +Di chuyển đến: +Äang thêm... +Äang di chuyển... +Äang đổi tên... +Chá»n thư mục đến. +Thao tác không được há»— trợ cho tập tin này. +Lá»—i khi đổi tên tệp hoặc thư mục +Xác nhận thêm +Bạn muốn thêm vào tệp nén? +6100 +Xác nhận xoá +Xác nhận xoá thư mục +Xác nhận xoá nhiá»u tệp +Bạn có chắc chắn muốn xoá '{0}'? +Bạn có chắc chắn muốn xoá thư mục '{0}' và má»i tập tin bên trong? +Bạn có chắc chắn muốn xoá {0} ? +Äang xóa... +Lá»—i khi Ä‘ang xóa tệp hoặc thư mục +Hệ thống không thể xóa tệp vá»›i đưá»ng dẫn dài vào Thùng rác +6300 +Tạo thư mục +Tạo tệp nén +Tên thư mục: +Tên tệp má»›i: +Thư mục má»›i +Tệp má»›i +Lá»—i khi tạo thư mục +Lá»—i khi tạo tệp +6400 +Chú thích +Chú thích: +Chá»n +Bá» chá»n +Ẩn danh: +6600 +Thuá»™c tính +Lịch sá»­ thư mục +Thông tin chẩn Ä‘oán +Thông tin +7100 +Máy tính +Mạng +Tài liệu +Hệ thống +7200 +Thêm +Giải nén +Kiểm tra +Sao chép +Di chuyển +Xoá +Thông tin +7300 +Chia nhá» tệp +Chia nhá» và lưu tại: +Chia thành nhiá»u phần, bytes: +Äang chia nhá»... +Xác nhận việc chia nhá» +Bạn có chắc muốn chia nhá» tệp nén thành {0} phần? +Kích cỡ má»—i phần phải nhá» hÆ¡n tệp gốc +Kích cỡ phần chia chưa đúng +Kích cỡ phần chia đã đặt: {0} bytes.\nBạn chắc chắn muốn chia tệp nén theo kích cỡ chia đã đặt? +7400 +Nối tệp +Nối thành: +Äang nối... +Chỉ chá»n phần đầu cá»§a tệp bị chia nhá» +Không thể xem tập tin như phần nối cá»§a tệp bị chia cắt +Không tìm thấy thêm phần nối nào cá»§a tệp chia cắt +7500 +Äang tính checksum... +Thông tin checksum +CRC checksum cho dữ liệu: +CRC checksum cho dữ liệu và tên: +7600 +Äo tốc độ +Bá»™ nhá»› sá»­ dụng: +Äang nén +Äang giải nén +Äánh giá +Tổng đánh giá +Hiện thá»i +Kết quả +Mức dùng CPU +Tốc độ / Mức dùng +Äã qua: diff --git a/Utils/7-Zip/Lang/yo.txt b/Utils/7-Zip/Lang/yo.txt new file mode 100644 index 000000000..3fa9560a5 --- /dev/null +++ b/Utils/7-Zip/Lang/yo.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 15.00 : 2015-03-29 : Ibrahim Oyekan +; +; +; +; +; +; +; +; +; +; +0 +7-Zip +Yoruba +Yoruba +401 +O DAA +Pa re + + + +&Bẹẹni +&Bẹẹká» +&Pádé +ÃŒrànlá»wá» + +&Tẹ́-síwájú +440 +Bẹẹni fun &gbogbo ẹ +Bẹẹká» fun &gbogbo ẹ +Dúró +Ṣàtúnbẹ̀rẹ̀ +&Ẹ̣̀hìn-ìgbéhìn +&Ojú-ìgbéhìn +&Dádúró +ÃŒdúró +á¹¢e ẹ dájú pe ẹnyin fẹ́ paarẹ +500 +&Faíli +&Tunká» +&ÃŒwò +&Aàyò +&Iriná¹£áº¹Ì +&ÃŒrànlá»wá» +540 +&á¹¢i +á¹¢i &si ínú +á¹¢i &si íta +&ÃŒwò +&Tunká» +&Tun oruká» ká» +&Ṣẹ̀dà si... +&Gbé si... +&Paarẹ +&Pín faíli... +Ṣà àwá»n faíli kópá»Ì€... +&Àbùdá +&ỌrỠìwòye... +á¹¢e iá¹£iro checksum +íyàtọ̀ +Dá àpò faíli silẹ́ +Dá faíli silẹ́ +&Pádé +ÃŒtá»Ìkasí +&Yiyan agbara détà +600 +Àṣàyàn &gbogbo faíli +Paa Àṣàyàn gbogbo faíli +&Yi Àṣàyàn Padà +Àṣàyàn... +Paa Àṣàyàn... +Àṣàyàn bi irú faíli +Paa Àṣàyàn bi irú faíli +700 +&Àmi Å„lá +&Àmi Kékeré +&Aká»jọ́ +&Awá»n alaye +730 +Lai tá» lẹsẹsẹ +Iwò á¹¢epẹtẹ +&Iriná¹£áº¹Ì méjì +&Pẹpẹ iriná¹£áº¹Ì +á¹¢i pìlẹ̀ +Lá» sókè lẹẹkan +ÃŒtàn àpò faíli... +&Sá»dá»Ì€tun +Isá»dá»Ì€tun aládÃ Ã¡á¹£áº¹Ì +750 +Kó pẹpẹ iriná¹£áº¹Ì jọ́pá» +Ojúlówó pẹpẹ iriná¹£áº¹Ì +Onini Å„lá +Fihàn ÃŒpilẹ̀sọ́ ọ̀rọ̀ Onini +800 +Fi àpò faíli si àpò faíli ayanfẹ +Àmì ìwé +900 +&ÃŒyàn... +&Ala +960na +&Àkóónú... +&Nípa 7-Zip... +1003 +Ọnà +Oruká» +ÃŒká»pá»mó +Àpò faíli +ÃŒwá»n +ÃŒwá»n àkójá»pá»Ì€ +Àwòmá»Ì +Idásílẹ́ +ÃŒráyè sí +Atúná¹£e +Alagbara +ỌrỠìwòye +ÃŒwépọ̀ +Ká»ká» pín +Pín lẹ́hìn +ÃŒwe itúmá»Ì€-èdè + +Irú +Egboogi +Ito lẹsẹsẹ +OS agbalejo +ÃŒlànà ètò fáìlì +Onilò +ÃŒwá»Ìpá»Ì€ +Dínà +ỌrỠìwòye +Ipò +ÃŒpele á»Ì€nà +Àpò faíli +Faíli +Ẹya +Ọ̀pá»Ì€ òǹlò +Ọ̀pá»Ì€ òǹlò á»pá»lá»pá» +Aiá¹£edeede +Awá»n ÃŒtá»Ìkasí +Àká»sílẹ +Awá»n á»Ì€pá»Ì€ òǹlò + +Bíìtì-8 +Endian itóbi +CPU +ÃŒwá»n gangan +ÃŒwá»n àká»sórí +Checksum +Ti ìwà +ÀdírẹÌẹ̀sì àfojúunúṣe +ID +Orúká» kuru +Ohun èlò eleda +ÃŒwá»n apákàan +Móòdù +ÃŒtá»Ìkasí to ni aami +Ìṣiá¹£e +ÃŒwá»n lapapá» +Aaye to ṣẹku +ÃŒwá»n ìṣùpá»Ì€ +Aṣàmì +Agbègbè orúká» +Olùpèsè +Ààbò NT +Yiyan agbara détà +Olurànlá»wá» +Ààtàn +O jẹ igi + + +Irú +Awá»n ìṣiá¹£e +Awá»n ìṣiá¹£e +Awá»n ìkìlá»Ì€ +ÃŒkìlá»Ì€ +Agbara détà +Yiyan agbara détà +ÃŒwá»n Yiyan Agbara +ÃŒwá»n àfojúunúṣe +ÃŒwá»n ìtú erú +ÃŒwá»n gangan lapapá» +Atá»Ìkà á»Ì€pá»Ì€ òǹlò +ẹ̀ka-ìwá»n +á»rỠìwòye kuru +Ojú-ìwé kóòdù + + + +ÃŒwá»n ìrú +ÃŒwá»n àfibá»Ì€ ìyá»kúrò +ÃŒtá»Ìkasí +ÃŒtá»Ìkasí lile +iNode + +Kàn kàá +2100 +ÃŒyàn +Èdè +Èdè: +Aṣàtúná¹£e +&Aṣàtúná¹£e: +&íyàtọ̀: +2200 +ÃŒlànà ètò +Da fáìlì pá»-má» 7-Zip: +Gbogbo onilò +2301 +Sá» 7-Zip pá»Ì€ má» mẹÌnù á»Ì€gàngán ipò ṣẹÌẹ̀lì +MẹÌnù á»Ì€gàngán ipò pérété +Ijẹri ninu mẹÌnù á»Ì€gàngán ipò: +Àmì ninu mẹÌnù á»Ì€gàngán ipò +2320 +<Àpò faíli> +<Àpò faíli àkójọ́pá»> +á¹¢i àpò faíli àkójọ́pá» +Tú faíli silẹ... +Fi si àpò faíli àkójọ́pá»... +Dán àpò faíli àkójọ́pá» wò +Tú faíli si inú ibí +Tú faíli si inú {0} +Fi si {0} +Kó faíli jọ́pá», ko rán í-meèlì... +Kó faíli jọ́pá» si {0} ko rán í-meèlì +2400 +Àpò faíli +&Àpò faíli iá¹£iá¹£áº¹Ì +&ÃŒlànà ètò àpò faíli ibùgbé +&Lá»Ìwá»Ìlá»Ìwá»Ì +&Pàtó kan: +Lò fun àwo àká-á»Ì€rá»Ì€ yiyá» nìkan +Yan ilé fun àpò faíli àkójọ́pá» ibùgbé +2500 +ÃŒtò +Fihàn ".." ijẹri +Fihàn àmì faíli gidi +Fihàn mẹÌnù ìlànà ètò +&Àṣàyàn gbogbo ìlà ìbú +Fihàn &àwá»n ìlà gírìdì +Ìṣíra tẹ̀ lẹÌẹ̀kan láti ṣí ijẹri +&Yiyan móòdù àṣàyàn +Lò &ojú-ìwé ti o nlò ibi ìpamá»Ì Å„lá +2900 +Nípa 7-Zip +Ẹ̀yà àìrídìmú á¹£'ofo ni 7-Zip +3000 +ÃŒlànà ètò ò le pín ibi ìpamá»Ì ti o tó +Kò si iá¹£iá¹£e kankan +Iye àṣàyàn: {0} +Kò lé dá àpò faíli '{0}' +Kò lé ṣàfikùn àpò faíli àkójọ́pá» yí. +Kò lé ṣí àpò faíli '{0}' sí àpò faíli àkójọ́pá» +Kò lé ṣí àpò faíli àkójọ́pá» (ìpàrokò). á»rá»Ì€ aṣínà láìpé +Faíli yí o baamu +Faíli {0} ti wà níbẹ +Faíli '{0}' títúná¹£e.\n á¹£e ẹ fẹ̀ túná¹£e si inú àpò faíli àkójọ́pá»? +Kò le ṣàfikún fun faíli yí \n'{0}' +Kò le ṣí Olótùú. +Faíli yí jỠàkóràn(Ààyè to wa ni àárín oruká» faíli ti poju). +Kò le pè ìmú iá¹£iá¹£áº¹Ì yí láti àpò faíli to ni á»Ì€nà gígùn. +Ẹ gbá»dỠṣàyàn faíli kan. +Ẹ gbá»dỠṣàyàn faíli kan tàbí faíli pupá» +Ijẹri ti ẹ ṣà ti pá»ju +Kò le ṣí faíli yí bí {0} àpò faíli àkójọ́pá» +Faíli yí ti ṣí bí {0} àpò faíli àkójọ́pá» +Àpò faíli àkójọ́pá» yí ti ṣí pẹlu aiá¹£edeede +3300 +ÃŒtúsilẹ +ÃŒkójọ́pá» +ÃŒdánwò +Ìṣíṣí... +Ìṣẹ̀dà àwòrán ... +ÃŒyá»kúrò +3320 +ÃŒfi-sí +Ìṣàfikùn +ÃŒtúpalẹ̀ +ÃŒfijá» +Ìṣatopá» +Mbẹ́ +ÃŒpaarẹ +ÃŒdá àká»sórí +3400 +Túsilẹ +&Tú faíli si inú: +Yan ibi ti awá»n faíli ma tú si. +3410 +Móòdù á»Ì€nà: +Gbogbo oruká» á»Ì€nà +Kò si oruká» á»Ì€nà +Oruká» á»Ì€nà á»lá»Ìgangan +Oruká» á»Ì€nà ìbátan +3420 +Móòdù ìká»sórí: +Béèrè kí ó tó ká» sórí faíli +Ká» sórí faíli lai béèrè +Mà wo faíli tó ti wà níbẹ +Tun oruká» ká» laládàáṣiá¹£áº¹Ì +Tun ká» oruká» faíli tó ti wà níbẹ laládàáṣiá¹£áº¹Ì +3430 +Mú ìfijỠàpò faíli ìpìlẹ̀ kúrò +Da ààbò faíli padà +3500 +Tẹnumọ́ àfirá»Ìpò faíli +Àpò fáìlì èbúté ti ní fáìlì ìgbésẹÌ. +á¹¢e ẹ fẹ̀ ṣàfirá»Ìpò fun fáìlì tó ti wà níbẹ +pẹlu fáìlì yí? +Báìtì {0} +&Tun oruká» ká» laládàáṣiá¹£áº¹Ì +3700 +Ká» si àtìlẹÌyìn fun ètò àkójọ́pá» yí '{0}'. +Ìṣiá¹£e détà ṣẹlẹ ní inú '{0}'. Fáìlì ti bajẹ. +ÃŒkùnà CRC ṣẹlẹ ní inú '{0}'. Fáìlì ti bajẹ. +Ìṣiá¹£e détà ṣẹlẹ ní inú fáìlì pàroko '{0}'. Tun wo á»Ì€rá»Ì€ aṣínà +ÃŒkùnà CRC ṣẹlẹ ní inú fáìlì pàroko '{0}'. Tun wo á»Ì€rá»Ì€ aṣínà +3710 +Tun wo á»Ì€rá»Ì€ aṣínà +3721 +Ko si àtìlẹÌyìn fun ètò àkójọ́pá» yí +Ìṣiá¹£e détà ṣẹlẹ +Détà ò á¹£eélò +ÃŒkùnà CRC +Détà ti parí lójijì +Détà á¹£i wa lẹ̀hìn détà á»Ì€gangan +Kò á¹£e faíli akójọ́pá» +Ìṣiá¹£e àká»lé +Tun wo á»Ì€rá»Ì€ aṣínà +3763 +ÃŒbẹ̀rẹ̀ faíli akójọ́pỠò á¹£eélò +ÃŒbẹ̀rẹ̀ faíli akójọ́pỠò á¹£e tẹnumọ́ + + + +Ko si àtìlẹÌyìn fun àfidámá»Ì€ yí +3800 +Tẹ̀ á»Ì€rá»Ì€ aṣínà +Tẹ̀ á»Ì€rá»Ì€ aṣínà: +ṣítẹ̀ á»Ì€rá»Ì€ aṣínà: +&Fihàn á»Ì€rá»Ì€ aṣínà +Awá»n á»Ì€rá»Ì€ aṣínà ò dá»gba +Lò ábídí, iye tàbí aami Gẹ̀ẹÌsì nìkan (!, #, $, ...) fun á»Ì€rá»Ì€ aṣínà +Ọ̀rá»Ì€ aṣínà ti gùn ju +Ọ̀rá»Ì€ aṣínà +3900 +Àsìkò ti okan: +Àsìkò ti o ku: +ÃŒwá»n lapapá»: +ÃŒyára: +Ìṣètò: +ÃŒpín ÃŒkójọ́pá»: +Awá»n ìṣiá¹£e: +Awá»n faíli àkójọ́pá»: +4000 +Fi si àpò faíli àkójọ́pá» +&Faíli àkójọ́pá»: +&Móòdù ìṣàfikún: +&ÃŒgúnrégé faíli àkójọ́pá»: +&ÃŒpele àkójọ́pá»: +&Ọ̀nà àkójọ́pá»: +&ÃŒwá»n àtúmá»Ì€-èdè: +&ÃŒwá»n ó̩ró̩gbólóhùn: +ÃŒwá»n sèdíwá»: +Iye èròjà atẹ̀lélànà CPU: +&Awá»n afòdiwá»Ì€n: +ÃŒyàn +Dá àpò faíli àkójọ́pá» fun SF&X +Ko faíli alájá»pín jọ́pá» +ÃŒpàrokò +Móòdù ìpàrokò: +&á¹¢e ìpàrokò fun orúká» faíli +ìlò ibi ìpamá»Ì fun àkójọ́pá»: +ìlò ibi ìpamá»Ì fun ìtúsilẹ: +Paarẹ faíli lẹ̀hìn àkójọ́pá» +4040 +Fi ìtá»Ìkasí to ni aami pamá»Ì +Fi ìtá»Ìkasí lile pamá»Ì +Fi agbara détà pamá»Ì +Fi àfirá»Ìpò faíli pamá»Ì +4050 +Ṣàfipamá»Ì +Kíá ju +Kíákíá +Déédéé +Ki o pá»ju +Ki o púpá»Ì€ +4060 +Fi faíli si ki o tun fi rá»Ìpò +Ṣàfikùn faili ki o tun fi si +Sá» awá»n faili lá»Ìwá»Ì di á»Ì€tun +Mú awá»n faíli dá»Ìgba +4070 +Wáròyìn +Gbogbo faíli +Lai le +Lile +6000 +Ṣẹ̀dà +Gbé +Ṣẹ̀dà si: +Gbé si: +Ìṣẹ̀dà +ÃŒgbé... +ÃŒtunká»... +Ṣàyàn èbúté àpò faíli. +ÃŒmú á¹£iá¹£áº¹Ì o ni àtìlẹÌyìn fun àpò faíli. +Ìṣiá¹£e ṣẹlẹ ní ìtunkỠàpò faíli +Tẹnumọ́ ìṣẹ̀dà faíli +á¹¢e áº¹Ì dájú pe ẹ fẹ́ ṣẹ̀dà awá»n faíli yí si àpò faíli àkójọ́pá»? +6100 +Tẹnumọ́ ìpaarẹ faíli +Tẹnumọ́ ìpaarẹ àpò faíli +Tẹnumọ́ ìpaarẹ àpò faíli á»Ì€pá»Ì€ +á¹¢e áº¹Ì dájú pe ẹ fẹ́ paarẹ '{0}'? +á¹¢e áº¹Ì dájú pe ẹ fẹ́ paarẹ '{0}' a àti gbogbo àkóónú ẹ? +á¹¢e áº¹Ì dájú pe ẹ fẹ́ paarẹ iye ijẹri yí {0} ? +ÃŒpaarẹ... +Ìṣiá¹£e ṣẹlẹ ni ìpaarẹ faíli tàbí àpò faíli +ÃŒlànà ètò o le gbé faíli to ni orúká» gígùn si inú Ààtàn +6300 +Dá àpò faíli +Dá faíli +OrúkỠàpò faíli: +Orúká» faíli: +Àpò faíli +Faíli tuntun +Ìṣiá¹£e ṣẹlẹ ni ìdá àpò faíli +Ìṣiá¹£e ṣẹlẹ ni ìdá faíli +6400 +ỌrỠìwòye +&ỌrỠìwòye: +Ṣàyàn +Paa Àṣàyàn +Asáº¹Ì iye: +6600 +Àbùdá +Aká»á»Ìlẹ̀ àpò faíli +Atá»pinpin-Àìṣedédé iṣé +Iṣé +7100 +Ká»Ì€mpútà +Alásopá»Ì€ +Àká»sílẹ̀ +ÃŒlànà ètò +7200 +Fi-sí +Túsilẹ +Dán-wò +Ṣẹ̀dà +Gbé +Paarẹ +ÃŒròyìn +7300 +Pín faíli +&Pín si: +Pín si &á»Ì€pá»Ì€ òǹlò, báìtì: +ÃŒpín... +Tẹnumọ́ ÃŒpín +á¹¢e áº¹Ì dájú pe ẹ fẹ́ pín faíli si á»Ì€pá»Ì€ òǹlò {0}? +Ọ̀̀pá»Ì€ òǹlò láti kéré ju ìwá»n faíli ojulowo lá» +ìwá»n á»Ì€pá»Ì€ òǹlò láìpé +Yan ìwá»n á»Ì€pá»Ì€ òǹlò: {0} báìtì.\ná¹¢e áº¹Ì dájú pe ẹ fẹ́ pín àpò faíli àkójọ́pá» si á»Ì€pá»Ì€ òǹlò awá»n yí? +7400 +Kó faíli pá»Ì€ +&Kó faíli pá»Ì€ si: +ÃŒkópá»Ì€... +Ṣàyàn aká»ká» faíli ìpín nìkan +Ko lè rí faíli ni inu faíli ìpín +Ko lè rí ju apá faíli kan lá» +7500 +ÃŒkà checksum... +ÃŒròyìn Checksum +CRC checksum fun détà: +CRC checksum fun détà àti orúká»: +7600 +Ala +ÃŒlò ibi ìpamá»Ì: +Akójọ́pá» +ÃŒtúsilẹ +ÃŒgbéléwá»Ì€n +Awá»n ìgbéléwá»Ì€n lapapá» +Lá»Ìwá»Ìlá»Ìwá»Ì +èsì +ÃŒlò CPU +ÃŒgbéléwá»Ì€n / ÃŒlò +ÃŒwé ìjáde: +7700 +ÃŒtá»Ìkasí +ÃŒtá»Ìkasí +ÃŒtá»Ìkasí láti: +ÃŒtá»Ìkasí: +7710 +Irú ÃŒtá»Ìkasí +ÃŒtá»Ìkasí lile +Faíli ÃŒtá»Ìkasí to ni aami +Iwé ilana ÃŒtá»Ìkasí to ni aami +Idapá» iwé ilana diff --git a/Utils/7-Zip/Lang/zh-cn.txt b/Utils/7-Zip/Lang/zh-cn.txt new file mode 100644 index 000000000..994abc726 --- /dev/null +++ b/Utils/7-Zip/Lang/zh-cn.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 2.30 : 2002-09-07 : Modern Tiger, kaZek, Hutu Li +; 3.08 : 2003-08-29 : Tunghsiao Liu (aka. Sparanoid) +; 16.00 : 2016-05-16 : Tunghsiao Liu (aka. Sparanoid) +; +; +; +; +; +; +; +; +0 +7-Zip +Chinese Simplified +简体中文 +401 +确定 +å–æ¶ˆ + + + +是(&Y) +å¦(&N) +关闭(&C) +帮助 + +ç»§ç»­(&C) +440 +全是(&A) +å…¨å¦(&L) +åœæ­¢ +釿–°å¼€å§‹ +åŽå°(&B) +å‰å°(&F) +æš‚åœ(&P) +å·²æš‚åœ +您真的è¦å–消å—? +500 +文件(&F) +编辑(&E) +查看(&V) +书签(&A) +工具(&T) +帮助(&H) +540 +打开(&O) +当å‰çª—壿‰“å¼€(&I) +æ–°å»ºçª—å£æ‰“å¼€(&U) +查看(&V) +编辑(&E) +é‡å‘½å(&M) +å¤åˆ¶åˆ°(&C)... +移动到(&M)... +删除(&D) +分割文件(&S)... +åˆå¹¶æ–‡ä»¶(&B)... +属性(&R) +注释(&N) +文件校验 +文件比较 +新建文件夹 +新建文件 +退出(&X) +链接 +äº¤æ›¿æ•°æ®æµ(&A) +600 +全选(&A) +å…¨éƒ¨å–æ¶ˆ +å选(&I) +选择... +å–æ¶ˆé€‰æ‹©... +选择相åŒç±»åž‹çš„æ–‡ä»¶ +å–æ¶ˆé€‰æ‹©ç›¸åŒç±»åž‹çš„æ–‡ä»¶ +700 +大图标(&G) +å°å›¾æ ‡(&M) +列表(&L) +详细信æ¯(&D) +730 +ä¸æŽ’åº +å¹³é“ºæ¨¡å¼ +åŒç‰ˆé¢(&2) +工具æ (&T) +打开根文件夹 +å‘上 +文件夹历å²... +刷新(&R) +自动刷新 +750 +æ¡£æ¡ˆå·¥å…·æ  +æ ‡å‡†å·¥å…·æ  +大按钮 +显示按钮文字 +800 +添加到书签(&A) +书签 +900 +选项(&O) +基准测试(&B) +960 +查看帮助(&C) +关于 7-Zip (&A) +1003 +路径 +åç§° +扩展å +文件夹 +å¤§å° +压缩åŽå¤§å° +属性 +创建时间 +访问时间 +修改时间 +固实 +注释 +加密 +之å‰åˆ†å‰² +之åŽåˆ†å‰² +å­—å…¸å¤§å° + +类型 +剔除项 +算法 +主æ“作系统 +文件系统 +用户 +组 +å­—å— +注释 +å®šä½ +路径å‰ç¼€ +文件夹 +文件 +版本 +å· +多å·åŽ‹ç¼© +åç§» +链接 +å­—å— +åˆ†å· + +64 ä½ +å¤§ç«¯åº +CPU +物ç†å¤§å° +æ–‡ä»¶å¤´å¤§å° +校验和 +ç‰¹å¾ +è™šæ‹Ÿåœ°å€ +ID +短文件å +åˆ›å»ºç¨‹åº +æ‰‡åŒºå¤§å° +æ¨¡å¼ +链接 +错误 +æ€»å¤§å° +å¯ç”¨ç©ºé—´ +ç°‡å¤§å° +å·æ ‡ +本地åç§° +供应者 +NT 安全 +äº¤æ›¿æ•°æ®æµ +Aux +已删除 +树状结构 + + +错误类型 +错误 +错误 +警告 +警告 +æ•°æ®æµ +äº¤æ›¿æ•°æ®æµ +äº¤æ›¿æ•°æ®æµå¤§å° +è™šæ‹Ÿå¤§å° +é‡Šæ”¾å¤§å° +完整物ç†å¤§å° +å·ç´¢å¼• +SubType +短评论 +代ç é¡µ + + + +å°¾éƒ¨å¤§å° +åµŒå…¥å°¾éƒ¨å¤§å° +链接类型 +硬链接 +iNode + +åªè¯» +2100 +选项 +语言 +选择语言: +编辑器 +指定编辑器(&E): +指定文件比较程åº(&D): +2200 +系统 +使用 7-Zip å…³è”的文件类型: +所有用户 +2301 +添加 7-Zip 到å³é”®èœå• +层å å³é”®èœå• +选择在å³é”®èœå•中显示的项目: +å³é”®èœå•显示图标 +2320 +<文件夹> +<档案> +打开压缩包 +æå–文件... +添加到压缩包... +测试压缩包 +æå–到当å‰ä½ç½® +æå–到 {0} +添加到 {0} +压缩并邮寄... +压缩 {0} 并邮寄 +2400 +文件夹 +工作文件夹(&W) +系统临时文件夹(&S) +当剿–‡ä»¶å¤¹(&C) +指定ä½ç½®(&S) +仅用于å¯ç§»åŠ¨è®¾å¤‡ +指定一个存放临时压缩包的ä½ç½®ã€‚ +2500 +显示 +显示「..ã€é¡¹ (åŒå‡»å‘上) +显示真实图标 +显示系统èœå• +整行选择(&F) +显示网格线(&G) +å•击打开项目 +7-Zip 传统选择模å¼(&A) +使用大内存页(&L) +2900 +关于 7-Zip +7-Zip 是一款自由软件。您å¯ä»¥é€šè¿‡æèµ çš„æ–¹å¼æ¥æ”¯æŒ 7-Zip 的开å‘。 +3000 +ç³»ç»Ÿæ— æ³•åˆ†é…æ‰€éœ€å†…å­˜ +未å‘现错误 +选定 {0} 个项目 +无法创建文件夹「{0}〠+䏿”¯æŒæ­¤åŽ‹ç¼©åŒ…çš„æ›´æ–°æ“作。 +无法作为压缩包打开文件「{0}〠+无法打开加密压缩包「{0}ã€ã€‚密ç é”™è¯¯ï¼Ÿ +䏿”¯æŒçš„åŽ‹ç¼©åŒ…æ ¼å¼ +文件 {0} 已存在 +文件「{0}ã€å·²ä¿®æ”¹ã€‚\n你想在压缩文件中更新它? +无法更新文件\n「{0}ã€ã€‚ +无法è¿è¡Œå¤–部编辑。 +此文件似乎是病毒文件(文件å中包å«å¤šä¸ªç©ºæ ¼)。 +无法为过长的路径完æˆè¯¥æ“作。 +您必须选择一个文件 +您至少è¦é€‰æ‹©ä¸€ä¸ªæ–‡ä»¶ +项目太多 +无法作为 {0} 压缩包打开该文件 +文件以 {0} æ ¼å¼æ‰“å¼€ +压缩包包å«åç§» +3300 +正在æå– +正在压缩 +正在测试 +正在打开... +正在æœç´¢... +正在移除 +3320 +正在添加 +正在更新 +æ­£åœ¨åˆ†æž +正在å¤åˆ¶ +正在打包 +正在跳过 +正在删除 +正在创建文件头 +3400 +æå– +æå–到(&X): +指定一个æå–文件的ä½ç½®ã€‚ +3410 +è·¯å¾„æ¨¡å¼ +完整路径 +无路径 +ç»å¯¹è·¯å¾„ +相对路径 +3420 +è¦†ç›–æ¨¡å¼ +在覆盖å‰è¯¢é—® +ä¸æç¤ºè‡ªåŠ¨è¦†ç›– +跳过已ç»å­˜åœ¨çš„æ–‡ä»¶ +自动é‡å‘½å +é‡å‘½å现有文件 +3430 +排除é‡å¤çš„æ ¹æ–‡ä»¶å¤¹ +æ¢å¤æ–‡ä»¶å®‰å…¨è®¾ç½® +3500 +ç¡®è®¤æ–‡ä»¶æ›¿æ¢ +此文件夹已包å«ä¸€ä¸ªç›¸åŒå称的文件。 +是å¦å°†çŽ°æœ‰æ–‡ä»¶ +替æ¢ä¸º +{0} 字节 +è‡ªåŠ¨é‡æ–°å‘½å(&U) +3700 +䏿”¯æŒçš„压缩算法「{0}ã€ã€‚ +æ•°æ®ã€Œ{0}ã€å‘生错误,文件已æŸå。 +CRC 校验「{0}ã€å¤±è´¥ï¼Œæ–‡ä»¶å·²æŸå。 +加密文件「{0}ã€æ•°æ®æœ‰è¯¯ï¼Œå¯†ç é”™è¯¯ï¼Ÿ +加密文件「{0}ã€CRC æ•°æ®æ ¡éªŒæœ‰è¯¯ï¼Œå¯†ç é”™è¯¯ï¼Ÿ +3710 +密ç é”™è¯¯ï¼Ÿ +3721 +䏿”¯æŒçš„压缩算法 +æ•°æ®é”™è¯¯ +CRC 校验失败 +æ•°æ®ä¸å¯ç”¨ +文件末端错误 +有效数æ®å¤–包å«é¢å¤–æ•°æ® +éžåŽ‹ç¼©åŒ… +头部错误 +密ç é”™è¯¯ +3763 +档案起始ä½ç½®ä¸å¯ç”¨ +无法确认档案起始ä½ç½® + + + +䏿”¯æŒæ­¤åŠŸèƒ½ +3800 +è¾“å…¥å¯†ç  +输入密ç ï¼š +釿–°è¾“入: +显示密ç (&S) +密ç ä¸åŒ¹é… +密ç åªå…许英文字符,数字,以åŠç‰¹æ®Šå­—符 (!ã€#ã€$...) +密ç è¿‡é•¿ +å¯†ç  +3900 +已用时间: +剩余时间: +总大å°ï¼š +速度: +已处ç†ï¼š +压缩率: +å‘生错误: +压缩包: +4000 +添加到压缩包 +压缩包(&A): +æ›´æ–°æ–¹å¼(&U): +压缩格å¼(&F): +压缩等级(&L): +压缩方法(&M): +字典大å°(&D): +å•è¯å¤§å°(&W): +固实数æ®å¤§å°ï¼š +CPU 线程数: +傿•°(&P): +选项 +创建自释放程åº(&X) +压缩共享文件 +加密 +加密算法: +加密文件å(&N) +压缩所需内存: +解压缩所需内存: +æ“作完æˆåŽåˆ é™¤æºæ–‡ä»¶ +4040 +ä¿å­˜ç¬¦å·é“¾æŽ¥ +ä¿å­˜ç¡¬é“¾æŽ¥ +ä¿å­˜äº¤æ›¿æ•°æ®æµ +ä¿å­˜æ–‡ä»¶å®‰å…¨è®¾ç½® +4050 +仅存储 +æžé€ŸåŽ‹ç¼© +快速压缩 +标准压缩 +最大压缩 +æžé™åŽ‹ç¼© +4060 +æ·»åŠ å¹¶æ›¿æ¢æ–‡ä»¶ +更新并添加文件 +åªåˆ·æ–°å·²å­˜åœ¨çš„æ–‡ä»¶ +åŒæ­¥åŽ‹ç¼©åŒ…å†…å®¹ +4070 +æµè§ˆ +所有文件 +éžå›ºå®ž +固实 +6000 +å¤åˆ¶ +移动 +å¤åˆ¶åˆ°ï¼š +移动到: +正在å¤åˆ¶... +正在移动... +æ­£åœ¨é‡æ–°å‘½å... +选择目标文件夹。 +䏿”¯æŒå½“剿“作 +无法é‡å‘½å文件或文件夹。 +确认文件å¤åˆ¶ +您确定å¤åˆ¶æ–‡ä»¶åˆ°åŽ‹ç¼©åŒ… +6100 +确认文件删除 +确认文件夹删除 +确认删除多个文件 +确实è¦åˆ é™¤ 「{0}ã€å—? +确实è¦åˆ é™¤æ–‡ä»¶å¤¹ã€Œ{0}ã€ä»¥åŠå…¨éƒ¨å†…容å—? +确实è¦åˆ é™¤è¿™ {0} 项? +正在删除... +无法删除文件或文件夹. +系统无法将过长路径的文件移动到回收站 +6300 +新建文件夹 +新建文件 +文件夹å称: +文件å: +新建文件夹 +新建文件.txt +无法创建文件夹 +无法新建文件 +6400 +注释 +注释(&C): +选择 +å–æ¶ˆé€‰å®š +掩ç ï¼š +6600 +属性 +æ–‡ä»¶å¤¹åŽ†å² +è¯Šæ–­ä¿¡æ¯ +ä¿¡æ¯ +7100 +我的电脑 +网络 +我的文档 +系统 +7200 +添加 +æå– +测试 +å¤åˆ¶ +移动 +删除 +ä¿¡æ¯ +7300 +分割文件 +分割文件到(&S): +分å·å¤§å°ï¼Œå­—节(&V): +正在分割... +确认分割 +您确认è¦å°†æ–‡ä»¶åˆ†å‰²ä¸º {0} 个分å·ï¼Ÿ +分å·å¤§å°å¿…é¡»å°äºŽåŽŸæ–‡ä»¶å¤§å° +分å·å¤§å°é”™è¯¯ +指定分å·å¤§å°ï¼š{0} 字节。\n您确定è¦åˆ†å‰²å½“剿–‡ä»¶å—? +7400 +åˆå¹¶æ–‡ä»¶ +åˆå¹¶æ–‡ä»¶åˆ°(&S): +正在åˆå¹¶... +请选择分å·çš„首个文件 +æ— æ³•è¯†åˆ«æ–‡ä»¶ä¸ºåŽ‹ç¼©åˆ†å· +æ— æ³•æ‰¾åˆ°å…¶ä»–åŽ‹ç¼©åˆ†å· +7500 +正在校验... +æ ¡éªŒä¿¡æ¯ +CRC æ•°æ®æ ¡éªŒï¼š +CRC æ•°æ®åŠæ–‡ä»¶å校验: +7600 +基准测试 +内存使用: +压缩 +解压缩 +评分 +总体评分 +å½“å‰ +结果 +CPU 使用率 +使用率评分 +已通过: +7700 +链接 +链接 +链接自: +链接到: +7710 +链接类型 +硬链接 +文件符å·é“¾æŽ¥ï¼ˆSymbolic Link) +目录符å·é“¾æŽ¥ï¼ˆSymbolic Link) +目录接åˆç‚¹ï¼ˆDirectory Junction) diff --git a/Utils/7-Zip/Lang/zh-tw.txt b/Utils/7-Zip/Lang/zh-tw.txt new file mode 100644 index 000000000..8b89469d7 --- /dev/null +++ b/Utils/7-Zip/Lang/zh-tw.txt @@ -0,0 +1,495 @@ +;!@Lang2@!UTF-8! +; 4.59 : Leon Tseng, sec2, ç¥ç€ +; 9.07 - 15.00 : Jack Pang : http://www.developershome.com/7-zip/ +; +; +; +; +; +; +; +; +; +0 +7-Zip +Chinese Traditional +ç¹é«”中文 +401 +確定 +å–æ¶ˆ + + + +是(&Y) +å¦(&N) +關閉(&C) +說明 + +繼續(&C) +440 +全部皆是(&A) +全部皆å¦(&L) +åœæ­¢ +釿–°é–‹å§‹ +背景作業(&B) +剿™¯ä½œæ¥­(&F) +æš«åœ(&P) +æš«åœ +您確定è¦å–消嗎? +500 +檔案(&F) +編輯(&E) +檢視(&V) +我的最愛(&A) +工具(&T) +說明(&H) +540 +開啟(&O) +在內部開啟(&I) +在外部開啟(&U) +檢視(&V) +編輯(&E) +釿–°å‘½å(&M) +複製到(&C)... +移動到(&M)... +刪除(&D) +分割檔案(&S)... +åˆä½µæª”案(&B)... +內容(&R) +註解(&N) +計算驗證值 +比較檔案 +建立資料夾 +建立檔案 +çµæŸ(&X) +é€£çµ +附加資料æµ(&A) +600 +å…¨é¸(&A) +å…¨ä¸é¸ +åå‘鏿“‡(&I) +é¸å–... +å–æ¶ˆé¸å–... +ä¾é¡žåž‹é¸å– +ä¾é¡žåž‹ä¸é¸å– +700 +大圖示(&G) +å°åœ–示(&M) +清單(&L) +詳細資料(&D) +730 +ä¸æŽ’åº +攤開檢視 +雙窗格(&2) +工具列(&T) +開啟根目錄 +上移一層 +資料夾歷程記錄... +釿–°æ•´ç†(&R) +è‡ªå‹•é‡æ–°æ•´ç† +750 +壓縮檔工具列 +標準工具列 +大型按鈕 +顯示按鈕文字 +800 +將資料夾加入我的最愛為(&A) +書籤 +900 +é¸é …(&O)... +效能測試(&B) +960 +內容(&C)... +關於 7-Zip(&A)... +1003 +路徑 +å稱 +副檔å +資料夾 +å¤§å° +å°è£å¾Œå¤§å° +屬性 +建立日期 +å­˜å–æ—¥æœŸ +修改日期 +緊密 +註解 +加密 +åˆ†å‰²å‰ +分割後 +å­—å…¸å¤§å° + +類型 +防護 +æ–¹å¼ +主機作業系統 +檔案系統 +使用者 +群組 +å€å¡Š +註解 +ä½ç½® +路徑å‰ç¶´ +資料夾 +檔案 +版本 +å· +å¤šå· +åç§» +é€£çµ +å€å¡Š +åˆ†å· + +64 ä½ +å¤§ç«¯åº +CPU +物ç†å¤§å° +æ¨™é ­å¤§å° +驗證值 +特徵 +è™›æ“¬åœ°å€ +ID +簡稱 +å‰µå»ºç¨‹å¼ +扇å€å¤§å° +æ¨¡å¼ +ç¬¦è™Ÿé€£çµ +錯誤 +å…¨éƒ¨å¤§å° +å¯ç”¨ç©ºé–“ +å¢é›†å¤§å° +標籤 +本機å稱 +æä¾›è€… +NT 安全性 +é™„åŠ è³‡æ–™æµ +Aux +刪除日期 +æ˜¯å¦æ¨¹ç‹€çµæ§‹ + + +錯誤類型 +錯誤 +錯誤 +警告 +警告 +è³‡æ–™æµ +é™„åŠ è³‡æ–™æµ +附加資料æµå¤§å° +è™›æ“¬å¤§å° +è§£å°å¾Œå¤§å° +總物ç†å¤§å° +å·ç´¢å¼• +å­é¡žåž‹ +簡短註解 +ä»£ç¢¼é  + + + +æ–‡ä»¶å°¾å¤§å° +åµŒå…¥å¼æ®˜ç«¯å¤§å° +é€£çµ +ç¡¬é€£çµ +iNode + +唯讀 +2100 +é¸é … +語言 +介é¢èªžè¨€: +編輯器 +編輯器(&E): +檔案比較程å¼(&D): +2200 +系統 +使 7-Zip 與之產生關è¯: +所有使用者 +2301 +å°‡ 7-Zip æ•´åˆåˆ°å¿«é¡¯åŠŸèƒ½è¡¨ä¸­ +串è¯å¼å¿«é¡¯åŠŸèƒ½è¡¨ +快顯功能表項目: +快顯功能表顯示圖示 +2320 +<資料夾> +<壓縮檔> +開啟壓縮檔 +解壓縮檔案... +加入壓縮檔... +測試壓縮檔 +解壓縮至此 +解壓縮至 {0} +加入 {0} +壓縮並郵寄... +å£“ç¸®æˆ {0} 並郵寄 +2400 +資料夾 +工作資料夾(&W) +系統暫存資料夾(&S) +ç›®å‰çš„資料夾(&C) +指定的資料夾(&S): +僅用於å¸é™¤å¼ç£ç¢Ÿæ©Ÿ +請指定存放暫存壓縮檔的ä½ç½®ã€‚ +2500 +設定 +顯示 ".." é …ç›® +顯示實際檔案圖示 +顯示系統é¸å–® +整列é¸å–(&F) +顯示格線(&G) +單擊開啟項目 +ä½¿ç”¨æ›¿ä»£é¸æ“‡æ¨¡å¼(&A) +使用大記憶體分é (&L) +2900 +關於 7-Zip +7-Zip 為自由軟體 +3000 +系統未能æä¾›æ‰€éœ€è¨˜æ†¶é«”空間 +沒有任何錯誤 +å·²é¸å– {0} 個物件 +無法建立資料夾 '{0}' +æ­¤å£“ç¸®æª”æœªæ”¯æ´æ›´æ–°æ“作。 +無法開啟壓縮檔 '{0}' +無法開啟加密的壓縮檔 '{0}'。錯誤的密碼? +䏿”¯æ´çš„壓縮檔類型 +檔案 {0} 已存在 +檔案 '{0}' 已被修改éŽã€‚\n您是å¦è¦åœ¨æ­¤å£“縮檔內更新檔案? +無法更新檔案\n'{0}' +無法啟動編輯器。 +檔案似是病毒(檔案åç¨±å«æœ‰å¾ˆå¤šç©ºæ ¼ï¼‰ã€‚ +ä¸èƒ½å®Œæˆæ“作,因資料夾路徑éŽé•·ã€‚ +æ‚¨å¿…é ˆé¸æ“‡ä¸€å€‹æª”案 +æ‚¨å¿…é ˆé¸æ“‡æœ€å°‘一個檔案 +é …ç›®éŽå¤š +無法開啟為 {0} 壓縮檔 +開啟為 {0} 壓縮檔 +壓縮檔以å移值開啟 +3300 +正在解壓縮 +正在壓縮 +測試 +正在開啟... +正在掃瞄... +正在移除 +3320 +正在加入 +正在更新 +æ­£åœ¨åˆ†æž +正在複製 +æ­£åœ¨é‡æ–°å°è£ +æ­£åœ¨ç•¥éŽ +正在刪除 +正在建立標頭 +3400 +解壓縮 +解壓縮至(&X): +請指定存放暫存壓縮檔的ä½ç½®ã€‚ +3410 +路徑模å¼: +完整的路徑å稱 +ä¸è¦è·¯å¾‘å稱 +絕å°è·¯å¾‘ +相å°è·¯å¾‘ +3420 +è¦†å¯«æ¨¡å¼ +覆寫å‰å…ˆè©¢å•我 +覆寫時ä¸è©¢å• +ç•¥éŽç¾æœ‰çš„æª”案 +è‡ªå‹•é‡æ–°å‘½å +è‡ªå‹•é‡æ–°å‘½åç¾æœ‰çš„æª”案 +3430 +刪除é‡è¤‡çš„æ ¹ç›®éŒ„ +æ¢å¾©æª”案安全設定 +3500 +確èªå–代檔案 +目的資料夾已包å«è¦è™•ç†çš„æª”案。 +您è¦å–ä»£ç¾æœ‰çš„æª”案 +而改用這個檔案嗎? +{0} ä½å…ƒçµ„ +è‡ªå‹•é‡æ–°å‘½å(&U) +3700 +'{0}' æœªæ”¯æ´æ­¤å£“縮方å¼ã€‚ +'{0}' ä¸­çš„è³‡æ–™å«æœ‰éŒ¯èª¤ã€‚檔案已æå£žã€‚ +'{0}' çš„ CRC 驗證失敗。檔案已æå£žã€‚ +加密檔 '{0}' ä¸­çš„è³‡æ–™å«æœ‰éŒ¯èª¤ã€‚錯誤的密碼? +加密檔 '{0}' çš„ CRC 驗證失敗。錯誤的密碼? +3710 +錯誤的密碼? +3721 +䏿”¯æ´æ­¤å£“ç¸®æ–¹å¼ +è³‡æ–™å«æœ‰éŒ¯èª¤ +CRC 驗證失敗 +資料ä¸èƒ½ä½¿ç”¨ +è³‡æ–™ç•°å¸¸çµ‚çµ +有效負載盡頭外還有其他資料 +䏿˜¯å£“縮檔 +標頭錯誤 +密碼錯誤 +3763 +壓縮檔的開端ä¸èƒ½ä½¿ç”¨ +未能確èªå£“縮檔的開端 + + + +䏿”¯æ´çš„功能 +3800 +輸入密碼 +輸入密碼: +釿–°è¼¸å…¥å¯†ç¢¼: +顯示密碼(&S) +密碼ä¸ä¸€è‡´ +僅能使用英文字æ¯ã€æ•¸å­—和特殊字元 (!, #, $, ...) 當作密碼 +密碼太長 +密碼 +3900 +ç¶“éŽæ™‚é–“: +剩餘時間: +大å°: +速度: +已處ç†: +壓縮率: +錯誤數: +壓縮檔: +4000 +加入壓縮檔 +壓縮檔(&A): +更新模å¼(&U): +壓縮檔格å¼(&F): +壓縮層級(&L): +壓縮方å¼(&M): +字典大å°(&D): +字組大å°(&W): +çµå¯¦å€å¡Šå¤§å°: +CPU 線程數: +åƒæ•¸(&P): +é¸é … +建立自解壓縮檔(&X) +壓縮共用檔案 +加密 +加密方法: +加密檔å(&N) +壓縮時記憶體使用: +解壓縮時記憶體使用: +壓縮後刪除檔案 +4040 +å„²å­˜ç¬¦è™Ÿé€£çµ +å„²å­˜ç¡¬é€£çµ +å„²å­˜é™„åŠ è³‡æ–™æµ +儲存檔案安全設定 +4050 +å°å­˜ +最快速壓縮 +快速壓縮 +一般壓縮 +最大壓縮 +極致壓縮 +4060 +加入並å–代檔案 +更新並加入檔案 +æ›´æ–°ç¾æœ‰çš„æª”案 +åŒæ­¥è™•ç†æª”案 +4070 +ç€è¦½ +所有檔案 +éžçµå¯¦ +çµå¯¦ +6000 +複製 +移動 +複製到: +移動到: +正在複製... +正在移動... +æ­£åœ¨é‡æ–°å‘½å... +鏿“‡ç›®æ¨™è³‡æ–™å¤¾ã€‚ +未支æ´çš„æ“ä½œã€‚ +釿–°å‘½å檔案或資料夾時發生錯誤 +確èªè¤‡è£½æª”案 +您確定è¦è¤‡è£½æª”案至壓縮檔? +6100 +確èªåˆªé™¤æª”案 +確èªåˆªé™¤è³‡æ–™å¤¾ +確èªåˆªé™¤å¤šå€‹æª”案 +您確定è¦åˆªé™¤ '{0}' å—Ž? +您確定è¦åˆªé™¤è³‡æ–™å¤¾ '{0}' 以åŠå®ƒæ‰€æœ‰çš„內容嗎? +您確定è¦åˆªé™¤é€™ {0} 個項目嗎? +正在刪除... +刪除檔案或資料夾時發生錯誤 +系統ä¸èƒ½ç§»å‹•路徑éŽé•·çš„æª”案到資æºå›žæ”¶ç­’ +6300 +建立資料夾 +建立檔案 +資料夾å稱: +檔案å稱: +新增資料夾 +新增檔案 +建立資料夾時發生錯誤 +建立檔案時發生錯誤 +6400 +註解 +註解(&C): +é¸å– +å–æ¶ˆé¸å– +é®ç½©: +6600 +內容 +資料夾歷程記錄 +è¨ºæ–·è¨Šæ¯ +è¨Šæ¯ +7100 +電腦 +網路 +文件 +系統 +7200 +加入 +解壓縮 +測試 +複製 +移動 +刪除 +資訊 +7300 +分割檔案 +分割到(&S): +分割壓縮檔,ä½å…ƒçµ„(&V): +正在分割... +確èªåˆ†å‰² +您確定è¦åˆ†å‰²æª”案為 {0} 個? +分割大å°å¿…é ˆå°æ–¼åŽŸå§‹æª”æ¡ˆå¤§å° +䏿­£ç¢ºçš„åˆ†å‰²å¤§å° +指定的分割大å°: {0} ä½å…ƒçµ„。\n您確定è¦åˆ†å‰²ç‚ºé€™äº›å£“縮檔嗎? +7400 +åˆä½µæª”案 +åˆä½µåˆ°(&C): +正在åˆä½µ... +僅é¸å–第一個檔案 +æœªèƒ½ç¢ºèªæ­¤æª”案為完整檔案分割出來的一部分 +找ä¸åˆ°å®Œæ•´æª”案的其他部分 +7500 +正在計算驗證值... +驗證值資訊 +資料的 CRC 驗證值: +資料åŠå稱的 CRC 驗證值: +7600 +效能測試 +記憶體使用: +壓縮 +解壓縮 +è©•ç­‰ +整體評等 +ç›®å‰ +çµæžœ +CPU 使用 +è©•ç­‰ / 使用 +é€šéŽæ•¸: +7700 +é€£çµ +é€£çµ +連çµè‡ª: +連çµåˆ°: +7710 +連çµé¡žåž‹ +ç¡¬é€£çµ +æª”æ¡ˆç¬¦è™Ÿé€£çµ +ç›®éŒ„ç¬¦è™Ÿé€£çµ +目錄連接點 diff --git a/Moose Mission Setup/Moose Mission Update/License.txt b/Utils/7-Zip/License.txt similarity index 100% rename from Moose Mission Setup/Moose Mission Update/License.txt rename to Utils/7-Zip/License.txt diff --git a/Moose Mission Setup/Moose Mission Update/Uninstall.exe b/Utils/7-Zip/Uninstall.exe similarity index 97% rename from Moose Mission Setup/Moose Mission Update/Uninstall.exe rename to Utils/7-Zip/Uninstall.exe index e214183c8..dab4e4e8a 100644 Binary files a/Moose Mission Setup/Moose Mission Update/Uninstall.exe and b/Utils/7-Zip/Uninstall.exe differ diff --git a/Moose Mission Setup/Moose Mission Update/descript.ion b/Utils/7-Zip/descript.ion similarity index 100% rename from Moose Mission Setup/Moose Mission Update/descript.ion rename to Utils/7-Zip/descript.ion diff --git a/Moose Mission Setup/Moose Mission Update/readme.txt b/Utils/7-Zip/readme.txt similarity index 99% rename from Moose Mission Setup/Moose Mission Update/readme.txt rename to Utils/7-Zip/readme.txt index c6a5e53c1..228e9a0ce 100644 --- a/Moose Mission Setup/Moose Mission Update/readme.txt +++ b/Utils/7-Zip/readme.txt @@ -1,4 +1,4 @@ -7-Zip 16.02 +7-Zip 16.04 ----------- 7-Zip is a file archiver for Windows NT / 2000 / 2003 / 2008 / 2012 / XP / Vista / 7 / 8 / 10. diff --git a/Utils/Deploy.sh b/Utils/Deploy.sh new file mode 100644 index 000000000..5527631f0 --- /dev/null +++ b/Utils/Deploy.sh @@ -0,0 +1,5 @@ +cd slate +git add -A +git commit -a -m "Doc Update" +git push +./deploy.sh \ No newline at end of file diff --git a/Utils/GenerateDocumentations.bat b/Utils/GenerateDocumentations.bat new file mode 100644 index 000000000..144cc2a6f --- /dev/null +++ b/Utils/GenerateDocumentations.bat @@ -0,0 +1,13 @@ +@echo off + +:: Generate Luadocumentor documentation +echo Generating LuaDocumentor Documentation +echo -------------------------------------- +call luadocumentor.bat + +rem :: Generate Slate documentation +rem echo Generating Slate Documentation +rem echo ------------------------------ +rem cd "Slate Documentation Generator" +rem call Generate.bat +rem cd .. \ No newline at end of file diff --git a/Utils/Patterns.txt b/Utils/Patterns.txt new file mode 100644 index 000000000..30f87edaa --- /dev/null +++ b/Utils/Patterns.txt @@ -0,0 +1,19 @@ +Replace hyperlinks from md to bb +\[(.*?)\]\((.*?)\) +[URL="$2"]$1[/URL] + +Replace bold from md to bb +\*\*(.*?)\*\* +[b]$1[/b] + +Replace heading 3 from md to bb +### (.*?)$ +[SIZE=5]$1[/SIZE] + +Replace heading 2 from md to bb +## (.*?)$ +[SIZE=6]$1[/SIZE] + +Replace heading 1 from md to bb +# (.*?)$ +[SIZE=7]$1[/SIZE] diff --git a/Utils/Slate Documentation Generator/Generate.bat b/Utils/Slate Documentation Generator/Generate.bat new file mode 100644 index 000000000..bcd2fd145 --- /dev/null +++ b/Utils/Slate Documentation Generator/Generate.bat @@ -0,0 +1,16 @@ +@echo off +:: Generate the Markdown doc +"./bin/SlateDocGenerator2.exe" "../../../MOOSE/Moose Development/Moose" ../../../slate/source + +:: Do some cleanup +del /s /q "TreeHierarchySorted.csv" +del /s /q "FuctionList.txt" +rmdir /s /q "./bin/TEMP" + +:: Copy the Images that go with the documentation +robocopy ../../docs/Presentations ../../../slate/source/includes/Pictures /MIR /NJH /NJS + +:: Deploy the Slate documentation +echo A shell will open. To deploy the Slate website, please run the following command. Otherwise, simply close the shell. +echo $ Moose/Utils/Deploy.sh +%localappdata%\GitHub\GitHub.appref-ms --open-shell \ No newline at end of file diff --git a/Moose Development Evironment Setup/MooseDevelopmentEnvironmentSetup.exe b/Utils/Slate Documentation Generator/bin/SlateDocGenerator2.exe similarity index 87% rename from Moose Development Evironment Setup/MooseDevelopmentEnvironmentSetup.exe rename to Utils/Slate Documentation Generator/bin/SlateDocGenerator2.exe index 507bf8bf5..ad688e4b7 100644 Binary files a/Moose Development Evironment Setup/MooseDevelopmentEnvironmentSetup.exe and b/Utils/Slate Documentation Generator/bin/SlateDocGenerator2.exe differ diff --git a/Utils/Slate Documentation Generator/src/DataStorer.au3 b/Utils/Slate Documentation Generator/src/DataStorer.au3 new file mode 100644 index 000000000..b5903009b --- /dev/null +++ b/Utils/Slate Documentation Generator/src/DataStorer.au3 @@ -0,0 +1,174 @@ +; This file only constains function related to storing the hierarchy in a tree-like tructure + +Func AddNode($Kind, $Module ,$Node, $Parent, $File, $CarretPos) + FileSetPos($DataFile, 0, $FILE_END) + + If $Parent == "" And $Kind == "type" Then + $Parent = "ROOT" + ElseIf $Kind == "module" Then + $Module = " " + $Parent = " " + EndIf + FileWrite($DataFile, "@K="&$Kind&", @M="&$Module&", @N="&$Node&", @P="&$Parent&", @F="&$File&", @C="&$CarretPos&","&@CRLF) +EndFunc + +; Search node by name and returns one data +Func GetData($Node, $Data) + FileSetPos($DataFile, 0, $FILE_BEGIN) + Local $CurrentLine = "" + Local $CurrentData + Local $RegexResult + Local $Regex + Switch $Data + Case "kind" + $Regex = "\@K=(.+?)," + Case "parent" + $Regex = "\@P=(.+?)," + Case "file" + $Regex = "\@F=(.+?)," + Case "carretpos" + $Regex = "\@C=(.+?)," + EndSwitch + FileSetPos($DataFile, 0, $FILE_BEGIN) + Do + $CurrentLine = FileReadLine($DataFile) + If @error == -1 Then + Return "" + ExitLoop + EndIf + $CurrentData = StringRegExp($CurrentLine, "\@N=(.+?),", $STR_REGEXPARRAYMATCH) + + Until $Node == $CurrentData[0] + $CurrentData = StringRegExp($CurrentLine, $Regex, $STR_REGEXPARRAYMATCH) + Return $CurrentData[0] +EndFunc + + +; Returns an array of parent nodes, up to the root, starting with the root +Func GetParents($Node) + Local $CurrentParent = $Node + Local $ParentsArray[0] + Local $NbOfParents = 1 + + While $CurrentParent <> "ROOT" + ReDim $ParentsArray[$NbOfParents] + $ParentsArray[$NbOfParents-1] = $CurrentParent + + $CurrentParent = GetData($CurrentParent, "parent") + If $CurrentParent == "" Then + FileWrite($Log, "ERROR : Couldn't find "&$ParentsArray[$NbOfParents-1]&"'s parent !") + $CurrentParent = "ERROR !" + ReDim $ParentsArray[$NbOfParents] + $ParentsArray[$NbOfParents-1] = $CurrentParent + ExitLoop + EndIf + $NbOfParents += 1 + WEnd + + _ArrayReverse($ParentsArray) + _ArrayDelete($ParentsArray, $NbOfParents) + Return $ParentsArray +EndFunc + + + +Func DataSort() + Local $SortedDataFile = FileOpen(@ScriptDir & "\TreeHierarchySorted.csv", $FO_OVERWRITE) + Local $Line = "" + Local $LineNb = 1 + Local $RegexResults + Local $CurrentModule + Local $CurrentType + + FileSetPos($DataFile, 0, $FILE_BEGIN) + + While True + $Line = FileReadLine($DataFile) + If @error then ExitLoop + + $RegexResults = StringRegExp($Line, "\@K=(.+?),", $STR_REGEXPARRAYMATCH) + If $RegexResults[0] == "module" Then + ConsoleWrite(".") + $RegexResults = StringRegExp($Line, "\@N=(.+?),", $STR_REGEXPARRAYMATCH) + $CurrentModule = $RegexResults[0] + FileWriteLine($SortedDataFile, $Line) + FileClose($DataFile) + _FileWriteToLine(@ScriptDir & "\TreeHierarchy.csv", $LineNb, "", True) + $DataFile = FileOpen(@ScriptDir & "\TreeHierarchy.csv", 1) + FileSetPos($DataFile, 0, $FILE_BEGIN) + $LineNb = 1 + + While True + $Line = FileReadLine($DataFile) + If @error then ExitLoop + + $RegexResults = StringRegExp($Line, "\@K=(.+?),", $STR_REGEXPARRAYMATCH) + If $RegexResults[0] == "type" Then + $RegexResults = StringRegExp($Line, "\@M=(.+?),", $STR_REGEXPARRAYMATCH) + If $RegexResults[0] == $CurrentModule Then + $RegexResults = StringRegExp($Line, "\@N=(.+?),", $STR_REGEXPARRAYMATCH) + $CurrentType = $RegexResults[0] + FileWriteLine($SortedDataFile, $Line) + FileClose($DataFile) + _FileWriteToLine(@ScriptDir & "\TreeHierarchy.csv", $LineNb, "", True) + $DataFile = FileOpen(@ScriptDir & "\TreeHierarchy.csv", 1) + FileSetPos($DataFile, 0, $FILE_BEGIN) + $LineNb = 1 + + While True + $Line = FileReadLine($DataFile) + If @error then ExitLoop + + $RegexResults = StringRegExp($Line, "\@K=(.+?),", $STR_REGEXPARRAYMATCH) + If $RegexResults[0] == "function" Then + $RegexResults = StringRegExp($Line, "\@P=(.+?),", $STR_REGEXPARRAYMATCH) + If $RegexResults[0] == $CurrentType Then + FileWriteLine($SortedDataFile, $Line) + FileClose($DataFile) + _FileWriteToLine(@ScriptDir & "\TreeHierarchy.csv", $LineNb, "", True) + $DataFile = FileOpen(@ScriptDir & "\TreeHierarchy.csv", 1) + FileSetPos($DataFile, 0, $FILE_BEGIN) + $LineNb = 0 + EndIf + EndIf + $LineNb += 1 + WEnd + FileSetPos($DataFile, 0, $FILE_BEGIN) + $LineNb = 0 + EndIf + EndIf + $LineNb += 1 + WEnd + FileSetPos($DataFile, 0, $FILE_BEGIN) + $LineNb = 0 + EndIf + $LineNb += 1 + Wend + If FileGetSize(@ScriptDir & "\TreeHierarchy.csv") <> 0 Then + FileWrite($Log, "ERROR : Some items couldn't be sorted. Verify them in the file TreeHierarchy.csv"&@CRLF) + ConsoleWrite(@CRLF&"INFO : Some items couldn't be sorted. Verify them in the file TreeHierarchy.csv"&@CRLF) + EndIf + FileClose($DataFile) + $DataFile = $SortedDataFile +EndFunc + + +Func FindInFunctionList($String) + Local $Line = "" + Local $TempStringArray + FileSetPos($FunctionList, 0, $FILE_BEGIN) + ;FileWrite($Log, 'Trying to find the function prototype for : ' & $String & @CRLF) + While 1 + $Line = FileReadLine($FunctionList) + If @error = -1 Then + SetError(0) + FileWrite($Log, "ERROR : Couldn't find " & $String & " in file. Does this method exitsts ?" & @CRLF) + Return $String + EndIf + If StringInStr($Line, $String) Then + $TempStringArray = StringSplit($Line, "-") + $Line = "[" & $TempStringArray[1] & ":" & $TempStringArray[2] & "()]" & '(#' & StringLower($Line) & ')' + Return $Line + EndIf + WEnd +EndFunc \ No newline at end of file diff --git a/Utils/Slate Documentation Generator/src/Parser.au3 b/Utils/Slate Documentation Generator/src/Parser.au3 new file mode 100644 index 000000000..307b98d20 --- /dev/null +++ b/Utils/Slate Documentation Generator/src/Parser.au3 @@ -0,0 +1,460 @@ +; This file include every function strictly related to the parsing of data in .lua files + +; Get the first comment block after $CarretPos +; We will also grab function declaration if possible/applicable +; The return is a Array : CarretPosition|BlockContent|Declaration|CarretPositionStart +Func ReadNextBlock($File, $CarretPos) + local $CommentBlock = "" ; This is where we'll store the comment block + local $Declaration = "" ; This is the next line after the comment block : usually the declaration statement + local $CurrentLine = "" + local $CurrentCarretPos = 0 + + local $IsCommentBlock = False + + local $RegExResult + local $RegexPos + + ; Start reading from $CarretPos + FileSetPos($File, $CarretPos, $FILE_BEGIN) + + ; Read till we find a comment block + Do + $CurrentLine = FileReadLine($File) + If @error Then ; We probably reached the eof + Local $ReturnArray[3] = [$CurrentCarretPos, "", ""] + Return $ReturnArray + ElseIf StringInStr($CurrentLine, "---") Then + $IsCommentBlock = True + EndIf + Until $IsCommentBlock + + Local $CarretPosStart = FileGetPos($File) - StringLen($CurrentLine) - 2 + + ; Add the first line to our comment block + $RegExResult = StringRegExp($CurrentLine, "---(.*)", $STR_REGEXPARRAYMATCH) + If Not @error Then ; The first line of the comment could be empty ! + $CommentBlock &= $RegExResult[0]&@CRLF + EndIf + + ; Read the comment block + Do + $CurrentCarretPos = FileGetPos($File) + $CurrentLine = FileReadLine($File) + If StringInStr($CurrentLine, "--") Then ; If we can't find any "--" in the line, then it's not the comment block anymore + $RegExResult = StringRegExp($CurrentLine, "--(.*)", $STR_REGEXPARRAYMATCH) + If Not @error Then; The line of the comment could be empty ! + $CommentBlock &= $RegExResult[0]&@CRLF + EndIf + Else + $IsCommentBlock = False + EndIf + Until Not $IsCommentBlock + + ; Ok, so now this is strange. If the comment block is class', we're going to have to check the + ; next comment block. If this next comment block contains a field, that is the same name as the class, then this + ; new comment block contains the whole informtion for the class. This is very shitty, but it's a workaround to + ; make intellisense show classes info while programing + If ParseForOneTag($CommentBlock, "@type") Then + Local $CommentBlock2 = "" + Do + $CurrentLine = FileReadLine($File) + If @error Then + Local $ReturnArray[3] = [$CurrentCarretPos, "", ""] + Return $ReturnArray + ElseIf StringInStr($CurrentLine, "---") Then + $IsCommentBlock = True + EndIf + Until $IsCommentBlock + + $RegExResult = StringRegExp($CurrentLine, "---(.*)", $STR_REGEXPARRAYMATCH) + If Not @error Then + $CommentBlock2 &= $RegExResult[0]&@CRLF + EndIf + + ; Yep, the next comment is the description of the class, let's read on ! + If StringInStr($CurrentLine, ParseForOneTag($CommentBlock, "@type")) And StringInStr($CurrentLine, "extend") Then + + Do + $CurrentLine = FileReadLine($File) + If StringInStr($CurrentLine, "--") Then + $RegExResult = StringRegExp($CurrentLine, "--(.*)", $STR_REGEXPARRAYMATCH) + If Not @error Then + $CommentBlock2 &= $RegExResult[0]&@CRLF + EndIf + Else + $IsCommentBlock = False + EndIf + Until Not $IsCommentBlock + + ; remove the line(s) with "@field" in the comment block. They are only needed for the intellisense hack + While 1 + $RegexResult = StringRegExp($CommentBlock2, "(.*)@field(.*)", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $CommentBlock2 = StringRegExpReplace($CommentBlock2, "(.*)@field(.*)", "", 1) + WEnd + + ; We also don't need the first line of the first comment block anymore... + ; $CommentBlock = StringRegExpReplace($CommentBlock, "(.*)", "", 1) + + ; append the description at the start of the comment block + $CommentBlock = $CommentBlock2&$CommentBlock + EndIf + + + ; We also need to check if the type is a list or a map. If so, the comment block does not describe a class, but a simple list / map. + ; It will have the formatting of a class, though, because it's closer closer to the actual code, even though it is highly confusing. + ; But it will only have 1 field : the list or map. + If StringInStr($CommentBlock, "@list") Then + $RegExResult = StringRegExp($CommentBlock, "@list\h<(.*?)>\h(.*)", $STR_REGEXPARRAYMATCH) + if not @error Then + $CommentBlock &= "@field #table["&$RegExResult[0]&"] "&$RegExResult[1] + EndIf + EndIf + ; TODO : Add support for @map the same way... + EndIf + + + + + ; We'll take the next line, as it might be the declaration statement + $Declaration = $CurrentLine + + + + ; let's do some cleanup + $CommentBlock = StringRegExpReplace($CommentBlock, "(?m)^\h+", "") ;remove leading whitespaces + $CommentBlock = StringRegExpReplace($CommentBlock, "(?m)\h+$", "") ;remove trailing whitespaces + $CommentBlock = StringRegExpReplace($CommentBlock, "(?m)^[#]+", "##### ") + $CommentBlock = StringRegExpReplace($CommentBlock, "(?m)^\h+", "") ;remove leading whitespaces again now that we removed the "#"s + $CommentBlock = StringRegExpReplace($CommentBlock, "(?m)-{3,}", "") ;remove sequences of at least 3 "-" which will mess up markdown + $CommentBlock = StringRegExpReplace($CommentBlock, "(?m)={3,}", "") ; remove sequences of at least 3 "=" which will mess up markdown + + Local $ReturnArray[4] = [$CurrentCarretPos, $CommentBlock, $Declaration, $CarretPosStart] + Return $ReturnArray +EndFunc + +; Parses the block and returns the data for one tag +; don't use it to find the function tag ! +Func ParseForOneTag($Block, $Tag) + Local $i = 1 + Local $DataArray[1] + Local $RegexResult[1] + Local $RegexPos = 1 + Local $Regex + + ; If we look for @usage, then it's a multiline data, the regex is different + If $Tag == "@usage" Then + $Regex = "(?s)@usage(.*)" + $RegexResult = StringRegExp($Block, $Regex, $STR_REGEXPARRAYMATCH, $RegexPos) + Else + $Regex = $Tag&"\h(.*)\s" + $RegexResult = StringRegExp($Block, $Regex, $STR_REGEXPARRAYMATCH, $RegexPos) + Endif + + If @error Then + Return "" + Else + Return $RegexResult[0] + EndIf + +EndFunc ;==>ReadOneTag + +; Parses the block and returns the data for multiple tags in an array +; Don't use it for @param ! +Func ParseForTags($Block, $Tag) + Local $i = 1 + Local $DataArray[1] + Local $RegexResult[1] + Local $RegexPos = 1 + + Local $Regex = $Tag&"(?m)\h([^\s]*)(?:\h)?([^\s]*)?(?:\h)?(.*)?$" + ; For each tag + While True + $RegexResult = StringRegExp($Block, $Regex, $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If $RegexPos == 0 Then ; We couldn't find any tag + If Not $DataArray[0] Then + Return "" + Else + Return $DataArray + EndIf + EndIf + + ; Add the tag to the array.The array looks like this : type1|param1|description1|type2... + ReDim $DataArray[$i * 3] + $DataArray[($i * 3) - 3] = $RegexResult[0] + If $RegexResult[1] == "" Then + $DataArray[($i * 3) - 2] = "self" ; if the first param doesn't have a name, then it's self + Else + $DataArray[($i * 3) - 2] = $RegexResult[1] + EndIf + $DataArray[($i * 3) - 1] = $RegexResult[2] + $i += 1 + WEnd +EndFunc + +; Parses both the comment block and the declaration to find the function name and it's type +; Compares both of them if possible, but will always return the one in the comment block if possible +Func ParseFunctionName($CommentBlock, $Declaration) + local $RegExResult + local $FunctionNameFromDec + local $FunctionNameFromComment + local $ReturnArray[2] + + ; Parse for function name in both the comment block and the desclaration + $RegExResult = StringRegExp($CommentBlock, "\@function\h(?:(\[.*\]\h))?(.*)", $STR_REGEXPARRAYMATCH) + If Not @error Then + $FunctionNameFromComment = $RegExResult[1] + EndIf + $RegExResult = StringRegExp($Declaration, "function\h(?:.*\:)?(.*)\(.*\)", $STR_REGEXPARRAYMATCH) + If Not @error Then + $FunctionNameFromDec = $RegExResult[0] + EndIf + + ; compare them to each other + If $FunctionNameFromComment Then + If $FunctionNameFromDec <> $FunctionNameFromComment Then + FileWrite($Log,"CAUTION : The commented function doesn't match its declaration : "&$FunctionNameFromComment& " -> "&$Declaration&@CRLF) + EndIf + $ReturnArray[0] = $FunctionNameFromComment + ElseIf $FunctionNameFromDec Then + ;FileWrite($Log, "CAUTION: No data matching @function found in block, inferring the function name from its declaration : "& $FunctionNameFromDec & @CRLF) + $ReturnArray[0] = $FunctionNameFromDec + Else + $ReturnArray[0] = "" + $ReturnArray[1] = "" + return $ReturnArray + EndIf + + ;parses for function type in both the comment block and the desclaration + local $TypeFromComment + local $TypeFromDec + + $RegExResult = StringRegExp($Declaration, "function\h(.*):", $STR_REGEXPARRAYMATCH) + If Not @error Then + $TypeFromDec = $RegExResult[0] + EndIf + $RegExResult = StringRegExp($CommentBlock, "function\h\[parent=#(.*)\]", $STR_REGEXPARRAYMATCH) + If Not @error Then + $TypeFromComment = $RegExResult[0] + EndIf + + ; compare them to each other + If $TypeFromComment Then + If $TypeFromDec <> $TypeFromComment Then + FileWrite($Log,"CAUTION : The commented function type doesn't match its declaration : "&$TypeFromComment& " -> "&$Declaration&@CRLF) + EndIf + $ReturnArray[1] = $TypeFromComment + ElseIf $TypeFromDec Then + ;FileWrite($Log, "CAUTION: No function type found in block, inferring the function type from its declaration : "& $TypeFromDec & @CRLF) + $ReturnArray[1] = $TypeFromDec + Else + $ReturnArray[0] = "" + $ReturnArray[1] = "" + return $ReturnArray + EndIf + + Return $ReturnArray +EndFunc + +; Specifically designed to parse for @param tags +; will verify the comment by matching with the declaration (theoretically, I'm pretty sure it's bugged) +Func ParseParams($CommentBlock, $Declaration) + Local $ParamsFromComment = ParseForTags($CommentBlock, "@param") + Local $RegExResult + Local $RegexPos = StringInStr($Declaration, "(") + Local $ParamsFromDec[0] + Local $NbParam = 0 + + If StringInStr($Declaration, ":") Then + $NbParam = 1 + ReDim $ParamsFromDec[1] + $ParamsFromDec[0] = "self" + EndIf + + ; extract params from function decaration + While True + $RegExResult = StringRegExp($Declaration, "([^,\(\)\h]+)", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NbParam += 1 + Redim $ParamsFromDec[$NbParam] + $ParamsFromDec[$NbParam-1] = $RegExResult[0] + WEnd + + ; compare these parameters with those found in the comment block + If UBound($ParamsFromComment) <> UBound($ParamsFromDec)*3 Then + FileWrite($Log, "CAUTION: The number of parameters don't match between the comment block and declaration "& @CRLF) + Else + + For $i=0 To $NbParam-1 + If $ParamsFromDec[$i] <> $ParamsFromComment[($i*3)+1] Then + FileWrite($Log, "CAUTION: Parameters missmatch between the comment block and declaration "& @CRLF) + FileWrite($Log, $ParamsFromComment[($i*3)+1]& " -> " & $ParamsFromDec[$i]&@CRLF) + ExitLoop + EndIf + Next + EndIf + + Return $ParamsFromComment +EndFunc + +; This does 3 things : +; - Replace the hyperlinks with new ones +; - change the stuff starting with # (#nil -> Nil) +; - Replace pictures paths +Func ReplaceHyperlinks($TempFile) + Local $StringFile = "" + Local $RegexResult + Local $RegexPos = 1 + Local $NewURL = "" + Local $i = 0 + FileSetPos($TempFile, 0, $FILE_BEGIN) + + $StringFile = FileRead($TempFile) + + ; Replace HyperLinks Using Regexs + ; --------------------------------------------------------- + While 1 ; @{File.Module} + $RegexResult = StringRegExp($StringFile, "\@{([A-Z][^\.#}]+)\.([^\.#}]+)}", $STR_REGEXPARRAYMATCH, $RegexPos) ; + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = "[" & $RegexResult[1] & "](#" & StringLower($RegexResult[1]) & "-module-)" + ;FileWrite($Log, "Module : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{([A-Z][^\.#}]+)\.([^\.#}]+)}", $NewURL, 1) + WEnd + While 1 ; @{Module} + $RegexResult = StringRegExp($StringFile, "\@{([A-Z][^\.#}]+)}", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = "[" & $RegexResult[0] & "](#" & StringLower($RegexResult[0]) & "-module-)" + ;FileWrite($Log, "Module : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{([A-Z][^\.#}]+)}", $NewURL, 1) + WEnd + While 1 ; @{File.Module#TYPE} + $RegexResult = StringRegExp($StringFile, "\@{([A-Z][^\.#}]+)\.([A-Z][^\.#}]+)#([A-Z,_]+)}", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = "[" & $RegexResult[2] & "](#" & StringLower($RegexResult[2]) & "-class-)" + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{([A-Z][^\.#}]+)\.([A-Z][^\.#}]+)#([A-Z,_]+)}", $NewURL, 1) + WEnd + While 1 ; @{Module#TYPE} + $RegexResult = StringRegExp($StringFile, "\@{([A-Z][^\.#}]+)#([A-Z,_]+)}", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = "[" & $RegexResult[1] & "](#" & StringLower($RegexResult[1]) & "-class-)" + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{([A-Z][^\.#}]+)#([A-Z,_]+)}", $NewURL, 1) + WEnd + While 1 ; @{#TYPE} + $RegexResult = StringRegExp($StringFile, "\@{#([A-Z,_]+)}", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = "[" & $RegexResult[0] & "](#" & StringLower($RegexResult[0]) & "-class-)" + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{#([A-Z,_]+)}", $NewURL, 1) + WEnd + While 1 ; #TYPE&@CR + $RegexResult = StringRegExp($StringFile, "\h#([A-Z,_]+)\s", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = " [" & $RegexResult[0] & "](#" & StringLower($RegexResult[0]) & "-class-)"&@CRLF + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\h#([A-Z,_]+)\s", $NewURL, 1) + WEnd + While 1 ; @{File.Module#TYPE.Function}(), catches the parenthesis + $RegexResult = StringRegExp($StringFile, "\@{([A-Z][^#}\.]+)\.([A-Z][^#}\.]+)#([A-Z,_]+)\.([^#\.]+)}[\(]?[\)]?", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = FindInFunctionList($RegexResult[2] & "-" & $RegexResult[3]&"-") + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{([A-Z][^#}\.]+)\.([A-Z][^#}\.]+)#([A-Z,_]+)\.([^#\.]+)}[\(]?[\)]?", $NewURL, 1) + WEnd + While 1 ; @{Module#TYPE.Function}(), catches the parenthesis + $RegexResult = StringRegExp($StringFile, "\@{([A-Z][^#}\.]+)#([A-Z,_]+)\.([^#}\.]+)}[\(]?[\)]?", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = FindInFunctionList($RegexResult[1] & "-" & $RegexResult[2]&"-") + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{([A-Z][^#}\.]+)#([A-Z,_]+)\.([^#}\.]+)}[\(]?[\)]?", $NewURL, 1) + WEnd + While 1 ; @{#TYPE.Function}(), catches the parenthesis + $RegexResult = StringRegExp($StringFile, "\@{#([A-Z,_]+)\.([^#}\.]+)}[\(]?[\)]?", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = FindInFunctionList($RegexResult[0] & "-" & $RegexResult[1]&"-") + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\@{#([A-Z,_]+)\.([^#}\.]+)}[\(]?[\)]?", $NewURL, 1) + WEnd + While 1 ; Module#TYPE + $RegexResult = StringRegExp($StringFile, "\h(\w+[^\h\_])#(.*?)\h", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = " [" & $RegexResult[1] & "](#" & StringLower($RegexResult[1]) & "-class-) " + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\h(\w+[^\h\_])#(.*?)\h", $NewURL, 1) + WEnd + While 1 ; File.Module#TYPE + $RegexResult = StringRegExp($StringFile, "\h(\w+)\.(\w+)#(.*?)\h", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = " [" & $RegexResult[2] & "](#" & StringLower($RegexResult[2]) & "-class-) " + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\h(\w+)\.(\w+)#(.*?)\h", $NewURL, 1) + WEnd + While 1 ; #TYPE.type (nested type... really annoying and confusing lua stuff) + $RegexResult = StringRegExp($StringFile, "\h#([A-Z,_]+)\.(\w+)\h", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewURL = " [" & $RegexResult[1] & "](#" &StringLower($RegexResult[0])& "-"& StringLower($RegexResult[1]) & "-class-)" + ;FileWrite($Log, "Class : " & $RegexPos & " : " & _ArrayToString($RegexResult) & " -> " & $NewURL & @CRLF) + $StringFile = StringRegExpReplace($StringFile, "\h#([A-Z,_]+)\.(\w+)\h", $NewURL, 1) + WEnd + + ; Clean stuff with # + ; --------------------------------------------------------- + $StringFile = StringReplace($StringFile, "#nil", "Nil") + $StringFile = StringReplace($StringFile, "#number", "Number") + $StringFile = StringReplace($StringFile, "#boolean", "Boolean") + $StringFile = StringReplace($StringFile, "#string", "String") + $StringFile = StringReplace($StringFile, "#table", "List[]") + $StringFile = StringReplace($StringFile, "#function", "Function()") + + ; And replace the pictures Path if any + ; --------------------------------------------------------- + While 1 + $RegexResult = StringRegExp($StringFile, "!\[(.*)\]\(.*\\(.*)\\(.*)\)", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + $NewPic = "![" & $RegexResult[0] & "](/includes/Pictures/" & $RegexResult[1] & "/"& $RegexResult[2]&")" + $StringFile = StringRegExpReplace($StringFile, "!\[(.*)\]\(.*\\(.*)\\(.*)\)", $NewPic, 1) + WEnd + + While 1 + $RegexResult = StringRegExp($StringFile, "(?m)^(\d(?:(\.\d))*\)(.*))$", $STR_REGEXPARRAYMATCH, $RegexPos) + $RegexPos = @extended + If @extended == 0 Then ExitLoop + + ;$StringFile = StringRegExpReplace($StringFile, "(?m)^(\d(?:(\.\d))*\)(.*))$", "

"&$RegExResult[0]&"

", 1) + $StringFile = StringRegExpReplace($StringFile, "(?m)^(\d(?:(\.\d))*\)(.*))$", "##### "&$RegExResult[0], 1) + WEnd + + Return $StringFile +EndFunc \ No newline at end of file diff --git a/Utils/Slate Documentation Generator/src/SlateGenerator2.au3 b/Utils/Slate Documentation Generator/src/SlateGenerator2.au3 new file mode 100644 index 000000000..75f8ea703 --- /dev/null +++ b/Utils/Slate Documentation Generator/src/SlateGenerator2.au3 @@ -0,0 +1,310 @@ +#Region ;**** Directives created by AutoIt3Wrapper_GUI **** +#AutoIt3Wrapper_Outfile=..\bin\SlateDocGenerator2.exe +#AutoIt3Wrapper_Change2CUI=y +#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** +#cs +This is the main script + +The script goal is to read .lua file, extract the documentation in comment blocks, and write .md files to be converted to html by Slate : https://github.com/lord/slate +It works in 5 steps : + +First, it reads the .lua files one bt one, indentifying the comment blocks. for each comment block, it determines the kind of content the comment block describes (module, class/type or function), +find some usefull stuff (for exemple in the declaration...) and writes all of this info in the creatively named TreeHierarchy.csv, with this format : +@K=kind, @M=ParentModule, @N=Name, @P=Parent, @F=FileWhereTheCommentBlockIsLocated, @C=CarretPositionOfTheCommentBlock +The functions used to do this step are mostly found in Parser.au3 + +Then the second step is the longest : we sort the TreeHiearchy.csv, and put the result into TreeHierarchySorted.csv +The idea is to have the data in this order : +Module A +Class A (belongs to Module A) +Function A (belongs to Class A) +Function B (belongs to Class A) +Class B Class A (belongs to Module A) +Function C (belongs to Class B) +Function D (belongs to Class B) +Module B ... +The functions used to do this step are found in DataStorer.au3 + +Then, step 3 : We read each line of TreeHierarchySorted.csv, read the appropriate comment block in the .lua source files, +and write the appropriate Markdown documentation for it in a temporary folder +This is where the markdown documentation is actually written for the first time. +The functions used to do this step are found in Writer.au3 + +Step 4 ! We read the newly created Markdown files, trying to find hyperlinks/picture paths... and we replace them with the new ones. +We copy each processed file into it's final destination. +The functions used to do this step are mostly found in Parser.au3 + +And finally Step 5 : We add the new markdown files to Slate's index and delete temporary files and folder +#ce + +#include +#include +#include +#include + +; Those are the arguments that need to be passed at the start +Global $SourceFolder = $CmdLine[1] ;"./Results" +Global $OutputFolder = $CmdLine[2] ;"@ScriptDir&"/source/index.html.md" + +Global $Log = FileOpen(@ScriptDir & "\SlateGenerator2.log", 2) +Global $DataFile = FileOpen(@ScriptDir & "\TreeHierarchy.csv", 2) +Global $FunctionList = FileOpen(@ScriptDir & "\FuctionList.txt", 2) + +#include "Parser.au3" +#include "DataStorer.au3" +#include "Writer.au3" + + +Func ExitCleanly() + FileClose($DataFile) + FileClose($FunctionList) + FileWrite($Log, "SlateGenerator2 exited cleanly") + FileClose($Log) +EndFunc + + +; Small function to determine if a comment block is describing a module, a type or a function +Func IdentifyBlock($Block, $Declaration) + Local $Kind + Local $KindFunction + + $Kind = ParseForOneTag($Block, "@module") + If $Kind Then + Return "module" + EndIf + + $Kind = ParseForOneTag($Block, "@type") + If $Kind Then + Return "type" + EndIf + + + $KindFunction = ParseFunctionName($Block, $Declaration) + If $KindFunction[0] Then + Return "function" + EndIf + + Return "" +EndFunc + + + + +; ----------------------------------------------------------------- +; Main +; ----------------------------------------------------------------- + +; Step 1 ! +; ----------------------------------------------------------------- + +Local $SourceList = _FileListToArrayRec($SourceFolder, "*", $FLTAR_FILES, $FLTAR_RECUR, $FLTAR_NOSORT, $FLTAR_FULLPATH) +Local $CurrentFile +Local $CarretPos = 0 +Local $CommentBlock +Local $CommentKind +Local $CommentInfo[2] +Local $CurrentModule + +ConsoleWrite("1. Parsing Source Files... ") +FileWrite($Log, @CRLF&@CRLF&@TAB&"INFO : Building Hierarchy" & @CRLF) +For $i=1 To $SourceList[0] ; for each .lua source file + + + FileWrite($Log, "DEBUG : "&$SourceList[$i]) + + ; let's read the next .lua source file + $CurrentFile = FileOpen($SourceList[$i], $FO_READ) + FileWrite($Log, @CRLF&"INFO : Reading File "&$SourceList[$i] & @CRLF) + While True ; for each comment block in the current .lua source file + + ; We read the next comment block. If we could not, it's probably eof, time to open the next .lua file + $CommentBlock = ReadNextBlock($CurrentFile, $CarretPos) + If Not $CommentBlock[1] Then + ExitLoop + EndIf + + $CarretPos = $CommentBlock[0] + $CommentKind = IdentifyBlock($CommentBlock[1], $CommentBlock[2]) + ; Depending on the kind of comment block it is, we write the appropriate line in TreeHierarchy.csv + Switch $CommentKind + Case "function" + $CommentInfo = ParseFunctionName($CommentBlock[1], $CommentBlock[2]) + AddNode("function", $CurrentModule, $CommentInfo[0], $CommentInfo[1], $SourceList[$i], $CommentBlock[3]) + FileWrite($Log, "INFO : Added function "&$CommentInfo[0]&" to hierarchy" & @CRLF) + Case "type" + $CommentInfo[0] = ParseForOneTag($CommentBlock[1], "@type") + $CommentInfo[1] = ParseForOneTag($CommentBlock[1], "@extends") + $CommentInfo[1] = StringRegExpReplace($CommentInfo[1], "(.*#)", "") + AddNode("type", $CurrentModule, $CommentInfo[0], $CommentInfo[1], $SourceList[$i], $CommentBlock[3]) + FileWrite($Log, "INFO : Added type "&$CommentInfo[0]&" to hierarchy" & @CRLF) + Case "module" + $CurrentModule = ParseForOneTag($CommentBlock[1], "@module") + AddNode("module", "", $CurrentModule, "", $SourceList[$i], $CommentBlock[3]) + FileWrite($Log, "INFO : Added module "&$CurrentModule&" to hierarchy" & @CRLF) + EndSwitch + + WEnd + $CarretPos = 0 + FileClose($CurrentFile) + +Next +ConsoleWrite("Done"&@CRLF) + + +; Step 2 ! +; ----------------------------------------------------------------- +ConsoleWrite("2. Sorting Hierarchy") +FileWrite($Log, @CRLF&@CRLF&@TAB&"INFO : Sorting Hierarchy" & @CRLF) +; The magic happens in DataStorer.au3 +DataSort() +ConsoleWrite("Done"&@CRLF) + + + +; Step 3 ! +; ----------------------------------------------------------------- +ConsoleWrite("3. Writing Markdown Documentation") +FileWrite($Log, @CRLF&@CRLF&@TAB&"INFO : Writing Markdown Documentation" & @CRLF) + +Local $CurrentOutput +Local $CurrentFolder +Local $RegexResult +Local $Line +Local $CarretPos = 0 +Local $Results +Local $Output +Local $Declaration + +FileSetPos($DataFile, 0, $FILE_BEGIN) +While True ; For each line in TreeHierarchySorted.csv + + ; read the next line until eof + FileSetPos($DataFile, $CarretPos, $FILE_BEGIN) + $Line = FileReadLine($DataFile) + If @error Then ; eof + ExitLoop + Endif + + $CarretPos = FileGetPos($DataFile) + + ; find the file/position of the next comment block referenced in the line + $RegexResult = StringRegExp($Line, "\@F=(.+?),", $STR_REGEXPARRAYMATCH) + $CurrentFile = FileOpen($RegexResult[0], $FO_READ) + + $RegexResult = StringRegExp($Line, "\@C=(.+?),", $STR_REGEXPARRAYMATCH) + $DataPos = $RegexResult[0] + + ; get the comment block itself + $Results = ReadNextBlock($CurrentFile, $DataPos) + $Block = $Results[1] + $Declaration = $Results[2] + + + ; choose the right function to write mardown depending on the type of comment block + $RegexResult = StringRegExp($Line, "\@K=(.+?),", $STR_REGEXPARRAYMATCH) + + If $RegexResult[0] == "module" Then + ConsoleWrite(".") + ; We need the name of the folder containing this particular source file + $RegexResult = StringRegExp($Line, "\@F=(.+?),", $STR_REGEXPARRAYMATCH) + $RegexResult = StringRegExp($RegexResult[0], "\\(.*)\\.*\.lua", $STR_REGEXPARRAYMATCH) + If @error Then + $CurrentFolder = "" + Else + $CurrentFolder = $RegexResult[0] + Endif + + ; Now we can write the markdown for this module + $CurrentOutput = WriteModule($Block, $CurrentFolder) + EndIf + + If $RegexResult[0] == "type" Then + ; We need the name of the Module containing the type + $RegexResult = StringRegExp($Line, "\@M=(.+?),", $STR_REGEXPARRAYMATCH) + + ; Now we can write the markdown for this type + WriteType($Block, $RegexResult[0], $CurrentOutput) + EndIf + + If $RegexResult[0] == "function" Then + ; We can write the markdown for this function + WriteFunction($Block, $Declaration, $CurrentOutput) + EndIf + + FileClose($CurrentFile) +Wend +ConsoleWrite("Done"&@CRLF) + + +; Step 4 ! +; ----------------------------------------------------------------- +ConsoleWrite("4. Processing Hyperlinks...") +FileWrite($Log, @CRLF&@CRLF&@TAB&"INFO : Processing Hyperlinks" & @CRLF) +Local $i=1 +Local $TempFilesArray = _FileListToArray(@ScriptDir & "/TEMP") +Local $CurrentFile +Local $FinalFile +While $i <= $TempFilesArray[0] ; For each markdown file in the temporary folder + + ;read the file + $CurrentFile = FileOpen(@ScriptDir & "/TEMP/" & $TempFilesArray[$i], 0) + ; The magic happens in Parser.au3 + $FinalString = ReplaceHyperlinks($CurrentFile) + + ; copy the result to the final file location + $FinalFile = FileOpen($OutputFolder & "/includes/" & $TempFilesArray[$i], 2) + FileWrite($FinalFile, $FinalString) + + FileClose($FinalFile) + FileClose($CurrentFile) + $i += 1 +WEnd +ConsoleWrite("Done"&@CRLF) + + +; Step 5 ! +; ----------------------------------------------------------------- +ConsoleWrite("5. Adding new documentation to index...") +FileWrite($Log, @CRLF&@CRLF&@TAB&"INFO : Adding new documentation to index" & @CRLF) + +; Now this is a bit annoying : there is no way to insert a line in a document. +; So we need to read the first half of it, read the second half, and the wipe the whole document +; This way, in the new doc, we can write the first half, what we wanted to insert, and then the second half ! + +; Let's store the index file in $IndexString +Local $IndexFile = $OutputFolder&"/index.html.md" +Local $IndexFileHandle = FileOpen($IndexFile, 0) +Local $IndexString = FileRead($IndexFileHandle) +$IndexString = StringRegExpReplace($IndexString, "-\h[A-Z][a-z]+\.[A-Z][a-z]+\s", "") + +; Now we slpit it into and store the results in $BeforeString and $AfterString +Local $SearchPos = StringInStr($IndexString, "search:") +local $BeforeString = StringTrimRight($IndexString, StringLen($IndexString) - $SearchPos + 5) +local $AfterString = StringTrimLeft($IndexString, $SearchPos - 1) + +; reopening the index file wiping everything +FileClose($IndexFileHandle) +$IndexFileHandle = FileOpen($IndexFile, 2) + +; write the first half +FileWrite($IndexFileHandle, $BeforeString) +Local $IncludePos = StringInStr($IndexString, "includes:") +FileSetPos($IndexFileHandle, $IncludePos + 10, $FILE_BEGIN) + +; add the new markdown files to the index +$i = 1 +While $i <= $TempFilesArray[0] + FileWrite($Log, StringTrimRight($TempFilesArray[$i], 3)&@CRLF) + + FileWrite($IndexFileHandle, " - "&StringTrimRight($TempFilesArray[$i], 3)&@CRLF) + $i+=1 +WEnd +FileWrite($IndexFileHandle, @CRLF) + +; append the second half of the file +FileWrite($IndexFileHandle, $AfterString) +FileClose($IndexFileHandle) +ConsoleWrite("Done"&@CRLF) + +; WE ARE DONE ! +ExitCleanly() diff --git a/Utils/Slate Documentation Generator/src/Writer.au3 b/Utils/Slate Documentation Generator/src/Writer.au3 new file mode 100644 index 000000000..b6a249540 --- /dev/null +++ b/Utils/Slate Documentation Generator/src/Writer.au3 @@ -0,0 +1,209 @@ +; Takes an array and returns it in a markdown flavored list +; If the list is a retun, then, there is no variable name... +Func ArrayToList($Array, $Return) + $String = "" + $i = 0 + do + $String &= "* " + $String &= $Array[$i] & " " + + If $Return Then + If $Array[$i + 2] == "" or $Array[$i + 2] == " " Then + $String &= @CRLF + Else + $String &= " " & $Array[$i + 1] & " " & $Array[$i + 2] & @CRLF + EndIf + Else + + $String &= $Array[$i + 1] + If $Array[$i + 2] == "" or $Array[$i + 2] == " " Then + $String &= @CRLF + Else + $String &= " : " & $Array[$i + 2] & @CRLF + EndIf + EndIf + $i += 3 + Until $i >= UBound($Array) + Return $String +EndFunc + + +Func WriteModule($Block, $Group) + Local $ModuleName = ParseForOneTag($Block, "@module") + DirCreate(@ScriptDir & "\TEMP") + Local $Output = FileOpen(@ScriptDir & "\TEMP\" & $Group & "." & $ModuleName & ".md", $FO_OVERWRITE) + Local $Data = "" + Local $DataPos = 1 + + FileWrite($Log, @CRLF&@TAB&"Writing "&$Group & "." & $ModuleName & ".md" &@CRLF) + FileWrite($Log, "Writing Module "&$ModuleName&@CRLF) + + ; Add title of Module + FileWrite($Output, "# " & $Group & "." & $ModuleName & " Module" & @CRLF) + + ; Copy the short description + While StringRight($Data, 1) <> @CRLF And StringRight($Data, 1) <> @CR + If StringRight($Data, 7) == "@module" Then ; If there is no comment in the module block + Return $Output + EndIf + $Data &= StringMid($Block, $DataPos, 1) + $DataPos += 1 + WEnd + $Data = StringTrimRight($Data, 1) + $Block = StringTrimLeft($Block, $DataPos) + FileWrite($Output, $Data & @CRLF) + + ; copy the long description + $DataPos = 1 + $Data = "" + $Omit = False + While StringRight($Data, 7) <> "@module" + $Data &= StringMid($Block, $DataPos, 1) + $DataPos += 1 + WEnd + $Data = StringTrimRight($Data, 8) + FileWrite($Output, $Data & @CRLF) + Return $Output +EndFunc + + +Func WriteType($Block, $ModuleName, $Output) + Local $TypeName = ParseForOneTag($Block, "@type") + Local $ParentClass = GetData($TypeName, "parent") + Local $Fields = ParseForTags($Block, "@field") + + FileWrite($Log, "Writing Type "&$TypeName&@CRLF) + + ; Add title of Type + FileWrite($Output, "## " & $TypeName & " Class" & @CRLF) + + ; Add hierearchy info if necessary. Some cool ASCII drawing is going on ! + If $ParentClass <> "ROOT" Then + FileWrite($Output, "
" & @CRLF)
+		FileWrite($Output, "Inheritance : The " & $TypeName & " Class inherits from the following parents :" & @CRLF)
+		Local $Hierarchy = GetParents($TypeName)
+		Local $String = ""
+		Local $TabBuffer = @TAB
+		$String &= $Hierarchy[0]&@CRLF
+		For $i=1 to UBound($Hierarchy)-1
+			$String &= $TabBuffer&"`-- "&$Hierarchy[$i]&@CRLF
+			$TabBuffer &= @TAB
+		Next
+		FileWrite($Output, $String)
+		FileWrite($Output, "
" & @CRLF) + Else + FileWrite($Output, "
" & @CRLF)
+		FileWrite($Output, "The " & $TypeName & " class does not inherit" & @CRLF)
+		FileWrite($Output, "
" & @CRLF) + EndIf + + ; Copy the long description + Local $DataPos = 1 + Local $Data = "" + Local $Omit = False + + While StringRight($Data, 1) <> @CR ; We discard the first line + $Data &= StringMid($Block, $DataPos, 1) + $DataPos += 1 + WEnd + ; If there is a tag in the first line, there is no description + if StringInStr($Data, "@type") == 0 and StringInStr($Data, "@extends") == 0 and StringInStr($Data, "@field") == 0 Then + $Data = "" + $DataPos += 1 + + While StringRight($Data, 5) <> "@type" + $Data &= StringMid($Block, $DataPos, 1) + $DataPos += 1 + WEnd + $Data = StringTrimRight($Data, 5) + FileWrite($Output, $Data & @CRLF) + EndIf + + ; Add the Attributes + If IsArray($Fields) Then + FileWrite($Output, "

Attributes

" & @CRLF & @CRLF) + FileWrite($Output, ArrayToList($Fields, False) & @CRLF) + EndIf + FileWrite($Output, @CRLF) + Return $TypeName +EndFunc + + + +Func WriteFunction($Block, $Declaration, $Output) + Local $RegexResult = ParseFunctionName($Block, $Declaration) + Local $FunctionName = $RegexResult[0] + Local $TypeName = $RegexResult[1] + Local $Parameters = ParseParams($Block, $Declaration) + Local $Returns = ParseForTags($Block, "@return") + Local $Usage = ParseForOneTag($Block, "@usage") + Local $RegexResult + + FileWrite($Log, "Writing Function "&$FunctionName&@CRLF) + + If StringLeft($FunctionName, 1) == "_" Then + _FileWriteLog($Log, @TAB&@Tab&"Function is private. Ignored." & @CRLF) + Return $FunctionName + EndIf + ; Add the class before the function name + If IsArray($Parameters) Then + If $Parameters[1] == "self" Then + $FunctionName = $TypeName & ":" & $FunctionName + EndIf + Else + $FunctionName = $TypeName & "." & $FunctionName + EndIf + + ; add the parameters in parenthesis + $FunctionName &= "(" + If IsArray($Parameters) Then + For $i = 3 To UBound($Parameters) - 3 Step 3 + $FunctionName &= $Parameters[$i + 1] & ", " + Next + If UBound($Parameters) > 3 Then + $FunctionName = StringTrimRight($FunctionName, 2) + EndIf + EndIf + $FunctionName &= ")" + + ;write the file name + FileWrite($Output, "### " & $FunctionName & @CRLF) + + ;Write the exemple if any + If $Usage <> "" Then + FileWrite($Output, "``` lua") + FileWrite($Output, $Usage) + FileWrite($Output, "```" & @CRLF) + EndIf + + ;Write the description + FileWrite($Log, $Block) + FileWrite($Log, StringTrimRight($Block, StringLen($Block) - StringInStr($Block, "@param") + 1) & @CRLF) + FileWrite($Output, StringTrimRight($Block, StringLen($Block) - StringInStr($Block, "@param") + 1) & @CRLF) + + ; Write the parameters + FileWrite($Output, "

Parameters

" & @CRLF) + If IsArray($Parameters) Then + FileWrite($Output, ArrayToList($Parameters, False) & @CRLF) + EndIf + + ; Write the returns + FileWrite($Output, "

Returns

" & @CRLF) + If IsArray($Returns) Then + FileWrite($Output, ArrayToList($Returns, True) & @CRLF) + EndIf + + FileWrite($Output, @CRLF) + + ; add to the list of function balises (useful for hyperlinks) + $RegexResult = ParseFunctionName($Block, $Declaration) + Local $URLBalise = $TypeName & "-" & $RegexResult[0] & "-" + If IsArray($Parameters) Then + For $i = 3 To UBound($Parameters) - 3 Step 3 + $URLBalise &= StringLower($Parameters[$i + 1]) & "-" + Next + EndIf + $URLBalise = StringTrimRight($URLBalise, 1) + FileWrite($FunctionList, $URLBalise & @CRLF) + return $FunctionName +EndFunc \ No newline at end of file diff --git a/Utils/luadocumentor.bat b/Utils/luadocumentor.bat new file mode 100644 index 000000000..8f28e39a8 --- /dev/null +++ b/Utils/luadocumentor.bat @@ -0,0 +1,3 @@ +@echo off +"./luarocks/lua5.1" -e "package.path=\"./luarocks/systree/share/lua/5.1/?.lua;./luarocks/systree/share/lua/5.1/?/init.lua;./luarocks/systree/share/lua/5.1/?.lua;./luarocks/systree/share/lua/5.1/?/init.lua;./luarocks/lua/?.lua;\"..package.path; package.cpath=\"./luarocks/lib/lua/5.1/?.dll;./luarocks/systree/lib/lua/5.1/?.dll;\"..package.cpath" -e "local k,l,_=pcall(require,\"luarocks.loader\") _=k and l.add_context(\"luadocumentor\",\"0.1.5-1\")" "./luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/bin/luadocumentor" -f doc -d "../docs/Documentation" -s "../docs/Stylesheet/stylesheet.css" "../Moose Development/Moose" %* +exit /b %ERRORLEVEL% \ No newline at end of file diff --git a/Utils/luarocks/Microsoft.VC80.CRT.manifest b/Utils/luarocks/Microsoft.VC80.CRT.manifest new file mode 100644 index 000000000..6a8a0e239 --- /dev/null +++ b/Utils/luarocks/Microsoft.VC80.CRT.manifest @@ -0,0 +1,8 @@ + + + + + n9On8FItNsK/DmT8UQxu6jYDtWQ= + 0KJ/VTwP4OUHx98HlIW2AdW1kuY= + YJuB+9Os2oxW4mY+2oC/r8lICZE= + \ No newline at end of file diff --git a/Utils/luarocks/bin2c5.1.exe b/Utils/luarocks/bin2c5.1.exe new file mode 100644 index 000000000..ca81d4b61 Binary files /dev/null and b/Utils/luarocks/bin2c5.1.exe differ diff --git a/Utils/luarocks/config-5.1.lua b/Utils/luarocks/config-5.1.lua new file mode 100644 index 000000000..cf02193f5 --- /dev/null +++ b/Utils/luarocks/config-5.1.lua @@ -0,0 +1,14 @@ +rocks_trees = { + home..[[/luarocks]], + { name = [[user]], + root = home..[[/luarocks]], + }, + { name = [[system]], + root = [[C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\systree]], + }, +} +variables = { + MSVCRT = 'MSVCR80', + LUALIB = 'lua5.1.lib' +} +verbose = false -- set to 'true' to enable verbose output diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lauxlib.h b/Utils/luarocks/include/lauxlib.h similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lauxlib.h rename to Utils/luarocks/include/lauxlib.h diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lua.h b/Utils/luarocks/include/lua.h similarity index 98% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lua.h rename to Utils/luarocks/include/lua.h index a4b73e743..e4bdfd3b9 100644 --- a/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lua.h +++ b/Utils/luarocks/include/lua.h @@ -1,5 +1,5 @@ /* -** $Id: lua.h,v 1.218.1.7 2012/01/13 20:36:20 roberto Exp $ +** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 roberto Exp $ ** Lua - An Extensible Extension Language ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) ** See Copyright Notice at the end of this file @@ -17,9 +17,9 @@ #define LUA_VERSION "Lua 5.1" -#define LUA_RELEASE "Lua 5.1.5" +#define LUA_RELEASE "Lua 5.1.4" #define LUA_VERSION_NUM 501 -#define LUA_COPYRIGHT "Copyright (C) 1994-2012 Lua.org, PUC-Rio" +#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes" @@ -362,7 +362,7 @@ struct lua_Debug { /****************************************************************************** -* Copyright (C) 1994-2012 Lua.org, PUC-Rio. All rights reserved. +* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lua.hpp b/Utils/luarocks/include/lua.hpp similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lua.hpp rename to Utils/luarocks/include/lua.hpp diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/luaconf.h b/Utils/luarocks/include/luaconf.h similarity index 98% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/include/luaconf.h rename to Utils/luarocks/include/luaconf.h index e2cb26163..5e7b98be6 100644 --- a/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/luaconf.h +++ b/Utils/luarocks/include/luaconf.h @@ -91,7 +91,7 @@ ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua" #define LUA_CPATH_DEFAULT \ - ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" + ".\\?.dll;" ".\\?51.dll;" LUA_CDIR"?.dll;" LUA_CDIR"?51.dll;" LUA_CDIR"clibs\\?.dll;" LUA_CDIR"clibs\\?51.dll;" LUA_CDIR"loadall.dll;" LUA_CDIR"clibs\\loadall.dll" #else #define LUA_ROOT "/usr/local/" @@ -101,7 +101,7 @@ "./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua" #define LUA_CPATH_DEFAULT \ - "./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so" + "./?.so;" "./lib?51.so;" LUA_CDIR"?.so;" LUA_CDIR"lib?51.so;" LUA_CDIR"loadall.so" #endif diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lualib.h b/Utils/luarocks/include/lualib.h similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/include/lualib.h rename to Utils/luarocks/include/lualib.h diff --git a/Utils/luarocks/lua.ico b/Utils/luarocks/lua.ico new file mode 100644 index 000000000..56dc4fe15 Binary files /dev/null and b/Utils/luarocks/lua.ico differ diff --git a/Utils/luarocks/lua/luarocks/add.lua b/Utils/luarocks/lua/luarocks/add.lua new file mode 100644 index 000000000..f37d334d3 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/add.lua @@ -0,0 +1,122 @@ + +--- Module implementing the luarocks-admin "add" command. +-- Adds a rock or rockspec to a rocks server. +local add = {} +package.loaded["luarocks.add"] = add + +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local manif = require("luarocks.manif") +local index = require("luarocks.index") +local fs = require("luarocks.fs") +local cache = require("luarocks.cache") + +util.add_run_function(add) +add.help_summary = "Add a rock or rockspec to a rocks server." +add.help_arguments = "[--server=] [--no-refresh] {|...}" +add.help = [[ +Arguments are local files, which may be rockspecs or rocks. +The flag --server indicates which server to use. +If not given, the default server set in the upload_server variable +from the configuration file is used instead. +The flag --no-refresh indicates the local cache should not be refreshed +prior to generation of the updated manifest. +]] + +local function add_files_to_server(refresh, rockfiles, server, upload_server) + assert(type(refresh) == "boolean" or not refresh) + assert(type(rockfiles) == "table") + assert(type(server) == "string") + assert(type(upload_server) == "table" or not upload_server) + + local download_url, login_url = cache.get_server_urls(server, upload_server) + local at = fs.current_dir() + local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url + + local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password) + if not local_cache then + return nil, protocol + end + if protocol == "file" then + return nil, "Server "..server.." is not recognized, check your configuration." + end + + if not login_url then + login_url = protocol.."://"..server_path + end + + local ok, err = fs.change_dir(at) + if not ok then return nil, err end + + local files = {} + for _, rockfile in ipairs(rockfiles) do + if fs.exists(rockfile) then + util.printout("Copying file "..rockfile.." to "..local_cache.."...") + local absolute = fs.absolute_name(rockfile) + fs.copy(absolute, local_cache, cfg.perm_read) + table.insert(files, dir.base_name(absolute)) + else + util.printerr("File "..rockfile.." not found") + end + end + if #files == 0 then + return nil, "No files found" + end + + local ok, err = fs.change_dir(local_cache) + if not ok then return nil, err end + + util.printout("Updating manifest...") + manif.make_manifest(local_cache, "one", true) + + manif.zip_manifests() + + util.printout("Updating index.html...") + index.make_index(local_cache) + + local login_info = "" + if user then login_info = " -u "..user end + if password then login_info = login_info..":"..password end + if not login_url:match("/$") then + login_url = login_url .. "/" + end + + table.insert(files, "index.html") + table.insert(files, "manifest") + for ver in util.lua_versions() do + table.insert(files, "manifest-"..ver) + table.insert(files, "manifest-"..ver..".zip") + end + + -- TODO abstract away explicit 'curl' call + + local cmd + if protocol == "rsync" then + local srv, path = server_path:match("([^/]+)(/.+)") + cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" + elseif upload_server and upload_server.sftp then + local part1, part2 = upload_server.sftp:match("^([^/]*)/(.*)$") + cmd = cfg.variables.SCP.." "..table.concat(files, " ").." "..user.."@"..part1..":/"..part2 + else + cmd = cfg.variables.CURL.." "..login_info.." -T '{"..table.concat(files, ",").."}' "..login_url + end + + util.printout(cmd) + fs.execute(cmd) + + return true +end + +function add.command(flags, ...) + local files = {...} + if #files < 1 then + return nil, "Argument missing. "..util.see_help("add", "luarocks-admin") + end + local server, server_table = cache.get_upload_server(flags["server"]) + if not server then return nil, server_table end + return add_files_to_server(not flags["no-refresh"], files, server, server_table) +end + + +return add diff --git a/Utils/luarocks/lua/luarocks/admin_remove.lua b/Utils/luarocks/lua/luarocks/admin_remove.lua new file mode 100644 index 000000000..621f13178 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/admin_remove.lua @@ -0,0 +1,92 @@ + +--- Module implementing the luarocks-admin "remove" command. +-- Removes a rock or rockspec from a rocks server. +local admin_remove = {} +package.loaded["luarocks.admin_remove"] = admin_remove + +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local manif = require("luarocks.manif") +local index = require("luarocks.index") +local fs = require("luarocks.fs") +local cache = require("luarocks.cache") + +util.add_run_function(admin_remove) +admin_remove.help_summary = "Remove a rock or rockspec from a rocks server." +admin_remove.help_arguments = "[--server=] [--no-refresh] {|...}" +admin_remove.help = [[ +Arguments are local files, which may be rockspecs or rocks. +The flag --server indicates which server to use. +If not given, the default server set in the upload_server variable +from the configuration file is used instead. +The flag --no-refresh indicates the local cache should not be refreshed +prior to generation of the updated manifest. +]] + +local function remove_files_from_server(refresh, rockfiles, server, upload_server) + assert(type(refresh) == "boolean" or not refresh) + assert(type(rockfiles) == "table") + assert(type(server) == "string") + assert(type(upload_server) == "table" or not upload_server) + + local download_url, login_url = cache.get_server_urls(server, upload_server) + local at = fs.current_dir() + local refresh_fn = refresh and cache.refresh_local_cache or cache.split_server_url + + local local_cache, protocol, server_path, user, password = refresh_fn(server, download_url, cfg.upload_user, cfg.upload_password) + if not local_cache then + return nil, protocol + end + if protocol ~= "rsync" then + return nil, "This command requires 'rsync', check your configuration." + end + + local ok, err = fs.change_dir(at) + if not ok then return nil, err end + + local nr_files = 0 + for _, rockfile in ipairs(rockfiles) do + local basename = dir.base_name(rockfile) + local file = dir.path(local_cache, basename) + util.printout("Removing file "..file.."...") + fs.delete(file) + if not fs.exists(file) then + nr_files = nr_files + 1 + else + util.printerr("Failed removing "..file) + end + end + if nr_files == 0 then + return nil, "No files removed." + end + + local ok, err = fs.change_dir(local_cache) + if not ok then return nil, err end + + util.printout("Updating manifest...") + manif.make_manifest(local_cache, "one", true) + util.printout("Updating index.html...") + index.make_index(local_cache) + + local srv, path = server_path:match("([^/]+)(/.+)") + local cmd = cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." --delete -e ssh "..local_cache.."/ "..user.."@"..srv..":"..path.."/" + + util.printout(cmd) + fs.execute(cmd) + + return true +end + +function admin_remove.command(flags, ...) + local files = {...} + if #files < 1 then + return nil, "Argument missing. "..util.see_help("remove", "luarocks-admin") + end + local server, server_table = cache.get_upload_server(flags["server"]) + if not server then return nil, server_table end + return remove_files_from_server(not flags["no-refresh"], files, server, server_table) +end + + +return admin_remove diff --git a/Utils/luarocks/lua/luarocks/build.lua b/Utils/luarocks/lua/luarocks/build.lua new file mode 100644 index 000000000..96b232ffb --- /dev/null +++ b/Utils/luarocks/lua/luarocks/build.lua @@ -0,0 +1,415 @@ + +--- Module implementing the LuaRocks "build" command. +-- Builds a rock, compiling its C parts if any. +local build = {} +package.loaded["luarocks.build"] = build + +local pack = require("luarocks.pack") +local path = require("luarocks.path") +local util = require("luarocks.util") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local deps = require("luarocks.deps") +local manif = require("luarocks.manif") +local remove = require("luarocks.remove") +local cfg = require("luarocks.cfg") + +util.add_run_function(build) +build.help_summary = "Build/compile a rock." +build.help_arguments = "[--pack-binary-rock] [--keep] {|| []}" +build.help = [[ +Build and install a rock, compiling its C parts if any. +Argument may be a rockspec file, a source rock file +or the name of a rock to be fetched from a repository. + +--pack-binary-rock Do not install rock. Instead, produce a .rock file + with the contents of compilation in the current + directory. + +--keep Do not remove previously installed versions of the + rock after building a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. + +--branch= Override the `source.branch` field in the loaded + rockspec. Allows to specify a different branch to + fetch. Particularly for SCM rocks. + +--only-deps Installs only the dependencies of the rock. + +]]..util.deps_mode_help() + +--- Install files to a given location. +-- Takes a table where the array part is a list of filenames to be copied. +-- In the hash part, other keys, if is_module_path is set, are identifiers +-- in Lua module format, to indicate which subdirectory the file should be +-- copied to. For example, install_files({["foo.bar"] = "src/bar.lua"}, "boo") +-- will copy src/bar.lua to boo/foo. +-- @param files table or nil: A table containing a list of files to copy in +-- the format described above. If nil is passed, this function is a no-op. +-- Directories should be delimited by forward slashes as in internet URLs. +-- @param location string: The base directory files should be copied to. +-- @param is_module_path boolean: True if string keys in files should be +-- interpreted as dotted module paths. +-- @param perms string: Permissions of the newly created files installed. +-- Directories are always created with the default permissions. +-- @return boolean or (nil, string): True if succeeded or +-- nil and an error message. +local function install_files(files, location, is_module_path, perms) + assert(type(files) == "table" or not files) + assert(type(location) == "string") + if files then + for k, file in pairs(files) do + local dest = location + local filename = dir.base_name(file) + if type(k) == "string" then + local modname = k + if is_module_path then + dest = dir.path(location, path.module_to_path(modname)) + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end + if filename:match("%.lua$") then + local basename = modname:match("([^.]+)$") + filename = basename..".lua" + end + else + dest = dir.path(location, dir.dir_name(modname)) + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end + filename = dir.base_name(modname) + end + else + local ok, err = fs.make_dir(dest) + if not ok then return nil, err end + end + local ok = fs.copy(dir.path(file), dir.path(dest, filename), perms) + if not ok then + return nil, "Failed copying "..file + end + end + end + return true +end + +--- Write to the current directory the contents of a table, +-- where each key is a file name and its value is the file content. +-- @param files table: The table of files to be written. +local function extract_from_rockspec(files) + for name, content in pairs(files) do + local fd = io.open(dir.path(fs.current_dir(), name), "w+") + fd:write(content) + fd:close() + end +end + +--- Applies patches inlined in the build.patches section +-- and extracts files inlined in the build.extra_files section +-- of a rockspec. +-- @param rockspec table: A rockspec table. +-- @return boolean or (nil, string): True if succeeded or +-- nil and an error message. +function build.apply_patches(rockspec) + assert(type(rockspec) == "table") + + local build_spec = rockspec.build + if build_spec.extra_files then + extract_from_rockspec(build_spec.extra_files) + end + if build_spec.patches then + extract_from_rockspec(build_spec.patches) + for patch, patchdata in util.sortedpairs(build_spec.patches) do + util.printout("Applying patch "..patch.."...") + local ok, err = fs.apply_patch(tostring(patch), patchdata) + if not ok then + return nil, "Failed applying patch "..patch + end + end + end + return true +end + +local function install_default_docs(name, version) + local patterns = { "readme", "license", "copying", ".*%.md" } + local dest = dir.path(path.install_dir(name, version), "doc") + local has_dir = false + for file in fs.dir() do + for _, pattern in ipairs(patterns) do + if file:lower():match("^"..pattern) then + if not has_dir then + fs.make_dir(dest) + has_dir = true + end + fs.copy(file, dest, cfg.perm_read) + break + end + end + end +end + +--- Build and install a rock given a rockspec. +-- @param rockspec_file string: local or remote filename of a rockspec. +-- @param need_to_fetch boolean: true if sources need to be fetched, +-- false if the rockspec was obtained from inside a source rock. +-- @param minimal_mode boolean: true if there's no need to fetch, +-- unpack or change dir (this is used by "luarocks make"). Implies +-- need_to_fetch = false. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for no trees. +-- @param build_only_deps boolean: true to build the listed dependencies only. +-- @return (string, string) or (nil, string, [string]): Name and version of +-- installed rock if succeeded or nil and an error message followed by an error code. +function build.build_rockspec(rockspec_file, need_to_fetch, minimal_mode, deps_mode, build_only_deps) + assert(type(rockspec_file) == "string") + assert(type(need_to_fetch) == "boolean") + + local rockspec, err, errcode = fetch.load_rockspec(rockspec_file) + if err then + return nil, err, errcode + elseif not rockspec.build then + return nil, "Rockspec error: build table not specified" + elseif not rockspec.build.type then + return nil, "Rockspec error: build type not specified" + end + + local ok + if not build_only_deps then + ok, err, errcode = deps.check_external_deps(rockspec, "build") + if err then + return nil, err, errcode + end + end + + if deps_mode == "none" then + util.printerr("Warning: skipping dependency checks.") + else + local ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then + return nil, err, errcode + end + end + + local name, version = rockspec.name, rockspec.version + if build_only_deps then + util.printout("Stopping after installing dependencies for " ..name.." "..version) + util.printout() + return name, version + end + + if repos.is_installed(name, version) then + repos.delete_version(name, version, deps_mode) + end + + if not minimal_mode then + local source_dir + if need_to_fetch then + ok, source_dir, errcode = fetch.fetch_sources(rockspec, true) + if not ok then + return nil, source_dir, errcode + end + local ok, err = fs.change_dir(source_dir) + if not ok then return nil, err end + elseif rockspec.source.file then + local ok, err = fs.unpack_archive(rockspec.source.file) + if not ok then + return nil, err + end + end + fs.change_dir(rockspec.source.dir) + end + + local dirs = { + lua = { name = path.lua_dir(name, version), is_module_path = true, perms = cfg.perm_read }, + lib = { name = path.lib_dir(name, version), is_module_path = true, perms = cfg.perm_exec }, + conf = { name = path.conf_dir(name, version), is_module_path = false, perms = cfg.perm_read }, + bin = { name = path.bin_dir(name, version), is_module_path = false, perms = cfg.perm_exec }, + } + + for _, d in pairs(dirs) do + local ok, err = fs.make_dir(d.name) + if not ok then return nil, err end + end + local rollback = util.schedule_function(function() + fs.delete(path.install_dir(name, version)) + fs.remove_dir_if_empty(path.versions_dir(name)) + end) + + local build_spec = rockspec.build + + if not minimal_mode then + ok, err = build.apply_patches(rockspec) + if err then + return nil, err + end + end + + if build_spec.type ~= "none" then + + -- Temporary compatibility + if build_spec.type == "module" then + util.printout("Do not use 'module' as a build type. Use 'builtin' instead.") + build_spec.type = "builtin" + end + + if cfg.accepted_build_types and util.array_contains(cfg.accepted_build_types, build_spec.type) then + return nil, "This rockspec uses the '"..build_spec.type.."' build type, which is blocked by the 'accepted_build_types' setting in your LuaRocks configuration." + end + + local build_type + ok, build_type = pcall(require, "luarocks.build." .. build_spec.type) + if not ok or not type(build_type) == "table" then + return nil, "Failed initializing build back-end for build type '"..build_spec.type.."': "..build_type + end + + ok, err = build_type.run(rockspec) + if not ok then + return nil, "Build error: " .. err + end + end + + if build_spec.install then + for id, install_dir in pairs(dirs) do + ok, err = install_files(build_spec.install[id], install_dir.name, install_dir.is_module_path, install_dir.perms) + if not ok then + return nil, err + end + end + end + + local copy_directories = build_spec.copy_directories + local copying_default = false + if not copy_directories then + copy_directories = {"doc"} + copying_default = true + end + + local any_docs = false + for _, copy_dir in pairs(copy_directories) do + if fs.is_dir(copy_dir) then + local dest = dir.path(path.install_dir(name, version), copy_dir) + fs.make_dir(dest) + fs.copy_contents(copy_dir, dest) + any_docs = true + else + if not copying_default then + return nil, "Directory '"..copy_dir.."' not found" + end + end + end + + if not any_docs then + install_default_docs(name, version) + end + + for _, d in pairs(dirs) do + fs.remove_dir_if_empty(d.name) + end + + fs.pop_dir() + + fs.copy(rockspec.local_filename, path.rockspec_file(name, version), cfg.perm_read) + if need_to_fetch then + fs.pop_dir() + end + + ok, err = manif.make_rock_manifest(name, version) + if err then return nil, err end + + ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) + if err then return nil, err end + + util.remove_scheduled_function(rollback) + rollback = util.schedule_function(function() + repos.delete_version(name, version, deps_mode) + end) + + ok, err = repos.run_hook(rockspec, "post_install") + if err then return nil, err end + + util.announce_install(rockspec) + util.remove_scheduled_function(rollback) + return name, version +end + +--- Build and install a rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param need_to_fetch boolean: true if sources need to be fetched, +-- false if the rockspec was obtained from inside a source rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @param build_only_deps boolean: true to build the listed dependencies only. +-- @return boolean or (nil, string, [string]): True if build was successful, +-- or false and an error message and an optional error code. +function build.build_rock(rock_file, need_to_fetch, deps_mode, build_only_deps) + assert(type(rock_file) == "string") + assert(type(need_to_fetch) == "boolean") + + local ok, err, errcode + local unpack_dir + unpack_dir, err, errcode = fetch.fetch_and_unpack_rock(rock_file) + if not unpack_dir then + return nil, err, errcode + end + local rockspec_file = path.rockspec_name_from_rock(rock_file) + ok, err = fs.change_dir(unpack_dir) + if not ok then return nil, err end + ok, err, errcode = build.build_rockspec(rockspec_file, need_to_fetch, false, deps_mode, build_only_deps) + fs.pop_dir() + return ok, err, errcode +end + +local function do_build(name, version, deps_mode, build_only_deps) + if name:match("%.rockspec$") then + return build.build_rockspec(name, true, false, deps_mode, build_only_deps) + elseif name:match("%.src%.rock$") then + return build.build_rock(name, false, deps_mode, build_only_deps) + elseif name:match("%.all%.rock$") then + local install = require("luarocks.install") + local install_fun = build_only_deps and install.install_binary_rock_deps or install.install_binary_rock + return install_fun(name, deps_mode) + elseif name:match("%.rock$") then + return build.build_rock(name, true, deps_mode, build_only_deps) + elseif not name:match(dir.separator) then + local search = require("luarocks.search") + return search.act_on_src_or_rockspec(do_build, name:lower(), version, nil, deps_mode, build_only_deps) + end + return nil, "Don't know what to do with "..name +end + +--- Driver function for "build" command. +-- @param name string: A local or remote rockspec or rock file. +-- If a package name is given, forwards the request to "search" and, +-- if returned a result, installs the matching rock. +-- @param version string: When passing a package name, a version number may +-- also be given. +-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an +-- error message otherwise. exitcode is optionally returned. +function build.command(flags, name, version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("build") + end + assert(type(version) == "string" or not version) + + if flags["pack-binary-rock"] then + return pack.pack_binary_rock(name, version, do_build, name, version, deps.get_deps_mode(flags)) + else + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + ok, err = do_build(name, version, deps.get_deps_mode(flags), flags["only-deps"]) + if not ok then return nil, err end + name, version = ok, err + + if (not flags["only-deps"]) and (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) + if not ok then util.printerr(err) end + end + + manif.check_dependencies(nil, deps.get_deps_mode(flags)) + return name, version + end +end + +return build diff --git a/Utils/luarocks/lua/luarocks/cache.lua b/Utils/luarocks/lua/luarocks/cache.lua new file mode 100644 index 000000000..4a95f70e3 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/cache.lua @@ -0,0 +1,78 @@ + +--- Module handling the LuaRocks local cache. +-- Adds a rock or rockspec to a rocks server. +local cache = {} +package.loaded["luarocks.cache"] = cache + +local fs = require("luarocks.fs") +local cfg = require("luarocks.cfg") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +function cache.get_upload_server(server) + if not server then server = cfg.upload_server end + if not server then + return nil, "No server specified and no default configured with upload_server." + end + return server, cfg.upload_servers and cfg.upload_servers[server] +end + +function cache.get_server_urls(server, upload_server) + local download_url = server + local login_url = nil + if upload_server then + if upload_server.rsync then download_url = "rsync://"..upload_server.rsync + elseif upload_server.http then download_url = "http://"..upload_server.http + elseif upload_server.ftp then download_url = "ftp://"..upload_server.ftp + end + + if upload_server.ftp then login_url = "ftp://"..upload_server.ftp + elseif upload_server.sftp then login_url = "sftp://"..upload_server.sftp + end + end + return download_url, login_url +end + +function cache.split_server_url(server, url, user, password) + local protocol, server_path = dir.split_url(url) + if server_path:match("@") then + local credentials + credentials, server_path = server_path:match("([^@]*)@(.*)") + if credentials:match(":") then + user, password = credentials:match("([^:]*):(.*)") + else + user = credentials + end + end + local local_cache = cfg.local_cache .. "/" .. server + return local_cache, protocol, server_path, user, password +end + +function cache.refresh_local_cache(server, url, user, password) + local local_cache, protocol, server_path, user, password = cache.split_server_url(server, url, user, password) + local ok, err = fs.make_dir(local_cache) + if not ok then + return nil, "Failed creating local cache dir: "..err + end + fs.change_dir(local_cache) + if not ok then return nil, err end + util.printout("Refreshing cache "..local_cache.."...") + + -- TODO abstract away explicit 'wget' call + local ok = false + if protocol == "rsync" then + local srv, path = server_path:match("([^/]+)(/.+)") + ok = fs.execute(cfg.variables.RSYNC.." "..cfg.variables.RSYNCFLAGS.." -e ssh "..user.."@"..srv..":"..path.."/ "..local_cache.."/") + else + local login_info = "" + if user then login_info = " --user="..user end + if password then login_info = login_info .. " --password="..password end + ok = fs.execute(cfg.variables.WGET.." --no-cache -q -m -np -nd "..protocol.."://"..server_path..login_info) + end + if not ok then + return nil, "Failed downloading cache." + end + return local_cache, protocol, server_path, user, password +end + +return cache diff --git a/Utils/luarocks/lua/luarocks/cfg.lua b/Utils/luarocks/lua/luarocks/cfg.lua new file mode 100644 index 000000000..c997c1240 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/cfg.lua @@ -0,0 +1,760 @@ +--- Configuration for LuaRocks. +-- Tries to load the user's configuration file and +-- defines defaults for unset values. See the +-- config +-- file format documentation for details. +-- +-- End-users shouldn't edit this file. They can override any defaults +-- set in this file using their system-wide or user-specific configuration +-- files. Run `luarocks` with no arguments to see the locations of +-- these files in your platform. + +local rawset, next, table, pairs, require, io, os, setmetatable, pcall, ipairs, package, tonumber, type, assert, _VERSION = + rawset, next, table, pairs, require, io, os, setmetatable, pcall, ipairs, package, tonumber, type, assert, _VERSION + +--module("luarocks.cfg") +local cfg = {} +package.loaded["luarocks.cfg"] = cfg + +local util = require("luarocks.util") + +cfg.lua_version = _VERSION:match(" (5%.[123])$") or "5.1" +local version_suffix = cfg.lua_version:gsub("%.", "_") + +-- Load site-local global configurations +local ok, site_config = pcall(require, "luarocks.site_config_"..version_suffix) +if not ok then + ok, site_config = pcall(require, "luarocks.site_config") +end +if not ok then + io.stderr:write("Site-local luarocks/site_config.lua file not found. Incomplete installation?\n") + site_config = {} +end + +cfg.program_version = "2.4.2" +cfg.program_series = "2.4" +cfg.major_version = (cfg.program_version:match("([^.]%.[^.])")) or cfg.program_series +cfg.variables = {} +cfg.rocks_trees = {} +cfg.platforms = {} + +local persist = require("luarocks.persist") + +cfg.errorcodes = setmetatable({ + OK = 0, + UNSPECIFIED = 1, + PERMISSIONDENIED = 2, + CONFIGFILE = 3, + CRASH = 99 +},{ + __index = function(t, key) + local val = rawget(t, key) + if not val then + error("'"..tostring(key).."' is not a valid errorcode", 2) + end + return val + end +}) + + +local popen_ok, popen_result = pcall(io.popen, "") +if popen_ok then + if popen_result then + popen_result:close() + end +else + io.stderr:write("Your version of Lua does not support io.popen,\n") + io.stderr:write("which is required by LuaRocks. Please check your Lua installation.\n") + os.exit(cfg.errorcodes.UNSPECIFIED) +end + +-- System detection: + +-- A proper installation of LuaRocks will hardcode the system +-- and proc values with site_config.LUAROCKS_UNAME_S and site_config.LUAROCKS_UNAME_M, +-- so that this detection does not run every time. When it is +-- performed, we use the Unix way to identify the system, +-- even on Windows (assuming UnxUtils or Cygwin). +local system = site_config.LUAROCKS_UNAME_S or io.popen("uname -s"):read("*l") +local proc = site_config.LUAROCKS_UNAME_M or io.popen("uname -m"):read("*l") +if proc:match("i[%d]86") then + cfg.target_cpu = "x86" +elseif proc:match("amd64") or proc:match("x86_64") then + cfg.target_cpu = "x86_64" +elseif proc:match("Power Macintosh") then + cfg.target_cpu = "powerpc" + else + cfg.target_cpu = proc +end + +if system == "FreeBSD" then + cfg.platforms.unix = true + cfg.platforms.freebsd = true + cfg.platforms.bsd = true +elseif system == "OpenBSD" then + cfg.platforms.unix = true + cfg.platforms.openbsd = true + cfg.platforms.bsd = true +elseif system == "NetBSD" then + cfg.platforms.unix = true + cfg.platforms.netbsd = true + cfg.platforms.bsd = true +elseif system == "Darwin" then + cfg.platforms.unix = true + cfg.platforms.macosx = true + cfg.platforms.bsd = true +elseif system == "Linux" then + cfg.platforms.unix = true + cfg.platforms.linux = true +elseif system == "SunOS" then + cfg.platforms.unix = true + cfg.platforms.solaris = true +elseif system and system:match("^CYGWIN") then + cfg.platforms.unix = true + cfg.platforms.cygwin = true +elseif system and system:match("^MSYS") then + cfg.platforms.unix = true + cfg.platforms.msys = true + cfg.platforms.cygwin = true +elseif system and system:match("^Windows") then + cfg.platforms.windows = true + cfg.platforms.win32 = true +elseif system and system:match("^MINGW") then + cfg.platforms.windows = true + cfg.platforms.mingw32 = true + cfg.platforms.win32 = true +elseif system == "Haiku" then + cfg.platforms.unix = true + cfg.platforms.haiku = true +else + cfg.platforms.unix = true + -- Fall back to Unix in unknown systems. +end + +-- Set order for platform overrides. +-- More general platform identifiers should be listed first, +-- more specific ones later. +local platform_order = { + -- Unixes + "unix", + "bsd", + "solaris", + "netbsd", + "openbsd", + "freebsd", + "linux", + "macosx", + "cygwin", + "msys", + "haiku", + -- Windows + "win32", + "mingw32", + "windows", +} + +-- Path configuration: +local sys_config_file, home_config_file +local sys_config_file_default, home_config_file_default +local sys_config_dir, home_config_dir +local sys_config_ok, home_config_ok = false, false +local extra_luarocks_module_dir +sys_config_dir = site_config.LUAROCKS_SYSCONFDIR or site_config.LUAROCKS_PREFIX +if cfg.platforms.windows then + cfg.home = os.getenv("APPDATA") or "c:" + sys_config_dir = sys_config_dir or "c:/luarocks" + home_config_dir = cfg.home.."/luarocks" + cfg.home_tree = cfg.home.."/luarocks/" +else + cfg.home = os.getenv("HOME") or "" + sys_config_dir = sys_config_dir or "/etc/luarocks" + home_config_dir = cfg.home.."/.luarocks" + cfg.home_tree = (os.getenv("USER") ~= "root") and cfg.home.."/.luarocks/" +end + +-- Create global environment for the config files; +local env_for_config_file = function() + local e + e = { + home = cfg.home, + lua_version = cfg.lua_version, + platforms = util.make_shallow_copy(cfg.platforms), + processor = cfg.target_cpu, -- remains for compat reasons + target_cpu = cfg.target_cpu, -- replaces `processor` + os_getenv = os.getenv, + dump_env = function() + -- debug function, calling it from a config file will show all + -- available globals to that config file + print(util.show_table(e, "global environment")) + end, + } + return e +end + +-- Merge values from config files read into the `cfg` table +local merge_overrides = function(overrides) + -- remove some stuff we do not want to integrate + overrides.os_getenv = nil + overrides.dump_env = nil + -- remove tables to be copied verbatim instead of deeply merged + if overrides.rocks_trees then cfg.rocks_trees = nil end + if overrides.rocks_servers then cfg.rocks_servers = nil end + -- perform actual merge + util.deep_merge(cfg, overrides) +end + +-- load config file from a list until first succesful one. Info is +-- added to `cfg` module table, returns filepath of succesfully loaded +-- file or nil if it failed +local load_config_file = function(list) + for _, filepath in ipairs(list) do + local result, err, errcode = persist.load_into_table(filepath, env_for_config_file()) + if (not result) and errcode ~= "open" then + -- errcode is either "load" or "run"; bad config file, so error out + io.stderr:write(err.."\n") + os.exit(cfg.errorcodes.CONFIGFILE) + end + if result then + -- succes in loading and running, merge contents and exit + merge_overrides(result) + return filepath + end + end + return nil -- nothing was loaded +end + + +-- Load system configuration file +do + sys_config_file_default = sys_config_dir.."/config-"..cfg.lua_version..".lua" + sys_config_file = load_config_file({ + site_config.LUAROCKS_SYSCONFIG or sys_config_file_default, + sys_config_dir.."/config.lua", + }) + sys_config_ok = (sys_config_file ~= nil) +end + +-- Load user configuration file (if allowed) +if not site_config.LUAROCKS_FORCE_CONFIG then + + home_config_file_default = home_config_dir.."/config-"..cfg.lua_version..".lua" + + local config_env_var = "LUAROCKS_CONFIG_" .. version_suffix + local config_env_value = os.getenv(config_env_var) + if not config_env_value then + config_env_var = "LUAROCKS_CONFIG" + config_env_value = os.getenv(config_env_var) + end + + -- first try environment provided file, so we can explicitly warn when it is missing + if config_env_value then + local list = { config_env_value } + home_config_file = load_config_file(list) + home_config_ok = (home_config_file ~= nil) + if not home_config_ok then + io.stderr:write("Warning: could not load configuration file `"..config_env_value.."` given in environment variable "..config_env_var.."\n") + end + end + + -- try the alternative defaults if there was no environment specified file or it didn't work + if not home_config_ok then + local list = { + home_config_file_default, + home_config_dir.."/config.lua", + } + home_config_file = load_config_file(list) + home_config_ok = (home_config_file ~= nil) + end +end + + +if not next(cfg.rocks_trees) then + if cfg.home_tree then + table.insert(cfg.rocks_trees, { name = "user", root = cfg.home_tree } ) + end + if site_config.LUAROCKS_ROCKS_TREE then + table.insert(cfg.rocks_trees, { name = "system", root = site_config.LUAROCKS_ROCKS_TREE } ) + end +end + +-- update platforms list; keyed -> array +do + -- if explicitly given by user, + if cfg.platforms[1] then + local is_windows = cfg.platforms.windows + -- Clear auto-detected values + for k, _ in pairs(cfg.platforms) do + if type(k) == "string" then + cfg.platforms[k] = nil + end + end + -- and set the ones given by the user. + for _, plat in ipairs(cfg.platforms) do + cfg.platforms[plat] = true + end + -- If no major platform family was set by the user, + if not (cfg.platforms.unix or cfg.platforms.windows) then + -- set some fallback defaults in case the user provides an incomplete configuration. + -- LuaRocks expects a set of defaults to be available. + -- This is used for setting defaults here only; the platform overrides + -- will use only the user's list. + if is_windows then + cfg.platforms.windows = true + table.insert(cfg.platforms, "windows") + else + cfg.platforms.unix = true + table.insert(cfg.platforms, "unix") + end + end + else + -- Sort detected platform defaults + local order = {} + for i, v in ipairs(platform_order) do + order[v] = i + end + local entries = {} + for k, v in pairs(cfg.platforms) do + if type(k) == "string" and v == true then + table.insert(entries, k) + end + end + table.sort(entries, function(a, b) return order[a] < order[b] end) + util.deep_merge(cfg.platforms, entries) + end +end + +-- Configure defaults: +local defaults = { + + local_by_default = false, + accept_unknown_fields = false, + fs_use_modules = true, + hooks_enabled = true, + deps_mode = "one", + check_certificates = false, + perm_read = "0644", + perm_exec = "0755", + + lua_modules_path = "/share/lua/"..cfg.lua_version, + lib_modules_path = "/lib/lua/"..cfg.lua_version, + rocks_subdir = site_config.LUAROCKS_ROCKS_SUBDIR or "/lib/luarocks/rocks", + + arch = "unknown", + lib_extension = "unknown", + obj_extension = "unknown", + link_lua_explicitly = false, + + rocks_servers = { + { + "https://luarocks.org", + "https://raw.githubusercontent.com/rocks-moonscript-org/moonrocks-mirror/master/", + "http://luafr.org/moonrocks/", + "http://luarocks.logiceditor.com/rocks", + } + }, + disabled_servers = {}, + + upload = { + server = "https://luarocks.org", + tool_version = "1.0.0", + api_version = "1", + }, + + lua_extension = "lua", + lua_interpreter = site_config.LUA_INTERPRETER or "lua", + downloader = site_config.LUAROCKS_DOWNLOADER or "wget", + md5checker = site_config.LUAROCKS_MD5CHECKER or "md5sum", + connection_timeout = 30, -- 0 = no timeout + + variables = { + MAKE = "make", + CC = "cc", + LD = "ld", + + CVS = "cvs", + GIT = "git", + SSCM = "sscm", + SVN = "svn", + HG = "hg", + + RSYNC = "rsync", + WGET = "wget", + SCP = "scp", + CURL = "curl", + + PWD = "pwd", + MKDIR = "mkdir", + RMDIR = "rmdir", + CP = "cp", + LS = "ls", + RM = "rm", + FIND = "find", + TEST = "test", + CHMOD = "chmod", + MKTEMP = "mktemp", + + ZIP = "zip", + UNZIP = "unzip -n", + GUNZIP = "gunzip", + BUNZIP2 = "bunzip2", + TAR = "tar", + + MD5SUM = "md5sum", + OPENSSL = "openssl", + MD5 = "md5", + STAT = "stat", + TOUCH = "touch", + + CMAKE = "cmake", + SEVENZ = "7z", + + RSYNCFLAGS = "--exclude=.git -Oavz", + STATFLAG = "-c '%a'", + CURLNOCERTFLAG = "", + WGETNOCERTFLAG = "", + }, + + external_deps_subdirs = site_config.LUAROCKS_EXTERNAL_DEPS_SUBDIRS or { + bin = "bin", + lib = "lib", + include = "include" + }, + runtime_external_deps_subdirs = site_config.LUAROCKS_RUNTIME_EXTERNAL_DEPS_SUBDIRS or { + bin = "bin", + lib = "lib", + include = "include" + }, + + rocks_provided = {} +} + +if cfg.platforms.windows then + local full_prefix = (site_config.LUAROCKS_PREFIX or (os.getenv("PROGRAMFILES")..[[\LuaRocks]])) + extra_luarocks_module_dir = full_prefix.."/lua/?.lua" + + home_config_file = home_config_file and home_config_file:gsub("\\","/") + defaults.fs_use_modules = false + defaults.arch = "win32-"..cfg.target_cpu + defaults.lib_extension = "dll" + defaults.external_lib_extension = "dll" + defaults.obj_extension = "obj" + defaults.external_deps_dirs = { "c:/external/" } + defaults.variables.LUA_BINDIR = site_config.LUA_BINDIR and site_config.LUA_BINDIR:gsub("\\", "/") or "c:/lua"..cfg.lua_version.."/bin" + defaults.variables.LUA_INCDIR = site_config.LUA_INCDIR and site_config.LUA_INCDIR:gsub("\\", "/") or "c:/lua"..cfg.lua_version.."/include" + defaults.variables.LUA_LIBDIR = site_config.LUA_LIBDIR and site_config.LUA_LIBDIR:gsub("\\", "/") or "c:/lua"..cfg.lua_version.."/lib" + + defaults.makefile = "Makefile.win" + defaults.variables.MAKE = "nmake" + defaults.variables.CC = "cl" + defaults.variables.RC = "rc" + defaults.variables.WRAPPER = full_prefix.."\\rclauncher.c" + defaults.variables.LD = "link" + defaults.variables.MT = "mt" + defaults.variables.LUALIB = "lua"..cfg.lua_version..".lib" + defaults.variables.CFLAGS = "/nologo /MD /O2" + defaults.variables.LIBFLAG = "/nologo /dll" + + local bins = { "SEVENZ", "CP", "FIND", "LS", "MD5SUM", + "MKDIR", "MV", "PWD", "RMDIR", "TEST", "UNAME", "WGET" } + for _, var in ipairs(bins) do + if defaults.variables[var] then + defaults.variables[var] = full_prefix.."\\tools\\"..defaults.variables[var] + end + end + + defaults.external_deps_patterns = { + bin = { "?.exe", "?.bat" }, + lib = { "?.lib", "?.dll", "lib?.dll" }, + include = { "?.h" } + } + defaults.runtime_external_deps_patterns = { + bin = { "?.exe", "?.bat" }, + lib = { "?.dll", "lib?.dll" }, + include = { "?.h" } + } + defaults.export_path = "SET PATH=%s" + defaults.export_path_separator = ";" + defaults.export_lua_path = "SET LUA_PATH=%s" + defaults.export_lua_cpath = "SET LUA_CPATH=%s" + defaults.wrapper_suffix = ".bat" + + local localappdata = os.getenv("LOCALAPPDATA") + if not localappdata then + -- for Windows versions below Vista + localappdata = os.getenv("USERPROFILE").."/Local Settings/Application Data" + end + defaults.local_cache = localappdata.."/LuaRocks/Cache" + defaults.web_browser = "start" +end + +if cfg.platforms.mingw32 then + defaults.obj_extension = "o" + defaults.cmake_generator = "MinGW Makefiles" + defaults.variables.MAKE = "mingw32-make" + defaults.variables.CC = "mingw32-gcc" + defaults.variables.RC = "windres" + defaults.variables.LD = "mingw32-gcc" + defaults.variables.CFLAGS = "-O2" + defaults.variables.LIBFLAG = "-shared" + defaults.makefile = "Makefile" + defaults.external_deps_patterns = { + bin = { "?.exe", "?.bat" }, + -- mingw lookup list from http://stackoverflow.com/a/15853231/1793220 + -- ...should we keep ?.lib at the end? It's not in the above list. + lib = { "lib?.dll.a", "?.dll.a", "lib?.a", "cyg?.dll", "lib?.dll", "?.dll", "?.lib" }, + include = { "?.h" } + } + defaults.runtime_external_deps_patterns = { + bin = { "?.exe", "?.bat" }, + lib = { "cyg?.dll", "?.dll", "lib?.dll" }, + include = { "?.h" } + } + +end + +if cfg.platforms.unix then + defaults.lib_extension = "so" + defaults.external_lib_extension = "so" + defaults.obj_extension = "o" + defaults.external_deps_dirs = { "/usr/local", "/usr" } + defaults.variables.LUA_BINDIR = site_config.LUA_BINDIR or "/usr/local/bin" + defaults.variables.LUA_INCDIR = site_config.LUA_INCDIR or "/usr/local/include" + defaults.variables.LUA_LIBDIR = site_config.LUA_LIBDIR or "/usr/local/lib" + defaults.variables.CFLAGS = "-O2" + defaults.cmake_generator = "Unix Makefiles" + defaults.variables.CC = "gcc" + defaults.variables.LD = "gcc" + defaults.gcc_rpath = true + defaults.variables.LIBFLAG = "-shared" + defaults.external_deps_patterns = { + bin = { "?" }, + lib = { "lib?.a", "lib?.so", "lib?.so.*" }, + include = { "?.h" } + } + defaults.runtime_external_deps_patterns = { + bin = { "?" }, + lib = { "lib?.so", "lib?.so.*" }, + include = { "?.h" } + } + defaults.export_path = "export PATH='%s'" + defaults.export_path_separator = ":" + defaults.export_lua_path = "export LUA_PATH='%s'" + defaults.export_lua_cpath = "export LUA_CPATH='%s'" + defaults.wrapper_suffix = "" + defaults.local_cache = cfg.home.."/.cache/luarocks" + if not defaults.variables.CFLAGS:match("-fPIC") then + defaults.variables.CFLAGS = defaults.variables.CFLAGS.." -fPIC" + end + defaults.web_browser = "xdg-open" +end + +if cfg.platforms.cygwin then + defaults.lib_extension = "so" -- can be overridden in the config file for mingw builds + defaults.arch = "cygwin-"..cfg.target_cpu + defaults.cmake_generator = "Unix Makefiles" + defaults.variables.CC = "echo -llua | xargs gcc" + defaults.variables.LD = "echo -llua | xargs gcc" + defaults.variables.LIBFLAG = "-shared" + defaults.link_lua_explicitly = true +end + +if cfg.platforms.msys then + -- msys is basically cygwin made out of mingw, meaning the subsytem is unixish + -- enough, yet we can freely mix with native win32 + defaults.external_deps_patterns = { + bin = { "?.exe", "?.bat", "?" }, + lib = { "lib?.so", "lib?.so.*", "lib?.dll.a", "?.dll.a", + "lib?.a", "lib?.dll", "?.dll", "?.lib" }, + include = { "?.h" } + } + defaults.runtime_external_deps_patterns = { + bin = { "?.exe", "?.bat" }, + lib = { "lib?.so", "?.dll", "lib?.dll" }, + include = { "?.h" } + } +end + + +if cfg.platforms.bsd then + defaults.variables.MAKE = "gmake" + defaults.variables.STATFLAG = "-f '%OLp'" +end + +if cfg.platforms.macosx then + defaults.variables.MAKE = "make" + defaults.external_lib_extension = "dylib" + defaults.arch = "macosx-"..cfg.target_cpu + defaults.variables.LIBFLAG = "-bundle -undefined dynamic_lookup -all_load" + defaults.variables.STAT = "/usr/bin/stat" + defaults.variables.STATFLAG = "-f '%A'" + local version = io.popen("sw_vers -productVersion"):read("*l") + version = tonumber(version and version:match("^[^.]+%.([^.]+)")) or 3 + if version >= 10 then + version = 8 + elseif version >= 5 then + version = 5 + else + defaults.gcc_rpath = false + end + defaults.variables.CC = "env MACOSX_DEPLOYMENT_TARGET=10."..version.." gcc" + defaults.variables.LD = "env MACOSX_DEPLOYMENT_TARGET=10."..version.." gcc" + defaults.web_browser = "open" +end + +if cfg.platforms.linux then + defaults.arch = "linux-"..cfg.target_cpu +end + +if cfg.platforms.freebsd then + defaults.arch = "freebsd-"..cfg.target_cpu + defaults.gcc_rpath = false + defaults.variables.CC = "cc" + defaults.variables.LD = "cc" +end + +if cfg.platforms.openbsd then + defaults.arch = "openbsd-"..cfg.target_cpu +end + +if cfg.platforms.netbsd then + defaults.arch = "netbsd-"..cfg.target_cpu +end + +if cfg.platforms.solaris then + defaults.arch = "solaris-"..cfg.target_cpu + --defaults.platforms = {"unix", "solaris"} + defaults.variables.MAKE = "gmake" +end + +-- Expose some more values detected by LuaRocks for use by rockspec authors. +defaults.variables.LIB_EXTENSION = defaults.lib_extension +defaults.variables.OBJ_EXTENSION = defaults.obj_extension +defaults.variables.LUAROCKS_PREFIX = site_config.LUAROCKS_PREFIX +defaults.variables.LUA = site_config.LUA_DIR_SET and (defaults.variables.LUA_BINDIR.."/"..defaults.lua_interpreter) or defaults.lua_interpreter + +-- Add built-in modules to rocks_provided +defaults.rocks_provided["lua"] = cfg.lua_version.."-1" + +if bit32 then -- Lua 5.2+ + defaults.rocks_provided["bit32"] = cfg.lua_version.."-1" +end + +if utf8 then -- Lua 5.3+ + defaults.rocks_provided["utf8"] = cfg.lua_version.."-1" +end + +if package.loaded.jit then + -- LuaJIT + local lj_version = package.loaded.jit.version:match("LuaJIT (.*)"):gsub("%-","") + --defaults.rocks_provided["luajit"] = lj_version.."-1" + defaults.rocks_provided["luabitop"] = lj_version.."-1" +end + +-- Use defaults: + +-- Populate some arrays with values from their 'defaults' counterparts +-- if they were not already set by user. +for _, entry in ipairs({"variables", "rocks_provided"}) do + if not cfg[entry] then + cfg[entry] = {} + end + for k,v in pairs(defaults[entry]) do + if not cfg[entry][k] then + cfg[entry][k] = v + end + end +end + +-- For values not set in the config file, use values from the 'defaults' table. +local cfg_mt = { + __index = function(t, k) + local default = defaults[k] + if default then + rawset(t, k, default) + end + return default + end +} +setmetatable(cfg, cfg_mt) + +if not cfg.check_certificates then + cfg.variables.CURLNOCERTFLAG = "-k" + cfg.variables.WGETNOCERTFLAG = "--no-check-certificate" +end + +function cfg.make_paths_from_tree(tree) + local lua_path, lib_path, bin_path + if type(tree) == "string" then + lua_path = tree..cfg.lua_modules_path + lib_path = tree..cfg.lib_modules_path + bin_path = tree.."/bin" + else + lua_path = tree.lua_dir or tree.root..cfg.lua_modules_path + lib_path = tree.lib_dir or tree.root..cfg.lib_modules_path + bin_path = tree.bin_dir or tree.root.."/bin" + end + return lua_path, lib_path, bin_path +end + +function cfg.package_paths(current) + local new_path, new_cpath, new_bin = {}, {}, {} + local function add_tree_to_paths(tree) + local lua_path, lib_path, bin_path = cfg.make_paths_from_tree(tree) + table.insert(new_path, lua_path.."/?.lua") + table.insert(new_path, lua_path.."/?/init.lua") + table.insert(new_cpath, lib_path.."/?."..cfg.lib_extension) + table.insert(new_bin, bin_path) + end + if current then + add_tree_to_paths(current) + end + for _,tree in ipairs(cfg.rocks_trees) do + add_tree_to_paths(tree) + end + if extra_luarocks_module_dir then + table.insert(new_path, extra_luarocks_module_dir) + end + return table.concat(new_path, ";"), table.concat(new_cpath, ";"), table.concat(new_bin, cfg.export_path_separator) +end + +function cfg.init_package_paths() + local lr_path, lr_cpath, lr_bin = cfg.package_paths() + package.path = util.remove_path_dupes(package.path .. ";" .. lr_path, ";") + package.cpath = util.remove_path_dupes(package.cpath .. ";" .. lr_cpath, ";") +end + +function cfg.which_config() + local ret = { + system = { + file = sys_config_file or sys_config_file_default, + ok = sys_config_ok, + }, + user = { + file = home_config_file or home_config_file_default, + ok = home_config_ok, + } + } + ret.nearest = (ret.user.ok and ret.user.file) or ret.system.file + return ret +end + +cfg.user_agent = "LuaRocks/"..cfg.program_version.." "..cfg.arch + +cfg.http_proxy = os.getenv("http_proxy") +cfg.https_proxy = os.getenv("https_proxy") +cfg.no_proxy = os.getenv("no_proxy") + +--- Check if platform was detected +-- @param query string: The platform name to check. +-- @return boolean: true if LuaRocks is currently running on queried platform. +function cfg.is_platform(query) + assert(type(query) == "string") + + for _, platform in ipairs(cfg.platforms) do + if platform == query then + return true + end + end +end + +return cfg diff --git a/Utils/luarocks/lua/luarocks/command_line.lua b/Utils/luarocks/lua/luarocks/command_line.lua new file mode 100644 index 000000000..ecf3a61b1 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/command_line.lua @@ -0,0 +1,199 @@ + +--- Functions for command-line scripts. +local command_line = {} + +local unpack = unpack or table.unpack + +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") +local path = require("luarocks.path") +local dir = require("luarocks.dir") +local deps = require("luarocks.deps") +local fs = require("luarocks.fs") + +local program = util.this_program("luarocks") + +local function error_handler(err) + return debug.traceback("LuaRocks "..cfg.program_version.. + " bug (please report at https://github.com/keplerproject/luarocks/issues).\n"..err, 2) +end + +--- Display an error message and exit. +-- @param message string: The error message. +-- @param exitcode number: the exitcode to use +local function die(message, exitcode) + assert(type(message) == "string") + util.printerr("\nError: "..message) + + local ok, err = xpcall(util.run_scheduled_functions, error_handler) + if not ok then + util.printerr("\nError: "..err) + exitcode = cfg.errorcodes.CRASH + end + + os.exit(exitcode or cfg.errorcodes.UNSPECIFIED) +end + +local function replace_tree(flags, tree) + tree = dir.normalize(tree) + flags["tree"] = tree + path.use_tree(tree) +end + +--- Main command-line processor. +-- Parses input arguments and calls the appropriate driver function +-- to execute the action requested on the command-line, forwarding +-- to it any additional arguments passed by the user. +-- Uses the global table "commands", which contains +-- the loaded modules representing commands. +-- @param ... string: Arguments given on the command-line. +function command_line.run_command(...) + local args = {...} + local cmdline_vars = {} + for i = #args, 1, -1 do + local arg = args[i] + if arg:match("^[^-][^=]*=") then + local var, val = arg:match("^([A-Z_][A-Z0-9_]*)=(.*)") + if val then + cmdline_vars[var] = val + table.remove(args, i) + else + die("Invalid assignment: "..arg) + end + end + end + local nonflags = { util.parse_flags(unpack(args)) } + local flags = table.remove(nonflags, 1) + if flags.ERROR then + die(flags.ERROR.." See --help.") + end + + if flags["from"] then flags["server"] = flags["from"] end + if flags["only-from"] then flags["only-server"] = flags["only-from"] end + if flags["only-sources-from"] then flags["only-sources"] = flags["only-sources-from"] end + if flags["to"] then flags["tree"] = flags["to"] end + if flags["nodeps"] then + flags["deps-mode"] = "none" + end + + cfg.flags = flags + + local command + + if flags["verbose"] then -- setting it in the config file will kick-in earlier in the process + cfg.verbose = true + fs.verbose() + end + + if flags["timeout"] then -- setting it in the config file will kick-in earlier in the process + local timeout = tonumber(flags["timeout"]) + if timeout then + cfg.connection_timeout = timeout + else + die "Argument error: --timeout expects a numeric argument." + end + end + + if flags["version"] then + util.printout(program.." "..cfg.program_version) + util.printout(program_description) + util.printout() + os.exit(cfg.errorcodes.OK) + elseif flags["help"] or #nonflags == 0 then + command = "help" + else + command = table.remove(nonflags, 1) + end + command = command:gsub("-", "_") + + if cfg.local_by_default then + flags["local"] = true + end + + if flags["deps-mode"] and not deps.check_deps_mode_flag(flags["deps-mode"]) then + die("Invalid entry for --deps-mode.") + end + + if flags["branch"] then + cfg.branch = flags["branch"] + end + + if flags["tree"] then + local named = false + for _, tree in ipairs(cfg.rocks_trees) do + if type(tree) == "table" and flags["tree"] == tree.name then + if not tree.root then + die("Configuration error: tree '"..tree.name.."' has no 'root' field.") + end + replace_tree(flags, tree.root) + named = true + break + end + end + if not named then + local root_dir = fs.absolute_name(flags["tree"]) + replace_tree(flags, root_dir) + end + elseif flags["local"] then + if not cfg.home_tree then + die("The --local flag is meant for operating in a user's home directory.\n".. + "You are running as a superuser, which is intended for system-wide operation.\n".. + "To force using the superuser's home, use --tree explicitly.") + end + replace_tree(flags, cfg.home_tree) + else + local trees = cfg.rocks_trees + path.use_tree(trees[#trees]) + end + + if type(cfg.root_dir) == "string" then + cfg.root_dir = cfg.root_dir:gsub("/+$", "") + else + cfg.root_dir.root = cfg.root_dir.root:gsub("/+$", "") + end + cfg.rocks_dir = cfg.rocks_dir:gsub("/+$", "") + cfg.deploy_bin_dir = cfg.deploy_bin_dir:gsub("/+$", "") + cfg.deploy_lua_dir = cfg.deploy_lua_dir:gsub("/+$", "") + cfg.deploy_lib_dir = cfg.deploy_lib_dir:gsub("/+$", "") + + cfg.variables.ROCKS_TREE = cfg.rocks_dir + cfg.variables.SCRIPTS_DIR = cfg.deploy_bin_dir + + if flags["server"] then + local protocol, path = dir.split_url(flags["server"]) + table.insert(cfg.rocks_servers, 1, protocol.."://"..path) + end + + if flags["only-server"] then + cfg.rocks_servers = { flags["only-server"] } + end + + if flags["only-sources"] then + cfg.only_sources_from = flags["only-sources"] + end + + if command ~= "help" then + for k, v in pairs(cmdline_vars) do + cfg.variables[k] = v + end + end + + if not fs.current_dir() or fs.current_dir() == "" then + die("Current directory does not exist. Please run LuaRocks from an existing directory.") + end + + if commands[command] then + local cmd = require(commands[command]) + local call_ok, ok, err, exitcode = xpcall(function() return cmd.command(flags, unpack(nonflags)) end, error_handler) + if not call_ok then + die(ok, cfg.errorcodes.CRASH) + elseif not ok then + die(err, exitcode) + end + else + die("Unknown command: "..command) + end + util.run_scheduled_functions() +end + +return command_line diff --git a/Utils/luarocks/lua/luarocks/config_cmd.lua b/Utils/luarocks/lua/luarocks/config_cmd.lua new file mode 100644 index 000000000..fe3cc637d --- /dev/null +++ b/Utils/luarocks/lua/luarocks/config_cmd.lua @@ -0,0 +1,72 @@ +--- Module implementing the LuaRocks "config" command. +-- Queries information about the LuaRocks configuration. +local config_cmd = {} + +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") + +util.add_run_function(config_cmd) +config_cmd.help_summary = "Query information about the LuaRocks configuration." +config_cmd.help_arguments = "" +config_cmd.help = [[ +--lua-incdir Path to Lua header files. + +--lua-libdir Path to Lua library files. + +--lua-ver Lua version (in major.minor format). e.g. 5.1 + +--system-config Location of the system config file. + +--user-config Location of the user config file. + +--rock-trees Rocks trees in use. First the user tree, then the system tree. +]] + +local function config_file(conf) + print(dir.normalize(conf.file)) + if conf.ok then + return true + else + return nil, "file not found" + end +end + +--- Driver function for "config" command. +-- @return boolean: True if succeeded, nil on errors. +function config_cmd.command(flags) + if flags["lua-incdir"] then + print(cfg.variables.LUA_INCDIR) + return true + end + if flags["lua-libdir"] then + print(cfg.variables.LUA_LIBDIR) + return true + end + if flags["lua-ver"] then + print(cfg.lua_version) + return true + end + local conf = cfg.which_config() + if flags["system-config"] then + return config_file(conf.system) + end + if flags["user-config"] then + return config_file(conf.user) + end + if flags["rock-trees"] then + for _, tree in ipairs(cfg.rocks_trees) do + if type(tree) == "string" then + util.printout(dir.normalize(tree)) + else + local name = tree.name and "\t"..tree.name or "" + util.printout(dir.normalize(tree.root)..name) + end + end + return true + end + + return nil, "Please provide a flag for querying configuration values. "..util.see_help("config") +end + +return config_cmd diff --git a/Utils/luarocks/lua/luarocks/deps.lua b/Utils/luarocks/lua/luarocks/deps.lua new file mode 100644 index 000000000..dcebec9b2 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/deps.lua @@ -0,0 +1,752 @@ + +--- Dependency handling functions. +-- Dependencies are represented in LuaRocks through strings with +-- a package name followed by a comma-separated list of constraints. +-- Each constraint consists of an operator and a version number. +-- In this string format, version numbers are represented as +-- naturally as possible, like they are used by upstream projects +-- (e.g. "2.0beta3"). Internally, LuaRocks converts them to a purely +-- numeric representation, allowing comparison following some +-- "common sense" heuristics. The precise specification of the +-- comparison criteria is the source code of this module, but the +-- test/test_deps.lua file included with LuaRocks provides some +-- insights on what these criteria are. +local deps = {} +package.loaded["luarocks.deps"] = deps + +local cfg = require("luarocks.cfg") +local manif_core = require("luarocks.manif_core") +local path = require("luarocks.path") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +local operators = { + ["=="] = "==", + ["~="] = "~=", + [">"] = ">", + ["<"] = "<", + [">="] = ">=", + ["<="] = "<=", + ["~>"] = "~>", + -- plus some convenience translations + [""] = "==", + ["="] = "==", + ["!="] = "~=" +} + +local deltas = { + scm = 1100, + cvs = 1000, + rc = -1000, + pre = -10000, + beta = -100000, + alpha = -1000000 +} + +local version_mt = { + --- Equality comparison for versions. + -- All version numbers must be equal. + -- If both versions have revision numbers, they must be equal; + -- otherwise the revision number is ignored. + -- @param v1 table: version table to compare. + -- @param v2 table: version table to compare. + -- @return boolean: true if they are considered equivalent. + __eq = function(v1, v2) + if #v1 ~= #v2 then + return false + end + for i = 1, #v1 do + if v1[i] ~= v2[i] then + return false + end + end + if v1.revision and v2.revision then + return (v1.revision == v2.revision) + end + return true + end, + --- Size comparison for versions. + -- All version numbers are compared. + -- If both versions have revision numbers, they are compared; + -- otherwise the revision number is ignored. + -- @param v1 table: version table to compare. + -- @param v2 table: version table to compare. + -- @return boolean: true if v1 is considered lower than v2. + __lt = function(v1, v2) + for i = 1, math.max(#v1, #v2) do + local v1i, v2i = v1[i] or 0, v2[i] or 0 + if v1i ~= v2i then + return (v1i < v2i) + end + end + if v1.revision and v2.revision then + return (v1.revision < v2.revision) + end + return false + end +} + +local version_cache = {} +setmetatable(version_cache, { + __mode = "kv" +}) + +--- Parse a version string, converting to table format. +-- A version table contains all components of the version string +-- converted to numeric format, stored in the array part of the table. +-- If the version contains a revision, it is stored numerically +-- in the 'revision' field. The original string representation of +-- the string is preserved in the 'string' field. +-- Returned version tables use a metatable +-- allowing later comparison through relational operators. +-- @param vstring string: A version number in string format. +-- @return table or nil: A version table or nil +-- if the input string contains invalid characters. +function deps.parse_version(vstring) + if not vstring then return nil end + assert(type(vstring) == "string") + + local cached = version_cache[vstring] + if cached then + return cached + end + + local version = {} + local i = 1 + + local function add_token(number) + version[i] = version[i] and version[i] + number/100000 or number + i = i + 1 + end + + -- trim leading and trailing spaces + vstring = vstring:match("^%s*(.*)%s*$") + version.string = vstring + -- store revision separately if any + local main, revision = vstring:match("(.*)%-(%d+)$") + if revision then + vstring = main + version.revision = tonumber(revision) + end + while #vstring > 0 do + -- extract a number + local token, rest = vstring:match("^(%d+)[%.%-%_]*(.*)") + if token then + add_token(tonumber(token)) + else + -- extract a word + token, rest = vstring:match("^(%a+)[%.%-%_]*(.*)") + if not token then + util.printerr("Warning: version number '"..vstring.."' could not be parsed.") + version[i] = 0 + break + end + version[i] = deltas[token] or (token:byte() / 1000) + end + vstring = rest + end + setmetatable(version, version_mt) + version_cache[vstring] = version + return version +end + +--- Utility function to compare version numbers given as strings. +-- @param a string: one version. +-- @param b string: another version. +-- @return boolean: True if a > b. +function deps.compare_versions(a, b) + return deps.parse_version(a) > deps.parse_version(b) +end + +--- Consumes a constraint from a string, converting it to table format. +-- For example, a string ">= 1.0, > 2.0" is converted to a table in the +-- format {op = ">=", version={1,0}} and the rest, "> 2.0", is returned +-- back to the caller. +-- @param input string: A list of constraints in string format. +-- @return (table, string) or nil: A table representing the same +-- constraints and the string with the unused input, or nil if the +-- input string is invalid. +local function parse_constraint(input) + assert(type(input) == "string") + + local no_upgrade, op, version, rest = input:match("^(@?)([<>=~!]*)%s*([%w%.%_%-]+)[%s,]*(.*)") + local _op = operators[op] + version = deps.parse_version(version) + if not _op then + return nil, "Encountered bad constraint operator: '"..tostring(op).."' in '"..input.."'" + end + if not version then + return nil, "Could not parse version from constraint: '"..input.."'" + end + return { op = _op, version = version, no_upgrade = no_upgrade=="@" and true or nil }, rest +end + +--- Convert a list of constraints from string to table format. +-- For example, a string ">= 1.0, < 2.0" is converted to a table in the format +-- {{op = ">=", version={1,0}}, {op = "<", version={2,0}}}. +-- Version tables use a metatable allowing later comparison through +-- relational operators. +-- @param input string: A list of constraints in string format. +-- @return table or nil: A table representing the same constraints, +-- or nil if the input string is invalid. +function deps.parse_constraints(input) + assert(type(input) == "string") + + local constraints, constraint, oinput = {}, nil, input + while #input > 0 do + constraint, input = parse_constraint(input) + if constraint then + table.insert(constraints, constraint) + else + return nil, "Failed to parse constraint '"..tostring(oinput).."' with error: ".. input + end + end + return constraints +end + +--- Convert a dependency from string to table format. +-- For example, a string "foo >= 1.0, < 2.0" +-- is converted to a table in the format +-- {name = "foo", constraints = {{op = ">=", version={1,0}}, +-- {op = "<", version={2,0}}}}. Version tables use a metatable +-- allowing later comparison through relational operators. +-- @param dep string: A dependency in string format +-- as entered in rockspec files. +-- @return table or nil: A table representing the same dependency relation, +-- or nil if the input string is invalid. +function deps.parse_dep(dep) + assert(type(dep) == "string") + + local name, rest = dep:match("^%s*([a-zA-Z0-9][a-zA-Z0-9%.%-%_]*)%s*(.*)") + if not name then return nil, "failed to extract dependency name from '"..tostring(dep).."'" end + local constraints, err = deps.parse_constraints(rest) + if not constraints then return nil, err end + return { name = name, constraints = constraints } +end + +--- Convert a version table to a string. +-- @param v table: The version table +-- @param internal boolean or nil: Whether to display versions in their +-- internal representation format or how they were specified. +-- @return string: The dependency information pretty-printed as a string. +function deps.show_version(v, internal) + assert(type(v) == "table") + assert(type(internal) == "boolean" or not internal) + + return (internal + and table.concat(v, ":")..(v.revision and tostring(v.revision) or "") + or v.string) +end + +--- Convert a dependency in table format to a string. +-- @param dep table: The dependency in table format +-- @param internal boolean or nil: Whether to display versions in their +-- internal representation format or how they were specified. +-- @return string: The dependency information pretty-printed as a string. +function deps.show_dep(dep, internal) + assert(type(dep) == "table") + assert(type(internal) == "boolean" or not internal) + + if #dep.constraints > 0 then + local pretty = {} + for _, c in ipairs(dep.constraints) do + table.insert(pretty, c.op .. " " .. deps.show_version(c.version, internal)) + end + return dep.name.." "..table.concat(pretty, ", ") + else + return dep.name + end +end + +--- A more lenient check for equivalence between versions. +-- This returns true if the requested components of a version +-- match and ignore the ones that were not given. For example, +-- when requesting "2", then "2", "2.1", "2.3.5-9"... all match. +-- When requesting "2.1", then "2.1", "2.1.3" match, but "2.2" +-- doesn't. +-- @param version string or table: Version to be tested; may be +-- in string format or already parsed into a table. +-- @param requested string or table: Version requested; may be +-- in string format or already parsed into a table. +-- @return boolean: True if the tested version matches the requested +-- version, false otherwise. +local function partial_match(version, requested) + assert(type(version) == "string" or type(version) == "table") + assert(type(requested) == "string" or type(version) == "table") + + if type(version) ~= "table" then version = deps.parse_version(version) end + if type(requested) ~= "table" then requested = deps.parse_version(requested) end + if not version or not requested then return false end + + for i, ri in ipairs(requested) do + local vi = version[i] or 0 + if ri ~= vi then return false end + end + if requested.revision then + return requested.revision == version.revision + end + return true +end + +--- Check if a version satisfies a set of constraints. +-- @param version table: A version in table format +-- @param constraints table: An array of constraints in table format. +-- @return boolean: True if version satisfies all constraints, +-- false otherwise. +function deps.match_constraints(version, constraints) + assert(type(version) == "table") + assert(type(constraints) == "table") + local ok = true + setmetatable(version, version_mt) + for _, constr in pairs(constraints) do + if type(constr.version) == "string" then + constr.version = deps.parse_version(constr.version) + end + local constr_version, constr_op = constr.version, constr.op + setmetatable(constr_version, version_mt) + if constr_op == "==" then ok = version == constr_version + elseif constr_op == "~=" then ok = version ~= constr_version + elseif constr_op == ">" then ok = version > constr_version + elseif constr_op == "<" then ok = version < constr_version + elseif constr_op == ">=" then ok = version >= constr_version + elseif constr_op == "<=" then ok = version <= constr_version + elseif constr_op == "~>" then ok = partial_match(version, constr_version) + end + if not ok then break end + end + return ok +end + +--- Attempt to match a dependency to an installed rock. +-- @param dep table: A dependency parsed in table format. +-- @param blacklist table: Versions that can't be accepted. Table where keys +-- are program versions and values are 'true'. +-- @return string or nil: latest installed version of the rock matching the dependency +-- or nil if it could not be matched. +local function match_dep(dep, blacklist, deps_mode) + assert(type(dep) == "table") + + local versions + if cfg.rocks_provided[dep.name] then + -- provided rocks have higher priority than manifest's rocks + versions = { cfg.rocks_provided[dep.name] } + else + versions = manif_core.get_versions(dep.name, deps_mode) + end + + local latest_version + for _, vstring in ipairs(versions) do + if not blacklist or not blacklist[vstring] then + local version = deps.parse_version(vstring) + if deps.match_constraints(version, dep.constraints) then + if not latest_version or version > latest_version then + latest_version = version + end + end + end + end + + return latest_version and latest_version.string +end + +--- Attempt to match dependencies of a rockspec to installed rocks. +-- @param rockspec table: The rockspec loaded as a table. +-- @param blacklist table or nil: Program versions to not use as valid matches. +-- Table where keys are program names and values are tables where keys +-- are program versions and values are 'true'. +-- @return table, table, table: A table where keys are dependencies parsed +-- in table format and values are tables containing fields 'name' and +-- version' representing matches; a table of missing dependencies +-- parsed as tables; and a table of "no-upgrade" missing dependencies +-- (to be used in plugin modules so that a plugin does not force upgrade of +-- its parent application). +function deps.match_deps(rockspec, blacklist, deps_mode) + assert(type(rockspec) == "table") + assert(type(blacklist) == "table" or not blacklist) + local matched, missing, no_upgrade = {}, {}, {} + + for _, dep in ipairs(rockspec.dependencies) do + local found = match_dep(dep, blacklist and blacklist[dep.name] or nil, deps_mode) + if found then + if not cfg.rocks_provided[dep.name] then + matched[dep] = {name = dep.name, version = found} + end + else + if dep.constraints[1] and dep.constraints[1].no_upgrade then + no_upgrade[dep.name] = dep + else + missing[dep.name] = dep + end + end + end + return matched, missing, no_upgrade +end + +--- Return a set of values of a table. +-- @param tbl table: The input table. +-- @return table: The array of keys. +local function values_set(tbl) + local set = {} + for _, v in pairs(tbl) do + set[v] = true + end + return set +end + +local function rock_status(name, deps_mode) + local search = require("luarocks.search") + local installed = match_dep(search.make_query(name), nil, deps_mode) + local installation_type = cfg.rocks_provided[name] and "provided by VM" or "installed" + return installed and installed.." "..installation_type or "not installed" +end + +--- Check depenendencies of a package and report any missing ones. +-- @param name string: package name. +-- @param version string: package version. +-- @param dependencies table: array of dependencies. +-- @param deps_mode string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +function deps.report_missing_dependencies(name, version, dependencies, deps_mode) + local first_missing_dep = true + + for _, dep in ipairs(dependencies) do + if not match_dep(dep, nil, deps_mode) then + if first_missing_dep then + util.printout(("Missing dependencies for %s %s:"):format(name, version)) + first_missing_dep = false + end + + util.printout((" %s (%s)"):format(deps.show_dep(dep), rock_status(dep.name, deps_mode))) + end + end +end + +--- Check dependencies of a rock and attempt to install any missing ones. +-- Packages are installed using the LuaRocks "install" command. +-- Aborts the program if a dependency could not be fulfilled. +-- @param rockspec table: A rockspec in table format. +-- @return boolean or (nil, string, [string]): True if no errors occurred, or +-- nil and an error message if any test failed, followed by an optional +-- error code. +function deps.fulfill_dependencies(rockspec, deps_mode) + + local search = require("luarocks.search") + local install = require("luarocks.install") + + if rockspec.supported_platforms then + if not deps.platforms_set then + deps.platforms_set = values_set(cfg.platforms) + end + local supported = nil + for _, plat in pairs(rockspec.supported_platforms) do + local neg, plat = plat:match("^(!?)(.*)") + if neg == "!" then + if deps.platforms_set[plat] then + return nil, "This rockspec for "..rockspec.package.." does not support "..plat.." platforms." + end + else + if deps.platforms_set[plat] then + supported = true + else + if supported == nil then + supported = false + end + end + end + end + if supported == false then + local plats = table.concat(cfg.platforms, ", ") + return nil, "This rockspec for "..rockspec.package.." does not support "..plats.." platforms." + end + end + + deps.report_missing_dependencies(rockspec.name, rockspec.version, rockspec.dependencies, deps_mode) + + local first_missing_dep = true + + for _, dep in ipairs(rockspec.dependencies) do + if not match_dep(dep, nil, deps_mode) then + if first_missing_dep then + util.printout() + first_missing_dep = false + end + + util.printout(("%s %s depends on %s (%s)"):format( + rockspec.name, rockspec.version, deps.show_dep(dep), rock_status(dep.name, deps_mode))) + + if dep.constraints[1] and dep.constraints[1].no_upgrade then + util.printerr("This version of "..rockspec.name.." is designed for use with") + util.printerr(deps.show_dep(dep)..", but is configured to avoid upgrading it") + util.printerr("automatically. Please upgrade "..dep.name.." with") + util.printerr(" luarocks install "..dep.name) + util.printerr("or choose an older version of "..rockspec.name.." with") + util.printerr(" luarocks search "..rockspec.name) + return nil, "Failed matching dependencies" + end + + local url, search_err = search.find_suitable_rock(dep) + if not url then + return nil, "Could not satisfy dependency "..deps.show_dep(dep)..": "..search_err + end + util.printout("Installing "..url) + local ok, install_err, errcode = install.command({deps_mode = deps_mode}, url) + if not ok then + return nil, "Failed installing dependency: "..url.." - "..install_err, errcode + end + end + end + + return true +end + +--- If filename matches a pattern, return the capture. +-- For example, given "libfoo.so" and "lib?.so" is a pattern, +-- returns "foo" (which can then be used to build names +-- based on other patterns. +-- @param file string: a filename +-- @param pattern string: a pattern, where ? is to be matched by the filename. +-- @return string The pattern, if found, or nil. +local function deconstruct_pattern(file, pattern) + local depattern = "^"..(pattern:gsub("%.", "%%."):gsub("%*", ".*"):gsub("?", "(.*)")).."$" + return (file:match(depattern)) +end + +--- Construct all possible patterns for a name and add to the files array. +-- Run through the patterns array replacing all occurrences of "?" +-- with the given file name and store them in the files array. +-- @param file string A raw name (e.g. "foo") +-- @param array of string An array of patterns with "?" as the wildcard +-- (e.g. {"?.so", "lib?.so"}) +-- @param files The array of constructed names +local function add_all_patterns(file, patterns, files) + for _, pattern in ipairs(patterns) do + table.insert(files, (pattern:gsub("?", file))) + end +end + +--- Set up path-related variables for external dependencies. +-- For each key in the external_dependencies table in the +-- rockspec file, four variables are created: _DIR, _BINDIR, +-- _INCDIR and _LIBDIR. These are not overwritten +-- if already set (e.g. by the LuaRocks config file or through the +-- command-line). Values in the external_dependencies table +-- are tables that may contain a "header" or a "library" field, +-- with filenames to be tested for existence. +-- @param rockspec table: The rockspec table. +-- @param mode string: if "build" is given, checks all files; +-- if "install" is given, do not scan for headers. +-- @return boolean or (nil, string): True if no errors occurred, or +-- nil and an error message if any test failed. +function deps.check_external_deps(rockspec, mode) + assert(type(rockspec) == "table") + + local fs = require("luarocks.fs") + + local vars = rockspec.variables + local patterns = cfg.external_deps_patterns + local subdirs = cfg.external_deps_subdirs + if mode == "install" then + patterns = cfg.runtime_external_deps_patterns + subdirs = cfg.runtime_external_deps_subdirs + end + if rockspec.external_dependencies then + for name, ext_files in util.sortedpairs(rockspec.external_dependencies) do + local ok = true + local failed_files = {program = {}, header = {}, library = {}} + local failed_dirname + local failed_testfile + for _, extdir in ipairs(cfg.external_deps_dirs) do + ok = true + local prefix = vars[name.."_DIR"] + local dirs = { + BINDIR = { subdir = subdirs.bin, testfile = "program", pattern = patterns.bin }, + INCDIR = { subdir = subdirs.include, testfile = "header", pattern = patterns.include }, + LIBDIR = { subdir = subdirs.lib, testfile = "library", pattern = patterns.lib } + } + if mode == "install" then + dirs.INCDIR = nil + end + if not prefix then + prefix = extdir + end + if type(prefix) == "table" then + if prefix.bin then + dirs.BINDIR.subdir = prefix.bin + end + if prefix.include then + if dirs.INCDIR then + dirs.INCDIR.subdir = prefix.include + end + end + if prefix.lib then + dirs.LIBDIR.subdir = prefix.lib + end + prefix = prefix.prefix + end + for dirname, dirdata in util.sortedpairs(dirs) do + local paths + local path_var_value = vars[name.."_"..dirname] + if path_var_value then + paths = { path_var_value } + elseif type(dirdata.subdir) == "table" then + paths = {} + for i,v in ipairs(dirdata.subdir) do + paths[i] = dir.path(prefix, v) + end + else + paths = { dir.path(prefix, dirdata.subdir) } + end + dirdata.dir = paths[1] + local file = ext_files[dirdata.testfile] + if file then + local files = {} + if not file:match("%.") then + add_all_patterns(file, dirdata.pattern, files) + else + for _, pattern in ipairs(dirdata.pattern) do + local matched = deconstruct_pattern(file, pattern) + if matched then + add_all_patterns(matched, dirdata.pattern, files) + end + end + table.insert(files, file) + end + local found = false + for _, f in ipairs(files) do + + -- small convenience hack + if f:match("%.so$") or f:match("%.dylib$") or f:match("%.dll$") then + f = f:gsub("%.[^.]+$", "."..cfg.external_lib_extension) + end + + local pattern + if f:match("%*") then + pattern = f:gsub("%.", "%%."):gsub("%*", ".*") + f = "matching "..f + end + + for _, d in ipairs(paths) do + if pattern then + for entry in fs.dir(d) do + if entry:match(pattern) then + found = true + break + end + end + else + found = fs.is_file(dir.path(d, f)) + end + if found then + dirdata.dir = d + break + else + table.insert(failed_files[dirdata.testfile], f.." in "..d) + end + end + if found then + break + end + end + if not found then + ok = false + failed_dirname = dirname + failed_testfile = dirdata.testfile + break + end + end + end + if ok then + for dirname, dirdata in pairs(dirs) do + vars[name.."_"..dirname] = dirdata.dir + end + vars[name.."_DIR"] = prefix + break + end + end + if not ok then + local lines = {"Could not find "..failed_testfile.." file for "..name} + + local failed_paths = {} + for _, failed_file in ipairs(failed_files[failed_testfile]) do + if not failed_paths[failed_file] then + failed_paths[failed_file] = true + table.insert(lines, " No file "..failed_file) + end + end + + table.insert(lines, "You may have to install "..name.." in your system and/or pass "..name.."_DIR or "..name.."_"..failed_dirname.." to the luarocks command.") + table.insert(lines, "Example: luarocks install "..rockspec.name.." "..name.."_DIR=/usr/local") + + return nil, table.concat(lines, "\n"), "dependency" + end + end + end + return true +end + +--- Recursively add satisfied dependencies of a package to a table, +-- to build a transitive closure of all dependent packages. +-- Additionally ensures that `dependencies` table of the manifest is up-to-date. +-- @param results table: The results table being built, maps package names to versions. +-- @param manifest table: The manifest table containing dependencies. +-- @param name string: Package name. +-- @param version string: Package version. +function deps.scan_deps(results, manifest, name, version, deps_mode) + assert(type(results) == "table") + assert(type(manifest) == "table") + assert(type(name) == "string") + assert(type(version) == "string") + + local fetch = require("luarocks.fetch") + + if results[name] then + return + end + if not manifest.dependencies then manifest.dependencies = {} end + local dependencies = manifest.dependencies + if not dependencies[name] then dependencies[name] = {} end + local dependencies_name = dependencies[name] + local deplist = dependencies_name[version] + local rockspec, err + if not deplist then + rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version), false) + if not rockspec then + util.printerr("Couldn't load rockspec for "..name.." "..version..": "..err) + return + end + dependencies_name[version] = rockspec.dependencies + else + rockspec = { dependencies = deplist } + end + local matched = deps.match_deps(rockspec, nil, deps_mode) + results[name] = version + for _, match in pairs(matched) do + deps.scan_deps(results, manifest, match.name, match.version, deps_mode) + end +end + +local valid_deps_modes = { + one = true, + order = true, + all = true, + none = true, +} + +function deps.check_deps_mode_flag(flag) + return valid_deps_modes[flag] +end + +function deps.get_deps_mode(flags) + if flags["deps-mode"] then + return flags["deps-mode"] + else + return cfg.deps_mode + end +end + +function deps.deps_mode_to_flag(deps_mode) + return "--deps-mode="..deps_mode +end + +return deps diff --git a/Utils/luarocks/lua/luarocks/dir.lua b/Utils/luarocks/lua/luarocks/dir.lua new file mode 100644 index 000000000..f72ebd6c1 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/dir.lua @@ -0,0 +1,74 @@ + +--- Generic utilities for handling pathnames. +local dir = {} +package.loaded["luarocks.dir"] = dir + +dir.separator = "/" + +--- Strip the path off a path+filename. +-- @param pathname string: A path+name, such as "/a/b/c" +-- or "\a\b\c". +-- @return string: The filename without its path, such as "c". +function dir.base_name(pathname) + assert(type(pathname) == "string") + + local base = pathname:gsub("[/\\]*$", ""):match(".*[/\\]([^/\\]*)") + return base or pathname +end + +--- Strip the name off a path+filename. +-- @param pathname string: A path+name, such as "/a/b/c". +-- @return string: The filename without its path, such as "/a/b". +-- For entries such as "/a/b/", "/a" is returned. If there are +-- no directory separators in input, "" is returned. +function dir.dir_name(pathname) + assert(type(pathname) == "string") + return (pathname:gsub("/*$", ""):match("(.*)[/]+[^/]*")) or "" +end + +--- Describe a path in a cross-platform way. +-- Use this function to avoid platform-specific directory +-- separators in other modules. Removes trailing slashes from +-- each component given, to avoid repeated separators. +-- Separators inside strings are kept, to handle URLs containing +-- protocols. +-- @param ... strings representing directories +-- @return string: a string with a platform-specific representation +-- of the path. +function dir.path(...) + local t = {...} + while t[1] == "" do + table.remove(t, 1) + end + return (table.concat(t, "/"):gsub("([^:])/+", "%1/"):gsub("^/+", "/"):gsub("/*$", "")) +end + +--- Split protocol and path from an URL or local pathname. +-- URLs should be in the "protocol://path" format. +-- For local pathnames, "file" is returned as the protocol. +-- @param url string: an URL or a local pathname. +-- @return string, string: the protocol, and the pathname without the protocol. +function dir.split_url(url) + assert(type(url) == "string") + + local protocol, pathname = url:match("^([^:]*)://(.*)") + if not protocol then + protocol = "file" + pathname = url + end + return protocol, pathname +end + +--- Normalize a url or local path. +-- URLs should be in the "protocol://path" format. System independent +-- forward slashes are used, removing trailing and double slashes +-- @param url string: an URL or a local pathname. +-- @return string: Normalized result. +function dir.normalize(name) + local protocol, pathname = dir.split_url(name) + pathname = pathname:gsub("\\", "/"):gsub("(.)/*$", "%1"):gsub("//", "/") + if protocol ~= "file" then pathname = protocol .."://"..pathname end + return pathname +end + +return dir diff --git a/Utils/luarocks/lua/luarocks/doc.lua b/Utils/luarocks/lua/luarocks/doc.lua new file mode 100644 index 000000000..423ebe027 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/doc.lua @@ -0,0 +1,157 @@ + +--- Module implementing the LuaRocks "doc" command. +-- Shows documentation for an installed rock. +local doc = {} +package.loaded["luarocks.doc"] = doc + +local util = require("luarocks.util") +local search = require("luarocks.search") +local path = require("luarocks.path") +local dir = require("luarocks.dir") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local download = require("luarocks.download") + +util.add_run_function(doc) +doc.help_summary = "Show documentation for an installed rock." + +doc.help = [[ + is an existing package name. +Without any flags, tries to load the documentation +using a series of heuristics. +With these flags, return only the desired information: + +--home Open the home page of project. +--list List documentation files only. + +For more information about a rock, see the 'show' command. +]] + +local function show_homepage(homepage, name, version) + if not homepage then + return nil, "No 'homepage' field in rockspec for "..name.." "..version + end + util.printout("Opening "..homepage.." ...") + fs.browser(homepage) + return true +end + +local function try_to_open_homepage(name, version) + local temp_dir, err = fs.make_temp_dir("doc-"..name.."-"..(version or "")) + if not temp_dir then + return nil, "Failed creating temporary directory: "..err + end + util.schedule_function(fs.delete, temp_dir) + local ok, err = fs.change_dir(temp_dir) + if not ok then return nil, err end + local filename, err = download.download("rockspec", name, version) + if not filename then return nil, err end + local rockspec, err = fetch.load_local_rockspec(filename) + if not rockspec then return nil, err end + fs.pop_dir() + local descript = rockspec.description or {} + if not descript.homepage then return nil, "No homepage defined for "..name end + return show_homepage(descript.homepage, name, version) +end + +--- Driver function for "doc" command. +-- @param name or nil: an existing package name. +-- @param version string or nil: a version may also be passed. +-- @return boolean: True if succeeded, nil on errors. +function doc.command(flags, name, version) + if not name then + return nil, "Argument missing. "..util.see_help("doc") + end + + name = name:lower() + + local iname, iversion, repo = search.pick_installed_rock(name, version, flags["tree"]) + if not iname then + util.printout(name..(version and " "..version or "").." is not installed. Looking for it in the rocks servers...") + return try_to_open_homepage(name, version) + end + name, version = iname, iversion + + local rockspec, err = fetch.load_local_rockspec(path.rockspec_file(name, version, repo)) + if not rockspec then return nil,err end + local descript = rockspec.description or {} + + if flags["home"] then + return show_homepage(descript.homepage, name, version) + end + + local directory = path.install_dir(name,version,repo) + + local docdir + local directories = { "doc", "docs" } + for _, d in ipairs(directories) do + local dirname = dir.path(directory, d) + if fs.is_dir(dirname) then + docdir = dirname + break + end + end + if not docdir then + if descript.homepage and not flags["list"] then + util.printout("Local documentation directory not found -- opening "..descript.homepage.." ...") + fs.browser(descript.homepage) + return true + end + return nil, "Documentation directory not found for "..name.." "..version + end + + docdir = dir.normalize(docdir):gsub("/+", "/") + local files = fs.find(docdir) + local htmlpatt = "%.html?$" + local extensions = { htmlpatt, "%.md$", "%.txt$", "%.textile$", "" } + local basenames = { "index", "readme", "manual" } + + local porcelain = flags["porcelain"] + if #files > 0 then + util.title("Documentation files for "..name.." "..version, porcelain) + if porcelain then + for _, file in ipairs(files) do + util.printout(docdir.."/"..file) + end + else + util.printout(docdir.."/") + for _, file in ipairs(files) do + util.printout("\t"..file) + end + end + end + + if flags["list"] then + return true + end + + for _, extension in ipairs(extensions) do + for _, basename in ipairs(basenames) do + local filename = basename..extension + local found + for _, file in ipairs(files) do + if file:lower():match(filename) and ((not found) or #file < #found) then + found = file + end + end + if found then + local pathname = dir.path(docdir, found) + util.printout() + util.printout("Opening "..pathname.." ...") + util.printout() + local ok = fs.browser(pathname) + if not ok and not pathname:match(htmlpatt) then + local fd = io.open(pathname, "r") + util.printout(fd:read("*a")) + fd:close() + end + return true + end + end + end + + return true +end + + +return doc diff --git a/Utils/luarocks/lua/luarocks/download.lua b/Utils/luarocks/lua/luarocks/download.lua new file mode 100644 index 000000000..e434cdbb2 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/download.lua @@ -0,0 +1,109 @@ + +--- Module implementing the luarocks "download" command. +-- Download a rock from the repository. +local download = {} +package.loaded["luarocks.download"] = download + +local util = require("luarocks.util") +local path = require("luarocks.path") +local fetch = require("luarocks.fetch") +local search = require("luarocks.search") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local cfg = require("luarocks.cfg") + +util.add_run_function(download) +download.help_summary = "Download a specific rock file from a rocks server." +download.help_arguments = "[--all] [--arch= | --source | --rockspec] [ []]" + +download.help = [[ +--all Download all files if there are multiple matches. +--source Download .src.rock if available. +--rockspec Download .rockspec if available. +--arch= Download rock for a specific architecture. +]] + +local function get_file(filename) + local protocol, pathname = dir.split_url(filename) + if protocol == "file" then + local ok, err = fs.copy(pathname, fs.current_dir(), cfg.perm_read) + if ok then + return pathname + else + return nil, err + end + else + return fetch.fetch_url(filename) + end +end + +function download.download(arch, name, version, all) + local query = search.make_query(name, version) + if arch then query.arch = arch end + local search_err + + if all then + if name == "" then query.exact_name = false end + local results = search.search_repos(query) + local has_result = false + local all_ok = true + local any_err = "" + for name, result in pairs(results) do + for version, items in pairs(result) do + for _, item in ipairs(items) do + -- Ignore provided rocks. + if item.arch ~= "installed" then + has_result = true + local filename = path.make_url(item.repo, name, version, item.arch) + local ok, err = get_file(filename) + if not ok then + all_ok = false + any_err = any_err .. "\n" .. err + end + end + end + end + end + + if has_result then + return all_ok, any_err + end + else + local url + url, search_err = search.find_suitable_rock(query) + if url then + return get_file(url) + end + end + return nil, "Could not find a result named "..name..(version and " "..version or "").. + (search_err and ": "..search_err or ".") +end + +--- Driver function for the "download" command. +-- @param name string: a rock name. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function download.command(flags, name, version) + assert(type(version) == "string" or not version) + if type(name) ~= "string" and not flags["all"] then + return nil, "Argument missing. "..util.see_help("download") + end + if not name then name, version = "", "" end + + local arch + + if flags["source"] then + arch = "src" + elseif flags["rockspec"] then + arch = "rockspec" + elseif flags["arch"] then + arch = flags["arch"] + end + + local dl, err = download.download(arch, name:lower(), version, flags["all"]) + return dl and true, err +end + +return download diff --git a/Utils/luarocks/lua/luarocks/fetch.lua b/Utils/luarocks/lua/luarocks/fetch.lua new file mode 100644 index 000000000..76f366cd2 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch.lua @@ -0,0 +1,394 @@ + +--- Functions related to fetching and loading local and remote files. +local fetch = {} +package.loaded["luarocks.fetch"] = fetch + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local type_check = require("luarocks.type_check") +local path = require("luarocks.path") +local deps = require("luarocks.deps") +local persist = require("luarocks.persist") +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") + +function fetch.is_basic_protocol(protocol, remote) + return protocol == "http" or protocol == "https" or protocol == "ftp" or (not remote and protocol == "file") +end + +--- Fetch a local or remote file. +-- Make a remote or local URL/pathname local, fetching the file if necessary. +-- Other "fetch" and "load" functions use this function to obtain files. +-- If a local pathname is given, it is returned as a result. +-- @param url string: a local pathname or a remote URL. +-- @param filename string or nil: this function attempts to detect the +-- resulting local filename of the remote file as the basename of the URL; +-- if that is not correct (due to a redirection, for example), the local +-- filename can be given explicitly as this second argument. +-- @return string or (nil, string, [string]): the absolute local pathname for the +-- fetched file, or nil and a message in case of errors, followed by +-- an optional error code. +function fetch.fetch_url(url, filename, cache) + assert(type(url) == "string") + assert(type(filename) == "string" or not filename) + + local protocol, pathname = dir.split_url(url) + if protocol == "file" then + return fs.absolute_name(pathname) + elseif fetch.is_basic_protocol(protocol, true) then + local ok, name = fs.download(url, filename, cache) + if not ok then + return nil, "Failed downloading "..url..(filename and " - "..filename or ""), "network" + end + return name + else + return nil, "Unsupported protocol "..protocol + end +end + +--- For remote URLs, create a temporary directory and download URL inside it. +-- This temporary directory will be deleted on program termination. +-- For local URLs, just return the local pathname and its directory. +-- @param url string: URL to be downloaded +-- @param tmpname string: name pattern to use for avoiding conflicts +-- when creating temporary directory. +-- @param filename string or nil: local filename of URL to be downloaded, +-- in case it can't be inferred from the URL. +-- @return (string, string) or (nil, string, [string]): absolute local pathname of +-- the fetched file and temporary directory name; or nil and an error message +-- followed by an optional error code +function fetch.fetch_url_at_temp_dir(url, tmpname, filename) + assert(type(url) == "string") + assert(type(tmpname) == "string") + assert(type(filename) == "string" or not filename) + filename = filename or dir.base_name(url) + + local protocol, pathname = dir.split_url(url) + if protocol == "file" then + if fs.exists(pathname) then + return pathname, dir.dir_name(fs.absolute_name(pathname)) + else + return nil, "File not found: "..pathname + end + else + local temp_dir, err = fs.make_temp_dir(tmpname) + if not temp_dir then + return nil, "Failed creating temporary directory "..tmpname..": "..err + end + util.schedule_function(fs.delete, temp_dir) + local ok, err = fs.change_dir(temp_dir) + if not ok then return nil, err end + local file, err, errcode = fetch.fetch_url(url, filename) + fs.pop_dir() + if not file then + return nil, "Error fetching file: "..err, errcode + end + return file, temp_dir + end +end + +-- Determine base directory of a fetched URL by extracting its +-- archive and looking for a directory in the root. +-- @param file string: absolute local pathname of the fetched file +-- @param temp_dir string: temporary directory in which URL was fetched. +-- @param src_url string: URL to use when inferring base directory. +-- @param src_dir string or nil: expected base directory (inferred +-- from src_url if not given). +-- @return (string, string) or (string, nil) or (nil, string): +-- The inferred base directory and the one actually found (which may +-- be nil if not found), or nil followed by an error message. +-- The inferred dir is returned first to avoid confusion with errors, +-- because it is never nil. +function fetch.find_base_dir(file, temp_dir, src_url, src_dir) + local ok, err = fs.change_dir(temp_dir) + if not ok then return nil, err end + fs.unpack_archive(file) + local inferred_dir = src_dir or fetch.url_to_base_dir(src_url) + local found_dir = nil + if fs.exists(inferred_dir) then + found_dir = inferred_dir + else + util.printerr("Directory "..inferred_dir.." not found") + local files = fs.list_dir() + if files then + table.sort(files) + for i,filename in ipairs(files) do + if fs.is_dir(filename) then + util.printerr("Found "..filename) + found_dir = filename + break + end + end + end + end + fs.pop_dir() + return inferred_dir, found_dir +end + +--- Obtain a rock and unpack it. +-- If a directory is not given, a temporary directory will be created, +-- which will be deleted on program termination. +-- @param rock_file string: URL or filename of the rock. +-- @param dest string or nil: if given, directory will be used as +-- a permanent destination. +-- @return string or (nil, string, [string]): the directory containing the contents +-- of the unpacked rock. +function fetch.fetch_and_unpack_rock(rock_file, dest) + assert(type(rock_file) == "string") + assert(type(dest) == "string" or not dest) + + local name = dir.base_name(rock_file):match("(.*)%.[^.]*%.rock") + + local rock_file, err, errcode = fetch.fetch_url_at_temp_dir(rock_file,"luarocks-rock-"..name) + if not rock_file then + return nil, "Could not fetch rock file: " .. err, errcode + end + + rock_file = fs.absolute_name(rock_file) + local unpack_dir + if dest then + unpack_dir = dest + local ok, err = fs.make_dir(unpack_dir) + if not ok then + return nil, "Failed unpacking rock file: " .. err + end + else + unpack_dir = fs.make_temp_dir(name) + end + if not dest then + util.schedule_function(fs.delete, unpack_dir) + end + local ok, err = fs.change_dir(unpack_dir) + if not ok then return nil, err end + ok = fs.unzip(rock_file) + if not ok then + return nil, "Failed unpacking rock file: " .. rock_file + end + fs.pop_dir() + return unpack_dir +end + +function fetch.url_to_base_dir(url) + -- for extensions like foo.tar.gz, "gz" is stripped first + local known_exts = {} + for _, ext in ipairs{"zip", "git", "tgz", "tar", "gz", "bz2"} do + known_exts[ext] = "" + end + local base = dir.base_name(url) + return (base:gsub("%.([^.]*)$", known_exts):gsub("%.tar", "")) +end + +--- Back-end function that actually loads the local rockspec. +-- Performs some validation and postprocessing of the rockspec contents. +-- @param filename string: The local filename of the rockspec file. +-- @param quick boolean: if true, skips some steps when loading +-- rockspec. +-- @return table or (nil, string): A table representing the rockspec +-- or nil followed by an error message. +function fetch.load_local_rockspec(filename, quick) + assert(type(filename) == "string") + filename = fs.absolute_name(filename) + local rockspec, err = persist.load_into_table(filename) + if not rockspec then + return nil, "Could not load rockspec file "..filename.." ("..err..")" + end + if cfg.branch and (type(rockspec.source) == "table") then + rockspec.source.branch = cfg.branch + end + local globals = err + + if rockspec.rockspec_format then + if deps.compare_versions(rockspec.rockspec_format, type_check.rockspec_format) then + return nil, "Rockspec format "..rockspec.rockspec_format.." is not supported, please upgrade LuaRocks." + end + end + + if not quick then + local ok, err = type_check.type_check_rockspec(rockspec, globals) + if not ok then + return nil, filename..": "..err + end + end + + util.platform_overrides(rockspec.build) + util.platform_overrides(rockspec.dependencies) + util.platform_overrides(rockspec.external_dependencies) + util.platform_overrides(rockspec.source) + util.platform_overrides(rockspec.hooks) + + local basename = dir.base_name(filename) + if basename == "rockspec" then + rockspec.name = rockspec.package:lower() + else + rockspec.name = basename:match("(.*)-[^-]*-[0-9]*") + if not rockspec.name then + return nil, "Expected filename in format 'name-version-revision.rockspec'." + end + end + + local protocol, pathname = dir.split_url(rockspec.source.url) + if fetch.is_basic_protocol(protocol) then + rockspec.source.file = rockspec.source.file or dir.base_name(rockspec.source.url) + end + rockspec.source.protocol, rockspec.source.pathname = protocol, pathname + + -- Temporary compatibility + if rockspec.source.cvs_module then rockspec.source.module = rockspec.source.cvs_module end + if rockspec.source.cvs_tag then rockspec.source.tag = rockspec.source.cvs_tag end + + local name_version = rockspec.package:lower() .. "-" .. rockspec.version + if basename ~= "rockspec" and basename ~= name_version .. ".rockspec" then + return nil, "Inconsistency between rockspec filename ("..basename..") and its contents ("..name_version..".rockspec)." + end + + rockspec.local_filename = filename + local filebase = rockspec.source.file or rockspec.source.url + local base = fetch.url_to_base_dir(filebase) + rockspec.source.dir = rockspec.source.dir + or rockspec.source.module + or ((filebase:match("%.lua$") or filebase:match("%.c$")) and ".") + or base + if rockspec.dependencies then + for i = 1, #rockspec.dependencies do + local parsed, err = deps.parse_dep(rockspec.dependencies[i]) + if not parsed then + return nil, "Parse error processing dependency '"..rockspec.dependencies[i].."': "..tostring(err) + end + rockspec.dependencies[i] = parsed + end + else + rockspec.dependencies = {} + end + if not quick then + path.configure_paths(rockspec) + end + + return rockspec +end + +--- Load a local or remote rockspec into a table. +-- This is the entry point for the LuaRocks tools. +-- Only the LuaRocks runtime loader should use +-- load_local_rockspec directly. +-- @param filename string: Local or remote filename of a rockspec. +-- @param location string or nil: Where to download. If not given, +-- a temporary dir is created. +-- @return table or (nil, string, [string]): A table representing the rockspec +-- or nil followed by an error message and optional error code. +function fetch.load_rockspec(filename, location) + assert(type(filename) == "string") + + local name + local basename = dir.base_name(filename) + if basename == "rockspec" then + name = "rockspec" + else + name = basename:match("(.*)%.rockspec") + if not name then + return nil, "Filename '"..filename.."' does not look like a rockspec." + end + end + + local err, errcode + if location then + local ok, err = fs.change_dir(location) + if not ok then return nil, err end + filename, err = fetch.fetch_url(filename) + fs.pop_dir() + else + filename, err, errcode = fetch.fetch_url_at_temp_dir(filename,"luarocks-rockspec-"..name) + end + if not filename then + return nil, err, errcode + end + + return fetch.load_local_rockspec(filename) +end + +--- Download sources for building a rock using the basic URL downloader. +-- @param rockspec table: The rockspec table +-- @param extract boolean: Whether to extract the sources from +-- the fetched source tarball or not. +-- @param dest_dir string or nil: If set, will extract to the given directory; +-- if not given, will extract to a temporary directory. +-- @return (string, string) or (nil, string, [string]): The absolute pathname of +-- the fetched source tarball and the temporary directory created to +-- store it; or nil and an error message and optional error code. +function fetch.get_sources(rockspec, extract, dest_dir) + assert(type(rockspec) == "table") + assert(type(extract) == "boolean") + assert(type(dest_dir) == "string" or not dest_dir) + + local url = rockspec.source.url + local name = rockspec.name.."-"..rockspec.version + local filename = rockspec.source.file + local source_file, store_dir + local ok, err, errcode + if dest_dir then + ok, err = fs.change_dir(dest_dir) + if not ok then return nil, err, "dest_dir" end + source_file, err, errcode = fetch.fetch_url(url, filename) + fs.pop_dir() + store_dir = dest_dir + else + source_file, store_dir, errcode = fetch.fetch_url_at_temp_dir(url, "luarocks-source-"..name, filename) + end + if not source_file then + return nil, err or store_dir, errcode + end + if rockspec.source.md5 then + if not fs.check_md5(source_file, rockspec.source.md5) then + return nil, "MD5 check for "..filename.." has failed.", "md5" + end + end + if extract then + local ok, err = fs.change_dir(store_dir) + if not ok then return nil, err end + ok, err = fs.unpack_archive(rockspec.source.file) + if not ok then return nil, err end + if not fs.exists(rockspec.source.dir) then + return nil, "Directory "..rockspec.source.dir.." not found inside archive "..rockspec.source.file, "source.dir", source_file, store_dir + end + fs.pop_dir() + end + return source_file, store_dir +end + +--- Download sources for building a rock, calling the appropriate protocol method. +-- @param rockspec table: The rockspec table +-- @param extract boolean: When downloading compressed formats, whether to extract +-- the sources from the fetched archive or not. +-- @param dest_dir string or nil: If set, will extract to the given directory. +-- if not given, will extract to a temporary directory. +-- @return (string, string) or (nil, string): The absolute pathname of +-- the fetched source tarball and the temporary directory created to +-- store it; or nil and an error message. +function fetch.fetch_sources(rockspec, extract, dest_dir) + assert(type(rockspec) == "table") + assert(type(extract) == "boolean") + assert(type(dest_dir) == "string" or not dest_dir) + + local protocol = rockspec.source.protocol + local ok, proto + if fetch.is_basic_protocol(protocol) then + proto = fetch + else + ok, proto = pcall(require, "luarocks.fetch."..protocol:gsub("[+-]", "_")) + if not ok then + return nil, "Unknown protocol "..protocol + end + end + + if cfg.only_sources_from + and rockspec.source.pathname + and #rockspec.source.pathname > 0 then + if #cfg.only_sources_from == 0 then + return nil, "Can't download "..rockspec.source.url.." -- download from remote servers disabled" + elseif rockspec.source.pathname:find(cfg.only_sources_from, 1, true) ~= 1 then + return nil, "Can't download "..rockspec.source.url.." -- only downloading from "..cfg.only_sources_from + end + end + return proto.get_sources(rockspec, extract, dest_dir) +end + +return fetch diff --git a/Utils/luarocks/lua/luarocks/fetch/cvs.lua b/Utils/luarocks/lua/luarocks/fetch/cvs.lua new file mode 100644 index 000000000..ece711b6f --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch/cvs.lua @@ -0,0 +1,55 @@ + +--- Fetch back-end for retrieving sources from CVS. +local cvs = {} + +local unpack = unpack or table.unpack + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +--- Download sources for building a rock, using CVS. +-- @param rockspec table: The rockspec table +-- @param extract boolean: Unused in this module (required for API purposes.) +-- @param dest_dir string or nil: If set, will extract to the given directory. +-- @return (string, string) or (nil, string): The absolute pathname of +-- the fetched source tarball and the temporary directory created to +-- store it; or nil and an error message. +function cvs.get_sources(rockspec, extract, dest_dir) + assert(type(rockspec) == "table") + assert(type(dest_dir) == "string" or not dest_dir) + + local cvs_cmd = rockspec.variables.CVS + local ok, err_msg = fs.is_tool_available(cvs_cmd, "CVS") + if not ok then + return nil, err_msg + end + + local name_version = rockspec.name .. "-" .. rockspec.version + local module = rockspec.source.module or dir.base_name(rockspec.source.url) + local command = {cvs_cmd, "-d"..rockspec.source.pathname, "export", module} + if rockspec.source.tag then + table.insert(command, 4, "-r") + table.insert(command, 5, rockspec.source.tag) + end + local store_dir + if not dest_dir then + store_dir = fs.make_temp_dir(name_version) + if not store_dir then + return nil, "Failed creating temporary directory." + end + util.schedule_function(fs.delete, store_dir) + else + store_dir = dest_dir + end + local ok, err = fs.change_dir(store_dir) + if not ok then return nil, err end + if not fs.execute(unpack(command)) then + return nil, "Failed fetching files from CVS." + end + fs.pop_dir() + return module, store_dir +end + + +return cvs diff --git a/Utils/luarocks/lua/luarocks/fetch/git.lua b/Utils/luarocks/lua/luarocks/fetch/git.lua new file mode 100644 index 000000000..f61d89e9c --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch/git.lua @@ -0,0 +1,92 @@ + +--- Fetch back-end for retrieving sources from GIT. +local git = {} + +local unpack = unpack or table.unpack + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +--- Git >= 1.7.10 can clone a branch **or tag**, < 1.7.10 by branch only. We +-- need to know this in order to build the appropriate command; if we can't +-- clone by tag then we'll have to issue a subsequent command to check out the +-- given tag. +-- @return boolean: Whether Git can clone by tag. +local function git_can_clone_by_tag(git_cmd) + local version_string = io.popen(fs.Q(git_cmd)..' --version'):read() + local major, minor, tiny = version_string:match('(%d-)%.(%d+)%.?(%d*)') + major, minor, tiny = tonumber(major), tonumber(minor), tonumber(tiny) or 0 + local value = major > 1 or (major == 1 and (minor > 7 or (minor == 7 and tiny >= 10))) + git_can_clone_by_tag = function() return value end + return value +end + +--- Download sources for building a rock, using git. +-- @param rockspec table: The rockspec table +-- @param extract boolean: Unused in this module (required for API purposes.) +-- @param dest_dir string or nil: If set, will extract to the given directory. +-- @return (string, string) or (nil, string): The absolute pathname of +-- the fetched source tarball and the temporary directory created to +-- store it; or nil and an error message. +function git.get_sources(rockspec, extract, dest_dir, depth) + assert(type(rockspec) == "table") + assert(type(dest_dir) == "string" or not dest_dir) + + local git_cmd = rockspec.variables.GIT + local name_version = rockspec.name .. "-" .. rockspec.version + local module = dir.base_name(rockspec.source.url) + -- Strip off .git from base name if present + module = module:gsub("%.git$", "") + + local ok, err_msg = fs.is_tool_available(git_cmd, "Git") + if not ok then + return nil, err_msg + end + + local store_dir + if not dest_dir then + store_dir = fs.make_temp_dir(name_version) + if not store_dir then + return nil, "Failed creating temporary directory." + end + util.schedule_function(fs.delete, store_dir) + else + store_dir = dest_dir + end + store_dir = fs.absolute_name(store_dir) + local ok, err = fs.change_dir(store_dir) + if not ok then return nil, err end + + local command = {fs.Q(git_cmd), "clone", depth or "--depth=1", rockspec.source.url, module} + local tag_or_branch = rockspec.source.tag or rockspec.source.branch + -- If the tag or branch is explicitly set to "master" in the rockspec, then + -- we can avoid passing it to Git since it's the default. + if tag_or_branch == "master" then tag_or_branch = nil end + if tag_or_branch then + if git_can_clone_by_tag(git_cmd) then + -- The argument to `--branch` can actually be a branch or a tag as of + -- Git 1.7.10. + table.insert(command, 3, "--branch=" .. tag_or_branch) + end + end + if not fs.execute(unpack(command)) then + return nil, "Failed cloning git repository." + end + ok, err = fs.change_dir(module) + if not ok then return nil, err end + if tag_or_branch and not git_can_clone_by_tag() then + local checkout_command = {fs.Q(git_cmd), "checkout", tag_or_branch} + if not fs.execute(unpack(checkout_command)) then + return nil, 'Failed to check out the "' .. tag_or_branch ..'" tag or branch.' + end + end + + fs.delete(dir.path(store_dir, module, ".git")) + fs.delete(dir.path(store_dir, module, ".gitignore")) + fs.pop_dir() + fs.pop_dir() + return module, store_dir +end + +return git diff --git a/Utils/luarocks/lua/luarocks/fetch/git_file.lua b/Utils/luarocks/lua/luarocks/fetch/git_file.lua new file mode 100644 index 000000000..8d46bbca3 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch/git_file.lua @@ -0,0 +1,19 @@ + +--- Fetch back-end for retrieving sources from local Git repositories. +local git_file = {} + +local git = require("luarocks.fetch.git") + +--- Fetch sources for building a rock from a local Git repository. +-- @param rockspec table: The rockspec table +-- @param extract boolean: Unused in this module (required for API purposes.) +-- @param dest_dir string or nil: If set, will extract to the given directory. +-- @return (string, string) or (nil, string): The absolute pathname of +-- the fetched source tarball and the temporary directory created to +-- store it; or nil and an error message. +function git_file.get_sources(rockspec, extract, dest_dir) + rockspec.source.url = rockspec.source.url:gsub("^git.file://", "") + return git.get_sources(rockspec, extract, dest_dir) +end + +return git_file diff --git a/Utils/luarocks/lua/luarocks/fetch/git_http.lua b/Utils/luarocks/lua/luarocks/fetch/git_http.lua new file mode 100644 index 000000000..d85e2572d --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch/git_http.lua @@ -0,0 +1,26 @@ + +--- Fetch back-end for retrieving sources from Git repositories +-- that use http:// transport. For example, for fetching a repository +-- that requires the following command line: +-- `git clone http://example.com/foo.git` +-- you can use this in the rockspec: +-- source = { url = "git+http://example.com/foo.git" } +-- Prefer using the normal git:// fetch mode as it is more widely +-- available in older versions of LuaRocks. +local git_http = {} + +local git = require("luarocks.fetch.git") + +--- Fetch sources for building a rock from a local Git repository. +-- @param rockspec table: The rockspec table +-- @param extract boolean: Unused in this module (required for API purposes.) +-- @param dest_dir string or nil: If set, will extract to the given directory. +-- @return (string, string) or (nil, string): The absolute pathname of +-- the fetched source tarball and the temporary directory created to +-- store it; or nil and an error message. +function git_http.get_sources(rockspec, extract, dest_dir) + rockspec.source.url = rockspec.source.url:gsub("^git.", "") + return git.get_sources(rockspec, extract, dest_dir, "--") +end + +return git_http diff --git a/Utils/luarocks/lua/luarocks/fetch/git_https.lua b/Utils/luarocks/lua/luarocks/fetch/git_https.lua new file mode 100644 index 000000000..67f8ad6cc --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch/git_https.lua @@ -0,0 +1,7 @@ +--- Fetch back-end for retrieving sources from Git repositories +-- that use https:// transport. For example, for fetching a repository +-- that requires the following command line: +-- `git clone https://example.com/foo.git` +-- you can use this in the rockspec: +-- source = { url = "git+https://example.com/foo.git" } +return require "luarocks.fetch.git_http" diff --git a/Utils/luarocks/lua/luarocks/fetch/git_ssh.lua b/Utils/luarocks/lua/luarocks/fetch/git_ssh.lua new file mode 100644 index 000000000..0c2c0750f --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch/git_ssh.lua @@ -0,0 +1,32 @@ +--- Fetch back-end for retrieving sources from Git repositories +-- that use ssh:// transport. For example, for fetching a repository +-- that requires the following command line: +-- `git clone ssh://git@example.com/path/foo.git +-- you can use this in the rockspec: +-- source = { url = "git+ssh://git@example.com/path/foo.git" } +-- It also handles scp-style ssh urls: git@example.com:path/foo.git, +-- but you have to prepend the "git+ssh://" and why not use the "newer" +-- style anyway? +local git_ssh = {} + +local git = require("luarocks.fetch.git") + +--- Fetch sources for building a rock from a local Git repository. +-- @param rockspec table: The rockspec table +-- @param extract boolean: Unused in this module (required for API purposes.) +-- @param dest_dir string or nil: If set, will extract to the given directory. +-- @return (string, string) or (nil, string): The absolute pathname of +-- the fetched source tarball and the temporary directory created to +-- store it; or nil and an error message. +function git_ssh.get_sources(rockspec, extract, dest_dir) + rockspec.source.url = rockspec.source.url:gsub("^git.", "") + + -- Handle old-style scp-like git ssh urls + if rockspec.source.url:match("^ssh://[^/]+:[^%d]") then + rockspec.source.url = rockspec.source.url:gsub("^ssh://", "") + end + + return git.get_sources(rockspec, extract, dest_dir, "--") +end + +return git_ssh diff --git a/Utils/luarocks/lua/luarocks/fetch/hg.lua b/Utils/luarocks/lua/luarocks/fetch/hg.lua new file mode 100644 index 000000000..4cf8d0280 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch/hg.lua @@ -0,0 +1,65 @@ + +--- Fetch back-end for retrieving sources from HG. +local hg = {} + +local unpack = unpack or table.unpack + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +--- Download sources for building a rock, using hg. +-- @param rockspec table: The rockspec table +-- @param extract boolean: Unused in this module (required for API purposes.) +-- @param dest_dir string or nil: If set, will extract to the given directory. +-- @return (string, string) or (nil, string): The absolute pathname of +-- the fetched source tarball and the temporary directory created to +-- store it; or nil and an error message. +function hg.get_sources(rockspec, extract, dest_dir) + assert(type(rockspec) == "table") + assert(type(dest_dir) == "string" or not dest_dir) + + local hg_cmd = rockspec.variables.HG + local ok, err_msg = fs.is_tool_available(hg_cmd, "Mercurial") + if not ok then + return nil, err_msg + end + + local name_version = rockspec.name .. "-" .. rockspec.version + -- Strip off special hg:// protocol type + local url = rockspec.source.url:gsub("^hg://", "") + + local module = dir.base_name(url) + + local command = {hg_cmd, "clone", url, module} + local tag_or_branch = rockspec.source.tag or rockspec.source.branch + if tag_or_branch then + command = {hg_cmd, "clone", "--rev", tag_or_branch, url, module} + end + local store_dir + if not dest_dir then + store_dir = fs.make_temp_dir(name_version) + if not store_dir then + return nil, "Failed creating temporary directory." + end + util.schedule_function(fs.delete, store_dir) + else + store_dir = dest_dir + end + local ok, err = fs.change_dir(store_dir) + if not ok then return nil, err end + if not fs.execute(unpack(command)) then + return nil, "Failed cloning hg repository." + end + ok, err = fs.change_dir(module) + if not ok then return nil, err end + + fs.delete(dir.path(store_dir, module, ".hg")) + fs.delete(dir.path(store_dir, module, ".hgignore")) + fs.pop_dir() + fs.pop_dir() + return module, store_dir +end + + +return hg diff --git a/Utils/luarocks/lua/luarocks/fetch/hg_http.lua b/Utils/luarocks/lua/luarocks/fetch/hg_http.lua new file mode 100644 index 000000000..8f506daff --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch/hg_http.lua @@ -0,0 +1,24 @@ + +--- Fetch back-end for retrieving sources from hg repositories +-- that use http:// transport. For example, for fetching a repository +-- that requires the following command line: +-- `hg clone http://example.com/foo` +-- you can use this in the rockspec: +-- source = { url = "hg+http://example.com/foo" } +local hg_http = {} + +local hg = require("luarocks.fetch.hg") + +--- Download sources for building a rock, using hg over http. +-- @param rockspec table: The rockspec table +-- @param extract boolean: Unused in this module (required for API purposes.) +-- @param dest_dir string or nil: If set, will extract to the given directory. +-- @return (string, string) or (nil, string): The absolute pathname of +-- the fetched source tarball and the temporary directory created to +-- store it; or nil and an error message. +function hg_http.get_sources(rockspec, extract, dest_dir) + rockspec.source.url = rockspec.source.url:gsub("^hg.", "") + return hg.get_sources(rockspec, extract, dest_dir) +end + +return hg_http diff --git a/Utils/luarocks/lua/luarocks/fetch/hg_https.lua b/Utils/luarocks/lua/luarocks/fetch/hg_https.lua new file mode 100644 index 000000000..e67417fe8 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch/hg_https.lua @@ -0,0 +1,8 @@ + +--- Fetch back-end for retrieving sources from hg repositories +-- that use https:// transport. For example, for fetching a repository +-- that requires the following command line: +-- `hg clone https://example.com/foo` +-- you can use this in the rockspec: +-- source = { url = "hg+https://example.com/foo" } +return require "luarocks.fetch.hg_http" diff --git a/Utils/luarocks/lua/luarocks/fetch/hg_ssh.lua b/Utils/luarocks/lua/luarocks/fetch/hg_ssh.lua new file mode 100644 index 000000000..0c365fabe --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch/hg_ssh.lua @@ -0,0 +1,8 @@ + +--- Fetch back-end for retrieving sources from hg repositories +-- that use ssh:// transport. For example, for fetching a repository +-- that requires the following command line: +-- `hg clone ssh://example.com/foo` +-- you can use this in the rockspec: +-- source = { url = "hg+ssh://example.com/foo" } +return require "luarocks.fetch.hg_http" diff --git a/Utils/luarocks/lua/luarocks/fetch/sscm.lua b/Utils/luarocks/lua/luarocks/fetch/sscm.lua new file mode 100644 index 000000000..5add10dba --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch/sscm.lua @@ -0,0 +1,44 @@ + +--- Fetch back-end for retrieving sources from Surround SCM Server +local sscm = {} + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") + +--- Download sources via Surround SCM Server for building a rock. +-- @param rockspec table: The rockspec table +-- @param extract boolean: Unused in this module (required for API purposes.) +-- @param dest_dir string or nil: If set, will extract to the given directory. +-- @return (string, string) or (nil, string): The absolute pathname of +-- the fetched source tarball and the temporary directory created to +-- store it; or nil and an error message. +function sscm.get_sources(rockspec, extract, dest_dir) + assert(type(rockspec) == "table") + assert(type(dest_dir) == "string" or not dest_dir) + + local sscm_cmd = rockspec.variables.SSCM + local module = rockspec.source.module or dir.base_name(rockspec.source.url) + local branch, repository = string.match(rockspec.source.pathname, "^([^/]*)/(.*)") + if not branch or not repository then + return nil, "Error retrieving branch and repository from rockspec." + end + -- Search for working directory. + local working_dir + local tmp = io.popen(string.format(sscm_cmd..[[ property "/" -d -b%s -p%s]], branch, repository)) + for line in tmp:lines() do + --%c because a chr(13) comes in the end. + working_dir = string.match(line, "Working directory:[%s]*(.*)%c$") + if working_dir then break end + end + tmp:close() + if not working_dir then + return nil, "Error retrieving working directory from SSCM." + end + if not fs.execute(sscm_cmd, "get", "*", "-e" , "-r", "-b"..branch, "-p"..repository, "-tmodify", "-wreplace") then + return nil, "Failed fetching files from SSCM." + end + -- FIXME: This function does not honor the dest_dir parameter. + return module, working_dir +end + +return sscm diff --git a/Utils/luarocks/lua/luarocks/fetch/svn.lua b/Utils/luarocks/lua/luarocks/fetch/svn.lua new file mode 100644 index 000000000..29bce1b5f --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fetch/svn.lua @@ -0,0 +1,64 @@ + +--- Fetch back-end for retrieving sources from Subversion. +local svn = {} + +local unpack = unpack or table.unpack + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +--- Download sources for building a rock, using Subversion. +-- @param rockspec table: The rockspec table +-- @param extract boolean: Unused in this module (required for API purposes.) +-- @param dest_dir string or nil: If set, will extract to the given directory. +-- @return (string, string) or (nil, string): The absolute pathname of +-- the fetched source tarball and the temporary directory created to +-- store it; or nil and an error message. +function svn.get_sources(rockspec, extract, dest_dir) + assert(type(rockspec) == "table") + assert(type(dest_dir) == "string" or not dest_dir) + + local svn_cmd = rockspec.variables.SVN + local ok, err_msg = fs.is_tool_available(svn_cmd, "--version", "Subversion") + if not ok then + return nil, err_msg + end + + local name_version = rockspec.name .. "-" .. rockspec.version + local module = rockspec.source.module or dir.base_name(rockspec.source.url) + local url = rockspec.source.url:gsub("^svn://", "") + local command = {svn_cmd, "checkout", url, module} + if rockspec.source.tag then + table.insert(command, 5, "-r") + table.insert(command, 6, rockspec.source.tag) + end + local store_dir + if not dest_dir then + store_dir = fs.make_temp_dir(name_version) + if not store_dir then + return nil, "Failed creating temporary directory." + end + util.schedule_function(fs.delete, store_dir) + else + store_dir = dest_dir + end + local ok, err = fs.change_dir(store_dir) + if not ok then return nil, err end + if not fs.execute(unpack(command)) then + return nil, "Failed fetching files from Subversion." + end + ok, err = fs.change_dir(module) + if not ok then return nil, err end + for _, d in ipairs(fs.find(".")) do + if dir.base_name(d) == ".svn" then + fs.delete(dir.path(store_dir, module, d)) + end + end + fs.pop_dir() + fs.pop_dir() + return module, store_dir +end + + +return svn diff --git a/Utils/luarocks/lua/luarocks/fs.lua b/Utils/luarocks/lua/luarocks/fs.lua new file mode 100644 index 000000000..54cc7d73d --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fs.lua @@ -0,0 +1,76 @@ + +--- Proxy module for filesystem and platform abstractions. +-- All code using "fs" code should require "luarocks.fs", +-- and not the various platform-specific implementations. +-- However, see the documentation of the implementation +-- for the API reference. + +local pairs = pairs + +local fs = {} +package.loaded["luarocks.fs"] = fs + +local cfg = require("luarocks.cfg") + +local pack = table.pack or function(...) return { n = select("#", ...), ... } end +local unpack = table.unpack or unpack + +local old_popen, old_exec +fs.verbose = function() -- patch io.popen and os.execute to display commands in verbose mode + if old_popen or old_exec then return end + old_popen = io.popen + io.popen = function(one, two) + if two == nil then + print("\nio.popen: ", one) + else + print("\nio.popen: ", one, "Mode:", two) + end + return old_popen(one, two) + end + + old_exec = os.execute + os.execute = function(cmd) + -- redact api keys if present + print("\nos.execute: ", (cmd:gsub("(/api/[^/]+/)([^/]+)/", function(cap, key) return cap.."/" end)) ) + local code = pack(old_exec(cmd)) + print("Results: "..tostring(code.n)) + for i = 1,code.n do + print(" "..tostring(i).." ("..type(code[i]).."): "..tostring(code[i])) + end + return unpack(code, 1, code.n) + end +end +if cfg.verbose then fs.verbose() end + +local function load_fns(fs_table) + for name, fn in pairs(fs_table) do + if not fs[name] then + fs[name] = fn + end + end +end + +-- Load platform-specific functions +local loaded_platform = nil +for _, platform in ipairs(cfg.platforms) do + local ok, fs_plat = pcall(require, "luarocks.fs."..platform) + if ok and fs_plat then + loaded_platform = platform + load_fns(fs_plat) + break + end +end + +-- Load platform-independent pure-Lua functionality +local fs_lua = require("luarocks.fs.lua") +load_fns(fs_lua) + +-- Load platform-specific fallbacks for missing Lua modules +local ok, fs_plat_tools = pcall(require, "luarocks.fs."..loaded_platform..".tools") +if ok and fs_plat_tools then + load_fns(fs_plat_tools) + load_fns(require("luarocks.fs.tools")) +end + + +return fs diff --git a/Utils/luarocks/lua/luarocks/fs/lua.lua b/Utils/luarocks/lua/luarocks/fs/lua.lua new file mode 100644 index 000000000..41711eab6 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fs/lua.lua @@ -0,0 +1,873 @@ + +--- Native Lua implementation of filesystem and platform abstractions, +-- using LuaFileSystem, LZLib, MD5 and LuaCurl. +-- module("luarocks.fs.lua") +local fs_lua = {} + +local fs = require("luarocks.fs") + +local cfg = require("luarocks.cfg") +local dir = require("luarocks.dir") +local util = require("luarocks.util") +local path = require("luarocks.path") + +local socket_ok, zip_ok, unzip_ok, lfs_ok, md5_ok, posix_ok, _ +local http, ftp, lrzip, luazip, lfs, md5, posix + +if cfg.fs_use_modules then + socket_ok, http = pcall(require, "socket.http") + _, ftp = pcall(require, "socket.ftp") + zip_ok, lrzip = pcall(require, "luarocks.tools.zip") + unzip_ok, luazip = pcall(require, "zip"); _G.zip = nil + lfs_ok, lfs = pcall(require, "lfs") + md5_ok, md5 = pcall(require, "md5") + posix_ok, posix = pcall(require, "posix") +end + +local patch = require("luarocks.tools.patch") + +local dir_stack = {} + +local dir_separator = "/" + +--- Test is file/dir is writable. +-- Warning: testing if a file/dir is writable does not guarantee +-- that it will remain writable and therefore it is no replacement +-- for checking the result of subsequent operations. +-- @param file string: filename to test +-- @return boolean: true if file exists, false otherwise. +function fs_lua.is_writable(file) + assert(file) + file = dir.normalize(file) + local result + if fs.is_dir(file) then + local file2 = dir.path(file, '.tmpluarockstestwritable') + local fh = io.open(file2, 'wb') + result = fh ~= nil + if fh then fh:close() end + os.remove(file2) + else + local fh = io.open(file, 'r+b') + result = fh ~= nil + if fh then fh:close() end + end + return result +end + +local function quote_args(command, ...) + local out = { command } + for _, arg in ipairs({...}) do + assert(type(arg) == "string") + out[#out+1] = fs.Q(arg) + end + return table.concat(out, " ") +end + +--- Run the given command, quoting its arguments. +-- The command is executed in the current directory in the dir stack. +-- @param command string: The command to be executed. No quoting/escaping +-- is applied. +-- @param ... Strings containing additional arguments, which are quoted. +-- @return boolean: true if command succeeds (status code 0), false +-- otherwise. +function fs_lua.execute(command, ...) + assert(type(command) == "string") + return fs.execute_string(quote_args(command, ...)) +end + +--- Run the given command, quoting its arguments, silencing its output. +-- The command is executed in the current directory in the dir stack. +-- Silencing is omitted if 'verbose' mode is enabled. +-- @param command string: The command to be executed. No quoting/escaping +-- is applied. +-- @param ... Strings containing additional arguments, which will be quoted. +-- @return boolean: true if command succeeds (status code 0), false +-- otherwise. +function fs_lua.execute_quiet(command, ...) + assert(type(command) == "string") + if cfg.verbose then -- omit silencing output + return fs.execute_string(quote_args(command, ...)) + else + return fs.execute_string(fs.quiet(quote_args(command, ...))) + end +end + +--- Checks if the given tool is available. +-- The tool is executed using a flag, usually just to ask its version. +-- @param tool_cmd string: The command to be used to check the tool's presence (e.g. hg in case of Mercurial) +-- @param tool_name string: The actual name of the tool (e.g. Mercurial) +-- @param arg string: The flag to pass to the tool. '--version' by default. +function fs_lua.is_tool_available(tool_cmd, tool_name, arg) + assert(type(tool_cmd) == "string") + assert(type(tool_name) == "string") + + arg = arg or "--version" + assert(type(arg) == "string") + + if not fs.execute_quiet(fs.Q(tool_cmd), arg) then + local msg = "'%s' program not found. Make sure %s is installed and is available in your PATH " .. + "(or you may want to edit the 'variables.%s' value in file '%s')" + return nil, msg:format(tool_cmd, tool_name, tool_name:upper(), cfg.which_config().nearest) + else + return true + end +end + +--- Check the MD5 checksum for a file. +-- @param file string: The file to be checked. +-- @param md5sum string: The string with the expected MD5 checksum. +-- @return boolean: true if the MD5 checksum for 'file' equals 'md5sum', false + msg if not +-- or if it could not perform the check for any reason. +function fs_lua.check_md5(file, md5sum) + file = dir.normalize(file) + local computed, msg = fs.get_md5(file) + if not computed then + return false, msg + end + if computed:match("^"..md5sum) then + return true + else + return false, "Mismatch MD5 hash for file "..file + end +end + +--- List the contents of a directory. +-- @param at string or nil: directory to list (will be the current +-- directory if none is given). +-- @return table: an array of strings with the filenames representing +-- the contents of a directory. +function fs_lua.list_dir(at) + local result = {} + for file in fs.dir(at) do + result[#result+1] = file + end + return result +end + +--- Iterate over the contents of a directory. +-- @param at string or nil: directory to list (will be the current +-- directory if none is given). +-- @return function: an iterator function suitable for use with +-- the for statement. +function fs_lua.dir(at) + if not at then + at = fs.current_dir() + end + at = dir.normalize(at) + if not fs.is_dir(at) then + return function() end + end + return coroutine.wrap(function() fs.dir_iterator(at) end) +end + +--------------------------------------------------------------------- +-- LuaFileSystem functions +--------------------------------------------------------------------- + +if lfs_ok then + +--- Run the given command. +-- The command is executed in the current directory in the dir stack. +-- @param cmd string: No quoting/escaping is applied to the command. +-- @return boolean: true if command succeeds (status code 0), false +-- otherwise. +function fs_lua.execute_string(cmd) + local code = os.execute(cmd) + return (code == 0 or code == true) +end + +--- Obtain current directory. +-- Uses the module's internal dir stack. +-- @return string: the absolute pathname of the current directory. +function fs_lua.current_dir() + return lfs.currentdir() +end + +--- Change the current directory. +-- Uses the module's internal dir stack. This does not have exact +-- semantics of chdir, as it does not handle errors the same way, +-- but works well for our purposes for now. +-- @param d string: The directory to switch to. +function fs_lua.change_dir(d) + table.insert(dir_stack, lfs.currentdir()) + d = dir.normalize(d) + return lfs.chdir(d) +end + +--- Change directory to root. +-- Allows leaving a directory (e.g. for deleting it) in +-- a crossplatform way. +function fs_lua.change_dir_to_root() + local current = lfs.currentdir() + if not current or current == "" then + return false + end + table.insert(dir_stack, current) + lfs.chdir("/") -- works on Windows too + return true +end + +--- Change working directory to the previous in the dir stack. +-- @return true if a pop ocurred, false if the stack was empty. +function fs_lua.pop_dir() + local d = table.remove(dir_stack) + if d then + lfs.chdir(d) + return true + else + return false + end +end + +--- Create a directory if it does not already exist. +-- If any of the higher levels in the path name do not exist +-- too, they are created as well. +-- @param directory string: pathname of directory to create. +-- @return boolean or (boolean, string): true on success or (false, error message) on failure. +function fs_lua.make_dir(directory) + assert(type(directory) == "string") + directory = dir.normalize(directory) + local path = nil + if directory:sub(2, 2) == ":" then + path = directory:sub(1, 2) + directory = directory:sub(4) + else + if directory:match("^/") then + path = "" + end + end + for d in directory:gmatch("([^"..dir.separator.."]+)"..dir.separator.."*") do + path = path and path .. dir.separator .. d or d + local mode = lfs.attributes(path, "mode") + if not mode then + local ok, err = lfs.mkdir(path) + if not ok then + return false, err + end + ok, err = fs.chmod(path, cfg.perm_exec) + if not ok then + return false, err + end + elseif mode ~= "directory" then + return false, path.." is not a directory" + end + end + return true +end + +--- Remove a directory if it is empty. +-- Does not return errors (for example, if directory is not empty or +-- if already does not exist) +-- @param d string: pathname of directory to remove. +function fs_lua.remove_dir_if_empty(d) + assert(d) + d = dir.normalize(d) + lfs.rmdir(d) +end + +--- Remove a directory if it is empty. +-- Does not return errors (for example, if directory is not empty or +-- if already does not exist) +-- @param d string: pathname of directory to remove. +function fs_lua.remove_dir_tree_if_empty(d) + assert(d) + d = dir.normalize(d) + for i=1,10 do + lfs.rmdir(d) + d = dir.dir_name(d) + end +end + +--- Copy a file. +-- @param src string: Pathname of source +-- @param dest string: Pathname of destination +-- @param perms string or nil: Permissions for destination file, +-- or nil to use the source filename permissions +-- @return boolean or (boolean, string): true on success, false on failure, +-- plus an error message. +function fs_lua.copy(src, dest, perms) + assert(src and dest) + src = dir.normalize(src) + dest = dir.normalize(dest) + local destmode = lfs.attributes(dest, "mode") + if destmode == "directory" then + dest = dir.path(dest, dir.base_name(src)) + end + if not perms then perms = fs.get_permissions(src) end + local src_h, err = io.open(src, "rb") + if not src_h then return nil, err end + local dest_h, err = io.open(dest, "w+b") + if not dest_h then src_h:close() return nil, err end + while true do + local block = src_h:read(8192) + if not block then break end + dest_h:write(block) + end + src_h:close() + dest_h:close() + fs.chmod(dest, perms) + return true +end + +--- Implementation function for recursive copy of directory contents. +-- Assumes paths are normalized. +-- @param src string: Pathname of source +-- @param dest string: Pathname of destination +-- @param perms string or nil: Optional permissions. +-- If not given, permissions of the source are copied over to the destination. +-- @return boolean or (boolean, string): true on success, false on failure +local function recursive_copy(src, dest, perms) + local srcmode = lfs.attributes(src, "mode") + + if srcmode == "file" then + local ok = fs.copy(src, dest, perms) + if not ok then return false end + elseif srcmode == "directory" then + local subdir = dir.path(dest, dir.base_name(src)) + local ok, err = fs.make_dir(subdir) + if not ok then return nil, err end + for file in lfs.dir(src) do + if file ~= "." and file ~= ".." then + local ok = recursive_copy(dir.path(src, file), subdir, perms) + if not ok then return false end + end + end + end + return true +end + +--- Recursively copy the contents of a directory. +-- @param src string: Pathname of source +-- @param dest string: Pathname of destination +-- @param perms string or nil: Optional permissions. +-- @return boolean or (boolean, string): true on success, false on failure, +-- plus an error message. +function fs_lua.copy_contents(src, dest, perms) + assert(src and dest) + src = dir.normalize(src) + dest = dir.normalize(dest) + assert(lfs.attributes(src, "mode") == "directory") + + for file in lfs.dir(src) do + if file ~= "." and file ~= ".." then + local ok = recursive_copy(dir.path(src, file), dest, perms) + if not ok then + return false, "Failed copying "..src.." to "..dest + end + end + end + return true +end + +--- Implementation function for recursive removal of directories. +-- Assumes paths are normalized. +-- @param name string: Pathname of file +-- @return boolean or (boolean, string): true on success, +-- or nil and an error message on failure. +local function recursive_delete(name) + local ok = os.remove(name) + if ok then return true end + local pok, ok, err = pcall(function() + for file in lfs.dir(name) do + if file ~= "." and file ~= ".." then + local ok, err = recursive_delete(dir.path(name, file)) + if not ok then return nil, err end + end + end + local ok, err = lfs.rmdir(name) + return ok, (not ok) and err + end) + if pok then + return ok, err + else + return pok, ok + end +end + +--- Delete a file or a directory and all its contents. +-- @param name string: Pathname of source +-- @return nil +function fs_lua.delete(name) + name = dir.normalize(name) + recursive_delete(name) +end + +--- Internal implementation function for fs.dir. +-- Yields a filename on each iteration. +-- @param at string: directory to list +-- @return nil +function fs_lua.dir_iterator(at) + for file in lfs.dir(at) do + if file ~= "." and file ~= ".." then + coroutine.yield(file) + end + end +end + +--- Implementation function for recursive find. +-- Assumes paths are normalized. +-- @param cwd string: Current working directory in recursion. +-- @param prefix string: Auxiliary prefix string to form pathname. +-- @param result table: Array of strings where results are collected. +local function recursive_find(cwd, prefix, result) + for file in lfs.dir(cwd) do + if file ~= "." and file ~= ".." then + local item = prefix .. file + table.insert(result, item) + local pathname = dir.path(cwd, file) + if lfs.attributes(pathname, "mode") == "directory" then + recursive_find(pathname, item..dir_separator, result) + end + end + end +end + +--- Recursively scan the contents of a directory. +-- @param at string or nil: directory to scan (will be the current +-- directory if none is given). +-- @return table: an array of strings with the filenames representing +-- the contents of a directory. +function fs_lua.find(at) + assert(type(at) == "string" or not at) + if not at then + at = fs.current_dir() + end + at = dir.normalize(at) + if not fs.is_dir(at) then + return {} + end + local result = {} + recursive_find(at, "", result) + return result +end + +--- Test for existance of a file. +-- @param file string: filename to test +-- @return boolean: true if file exists, false otherwise. +function fs_lua.exists(file) + assert(file) + file = dir.normalize(file) + return type(lfs.attributes(file)) == "table" +end + +--- Test is pathname is a directory. +-- @param file string: pathname to test +-- @return boolean: true if it is a directory, false otherwise. +function fs_lua.is_dir(file) + assert(file) + file = dir.normalize(file) + return lfs.attributes(file, "mode") == "directory" +end + +--- Test is pathname is a regular file. +-- @param file string: pathname to test +-- @return boolean: true if it is a file, false otherwise. +function fs_lua.is_file(file) + assert(file) + file = dir.normalize(file) + return lfs.attributes(file, "mode") == "file" +end + +function fs_lua.set_time(file, time) + file = dir.normalize(file) + return lfs.touch(file, time) +end + +end + +--------------------------------------------------------------------- +-- LuaZip functions +--------------------------------------------------------------------- + +if zip_ok then + +function fs_lua.zip(zipfile, ...) + return lrzip.zip(zipfile, ...) +end + +end + +if unzip_ok then +--- Uncompress files from a .zip archive. +-- @param zipfile string: pathname of .zip archive to be extracted. +-- @return boolean: true on success, false on failure. +function fs_lua.unzip(zipfile) + local zipfile, err = luazip.open(zipfile) + if not zipfile then return nil, err end + local files = zipfile:files() + local file = files() + repeat + if file.filename:sub(#file.filename) == "/" then + local ok, err = fs.make_dir(dir.path(fs.current_dir(), file.filename)) + if not ok then return nil, err end + else + local base = dir.dir_name(file.filename) + if base ~= "" then + base = dir.path(fs.current_dir(), base) + if not fs.is_dir(base) then + local ok, err = fs.make_dir(base) + if not ok then return nil, err end + end + end + local rf, err = zipfile:open(file.filename) + if not rf then zipfile:close(); return nil, err end + local contents = rf:read("*a") + rf:close() + local wf, err = io.open(dir.path(fs.current_dir(), file.filename), "wb") + if not wf then zipfile:close(); return nil, err end + wf:write(contents) + wf:close() + end + file = files() + until not file + zipfile:close() + return true +end + +end + +--------------------------------------------------------------------- +-- LuaSocket functions +--------------------------------------------------------------------- + +if socket_ok then + +local ltn12 = require("ltn12") +local luasec_ok, https = pcall(require, "ssl.https") + +local redirect_protocols = { + http = http, + https = luasec_ok and https, +} + +local function request(url, method, http, loop_control) + local result = {} + + local proxy = cfg.http_proxy + if type(proxy) ~= "string" then proxy = nil end + -- LuaSocket's http.request crashes when given URLs missing the scheme part. + if proxy and not proxy:find("://") then + proxy = "http://" .. proxy + end + + if cfg.show_downloads then + io.write(method.." "..url.." ...\n") + end + local dots = 0 + if cfg.connection_timeout and cfg.connection_timeout > 0 then + http.TIMEOUT = cfg.connection_timeout + end + local res, status, headers, err = http.request { + url = url, + proxy = proxy, + method = method, + redirect = false, + sink = ltn12.sink.table(result), + step = cfg.show_downloads and function(...) + io.write(".") + io.flush() + dots = dots + 1 + if dots == 70 then + io.write("\n") + dots = 0 + end + return ltn12.pump.step(...) + end, + headers = { + ["user-agent"] = cfg.user_agent.." via LuaSocket" + }, + } + if cfg.show_downloads then + io.write("\n") + end + if not res then + return nil, status + elseif status == 301 or status == 302 then + local location = headers.location + if location then + local protocol, rest = dir.split_url(location) + if redirect_protocols[protocol] then + if not loop_control then + loop_control = {} + elseif loop_control[location] then + return nil, "Redirection loop -- broken URL?" + end + loop_control[url] = true + return request(location, method, redirect_protocols[protocol], loop_control) + else + return nil, "URL redirected to unsupported protocol - install luasec to get HTTPS support.", "https" + end + end + return nil, err + elseif status ~= 200 then + return nil, err + else + return result, status, headers, err + end +end + +local function http_request(url, http, cached) + if cached then + local tsfd = io.open(cached..".timestamp", "r") + if tsfd then + local timestamp = tsfd:read("*a") + tsfd:close() + local result, status, headers, err = request(url, "HEAD", http) + if status == 200 and headers["last-modified"] == timestamp then + return true + end + if not result then + return nil, status, headers + end + end + end + local result, status, headers, err = request(url, "GET", http) + if result then + if cached and headers["last-modified"] then + local tsfd = io.open(cached..".timestamp", "w") + if tsfd then + tsfd:write(headers["last-modified"]) + tsfd:close() + end + end + return table.concat(result) + else + return nil, status, headers + end +end + +local downloader_warning = false + +--- Download a remote file. +-- @param url string: URL to be fetched. +-- @param filename string or nil: this function attempts to detect the +-- resulting local filename of the remote file as the basename of the URL; +-- if that is not correct (due to a redirection, for example), the local +-- filename can be given explicitly as this second argument. +-- @return (boolean, string): true and the filename on success, +-- false and the error message on failure. +function fs_lua.download(url, filename, cache) + assert(type(url) == "string") + assert(type(filename) == "string" or not filename) + + filename = fs.absolute_name(filename or dir.base_name(url)) + + -- delegate to the configured downloader so we don't have to deal with whitelists + if cfg.no_proxy then + return fs.use_downloader(url, filename, cache) + end + + local content, err, https_err + if util.starts_with(url, "http:") then + content, err, https_err = http_request(url, http, cache and filename) + elseif util.starts_with(url, "ftp:") then + content, err = ftp.get(url) + elseif util.starts_with(url, "https:") then + -- skip LuaSec when proxy is enabled since it is not supported + if luasec_ok and not cfg.https_proxy then + content, err = http_request(url, https, cache and filename) + else + https_err = true + end + else + err = "Unsupported protocol" + end + if https_err then + if not downloader_warning then + util.printerr("Warning: falling back to "..cfg.downloader.." - install luasec to get native HTTPS support") + downloader_warning = true + end + return fs.use_downloader(url, filename, cache) + end + if cache and content == true then + return true, filename + end + if not content then + return false, tostring(err) + end + local file = io.open(filename, "wb") + if not file then return false end + file:write(content) + file:close() + return true, filename +end + +else --...if socket_ok == false then + +function fs_lua.download(url, filename, cache) + return fs.use_downloader(url, filename, cache) +end + +end +--------------------------------------------------------------------- +-- MD5 functions +--------------------------------------------------------------------- + +if md5_ok then + +-- Support the interface of lmd5 by lhf in addition to md5 by Roberto +-- and the keplerproject. +if not md5.sumhexa and md5.digest then + md5.sumhexa = function(msg) + return md5.digest(msg) + end +end + +--- Get the MD5 checksum for a file. +-- @param file string: The file to be computed. +-- @return string: The MD5 checksum or nil + error +function fs_lua.get_md5(file) + file = fs.absolute_name(file) + local file_handler = io.open(file, "rb") + if not file_handler then return nil, "Failed to open file for reading: "..file end + local computed = md5.sumhexa(file_handler:read("*a")) + file_handler:close() + if computed then return computed end + return nil, "Failed to compute MD5 hash for file "..file +end + +end + +--------------------------------------------------------------------- +-- POSIX functions +--------------------------------------------------------------------- + +if posix_ok then + +local octal_to_rwx = { + ["0"] = "---", + ["1"] = "--x", + ["2"] = "-w-", + ["3"] = "-wx", + ["4"] = "r--", + ["5"] = "r-x", + ["6"] = "rw-", + ["7"] = "rwx", +} + +function fs_lua.chmod(file, mode) + -- LuaPosix (as of 5.1.15) does not support octal notation... + if mode:sub(1,1) == "0" then + local new_mode = {} + for c in mode:sub(-3):gmatch(".") do + table.insert(new_mode, octal_to_rwx[c]) + end + mode = table.concat(new_mode) + end + local err = posix.chmod(file, mode) + return err == 0 +end + +function fs_lua.get_permissions(file) + return posix.stat(file, "mode") +end + +--- Create a temporary directory. +-- @param name string: name pattern to use for avoiding conflicts +-- when creating temporary directory. +-- @return string or (nil, string): name of temporary directory or (nil, error message) on failure. +function fs_lua.make_temp_dir(name) + assert(type(name) == "string") + name = dir.normalize(name) + + return posix.mkdtemp((os.getenv("TMPDIR") or "/tmp") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-XXXXXX") +end + +end + +--------------------------------------------------------------------- +-- Other functions +--------------------------------------------------------------------- + +--- Apply a patch. +-- @param patchname string: The filename of the patch. +-- @param patchdata string or nil: The actual patch as a string. +function fs_lua.apply_patch(patchname, patchdata) + local p, all_ok = patch.read_patch(patchname, patchdata) + if not all_ok then + return nil, "Failed reading patch "..patchname + end + if p then + return patch.apply_patch(p, 1) + end +end + +--- Move a file. +-- @param src string: Pathname of source +-- @param dest string: Pathname of destination +-- @param perms string or nil: Permissions for destination file, +-- or nil to use the source filename permissions. +-- @return boolean or (boolean, string): true on success, false on failure, +-- plus an error message. +function fs_lua.move(src, dest, perms) + assert(src and dest) + if fs.exists(dest) and not fs.is_dir(dest) then + return false, "File already exists: "..dest + end + local ok, err = fs.copy(src, dest, perms) + if not ok then + return false, err + end + fs.delete(src) + if fs.exists(src) then + return false, "Failed move: could not delete "..src.." after copy." + end + return true +end + +--- Check if user has write permissions for the command. +-- Assumes the configuration variables under cfg have been previously set up. +-- @param flags table: the flags table passed to run() drivers. +-- @return boolean or (boolean, string): true on success, false on failure, +-- plus an error message. +function fs_lua.check_command_permissions(flags) + local root_dir = path.root_dir(cfg.rocks_dir) + local ok = true + local err = "" + for _, dir in ipairs { cfg.rocks_dir, root_dir } do + if fs.exists(dir) and not fs.is_writable(dir) then + ok = false + err = "Your user does not have write permissions in " .. dir + break + end + end + if ok and not fs.exists(root_dir) then + local root = fs.root_of(root_dir) + local parent = root_dir + repeat + parent = dir.dir_name(parent) + if parent == "" then + parent = root + end + until parent == root or fs.exists(parent) + if not fs.is_writable(parent) then + ok = false + err = root_dir.." does not exist and your user does not have write permissions in " .. parent + end + end + if ok then + return true + else + if flags["local"] then + err = err .. " \n-- please check your permissions." + else + err = err .. " \n-- you may want to run as a privileged user or use your local tree with --local." + end + return nil, err + end +end + +--- Check whether a file is a Lua script +-- When the file can be succesfully compiled by the configured +-- Lua interpreter, it's considered to be a valid Lua file. +-- @param name filename of file to check +-- @return boolean true, if it is a Lua script, false otherwise +function fs_lua.is_lua(name) + name = name:gsub([[%\]],"/") -- normalize on fw slash to prevent escaping issues + local lua = fs.Q(dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter)) -- get lua interpreter configured + -- execute on configured interpreter, might not be the same as the interpreter LR is run on + local result = fs.execute_string(lua..[[ -e "if loadfile(']]..name..[[') then os.exit() else os.exit(1) end"]]) + return (result == true) +end + +return fs_lua diff --git a/Utils/luarocks/lua/luarocks/fs/tools.lua b/Utils/luarocks/lua/luarocks/fs/tools.lua new file mode 100644 index 000000000..ed51b5458 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fs/tools.lua @@ -0,0 +1,156 @@ + +--- Common fs operations implemented with third-party tools. +local tools = {} + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local cfg = require("luarocks.cfg") + +local vars = cfg.variables + +local dir_stack = {} + +--- Obtain current directory. +-- Uses the module's internal directory stack. +-- @return string: the absolute pathname of the current directory. +function tools.current_dir() + local current = cfg.cache_pwd + if not current then + local pipe = io.popen(fs.quiet_stderr(fs.Q(vars.PWD))) + current = pipe:read("*l") + pipe:close() + cfg.cache_pwd = current + end + for _, directory in ipairs(dir_stack) do + current = fs.absolute_name(directory, current) + end + return current +end + +--- Change the current directory. +-- Uses the module's internal directory stack. This does not have exact +-- semantics of chdir, as it does not handle errors the same way, +-- but works well for our purposes for now. +-- @param directory string: The directory to switch to. +-- @return boolean or (nil, string): true if successful, (nil, error message) if failed. +function tools.change_dir(directory) + assert(type(directory) == "string") + if fs.is_dir(directory) then + table.insert(dir_stack, directory) + return true + end + return nil, "directory not found: "..directory +end + +--- Change directory to root. +-- Allows leaving a directory (e.g. for deleting it) in +-- a crossplatform way. +function tools.change_dir_to_root() + table.insert(dir_stack, "/") +end + +--- Change working directory to the previous in the directory stack. +function tools.pop_dir() + local directory = table.remove(dir_stack) + return directory ~= nil +end + +--- Run the given command. +-- The command is executed in the current directory in the directory stack. +-- @param cmd string: No quoting/escaping is applied to the command. +-- @return boolean: true if command succeeds (status code 0), false +-- otherwise. +function tools.execute_string(cmd) + local current = fs.current_dir() + if not current then return false end + cmd = fs.command_at(current, cmd) + local code = os.execute(cmd) + if code == 0 or code == true then + return true + else + return false + end +end + +--- Internal implementation function for fs.dir. +-- Yields a filename on each iteration. +-- @param at string: directory to list +-- @return nil +function tools.dir_iterator(at) + local pipe = io.popen(fs.command_at(at, fs.Q(vars.LS))) + for file in pipe:lines() do + if file ~= "." and file ~= ".." then + coroutine.yield(file) + end + end + pipe:close() +end + +--- Download a remote file. +-- @param url string: URL to be fetched. +-- @param filename string or nil: this function attempts to detect the +-- resulting local filename of the remote file as the basename of the URL; +-- if that is not correct (due to a redirection, for example), the local +-- filename can be given explicitly as this second argument. +-- @return (boolean, string): true and the filename on success, +-- false and the error message on failure. +function tools.use_downloader(url, filename, cache) + assert(type(url) == "string") + assert(type(filename) == "string" or not filename) + + filename = fs.absolute_name(filename or dir.base_name(url)) + + local ok + if cfg.downloader == "wget" then + local wget_cmd = fs.Q(vars.WGET).." "..vars.WGETNOCERTFLAG.." --no-cache --user-agent=\""..cfg.user_agent.." via wget\" --quiet " + if cfg.connection_timeout and cfg.connection_timeout > 0 then + wget_cmd = wget_cmd .. "--timeout="..tonumber(cfg.connection_timeout).." --tries=1 " + end + if cache then + -- --timestamping is incompatible with --output-document, + -- but that's not a problem for our use cases. + fs.change_dir(dir.dir_name(filename)) + ok = fs.execute_quiet(wget_cmd.." --timestamping ", url) + fs.pop_dir() + elseif filename then + ok = fs.execute_quiet(wget_cmd.." --output-document ", filename, url) + else + ok = fs.execute_quiet(wget_cmd, url) + end + elseif cfg.downloader == "curl" then + local curl_cmd = fs.Q(vars.CURL).." "..vars.CURLNOCERTFLAG.." -f -L --user-agent \""..cfg.user_agent.." via curl\" " + if cfg.connection_timeout and cfg.connection_timeout > 0 then + curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." " + end + ok = fs.execute_string(fs.quiet_stderr(curl_cmd..fs.Q(url).." > "..fs.Q(filename))) + end + if ok then + return true, filename + else + return false + end +end + +local md5_cmd = { + md5sum = fs.Q(vars.MD5SUM), + openssl = fs.Q(vars.OPENSSL).." md5", + md5 = fs.Q(vars.MD5), +} + +--- Get the MD5 checksum for a file. +-- @param file string: The file to be computed. +-- @return string: The MD5 checksum or nil + message +function tools.get_md5(file) + local cmd = md5_cmd[cfg.md5checker] + if not cmd then return nil, "no MD5 checker command configured" end + local pipe = io.popen(cmd.." "..fs.Q(fs.absolute_name(file))) + local computed = pipe:read("*a") + pipe:close() + if computed then + computed = computed:match("("..("%x"):rep(32)..")") + end + if computed then return computed end + return nil, "Failed to compute MD5 hash for file "..tostring(fs.absolute_name(file)) +end + +return tools diff --git a/Utils/luarocks/lua/luarocks/fs/unix.lua b/Utils/luarocks/lua/luarocks/fs/unix.lua new file mode 100644 index 000000000..e2bdc7b8f --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fs/unix.lua @@ -0,0 +1,135 @@ + +--- Unix implementation of filesystem and platform abstractions. +local unix = {} + +local fs = require("luarocks.fs") + +local cfg = require("luarocks.cfg") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +--- Annotate command string for quiet execution. +-- @param cmd string: A command-line string. +-- @return string: The command-line, with silencing annotation. +function unix.quiet(cmd) + return cmd.." 1> /dev/null 2> /dev/null" +end + +--- Annotate command string for execution with quiet stderr. +-- @param cmd string: A command-line string. +-- @return string: The command-line, with stderr silencing annotation. +function unix.quiet_stderr(cmd) + return cmd.." 2> /dev/null" +end + +--- Quote argument for shell processing. +-- Adds single quotes and escapes. +-- @param arg string: Unquoted argument. +-- @return string: Quoted argument. +function unix.Q(arg) + assert(type(arg) == "string") + return "'" .. arg:gsub("'", "'\\''") .. "'" +end + +--- Return an absolute pathname from a potentially relative one. +-- @param pathname string: pathname to convert. +-- @param relative_to string or nil: path to prepend when making +-- pathname absolute, or the current dir in the dir stack if +-- not given. +-- @return string: The pathname converted to absolute. +function unix.absolute_name(pathname, relative_to) + assert(type(pathname) == "string") + assert(type(relative_to) == "string" or not relative_to) + + relative_to = relative_to or fs.current_dir() + if pathname:sub(1,1) == "/" then + return pathname + else + return relative_to .. "/" .. pathname + end +end + +--- Return the root directory for the given path. +-- In Unix, root is always "/". +-- @param pathname string: pathname to use. +-- @return string: The root of the given pathname. +function unix.root_of(_) + return "/" +end + +--- Create a wrapper to make a script executable from the command-line. +-- @param file string: Pathname of script to be made executable. +-- @param dest string: Directory where to put the wrapper. +-- @param name string: rock name to be used in loader context. +-- @param version string: rock version to be used in loader context. +-- @return boolean or (nil, string): True if succeeded, or nil and +-- an error message. +function unix.wrap_script(file, dest, name, version) + assert(type(file) == "string") + assert(type(dest) == "string") + + local base = dir.base_name(file) + local wrapname = fs.is_dir(dest) and dest.."/"..base or dest + local lpath, lcpath = cfg.package_paths() + local wrapper = io.open(wrapname, "w") + if not wrapper then + return nil, "Could not open "..wrapname.." for writing." + end + wrapper:write("#!/bin/sh\n\n") + local lua = dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter) + local ppaths = "package.path="..util.LQ(lpath..";").."..package.path; package.cpath="..util.LQ(lcpath..";").."..package.cpath" + local addctx = "local k,l,_=pcall(require,"..util.LQ("luarocks.loader")..") _=k and l.add_context("..util.LQ(name)..","..util.LQ(version)..")" + wrapper:write('exec '..fs.Q(lua)..' -e '..fs.Q(ppaths)..' -e '..fs.Q(addctx)..' '..fs.Q(file)..' "$@"\n') + wrapper:close() + if fs.chmod(wrapname, cfg.perm_exec) then + return true + else + return nil, "Could not make "..wrapname.." executable." + end +end + +--- Check if a file (typically inside path.bin_dir) is an actual binary +-- or a Lua wrapper. +-- @param filename string: the file name with full path. +-- @return boolean: returns true if file is an actual binary +-- (or if it couldn't check) or false if it is a Lua wrapper. +function unix.is_actual_binary(filename) + if filename:match("%.lua$") then + return false + end + local file = io.open(filename) + if not file then + return true + end + local first = file:read(2) + file:close() + if not first then + util.printerr("Warning: could not read "..filename) + return true + end + return first ~= "#!" +end + +function unix.copy_binary(filename, dest) + return fs.copy(filename, dest, cfg.perm_exec) +end + +--- Move a file on top of the other. +-- The new file ceases to exist under its original name, +-- and takes over the name of the old file. +-- On Unix this is done through a single rename operation. +-- @param old_file The name of the original file, +-- which will be the new name of new_file. +-- @param new_file The name of the new file, +-- which will replace old_file. +-- @return boolean or (nil, string): True if succeeded, or nil and +-- an error message. +function unix.replace_file(old_file, new_file) + return os.rename(new_file, old_file) +end + +function unix.tmpname() + return os.tmpname() +end + +return unix diff --git a/Utils/luarocks/lua/luarocks/fs/unix/tools.lua b/Utils/luarocks/lua/luarocks/fs/unix/tools.lua new file mode 100644 index 000000000..d0802725a --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fs/unix/tools.lua @@ -0,0 +1,237 @@ + +--- fs operations implemented with third-party tools for Unix platform abstractions. +local tools = {} + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local cfg = require("luarocks.cfg") + +local vars = cfg.variables + +--- Adds prefix to command to make it run from a directory. +-- @param directory string: Path to a directory. +-- @param cmd string: A command-line string. +-- @return string: The command-line with prefix. +function tools.command_at(directory, cmd) + return "cd " .. fs.Q(fs.absolute_name(directory)) .. " && " .. cmd +end + +--- Create a directory if it does not already exist. +-- If any of the higher levels in the path name does not exist +-- too, they are created as well. +-- @param directory string: pathname of directory to create. +-- @return boolean: true on success, false on failure. +function tools.make_dir(directory) + assert(directory) + local ok, err = fs.execute(vars.MKDIR.." -p", directory) + if not ok then + err = "failed making directory "..directory + end + return ok, err +end + +--- Remove a directory if it is empty. +-- Does not return errors (for example, if directory is not empty or +-- if already does not exist) +-- @param directory string: pathname of directory to remove. +function tools.remove_dir_if_empty(directory) + assert(directory) + fs.execute_quiet(vars.RMDIR, directory) +end + +--- Remove a directory if it is empty. +-- Does not return errors (for example, if directory is not empty or +-- if already does not exist) +-- @param directory string: pathname of directory to remove. +function tools.remove_dir_tree_if_empty(directory) + assert(directory) + fs.execute_quiet(vars.RMDIR, "-p", directory) +end + +--- Copy a file. +-- @param src string: Pathname of source +-- @param dest string: Pathname of destination +-- @param perm string or nil: Permissions for destination file, +-- @return boolean or (boolean, string): true on success, false on failure, +-- plus an error message. +function tools.copy(src, dest, perm) + assert(src and dest) + if fs.execute(vars.CP, src, dest) then + if perm then + if fs.is_dir(dest) then + dest = dir.path(dest, dir.base_name(src)) + end + if fs.chmod(dest, perm) then + return true + else + return false, "Failed setting permissions of "..dest + end + end + return true + else + return false, "Failed copying "..src.." to "..dest + end +end + +--- Recursively copy the contents of a directory. +-- @param src string: Pathname of source +-- @param dest string: Pathname of destination +-- @return boolean or (boolean, string): true on success, false on failure, +-- plus an error message. +function tools.copy_contents(src, dest) + assert(src and dest) + if fs.execute_quiet(vars.CP.." -pPR "..fs.Q(src).."/* "..fs.Q(dest)) then + return true + else + return false, "Failed copying "..src.." to "..dest + end +end +--- Delete a file or a directory and all its contents. +-- For safety, this only accepts absolute paths. +-- @param arg string: Pathname of source +-- @return nil +function tools.delete(arg) + assert(arg) + assert(arg:sub(1,1) == "/") + fs.execute_quiet(vars.RM, "-rf", arg) +end + +--- Recursively scan the contents of a directory. +-- @param at string or nil: directory to scan (will be the current +-- directory if none is given). +-- @return table: an array of strings with the filenames representing +-- the contents of a directory. +function tools.find(at) + assert(type(at) == "string" or not at) + if not at then + at = fs.current_dir() + end + if not fs.is_dir(at) then + return {} + end + local result = {} + local pipe = io.popen(fs.command_at(at, fs.quiet_stderr(vars.FIND.." *"))) + for file in pipe:lines() do + table.insert(result, file) + end + pipe:close() + return result +end + +--- Compress files in a .zip archive. +-- @param zipfile string: pathname of .zip archive to be created. +-- @param ... Filenames to be stored in the archive are given as +-- additional arguments. +-- @return boolean: true on success, false on failure. +function tools.zip(zipfile, ...) + return fs.execute(vars.ZIP.." -r", zipfile, ...) +end + +--- Uncompress files from a .zip archive. +-- @param zipfile string: pathname of .zip archive to be extracted. +-- @return boolean: true on success, false on failure. +function tools.unzip(zipfile) + assert(zipfile) + return fs.execute_quiet(vars.UNZIP, zipfile) +end + +--- Test is file/directory exists +-- @param file string: filename to test +-- @return boolean: true if file exists, false otherwise. +function tools.exists(file) + assert(file) + return fs.execute(vars.TEST, "-e", file) +end + +--- Test is pathname is a directory. +-- @param file string: pathname to test +-- @return boolean: true if it is a directory, false otherwise. +function tools.is_dir(file) + assert(file) + return fs.execute(vars.TEST, "-d", file) +end + +--- Test is pathname is a regular file. +-- @param file string: pathname to test +-- @return boolean: true if it is a regular file, false otherwise. +function tools.is_file(file) + assert(file) + return fs.execute(vars.TEST, "-f", file) +end + +function tools.chmod(pathname, mode) + if mode then + return fs.execute(vars.CHMOD, mode, pathname) + else + return false + end +end + +--- Unpack an archive. +-- Extract the contents of an archive, detecting its format by +-- filename extension. +-- @param archive string: Filename of archive. +-- @return boolean or (boolean, string): true on success, false and an error message on failure. +function tools.unpack_archive(archive) + assert(type(archive) == "string") + + local pipe_to_tar = " | "..vars.TAR.." -xf -" + + if not cfg.verbose then + pipe_to_tar = " 2> /dev/null"..fs.quiet(pipe_to_tar) + end + + local ok + if archive:match("%.tar%.gz$") or archive:match("%.tgz$") then + ok = fs.execute_string(vars.GUNZIP.." -c "..fs.Q(archive)..pipe_to_tar) + elseif archive:match("%.tar%.bz2$") then + ok = fs.execute_string(vars.BUNZIP2.." -c "..fs.Q(archive)..pipe_to_tar) + elseif archive:match("%.zip$") then + ok = fs.execute_quiet(vars.UNZIP, archive) + elseif archive:match("%.lua$") or archive:match("%.c$") then + -- Ignore .lua and .c files; they don't need to be extracted. + return true + else + return false, "Couldn't extract archive "..archive..": unrecognized filename extension" + end + if not ok then + return false, "Failed extracting "..archive + end + return true +end + +function tools.get_permissions(filename) + local pipe = io.popen(vars.STAT.." "..vars.STATFLAG.." "..fs.Q(filename)) + local ret = pipe:read("*l") + pipe:close() + return ret +end + +function tools.browser(url) + return fs.execute(cfg.web_browser, url) +end + +function tools.set_time(file, time) + file = dir.normalize(file) + return fs.execute(vars.TOUCH, "-d", "@"..tostring(time), file) +end + +--- Create a temporary directory. +-- @param name string: name pattern to use for avoiding conflicts +-- when creating temporary directory. +-- @return string or (nil, string): name of temporary directory or (nil, error message) on failure. +function tools.make_temp_dir(name) + assert(type(name) == "string") + name = dir.normalize(name) + + local template = (os.getenv("TMPDIR") or "/tmp") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-XXXXXX" + local pipe = io.popen(vars.MKTEMP.." -d "..fs.Q(template)) + local dirname = pipe:read("*l") + pipe:close() + if dirname and dirname:match("^/") then + return dirname + end + return nil, "Failed to create temporary directory "..tostring(dirname) +end + +return tools diff --git a/Utils/luarocks/lua/luarocks/fs/win32.lua b/Utils/luarocks/lua/luarocks/fs/win32.lua new file mode 100644 index 000000000..cfc28d35f --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fs/win32.lua @@ -0,0 +1,266 @@ +--- Windows implementation of filesystem and platform abstractions. +-- Download http://unxutils.sourceforge.net/ for Windows GNU utilities +-- used by this module. +local win32 = {} + +local fs = require("luarocks.fs") + +local cfg = require("luarocks.cfg") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +math.randomseed(os.time()) + +-- Monkey patch io.popen and os.execute to make sure quoting +-- works as expected. +-- See http://lua-users.org/lists/lua-l/2013-11/msg00367.html +local _prefix = "type NUL && " +local _popen, _execute = io.popen, os.execute +io.popen = function(cmd, ...) return _popen(_prefix..cmd, ...) end +os.execute = function(cmd, ...) return _execute(_prefix..cmd, ...) end + +--- Annotate command string for quiet execution. +-- @param cmd string: A command-line string. +-- @return string: The command-line, with silencing annotation. +function win32.quiet(cmd) + return cmd.." 2> NUL 1> NUL" +end + +--- Annotate command string for execution with quiet stderr. +-- @param cmd string: A command-line string. +-- @return string: The command-line, with stderr silencing annotation. +function win32.quiet_stderr(cmd) + return cmd.." 2> NUL" +end + +-- Split path into root and the rest. +-- Root part consists of an optional drive letter (e.g. "C:") +-- and an optional directory separator. +local function split_root(path) + local root = "" + + if path:match("^.:") then + root = path:sub(1, 2) + path = path:sub(3) + end + + if path:match("^[\\/]") then + root = path:sub(1, 1) + path = path:sub(2) + end + + return root, path +end + +--- Quote argument for shell processing. Fixes paths on Windows. +-- Adds double quotes and escapes. +-- @param arg string: Unquoted argument. +-- @return string: Quoted argument. +function win32.Q(arg) + assert(type(arg) == "string") + -- Use Windows-specific directory separator for paths. + -- Paths should be converted to absolute by now. + if split_root(arg) ~= "" then + arg = arg:gsub("/", "\\") + end + if arg == "\\" then + return '\\' -- CHDIR needs special handling for root dir + end + -- URLs and anything else + arg = arg:gsub('\\(\\*)"', '\\%1%1"') + arg = arg:gsub('\\+$', '%0%0') + arg = arg:gsub('"', '\\"') + arg = arg:gsub('(\\*)%%', '%1%1"%%"') + return '"' .. arg .. '"' +end + +--- Quote argument for shell processing in batch files. +-- Adds double quotes and escapes. +-- @param arg string: Unquoted argument. +-- @return string: Quoted argument. +function win32.Qb(arg) + assert(type(arg) == "string") + -- Use Windows-specific directory separator for paths. + -- Paths should be converted to absolute by now. + if split_root(arg) ~= "" then + arg = arg:gsub("/", "\\") + end + if arg == "\\" then + return '\\' -- CHDIR needs special handling for root dir + end + -- URLs and anything else + arg = arg:gsub('\\(\\*)"', '\\%1%1"') + arg = arg:gsub('\\+$', '%0%0') + arg = arg:gsub('"', '\\"') + arg = arg:gsub('%%', '%%%%') + return '"' .. arg .. '"' +end + +--- Return an absolute pathname from a potentially relative one. +-- @param pathname string: pathname to convert. +-- @param relative_to string or nil: path to prepend when making +-- pathname absolute, or the current dir in the dir stack if +-- not given. +-- @return string: The pathname converted to absolute. +function win32.absolute_name(pathname, relative_to) + assert(type(pathname) == "string") + assert(type(relative_to) == "string" or not relative_to) + + relative_to = relative_to or fs.current_dir() + local root, rest = split_root(pathname) + if root:match("[\\/]$") then + -- It's an absolute path already. + return pathname + else + -- It's a relative path, join it with base path. + -- This drops drive letter from paths like "C:foo". + return relative_to .. "/" .. rest + end +end + +--- Return the root directory for the given path. +-- For example, for "c:\hello", returns "c:\" +-- @param pathname string: pathname to use. +-- @return string: The root of the given pathname. +function win32.root_of(pathname) + return (split_root(fs.absolute_name(pathname))) +end + +--- Create a wrapper to make a script executable from the command-line. +-- @param file string: Pathname of script to be made executable. +-- @param dest string: Directory where to put the wrapper. +-- @param name string: rock name to be used in loader context. +-- @param version string: rock version to be used in loader context. +-- @return boolean or (nil, string): True if succeeded, or nil and +-- an error message. +function win32.wrap_script(file, dest, name, version) + assert(type(file) == "string") + assert(type(dest) == "string") + + local base = dir.base_name(file) + local wrapname = fs.is_dir(dest) and dest.."/"..base or dest + wrapname = wrapname..".bat" + local lpath, lcpath = cfg.package_paths() + lpath = util.remove_path_dupes(lpath, ";") + lcpath = util.remove_path_dupes(lcpath, ";") + local wrapper = io.open(wrapname, "w") + if not wrapper then + return nil, "Could not open "..wrapname.." for writing." + end + wrapper:write("@echo off\n") + local lua = dir.path(cfg.variables["LUA_BINDIR"], cfg.lua_interpreter) + local ppaths = "package.path="..util.LQ(lpath..";").."..package.path; package.cpath="..util.LQ(lcpath..";").."..package.cpath" + local addctx = "local k,l,_=pcall(require,"..util.LQ("luarocks.loader")..") _=k and l.add_context("..util.LQ(name)..","..util.LQ(version)..")" + wrapper:write(fs.Qb(lua)..' -e '..fs.Qb(ppaths)..' -e '..fs.Qb(addctx)..' '..fs.Qb(file)..' %*\n') + wrapper:write("exit /b %ERRORLEVEL%\n") + wrapper:close() + return true +end + +function win32.is_actual_binary(name) + name = name:lower() + if name:match("%.bat$") or name:match("%.exe$") then + return true + end + return false +end + +function win32.copy_binary(filename, dest) + local ok, err = fs.copy(filename, dest) + if not ok then + return nil, err + end + local exe_pattern = "%.[Ee][Xx][Ee]$" + local base = dir.base_name(filename) + dest = dir.dir_name(dest) + if base:match(exe_pattern) then + base = base:gsub(exe_pattern, ".lua") + local helpname = dest.."/"..base + local helper = io.open(helpname, "w") + if not helper then + return nil, "Could not open "..helpname.." for writing." + end + helper:write('package.path=\"'..package.path:gsub("\\","\\\\")..';\"..package.path\n') + helper:write('package.cpath=\"'..package.path:gsub("\\","\\\\")..';\"..package.cpath\n') + helper:close() + end + return true +end + +function win32.chmod(filename, mode) + return true +end + +function win32.get_permissions(filename) + return "" +end + +--- Move a file on top of the other. +-- The new file ceases to exist under its original name, +-- and takes over the name of the old file. +-- On Windows this is done by removing the original file and +-- renaming the new file to its original name. +-- @param old_file The name of the original file, +-- which will be the new name of new_file. +-- @param new_file The name of the new file, +-- which will replace old_file. +-- @return boolean or (nil, string): True if succeeded, or nil and +-- an error message. +function win32.replace_file(old_file, new_file) + os.remove(old_file) + return os.rename(new_file, old_file) +end + +--- Test is file/dir is writable. +-- Warning: testing if a file/dir is writable does not guarantee +-- that it will remain writable and therefore it is no replacement +-- for checking the result of subsequent operations. +-- @param file string: filename to test +-- @return boolean: true if file exists, false otherwise. +function win32.is_writable(file) + assert(file) + file = dir.normalize(file) + local result + local tmpname = 'tmpluarockstestwritable.deleteme' + if fs.is_dir(file) then + local file2 = dir.path(file, tmpname) + local fh = io.open(file2, 'wb') + result = fh ~= nil + if fh then fh:close() end + if result then + -- the above test might give a false positive when writing to + -- c:\program files\ because of VirtualStore redirection on Vista and up + -- So check whether it's really there + result = fs.exists(file2) + end + os.remove(file2) + else + local fh = io.open(file, 'r+b') + result = fh ~= nil + if fh then fh:close() end + end + return result +end + +--- Create a temporary directory. +-- @param name string: name pattern to use for avoiding conflicts +-- when creating temporary directory. +-- @return string or (nil, string): name of temporary directory or (nil, error message) on failure. +function win32.make_temp_dir(name) + assert(type(name) == "string") + name = dir.normalize(name) + + local temp_dir = os.getenv("TMP") .. "/luarocks_" .. name:gsub(dir.separator, "_") .. "-" .. tostring(math.floor(math.random() * 10000)) + local ok, err = fs.make_dir(temp_dir) + if ok then + return temp_dir + else + return nil, err + end +end + +function win32.tmpname() + return os.getenv("TMP")..os.tmpname() +end + +return win32 diff --git a/Utils/luarocks/lua/luarocks/fs/win32/tools.lua b/Utils/luarocks/lua/luarocks/fs/win32/tools.lua new file mode 100644 index 000000000..4adc78d1e --- /dev/null +++ b/Utils/luarocks/lua/luarocks/fs/win32/tools.lua @@ -0,0 +1,227 @@ + +--- fs operations implemented with third-party tools for Windows platform abstractions. +-- Download http://unxutils.sourceforge.net/ for Windows GNU utilities +-- used by this module. +local tools = {} + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local cfg = require("luarocks.cfg") + +local vars = cfg.variables + +--- Adds prefix to command to make it run from a directory. +-- @param directory string: Path to a directory. +-- @param cmd string: A command-line string. +-- @return string: The command-line with prefix. +function tools.command_at(directory, cmd) + local drive = directory:match("^([A-Za-z]:)") + cmd = "cd " .. fs.Q(directory) .. " & " .. cmd + if drive then + cmd = drive .. " & " .. cmd + end + return cmd +end + +--- Create a directory if it does not already exist. +-- If any of the higher levels in the path name does not exist +-- too, they are created as well. +-- @param directory string: pathname of directory to create. +-- @return boolean: true on success, false on failure. +function tools.make_dir(directory) + assert(directory) + directory = dir.normalize(directory) + fs.execute_quiet(fs.Q(vars.MKDIR).." -p ", directory) + if not fs.is_dir(directory) then + return false, "failed making directory "..directory + end + return true +end + +--- Remove a directory if it is empty. +-- Does not return errors (for example, if directory is not empty or +-- if already does not exist) +-- @param directory string: pathname of directory to remove. +function tools.remove_dir_if_empty(directory) + assert(directory) + fs.execute_quiet(fs.Q(vars.RMDIR), directory) +end + +--- Remove a directory if it is empty. +-- Does not return errors (for example, if directory is not empty or +-- if already does not exist) +-- @param directory string: pathname of directory to remove. +function tools.remove_dir_tree_if_empty(directory) + assert(directory) + fs.execute_quiet(fs.Q(vars.RMDIR), directory) +end + +--- Copy a file. +-- @param src string: Pathname of source +-- @param dest string: Pathname of destination +-- @return boolean or (boolean, string): true on success, false on failure, +-- plus an error message. +function tools.copy(src, dest) + assert(src and dest) + if dest:match("[/\\]$") then dest = dest:sub(1, -2) end + local ok = fs.execute(fs.Q(vars.CP), src, dest) + if ok then + return true + else + return false, "Failed copying "..src.." to "..dest + end +end + +--- Recursively copy the contents of a directory. +-- @param src string: Pathname of source +-- @param dest string: Pathname of destination +-- @return boolean or (boolean, string): true on success, false on failure, +-- plus an error message. +function tools.copy_contents(src, dest) + assert(src and dest) + if fs.execute_quiet(fs.Q(vars.CP), "-dR", src.."\\*.*", dest) then + return true + else + return false, "Failed copying "..src.." to "..dest + end +end + +--- Delete a file or a directory and all its contents. +-- For safety, this only accepts absolute paths. +-- @param arg string: Pathname of source +-- @return nil +function tools.delete(arg) + assert(arg) + assert(arg:match("^[a-zA-Z]?:?[\\/]")) + fs.execute_quiet("if exist "..fs.Q(arg.."\\").." ( RMDIR /S /Q "..fs.Q(arg).." ) else ( DEL /Q /F "..fs.Q(arg).." )") +end + +--- Recursively scan the contents of a directory. +-- @param at string or nil: directory to scan (will be the current +-- directory if none is given). +-- @return table: an array of strings with the filenames representing +-- the contents of a directory. Paths are returned with forward slashes. +function tools.find(at) + assert(type(at) == "string" or not at) + if not at then + at = fs.current_dir() + end + if not fs.is_dir(at) then + return {} + end + local result = {} + local pipe = io.popen(fs.command_at(at, fs.quiet_stderr(fs.Q(vars.FIND)))) + for file in pipe:lines() do + -- Windows find is a bit different + local first_two = file:sub(1,2) + if first_two == ".\\" or first_two == "./" then file=file:sub(3) end + if file ~= "." then + table.insert(result, (file:gsub("\\", "/"))) + end + end + pipe:close() + return result +end + +--- Compress files in a .zip archive. +-- @param zipfile string: pathname of .zip archive to be created. +-- @param ... Filenames to be stored in the archive are given as +-- additional arguments. +-- @return boolean: true on success, false on failure. +function tools.zip(zipfile, ...) + return fs.execute_quiet(fs.Q(vars.SEVENZ).." -aoa a -tzip", zipfile, ...) +end + +--- Uncompress files from a .zip archive. +-- @param zipfile string: pathname of .zip archive to be extracted. +-- @return boolean: true on success, false on failure. +function tools.unzip(zipfile) + assert(zipfile) + return fs.execute_quiet(fs.Q(vars.SEVENZ).." -aoa x", zipfile) +end + +--- Test is pathname is a directory. +-- @param file string: pathname to test +-- @return boolean: true if it is a directory, false otherwise. +function tools.is_dir(file) + assert(file) + return fs.execute_quiet("if not exist " .. fs.Q(file.."\\").." invalidcommandname") +end + +--- Test is pathname is a regular file. +-- @param file string: pathname to test +-- @return boolean: true if it is a regular file, false otherwise. +function tools.is_file(file) + assert(file) + return fs.execute(fs.Q(vars.TEST).." -f", file) +end + +--- Strip the last extension of a filename. +-- Example: "foo.tar.gz" becomes "foo.tar". +-- If filename has no dots, returns it unchanged. +-- @param filename string: The file name to strip. +-- @return string: The stripped name. +local function strip_extension(filename) + assert(type(filename) == "string") + return (filename:gsub("%.[^.]+$", "")) or filename +end + +--- Uncompress gzip file. +-- @param archive string: Filename of archive. +-- @return boolean : success status +local function gunzip(archive) + return fs.execute_quiet(fs.Q(vars.SEVENZ).." -aoa x", archive) +end + +--- Unpack an archive. +-- Extract the contents of an archive, detecting its format by +-- filename extension. +-- @param archive string: Filename of archive. +-- @return boolean or (boolean, string): true on success, false and an error message on failure. +function tools.unpack_archive(archive) + assert(type(archive) == "string") + + local ok + local sevenzx = fs.Q(vars.SEVENZ).." -aoa x" + if archive:match("%.tar%.gz$") then + ok = gunzip(archive) + if ok then + ok = fs.execute_quiet(sevenzx, strip_extension(archive)) + end + elseif archive:match("%.tgz$") then + ok = gunzip(archive) + if ok then + ok = fs.execute_quiet(sevenzx, strip_extension(archive)..".tar") + end + elseif archive:match("%.tar%.bz2$") then + ok = fs.execute_quiet(sevenzx, archive) + if ok then + ok = fs.execute_quiet(sevenzx, strip_extension(archive)) + end + elseif archive:match("%.zip$") then + ok = fs.execute_quiet(sevenzx, archive) + elseif archive:match("%.lua$") or archive:match("%.c$") then + -- Ignore .lua and .c files; they don't need to be extracted. + return true + else + return false, "Couldn't extract archive "..archive..": unrecognized filename extension" + end + if not ok then + return false, "Failed extracting "..archive + end + return true +end + +--- Test for existance of a file. +-- @param file string: filename to test +-- @return boolean: true if file exists, false otherwise. +function tools.exists(file) + assert(file) + return fs.execute_quiet("if not exist " .. fs.Q(file) .. " invalidcommandname") +end + +function tools.browser(url) + return fs.execute(cfg.web_browser..' "Starting docs..." '..fs.Q(url)) +end + +return tools diff --git a/Utils/luarocks/lua/luarocks/help.lua b/Utils/luarocks/lua/luarocks/help.lua new file mode 100644 index 000000000..871e97e99 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/help.lua @@ -0,0 +1,118 @@ + +--- Module implementing the LuaRocks "help" command. +-- This is a generic help display module, which +-- uses a global table called "commands" to find commands +-- to show help for; each command should be represented by a +-- table containing "help" and "help_summary" fields. +local help = {} + +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") +local dir = require("luarocks.dir") + +local program = util.this_program("luarocks") + +util.add_run_function(help) +help.help_summary = "Help on commands. Type '"..program.." help ' for more." + +help.help_arguments = "[]" +help.help = [[ + is the command to show help for. +]] + +local function print_banner() + util.printout("\nLuaRocks "..cfg.program_version..", a module deployment system for Lua") +end + +local function print_section(section) + util.printout("\n"..section) +end + +local function get_status(status) + if status then + return "ok" + else + return "not found" + end +end + +--- Driver function for the "help" command. +-- @param command string or nil: command to show help for; if not +-- given, help summaries for all commands are shown. +-- @return boolean or (nil, string): true if there were no errors +-- or nil and an error message if an invalid command was requested. +function help.command(flags, command) + if not command then + local conf = cfg.which_config() + print_banner() + print_section("NAME") + util.printout("\t"..program..[[ - ]]..program_description) + print_section("SYNOPSIS") + util.printout("\t"..program..[[ [--from= | --only-from=] [--to=] [VAR=VALUE]... [] ]]) + print_section("GENERAL OPTIONS") + util.printout([[ + These apply to all commands, as appropriate: + + --server= Fetch rocks/rockspecs from this server + (takes priority over config file) + --only-server= Fetch rocks/rockspecs from this server only + (overrides any entries in the config file) + --only-sources= Restrict downloads to paths matching the + given URL. + --tree= Which tree to operate on. + --local Use the tree in the user's home directory. + To enable it, see ']]..program..[[ help path'. + --verbose Display verbose output of commands executed. + --timeout= Timeout on network operations, in seconds. + 0 means no timeout (wait forever). + Default is ]]..tostring(cfg.connection_timeout)..[[.]]) + print_section("VARIABLES") + util.printout([[ + Variables from the "variables" table of the configuration file + can be overriden with VAR=VALUE assignments.]]) + print_section("COMMANDS") + for name, command in util.sortedpairs(commands) do + local cmd = require(command) + util.printout("", name) + util.printout("\t", cmd.help_summary) + end + print_section("CONFIGURATION") + util.printout("\tLua version: " .. cfg.lua_version) + util.printout("\tConfiguration files:") + util.printout("\t\tSystem: ".. dir.normalize(conf.system.file) .. " (" .. get_status(conf.system.ok) ..")") + if conf.user.file then + util.printout("\t\tUser : ".. dir.normalize(conf.user.file) .. " (" .. get_status(conf.user.ok) ..")\n") + else + util.printout("\t\tUser : disabled in this LuaRocks installation.\n") + end + util.printout("\tRocks trees in use: ") + for _, tree in ipairs(cfg.rocks_trees) do + if type(tree) == "string" then + util.printout("\t\t"..dir.normalize(tree)) + else + local name = tree.name and " (\""..tree.name.."\")" or "" + util.printout("\t\t"..dir.normalize(tree.root)..name) + end + end + else + command = command:gsub("-", "_") + local cmd = commands[command] and require(commands[command]) + if cmd then + local arguments = cmd.help_arguments or "" + print_banner() + print_section("NAME") + util.printout("\t"..program.." "..command.." - "..cmd.help_summary) + print_section("SYNOPSIS") + util.printout("\t"..program.." "..command.." "..arguments) + print_section("DESCRIPTION") + util.printout("",(cmd.help:gsub("\n","\n\t"):gsub("\n\t$",""))) + print_section("SEE ALSO") + util.printout("","'"..program.." help' for general options and configuration.\n") + else + return nil, "Unknown command: "..command + end + end + return true +end + +return help diff --git a/Utils/luarocks/lua/luarocks/index.lua b/Utils/luarocks/lua/luarocks/index.lua new file mode 100644 index 000000000..e1f563ef2 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/index.lua @@ -0,0 +1,186 @@ + +--- Module which builds the index.html page to be used in rocks servers. +local index = {} +package.loaded["luarocks.index"] = index + +local util = require("luarocks.util") +local fs = require("luarocks.fs") +local deps = require("luarocks.deps") +local persist = require("luarocks.persist") +local dir = require("luarocks.dir") +local manif = require("luarocks.manif") + +local ext_url_target = ' target="_blank"' + +local index_header = [[ + + + +Available rocks + + + + +

Available rocks

+

+Lua modules available from this location for use with LuaRocks: +

+ +]] + +local index_package_begin = [[ + + + +]] + +local index_footer_begin = [[ +
+

$package - $summary
+

$detailed
+$externaldependencies +latest sources $homepage | License: $license

+
+]] + +local index_package_end = [[ +
+

+manifest file +]] +local index_manifest_ver = [[ +• Lua $VER manifest file (zip) +]] +local index_footer_end = [[ +

+ + +]] + +function index.format_external_dependencies(rockspec) + if rockspec.external_dependencies then + local deplist = {} + local listed_set = {} + local plats = nil + for name, desc in util.sortedpairs(rockspec.external_dependencies) do + if name ~= "platforms" then + table.insert(deplist, name:lower()) + listed_set[name] = true + else + plats = desc + end + end + if plats then + for plat, entries in util.sortedpairs(plats) do + for name, desc in util.sortedpairs(entries) do + if not listed_set[name] then + table.insert(deplist, name:lower() .. " (on "..plat..")") + end + end + end + end + return '

External dependencies: ' .. table.concat(deplist, ', ').. '

' + else + return "" + end +end + +function index.make_index(repo) + if not fs.is_dir(repo) then + return nil, "Cannot access repository at "..repo + end + local manifest = manif.load_manifest(repo) + local out = io.open(dir.path(repo, "index.html"), "w") + + out:write(index_header) + for package, version_list in util.sortedpairs(manifest.repository) do + local latest_rockspec = nil + local output = index_package_begin + for version, data in util.sortedpairs(version_list, deps.compare_versions) do + local versions = {} + output = output..version..': ' + table.sort(data, function(a,b) return a.arch < b.arch end) + for _, item in ipairs(data) do + local file + if item.arch == 'rockspec' then + file = ("%s-%s.rockspec"):format(package, version) + if not latest_rockspec then latest_rockspec = file end + else + file = ("%s-%s.%s.rock"):format(package, version, item.arch) + end + table.insert(versions, ''..item.arch..'') + end + output = output .. table.concat(versions, ', ') .. '
' + end + output = output .. index_package_end + if latest_rockspec then + local rockspec = persist.load_into_table(dir.path(repo, latest_rockspec)) + local descript = rockspec.description or {} + local vars = { + anchor = package, + package = rockspec.package, + original = rockspec.source.url, + summary = descript.summary or "", + detailed = descript.detailed or "", + license = descript.license or "N/A", + homepage = descript.homepage and ('| project homepage') or "", + externaldependencies = index.format_external_dependencies(rockspec) + } + vars.detailed = vars.detailed:gsub("\n\n", "

"):gsub("%s+", " ") + vars.detailed = vars.detailed:gsub("(https?://[a-zA-Z0-9%.%%-_%+%[%]=%?&/$@;:]+)", '%1') + output = output:gsub("$(%w+)", vars) + else + output = output:gsub("$anchor", package) + output = output:gsub("$package", package) + output = output:gsub("$(%w+)", "") + end + out:write(output) + end + out:write(index_footer_begin) + for ver in util.lua_versions() do + out:write((index_manifest_ver:gsub("$VER", ver))) + end + out:write(index_footer_end) + out:close() +end + +return index diff --git a/Utils/luarocks/lua/luarocks/install.lua b/Utils/luarocks/lua/luarocks/install.lua new file mode 100644 index 000000000..e28c24f08 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/install.lua @@ -0,0 +1,188 @@ +--- Module implementing the LuaRocks "install" command. +-- Installs binary rocks. +local install = {} +package.loaded["luarocks.install"] = install + +local path = require("luarocks.path") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local util = require("luarocks.util") +local fs = require("luarocks.fs") +local deps = require("luarocks.deps") +local manif = require("luarocks.manif") +local remove = require("luarocks.remove") +local cfg = require("luarocks.cfg") + +util.add_run_function(install) +install.help_summary = "Install a rock." + +install.help_arguments = "{| []}" + +install.help = [[ +Argument may be the name of a rock to be fetched from a repository +or a filename of a locally available rock. + +--keep Do not remove previously installed versions of the + rock after installing a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. + +--only-deps Installs only the dependencies of the rock. +]]..util.deps_mode_help() + + +--- Install a binary rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @return (string, string) or (nil, string, [string]): Name and version of +-- installed rock if succeeded or nil and an error message followed by an error code. +function install.install_binary_rock(rock_file, deps_mode) + assert(type(rock_file) == "string") + + local name, version, arch = path.parse_name(rock_file) + if not name then + return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." + end + + if arch ~= "all" and arch ~= cfg.arch then + return nil, "Incompatible architecture "..arch, "arch" + end + if repos.is_installed(name, version) then + repos.delete_version(name, version, deps_mode) + end + + local rollback = util.schedule_function(function() + fs.delete(path.install_dir(name, version)) + fs.remove_dir_if_empty(path.versions_dir(name)) + end) + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) + if not ok then return nil, err, errcode end + + local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) + if err then + return nil, "Failed loading rockspec for installed package: "..err, errcode + end + + if deps_mode == "none" then + util.printerr("Warning: skipping dependency checks.") + else + ok, err, errcode = deps.check_external_deps(rockspec, "install") + if err then return nil, err, errcode end + end + + -- For compatibility with .rock files built with LuaRocks 1 + if not fs.exists(path.rock_manifest_file(name, version)) then + ok, err = manif.make_rock_manifest(name, version) + if err then return nil, err end + end + + if deps_mode ~= "none" then + ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then return nil, err, errcode end + end + + ok, err = repos.deploy_files(name, version, repos.should_wrap_bin_scripts(rockspec), deps_mode) + if err then return nil, err end + + util.remove_scheduled_function(rollback) + rollback = util.schedule_function(function() + repos.delete_version(name, version, deps_mode) + end) + + ok, err = repos.run_hook(rockspec, "post_install") + if err then return nil, err end + + util.announce_install(rockspec) + util.remove_scheduled_function(rollback) + return name, version +end + +--- Installs the dependencies of a binary rock. +-- @param rock_file string: local or remote filename of a rock. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @return (string, string) or (nil, string, [string]): Name and version of +-- the rock whose dependencies were installed if succeeded or nil and an error message +-- followed by an error code. +function install.install_binary_rock_deps(rock_file, deps_mode) + assert(type(rock_file) == "string") + + local name, version, arch = path.parse_name(rock_file) + if not name then + return nil, "Filename "..rock_file.." does not match format 'name-version-revision.arch.rock'." + end + + if arch ~= "all" and arch ~= cfg.arch then + return nil, "Incompatible architecture "..arch, "arch" + end + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, path.install_dir(name, version)) + if not ok then return nil, err, errcode end + + local rockspec, err, errcode = fetch.load_rockspec(path.rockspec_file(name, version)) + if err then + return nil, "Failed loading rockspec for installed package: "..err, errcode + end + + ok, err, errcode = deps.fulfill_dependencies(rockspec, deps_mode) + if err then return nil, err, errcode end + + util.printout() + util.printout("Successfully installed dependencies for " ..name.." "..version) + + return name, version +end + +--- Driver function for the "install" command. +-- @param name string: name of a binary rock. If an URL or pathname +-- to a binary rock is given, fetches and installs it. If a rockspec or a +-- source rock is given, forwards the request to the "build" command. +-- If a package name is given, forwards the request to "search" and, +-- if returned a result, installs the matching rock. +-- @param version string: When passing a package name, a version number +-- may also be given. +-- @return boolean or (nil, string, exitcode): True if installation was +-- successful, nil and an error message otherwise. exitcode is optionally returned. +function install.command(flags, name, version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("install") + end + + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + + if name:match("%.rockspec$") or name:match("%.src%.rock$") then + local build = require("luarocks.build") + return build.command(flags, name) + elseif name:match("%.rock$") then + if flags["only-deps"] then + ok, err = install.install_binary_rock_deps(name, deps.get_deps_mode(flags)) + else + ok, err = install.install_binary_rock(name, deps.get_deps_mode(flags)) + end + if not ok then return nil, err end + name, version = ok, err + + if (not flags["only-deps"]) and (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) + if not ok then util.printerr(err) end + end + + manif.check_dependencies(nil, deps.get_deps_mode(flags)) + return name, version + else + local search = require("luarocks.search") + local url, err = search.find_suitable_rock(search.make_query(name:lower(), version)) + if not url then + return nil, err + end + util.printout("Installing "..url) + return install.command(flags, url) + end +end + +return install diff --git a/Utils/luarocks/lua/luarocks/lint.lua b/Utils/luarocks/lua/luarocks/lint.lua new file mode 100644 index 000000000..0fd81a203 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/lint.lua @@ -0,0 +1,55 @@ + +--- Module implementing the LuaRocks "lint" command. +-- Utility function that checks syntax of the rockspec. +local lint = {} +package.loaded["luarocks.lint"] = lint + +local util = require("luarocks.util") +local download = require("luarocks.download") +local fetch = require("luarocks.fetch") + +util.add_run_function(lint) +lint.help_summary = "Check syntax of a rockspec." +lint.help_arguments = "" +lint.help = [[ +This is a utility function that checks the syntax of a rockspec. + +It returns success or failure if the text of a rockspec is +syntactically correct. +]] + +function lint.command(flags, input) + if not input then + return nil, "Argument missing. "..util.see_help("lint") + end + + local filename = input + if not input:match(".rockspec$") then + local err + filename, err = download.download("rockspec", input:lower()) + if not filename then + return nil, err + end + end + + local rs, err = fetch.load_local_rockspec(filename) + if not rs then + return nil, "Failed loading rockspec: "..err + end + + local ok = true + + -- This should have been done in the type checker, + -- but it would break compatibility of other commands. + -- Making 'lint' alone be stricter shouldn't be a problem, + -- because extra-strict checks is what lint-type commands + -- are all about. + if not rs.description.license then + util.printerr("Rockspec has no license field.") + ok = false + end + + return ok, ok or filename.." failed consistency checks." +end + +return lint diff --git a/Utils/luarocks/lua/luarocks/list.lua b/Utils/luarocks/lua/luarocks/list.lua new file mode 100644 index 000000000..c65e058f9 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/list.lua @@ -0,0 +1,97 @@ + +--- Module implementing the LuaRocks "list" command. +-- Lists currently installed rocks. +local list = {} +package.loaded["luarocks.list"] = list + +local search = require("luarocks.search") +local deps = require("luarocks.deps") +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") +local path = require("luarocks.path") + +util.add_run_function(list) +list.help_summary = "List currently installed rocks." +list.help_arguments = "[--porcelain] " +list.help = [[ + is a substring of a rock name to filter by. + +--outdated List only rocks for which there is a + higher version available in the rocks server. + +--porcelain Produce machine-friendly output. +]] + +local function check_outdated(trees, query) + local results_installed = {} + for _, tree in ipairs(trees) do + search.manifest_search(results_installed, path.rocks_dir(tree), query) + end + local outdated = {} + for name, versions in util.sortedpairs(results_installed) do + versions = util.keys(versions) + table.sort(versions, deps.compare_versions) + local latest_installed = versions[1] + + local query_available = search.make_query(name:lower()) + query.exact_name = true + local results_available, err = search.search_repos(query_available) + + if results_available[name] then + local available_versions = util.keys(results_available[name]) + table.sort(available_versions, deps.compare_versions) + local latest_available = available_versions[1] + local latest_available_repo = results_available[name][latest_available][1].repo + + if deps.compare_versions(latest_available, latest_installed) then + table.insert(outdated, { name = name, installed = latest_installed, available = latest_available, repo = latest_available_repo }) + end + end + end + return outdated +end + +local function list_outdated(trees, query, porcelain) + util.title("Outdated rocks:", porcelain) + local outdated = check_outdated(trees, query) + for _, item in ipairs(outdated) do + if porcelain then + util.printout(item.name, item.installed, item.available, item.repo) + else + util.printout(item.name) + util.printout(" "..item.installed.." < "..item.available.." at "..item.repo) + util.printout() + end + end + return true +end + +--- Driver function for "list" command. +-- @param filter string or nil: A substring of a rock name to filter by. +-- @param version string or nil: a version may also be passed. +-- @return boolean: True if succeeded, nil on errors. +function list.command(flags, filter, version) + local query = search.make_query(filter and filter:lower() or "", version) + query.exact_name = false + local trees = cfg.rocks_trees + if flags["tree"] then + trees = { flags["tree"] } + end + + if flags["outdated"] then + return list_outdated(trees, query, flags["porcelain"]) + end + + local results = {} + for _, tree in ipairs(trees) do + local ok, err, errcode = search.manifest_search(results, path.rocks_dir(tree), query) + if not ok and errcode ~= "open" then + util.warning(err) + end + end + util.title("Installed rocks:", flags["porcelain"]) + search.print_results(results, flags["porcelain"]) + return true +end + +return list diff --git a/Utils/luarocks/lua/luarocks/loader.lua b/Utils/luarocks/lua/luarocks/loader.lua new file mode 100644 index 000000000..874bc10c3 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/loader.lua @@ -0,0 +1,249 @@ + +--- A module which installs a Lua package loader that is LuaRocks-aware. +-- This loader uses dependency information from the LuaRocks tree to load +-- correct versions of modules. It does this by constructing a "context" +-- table in the environment, which records which versions of packages were +-- used to load previous modules, so that the loader chooses versions +-- that are declared to be compatible with the ones loaded earlier. +local loaders = package.loaders or package.searchers +local package, require, ipairs, table, type, next, tostring, error = + package, require, ipairs, table, type, next, tostring, error +local unpack = unpack or table.unpack + +--module("luarocks.loader") +local loader = {} +package.loaded["luarocks.loader"] = loader + +local cfg = require("luarocks.cfg") +cfg.init_package_paths() + +local path = require("luarocks.path") +local manif_core = require("luarocks.manif_core") +local deps = require("luarocks.deps") +local util = require("luarocks.util") + +-- Workaround for wrappers produced by older versions of LuaRocks +local temporary_global = false +if luarocks then + -- The site_config.lua file generated by old versions uses module(), + -- so it produces a global `luarocks` table. Since we have the table, + -- add the `loader` field to make the old wrappers happy. + luarocks.loader = loader +else + -- When a new version is installed on top of an old version, + -- site_config.lua may be replaced, and then it no longer creates + -- a global. + -- Detect when being called via -lluarocks.loader; this is + -- most likely a wrapper. + local info = debug.getinfo(2, "nS") + if info.what == "C" and not info.name then + luarocks = { loader = loader } + temporary_global = true + -- For the other half of this hack, + -- see the next use of `temporary_global` below. + end +end + +loader.context = {} + +-- Contains a table when rocks trees are loaded, +-- or 'false' to indicate rocks trees failed to load. +-- 'nil' indicates rocks trees were not attempted to be loaded yet. +loader.rocks_trees = nil + +local function load_rocks_trees() + local any_ok = false + local trees = {} + for _, tree in ipairs(cfg.rocks_trees) do + local manifest, err = manif_core.load_local_manifest(path.rocks_dir(tree)) + if manifest then + any_ok = true + table.insert(trees, {tree=tree, manifest=manifest}) + end + end + if not any_ok then + loader.rocks_trees = false + return false + end + loader.rocks_trees = trees + return true +end + +--- Process the dependencies of a package to determine its dependency +-- chain for loading modules. +-- @param name string: The name of an installed rock. +-- @param version string: The version of the rock, in string format +function loader.add_context(name, version) + -- assert(type(name) == "string") + -- assert(type(version) == "string") + + if temporary_global then + -- The first thing a wrapper does is to call add_context. + -- From here on, it's safe to clean the global environment. + luarocks = nil + temporary_global = false + end + + if loader.context[name] then + return + end + loader.context[name] = version + + if not loader.rocks_trees and not load_rocks_trees() then + return nil + end + + for _, tree in ipairs(loader.rocks_trees) do + local manifest = tree.manifest + + local pkgdeps + if manifest.dependencies and manifest.dependencies[name] then + pkgdeps = manifest.dependencies[name][version] + end + if not pkgdeps then + return nil + end + for _, dep in ipairs(pkgdeps) do + local pkg, constraints = dep.name, dep.constraints + + for _, tree in ipairs(loader.rocks_trees) do + local entries = tree.manifest.repository[pkg] + if entries then + for version, pkgs in util.sortedpairs(entries, deps.compare_versions) do + if (not constraints) or deps.match_constraints(deps.parse_version(version), constraints) then + loader.add_context(pkg, version) + end + end + end + end + end + end +end + +--- Internal sorting function. +-- @param a table: A provider table. +-- @param b table: Another provider table. +-- @return boolean: True if the version of a is greater than that of b. +local function sort_versions(a,b) + return a.version > b.version +end + +--- Request module to be loaded through other loaders, +-- once the proper name of the module has been determined. +-- For example, in case the module "socket.core" has been requested +-- to the LuaRocks loader and it determined based on context that +-- the version 2.0.2 needs to be loaded and it is not the current +-- version, the module requested for the other loaders will be +-- "socket.core_2_0_2". +-- @param module The module name requested by the user, such as "socket.core" +-- @param name The rock name, such as "luasocket" +-- @param version The rock version, such as "2.0.2-1" +-- @param module_name The actual module name, such as "socket.core" or "socket.core_2_0_2". +-- @return table or (nil, string): The module table as returned by some other loader, +-- or nil followed by an error message if no other loader managed to load the module. +local function call_other_loaders(module, name, version, module_name) + for i, a_loader in ipairs(loaders) do + if a_loader ~= loader.luarocks_loader then + local results = { a_loader(module_name) } + if type(results[1]) == "function" then + return unpack(results) + end + end + end + return "Failed loading module "..module.." in LuaRocks rock "..name.." "..version +end + +--- Search for a module in the rocks trees +-- @param module string: module name (eg. "socket.core") +-- @param filter_file_name function(string, string, string, string, number): +-- a function that takes the module file name (eg "socket/core.so"), the rock name +-- (eg "luasocket"), the version (eg "2.0.2-1"), the path of the rocks tree +-- (eg "/usr/local"), and the numeric index of the matching entry, so the +-- filter function can know if the matching module was the first entry or not. +-- @return string, string, string, (string or table): +-- * name of the rock containing the module (eg. "luasocket") +-- * version of the rock (eg. "2.0.2-1") +-- * return value of filter_file_name +-- * tree of the module (string or table in `rocks_trees` format) +local function select_module(module, filter_file_name) + --assert(type(module) == "string") + --assert(type(filter_module_name) == "function") + + if not loader.rocks_trees and not load_rocks_trees() then + return nil + end + + local providers = {} + for _, tree in ipairs(loader.rocks_trees) do + local entries = tree.manifest.modules[module] + if entries then + for i, entry in ipairs(entries) do + local name, version = entry:match("^([^/]*)/(.*)$") + local file_name = tree.manifest.repository[name][version][1].modules[module] + if type(file_name) ~= "string" then + error("Invalid data in manifest file for module "..tostring(module).." (invalid data for "..tostring(name).." "..tostring(version)..")") + end + file_name = filter_file_name(file_name, name, version, tree.tree, i) + if loader.context[name] == version then + return name, version, file_name + end + version = deps.parse_version(version) + table.insert(providers, {name = name, version = version, module_name = file_name, tree = tree}) + end + end + end + + if next(providers) then + table.sort(providers, sort_versions) + local first = providers[1] + return first.name, first.version.string, first.module_name, first.tree + end +end + +--- Search for a module +-- @param module string: module name (eg. "socket.core") +-- @return string, string, string, (string or table): +-- * name of the rock containing the module (eg. "luasocket") +-- * version of the rock (eg. "2.0.2-1") +-- * name of the module (eg. "socket.core", or "socket.core_2_0_2" if file is stored versioned). +-- * tree of the module (string or table in `rocks_trees` format) +local function pick_module(module) + return + select_module(module, function(file_name, name, version, tree, i) + if i > 1 then + file_name = path.versioned_name(file_name, "", name, version) + end + return path.path_to_module(file_name) + end) +end + +--- Return the pathname of the file that would be loaded for a module. +-- @param module string: module name (eg. "socket.core") +-- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so") +function loader.which(module) + local _, _, file_name = select_module(module, path.which_i) + return file_name +end + +--- Package loader for LuaRocks support. +-- A module is searched in installed rocks that match the +-- current LuaRocks context. If module is not part of the +-- context, or if a context has not yet been set, the module +-- in the package with the highest version is used. +-- @param module string: The module name, like in plain require(). +-- @return table: The module table (typically), like in plain +-- require(). See require() +-- in the Lua reference manual for details. +function loader.luarocks_loader(module) + local name, version, module_name = pick_module(module) + if not name then + return "No LuaRocks module found for "..module + else + loader.add_context(name, version) + return call_other_loaders(module, name, version, module_name) + end +end + +table.insert(loaders, 1, loader.luarocks_loader) + +return loader diff --git a/Utils/luarocks/lua/luarocks/make.lua b/Utils/luarocks/lua/luarocks/make.lua new file mode 100644 index 000000000..15167c1eb --- /dev/null +++ b/Utils/luarocks/lua/luarocks/make.lua @@ -0,0 +1,92 @@ + +--- Module implementing the LuaRocks "make" command. +-- Builds sources in the current directory, but unlike "build", +-- it does not fetch sources, etc., assuming everything is +-- available in the current directory. +local make = {} +package.loaded["luarocks.make"] = make + +local build = require("luarocks.build") +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") +local fetch = require("luarocks.fetch") +local pack = require("luarocks.pack") +local remove = require("luarocks.remove") +local deps = require("luarocks.deps") +local manif = require("luarocks.manif") + +util.add_run_function(make) +make.help_summary = "Compile package in current directory using a rockspec." +make.help_arguments = "[--pack-binary-rock] []" +make.help = [[ +Builds sources in the current directory, but unlike "build", +it does not fetch sources, etc., assuming everything is +available in the current directory. If no argument is given, +it looks for a rockspec in the current directory and in "rockspec/" +and "rockspecs/" subdirectories, picking the rockspec with newest version +or without version name. If rockspecs for different rocks are found +or there are several rockspecs without version, you must specify which to use, +through the command-line. + +This command is useful as a tool for debugging rockspecs. +To install rocks, you'll normally want to use the "install" and +"build" commands. See the help on those for details. + +--pack-binary-rock Do not install rock. Instead, produce a .rock file + with the contents of compilation in the current + directory. + +--keep Do not remove previously installed versions of the + rock after installing a new one. This behavior can + be made permanent by setting keep_other_versions=true + in the configuration file. + +--branch= Override the `source.branch` field in the loaded + rockspec. Allows to specify a different branch to + fetch. Particularly for SCM rocks. + +]] + +--- Driver function for "make" command. +-- @param name string: A local rockspec. +-- @return boolean or (nil, string, exitcode): True if build was successful; nil and an +-- error message otherwise. exitcode is optionally returned. +function make.command(flags, rockspec) + assert(type(rockspec) == "string" or not rockspec) + + if not rockspec then + local err + rockspec, err = util.get_default_rockspec() + if not rockspec then + return nil, err + end + end + if not rockspec:match("rockspec$") then + return nil, "Invalid argument: 'make' takes a rockspec as a parameter. "..util.see_help("make") + end + + if flags["pack-binary-rock"] then + local rspec, err, errcode = fetch.load_rockspec(rockspec) + if not rspec then + return nil, err + end + return pack.pack_binary_rock(rspec.name, rspec.version, build.build_rockspec, rockspec, false, true, deps.get_deps_mode(flags)) + else + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + ok, err = build.build_rockspec(rockspec, false, true, deps.get_deps_mode(flags)) + if not ok then return nil, err end + local name, version = ok, err + + if (not flags["keep"]) and not cfg.keep_other_versions then + local ok, err = remove.remove_other_versions(name, version, flags["force"], flags["force-fast"]) + if not ok then util.printerr(err) end + end + + manif.check_dependencies(nil, deps.get_deps_mode(flags)) + return name, version + end +end + +return make diff --git a/Utils/luarocks/lua/luarocks/make_manifest.lua b/Utils/luarocks/lua/luarocks/make_manifest.lua new file mode 100644 index 000000000..c39c2939f --- /dev/null +++ b/Utils/luarocks/lua/luarocks/make_manifest.lua @@ -0,0 +1,53 @@ + +--- Module implementing the luarocks-admin "make_manifest" command. +-- Compile a manifest file for a repository. +local make_manifest = {} +package.loaded["luarocks.make_manifest"] = make_manifest + +local manif = require("luarocks.manif") +local index = require("luarocks.index") +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") +local deps = require("luarocks.deps") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") + +util.add_run_function(make_manifest) +make_manifest.help_summary = "Compile a manifest file for a repository." + +make_manifest.help = [[ +, if given, is a local repository pathname. + +--local-tree If given, do not write versioned versions of the manifest file. + Use this when rebuilding the manifest of a local rocks tree. +]] + +--- Driver function for "make_manifest" command. +-- @param repo string or nil: Pathname of a local repository. If not given, +-- the default local repository configured as cfg.rocks_dir is used. +-- @return boolean or (nil, string): True if manifest was generated, +-- or nil and an error message. +function make_manifest.command(flags, repo) + assert(type(repo) == "string" or not repo) + repo = repo or cfg.rocks_dir + + util.printout("Making manifest for "..repo) + + if repo:match("/lib/luarocks") and not flags["local-tree"] then + util.warning("This looks like a local rocks tree, but you did not pass --local-tree.") + end + + local ok, err = manif.make_manifest(repo, deps.get_deps_mode(flags), not flags["local-tree"]) + if ok and not flags["local-tree"] then + util.printout("Generating index.html for "..repo) + index.make_index(repo) + end + if flags["local-tree"] then + for luaver in util.lua_versions() do + fs.delete(dir.path(repo, "manifest-"..luaver)) + end + end + return ok, err +end + +return make_manifest diff --git a/Utils/luarocks/lua/luarocks/manif.lua b/Utils/luarocks/lua/luarocks/manif.lua new file mode 100644 index 000000000..88a13f107 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/manif.lua @@ -0,0 +1,628 @@ +--- Module for handling manifest files and tables. +-- Manifest files describe the contents of a LuaRocks tree or server. +-- They are loaded into manifest tables, which are then used for +-- performing searches, matching dependencies, etc. +local manif = {} +package.loaded["luarocks.manif"] = manif + +local manif_core = require("luarocks.manif_core") +local persist = require("luarocks.persist") +local fetch = require("luarocks.fetch") +local dir = require("luarocks.dir") +local fs = require("luarocks.fs") +local search = require("luarocks.search") +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") +local path = require("luarocks.path") +local repos = require("luarocks.repos") +local deps = require("luarocks.deps") + +manif.rock_manifest_cache = {} + +--- Commit a table to disk in given local path. +-- @param where string: The directory where the table should be saved. +-- @param name string: The filename. +-- @param tbl table: The table to be saved. +-- @return boolean or (nil, string): true if successful, or nil and a +-- message in case of errors. +local function save_table(where, name, tbl) + assert(type(where) == "string") + assert(type(name) == "string") + assert(type(tbl) == "table") + + local filename = dir.path(where, name) + local ok, err = persist.save_from_table(filename..".tmp", tbl) + if ok then + ok, err = fs.replace_file(filename, filename..".tmp") + end + return ok, err +end + +function manif.load_rock_manifest(name, version, root) + assert(type(name) == "string") + assert(type(version) == "string") + + local name_version = name.."/"..version + if manif.rock_manifest_cache[name_version] then + return manif.rock_manifest_cache[name_version].rock_manifest + end + local pathname = path.rock_manifest_file(name, version, root) + local rock_manifest = persist.load_into_table(pathname) + if not rock_manifest then return nil end + manif.rock_manifest_cache[name_version] = rock_manifest + return rock_manifest.rock_manifest +end + +function manif.make_rock_manifest(name, version) + local install_dir = path.install_dir(name, version) + local tree = {} + for _, file in ipairs(fs.find(install_dir)) do + local full_path = dir.path(install_dir, file) + local walk = tree + local last + local last_name + for name in file:gmatch("[^/]+") do + local next = walk[name] + if not next then + next = {} + walk[name] = next + end + last = walk + last_name = name + walk = next + end + if fs.is_file(full_path) then + local sum, err = fs.get_md5(full_path) + if not sum then + return nil, "Failed producing checksum: "..tostring(err) + end + last[last_name] = sum + end + end + local rock_manifest = { rock_manifest=tree } + manif.rock_manifest_cache[name.."/"..version] = rock_manifest + save_table(install_dir, "rock_manifest", rock_manifest ) +end + +local function fetch_manifest_from(repo_url, filename) + local url = dir.path(repo_url, filename) + local name = repo_url:gsub("[/:]","_") + local cache_dir = dir.path(cfg.local_cache, name) + local ok = fs.make_dir(cache_dir) + if not ok then + return nil, "Failed creating temporary cache directory "..cache_dir + end + local file, err, errcode = fetch.fetch_url(url, dir.path(cache_dir, filename), true) + if not file then + return nil, "Failed fetching manifest for "..repo_url..(err and " - "..err or ""), errcode + end + return file +end + +--- Load a local or remote manifest describing a repository. +-- All functions that use manifest tables assume they were obtained +-- through either this function or load_local_manifest. +-- @param repo_url string: URL or pathname for the repository. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @return table or (nil, string, [string]): A table representing the manifest, +-- or nil followed by an error message and an optional error code. +function manif.load_manifest(repo_url, lua_version) + assert(type(repo_url) == "string") + assert(type(lua_version) == "string" or not lua_version) + lua_version = lua_version or cfg.lua_version + + local cached_manifest = manif_core.get_cached_manifest(repo_url, lua_version) + if cached_manifest then + return cached_manifest + end + + local filenames = { + "manifest-"..lua_version..".zip", + "manifest-"..lua_version, + "manifest", + } + + local protocol, repodir = dir.split_url(repo_url) + local pathname + if protocol == "file" then + for _, filename in ipairs(filenames) do + pathname = dir.path(repodir, filename) + if fs.exists(pathname) then + break + end + end + else + local err, errcode + for _, filename in ipairs(filenames) do + pathname, err, errcode = fetch_manifest_from(repo_url, filename) + if pathname then + break + end + end + if not pathname then + return nil, err, errcode + end + end + if pathname:match(".*%.zip$") then + pathname = fs.absolute_name(pathname) + local dir = dir.dir_name(pathname) + fs.change_dir(dir) + local nozip = pathname:match("(.*)%.zip$") + fs.delete(nozip) + local ok = fs.unzip(pathname) + fs.pop_dir() + if not ok then + fs.delete(pathname) + fs.delete(pathname..".timestamp") + return nil, "Failed extracting manifest file" + end + pathname = nozip + end + return manif_core.manifest_loader(pathname, repo_url, lua_version) +end + +--- Update storage table to account for items provided by a package. +-- @param storage table: a table storing items in the following format: +-- keys are item names and values are arrays of packages providing each item, +-- where a package is specified as string `name/version`. +-- @param items table: a table mapping item names to paths. +-- @param name string: package name. +-- @param version string: package version. +local function store_package_items(storage, name, version, items) + assert(type(storage) == "table") + assert(type(items) == "table") + assert(type(name) == "string") + assert(type(version) == "string") + + local package_identifier = name.."/"..version + + for item_name, path in pairs(items) do + if not storage[item_name] then + storage[item_name] = {} + end + + table.insert(storage[item_name], package_identifier) + end +end + +--- Update storage table removing items provided by a package. +-- @param storage table: a table storing items in the following format: +-- keys are item names and values are arrays of packages providing each item, +-- where a package is specified as string `name/version`. +-- @param items table: a table mapping item names to paths. +-- @param name string: package name. +-- @param version string: package version. +local function remove_package_items(storage, name, version, items) + assert(type(storage) == "table") + assert(type(items) == "table") + assert(type(name) == "string") + assert(type(version) == "string") + + local package_identifier = name.."/"..version + + for item_name, path in pairs(items) do + local all_identifiers = storage[item_name] + + for i, identifier in ipairs(all_identifiers) do + if identifier == package_identifier then + table.remove(all_identifiers, i) + break + end + end + + if #all_identifiers == 0 then + storage[item_name] = nil + end + end +end + +--- Sort function for ordering rock identifiers in a manifest's +-- modules table. Rocks are ordered alphabetically by name, and then +-- by version which greater first. +-- @param a string: Version to compare. +-- @param b string: Version to compare. +-- @return boolean: The comparison result, according to the +-- rule outlined above. +local function sort_pkgs(a, b) + assert(type(a) == "string") + assert(type(b) == "string") + + local na, va = a:match("(.*)/(.*)$") + local nb, vb = b:match("(.*)/(.*)$") + + return (na == nb) and deps.compare_versions(va, vb) or na < nb +end + +--- Sort items of a package matching table by version number (higher versions first). +-- @param tbl table: the package matching table: keys should be strings +-- and values arrays of strings with packages names in "name/version" format. +local function sort_package_matching_table(tbl) + assert(type(tbl) == "table") + + if next(tbl) then + for item, pkgs in pairs(tbl) do + if #pkgs > 1 then + table.sort(pkgs, sort_pkgs) + -- Remove duplicates from the sorted array. + local prev = nil + local i = 1 + while pkgs[i] do + local curr = pkgs[i] + if curr == prev then + table.remove(pkgs, i) + else + prev = curr + i = i + 1 + end + end + end + end + end +end + +--- Process the dependencies of a manifest table to determine its dependency +-- chains for loading modules. The manifest dependencies information is filled +-- and any dependency inconsistencies or missing dependencies are reported to +-- standard error. +-- @param manifest table: a manifest table. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for no trees. +local function update_dependencies(manifest, deps_mode) + assert(type(manifest) == "table") + assert(type(deps_mode) == "string") + + for pkg, versions in pairs(manifest.repository) do + for version, repositories in pairs(versions) do + for _, repo in ipairs(repositories) do + if repo.arch == "installed" then + repo.dependencies = {} + deps.scan_deps(repo.dependencies, manifest, pkg, version, deps_mode) + repo.dependencies[pkg] = nil + end + end + end + end +end + +--- Filter manifest table by Lua version, removing rockspecs whose Lua version +-- does not match. +-- @param manifest table: a manifest table. +-- @param lua_version string or nil: filter by Lua version +-- @param repodir string: directory of repository being scanned +-- @param cache table: temporary rockspec cache table +local function filter_by_lua_version(manifest, lua_version, repodir, cache) + assert(type(manifest) == "table") + assert(type(repodir) == "string") + assert((not cache) or type(cache) == "table") + + cache = cache or {} + lua_version = deps.parse_version(lua_version) + for pkg, versions in pairs(manifest.repository) do + local to_remove = {} + for version, repositories in pairs(versions) do + for _, repo in ipairs(repositories) do + if repo.arch == "rockspec" then + local pathname = dir.path(repodir, pkg.."-"..version..".rockspec") + local rockspec, err = cache[pathname] + if not rockspec then + rockspec, err = fetch.load_local_rockspec(pathname, true) + end + if rockspec then + cache[pathname] = rockspec + for _, dep in ipairs(rockspec.dependencies) do + if dep.name == "lua" then + if not deps.match_constraints(lua_version, dep.constraints) then + table.insert(to_remove, version) + end + break + end + end + else + util.printerr("Error loading rockspec for "..pkg.." "..version..": "..err) + end + end + end + end + if next(to_remove) then + for _, incompat in ipairs(to_remove) do + versions[incompat] = nil + end + if not next(versions) then + manifest.repository[pkg] = nil + end + end + end +end + +--- Store search results in a manifest table. +-- @param results table: The search results as returned by search.disk_search. +-- @param manifest table: A manifest table (must contain repository, modules, commands tables). +-- It will be altered to include the search results. +-- @return boolean or (nil, string): true in case of success, or nil followed by an error message. +local function store_results(results, manifest) + assert(type(results) == "table") + assert(type(manifest) == "table") + + for name, versions in pairs(results) do + local pkgtable = manifest.repository[name] or {} + for version, entries in pairs(versions) do + local versiontable = {} + for _, entry in ipairs(entries) do + local entrytable = {} + entrytable.arch = entry.arch + if entry.arch == "installed" then + local rock_manifest = manif.load_rock_manifest(name, version) + if not rock_manifest then + return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" + end + + entrytable.modules = repos.package_modules(name, version) + store_package_items(manifest.modules, name, version, entrytable.modules) + entrytable.commands = repos.package_commands(name, version) + store_package_items(manifest.commands, name, version, entrytable.commands) + end + table.insert(versiontable, entrytable) + end + pkgtable[version] = versiontable + end + manifest.repository[name] = pkgtable + end + sort_package_matching_table(manifest.modules) + sort_package_matching_table(manifest.commands) + return true +end + +--- Scan a LuaRocks repository and output a manifest file. +-- A file called 'manifest' will be written in the root of the given +-- repository directory. +-- @param repo A local repository directory. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for the default dependency mode from the configuration. +-- @param remote boolean: 'true' if making a manifest for a rocks server. +-- @return boolean or (nil, string): True if manifest was generated, +-- or nil and an error message. +function manif.make_manifest(repo, deps_mode, remote) + assert(type(repo) == "string") + assert(type(deps_mode) == "string") + + if deps_mode == "none" then deps_mode = cfg.deps_mode end + + if not fs.is_dir(repo) then + return nil, "Cannot access repository at "..repo + end + + local query = search.make_query("") + query.exact_name = false + query.arch = "any" + local results = search.disk_search(repo, query) + local manifest = { repository = {}, modules = {}, commands = {} } + + manif_core.cache_manifest(repo, nil, manifest) + + local ok, err = store_results(results, manifest) + if not ok then return nil, err end + + if remote then + local cache = {} + for luaver in util.lua_versions() do + local vmanifest = { repository = {}, modules = {}, commands = {} } + local ok, err = store_results(results, vmanifest) + filter_by_lua_version(vmanifest, luaver, repo, cache) + save_table(repo, "manifest-"..luaver, vmanifest) + end + else + update_dependencies(manifest, deps_mode) + end + + return save_table(repo, "manifest", manifest) +end + +--- Update manifest file for a local repository +-- adding information about a version of a package installed in that repository. +-- @param name string: Name of a package from the repository. +-- @param version string: Version of a package from the repository. +-- @param repo string or nil: Pathname of a local repository. If not given, +-- the default local repository is used. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for using the default dependency mode from the configuration. +-- @return boolean or (nil, string): True if manifest was updated successfully, +-- or nil and an error message. +function manif.add_to_manifest(name, version, repo, deps_mode) + assert(type(name) == "string") + assert(type(version) == "string") + local rocks_dir = path.rocks_dir(repo or cfg.root_dir) + assert(type(deps_mode) == "string") + + if deps_mode == "none" then deps_mode = cfg.deps_mode end + + local manifest, err = manif_core.load_local_manifest(rocks_dir) + if not manifest then + util.printerr("No existing manifest. Attempting to rebuild...") + -- Manifest built by `manif.make_manifest` should already + -- include information about given name and version, + -- no need to update it. + return manif.make_manifest(rocks_dir, deps_mode) + end + + local results = {[name] = {[version] = {{arch = "installed", repo = rocks_dir}}}} + + local ok, err = store_results(results, manifest) + if not ok then return nil, err end + + update_dependencies(manifest, deps_mode) + return save_table(rocks_dir, "manifest", manifest) +end + +--- Update manifest file for a local repository +-- removing information about a version of a package. +-- @param name string: Name of a package removed from the repository. +-- @param version string: Version of a package removed from the repository. +-- @param repo string or nil: Pathname of a local repository. If not given, +-- the default local repository is used. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for using the default dependency mode from the configuration. +-- @return boolean or (nil, string): True if manifest was updated successfully, +-- or nil and an error message. +function manif.remove_from_manifest(name, version, repo, deps_mode) + assert(type(name) == "string") + assert(type(version) == "string") + local rocks_dir = path.rocks_dir(repo or cfg.root_dir) + assert(type(deps_mode) == "string") + + if deps_mode == "none" then deps_mode = cfg.deps_mode end + + local manifest, err = manif_core.load_local_manifest(rocks_dir) + if not manifest then + util.printerr("No existing manifest. Attempting to rebuild...") + -- Manifest built by `manif.make_manifest` should already + -- include up-to-date information, no need to update it. + return manif.make_manifest(rocks_dir, deps_mode) + end + + local package_entry = manifest.repository[name] + + local version_entry = package_entry[version][1] + remove_package_items(manifest.modules, name, version, version_entry.modules) + remove_package_items(manifest.commands, name, version, version_entry.commands) + + package_entry[version] = nil + manifest.dependencies[name][version] = nil + + if not next(package_entry) then + -- No more versions of this package. + manifest.repository[name] = nil + manifest.dependencies[name] = nil + end + + update_dependencies(manifest, deps_mode) + return save_table(rocks_dir, "manifest", manifest) +end + +--- Report missing dependencies for all rocks installed in a repository. +-- @param repo string or nil: Pathname of a local repository. If not given, +-- the default local repository is used. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for using the default dependency mode from the configuration. +function manif.check_dependencies(repo, deps_mode) + local rocks_dir = path.rocks_dir(repo or cfg.root_dir) + assert(type(deps_mode) == "string") + if deps_mode == "none" then deps_mode = cfg.deps_mode end + + local manifest = manif_core.load_local_manifest(rocks_dir) + if not manifest then + return + end + + for name, versions in util.sortedpairs(manifest.repository) do + for version, version_entries in util.sortedpairs(versions, deps.compare_versions) do + for _, entry in ipairs(version_entries) do + if entry.arch == "installed" then + if manifest.dependencies[name] and manifest.dependencies[name][version] then + deps.report_missing_dependencies(name, version, manifest.dependencies[name][version], deps_mode) + end + end + end + end + end +end + +function manif.zip_manifests() + for ver in util.lua_versions() do + local file = "manifest-"..ver + local zip = file..".zip" + fs.delete(dir.path(fs.current_dir(), zip)) + fs.zip(zip, file) + end +end + +--- Get type and name of an item (a module or a command) provided by a file. +-- @param deploy_type string: rock manifest subtree the file comes from ("bin", "lua", or "lib"). +-- @param file_path string: path to the file relatively to deploy_type subdirectory. +-- @return (string, string): item type ("module" or "command") and name. +function manif.get_provided_item(deploy_type, file_path) + assert(type(deploy_type) == "string") + assert(type(file_path) == "string") + local item_type = deploy_type == "bin" and "command" or "module" + local item_name = item_type == "command" and file_path or path.path_to_module(file_path) + return item_type, item_name +end + +local function get_providers(item_type, item_name, repo) + assert(type(item_type) == "string") + assert(type(item_name) == "string") + local rocks_dir = path.rocks_dir(repo or cfg.root_dir) + local manifest = manif_core.load_local_manifest(rocks_dir) + return manifest and manifest[item_type .. "s"][item_name] +end + +--- Given a name of a module or a command, figure out which rock name and version +-- correspond to it in the rock tree manifest. +-- @param item_type string: "module" or "command". +-- @param item_name string: module or command name. +-- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used. +-- @return (string, string) or nil: name and version of the provider rock or nil if there +-- is no provider. +function manif.get_current_provider(item_type, item_name, repo) + local providers = get_providers(item_type, item_name, repo) + if providers then + return providers[1]:match("([^/]*)/([^/]*)") + end +end + +function manif.get_next_provider(item_type, item_name, repo) + local providers = get_providers(item_type, item_name, repo) + if providers and providers[2] then + return providers[2]:match("([^/]*)/([^/]*)") + end +end + +--- Given a name of a module or a command provided by a package, figure out +-- which file provides it. +-- @param name string: package name. +-- @param version string: package version. +-- @param item_type string: "module" or "command". +-- @param item_name string: module or command name. +-- @param root string or nil: A local root dir for a rocks tree. If not given, the default is used. +-- @return (string, string): rock manifest subtree the file comes from ("bin", "lua", or "lib") +-- and path to the providing file relatively to that subtree. +function manif.get_providing_file(name, version, item_type, item_name, repo) + local rocks_dir = path.rocks_dir(repo or cfg.root_dir) + local manifest = manif_core.load_local_manifest(rocks_dir) + + local entry_table = manifest.repository[name][version][1] + local file_path = entry_table[item_type .. "s"][item_name] + + if item_type == "command" then + return "bin", file_path + end + + -- A module can be in "lua" or "lib". Decide based on extension first: + -- most likely Lua modules are in "lua/" and C modules are in "lib/". + if file_path:match("%." .. cfg.lua_extension .. "$") then + return "lua", file_path + elseif file_path:match("%." .. cfg.lib_extension .. "$") then + return "lib", file_path + end + + -- Fallback to rock manifest scanning. + local rock_manifest = manif.load_rock_manifest(name, version) + local subtree = rock_manifest.lib + + for path_part in file_path:gmatch("[^/]+") do + if type(subtree) == "table" then + subtree = subtree[path_part] + else + -- Assume it's in "lua/" if it's not in "lib/". + return "lua", file_path + end + end + + return type(subtree) == "string" and "lib" or "lua", file_path +end + +return manif diff --git a/Utils/luarocks/lua/luarocks/manif_core.lua b/Utils/luarocks/lua/luarocks/manif_core.lua new file mode 100644 index 000000000..82e7ea4de --- /dev/null +++ b/Utils/luarocks/lua/luarocks/manif_core.lua @@ -0,0 +1,106 @@ + +--- Core functions for querying manifest files. +-- This module requires no specific 'fs' functionality. +local manif_core = {} +package.loaded["luarocks.manif_core"] = manif_core + +local persist = require("luarocks.persist") +local type_check = require("luarocks.type_check") +local cfg = require("luarocks.cfg") +local dir = require("luarocks.dir") +local util = require("luarocks.util") +local path = require("luarocks.path") + +-- Table with repository identifiers as keys and tables mapping +-- Lua versions to cached loaded manifests as values. +local manifest_cache = {} + +--- Cache a loaded manifest. +-- @param repo_url string: The repository identifier. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @param manifest table: the manifest to be cached. +function manif_core.cache_manifest(repo_url, lua_version, manifest) + lua_version = lua_version or cfg.lua_version + manifest_cache[repo_url] = manifest_cache[repo_url] or {} + manifest_cache[repo_url][lua_version] = manifest +end + +--- Attempt to get cached loaded manifest. +-- @param repo_url string: The repository identifier. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @return table or nil: loaded manifest or nil if cache is empty. +function manif_core.get_cached_manifest(repo_url, lua_version) + lua_version = lua_version or cfg.lua_version + return manifest_cache[repo_url] and manifest_cache[repo_url][lua_version] +end + +--- Back-end function that actually loads the manifest +-- and stores it in the manifest cache. +-- @param file string: The local filename of the manifest file. +-- @param repo_url string: The repository identifier. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @param quick boolean: If given, skips type checking. +-- @return table or (nil, string, string): the manifest or nil, +-- error message and error code ("open", "load", "run" or "type"). +function manif_core.manifest_loader(file, repo_url, lua_version, quick) + local manifest, err, errcode = persist.load_into_table(file) + if not manifest then + return nil, "Failed loading manifest for "..repo_url..": "..err, errcode + end + local globals = err + if not quick then + local ok, err = type_check.type_check_manifest(manifest, globals) + if not ok then + return nil, "Error checking manifest: "..err, "type" + end + end + + manif_core.cache_manifest(repo_url, lua_version, manifest) + return manifest +end + +--- Load a local manifest describing a repository. +-- All functions that use manifest tables assume they were obtained +-- through either this function or load_manifest. +-- @param repo_url string: URL or pathname for the repository. +-- @return table or (nil, string, string): A table representing the manifest, +-- or nil followed by an error message and an error code, see manifest_loader. +function manif_core.load_local_manifest(repo_url) + assert(type(repo_url) == "string") + + local cached_manifest = manif_core.get_cached_manifest(repo_url) + if cached_manifest then + return cached_manifest + end + + local pathname = dir.path(repo_url, "manifest") + return manif_core.manifest_loader(pathname, repo_url, nil, true) +end + +--- Get all versions of a package listed in a manifest file. +-- @param name string: a package name. +-- @param deps_mode string: "one", to use only the currently +-- configured tree; "order" to select trees based on order +-- (use the current tree and all trees below it on the list) +-- or "all", to use all trees. +-- @return table: An array of strings listing installed +-- versions of a package. +function manif_core.get_versions(name, deps_mode) + assert(type(name) == "string") + assert(type(deps_mode) == "string") + + local version_set = {} + path.map_trees(deps_mode, function(tree) + local manifest = manif_core.load_local_manifest(path.rocks_dir(tree)) + + if manifest and manifest.repository[name] then + for version in pairs(manifest.repository[name]) do + version_set[version] = true + end + end + end) + + return util.keys(version_set) +end + +return manif_core diff --git a/Utils/luarocks/lua/luarocks/new_version.lua b/Utils/luarocks/lua/luarocks/new_version.lua new file mode 100644 index 000000000..91f7607c3 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/new_version.lua @@ -0,0 +1,200 @@ + +--- Module implementing the LuaRocks "new_version" command. +-- Utility function that writes a new rockspec, updating data from a previous one. +local new_version = {} + +local util = require("luarocks.util") +local download = require("luarocks.download") +local fetch = require("luarocks.fetch") +local persist = require("luarocks.persist") +local fs = require("luarocks.fs") +local type_check = require("luarocks.type_check") + +util.add_run_function(new_version) +new_version.help_summary = "Auto-write a rockspec for a new version of a rock." +new_version.help_arguments = "[--tag=] [|] [] []" +new_version.help = [[ +This is a utility function that writes a new rockspec, updating data +from a previous one. + +If a package name is given, it downloads the latest rockspec from the +default server. If a rockspec is given, it uses it instead. If no argument +is given, it looks for a rockspec same way 'luarocks make' does. + +If the version number is not given and tag is passed using --tag, +it is used as the version, with 'v' removed from beginning. +Otherwise, it only increments the revision number of the given +(or downloaded) rockspec. + +If a URL is given, it replaces the one from the old rockspec with the +given URL. If a URL is not given and a new version is given, it tries +to guess the new URL by replacing occurrences of the version number +in the URL or tag. It also tries to download the new URL to determine +the new MD5 checksum. + +If a tag is given, it replaces the one from the old rockspec. If there is +an old tag but no new one passed, it is guessed in the same way URL is. + +WARNING: it writes the new rockspec to the current directory, +overwriting the file if it already exists. +]] + +local function try_replace(tbl, field, old, new) + if not tbl[field] then + return false + end + local old_field = tbl[field] + local new_field = tbl[field]:gsub(old, new) + if new_field ~= old_field then + util.printout("Guessing new '"..field.."' field as "..new_field) + tbl[field] = new_field + return true + end + return false +end + +-- Try to download source file using URL from a rockspec. +-- If it specified MD5, update it. +-- @return (true, false) if MD5 was not specified or it stayed same, +-- (true, true) if MD5 changed, (nil, string) on error. +local function check_url_and_update_md5(out_rs) + local file, temp_dir = fetch.fetch_url_at_temp_dir(out_rs.source.url, "luarocks-new-version-"..out_rs.package) + if not file then + util.printerr("Warning: invalid URL - "..temp_dir) + return true, false + end + + local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, out_rs.source.url, out_rs.source.dir) + if not inferred_dir then + return nil, found_dir + end + + if found_dir and found_dir ~= inferred_dir then + out_rs.source.dir = found_dir + end + + if file then + if out_rs.source.md5 then + util.printout("File successfully downloaded. Updating MD5 checksum...") + local new_md5, err = fs.get_md5(file) + if not new_md5 then + return nil, err + end + local old_md5 = out_rs.source.md5 + out_rs.source.md5 = new_md5 + return true, new_md5 ~= old_md5 + else + util.printout("File successfully downloaded.") + return true, false + end + end +end + +local function update_source_section(out_rs, url, tag, old_ver, new_ver) + if tag then + out_rs.source.tag = tag + end + if url then + out_rs.source.url = url + return check_url_and_update_md5(out_rs) + end + if new_ver == old_ver then + return true + end + if out_rs.source.dir then + try_replace(out_rs.source, "dir", old_ver, new_ver) + end + if out_rs.source.file then + try_replace(out_rs.source, "file", old_ver, new_ver) + end + if try_replace(out_rs.source, "url", old_ver, new_ver) then + return check_url_and_update_md5(out_rs) + end + if tag or try_replace(out_rs.source, "tag", old_ver, new_ver) then + return true + end + -- Couldn't replace anything significant, use the old URL. + local ok, md5_changed = check_url_and_update_md5(out_rs) + if not ok then + return nil, md5_changed + end + if md5_changed then + util.printerr("Warning: URL is the same, but MD5 has changed. Old rockspec is broken.") + end + return true +end + +function new_version.command(flags, input, version, url) + if not input then + local err + input, err = util.get_default_rockspec() + if not input then + return nil, err + end + end + assert(type(input) == "string") + + local filename, err + if input:match("rockspec$") then + filename, err = fetch.fetch_url(input) + if not filename then + return nil, err + end + else + filename, err = download.download("rockspec", input:lower()) + if not filename then + return nil, err + end + end + + local valid_rs, err = fetch.load_rockspec(filename) + if not valid_rs then + return nil, err + end + + local old_ver, old_rev = valid_rs.version:match("(.*)%-(%d+)$") + local new_ver, new_rev + + if flags.tag and not version then + version = flags.tag:gsub("^v", "") + end + + if version then + new_ver, new_rev = version:match("(.*)%-(%d+)$") + new_rev = tonumber(new_rev) + if not new_rev then + new_ver = version + new_rev = 1 + end + else + new_ver = old_ver + new_rev = tonumber(old_rev) + 1 + end + local new_rockver = new_ver:gsub("-", "") + + local out_rs, err = persist.load_into_table(filename) + local out_name = out_rs.package:lower() + out_rs.version = new_rockver.."-"..new_rev + + local ok, err = update_source_section(out_rs, url, flags.tag, old_ver, new_ver) + if not ok then return nil, err end + + if out_rs.build and out_rs.build.type == "module" then + out_rs.build.type = "builtin" + end + + local out_filename = out_name.."-"..new_rockver.."-"..new_rev..".rockspec" + + persist.save_from_table(out_filename, out_rs, type_check.rockspec_order) + + util.printout("Wrote "..out_filename) + + local valid_out_rs, err = fetch.load_local_rockspec(out_filename) + if not valid_out_rs then + return nil, "Failed loading generated rockspec: "..err + end + + return true +end + +return new_version diff --git a/Utils/luarocks/lua/luarocks/pack.lua b/Utils/luarocks/lua/luarocks/pack.lua new file mode 100644 index 000000000..35bbe8380 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/pack.lua @@ -0,0 +1,197 @@ + +--- Module implementing the LuaRocks "pack" command. +-- Creates a rock, packing sources or binaries. +local pack = {} +package.loaded["luarocks.pack"] = pack + +local unpack = unpack or table.unpack + +local path = require("luarocks.path") +local repos = require("luarocks.repos") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local manif = require("luarocks.manif") +local search = require("luarocks.search") + +util.add_run_function(pack) +pack.help_summary = "Create a rock, packing sources or binaries." +pack.help_arguments = "{| []}" +pack.help = [[ +Argument may be a rockspec file, for creating a source rock, +or the name of an installed package, for creating a binary rock. +In the latter case, the app version may be given as a second +argument. +]] + +--- Create a source rock. +-- Packages a rockspec and its required source files in a rock +-- file with the .src.rock extension, which can later be built and +-- installed with the "build" command. +-- @param rockspec_file string: An URL or pathname for a rockspec file. +-- @return string or (nil, string): The filename of the resulting +-- .src.rock file; or nil and an error message. +function pack.pack_source_rock(rockspec_file) + assert(type(rockspec_file) == "string") + + local rockspec, err = fetch.load_rockspec(rockspec_file) + if err then + return nil, "Error loading rockspec: "..err + end + rockspec_file = rockspec.local_filename + + local name_version = rockspec.name .. "-" .. rockspec.version + local rock_file = fs.absolute_name(name_version .. ".src.rock") + + local source_file, source_dir = fetch.fetch_sources(rockspec, false) + if not source_file then + return nil, source_dir + end + local ok, err = fs.change_dir(source_dir) + if not ok then return nil, err end + + fs.delete(rock_file) + fs.copy(rockspec_file, source_dir, cfg.perm_read) + if not fs.zip(rock_file, dir.base_name(rockspec_file), dir.base_name(source_file)) then + return nil, "Failed packing "..rock_file + end + fs.pop_dir() + + return rock_file +end + +local function copy_back_files(name, version, file_tree, deploy_dir, pack_dir, perms) + local ok, err = fs.make_dir(pack_dir) + if not ok then return nil, err end + for file, sub in pairs(file_tree) do + local source = dir.path(deploy_dir, file) + local target = dir.path(pack_dir, file) + if type(sub) == "table" then + local ok, err = copy_back_files(name, version, sub, source, target) + if not ok then return nil, err end + else + local versioned = path.versioned_name(source, deploy_dir, name, version) + if fs.exists(versioned) then + fs.copy(versioned, target, perms) + else + fs.copy(source, target, perms) + end + end + end + return true +end + +-- @param name string: Name of package to pack. +-- @param version string or nil: A version number may also be passed. +-- @param tree string or nil: An optional tree to pick the package from. +-- @return string or (nil, string): The filename of the resulting +-- .src.rock file; or nil and an error message. +local function do_pack_binary_rock(name, version, tree) + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local repo, repo_url + name, version, repo, repo_url = search.pick_installed_rock(name, version, tree) + if not name then + return nil, version + end + + local root = path.root_dir(repo_url) + local prefix = path.install_dir(name, version, root) + if not fs.exists(prefix) then + return nil, "'"..name.." "..version.."' does not seem to be an installed rock." + end + + local rock_manifest = manif.load_rock_manifest(name, version, root) + if not rock_manifest then + return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" + end + + local name_version = name .. "-" .. version + local rock_file = fs.absolute_name(name_version .. "."..cfg.arch..".rock") + + local temp_dir = fs.make_temp_dir("pack") + fs.copy_contents(prefix, temp_dir) + + local is_binary = false + if rock_manifest.lib then + local ok, err = copy_back_files(name, version, rock_manifest.lib, path.deploy_lib_dir(root), dir.path(temp_dir, "lib"), cfg.perm_exec) + if not ok then return nil, "Failed copying back files: " .. err end + is_binary = true + end + if rock_manifest.lua then + local ok, err = copy_back_files(name, version, rock_manifest.lua, path.deploy_lua_dir(root), dir.path(temp_dir, "lua"), cfg.perm_read) + if not ok then return nil, "Failed copying back files: " .. err end + end + + local ok, err = fs.change_dir(temp_dir) + if not ok then return nil, err end + if not is_binary and not repos.has_binaries(name, version) then + rock_file = rock_file:gsub("%."..cfg.arch:gsub("%-","%%-").."%.", ".all.") + end + fs.delete(rock_file) + if not fs.zip(rock_file, unpack(fs.list_dir())) then + return nil, "Failed packing "..rock_file + end + fs.pop_dir() + fs.delete(temp_dir) + return rock_file +end + +function pack.pack_binary_rock(name, version, cmd, ...) + + -- The --pack-binary-rock option for "luarocks build" basically performs + -- "luarocks build" on a temporary tree and then "luarocks pack". The + -- alternative would require refactoring parts of luarocks.build and + -- luarocks.pack, which would save a few file operations: the idea would be + -- to shave off the final deploy steps from the build phase and the initial + -- collect steps from the pack phase. + + local temp_dir, err = fs.make_temp_dir("luarocks-build-pack-"..dir.base_name(name)) + if not temp_dir then + return nil, "Failed creating temporary directory: "..err + end + util.schedule_function(fs.delete, temp_dir) + + path.use_tree(temp_dir) + local ok, err = cmd(...) + if not ok then + return nil, err + end + local rname, rversion = path.parse_name(name) + if not rname then + rname, rversion = name, version + end + return do_pack_binary_rock(rname, rversion, temp_dir) +end + +--- Driver function for the "pack" command. +-- @param arg string: may be a rockspec file, for creating a source rock, +-- or the name of an installed package, for creating a binary rock. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function pack.command(flags, arg, version) + assert(type(version) == "string" or not version) + if type(arg) ~= "string" then + return nil, "Argument missing. "..util.see_help("pack") + end + + local file, err + if arg:match(".*%.rockspec") then + file, err = pack.pack_source_rock(arg) + else + file, err = do_pack_binary_rock(arg:lower(), version, flags["tree"]) + end + if err then + return nil, err + else + util.printout("Packed: "..file) + return true + end +end + +return pack diff --git a/Utils/luarocks/lua/luarocks/path.lua b/Utils/luarocks/lua/luarocks/path.lua new file mode 100644 index 000000000..dafc64e7c --- /dev/null +++ b/Utils/luarocks/lua/luarocks/path.lua @@ -0,0 +1,388 @@ + +--- LuaRocks-specific path handling functions. +-- All paths are configured in this module, making it a single +-- point where the layout of the local installation is defined in LuaRocks. +local path = {} + +local dir = require("luarocks.dir") +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") + +--- Infer rockspec filename from a rock filename. +-- @param rock_name string: Pathname of a rock file. +-- @return string: Filename of the rockspec, without path. +function path.rockspec_name_from_rock(rock_name) + assert(type(rock_name) == "string") + local base_name = dir.base_name(rock_name) + return base_name:match("(.*)%.[^.]*.rock") .. ".rockspec" +end + +function path.rocks_dir(tree) + if type(tree) == "string" then + return dir.path(tree, cfg.rocks_subdir) + else + assert(type(tree) == "table") + return tree.rocks_dir or dir.path(tree.root, cfg.rocks_subdir) + end +end + +function path.root_dir(rocks_dir) + assert(type(rocks_dir) == "string") + return rocks_dir:match("(.*)" .. util.matchquote(cfg.rocks_subdir) .. ".*$") +end + +function path.rocks_tree_to_string(tree) + if type(tree) == "string" then + return tree + else + assert(type(tree) == "table") + return tree.root + end +end + +function path.deploy_bin_dir(tree) + if type(tree) == "string" then + return dir.path(tree, "bin") + else + assert(type(tree) == "table") + return tree.bin_dir or dir.path(tree.root, "bin") + end +end + +function path.deploy_lua_dir(tree) + if type(tree) == "string" then + return dir.path(tree, cfg.lua_modules_path) + else + assert(type(tree) == "table") + return tree.lua_dir or dir.path(tree.root, cfg.lua_modules_path) + end +end + +function path.deploy_lib_dir(tree) + if type(tree) == "string" then + return dir.path(tree, cfg.lib_modules_path) + else + assert(type(tree) == "table") + return tree.lib_dir or dir.path(tree.root, cfg.lib_modules_path) + end +end + +function path.manifest_file(tree) + if type(tree) == "string" then + return dir.path(tree, cfg.rocks_subdir, "manifest") + else + assert(type(tree) == "table") + return (tree.rocks_dir and dir.path(tree.rocks_dir, "manifest")) or dir.path(tree.root, cfg.rocks_subdir, "manifest") + end +end + +--- Get the directory for all versions of a package in a tree. +-- @param name string: The package name. +-- @return string: The resulting path -- does not guarantee that +-- @param tree string or nil: If given, specifies the local tree to use. +-- the package (and by extension, the path) exists. +function path.versions_dir(name, tree) + assert(type(name) == "string") + tree = tree or cfg.root_dir + return dir.path(path.rocks_dir(tree), name) +end + +--- Get the local installation directory (prefix) for a package. +-- @param name string: The package name. +-- @param version string: The package version. +-- @param tree string or nil: If given, specifies the local tree to use. +-- @return string: The resulting path -- does not guarantee that +-- the package (and by extension, the path) exists. +function path.install_dir(name, version, tree) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(path.rocks_dir(tree), name, version) +end + +--- Get the local filename of the rockspec of an installed rock. +-- @param name string: The package name. +-- @param version string: The package version. +-- @param tree string or nil: If given, specifies the local tree to use. +-- @return string: The resulting path -- does not guarantee that +-- the package (and by extension, the file) exists. +function path.rockspec_file(name, version, tree) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(path.rocks_dir(tree), name, version, name.."-"..version..".rockspec") +end + +--- Get the local filename of the rock_manifest file of an installed rock. +-- @param name string: The package name. +-- @param version string: The package version. +-- @param tree string or nil: If given, specifies the local tree to use. +-- @return string: The resulting path -- does not guarantee that +-- the package (and by extension, the file) exists. +function path.rock_manifest_file(name, version, tree) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(path.rocks_dir(tree), name, version, "rock_manifest") +end + +--- Get the local installation directory for C libraries of a package. +-- @param name string: The package name. +-- @param version string: The package version. +-- @param tree string or nil: If given, specifies the local tree to use. +-- @return string: The resulting path -- does not guarantee that +-- the package (and by extension, the path) exists. +function path.lib_dir(name, version, tree) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(path.rocks_dir(tree), name, version, "lib") +end + +--- Get the local installation directory for Lua modules of a package. +-- @param name string: The package name. +-- @param version string: The package version. +-- @param tree string or nil: If given, specifies the local tree to use. +-- @return string: The resulting path -- does not guarantee that +-- the package (and by extension, the path) exists. +function path.lua_dir(name, version, tree) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(path.rocks_dir(tree), name, version, "lua") +end + +--- Get the local installation directory for documentation of a package. +-- @param name string: The package name. +-- @param version string: The package version. +-- @param tree string or nil: If given, specifies the local tree to use. +-- @return string: The resulting path -- does not guarantee that +-- the package (and by extension, the path) exists. +function path.doc_dir(name, version, tree) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(path.rocks_dir(tree), name, version, "doc") +end + +--- Get the local installation directory for configuration files of a package. +-- @param name string: The package name. +-- @param version string: The package version. +-- @param tree string or nil: If given, specifies the local tree to use. +-- @return string: The resulting path -- does not guarantee that +-- the package (and by extension, the path) exists. +function path.conf_dir(name, version, tree) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(path.rocks_dir(tree), name, version, "conf") +end + +--- Get the local installation directory for command-line scripts +-- of a package. +-- @param name string: The package name. +-- @param version string: The package version. +-- @param tree string or nil: If given, specifies the local tree to use. +-- @return string: The resulting path -- does not guarantee that +-- the package (and by extension, the path) exists. +function path.bin_dir(name, version, tree) + assert(type(name) == "string") + assert(type(version) == "string") + tree = tree or cfg.root_dir + return dir.path(path.rocks_dir(tree), name, version, "bin") +end + +--- Extract name, version and arch of a rock filename, +-- or name, version and "rockspec" from a rockspec name. +-- @param file_name string: pathname of a rock or rockspec +-- @return (string, string, string) or nil: name, version and arch +-- or nil if name could not be parsed +function path.parse_name(file_name) + assert(type(file_name) == "string") + if file_name:match("%.rock$") then + return dir.base_name(file_name):match("(.*)-([^-]+-%d+)%.([^.]+)%.rock$") + else + return dir.base_name(file_name):match("(.*)-([^-]+-%d+)%.(rockspec)") + end +end + +--- Make a rockspec or rock URL. +-- @param pathname string: Base URL or pathname. +-- @param name string: Package name. +-- @param version string: Package version. +-- @param arch string: Architecture identifier, or "rockspec" or "installed". +-- @return string: A URL or pathname following LuaRocks naming conventions. +function path.make_url(pathname, name, version, arch) + assert(type(pathname) == "string") + assert(type(name) == "string") + assert(type(version) == "string") + assert(type(arch) == "string") + + local filename = name.."-"..version + if arch == "installed" then + filename = dir.path(name, version, filename..".rockspec") + elseif arch == "rockspec" then + filename = filename..".rockspec" + else + filename = filename.."."..arch..".rock" + end + return dir.path(pathname, filename) +end + +--- Convert a pathname to a module identifier. +-- In Unix, for example, a path "foo/bar/baz.lua" is converted to +-- "foo.bar.baz"; "bla/init.lua" returns "bla"; "foo.so" returns "foo". +-- @param file string: Pathname of module +-- @return string: The module identifier, or nil if given path is +-- not a conformant module path (the function does not check if the +-- path actually exists). +function path.path_to_module(file) + assert(type(file) == "string") + + local name = file:match("(.*)%."..cfg.lua_extension.."$") + if name then + name = name:gsub(dir.separator, ".") + local init = name:match("(.*)%.init$") + if init then + name = init + end + else + name = file:match("(.*)%."..cfg.lib_extension.."$") + if name then + name = name:gsub(dir.separator, ".") + end + end + if not name then name = file end + name = name:gsub("^%.+", ""):gsub("%.+$", "") + return name +end + +--- Obtain the directory name where a module should be stored. +-- For example, on Unix, "foo.bar.baz" will return "foo/bar". +-- @param mod string: A module name in Lua dot-separated format. +-- @return string: A directory name using the platform's separator. +function path.module_to_path(mod) + assert(type(mod) == "string") + return (mod:gsub("[^.]*$", ""):gsub("%.", dir.separator)) +end + +--- Set up path-related variables for a given rock. +-- Create a "variables" table in the rockspec table, containing +-- adjusted variables according to the configuration file. +-- @param rockspec table: The rockspec table. +function path.configure_paths(rockspec) + assert(type(rockspec) == "table") + local vars = {} + for k,v in pairs(cfg.variables) do + vars[k] = v + end + local name, version = rockspec.name, rockspec.version + vars.PREFIX = path.install_dir(name, version) + vars.LUADIR = path.lua_dir(name, version) + vars.LIBDIR = path.lib_dir(name, version) + vars.CONFDIR = path.conf_dir(name, version) + vars.BINDIR = path.bin_dir(name, version) + vars.DOCDIR = path.doc_dir(name, version) + rockspec.variables = vars +end + +--- Produce a versioned version of a filename. +-- @param file string: filename (must start with prefix) +-- @param prefix string: Path prefix for file +-- @param name string: Rock name +-- @param version string: Rock version +-- @return string: a pathname with the same directory parts and a versioned basename. +function path.versioned_name(file, prefix, name, version) + assert(type(file) == "string") + assert(type(name) == "string") + assert(type(version) == "string") + + local rest = file:sub(#prefix+1):gsub("^/*", "") + local name_version = (name.."_"..version):gsub("%-", "_"):gsub("%.", "_") + return dir.path(prefix, name_version.."-"..rest) +end + +function path.use_tree(tree) + cfg.root_dir = tree + cfg.rocks_dir = path.rocks_dir(tree) + cfg.deploy_bin_dir = path.deploy_bin_dir(tree) + cfg.deploy_lua_dir = path.deploy_lua_dir(tree) + cfg.deploy_lib_dir = path.deploy_lib_dir(tree) +end + +--- Apply a given function to the active rocks trees based on chosen dependency mode. +-- @param deps_mode string: Dependency mode: "one" for the current default tree, +-- "all" for all trees, "order" for all trees with priority >= the current default, +-- "none" for no trees (this function becomes a nop). +-- @param fn function: function to be applied, with the tree dir (string) as the first +-- argument and the remaining varargs of map_trees as the following arguments. +-- @return a table with all results of invocations of fn collected. +function path.map_trees(deps_mode, fn, ...) + local result = {} + if deps_mode == "one" then + table.insert(result, (fn(cfg.root_dir, ...)) or 0) + elseif deps_mode == "all" or deps_mode == "order" then + local use = false + if deps_mode == "all" then + use = true + end + for _, tree in ipairs(cfg.rocks_trees) do + if dir.normalize(path.rocks_tree_to_string(tree)) == dir.normalize(path.rocks_tree_to_string(cfg.root_dir)) then + use = true + end + if use then + table.insert(result, (fn(tree, ...)) or 0) + end + end + end + return result +end + +local is_src_extension = { [".lua"] = true, [".tl"] = true, [".tld"] = true, [".moon"] = true } + +--- Return the pathname of the file that would be loaded for a module, indexed. +-- @param file_name string: module file name as in manifest (eg. "socket/core.so") +-- @param name string: name of the package (eg. "luasocket") +-- @param version string: version number (eg. "2.0.2-1") +-- @param tree string: repository path (eg. "/usr/local") +-- @param i number: the index, 1 if version is the current default, > 1 otherwise. +-- This is done this way for use by select_module in luarocks.loader. +-- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so") +function path.which_i(file_name, name, version, tree, i) + local deploy_dir + local extension = file_name:match("%.[a-z]+$") + if is_src_extension[extension] then + deploy_dir = path.deploy_lua_dir(tree) + file_name = dir.path(deploy_dir, file_name) + else + deploy_dir = path.deploy_lib_dir(tree) + file_name = dir.path(deploy_dir, file_name) + end + if i > 1 then + file_name = path.versioned_name(file_name, deploy_dir, name, version) + end + return file_name +end + +--- Return the pathname of the file that would be loaded for a module, +-- returning the versioned pathname if given version is not the default version +-- in the given manifest. +-- @param module_name string: module name (eg. "socket.core") +-- @param file_name string: module file name as in manifest (eg. "socket/core.so") +-- @param name string: name of the package (eg. "luasocket") +-- @param version string: version number (eg. "2.0.2-1") +-- @param tree string: repository path (eg. "/usr/local") +-- @param manifest table: the manifest table for the tree. +-- @return string: filename of the module (eg. "/usr/local/lib/lua/5.1/socket/core.so") +function path.which(module_name, file_name, name, version, tree, manifest) + local versions = manifest.modules[module_name] + assert(versions) + for i, name_version in ipairs(versions) do + if name_version == name.."/"..version then + return path.which_i(file_name, name, version, tree, i):gsub("//", "/") + end + end + assert(false) +end + +return path diff --git a/Utils/luarocks/lua/luarocks/path_cmd.lua b/Utils/luarocks/lua/luarocks/path_cmd.lua new file mode 100644 index 000000000..eba85d46d --- /dev/null +++ b/Utils/luarocks/lua/luarocks/path_cmd.lua @@ -0,0 +1,69 @@ + +--- @module luarocks.path_cmd +-- Driver for the `luarocks path` command. +local path_cmd = {} + +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") + +util.add_run_function(path_cmd) +path_cmd.help_summary = "Return the currently configured package path." +path_cmd.help_arguments = "" +path_cmd.help = [[ +Returns the package path currently configured for this installation +of LuaRocks, formatted as shell commands to update LUA_PATH and LUA_CPATH. + +--bin Adds the system path to the output + +--append Appends the paths to the existing paths. Default is to prefix + the LR paths to the existing paths. + +--lr-path Exports the Lua path (not formatted as shell command) + +--lr-cpath Exports the Lua cpath (not formatted as shell command) + +--lr-bin Exports the system path (not formatted as shell command) + + +On Unix systems, you may run: + eval `luarocks path` +And on Windows: + luarocks path > "%temp%\_lrp.bat" && call "%temp%\_lrp.bat" && del "%temp%\_lrp.bat" +]] + +--- Driver function for "path" command. +-- @return boolean This function always succeeds. +function path_cmd.command(flags) + local lr_path, lr_cpath, lr_bin = cfg.package_paths(flags["tree"]) + local path_sep = cfg.export_path_separator + + if flags["lr-path"] then + util.printout(util.remove_path_dupes(lr_path, ';')) + return true + elseif flags["lr-cpath"] then + util.printout(util.remove_path_dupes(lr_cpath, ';')) + return true + elseif flags["lr-bin"] then + util.printout(util.remove_path_dupes(lr_bin, path_sep)) + return true + end + + if flags["append"] then + lr_path = package.path .. ";" .. lr_path + lr_cpath = package.cpath .. ";" .. lr_cpath + lr_bin = os.getenv("PATH") .. path_sep .. lr_bin + else + lr_path = lr_path.. ";" .. package.path + lr_cpath = lr_cpath .. ";" .. package.cpath + lr_bin = lr_bin .. path_sep .. os.getenv("PATH") + end + + util.printout(cfg.export_lua_path:format(util.remove_path_dupes(lr_path, ';'))) + util.printout(cfg.export_lua_cpath:format(util.remove_path_dupes(lr_cpath, ';'))) + if flags["bin"] then + util.printout(cfg.export_path:format(util.remove_path_dupes(lr_bin, path_sep))) + end + return true +end + +return path_cmd diff --git a/Utils/luarocks/lua/luarocks/persist.lua b/Utils/luarocks/lua/luarocks/persist.lua new file mode 100644 index 000000000..708f07bed --- /dev/null +++ b/Utils/luarocks/lua/luarocks/persist.lua @@ -0,0 +1,210 @@ + +--- Utility module for loading files into tables and +-- saving tables into files. +-- Implemented separately to avoid interdependencies, +-- as it is used in the bootstrapping stage of the cfg module. +local persist = {} +package.loaded["luarocks.persist"] = persist + +local util = require("luarocks.util") + +--- Load and run a Lua file in an environment. +-- @param filename string: the name of the file. +-- @param env table: the environment table. +-- @return (true, any) or (nil, string, string): true and the return value +-- of the file, or nil, an error message and an error code ("open", "load" +-- or "run") in case of errors. +local function run_file(filename, env) + local fd, err = io.open(filename) + if not fd then + return nil, err, "open" + end + local str, err = fd:read("*a") + fd:close() + if not str then + return nil, err, "open" + end + str = str:gsub("^#![^\n]*\n", "") + local chunk, ran + if _VERSION == "Lua 5.1" then -- Lua 5.1 + chunk, err = loadstring(str, filename) + if chunk then + setfenv(chunk, env) + ran, err = pcall(chunk) + end + else -- Lua 5.2 + chunk, err = load(str, filename, "t", env) + if chunk then + ran, err = pcall(chunk) + end + end + if not chunk then + return nil, "Error loading file: "..err, "load" + end + if not ran then + return nil, "Error running file: "..err, "run" + end + return true, err +end + +--- Load a Lua file containing assignments, storing them in a table. +-- The global environment is not propagated to the loaded file. +-- @param filename string: the name of the file. +-- @param tbl table or nil: if given, this table is used to store +-- loaded values. +-- @return (table, table) or (nil, string, string): a table with the file's assignments +-- as fields and set of undefined globals accessed in file, +-- or nil, an error message and an error code ("open"; couldn't open the file, +-- "load"; compile-time error, or "run"; run-time error) +-- in case of errors. +function persist.load_into_table(filename, tbl) + assert(type(filename) == "string") + assert(type(tbl) == "table" or not tbl) + + local result = tbl or {} + local globals = {} + local globals_mt = { + __index = function(t, k) + globals[k] = true + end + } + local save_mt = getmetatable(result) + setmetatable(result, globals_mt) + + local ok, err, errcode = run_file(filename, result) + + setmetatable(result, save_mt) + + if not ok then + return nil, err, errcode + end + return result, globals +end + +local write_table + +--- Write a value as Lua code. +-- This function handles only numbers and strings, invoking write_table +-- to write tables. +-- @param out table or userdata: a writer object supporting :write() method. +-- @param v: the value to be written. +-- @param level number: the indentation level +-- @param sub_order table: optional prioritization table +-- @see write_table +local function write_value(out, v, level, sub_order) + if type(v) == "table" then + write_table(out, v, level + 1, sub_order) + elseif type(v) == "string" then + if v:match("[\r\n]") then + local open, close = "[[", "]]" + local equals = 0 + local v_with_bracket = v.."]" + while v_with_bracket:find(close, 1, true) do + equals = equals + 1 + local eqs = ("="):rep(equals) + open, close = "["..eqs.."[", "]"..eqs.."]" + end + out:write(open.."\n"..v..close) + else + out:write(("%q"):format(v)) + end + else + out:write(tostring(v)) + end +end + +--- Write a table as Lua code in curly brackets notation to a writer object. +-- Only numbers, strings and tables (containing numbers, strings +-- or other recursively processed tables) are supported. +-- @param out table or userdata: a writer object supporting :write() method. +-- @param tbl table: the table to be written. +-- @param level number: the indentation level +-- @param field_order table: optional prioritization table +write_table = function(out, tbl, level, field_order) + out:write("{") + local sep = "\n" + local indentation = " " + local indent = true + local i = 1 + for k, v, sub_order in util.sortedpairs(tbl, field_order) do + out:write(sep) + if indent then + for n = 1,level do out:write(indentation) end + end + + if k == i then + i = i + 1 + else + if type(k) == "string" and k:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then + out:write(k) + else + out:write("[") + write_value(out, k, level) + out:write("]") + end + + out:write(" = ") + end + + write_value(out, v, level, sub_order) + if type(v) == "number" then + sep = ", " + indent = false + else + sep = ",\n" + indent = true + end + end + if sep ~= "\n" then + out:write("\n") + for n = 1,level-1 do out:write(indentation) end + end + out:write("}") +end + +--- Write a table as series of assignments to a writer object. +-- @param out table or userdata: a writer object supporting :write() method. +-- @param tbl table: the table to be written. +-- @param field_order table: optional prioritization table +local function write_table_as_assignments(out, tbl, field_order) + for k, v, sub_order in util.sortedpairs(tbl, field_order) do + out:write(k.." = ") + write_value(out, v, 0, sub_order) + out:write("\n") + end +end + +--- Save the contents of a table to a string. +-- Each element of the table is saved as a global assignment. +-- Only numbers, strings and tables (containing numbers, strings +-- or other recursively processed tables) are supported. +-- @param tbl table: the table containing the data to be written +-- @param field_order table: an optional array indicating the order of top-level fields. +-- @return string +function persist.save_from_table_to_string(tbl, field_order) + local out = {buffer = {}} + function out:write(data) table.insert(self.buffer, data) end + write_table_as_assignments(out, tbl, field_order) + return table.concat(out.buffer) +end + +--- Save the contents of a table in a file. +-- Each element of the table is saved as a global assignment. +-- Only numbers, strings and tables (containing numbers, strings +-- or other recursively processed tables) are supported. +-- @param filename string: the output filename +-- @param tbl table: the table containing the data to be written +-- @param field_order table: an optional array indicating the order of top-level fields. +-- @return boolean or (nil, string): true if successful, or nil and a +-- message in case of errors. +function persist.save_from_table(filename, tbl, field_order) + local out = io.open(filename, "w") + if not out then + return nil, "Cannot create file at "..filename + end + write_table_as_assignments(out, tbl, field_order) + out:close() + return true +end + +return persist diff --git a/Utils/luarocks/lua/luarocks/purge.lua b/Utils/luarocks/lua/luarocks/purge.lua new file mode 100644 index 000000000..17724e847 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/purge.lua @@ -0,0 +1,79 @@ + +--- Module implementing the LuaRocks "purge" command. +-- Remove all rocks from a given tree. +local purge = {} +package.loaded["luarocks.purge"] = purge + +local util = require("luarocks.util") +local fs = require("luarocks.fs") +local path = require("luarocks.path") +local search = require("luarocks.search") +local deps = require("luarocks.deps") +local repos = require("luarocks.repos") +local manif = require("luarocks.manif") +local cfg = require("luarocks.cfg") +local remove = require("luarocks.remove") + +util.add_run_function(purge) +purge.help_summary = "Remove all installed rocks from a tree." +purge.help_arguments = "--tree= [--old-versions]" +purge.help = [[ +This command removes rocks en masse from a given tree. +By default, it removes all rocks from a tree. + +The --tree argument is mandatory: luarocks purge does not +assume a default tree. + +--old-versions Keep the highest-numbered version of each + rock and remove the other ones. By default + it only removes old versions if they are + not needed as dependencies. This can be + overridden with the flag --force. +]] + +function purge.command(flags) + local tree = flags["tree"] + + if type(tree) ~= "string" then + return nil, "The --tree argument is mandatory. "..util.see_help("purge") + end + + local results = {} + local query = search.make_query("") + query.exact_name = false + if not fs.is_dir(tree) then + return nil, "Directory not found: "..tree + end + + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + + search.manifest_search(results, path.rocks_dir(tree), query) + + local sort = function(a,b) return deps.compare_versions(b,a) end + if flags["old-versions"] then + sort = deps.compare_versions + end + + for package, versions in util.sortedpairs(results) do + for version, repositories in util.sortedpairs(versions, sort) do + if flags["old-versions"] then + util.printout("Keeping "..package.." "..version.."...") + local ok, err = remove.remove_other_versions(package, version, flags["force"], flags["force-fast"]) + if not ok then + util.printerr(err) + end + break + else + util.printout("Removing "..package.." "..version.."...") + local ok, err = repos.delete_version(package, version, "none", true) + if not ok then + util.printerr(err) + end + end + end + end + return manif.make_manifest(cfg.rocks_dir, "one") +end + +return purge diff --git a/Utils/luarocks/lua/luarocks/refresh_cache.lua b/Utils/luarocks/lua/luarocks/refresh_cache.lua new file mode 100644 index 000000000..bbfd1f4d8 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/refresh_cache.lua @@ -0,0 +1,33 @@ + +--- Module implementing the luarocks-admin "refresh_cache" command. +local refresh_cache = {} +package.loaded["luarocks.refresh_cache"] = refresh_cache + +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") +local cache = require("luarocks.cache") + +util.add_run_function(refresh_cache) +refresh_cache.help_summary = "Refresh local cache of a remote rocks server." +refresh_cache.help_arguments = "[--from=]" +refresh_cache.help = [[ +The flag --from indicates which server to use. +If not given, the default server set in the upload_server variable +from the configuration file is used instead. +]] + +function refresh_cache.command(flags) + local server, upload_server = cache.get_upload_server(flags["server"]) + if not server then return nil, upload_server end + local download_url = cache.get_server_urls(server, upload_server) + + local ok, err = cache.refresh_local_cache(server, download_url, cfg.upload_user, cfg.upload_password) + if not ok then + return nil, err + else + return true + end +end + + +return refresh_cache diff --git a/Utils/luarocks/lua/luarocks/remove.lua b/Utils/luarocks/lua/luarocks/remove.lua new file mode 100644 index 000000000..514c6dfa1 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/remove.lua @@ -0,0 +1,174 @@ + +--- Module implementing the LuaRocks "remove" command. +-- Uninstalls rocks. +local remove = {} +package.loaded["luarocks.remove"] = remove + +local search = require("luarocks.search") +local deps = require("luarocks.deps") +local fetch = require("luarocks.fetch") +local repos = require("luarocks.repos") +local path = require("luarocks.path") +local util = require("luarocks.util") +local cfg = require("luarocks.cfg") +local fs = require("luarocks.fs") +local manif = require("luarocks.manif") + +util.add_run_function(remove) +remove.help_summary = "Uninstall a rock." +remove.help_arguments = "[--force|--force-fast] []" +remove.help = [[ +Argument is the name of a rock to be uninstalled. +If a version is not given, try to remove all versions at once. +Will only perform the removal if it does not break dependencies. +To override this check and force the removal, use --force. +To perform a forced removal without reporting dependency issues, +use --force-fast. + +]]..util.deps_mode_help() + +--- Obtain a list of packages that depend on the given set of packages +-- (where all packages of the set are versions of one program). +-- @param name string: the name of a program +-- @param versions array of string: the versions to be deleted. +-- @return array of string: an empty table if no packages depend on any +-- of the given list, or an array of strings in "name/version" format. +local function check_dependents(name, versions, deps_mode) + local dependents = {} + local blacklist = {} + blacklist[name] = {} + for version, _ in pairs(versions) do + blacklist[name][version] = true + end + local local_rocks = {} + local query_all = search.make_query("") + query_all.exact_name = false + search.manifest_search(local_rocks, cfg.rocks_dir, query_all) + local_rocks[name] = nil + for rock_name, rock_versions in pairs(local_rocks) do + for rock_version, _ in pairs(rock_versions) do + local rockspec, err = fetch.load_rockspec(path.rockspec_file(rock_name, rock_version)) + if rockspec then + local _, missing = deps.match_deps(rockspec, blacklist, deps_mode) + if missing[name] then + table.insert(dependents, { name = rock_name, version = rock_version }) + end + end + end + end + return dependents +end + +--- Delete given versions of a program. +-- @param name string: the name of a program +-- @param versions array of string: the versions to be deleted. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @return boolean or (nil, string): true on success or nil and an error message. +local function delete_versions(name, versions, deps_mode) + + for version, _ in pairs(versions) do + util.printout("Removing "..name.." "..version.."...") + local ok, err = repos.delete_version(name, version, deps_mode) + if not ok then return nil, err end + end + + return true +end + +function remove.remove_search_results(results, name, deps_mode, force, fast) + local versions = results[name] + + local version = next(versions) + local second = next(versions, version) + + local dependents = {} + if not fast then + util.printout("Checking stability of dependencies in the absence of") + util.printout(name.." "..table.concat(util.keys(versions), ", ").."...") + util.printout() + dependents = check_dependents(name, versions, deps_mode) + end + + if #dependents > 0 then + if force or fast then + util.printerr("The following packages may be broken by this forced removal:") + for _, dependent in ipairs(dependents) do + util.printerr(dependent.name.." "..dependent.version) + end + util.printerr() + else + if not second then + util.printerr("Will not remove "..name.." "..version..".") + util.printerr("Removing it would break dependencies for: ") + else + util.printerr("Will not remove installed versions of "..name..".") + util.printerr("Removing them would break dependencies for: ") + end + for _, dependent in ipairs(dependents) do + util.printerr(dependent.name.." "..dependent.version) + end + util.printerr() + util.printerr("Use --force to force removal (warning: this may break modules).") + return nil, "Failed removing." + end + end + + local ok, err = delete_versions(name, versions, deps_mode) + if not ok then return nil, err end + + util.printout("Removal successful.") + return true +end + +function remove.remove_other_versions(name, version, force, fast) + local results = {} + search.manifest_search(results, cfg.rocks_dir, { name = name, exact_name = true, constraints = {{ op = "~=", version = version}} }) + if results[name] then + return remove.remove_search_results(results, name, cfg.deps_mode, force, fast) + end + return true +end + +--- Driver function for the "remove" command. +-- @param name string: name of a rock. If a version is given, refer to +-- a specific version; otherwise, try to remove all versions. +-- @param version string: When passing a package name, a version number +-- may also be given. +-- @return boolean or (nil, string, exitcode): True if removal was +-- successful, nil and an error message otherwise. exitcode is optionally returned. +function remove.command(flags, name, version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("remove") + end + + local deps_mode = flags["deps-mode"] or cfg.deps_mode + + local ok, err = fs.check_command_permissions(flags) + if not ok then return nil, err, cfg.errorcodes.PERMISSIONDENIED end + + local rock_type = name:match("%.(rock)$") or name:match("%.(rockspec)$") + local filename = name + if rock_type then + name, version = path.parse_name(filename) + if not name then return nil, "Invalid "..rock_type.." filename: "..filename end + end + + local results = {} + name = name:lower() + search.manifest_search(results, cfg.rocks_dir, search.make_query(name, version)) + if not results[name] then + return nil, "Could not find rock '"..name..(version and " "..version or "").."' in "..path.rocks_tree_to_string(cfg.root_dir) + end + + local ok, err = remove.remove_search_results(results, name, deps_mode, flags["force"], flags["force-fast"]) + if not ok then + return nil, err + end + + manif.check_dependencies(nil, deps.get_deps_mode(flags)) + return true +end + +return remove diff --git a/Utils/luarocks/lua/luarocks/repos.lua b/Utils/luarocks/lua/luarocks/repos.lua new file mode 100644 index 000000000..5d5eac708 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/repos.lua @@ -0,0 +1,422 @@ + +--- Functions for managing the repository on disk. +local repos = {} +package.loaded["luarocks.repos"] = repos + +local fs = require("luarocks.fs") +local path = require("luarocks.path") +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") +local dir = require("luarocks.dir") +local manif = require("luarocks.manif") +local deps = require("luarocks.deps") + +-- Tree of files installed by a package are stored +-- in its rock manifest. Some of these files have to +-- be deployed to locations where Lua can load them as +-- modules or where they can be used as commands. +-- These files are characterised by pair +-- (deploy_type, file_path), where deploy_type is the first +-- component of the file path and file_path is the rest of the +-- path. Only files with deploy_type in {"lua", "lib", "bin"} +-- are deployed somewhere. +-- Each deployed file provides an "item". An item is +-- characterised by pair (item_type, item_name). +-- item_type is "command" for files with deploy_type +-- "bin" and "module" for deploy_type in {"lua", "lib"}. +-- item_name is same as file_path for commands +-- and is produced using path.path_to_module(file_path) +-- for modules. + +--- Get all installed versions of a package. +-- @param name string: a package name. +-- @return table or nil: An array of strings listing installed +-- versions of a package, or nil if none is available. +local function get_installed_versions(name) + assert(type(name) == "string") + + local dirs = fs.list_dir(path.versions_dir(name)) + return (dirs and #dirs > 0) and dirs or nil +end + +--- Check if a package exists in a local repository. +-- Version numbers are compared as exact string comparison. +-- @param name string: name of package +-- @param version string: package version in string format +-- @return boolean: true if a package is installed, +-- false otherwise. +function repos.is_installed(name, version) + assert(type(name) == "string") + assert(type(version) == "string") + + return fs.is_dir(path.install_dir(name, version)) +end + +local function recurse_rock_manifest_tree(file_tree, action) + assert(type(file_tree) == "table") + assert(type(action) == "function") + local function do_recurse_rock_manifest_tree(tree, parent_path, parent_module) + + for file, sub in pairs(tree) do + if type(sub) == "table" then + local ok, err = do_recurse_rock_manifest_tree(sub, parent_path..file.."/", parent_module..file..".") + if not ok then return nil, err end + else + local ok, err = action(parent_path, parent_module, file) + if not ok then return nil, err end + end + end + return true + end + return do_recurse_rock_manifest_tree(file_tree, "", "") +end + +local function store_package_data(result, name, file_tree) + if not file_tree then return end + return recurse_rock_manifest_tree(file_tree, + function(parent_path, parent_module, file) + local pathname = parent_path..file + result[path.path_to_module(pathname)] = pathname + return true + end + ) +end + +--- Obtain a list of modules within an installed package. +-- @param package string: The package name; for example "luasocket" +-- @param version string: The exact version number including revision; +-- for example "2.0.1-1". +-- @return table: A table of modules where keys are module identifiers +-- in "foo.bar" format and values are pathnames in architecture-dependent +-- "foo/bar.so" format. If no modules are found or if package or version +-- are invalid, an empty table is returned. +function repos.package_modules(package, version) + assert(type(package) == "string") + assert(type(version) == "string") + + local result = {} + local rock_manifest = manif.load_rock_manifest(package, version) + store_package_data(result, package, rock_manifest.lib) + store_package_data(result, package, rock_manifest.lua) + return result +end + +--- Obtain a list of command-line scripts within an installed package. +-- @param package string: The package name; for example "luasocket" +-- @param version string: The exact version number including revision; +-- for example "2.0.1-1". +-- @return table: A table of items where keys are command names +-- as strings and values are pathnames in architecture-dependent +-- ".../bin/foo" format. If no modules are found or if package or version +-- are invalid, an empty table is returned. +function repos.package_commands(package, version) + assert(type(package) == "string") + assert(type(version) == "string") + + local result = {} + local rock_manifest = manif.load_rock_manifest(package, version) + store_package_data(result, package, rock_manifest.bin) + return result +end + + +--- Check if a rock contains binary executables. +-- @param name string: name of an installed rock +-- @param version string: version of an installed rock +-- @return boolean: returns true if rock contains platform-specific +-- binary executables, or false if it is a pure-Lua rock. +function repos.has_binaries(name, version) + assert(type(name) == "string") + assert(type(version) == "string") + + local rock_manifest = manif.load_rock_manifest(name, version) + if rock_manifest.bin then + for name, md5 in pairs(rock_manifest.bin) do + -- TODO verify that it is the same file. If it isn't, find the actual command. + if fs.is_actual_binary(dir.path(cfg.deploy_bin_dir, name)) then + return true + end + end + end + return false +end + +function repos.run_hook(rockspec, hook_name) + assert(type(rockspec) == "table") + assert(type(hook_name) == "string") + + local hooks = rockspec.hooks + if not hooks then + return true + end + + if cfg.hooks_enabled == false then + return nil, "This rockspec contains hooks, which are blocked by the 'hooks_enabled' setting in your LuaRocks configuration." + end + + if not hooks.substituted_variables then + util.variable_substitutions(hooks, rockspec.variables) + hooks.substituted_variables = true + end + local hook = hooks[hook_name] + if hook then + util.printout(hook) + if not fs.execute(hook) then + return nil, "Failed running "..hook_name.." hook." + end + end + return true +end + +function repos.should_wrap_bin_scripts(rockspec) + assert(type(rockspec) == "table") + + if cfg.wrap_bin_scripts ~= nil then + return cfg.wrap_bin_scripts + end + if rockspec.deploy and rockspec.deploy.wrap_bin_scripts == false then + return false + end + return true +end + +local function find_suffixed(file, suffix) + local filenames = {file} + if suffix and suffix ~= "" then + table.insert(filenames, 1, file .. suffix) + end + + for _, filename in ipairs(filenames) do + if fs.exists(filename) then + return filename + end + end + + return nil, table.concat(filenames, ", ") .. " not found" +end + +local function move_suffixed(from_file, to_file, suffix) + local suffixed_from_file, err = find_suffixed(from_file, suffix) + if not suffixed_from_file then + return nil, "Could not move " .. from_file .. " to " .. to_file .. ": " .. err + end + + suffix = suffixed_from_file:sub(#from_file + 1) + local suffixed_to_file = to_file .. suffix + return fs.move(suffixed_from_file, suffixed_to_file) +end + +local function delete_suffixed(file, suffix) + local suffixed_file, err = find_suffixed(file, suffix) + if not suffixed_file then + return nil, "Could not remove " .. file .. ": " .. err + end + + fs.delete(suffixed_file) + if fs.exists(suffixed_file) then + return nil, "Failed deleting " .. suffixed_file .. ": file still exists" + end + + return true +end + +-- Files can be deployed using versioned and non-versioned names. +-- Several items with same type and name can exist if they are +-- provided by different packages or versions. In any case +-- item from the newest version of lexicographically smallest package +-- is deployed using non-versioned name and others use versioned names. + +local function get_deploy_paths(name, version, deploy_type, file_path) + local deploy_dir = cfg["deploy_" .. deploy_type .. "_dir"] + local non_versioned = dir.path(deploy_dir, file_path) + local versioned = path.versioned_name(non_versioned, deploy_dir, name, version) + return non_versioned, versioned +end + +local function prepare_target(name, version, deploy_type, file_path, suffix) + local non_versioned, versioned = get_deploy_paths(name, version, deploy_type, file_path) + local item_type, item_name = manif.get_provided_item(deploy_type, file_path) + local cur_name, cur_version = manif.get_current_provider(item_type, item_name) + + if not cur_name then + return non_versioned + elseif name < cur_name or (name == cur_name and deps.compare_versions(version, cur_version)) then + -- New version has priority. Move currently provided version back using versioned name. + local cur_deploy_type, cur_file_path = manif.get_providing_file(cur_name, cur_version, item_type, item_name) + local cur_non_versioned, cur_versioned = get_deploy_paths(cur_name, cur_version, cur_deploy_type, cur_file_path) + + local dir_ok, dir_err = fs.make_dir(dir.dir_name(cur_versioned)) + if not dir_ok then return nil, dir_err end + + local move_ok, move_err = move_suffixed(cur_non_versioned, cur_versioned, suffix) + if not move_ok then return nil, move_err end + + return non_versioned + else + -- Current version has priority, deploy new version using versioned name. + return versioned + end +end + +--- Deploy a package from the rocks subdirectory. +-- @param name string: name of package +-- @param version string: exact package version in string format +-- @param wrap_bin_scripts bool: whether commands written in Lua should be wrapped. +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode) + assert(type(name) == "string") + assert(type(version) == "string") + assert(type(wrap_bin_scripts) == "boolean") + + local rock_manifest = manif.load_rock_manifest(name, version) + + local function deploy_file_tree(deploy_type, source_dir, move_fn, suffix) + if not rock_manifest[deploy_type] then + return true + end + + return recurse_rock_manifest_tree(rock_manifest[deploy_type], function(parent_path, parent_module, file) + local file_path = parent_path .. file + local source = dir.path(source_dir, file_path) + + local target, prepare_err = prepare_target(name, version, deploy_type, file_path, suffix) + if not target then return nil, prepare_err end + + local dir_ok, dir_err = fs.make_dir(dir.dir_name(target)) + if not dir_ok then return nil, dir_err end + + local suffixed_target, mover = move_fn(source, target) + if fs.exists(suffixed_target) then + local backup = suffixed_target + repeat + backup = backup.."~" + until not fs.exists(backup) -- Slight race condition here, but shouldn't be a problem. + + util.printerr("Warning: "..suffixed_target.." is not tracked by this installation of LuaRocks. Moving it to "..backup) + local move_ok, move_err = fs.move(suffixed_target, backup) + if not move_ok then return nil, move_err end + end + + local move_ok, move_err = mover() + if not move_ok then return nil, move_err end + + fs.remove_dir_tree_if_empty(dir.dir_name(source)) + return true + end) + end + + local function install_binary(source, target) + if wrap_bin_scripts and fs.is_lua(source) then + return target .. (cfg.wrapper_suffix or ""), function() return fs.wrap_script(source, target, name, version) end + else + return target, function() return fs.copy_binary(source, target) end + end + end + + local function make_mover(perms) + return function(source, target) + return target, function() return fs.move(source, target, perms) end + end + end + + local ok, err = deploy_file_tree("bin", path.bin_dir(name, version), install_binary, cfg.wrapper_suffix) + if not ok then return nil, err end + + ok, err = deploy_file_tree("lua", path.lua_dir(name, version), make_mover(cfg.perm_read)) + if not ok then return nil, err end + + ok, err = deploy_file_tree("lib", path.lib_dir(name, version), make_mover(cfg.perm_exec)) + if not ok then return nil, err end + + return manif.add_to_manifest(name, version, nil, deps_mode) +end + +--- Delete a package from the local repository. +-- @param name string: name of package +-- @param version string: exact package version in string format +-- @param deps_mode: string: Which trees to check dependencies for: +-- "one" for the current default tree, "all" for all trees, +-- "order" for all trees with priority >= the current default, "none" for no trees. +-- @param quick boolean: do not try to fix the versioned name +-- of another version that provides the same module that +-- was deleted. This is used during 'purge', as every module +-- will be eventually deleted. +function repos.delete_version(name, version, deps_mode, quick) + assert(type(name) == "string") + assert(type(version) == "string") + assert(type(deps_mode) == "string") + + local rock_manifest = manif.load_rock_manifest(name, version) + if not rock_manifest then + return nil, "rock_manifest file not found for "..name.." "..version.." - not a LuaRocks 2 tree?" + end + + local function delete_deployed_file_tree(deploy_type, suffix) + if not rock_manifest[deploy_type] then + return true + end + + return recurse_rock_manifest_tree(rock_manifest[deploy_type], function(parent_path, parent_module, file) + local file_path = parent_path .. file + local non_versioned, versioned = get_deploy_paths(name, version, deploy_type, file_path) + + -- Figure out if the file is deployed using versioned or non-versioned name. + local target + local item_type, item_name = manif.get_provided_item(deploy_type, file_path) + local cur_name, cur_version = manif.get_current_provider(item_type, item_name) + + if cur_name == name and cur_version == version then + -- This package has highest priority, should be in non-versioned location. + target = non_versioned + else + target = versioned + end + + local ok, err = delete_suffixed(target, suffix) + if not ok then return nil, err end + + if not quick and target == non_versioned then + -- If another package provides this file, move its version + -- into non-versioned location instead. + local next_name, next_version = manif.get_next_provider(item_type, item_name) + + if next_name then + local next_deploy_type, next_file_path = manif.get_providing_file(next_name, next_version, item_type, item_name) + local next_non_versioned, next_versioned = get_deploy_paths(next_name, next_version, next_deploy_type, next_file_path) + + local move_ok, move_err = move_suffixed(next_versioned, next_non_versioned, suffix) + if not move_ok then return nil, move_err end + + fs.remove_dir_tree_if_empty(dir.dir_name(next_versioned)) + end + end + + fs.remove_dir_tree_if_empty(dir.dir_name(target)) + return true + end) + end + + local ok, err = delete_deployed_file_tree("bin", cfg.wrapper_suffix) + if not ok then return nil, err end + + ok, err = delete_deployed_file_tree("lua") + if not ok then return nil, err end + + ok, err = delete_deployed_file_tree("lib") + if not ok then return nil, err end + + fs.delete(path.install_dir(name, version)) + if not get_installed_versions(name) then + fs.delete(dir.path(cfg.rocks_dir, name)) + end + + if quick then + return true + end + + return manif.remove_from_manifest(name, version, nil, deps_mode) +end + +return repos diff --git a/Utils/luarocks/lua/luarocks/require.lua b/Utils/luarocks/lua/luarocks/require.lua new file mode 100644 index 000000000..902bd1a3c --- /dev/null +++ b/Utils/luarocks/lua/luarocks/require.lua @@ -0,0 +1,2 @@ +--- Retained for compatibility reasons only. Use luarocks.loader instead. +return require("luarocks.loader") diff --git a/Utils/luarocks/lua/luarocks/search.lua b/Utils/luarocks/lua/luarocks/search.lua new file mode 100644 index 000000000..d22c2a185 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/search.lua @@ -0,0 +1,483 @@ + +--- Module implementing the LuaRocks "search" command. +-- Queries LuaRocks servers. +local search = {} +package.loaded["luarocks.search"] = search + +local dir = require("luarocks.dir") +local path = require("luarocks.path") +local manif = require("luarocks.manif") +local deps = require("luarocks.deps") +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") + +util.add_run_function(search) +search.help_summary = "Query the LuaRocks servers." +search.help_arguments = "[--source] [--binary] { [] | --all }" +search.help = [[ +--source Return only rockspecs and source rocks, + to be used with the "build" command. +--binary Return only pure Lua and binary rocks (rocks that can be used + with the "install" command without requiring a C toolchain). +--all List all contents of the server that are suitable to + this platform, do not filter by name. +]] + +--- Convert the arch field of a query table to table format. +-- @param query table: A query table. +local function query_arch_as_table(query) + local format = type(query.arch) + if format == "table" then + return + elseif format == "nil" then + local accept = {} + accept["src"] = true + accept["all"] = true + accept["rockspec"] = true + accept["installed"] = true + accept[cfg.arch] = true + query.arch = accept + elseif format == "string" then + local accept = {} + for a in query.arch:gmatch("[%w_-]+") do + accept[a] = true + end + query.arch = accept + end +end + +--- Store a search result (a rock or rockspec) in the results table. +-- @param results table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- @param name string: Package name. +-- @param version string: Package version. +-- @param arch string: Architecture of rock ("all", "src" or platform +-- identifier), "rockspec" or "installed" +-- @param repo string: Pathname of a local repository of URL of +-- rocks server. +local function store_result(results, name, version, arch, repo) + assert(type(results) == "table") + assert(type(name) == "string") + assert(type(version) == "string") + assert(type(arch) == "string") + assert(type(repo) == "string") + + if not results[name] then results[name] = {} end + if not results[name][version] then results[name][version] = {} end + table.insert(results[name][version], { + arch = arch, + repo = repo + }) +end + +--- Test the name field of a query. +-- If query has a boolean field exact_name set to false, +-- then substring match is performed; otherwise, exact string +-- comparison is done. +-- @param query table: A query in dependency table format. +-- @param name string: A package name. +-- @return boolean: True if names match, false otherwise. +local function match_name(query, name) + assert(type(query) == "table") + assert(type(name) == "string") + if query.exact_name == false then + return name:find(query.name, 0, true) and true or false + else + return name == query.name + end +end + +--- Store a match in a results table if version matches query. +-- Name, version, arch and repository path are stored in a given +-- table, optionally checking if version and arch (if given) match +-- a query. +-- @param results table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- @param repo string: URL or pathname of the repository. +-- @param name string: The name of the package being tested. +-- @param version string: The version of the package being tested. +-- @param arch string: The arch of the package being tested. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +local function store_if_match(results, repo, name, version, arch, query) + if match_name(query, name) then + if query.arch[arch] or query.arch["any"] then + if deps.match_constraints(deps.parse_version(version), query.constraints) then + store_result(results, name, version, arch, repo) + end + end + end +end + +--- Perform search on a local repository. +-- @param repo string: The pathname of the local repository. +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +-- @param results table or nil: If given, this table will store the +-- results; if not given, a new table will be created. +-- @return table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- If a table was given in the "results" parameter, that is the result value. +function search.disk_search(repo, query, results) + assert(type(repo) == "string") + assert(type(query) == "table") + assert(type(results) == "table" or not results) + + local fs = require("luarocks.fs") + + if not results then + results = {} + end + query_arch_as_table(query) + + for name in fs.dir(repo) do + local pathname = dir.path(repo, name) + local rname, rversion, rarch = path.parse_name(name) + + if rname and (pathname:match(".rockspec$") or pathname:match(".rock$")) then + store_if_match(results, repo, rname, rversion, rarch, query) + elseif fs.is_dir(pathname) then + for version in fs.dir(pathname) do + if version:match("-%d+$") then + store_if_match(results, repo, name, version, "installed", query) + end + end + end + end + return results +end + +--- Perform search on a rocks server or tree. +-- @param results table: The results table, where keys are package names and +-- values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +-- @param repo string: The URL of a rocks server or +-- the pathname of a rocks tree (as returned by path.rocks_dir()). +-- @param query table: A table describing the query in dependency +-- format (for example, {name = "filesystem", exact_name = false, +-- constraints = {op = "~>", version = {1,0}}}, arch = "rockspec"). +-- If the arch field is omitted, the local architecture (cfg.arch) +-- is used. The special value "any" is also recognized, returning all +-- matches regardless of architecture. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @return true or, in case of errors, nil, an error message and an optional error code. +function search.manifest_search(results, repo, query, lua_version) + assert(type(results) == "table") + assert(type(repo) == "string") + assert(type(query) == "table") + + query_arch_as_table(query) + local manifest, err, errcode = manif.load_manifest(repo, lua_version) + if not manifest then + return nil, err, errcode + end + for name, versions in pairs(manifest.repository) do + for version, items in pairs(versions) do + for _, item in ipairs(items) do + store_if_match(results, repo, name, version, item.arch, query) + end + end + end + return true +end + +--- Search on all configured rocks servers. +-- @param query table: A dependency query. +-- @param lua_version string: Lua version in "5.x" format, defaults to installed version. +-- @return table: A table where keys are package names +-- and values are tables matching version strings to arrays of +-- tables with fields "arch" and "repo". +function search.search_repos(query, lua_version) + assert(type(query) == "table") + + local results = {} + for _, repo in ipairs(cfg.rocks_servers) do + if not cfg.disabled_servers[repo] then + if type(repo) == "string" then + repo = { repo } + end + for _, mirror in ipairs(repo) do + local protocol, pathname = dir.split_url(mirror) + if protocol == "file" then + mirror = pathname + end + local ok, err, errcode = search.manifest_search(results, mirror, query, lua_version) + if errcode == "network" then + cfg.disabled_servers[repo] = true + end + if ok then + break + else + util.warning("Failed searching manifest: "..err) + end + end + end + end + -- search through rocks in cfg.rocks_provided + local provided_repo = "provided by VM or rocks_provided" + for name, versions in pairs(cfg.rocks_provided) do + store_if_match(results, provided_repo, name, versions, "installed", query) + end + return results +end + +--- Prepare a query in dependency table format. +-- @param name string: The query name. +-- @param version string or nil: +-- @return table: A query in table format +function search.make_query(name, version) + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local query = { + name = name, + constraints = {} + } + if version then + table.insert(query.constraints, { op = "==", version = deps.parse_version(version)}) + end + return query +end + +--- Get the URL for the latest in a set of versions. +-- @param name string: The package name to be used in the URL. +-- @param versions table: An array of version informations, as stored +-- in search results tables. +-- @return string or nil: the URL for the latest version if one could +-- be picked, or nil. +local function pick_latest_version(name, versions) + assert(type(name) == "string") + assert(type(versions) == "table") + + local vtables = {} + for v, _ in pairs(versions) do + table.insert(vtables, deps.parse_version(v)) + end + table.sort(vtables) + local version = vtables[#vtables].string + local items = versions[version] + if items then + local pick = 1 + for i, item in ipairs(items) do + if (item.arch == 'src' and items[pick].arch == 'rockspec') + or (item.arch ~= 'src' and item.arch ~= 'rockspec') then + pick = i + end + end + return path.make_url(items[pick].repo, name, version, items[pick].arch) + end + return nil +end + +-- Find out which other Lua versions provide rock versions matching a query, +-- @param query table: A dependency query matching a single rock. +-- @return table: array of Lua versions supported, in "5.x" format. +local function supported_lua_versions(query) + local results = {} + + for lua_version in util.lua_versions() do + if lua_version ~= cfg.lua_version then + if search.search_repos(query, lua_version)[query.name] then + table.insert(results, lua_version) + end + end + end + + return results +end + +--- Attempt to get a single URL for a given search for a rock. +-- @param query table: A dependency query matching a single rock. +-- @return string or (nil, string): URL for latest matching version +-- of the rock if it was found, or nil followed by an error message. +function search.find_suitable_rock(query) + assert(type(query) == "table") + + local results = search.search_repos(query) + local first_rock = next(results) + if not first_rock then + if cfg.rocks_provided[query.name] == nil then + -- Check if constraints are satisfiable with other Lua versions. + local lua_versions = supported_lua_versions(query) + + if #lua_versions ~= 0 then + -- Build a nice message in "only Lua 5.x and 5.y but not 5.z." format + for i, lua_version in ipairs(lua_versions) do + lua_versions[i] = "Lua "..lua_version + end + + local versions_message = "only "..table.concat(lua_versions, " and ").. + " but not Lua "..cfg.lua_version.."." + + if #query.constraints == 0 then + return nil, query.name.." supports "..versions_message + elseif #query.constraints == 1 and query.constraints[1].op == "==" then + return nil, query.name.." "..query.constraints[1].version.string.." supports "..versions_message + else + return nil, "Matching "..query.name.." versions support "..versions_message + end + end + end + + return nil, "No results matching query were found." + elseif next(results, first_rock) then + -- Shouldn't happen as query must match only one package. + return nil, "Several rocks matched query." + elseif cfg.rocks_provided[query.name] ~= nil then + -- Do not install versions listed in cfg.rocks_provided. + return nil, "Rock "..query.name.." "..cfg.rocks_provided[query.name].. + " was found but it is provided by VM or 'rocks_provided' in the config file." + else + return pick_latest_version(query.name, results[first_rock]) + end +end + +--- Print a list of rocks/rockspecs on standard output. +-- @param results table: A table where keys are package names and versions +-- are tables matching version strings to an array of rocks servers. +-- @param porcelain boolean or nil: A flag to force machine-friendly output. +function search.print_results(results, porcelain) + assert(type(results) == "table") + assert(type(porcelain) == "boolean" or not porcelain) + + for package, versions in util.sortedpairs(results) do + if not porcelain then + util.printout(package) + end + for version, repos in util.sortedpairs(versions, deps.compare_versions) do + for _, repo in ipairs(repos) do + repo.repo = dir.normalize(repo.repo) + if porcelain then + util.printout(package, version, repo.arch, repo.repo) + else + util.printout(" "..version.." ("..repo.arch..") - "..repo.repo) + end + end + end + if not porcelain then + util.printout() + end + end +end + +--- Splits a list of search results into two lists, one for "source" results +-- to be used with the "build" command, and one for "binary" results to be +-- used with the "install" command. +-- @param results table: A search results table. +-- @return (table, table): Two tables, one for source and one for binary +-- results. +local function split_source_and_binary_results(results) + local sources, binaries = {}, {} + for name, versions in pairs(results) do + for version, repositories in pairs(versions) do + for _, repo in ipairs(repositories) do + local where = sources + if repo.arch == "all" or repo.arch == cfg.arch then + where = binaries + end + store_result(where, name, version, repo.arch, repo.repo) + end + end + end + return sources, binaries +end + +--- Given a name and optionally a version, try to find in the rocks +-- servers a single .src.rock or .rockspec file that satisfies +-- the request, and run the given function on it; or display to the +-- user possibilities if it couldn't narrow down a single match. +-- @param action function: A function that takes a .src.rock or +-- .rockspec URL as a parameter. +-- @param name string: A rock name +-- @param version string or nil: A version number may also be given. +-- @return The result of the action function, or nil and an error message. +function search.act_on_src_or_rockspec(action, name, version, ...) + assert(type(action) == "function") + assert(type(name) == "string") + assert(type(version) == "string" or not version) + + local query = search.make_query(name, version) + query.arch = "src|rockspec" + local url, err = search.find_suitable_rock(query) + if not url then + return nil, "Could not find a result named "..name..(version and " "..version or "")..": "..err + end + return action(url, ...) +end + +function search.pick_installed_rock(name, version, given_tree) + local results = {} + local query = search.make_query(name, version) + query.exact_name = true + local tree_map = {} + local trees = cfg.rocks_trees + if given_tree then + trees = { given_tree } + end + for _, tree in ipairs(trees) do + local rocks_dir = path.rocks_dir(tree) + tree_map[rocks_dir] = tree + search.manifest_search(results, rocks_dir, query) + end + + if not next(results) then -- + return nil,"cannot find package "..name.." "..(version or "").."\nUse 'list' to find installed rocks." + end + + version = nil + local repo_url + local package, versions = util.sortedpairs(results)() + --question: what do we do about multiple versions? This should + --give us the latest version on the last repo (which is usually the global one) + for vs, repositories in util.sortedpairs(versions, deps.compare_versions) do + if not version then version = vs end + for _, rp in ipairs(repositories) do repo_url = rp.repo end + end + + local repo = tree_map[repo_url] + return name, version, repo, repo_url +end + +--- Driver function for "search" command. +-- @param name string: A substring of a rock name to search. +-- @param version string or nil: a version may also be passed. +-- @return boolean or (nil, string): True if build was successful; nil and an +-- error message otherwise. +function search.command(flags, name, version) + if flags["all"] then + name, version = "", nil + end + + if type(name) ~= "string" and not flags["all"] then + return nil, "Enter name and version or use --all. "..util.see_help("search") + end + + local query = search.make_query(name:lower(), version) + query.exact_name = false + local results, err = search.search_repos(query) + local porcelain = flags["porcelain"] + util.title("Search results:", porcelain, "=") + local sources, binaries = split_source_and_binary_results(results) + if next(sources) and not flags["binary"] then + util.title("Rockspecs and source rocks:", porcelain) + search.print_results(sources, porcelain) + end + if next(binaries) and not flags["source"] then + util.title("Binary and pure-Lua rocks:", porcelain) + search.print_results(binaries, porcelain) + end + return true +end + +return search diff --git a/Utils/luarocks/lua/luarocks/show.lua b/Utils/luarocks/lua/luarocks/show.lua new file mode 100644 index 000000000..85c7edcbd --- /dev/null +++ b/Utils/luarocks/lua/luarocks/show.lua @@ -0,0 +1,152 @@ +--- Module implementing the LuaRocks "show" command. +-- Shows information about an installed rock. +local show = {} +package.loaded["luarocks.show"] = show + +local search = require("luarocks.search") +local cfg = require("luarocks.cfg") +local util = require("luarocks.util") +local path = require("luarocks.path") +local deps = require("luarocks.deps") +local fetch = require("luarocks.fetch") +local manif = require("luarocks.manif") + +util.add_run_function(show) +show.help_summary = "Show information about an installed rock." + +show.help = [[ + is an existing package name. +Without any flags, show all module information. +With these flags, return only the desired information: + +--home home page of project +--modules all modules provided by this package as used by require() +--deps packages this package depends on +--rockspec the full path of the rockspec file +--mversion the package version +--rock-tree local tree where rock is installed +--rock-dir data directory of the installed rock +]] + +local function keys_as_string(t, sep) + local keys = util.keys(t) + table.sort(keys) + return table.concat(keys, sep or " ") +end + +local function word_wrap(line) + local width = tonumber(os.getenv("COLUMNS")) or 80 + if width > 80 then width = 80 end + if #line > width then + local brk = width + while brk > 0 and line:sub(brk, brk) ~= " " do + brk = brk - 1 + end + if brk > 0 then + return line:sub(1, brk-1) .. "\n" .. word_wrap(line:sub(brk+1)) + end + end + return line +end + +local function format_text(text) + text = text:gsub("^%s*",""):gsub("%s$", ""):gsub("\n[ \t]+","\n"):gsub("([^\n])\n([^\n])","%1 %2") + local paragraphs = util.split_string(text, "\n\n") + for n, line in ipairs(paragraphs) do + paragraphs[n] = word_wrap(line) + end + return (table.concat(paragraphs, "\n\n"):gsub("%s$", "")) +end + +local function installed_rock_label(name, tree) + local installed, version + if cfg.rocks_provided[name] then + installed, version = true, cfg.rocks_provided[name] + else + installed, version = search.pick_installed_rock(name, nil, tree) + end + return installed and "(using "..version..")" or "(missing)" +end + +--- Driver function for "show" command. +-- @param name or nil: an existing package name. +-- @param version string or nil: a version may also be passed. +-- @return boolean: True if succeeded, nil on errors. +function show.command(flags, name, version) + if not name then + return nil, "Argument missing. "..util.see_help("show") + end + + local repo, repo_url + name, version, repo, repo_url = search.pick_installed_rock(name:lower(), version, flags["tree"]) + if not name then + return nil, version + end + + local directory = path.install_dir(name,version,repo) + local rockspec_file = path.rockspec_file(name, version, repo) + local rockspec, err = fetch.load_local_rockspec(rockspec_file) + if not rockspec then return nil,err end + + local descript = rockspec.description or {} + local manifest, err = manif.load_manifest(repo_url) + if not manifest then return nil,err end + local minfo = manifest.repository[name][version][1] + + if flags["rock-tree"] then util.printout(path.rocks_tree_to_string(repo)) + elseif flags["rock-dir"] then util.printout(directory) + elseif flags["home"] then util.printout(descript.homepage) + elseif flags["modules"] then util.printout(keys_as_string(minfo.modules, "\n")) + elseif flags["deps"] then util.printout(keys_as_string(minfo.dependencies)) + elseif flags["rockspec"] then util.printout(rockspec_file) + elseif flags["mversion"] then util.printout(version) + else + util.printout() + util.printout(rockspec.package.." "..rockspec.version.." - "..(descript.summary or "")) + util.printout() + if descript.detailed then + util.printout(format_text(descript.detailed)) + util.printout() + end + if descript.license then + util.printout("License: ", descript.license) + end + if descript.homepage then + util.printout("Homepage: ", descript.homepage) + end + util.printout("Installed in: ", path.rocks_tree_to_string(repo)) + if next(minfo.modules) then + util.printout() + util.printout("Modules:") + for mod, filename in util.sortedpairs(minfo.modules) do + util.printout("\t"..mod.." ("..path.which(mod, filename, name, version, repo, manifest)..")") + end + end + local direct_deps = {} + if #rockspec.dependencies > 0 then + util.printout() + util.printout("Depends on:") + for _, dep in ipairs(rockspec.dependencies) do + direct_deps[dep.name] = true + util.printout("\t"..deps.show_dep(dep).." "..installed_rock_label(dep.name, flags["tree"])) + end + end + local has_indirect_deps + for dep_name in util.sortedpairs(minfo.dependencies) do + if not direct_deps[dep_name] then + if not has_indirect_deps then + util.printout() + util.printout("Indirectly pulling:") + has_indirect_deps = true + end + + util.printout("\t"..dep_name.." "..installed_rock_label(dep_name, flags["tree"])) + end + end + util.printout() + end + return true +end + + +return show diff --git a/Utils/luarocks/lua/luarocks/site_config_5_1.lua b/Utils/luarocks/lua/luarocks/site_config_5_1.lua new file mode 100644 index 000000000..85ab3ddf4 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/site_config_5_1.lua @@ -0,0 +1,14 @@ +local site_config = {} +site_config.LUA_INCDIR=[[C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\include]] +site_config.LUA_LIBDIR=[[C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks]] +site_config.LUA_BINDIR=[[C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks]] +site_config.LUA_INTERPRETER=[[lua5.1]] +site_config.LUAROCKS_UNAME_S=[[MINGW]] +site_config.LUAROCKS_UNAME_M=[[x86]] +site_config.LUAROCKS_ROCKS_TREE=[[C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\systree]] +site_config.LUAROCKS_PREFIX=[[C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks]] +site_config.LUAROCKS_DOWNLOADER=[[wget]] +site_config.LUAROCKS_MD5CHECKER=[[md5sum]] +site_config.LUAROCKS_FORCE_CONFIG=true +site_config.LUAROCKS_SYSCONFDIR=[[C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks]] +return site_config diff --git a/Utils/luarocks/lua/luarocks/tools/patch.lua b/Utils/luarocks/lua/luarocks/tools/patch.lua new file mode 100644 index 000000000..44d00ef89 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/tools/patch.lua @@ -0,0 +1,700 @@ +--- Patch utility to apply unified diffs. +-- +-- http://lua-users.org/wiki/LuaPatch +-- +-- (c) 2008 David Manura, Licensed under the same terms as Lua (MIT license). +-- Code is heavilly based on the Python-based patch.py version 8.06-1 +-- Copyright (c) 2008 rainforce.org, MIT License +-- Project home: http://code.google.com/p/python-patch/ . +-- Version 0.1 + +local patch = {} + +local fs = require("luarocks.fs") +local util = require("luarocks.util") + +local io = io +local os = os +local string = string +local table = table +local format = string.format + +-- logging +local debugmode = false +local function debug(_) end +local function info(_) end +local function warning(s) io.stderr:write(s .. '\n') end + +-- Returns boolean whether string s2 starts with string s. +local function startswith(s, s2) + return s:sub(1, #s2) == s2 +end + +-- Returns boolean whether string s2 ends with string s. +local function endswith(s, s2) + return #s >= #s2 and s:sub(#s-#s2+1) == s2 +end + +-- Returns string s after filtering out any new-line characters from end. +local function endlstrip(s) + return s:gsub('[\r\n]+$', '') +end + +-- Returns shallow copy of table t. +local function table_copy(t) + local t2 = {} + for k,v in pairs(t) do t2[k] = v end + return t2 +end + +local function exists(filename) + local fh = io.open(filename) + local result = fh ~= nil + if fh then fh:close() end + return result +end +local function isfile() return true end --FIX? + +local function read_file(filename) + local fh, data, err, oserr + fh, err, oserr = io.open(filename, 'rb') + if not fh then return fh, err, oserr end + data, err, oserr = fh:read'*a' + fh:close() + if not data then return nil, err, oserr end + return data +end + +local function write_file(filename, data) + local fh, status, err, oserr + fh, err, oserr = io.open(filename 'wb') + if not fh then return fh, err, oserr end + status, err, oserr = fh:write(data) + fh:close() + if not status then return nil, err, oserr end + return true +end + +local function file_copy(src, dest) + local data, status, err, oserr + data, err, oserr = read_file(src) + if not data then return data, err, oserr end + status, err, oserr = write_file(dest) + if not status then return status, err, oserr end + return true +end + +local function string_as_file(s) + return { + at = 0, + str = s, + len = #s, + eof = false, + read = function(self, n) + if self.eof then return nil end + local chunk = self.str:sub(self.at, self.at + n - 1) + self.at = self.at + n + if self.at > self.len then + self.eof = true + end + return chunk + end, + close = function(self) + self.eof = true + end, + } +end + +-- +-- file_lines(f) is similar to f:lines() for file f. +-- The main difference is that read_lines includes +-- new-line character sequences ("\n", "\r\n", "\r"), +-- if any, at the end of each line. Embedded "\0" are also handled. +-- Caution: The newline behavior can depend on whether f is opened +-- in binary or ASCII mode. +-- (file_lines - version 20080913) +-- +local function file_lines(f) + local CHUNK_SIZE = 1024 + local buffer = "" + local pos_beg = 1 + return function() + local pos, chars + while 1 do + pos, chars = buffer:match('()([\r\n].)', pos_beg) + if pos or not f then + break + elseif f then + local chunk = f:read(CHUNK_SIZE) + if chunk then + buffer = buffer:sub(pos_beg) .. chunk + pos_beg = 1 + else + f = nil + end + end + end + if not pos then + pos = #buffer + elseif chars == '\r\n' then + pos = pos + 1 + end + local line = buffer:sub(pos_beg, pos) + pos_beg = pos + 1 + if #line > 0 then + return line + end + end +end + +local function match_linerange(line) + local m1, m2, m3, m4 = line:match("^@@ %-(%d+),(%d+) %+(%d+),(%d+)") + if not m1 then m1, m3, m4 = line:match("^@@ %-(%d+) %+(%d+),(%d+)") end + if not m1 then m1, m2, m3 = line:match("^@@ %-(%d+),(%d+) %+(%d+)") end + if not m1 then m1, m3 = line:match("^@@ %-(%d+) %+(%d+)") end + return m1, m2, m3, m4 +end + +function patch.read_patch(filename, data) + -- define possible file regions that will direct the parser flow + local state = 'header' + -- 'header' - comments before the patch body + -- 'filenames' - lines starting with --- and +++ + -- 'hunkhead' - @@ -R +R @@ sequence + -- 'hunkbody' + -- 'hunkskip' - skipping invalid hunk mode + + local all_ok = true + local lineends = {lf=0, crlf=0, cr=0} + local files = {source={}, target={}, hunks={}, fileends={}, hunkends={}} + local nextfileno = 0 + local nexthunkno = 0 --: even if index starts with 0 user messages + -- number hunks from 1 + + -- hunkinfo holds parsed values, hunkactual - calculated + local hunkinfo = { + startsrc=nil, linessrc=nil, starttgt=nil, linestgt=nil, + invalid=false, text={} + } + local hunkactual = {linessrc=nil, linestgt=nil} + + info(format("reading patch %s", filename)) + + local fp + if data then + fp = string_as_file(data) + else + fp = filename == '-' and io.stdin or assert(io.open(filename, "rb")) + end + local lineno = 0 + + for line in file_lines(fp) do + lineno = lineno + 1 + if state == 'header' then + if startswith(line, "--- ") then + state = 'filenames' + end + -- state is 'header' or 'filenames' + end + if state == 'hunkbody' then + -- skip hunkskip and hunkbody code until definition of hunkhead read + + if line:match"^[\r\n]*$" then + -- prepend space to empty lines to interpret them as context properly + line = " " .. line + end + + -- process line first + if line:match"^[- +\\]" then + -- gather stats about line endings + local he = files.hunkends[nextfileno] + if endswith(line, "\r\n") then + he.crlf = he.crlf + 1 + elseif endswith(line, "\n") then + he.lf = he.lf + 1 + elseif endswith(line, "\r") then + he.cr = he.cr + 1 + end + if startswith(line, "-") then + hunkactual.linessrc = hunkactual.linessrc + 1 + elseif startswith(line, "+") then + hunkactual.linestgt = hunkactual.linestgt + 1 + elseif startswith(line, "\\") then + -- nothing + else + hunkactual.linessrc = hunkactual.linessrc + 1 + hunkactual.linestgt = hunkactual.linestgt + 1 + end + table.insert(hunkinfo.text, line) + -- todo: handle \ No newline cases + else + warning(format("invalid hunk no.%d at %d for target file %s", + nexthunkno, lineno, files.target[nextfileno])) + -- add hunk status node + table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) + files.hunks[nextfileno][nexthunkno].invalid = true + all_ok = false + state = 'hunkskip' + end + + -- check exit conditions + if hunkactual.linessrc > hunkinfo.linessrc or + hunkactual.linestgt > hunkinfo.linestgt + then + warning(format("extra hunk no.%d lines at %d for target %s", + nexthunkno, lineno, files.target[nextfileno])) + -- add hunk status node + table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) + files.hunks[nextfileno][nexthunkno].invalid = true + state = 'hunkskip' + elseif hunkinfo.linessrc == hunkactual.linessrc and + hunkinfo.linestgt == hunkactual.linestgt + then + table.insert(files.hunks[nextfileno], table_copy(hunkinfo)) + state = 'hunkskip' + + -- detect mixed window/unix line ends + local ends = files.hunkends[nextfileno] + if (ends.cr~=0 and 1 or 0) + (ends.crlf~=0 and 1 or 0) + + (ends.lf~=0 and 1 or 0) > 1 + then + warning(format("inconsistent line ends in patch hunks for %s", + files.source[nextfileno])) + end + if debugmode then + local debuglines = {crlf=ends.crlf, lf=ends.lf, cr=ends.cr, + file=files.target[nextfileno], hunk=nexthunkno} + debug(format("crlf: %(crlf)d lf: %(lf)d cr: %(cr)d\t " .. + "- file: %(file)s hunk: %(hunk)d", debuglines)) + end + end + -- state is 'hunkbody' or 'hunkskip' + end + + if state == 'hunkskip' then + if match_linerange(line) then + state = 'hunkhead' + elseif startswith(line, "--- ") then + state = 'filenames' + if debugmode and #files.source > 0 then + debug(format("- %2d hunks for %s", #files.hunks[nextfileno], + files.source[nextfileno])) + end + end + -- state is 'hunkskip', 'hunkhead', or 'filenames' + end + local advance + if state == 'filenames' then + if startswith(line, "--- ") then + if util.array_contains(files.source, nextfileno) then + all_ok = false + warning(format("skipping invalid patch for %s", + files.source[nextfileno+1])) + table.remove(files.source, nextfileno+1) + -- double source filename line is encountered + -- attempt to restart from this second line + end + -- Accept a space as a terminator, like GNU patch does. + -- Breaks patches containing filenames with spaces... + -- FIXME Figure out what does GNU patch do in those cases. + local match = line:match("^%-%-%- ([^ \t\r\n]+)") + if not match then + all_ok = false + warning(format("skipping invalid filename at line %d", lineno+1)) + state = 'header' + else + table.insert(files.source, match) + end + elseif not startswith(line, "+++ ") then + if util.array_contains(files.source, nextfileno) then + all_ok = false + warning(format("skipping invalid patch with no target for %s", + files.source[nextfileno+1])) + table.remove(files.source, nextfileno+1) + else + -- this should be unreachable + warning("skipping invalid target patch") + end + state = 'header' + else + if util.array_contains(files.target, nextfileno) then + all_ok = false + warning(format("skipping invalid patch - double target at line %d", + lineno+1)) + table.remove(files.source, nextfileno+1) + table.remove(files.target, nextfileno+1) + nextfileno = nextfileno - 1 + -- double target filename line is encountered + -- switch back to header state + state = 'header' + else + -- Accept a space as a terminator, like GNU patch does. + -- Breaks patches containing filenames with spaces... + -- FIXME Figure out what does GNU patch do in those cases. + local re_filename = "^%+%+%+ ([^ \t\r\n]+)" + local match = line:match(re_filename) + if not match then + all_ok = false + warning(format( + "skipping invalid patch - no target filename at line %d", + lineno+1)) + state = 'header' + else + table.insert(files.target, match) + nextfileno = nextfileno + 1 + nexthunkno = 0 + table.insert(files.hunks, {}) + table.insert(files.hunkends, table_copy(lineends)) + table.insert(files.fileends, table_copy(lineends)) + state = 'hunkhead' + advance = true + end + end + end + -- state is 'filenames', 'header', or ('hunkhead' with advance) + end + if not advance and state == 'hunkhead' then + local m1, m2, m3, m4 = match_linerange(line) + if not m1 then + if not util.array_contains(files.hunks, nextfileno-1) then + all_ok = false + warning(format("skipping invalid patch with no hunks for file %s", + files.target[nextfileno])) + end + state = 'header' + else + hunkinfo.startsrc = tonumber(m1) + hunkinfo.linessrc = tonumber(m2 or 1) + hunkinfo.starttgt = tonumber(m3) + hunkinfo.linestgt = tonumber(m4 or 1) + hunkinfo.invalid = false + hunkinfo.text = {} + + hunkactual.linessrc = 0 + hunkactual.linestgt = 0 + + state = 'hunkbody' + nexthunkno = nexthunkno + 1 + end + -- state is 'header' or 'hunkbody' + end + end + if state ~= 'hunkskip' then + warning(format("patch file incomplete - %s", filename)) + all_ok = false + -- os.exit(?) + else + -- duplicated message when an eof is reached + if debugmode and #files.source > 0 then + debug(format("- %2d hunks for %s", #files.hunks[nextfileno], + files.source[nextfileno])) + end + end + + local sum = 0; for _,hset in ipairs(files.hunks) do sum = sum + #hset end + info(format("total files: %d total hunks: %d", #files.source, sum)) + fp:close() + return files, all_ok +end + +local function find_hunk(file, h, hno) + for fuzz=0,2 do + local lineno = h.startsrc + for i=0,#file do + local found = true + local location = lineno + for l, hline in ipairs(h.text) do + if l > fuzz then + -- todo: \ No newline at the end of file + if startswith(hline, " ") or startswith(hline, "-") then + local line = file[lineno] + lineno = lineno + 1 + if not line or #line == 0 then + found = false + break + end + if endlstrip(line) ~= endlstrip(hline:sub(2)) then + found = false + break + end + end + end + end + if found then + local offset = location - h.startsrc - fuzz + if offset ~= 0 then + warning(format("Hunk %d found at offset %d%s...", hno, offset, fuzz == 0 and "" or format(" (fuzz %d)", fuzz))) + end + h.startsrc = location + h.starttgt = h.starttgt + offset + for _=1,fuzz do + table.remove(h.text, 1) + table.remove(h.text, #h.text) + end + return true + end + lineno = i + end + end + return false +end + +local function load_file(filename) + local fp = assert(io.open(filename)) + local file = {} + local readline = file_lines(fp) + while true do + local line = readline() + if not line then break end + table.insert(file, line) + end + fp:close() + return file +end + +local function find_hunks(file, hunks) + for hno, h in ipairs(hunks) do + find_hunk(file, h, hno) + end +end + +local function check_patched(file, hunks) + local lineno = 1 + local ok, err = pcall(function() + if #file == 0 then + error('nomatch', 0) + end + for hno, h in ipairs(hunks) do + -- skip to line just before hunk starts + if #file < h.starttgt then + error('nomatch', 0) + end + lineno = h.starttgt + for _, hline in ipairs(h.text) do + -- todo: \ No newline at the end of file + if not startswith(hline, "-") and not startswith(hline, "\\") then + local line = file[lineno] + lineno = lineno + 1 + if #line == 0 then + error('nomatch', 0) + end + if endlstrip(line) ~= endlstrip(hline:sub(2)) then + warning(format("file is not patched - failed hunk: %d", hno)) + error('nomatch', 0) + end + end + end + end + end) + -- todo: display failed hunk, i.e. expected/found + return err ~= 'nomatch' +end + +local function patch_hunks(srcname, tgtname, hunks) + local src = assert(io.open(srcname, "rb")) + local tgt = assert(io.open(tgtname, "wb")) + + local src_readline = file_lines(src) + + -- todo: detect linefeeds early - in apply_files routine + -- to handle cases when patch starts right from the first + -- line and no lines are processed. At the moment substituted + -- lineends may not be the same at the start and at the end + -- of patching. Also issue a warning about mixed lineends + + local srclineno = 1 + local lineends = {['\n']=0, ['\r\n']=0, ['\r']=0} + for hno, h in ipairs(hunks) do + debug(format("processing hunk %d for file %s", hno, tgtname)) + -- skip to line just before hunk starts + while srclineno < h.startsrc do + local line = src_readline() + -- Python 'U' mode works only with text files + if endswith(line, "\r\n") then + lineends["\r\n"] = lineends["\r\n"] + 1 + elseif endswith(line, "\n") then + lineends["\n"] = lineends["\n"] + 1 + elseif endswith(line, "\r") then + lineends["\r"] = lineends["\r"] + 1 + end + tgt:write(line) + srclineno = srclineno + 1 + end + + for _,hline in ipairs(h.text) do + -- todo: check \ No newline at the end of file + if startswith(hline, "-") or startswith(hline, "\\") then + src_readline() + srclineno = srclineno + 1 + else + if not startswith(hline, "+") then + src_readline() + srclineno = srclineno + 1 + end + local line2write = hline:sub(2) + -- detect if line ends are consistent in source file + local sum = 0 + for _,v in pairs(lineends) do if v > 0 then sum=sum+1 end end + if sum == 1 then + local newline + for k,v in pairs(lineends) do if v ~= 0 then newline = k end end + tgt:write(endlstrip(line2write) .. newline) + else -- newlines are mixed or unknown + tgt:write(line2write) + end + end + end + end + for line in src_readline do + tgt:write(line) + end + tgt:close() + src:close() + return true +end + +local function strip_dirs(filename, strip) + if strip == nil then return filename end + for _=1,strip do + filename=filename:gsub("^[^/]*/", "") + end + return filename +end + +function patch.apply_patch(the_patch, strip) + local all_ok = true + local total = #the_patch.source + for fileno, filename in ipairs(the_patch.source) do + filename = strip_dirs(filename, strip) + local continue + local f2patch = filename + if not exists(f2patch) then + f2patch = strip_dirs(the_patch.target[fileno], strip) + f2patch = fs.absolute_name(f2patch) + if not exists(f2patch) then --FIX:if f2patch nil + warning(format("source/target file does not exist\n--- %s\n+++ %s", + filename, f2patch)) + all_ok = false + continue = true + end + end + if not continue and not isfile(f2patch) then + warning(format("not a file - %s", f2patch)) + all_ok = false + continue = true + end + if not continue then + + filename = f2patch + + info(format("processing %d/%d:\t %s", fileno, total, filename)) + + -- validate before patching + local hunks = the_patch.hunks[fileno] + local file = load_file(filename) + local hunkno = 1 + local hunk = hunks[hunkno] + local hunkfind = {} + local validhunks = 0 + local canpatch = false + local hunklineno + local isbreak + local lineno = 0 + + find_hunks(file, hunks) + + for _, line in ipairs(file) do + lineno = lineno + 1 + local continue + if not hunk or lineno < hunk.startsrc then + continue = true + elseif lineno == hunk.startsrc then + hunkfind = {} + for _,x in ipairs(hunk.text) do + if x:sub(1,1) == ' ' or x:sub(1,1) == '-' then + hunkfind[#hunkfind+1] = endlstrip(x:sub(2)) + end + end + hunklineno = 1 + + -- todo \ No newline at end of file + end + -- check hunks in source file + if not continue and lineno < hunk.startsrc + #hunkfind - 1 then + if endlstrip(line) == hunkfind[hunklineno] then + hunklineno = hunklineno + 1 + else + debug(format("hunk no.%d doesn't match source file %s", + hunkno, filename)) + -- file may be already patched, but check other hunks anyway + hunkno = hunkno + 1 + if hunkno <= #hunks then + hunk = hunks[hunkno] + continue = true + else + isbreak = true; break + end + end + end + -- check if processed line is the last line + if not continue and lineno == hunk.startsrc + #hunkfind - 1 then + debug(format("file %s hunk no.%d -- is ready to be patched", + filename, hunkno)) + hunkno = hunkno + 1 + validhunks = validhunks + 1 + if hunkno <= #hunks then + hunk = hunks[hunkno] + else + if validhunks == #hunks then + -- patch file + canpatch = true + isbreak = true; break + end + end + end + end + if not isbreak then + if hunkno <= #hunks then + warning(format("premature end of source file %s at hunk %d", + filename, hunkno)) + all_ok = false + end + end + if validhunks < #hunks then + if check_patched(file, hunks) then + warning(format("already patched %s", filename)) + else + warning(format("source file is different - %s", filename)) + all_ok = false + end + end + if canpatch then + local backupname = filename .. ".orig" + if exists(backupname) then + warning(format("can't backup original file to %s - aborting", + backupname)) + all_ok = false + else + assert(os.rename(filename, backupname)) + if patch_hunks(backupname, filename, hunks) then + warning(format("successfully patched %s", filename)) + assert(os.remove(backupname)) + else + warning(format("error patching file %s", filename)) + assert(file_copy(filename, filename .. ".invalid")) + warning(format("invalid version is saved to %s", + filename .. ".invalid")) + -- todo: proper rejects + assert(os.rename(backupname, filename)) + all_ok = false + end + end + end + + end -- if not continue + end -- for + -- todo: check for premature eof + return all_ok +end + +return patch diff --git a/Utils/luarocks/lua/luarocks/tools/tar.lua b/Utils/luarocks/lua/luarocks/tools/tar.lua new file mode 100644 index 000000000..637a6c953 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/tools/tar.lua @@ -0,0 +1,150 @@ + +--- A pure-Lua implementation of untar (unpacking .tar archives) +local tar = {} + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local util = require("luarocks.util") + +local blocksize = 512 + +local function get_typeflag(flag) + if flag == "0" or flag == "\0" then return "file" + elseif flag == "1" then return "link" + elseif flag == "2" then return "symlink" -- "reserved" in POSIX, "symlink" in GNU + elseif flag == "3" then return "character" + elseif flag == "4" then return "block" + elseif flag == "5" then return "directory" + elseif flag == "6" then return "fifo" + elseif flag == "7" then return "contiguous" -- "reserved" in POSIX, "contiguous" in GNU + elseif flag == "x" then return "next file" + elseif flag == "g" then return "global extended header" + elseif flag == "L" then return "long name" + elseif flag == "K" then return "long link name" + end + return "unknown" +end + +local function octal_to_number(octal) + local exp = 0 + local number = 0 + for i = #octal,1,-1 do + local digit = tonumber(octal:sub(i,i)) + if digit then + number = number + (digit * 8^exp) + exp = exp + 1 + end + end + return number +end + +local function checksum_header(block) + local sum = 256 + for i = 1,148 do + sum = sum + block:byte(i) + end + for i = 157,500 do + sum = sum + block:byte(i) + end + return sum +end + +local function nullterm(s) + return s:match("^[^%z]*") +end + +local function read_header_block(block) + local header = {} + header.name = nullterm(block:sub(1,100)) + header.mode = nullterm(block:sub(101,108)) + header.uid = octal_to_number(nullterm(block:sub(109,116))) + header.gid = octal_to_number(nullterm(block:sub(117,124))) + header.size = octal_to_number(nullterm(block:sub(125,136))) + header.mtime = octal_to_number(nullterm(block:sub(137,148))) + header.chksum = octal_to_number(nullterm(block:sub(149,156))) + header.typeflag = get_typeflag(block:sub(157,157)) + header.linkname = nullterm(block:sub(158,257)) + header.magic = block:sub(258,263) + header.version = block:sub(264,265) + header.uname = nullterm(block:sub(266,297)) + header.gname = nullterm(block:sub(298,329)) + header.devmajor = octal_to_number(nullterm(block:sub(330,337))) + header.devminor = octal_to_number(nullterm(block:sub(338,345))) + header.prefix = block:sub(346,500) + if header.magic ~= "ustar " and header.magic ~= "ustar\0" then + return false, "Invalid header magic "..header.magic + end + if header.version ~= "00" and header.version ~= " \0" then + return false, "Unknown version "..header.version + end + if not checksum_header(block) == header.chksum then + return false, "Failed header checksum" + end + return header +end + +function tar.untar(filename, destdir) + assert(type(filename) == "string") + assert(type(destdir) == "string") + + local tar_handle = io.open(filename, "r") + if not tar_handle then return nil, "Error opening file "..filename end + + local long_name, long_link_name + while true do + local block + repeat + block = tar_handle:read(blocksize) + until (not block) or checksum_header(block) > 256 + if not block then break end + local header, err = read_header_block(block) + if not header then + util.printerr(err) + end + + local file_data = tar_handle:read(math.ceil(header.size / blocksize) * blocksize):sub(1,header.size) + + if header.typeflag == "long name" then + long_name = nullterm(file_data) + elseif header.typeflag == "long link name" then + long_link_name = nullterm(file_data) + else + if long_name then + header.name = long_name + long_name = nil + end + if long_link_name then + header.name = long_link_name + long_link_name = nil + end + end + local pathname = dir.path(destdir, header.name) + if header.typeflag == "directory" then + local ok, err = fs.make_dir(pathname) + if not ok then return nil, err end + elseif header.typeflag == "file" then + local dirname = dir.dir_name(pathname) + if dirname ~= "" then + local ok, err = fs.make_dir(dirname) + if not ok then return nil, err end + end + local file_handle = io.open(pathname, "wb") + file_handle:write(file_data) + file_handle:close() + fs.set_time(pathname, header.mtime) + if fs.chmod then + fs.chmod(pathname, header.mode) + end + end + --[[ + for k,v in pairs(header) do + util.printout("[\""..tostring(k).."\"] = "..(type(v)=="number" and v or "\""..v:gsub("%z", "\\0").."\"")) + end + util.printout() + --]] + end + tar_handle:close() + return true +end + +return tar diff --git a/Utils/luarocks/lua/luarocks/tools/zip.lua b/Utils/luarocks/lua/luarocks/tools/zip.lua new file mode 100644 index 000000000..e6d9e36ae --- /dev/null +++ b/Utils/luarocks/lua/luarocks/tools/zip.lua @@ -0,0 +1,264 @@ + +--- A Lua implementation of .zip file archiving (used for creating .rock files), +-- using only lzlib or lua-lzib. +local zip = {} + +local zlib = require("zlib") +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") + +-- zlib module can be provided by both lzlib and lua-lzib packages. +-- Create a compatibility layer. +local zlib_compress, zlib_crc32 +if zlib._VERSION:match "^lua%-zlib" then + function zlib_compress(data) + return (zlib.deflate()(data, "finish")) + end + + function zlib_crc32(data) + return zlib.crc32()(data) + end +elseif zlib._VERSION:match "^lzlib" then + function zlib_compress(data) + return zlib.compress(data) + end + + function zlib_crc32(data) + return zlib.crc32(zlib.crc32(), data) + end +else + error("unknown zlib library", 0) +end + +local function number_to_bytestring(number, nbytes) + local out = {} + for _ = 1, nbytes do + local byte = number % 256 + table.insert(out, string.char(byte)) + number = (number - byte) / 256 + end + return table.concat(out) +end + +--- Begin a new file to be stored inside the zipfile. +-- @param self handle of the zipfile being written. +-- @param filename filenome of the file to be added to the zipfile. +-- @return true if succeeded, nil in case of failure. +local function zipwriter_open_new_file_in_zip(self, filename) + if self.in_open_file then + self:close_file_in_zip() + return nil + end + local lfh = {} + self.local_file_header = lfh + lfh.last_mod_file_time = 0 -- TODO + lfh.last_mod_file_date = 0 -- TODO + lfh.file_name_length = #filename + lfh.extra_field_length = 0 + lfh.file_name = filename:gsub("\\", "/") + lfh.external_attr = 0 -- TODO properly store permissions + self.in_open_file = true + return true +end + +--- Write data to the file currently being stored in the zipfile. +-- @param self handle of the zipfile being written. +-- @param data string containing full contents of the file. +-- @return true if succeeded, nil in case of failure. +local function zipwriter_write_file_in_zip(self, data) + if not self.in_open_file then + return nil + end + local lfh = self.local_file_header + local compressed = zlib_compress(data):sub(3, -5) + lfh.crc32 = zlib_crc32(data) + lfh.compressed_size = #compressed + lfh.uncompressed_size = #data + self.data = compressed + return true +end + +--- Complete the writing of a file stored in the zipfile. +-- @param self handle of the zipfile being written. +-- @return true if succeeded, nil in case of failure. +local function zipwriter_close_file_in_zip(self) + local zh = self.ziphandle + + if not self.in_open_file then + return nil + end + + -- Local file header + local lfh = self.local_file_header + lfh.offset = zh:seek() + zh:write(number_to_bytestring(0x04034b50, 4)) -- signature + zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0 + zh:write(number_to_bytestring(0, 2)) -- general purpose bit flag + zh:write(number_to_bytestring(8, 2)) -- compression method: deflate + zh:write(number_to_bytestring(lfh.last_mod_file_time, 2)) + zh:write(number_to_bytestring(lfh.last_mod_file_date, 2)) + zh:write(number_to_bytestring(lfh.crc32, 4)) + zh:write(number_to_bytestring(lfh.compressed_size, 4)) + zh:write(number_to_bytestring(lfh.uncompressed_size, 4)) + zh:write(number_to_bytestring(lfh.file_name_length, 2)) + zh:write(number_to_bytestring(lfh.extra_field_length, 2)) + zh:write(lfh.file_name) + + -- File data + zh:write(self.data) + + -- Data descriptor + zh:write(number_to_bytestring(lfh.crc32, 4)) + zh:write(number_to_bytestring(lfh.compressed_size, 4)) + zh:write(number_to_bytestring(lfh.uncompressed_size, 4)) + + table.insert(self.files, lfh) + self.in_open_file = false + + return true +end + +-- @return boolean or (boolean, string): true on success, +-- false and an error message on failure. +local function zipwriter_add(self, file) + local fin + local ok, err = self:open_new_file_in_zip(file) + if not ok then + err = "error in opening "..file.." in zipfile" + else + fin = io.open(fs.absolute_name(file), "rb") + if not fin then + ok = false + err = "error opening "..file.." for reading" + end + end + if ok then + local data = fin:read("*a") + if not data then + err = "error reading "..file + ok = false + else + ok = self:write_file_in_zip(data) + if not ok then + err = "error in writing "..file.." in the zipfile" + end + end + end + if fin then + fin:close() + end + if ok then + ok = self:close_file_in_zip() + if not ok then + err = "error in writing "..file.." in the zipfile" + end + end + return ok == true, err +end + +--- Complete the writing of the zipfile. +-- @param self handle of the zipfile being written. +-- @return true if succeeded, nil in case of failure. +local function zipwriter_close(self) + local zh = self.ziphandle + + local central_directory_offset = zh:seek() + + local size_of_central_directory = 0 + -- Central directory structure + for _, lfh in ipairs(self.files) do + zh:write(number_to_bytestring(0x02014b50, 4)) -- signature + zh:write(number_to_bytestring(3, 2)) -- version made by: UNIX + zh:write(number_to_bytestring(20, 2)) -- version needed to extract: 2.0 + zh:write(number_to_bytestring(0, 2)) -- general purpose bit flag + zh:write(number_to_bytestring(8, 2)) -- compression method: deflate + zh:write(number_to_bytestring(lfh.last_mod_file_time, 2)) + zh:write(number_to_bytestring(lfh.last_mod_file_date, 2)) + zh:write(number_to_bytestring(lfh.crc32, 4)) + zh:write(number_to_bytestring(lfh.compressed_size, 4)) + zh:write(number_to_bytestring(lfh.uncompressed_size, 4)) + zh:write(number_to_bytestring(lfh.file_name_length, 2)) + zh:write(number_to_bytestring(lfh.extra_field_length, 2)) + zh:write(number_to_bytestring(0, 2)) -- file comment length + zh:write(number_to_bytestring(0, 2)) -- disk number start + zh:write(number_to_bytestring(0, 2)) -- internal file attributes + zh:write(number_to_bytestring(lfh.external_attr, 4)) -- external file attributes + zh:write(number_to_bytestring(lfh.offset, 4)) -- relative offset of local header + zh:write(lfh.file_name) + size_of_central_directory = size_of_central_directory + 46 + lfh.file_name_length + end + + -- End of central directory record + zh:write(number_to_bytestring(0x06054b50, 4)) -- signature + zh:write(number_to_bytestring(0, 2)) -- number of this disk + zh:write(number_to_bytestring(0, 2)) -- number of disk with start of central directory + zh:write(number_to_bytestring(#self.files, 2)) -- total number of entries in the central dir on this disk + zh:write(number_to_bytestring(#self.files, 2)) -- total number of entries in the central dir + zh:write(number_to_bytestring(size_of_central_directory, 4)) + zh:write(number_to_bytestring(central_directory_offset, 4)) + zh:write(number_to_bytestring(0, 2)) -- zip file comment length + zh:close() + + return true +end + +--- Return a zip handle open for writing. +-- @param name filename of the zipfile to be created. +-- @return a zip handle, or nil in case of error. +function zip.new_zipwriter(name) + + local zw = {} + + zw.ziphandle = io.open(fs.absolute_name(name), "wb") + if not zw.ziphandle then + return nil + end + zw.files = {} + zw.in_open_file = false + + zw.add = zipwriter_add + zw.close = zipwriter_close + zw.open_new_file_in_zip = zipwriter_open_new_file_in_zip + zw.write_file_in_zip = zipwriter_write_file_in_zip + zw.close_file_in_zip = zipwriter_close_file_in_zip + + return zw +end + +--- Compress files in a .zip archive. +-- @param zipfile string: pathname of .zip archive to be created. +-- @param ... Filenames to be stored in the archive are given as +-- additional arguments. +-- @return boolean or (boolean, string): true on success, +-- false and an error message on failure. +function zip.zip(zipfile, ...) + local zw = zip.new_zipwriter(zipfile) + if not zw then + return nil, "error opening "..zipfile + end + + local ok, err + for _, file in pairs({...}) do + if fs.is_dir(file) then + for _, entry in pairs(fs.find(file)) do + local fullname = dir.path(file, entry) + if fs.is_file(fullname) then + ok, err = zw:add(fullname) + if not ok then break end + end + end + else + ok, err = zw:add(file) + if not ok then break end + end + end + + ok = zw:close() + if not ok then + return false, "error closing "..zipfile + end + return ok, err +end + + +return zip diff --git a/Utils/luarocks/lua/luarocks/type_check.lua b/Utils/luarocks/lua/luarocks/type_check.lua new file mode 100644 index 000000000..63c59ca21 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/type_check.lua @@ -0,0 +1,344 @@ +--- Type-checking functions. +-- Functions and definitions for doing a basic lint check on files +-- loaded by LuaRocks. +local type_check = {} +package.loaded["luarocks.type_check"] = type_check + +local cfg = require("luarocks.cfg") +local deps = require("luarocks.deps") + +type_check.rockspec_format = "1.1" + +local string_1 = { _type = "string" } +local number_1 = { _type = "number" } +local mandatory_string_1 = { _type = "string", _mandatory = true } + +-- Syntax for type-checking tables: +-- +-- A type-checking table describes typing data for a value. +-- Any key starting with an underscore has a special meaning: +-- _type (string) is the Lua type of the value. Default is "table". +-- _version (string) is the minimum rockspec_version that supports this value. Default is "1.0". +-- _mandatory (boolean) indicates if the value is a mandatory key in its container table. Default is false. +-- For "string" types only: +-- _pattern (string) is the string-matching pattern, valid for string types only. Default is ".*". +-- For "table" types only: +-- _any (table) is the type-checking table for unspecified keys, recursively checked. +-- _more (boolean) indicates that the table accepts unspecified keys and does not type-check them. +-- Any other string keys that don't start with an underscore represent known keys and are type-checking tables, recursively checked. + +local rockspec_types = { + rockspec_format = string_1, + package = mandatory_string_1, + version = { _type = "string", _pattern = "[%w.]+-[%d]+", _mandatory = true }, + description = { + summary = string_1, + detailed = string_1, + homepage = string_1, + license = string_1, + maintainer = string_1, + }, + dependencies = { + platforms = {}, -- recursively defined below + _any = string_1, + }, + supported_platforms = { + _any = string_1, + }, + external_dependencies = { + platforms = {}, -- recursively defined below + _any = { + program = string_1, + header = string_1, + library = string_1, + } + }, + source = { + _mandatory = true, + platforms = {}, -- recursively defined below + url = mandatory_string_1, + md5 = string_1, + file = string_1, + dir = string_1, + tag = string_1, + branch = string_1, + module = string_1, + cvs_tag = string_1, + cvs_module = string_1, + }, + build = { + platforms = {}, -- recursively defined below + type = string_1, + install = { + lua = { + _more = true + }, + lib = { + _more = true + }, + conf = { + _more = true + }, + bin = { + _more = true + } + }, + copy_directories = { + _any = string_1, + }, + _more = true, + _mandatory = true + }, + hooks = { + platforms = {}, -- recursively defined below + post_install = string_1, + }, + deploy = { + _version = "1.1", + wrap_bin_scripts = { _type = "boolean", _version = "1.1" }, + } +} + +type_check.rockspec_order = {"rockspec_format", "package", "version", + { "source", { "url", "tag", "branch", "md5" } }, + { "description", {"summary", "detailed", "homepage", "license" } }, + "supported_platforms", "dependencies", "external_dependencies", + { "build", {"type", "modules", "copy_directories", "platforms"} }, + "hooks"} + +rockspec_types.build.platforms._any = rockspec_types.build +rockspec_types.dependencies.platforms._any = rockspec_types.dependencies +rockspec_types.external_dependencies.platforms._any = rockspec_types.external_dependencies +rockspec_types.source.platforms._any = rockspec_types.source +rockspec_types.hooks.platforms._any = rockspec_types.hooks + +local manifest_types = { + repository = { + _mandatory = true, + -- packages + _any = { + -- versions + _any = { + -- items + _any = { + arch = mandatory_string_1, + modules = { _any = string_1 }, + commands = { _any = string_1 }, + dependencies = { _any = string_1 }, + -- TODO: to be extended with more metadata. + } + } + } + }, + modules = { + _mandatory = true, + -- modules + _any = { + -- providers + _any = string_1 + } + }, + commands = { + _mandatory = true, + -- modules + _any = { + -- commands + _any = string_1 + } + }, + dependencies = { + -- each module + _any = { + -- each version + _any = { + -- each dependency + _any = { + name = string_1, + constraints = { + _any = { + no_upgrade = { _type = "boolean" }, + op = string_1, + version = { + string = string_1, + _any = number_1, + } + } + } + } + } + } + } +} + +local function check_version(version, typetbl, context) + local typetbl_version = typetbl._version or "1.0" + if deps.compare_versions(typetbl_version, version) then + if context == "" then + return nil, "Invalid rockspec_format version number in rockspec? Please fix rockspec accordingly." + else + return nil, context.." is not supported in rockspec format "..version.." (requires version "..typetbl_version.."), please fix the rockspec_format field accordingly." + end + end + return true +end + +local type_check_table + +--- Type check an object. +-- The object is compared against an archetypical value +-- matching the expected type -- the actual values don't matter, +-- only their types. Tables are type checked recursively. +-- @param version string: The version of the item. +-- @param item any: The object being checked. +-- @param typetbl any: The type-checking table for the object. +-- @param context string: A string indicating the "context" where the +-- error occurred (the full table path), for error messages. +-- @return boolean or (nil, string): true if type checking +-- succeeded, or nil and an error message if it failed. +-- @see type_check_table +local function type_check_item(version, item, typetbl, context) + assert(type(version) == "string") + + local ok, err = check_version(version, typetbl, context) + if not ok then + return nil, err + end + + local item_type = type(item) or "nil" + local expected_type = typetbl._type or "table" + + if expected_type == "number" then + if not tonumber(item) then + return nil, "Type mismatch on field "..context..": expected a number" + end + elseif expected_type == "string" then + if item_type ~= "string" then + return nil, "Type mismatch on field "..context..": expected a string, got "..item_type + end + if typetbl._pattern then + if not item:match("^"..typetbl._pattern.."$") then + return nil, "Type mismatch on field "..context..": invalid value "..item.." does not match '"..typetbl._pattern.."'" + end + end + elseif expected_type == "table" then + if item_type ~= expected_type then + return nil, "Type mismatch on field "..context..": expected a table" + else + return type_check_table(version, item, typetbl, context) + end + elseif item_type ~= expected_type then + return nil, "Type mismatch on field "..context..": expected "..expected_type + end + return true +end + +local function mkfield(context, field) + if context == "" then + return tostring(field) + elseif type(field) == "string" then + return context.."."..field + else + return context.."["..tostring(field).."]" + end +end + +--- Type check the contents of a table. +-- The table's contents are compared against a reference table, +-- which contains the recognized fields, with archetypical values +-- matching the expected types -- the actual values of items in the +-- reference table don't matter, only their types (ie, for field x +-- in tbl that is correctly typed, type(tbl.x) == type(types.x)). +-- If the reference table contains a field called MORE, then +-- unknown fields in the checked table are accepted. +-- If it contains a field called ANY, then its type will be +-- used to check any unknown fields. If a field is prefixed +-- with MUST_, it is mandatory; its absence from the table is +-- a type error. +-- Tables are type checked recursively. +-- @param version string: The version of tbl. +-- @param tbl table: The table to be type checked. +-- @param typetbl table: The type-checking table, containing +-- values for recognized fields in the checked table. +-- @param context string: A string indicating the "context" where the +-- error occurred (such as the name of the table the item is a part of), +-- to be used by error messages. +-- @return boolean or (nil, string): true if type checking +-- succeeded, or nil and an error message if it failed. +type_check_table = function(version, tbl, typetbl, context) + assert(type(version) == "string") + assert(type(tbl) == "table") + assert(type(typetbl) == "table") + + local ok, err = check_version(version, typetbl, context) + if not ok then + return nil, err + end + + for k, v in pairs(tbl) do + local t = typetbl[k] or typetbl._any + if t then + local ok, err = type_check_item(version, v, t, mkfield(context, k)) + if not ok then return nil, err end + elseif typetbl._more then + -- Accept unknown field + else + if not cfg.accept_unknown_fields then + return nil, "Unknown field "..k + end + end + end + for k, v in pairs(typetbl) do + if k:sub(1,1) ~= "_" and v._mandatory then + if not tbl[k] then + return nil, "Mandatory field "..mkfield(context, k).." is missing." + end + end + end + return true +end + +local function check_undeclared_globals(globals, typetbl) + local undeclared = {} + for glob, _ in pairs(globals) do + if not (typetbl[glob] or typetbl["MUST_"..glob]) then + table.insert(undeclared, glob) + end + end + if #undeclared == 1 then + return nil, "Unknown variable: "..undeclared[1] + elseif #undeclared > 1 then + return nil, "Unknown variables: "..table.concat(undeclared, ", ") + end + return true +end + +--- Type check a rockspec table. +-- Verify the correctness of elements from a +-- rockspec table, reporting on unknown fields and type +-- mismatches. +-- @return boolean or (nil, string): true if type checking +-- succeeded, or nil and an error message if it failed. +function type_check.type_check_rockspec(rockspec, globals) + assert(type(rockspec) == "table") + if not rockspec.rockspec_format then + rockspec.rockspec_format = "1.0" + end + local ok, err = check_undeclared_globals(globals, rockspec_types) + if not ok then return nil, err end + return type_check_table(rockspec.rockspec_format, rockspec, rockspec_types, "") +end + +--- Type check a manifest table. +-- Verify the correctness of elements from a +-- manifest table, reporting on unknown fields and type +-- mismatches. +-- @return boolean or (nil, string): true if type checking +-- succeeded, or nil and an error message if it failed. +function type_check.type_check_manifest(manifest, globals) + assert(type(manifest) == "table") + local ok, err = check_undeclared_globals(globals, manifest_types) + if not ok then return nil, err end + return type_check_table("1.0", manifest, manifest_types, "") +end + +return type_check diff --git a/Utils/luarocks/lua/luarocks/unpack.lua b/Utils/luarocks/lua/luarocks/unpack.lua new file mode 100644 index 000000000..0922f9b90 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/unpack.lua @@ -0,0 +1,166 @@ + +--- Module implementing the LuaRocks "unpack" command. +-- Unpack the contents of a rock. +local unpack = {} +package.loaded["luarocks.unpack"] = unpack + +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local build = require("luarocks.build") +local dir = require("luarocks.dir") +local cfg = require("luarocks.cfg") + +util.add_run_function(unpack) +unpack.help_summary = "Unpack the contents of a rock." +unpack.help_arguments = "[--force] {| []}" +unpack.help = [[ +Unpacks the contents of a rock in a newly created directory. +Argument may be a rock file, or the name of a rock in a rocks server. +In the latter case, the app version may be given as a second argument. + +--force Unpack files even if the output directory already exists. +]] + +--- Load a rockspec file to the given directory, fetches the source +-- files specified in the rockspec, and unpack them inside the directory. +-- @param rockspec_file string: The URL for a rockspec file. +-- @param dir_name string: The directory where to store and unpack files. +-- @return table or (nil, string): the loaded rockspec table or +-- nil and an error message. +local function unpack_rockspec(rockspec_file, dir_name) + assert(type(rockspec_file) == "string") + assert(type(dir_name) == "string") + + local rockspec, err = fetch.load_rockspec(rockspec_file) + if not rockspec then + return nil, "Failed loading rockspec "..rockspec_file..": "..err + end + local ok, err = fs.change_dir(dir_name) + if not ok then return nil, err end + local ok, sources_dir = fetch.fetch_sources(rockspec, true, ".") + if not ok then + return nil, sources_dir + end + ok, err = fs.change_dir(sources_dir) + if not ok then return nil, err end + ok, err = build.apply_patches(rockspec) + fs.pop_dir() + if not ok then return nil, err end + return rockspec +end + +--- Load a .rock file to the given directory and unpack it inside it. +-- @param rock_file string: The URL for a .rock file. +-- @param dir_name string: The directory where to unpack. +-- @param kind string: the kind of rock file, as in the second-level +-- extension in the rock filename (eg. "src", "all", "linux-x86") +-- @return table or (nil, string): the loaded rockspec table or +-- nil and an error message. +local function unpack_rock(rock_file, dir_name, kind) + assert(type(rock_file) == "string") + assert(type(dir_name) == "string") + + local ok, err, errcode = fetch.fetch_and_unpack_rock(rock_file, dir_name) + if not ok then + return nil, "Failed unzipping rock "..rock_file, errcode + end + ok, err = fs.change_dir(dir_name) + if not ok then return nil, err end + local rockspec_file = dir_name..".rockspec" + local rockspec, err = fetch.load_rockspec(rockspec_file) + if not rockspec then + return nil, "Failed loading rockspec "..rockspec_file..": "..err + end + if kind == "src" then + if rockspec.source.file then + local ok, err = fs.unpack_archive(rockspec.source.file) + if not ok then + return nil, err + end + ok, err = fs.change_dir(rockspec.source.dir) + if not ok then return nil, err end + ok, err = build.apply_patches(rockspec) + fs.pop_dir() + if not ok then return nil, err end + end + end + return rockspec +end + +--- Create a directory and perform the necessary actions so that +-- the sources for the rock and its rockspec are unpacked inside it, +-- laid out properly so that the 'make' command is able to build the module. +-- @param file string: A rockspec or .rock URL. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +local function run_unpacker(file, force) + assert(type(file) == "string") + + local base_name = dir.base_name(file) + local dir_name, kind, extension = base_name:match("(.*)%.([^.]+)%.(rock)$") + if not extension then + dir_name, extension = base_name:match("(.*)%.(rockspec)$") + kind = "rockspec" + end + if not extension then + return nil, file.." does not seem to be a valid filename." + end + + local exists = fs.exists(dir_name) + if exists and not force then + return nil, "Directory "..dir_name.." already exists." + end + if not exists then + local ok, err = fs.make_dir(dir_name) + if not ok then return nil, err end + end + local rollback = util.schedule_function(fs.delete, fs.absolute_name(dir_name)) + + local rockspec, err + if extension == "rock" then + rockspec, err = unpack_rock(file, dir_name, kind) + elseif extension == "rockspec" then + rockspec, err = unpack_rockspec(file, dir_name) + end + if not rockspec then + return nil, err + end + if kind == "src" or kind == "rockspec" then + if rockspec.source.dir ~= "." then + local ok = fs.copy(rockspec.local_filename, rockspec.source.dir, cfg.perm_read) + if not ok then + return nil, "Failed copying unpacked rockspec into unpacked source directory." + end + end + util.printout() + util.printout("Done. You may now enter directory ") + util.printout(dir.path(dir_name, rockspec.source.dir)) + util.printout("and type 'luarocks make' to build.") + end + util.remove_scheduled_function(rollback) + return true +end + +--- Driver function for the "unpack" command. +-- @param name string: may be a rock filename, for unpacking a +-- rock file or the name of a rock to be fetched and unpacked. +-- @param version string or nil: if the name of a package is given, a +-- version may also be passed. +-- @return boolean or (nil, string): true if successful or nil followed +-- by an error message. +function unpack.command(flags, name, version) + assert(type(version) == "string" or not version) + if type(name) ~= "string" then + return nil, "Argument missing. "..util.see_help("unpack") + end + + if name:match(".*%.rock") or name:match(".*%.rockspec") then + return run_unpacker(name, flags["force"]) + else + local search = require("luarocks.search") + return search.act_on_src_or_rockspec(run_unpacker, name:lower(), version) + end +end + +return unpack diff --git a/Utils/luarocks/lua/luarocks/upload.lua b/Utils/luarocks/lua/luarocks/upload.lua new file mode 100644 index 000000000..7c0c416ce --- /dev/null +++ b/Utils/luarocks/lua/luarocks/upload.lua @@ -0,0 +1,95 @@ + +local upload = {} + +local util = require("luarocks.util") +local fetch = require("luarocks.fetch") +local pack = require("luarocks.pack") +local cfg = require("luarocks.cfg") +local Api = require("luarocks.upload.api") + +util.add_run_function(upload) +upload.help_summary = "Upload a rockspec to the public rocks repository." +upload.help_arguments = "[--skip-pack] [--api-key=] [--force] " +upload.help = [[ + Pack a source rock file (.src.rock extension), + upload rockspec and source rock to server. +--skip-pack Do not pack and send source rock. +--api-key= Give it an API key. It will be stored for subsequent uses. +--force Replace existing rockspec if the same revision of + a module already exists. This should be used only + in case of upload mistakes: when updating a rockspec, + increment the revision number instead. +]] + +function upload.command(flags, fname) + if not fname then + return nil, "Missing rockspec. "..util.see_help("upload") + end + + local api, err = Api.new(flags) + if not api then + return nil, err + end + if cfg.verbose then + api.debug = true + end + + local rockspec, err, errcode = fetch.load_rockspec(fname) + if err then + return nil, err, errcode + end + + util.printout("Sending " .. tostring(fname) .. " ...") + local res, err = api:method("check_rockspec", { + package = rockspec.package, + version = rockspec.version + }) + if not res then return nil, err end + + if not res.module then + util.printout("Will create new module (" .. tostring(rockspec.package) .. ")") + end + if res.version and not flags["force"] then + return nil, "Revision "..rockspec.version.." already exists on the server. "..util.see_help("upload") + end + + local rock_fname + if not flags["skip-pack"] and not rockspec.version:match("^scm") then + util.printout("Packing " .. tostring(rockspec.package)) + rock_fname, err = pack.pack_source_rock(fname) + if not rock_fname then + return nil, err + end + end + + local multipart = require("luarocks.upload.multipart") + + res, err = api:method("upload", nil, { + rockspec_file = multipart.new_file(fname) + }) + if not res then return nil, err end + + if res.is_new and #res.manifests == 0 then + util.printerr("Warning: module not added to root manifest due to name taken.") + end + + local module_url = res.module_url + + if rock_fname then + if (not res.version) or (not res.version.id) then + return nil, "Invalid response from server." + end + util.printout(("Sending " .. tostring(rock_fname) .. " ...")) + res, err = api:method("upload_rock/" .. ("%d"):format(res.version.id), nil, { + rock_file = multipart.new_file(rock_fname) + }) + if not res then return nil, err end + end + + util.printout() + util.printout("Done: " .. tostring(module_url)) + util.printout() + return true +end + +return upload diff --git a/Utils/luarocks/lua/luarocks/upload/api.lua b/Utils/luarocks/lua/luarocks/upload/api.lua new file mode 100644 index 000000000..ba657186a --- /dev/null +++ b/Utils/luarocks/lua/luarocks/upload/api.lua @@ -0,0 +1,284 @@ + +local api = {} + +local cfg = require("luarocks.cfg") +local fs = require("luarocks.fs") +local util = require("luarocks.util") +local persist = require("luarocks.persist") +local multipart = require("luarocks.upload.multipart") + +local Api = {} + +local function upload_config_file() + local conf = cfg.which_config() + if not conf.user.file then + return nil + end + return (conf.user.file:gsub("/[^/]+$", "/upload_config.lua")) +end + +function Api:load_config() + local upload_conf = upload_config_file() + if not upload_conf then return nil end + local cfg, err = persist.load_into_table(upload_conf) + return cfg +end + +function Api:save_config() + -- Test configuration before saving it. + local res, err = self:raw_method("status") + if not res then + return nil, err + end + if res.errors then + util.printerr("Server says: " .. tostring(res.errors[1])) + return + end + local upload_conf = upload_config_file() + if not upload_conf then return nil end + persist.save_from_table(upload_conf, self.config) + fs.chmod(upload_conf, "0600") +end + +function Api:check_version() + if not self._server_tool_version then + local tool_version = cfg.upload.tool_version + local res, err = self:request(tostring(self.config.server) .. "/api/tool_version", { + current = tool_version + }) + if not res then + return nil, err + end + if not res.version then + return nil, "failed to fetch tool version" + end + self._server_tool_version = res.version + if res.force_update then + return nil, "Your upload client is too out of date to continue, please upgrade LuaRocks." + end + if res.version ~= tool_version then + util.printerr("Warning: Your LuaRocks is out of date, consider upgrading.") + end + end + return true +end + +function Api:method(...) + local res, err = self:raw_method(...) + if not res then + return nil, err + end + if res.errors then + if res.errors[1] == "Invalid key" then + return nil, res.errors[1] .. " (use the --api-key flag to change)" + end + local msg = table.concat(res.errors, ", ") + return nil, "API Failed: " .. msg + end + return res +end + +function Api:raw_method(path, ...) + self:check_version() + local url = tostring(self.config.server) .. "/api/" .. tostring(cfg.upload.api_version) .. "/" .. tostring(self.config.key) .. "/" .. tostring(path) + return self:request(url, ...) +end + +local function encode_query_string(t, sep) + if sep == nil then + sep = "&" + end + local i = 0 + local buf = { } + for k, v in pairs(t) do + if type(k) == "number" and type(v) == "table" then + k, v = v[1], v[2] + end + buf[i + 1] = multipart.url_escape(k) + buf[i + 2] = "=" + buf[i + 3] = multipart.url_escape(v) + buf[i + 4] = sep + i = i + 4 + end + buf[i] = nil + return table.concat(buf) +end + +-- An ode to the multitude of JSON libraries out there... +local function require_json() + local list = { "cjson", "dkjson", "json" } + for _, lib in ipairs(list) do + local json_ok, json = pcall(require, lib) + if json_ok then + pcall(json.use_lpeg) -- optional feature in dkjson + return json_ok, json + end + end + local errmsg = "Failed loading " + for i, name in ipairs(list) do + if i == #list then + errmsg = errmsg .."and '"..name.."'. Use 'luarocks search ' to search for a library and 'luarocks install ' to install one." + else + errmsg = errmsg .."'"..name.."', " + end + end + return nil, errmsg +end + +local function redact_api_url(url) + url = tostring(url) + return (url:gsub(".*/api/[^/]+/[^/]+", "")) or "" +end + +local ltn12_ok, ltn12 = pcall(require, "ltn12") +if not ltn12_ok then -- If not using LuaSocket and/or LuaSec... + +function Api:request(url, params, post_params) + local vars = cfg.variables + local json_ok, json = require_json() + if not json_ok then return nil, "A JSON library is required for this command. "..json end + + if cfg.downloader == "wget" then + local curl_ok, err = fs.is_tool_available(vars.CURL, "curl") + if not curl_ok then + return nil, err + end + end + + if not self.config.key then + return nil, "Must have API key before performing any actions." + end + if params and next(params) then + url = url .. ("?" .. encode_query_string(params)) + end + local method = "GET" + local out + local tmpfile = fs.tmpname() + if post_params then + method = "POST" + local curl_cmd = fs.Q(vars.CURL).." -f -k -L --silent --user-agent \""..cfg.user_agent.." via curl\" " + for k,v in pairs(post_params) do + local var = v + if type(v) == "table" then + var = "@"..v.fname + end + curl_cmd = curl_cmd .. "--form \""..k.."="..var.."\" " + end + if cfg.connection_timeout and cfg.connection_timeout > 0 then + curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." " + end + local ok = fs.execute_string(curl_cmd..fs.Q(url).." -o "..fs.Q(tmpfile)) + if not ok then + return nil, "API failure: " .. redact_api_url(url) + end + else + local ok, err = fs.download(url, tmpfile) + if not ok then + return nil, "API failure: " .. tostring(err) .. " - " .. redact_api_url(url) + end + end + + local tmpfd = io.open(tmpfile) + if not tmpfd then + os.remove(tmpfile) + return nil, "API failure reading temporary file - " .. redact_api_url(url) + end + out = tmpfd:read("*a") + tmpfd:close() + os.remove(tmpfile) + + if self.debug then + util.printout("[" .. tostring(method) .. " via curl] " .. redact_api_url(url) .. " ... ") + end + + return json.decode(out) +end + +else -- use LuaSocket and LuaSec + +local warned_luasec = false + +function Api:request(url, params, post_params) + local json_ok, json = require_json() + if not json_ok then return nil, "A JSON library is required for this command. "..json end + local server = tostring(self.config.server) + local http_ok, http + local via = "luasocket" + if server:match("^https://") then + http_ok, http = pcall(require, "ssl.https") + if http_ok then + via = "luasec" + else + if not warned_luasec then + util.printerr("LuaSec is not available; using plain HTTP. Install 'luasec' to enable HTTPS.") + warned_luasec = true + end + http_ok, http = pcall(require, "socket.http") + url = url:gsub("^https", "http") + via = "luasocket" + end + else + http_ok, http = pcall(require, "socket.http") + end + if not http_ok then + return nil, "Failed loading socket library!" + end + + if not self.config.key then + return nil, "Must have API key before performing any actions." + end + local body + local headers = {} + if params and next(params) then + url = url .. ("?" .. encode_query_string(params)) + end + if post_params then + local boundary + body, boundary = multipart.encode(post_params) + headers["Content-length"] = #body + headers["Content-type"] = "multipart/form-data; boundary=" .. tostring(boundary) + end + local method = post_params and "POST" or "GET" + if self.debug then + util.printout("[" .. tostring(method) .. " via "..via.."] " .. redact_api_url(url) .. " ... ") + end + local out = {} + local _, status = http.request({ + url = url, + headers = headers, + method = method, + sink = ltn12.sink.table(out), + source = body and ltn12.source.string(body) + }) + if self.debug then + util.printout(tostring(status)) + end + if status ~= 200 then + return nil, "API returned " .. tostring(status) .. " - " .. redact_api_url(url) + end + return json.decode(table.concat(out)) +end + +end + +function api.new(flags) + local self = {} + setmetatable(self, { __index = Api }) + self.config = self:load_config() or {} + self.config.server = flags["server"] or self.config.server or cfg.upload.server + self.config.version = self.config.version or cfg.upload.version + self.config.key = flags["api-key"] or self.config.key + self.debug = flags["debug"] + if not self.config.key then + return nil, "You need an API key to upload rocks.\n" .. + "Navigate to "..self.config.server.."/settings to get a key\n" .. + "and then pass it through the --api-key= flag." + end + if flags["api-key"] then + self:save_config() + end + return self +end + +return api + diff --git a/Utils/luarocks/lua/luarocks/upload/multipart.lua b/Utils/luarocks/lua/luarocks/upload/multipart.lua new file mode 100644 index 000000000..aad2e4398 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/upload/multipart.lua @@ -0,0 +1,111 @@ + +local multipart = {} + +local File = {} + +local unpack = unpack or table.unpack + +math.randomseed(os.time()) + +-- socket.url.escape(s) from LuaSocket 3.0rc1 +function multipart.url_escape(s) + return (string.gsub(s, "([^A-Za-z0-9_])", function(c) + return string.format("%%%02x", string.byte(c)) + end)) +end + +function File:mime() + if not self.mimetype then + local mimetypes_ok, mimetypes = pcall(require, "mimetypes") + if mimetypes_ok then + self.mimetype = mimetypes.guess(self.fname) + end + self.mimetype = self.mimetype or "application/octet-stream" + end + return self.mimetype +end + +function File:content() + local fd = io.open(self.fname, "rb") + if not fd then + return nil, "Failed to open file: "..self.fname + end + local data = fd:read("*a") + fd:close() + return data +end + +local function rand_string(len) + local shuffled = {} + for i = 1, len do + local r = math.random(97, 122) + if math.random() >= 0.5 then + r = r - 32 + end + shuffled[i] = r + end + return string.char(unpack(shuffled)) +end + +-- multipart encodes params +-- returns encoded string,boundary +-- params is an a table of tuple tables: +-- params = { +-- {key1, value2}, +-- {key2, value2}, +-- key3: value3 +-- } +function multipart.encode(params) + local tuples = { } + for i = 1, #params do + tuples[i] = params[i] + end + for k,v in pairs(params) do + if type(k) == "string" then + table.insert(tuples, {k, v}) + end + end + local chunks = {} + for _, tuple in ipairs(tuples) do + local k,v = unpack(tuple) + k = multipart.url_escape(k) + local buffer = { 'Content-Disposition: form-data; name="' .. k .. '"' } + local content + if type(v) == "table" and v.__class == File then + buffer[1] = buffer[1] .. ('; filename="' .. v.fname:gsub(".*/", "") .. '"') + table.insert(buffer, "Content-type: " .. v:mime()) + content = v:content() + else + content = v + end + table.insert(buffer, "") + table.insert(buffer, content) + table.insert(chunks, table.concat(buffer, "\r\n")) + end + local boundary + while not boundary do + boundary = "Boundary" .. rand_string(16) + for _, chunk in ipairs(chunks) do + if chunk:find(boundary) then + boundary = nil + break + end + end + end + local inner = "\r\n--" .. boundary .. "\r\n" + return table.concat({ "--", boundary, "\r\n", + table.concat(chunks, inner), + "\r\n", "--", boundary, "--", "\r\n" }), boundary +end + +function multipart.new_file(fname, mime) + local self = {} + setmetatable(self, { __index = File }) + self.__class = File + self.fname = fname + self.mimetype = mime + return self +end + +return multipart + diff --git a/Utils/luarocks/lua/luarocks/util.lua b/Utils/luarocks/lua/luarocks/util.lua new file mode 100644 index 000000000..c9fb7d637 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/util.lua @@ -0,0 +1,714 @@ + +--- Assorted utilities for managing tables, plus a scheduler for rollback functions. +-- Does not requires modules directly (only as locals +-- inside specific functions) to avoid interdependencies, +-- as this is used in the bootstrapping stage of luarocks.cfg. + +local util = {} + +local unpack = unpack or table.unpack + +local scheduled_functions = {} +local debug = require("debug") + +--- Schedule a function to be executed upon program termination. +-- This is useful for actions such as deleting temporary directories +-- or failure rollbacks. +-- @param f function: Function to be executed. +-- @param ... arguments to be passed to function. +-- @return table: A token representing the scheduled execution, +-- which can be used to remove the item later from the list. +function util.schedule_function(f, ...) + assert(type(f) == "function") + + local item = { fn = f, args = {...} } + table.insert(scheduled_functions, item) + return item +end + +--- Unschedule a function. +-- This is useful for cancelling a rollback of a completed operation. +-- @param item table: The token representing the scheduled function that was +-- returned from the schedule_function call. +function util.remove_scheduled_function(item) + for k, v in pairs(scheduled_functions) do + if v == item then + table.remove(scheduled_functions, k) + return + end + end +end + +--- Execute scheduled functions. +-- Some calls create temporary files and/or directories and register +-- corresponding cleanup functions. Calling this function will run +-- these function, erasing temporaries. +-- Functions are executed in the inverse order they were scheduled. +function util.run_scheduled_functions() + local fs = require("luarocks.fs") + fs.change_dir_to_root() + for i = #scheduled_functions, 1, -1 do + local item = scheduled_functions[i] + item.fn(unpack(item.args)) + end +end + +--- Produce a Lua pattern that matches precisely the given string +-- (this is suitable to be concatenating to other patterns, +-- so it does not include beginning- and end-of-string markers (^$) +-- @param s string: The input string +-- @return string: The equivalent pattern +function util.matchquote(s) + return (s:gsub("[?%-+*%[%].%%()$^]","%%%1")) +end + +--- List of supported arguments. +-- Arguments that take no parameters are marked with the boolean true. +-- Arguments that take a parameter are marked with a descriptive string. +-- Arguments that may take an empty string are described in quotes, +-- (as in the value for --detailed=""). +-- For all other string values, it means the parameter is mandatory. +local supported_flags = { + ["all"] = true, + ["api-key"] = "", + ["append"] = true, + ["arch"] = "", + ["bin"] = true, + ["binary"] = true, + ["branch"] = "", + ["debug"] = true, + ["deps"] = true, + ["deps-mode"] = "", + ["detailed"] = "\"\"", + ["force"] = true, + ["force-fast"] = true, + ["from"] = "", + ["help"] = true, + ["home"] = true, + ["homepage"] = "\"\"", + ["keep"] = true, + ["lib"] = "", + ["license"] = "\"\"", + ["list"] = true, + ["local"] = true, + ["local-tree"] = true, + ["lr-bin"] = true, + ["lr-cpath"] = true, + ["lr-path"] = true, + ["lua-version"] = "", + ["lua-ver"] = true, + ["lua-incdir"] = true, + ["lua-libdir"] = true, + ["modules"] = true, + ["mversion"] = true, + ["no-refresh"] = true, + ["nodeps"] = true, + ["old-versions"] = true, + ["only-deps"] = true, + ["only-from"] = "", + ["only-server"] = "", + ["only-sources"] = "", + ["only-sources-from"] = "", + ["outdated"] = true, + ["output"] = "", + ["pack-binary-rock"] = true, + ["porcelain"] = true, + ["quick"] = true, + ["rock-dir"] = true, + ["rock-tree"] = true, + ["rock-trees"] = true, + ["rockspec"] = true, + ["rockspec-format"] = "", + ["server"] = "", + ["skip-pack"] = true, + ["source"] = true, + ["summary"] = "\"\"", + ["system-config"] = true, + ["tag"] = "", + ["timeout"] = "", + ["to"] = "", + ["tree"] = "", + ["user-config"] = true, + ["verbose"] = true, + ["version"] = true, +} + +--- Extract flags from an arguments list. +-- Given string arguments, extract flag arguments into a flags set. +-- For example, given "foo", "--tux=beep", "--bla", "bar", "--baz", +-- it would return the following: +-- {["bla"] = true, ["tux"] = "beep", ["baz"] = true}, "foo", "bar". +function util.parse_flags(...) + local args = {...} + local flags = {} + local i = 1 + local out = {} + local ignore_flags = false + while i <= #args do + local flag = args[i]:match("^%-%-(.*)") + if flag == "--" then + ignore_flags = true + end + if flag and not ignore_flags then + local var,val = flag:match("([a-z_%-]*)=(.*)") + if val then + local vartype = supported_flags[var] + if type(vartype) == "string" then + if val == "" and vartype:sub(1,1) ~= '"' then + return { ERROR = "Invalid argument: parameter to flag --"..var.."="..vartype.." cannot be empty." } + end + flags[var] = val + else + if vartype then + return { ERROR = "Invalid argument: flag --"..var.." does not take an parameter." } + else + return { ERROR = "Invalid argument: unknown flag --"..var.."." } + end + end + else + local var = flag + local vartype = supported_flags[var] + if type(vartype) == "string" then + i = i + 1 + local val = args[i] + if not val then + return { ERROR = "Invalid argument: flag --"..var.."="..vartype.." expects a parameter." } + end + if val:match("^%-%-.*") then + return { ERROR = "Invalid argument: flag --"..var.."="..vartype.." expects a parameter (if you really want to pass "..val.." as an argument to --"..var..", use --"..var.."="..val..")." } + else + if val == "" and vartype:sub(1,1) ~= '"' then + return { ERROR = "Invalid argument: parameter to flag --"..var.."="..vartype.." cannot be empty." } + end + flags[var] = val + end + elseif vartype == true then + flags[var] = true + else + return { ERROR = "Invalid argument: unknown flag --"..var.."." } + end + end + else + table.insert(out, args[i]) + end + i = i + 1 + end + return flags, unpack(out) +end + +-- Adds legacy 'run' function to a command module. +-- @param command table: command module with 'command' function, +-- the added 'run' function calls it after parseing command-line arguments. +function util.add_run_function(command) + command.run = function(...) return command.command(util.parse_flags(...)) end +end + +--- Merges contents of src on top of dst's contents. +-- @param dst Destination table, which will receive src's contents. +-- @param src Table which provides new contents to dst. +-- @see platform_overrides +function util.deep_merge(dst, src) + for k, v in pairs(src) do + if type(v) == "table" then + if not dst[k] then + dst[k] = {} + end + if type(dst[k]) == "table" then + util.deep_merge(dst[k], v) + else + dst[k] = v + end + else + dst[k] = v + end + end +end + +--- Perform platform-specific overrides on a table. +-- Overrides values of table with the contents of the appropriate +-- subset of its "platforms" field. The "platforms" field should +-- be a table containing subtables keyed with strings representing +-- platform names. Names that match the contents of the global +-- cfg.platforms setting are used. For example, if +-- cfg.platforms= {"foo"}, then the fields of +-- tbl.platforms.foo will overwrite those of tbl with the same +-- names. For table values, the operation is performed recursively +-- (tbl.platforms.foo.x.y.z overrides tbl.x.y.z; other contents of +-- tbl.x are preserved). +-- @param tbl table or nil: Table which may contain a "platforms" field; +-- if it doesn't (or if nil is passed), this function does nothing. +function util.platform_overrides(tbl) + assert(type(tbl) == "table" or not tbl) + + local cfg = require("luarocks.cfg") + + if not tbl then return end + + if tbl.platforms then + for _, platform in ipairs(cfg.platforms) do + local platform_tbl = tbl.platforms[platform] + if platform_tbl then + util.deep_merge(tbl, platform_tbl) + end + end + end + tbl.platforms = nil +end + +local var_format_pattern = "%$%((%a[%a%d_]+)%)" + +--- Create a new shallow copy of a table: a new table with +-- the same keys and values. Keys point to the same objects as +-- the original table (ie, does not copy recursively). +-- @param tbl table: the input table +-- @return table: a new table with the same contents. +function util.make_shallow_copy(tbl) + local copy = {} + for k,v in pairs(tbl) do + copy[k] = v + end + return copy +end + +-- Check if a set of needed variables are referenced +-- somewhere in a list of definitions, warning the user +-- about any unused ones. Each key in needed_set should +-- appear as a $(XYZ) variable at least once as a +-- substring of some value of var_defs. +-- @param var_defs: a table with string keys and string +-- values, containing variable definitions. +-- @param needed_set: a set where keys are the names of +-- needed variables. +-- @param msg string: the warning message to display. +function util.warn_if_not_used(var_defs, needed_set, msg) + needed_set = util.make_shallow_copy(needed_set) + for _, val in pairs(var_defs) do + for used in val:gmatch(var_format_pattern) do + needed_set[used] = nil + end + end + for var, _ in pairs(needed_set) do + util.warning(msg:format(var)) + end +end + +-- Output any entries that might remain in $(XYZ) format, +-- warning the user that substitutions have failed. +-- @param line string: the input string +local function warn_failed_matches(line) + local any_failed = false + if line:match(var_format_pattern) then + for unmatched in line:gmatch(var_format_pattern) do + util.warning("unmatched variable " .. unmatched) + any_failed = true + end + end + return any_failed +end + +--- Perform make-style variable substitutions on string values of a table. +-- For every string value tbl.x which contains a substring of the format +-- "$(XYZ)" will have this substring replaced by vars["XYZ"], if that field +-- exists in vars. Only string values are processed; this function +-- does not scan subtables recursively. +-- @param tbl table: Table to have its string values modified. +-- @param vars table: Table containing string-string key-value pairs +-- representing variables to replace in the strings values of tbl. +function util.variable_substitutions(tbl, vars) + assert(type(tbl) == "table") + assert(type(vars) == "table") + + local updated = {} + for k, v in pairs(tbl) do + if type(v) == "string" then + updated[k] = v:gsub(var_format_pattern, vars) + if warn_failed_matches(updated[k]) then + updated[k] = updated[k]:gsub(var_format_pattern, "") + end + end + end + for k, v in pairs(updated) do + tbl[k] = v + end +end + +--- Return an array of keys of a table. +-- @param tbl table: The input table. +-- @return table: The array of keys. +function util.keys(tbl) + local ks = {} + for k,_ in pairs(tbl) do + table.insert(ks, k) + end + return ks +end + +local function default_sort(a, b) + local ta = type(a) + local tb = type(b) + if ta == "number" and tb == "number" then + return a < b + elseif ta == "number" then + return true + elseif tb == "number" then + return false + else + return tostring(a) < tostring(b) + end +end + +--- A table iterator generator that returns elements sorted by key, +-- to be used in "for" loops. +-- @param tbl table: The table to be iterated. +-- @param sort_function function or table or nil: An optional comparison function +-- to be used by table.sort when sorting keys, or an array listing an explicit order +-- for keys. If a value itself is an array, it is taken so that the first element +-- is a string representing the field name, and the second element is a priority table +-- for that key, which is returned by the iterator as the third value after the key +-- and the value. +-- @return function: the iterator function. +function util.sortedpairs(tbl, sort_function) + sort_function = sort_function or default_sort + local keys = util.keys(tbl) + local sub_orders = {} + + if type(sort_function) == "function" then + table.sort(keys, sort_function) + else + local order = sort_function + local ordered_keys = {} + local all_keys = keys + keys = {} + + for _, order_entry in ipairs(order) do + local key, sub_order + if type(order_entry) == "table" then + key = order_entry[1] + sub_order = order_entry[2] + else + key = order_entry + end + + if tbl[key] then + ordered_keys[key] = true + sub_orders[key] = sub_order + table.insert(keys, key) + end + end + + table.sort(all_keys, default_sort) + for _, key in ipairs(all_keys) do + if not ordered_keys[key] then + table.insert(keys, key) + end + end + end + + local i = 1 + return function() + local key = keys[i] + i = i + 1 + return key, tbl[key], sub_orders[key] + end +end + +function util.lua_versions() + local versions = { "5.1", "5.2", "5.3" } + local i = 0 + return function() + i = i + 1 + return versions[i] + end +end + +function util.starts_with(s, prefix) + return s:sub(1,#prefix) == prefix +end + +--- Print a line to standard output +function util.printout(...) + io.stdout:write(table.concat({...},"\t")) + io.stdout:write("\n") +end + +--- Print a line to standard error +function util.printerr(...) + io.stderr:write(table.concat({...},"\t")) + io.stderr:write("\n") +end + +--- Display a warning message. +-- @param msg string: the warning message +function util.warning(msg) + util.printerr("Warning: "..msg) +end + +function util.title(msg, porcelain, underline) + if porcelain then return end + util.printout() + util.printout(msg) + util.printout((underline or "-"):rep(#msg)) + util.printout() +end + +function util.this_program(default) + local i = 1 + local last, cur = default, default + while i do + local dbg = debug.getinfo(i,"S") + if not dbg then break end + last = cur + cur = dbg.source + i=i+1 + end + return last:sub(2) +end + +function util.deps_mode_help(program) + local cfg = require("luarocks.cfg") + return [[ +--deps-mode= How to handle dependencies. Four modes are supported: + * all - use all trees from the rocks_trees list + for finding dependencies + * one - use only the current tree (possibly set + with --tree) + * order - use trees based on order (use the current + tree and all trees below it on the rocks_trees list) + * none - ignore dependencies altogether. + The default mode may be set with the deps_mode entry + in the configuration file. + The current default is "]]..cfg.deps_mode..[[". + Type ']]..util.this_program(program or "luarocks")..[[' with no arguments to see + your list of rocks trees. +]] +end + +function util.see_help(command, program) + return "See '"..util.this_program(program or "luarocks")..' help'..(command and " "..command or "").."'." +end + +function util.announce_install(rockspec) + local cfg = require("luarocks.cfg") + local path = require("luarocks.path") + + local suffix = "" + if rockspec.description and rockspec.description.license then + suffix = " (license: "..rockspec.description.license..")" + end + + local root_dir = path.root_dir(cfg.rocks_dir) + util.printout(rockspec.name.." "..rockspec.version.." is now installed in "..root_dir..suffix) + util.printout() +end + +--- Collect rockspecs located in a subdirectory. +-- @param versions table: A table mapping rock names to newest rockspec versions. +-- @param paths table: A table mapping rock names to newest rockspec paths. +-- @param unnamed_paths table: An array of rockspec paths that don't contain rock +-- name and version in regular format. +-- @param subdir string: path to subdirectory. +local function collect_rockspecs(versions, paths, unnamed_paths, subdir) + local fs = require("luarocks.fs") + local dir = require("luarocks.dir") + local path = require("luarocks.path") + local deps = require("luarocks.deps") + + if fs.is_dir(subdir) then + for file in fs.dir(subdir) do + file = dir.path(subdir, file) + + if file:match("rockspec$") and fs.is_file(file) then + local rock, version = path.parse_name(file) + + if rock then + if not versions[rock] or deps.compare_versions(version, versions[rock]) then + versions[rock] = version + paths[rock] = file + end + else + table.insert(unnamed_paths, file) + end + end + end + end +end + +--- Get default rockspec name for commands that take optional rockspec name. +-- @return string or (nil, string): path to the rockspec or nil and error message. +function util.get_default_rockspec() + local versions, paths, unnamed_paths = {}, {}, {} + -- Look for rockspecs in some common locations. + collect_rockspecs(versions, paths, unnamed_paths, ".") + collect_rockspecs(versions, paths, unnamed_paths, "rockspec") + collect_rockspecs(versions, paths, unnamed_paths, "rockspecs") + + if #unnamed_paths > 0 then + -- There are rockspecs not following "name-version.rockspec" format. + -- More than one are ambiguous. + if #unnamed_paths > 1 then + return nil, "Please specify which rockspec file to use." + else + return unnamed_paths[1] + end + else + local rock = next(versions) + + if rock then + -- If there are rockspecs for multiple rocks it's ambiguous. + if next(versions, rock) then + return nil, "Please specify which rockspec file to use." + else + return paths[rock] + end + else + return nil, "Argument missing: please specify a rockspec to use on current directory." + end + end +end + +-- from http://lua-users.org/wiki/SplitJoin +-- by PhilippeLhoste +function util.split_string(str, delim, maxNb) + -- Eliminate bad cases... + if string.find(str, delim) == nil then + return { str } + end + if maxNb == nil or maxNb < 1 then + maxNb = 0 -- No limit + end + local result = {} + local pat = "(.-)" .. delim .. "()" + local nb = 0 + local lastPos + for part, pos in string.gmatch(str, pat) do + nb = nb + 1 + result[nb] = part + lastPos = pos + if nb == maxNb then break end + end + -- Handle the last field + if nb ~= maxNb then + result[nb + 1] = string.sub(str, lastPos) + end + return result +end + +--- Remove repeated entries from a path-style string. +-- Example: given ("a;b;c;a;b;d", ";"), returns "a;b;c;d". +-- @param list string: A path string (from $PATH or package.path) +-- @param sep string: The separator +function util.remove_path_dupes(list, sep) + assert(type(list) == "string") + assert(type(sep) == "string") + local parts = util.split_string(list, sep) + local final, entries = {}, {} + for _, part in ipairs(parts) do + part = part:gsub("//", "/") + if not entries[part] then + table.insert(final, part) + entries[part] = true + end + end + return table.concat(final, sep) +end + +--- +-- Formats tables with cycles recursively to any depth. +-- References to other tables are shown as values. +-- Self references are indicated. +-- The string returned is "Lua code", which can be procesed +-- (in the case in which indent is composed by spaces or "--"). +-- Userdata and function keys and values are shown as strings, +-- which logically are exactly not equivalent to the original code. +-- This routine can serve for pretty formating tables with +-- proper indentations, apart from printing them: +-- io.write(table.show(t, "t")) -- a typical use +-- Written by Julio Manuel Fernandez-Diaz, +-- Heavily based on "Saving tables with cycles", PIL2, p. 113. +-- @param t table: is the table. +-- @param name string: is the name of the table (optional) +-- @param indent string: is a first indentation (optional). +-- @return string: the pretty-printed table +function util.show_table(t, name, indent) + local cart -- a container + local autoref -- for self references + + local function isemptytable(t) return next(t) == nil end + + local function basicSerialize (o) + local so = tostring(o) + if type(o) == "function" then + local info = debug.getinfo(o, "S") + -- info.name is nil because o is not a calling level + if info.what == "C" then + return ("%q"):format(so .. ", C function") + else + -- the information is defined through lines + return ("%q"):format(so .. ", defined in (" .. info.linedefined .. "-" .. info.lastlinedefined .. ")" .. info.source) + end + elseif type(o) == "number" then + return so + else + return ("%q"):format(so) + end + end + + local function addtocart (value, name, indent, saved, field) + indent = indent or "" + saved = saved or {} + field = field or name + + cart = cart .. indent .. field + + if type(value) ~= "table" then + cart = cart .. " = " .. basicSerialize(value) .. ";\n" + else + if saved[value] then + cart = cart .. " = {}; -- " .. saved[value] .. " (self reference)\n" + autoref = autoref .. name .. " = " .. saved[value] .. ";\n" + else + saved[value] = name + --if tablecount(value) == 0 then + if isemptytable(value) then + cart = cart .. " = {};\n" + else + cart = cart .. " = {\n" + for k, v in pairs(value) do + k = basicSerialize(k) + local fname = ("%s[%s]"):format(name, k) + field = ("[%s]"):format(k) + -- three spaces between levels + addtocart(v, fname, indent .. " ", saved, field) + end + cart = cart .. indent .. "};\n" + end + end + end + end + + name = name or "__unnamed__" + if type(t) ~= "table" then + return name .. " = " .. basicSerialize(t) + end + cart, autoref = "", "" + addtocart(t, name, indent) + return cart .. autoref +end + +function util.array_contains(tbl, value) + for _, v in ipairs(tbl) do + if v == value then + return true + end + end + return false +end + +-- Quote Lua string, analogous to fs.Q. +-- @param s A string, such as "hello" +-- @return string: A quoted string, such as '"hello"' +function util.LQ(s) + return ("%q"):format(s) +end + +return util diff --git a/Utils/luarocks/lua/luarocks/validate.lua b/Utils/luarocks/lua/luarocks/validate.lua new file mode 100644 index 000000000..c4570aa4b --- /dev/null +++ b/Utils/luarocks/lua/luarocks/validate.lua @@ -0,0 +1,159 @@ + +--- Sandboxed test of build/install of all packages in a repository (unfinished and disabled). +local validate = {} +package.loaded["luarocks.validate"] = validate + +local fs = require("luarocks.fs") +local dir = require("luarocks.dir") +local path = require("luarocks.path") +local cfg = require("luarocks.cfg") +local build = require("luarocks.build") +local install = require("luarocks.install") +local util = require("luarocks.util") + +util.add_run_function(validate) +validate.help_summary = "Sandboxed test of build/install of all packages in a repository." + +validate.help = [[ +, if given, is a local repository pathname. +]] + +local function save_settings(repo) + local protocol, path = dir.split_url(repo) + table.insert(cfg.rocks_servers, 1, protocol.."://"..path) + return { + root_dir = cfg.root_dir, + rocks_dir = cfg.rocks_dir, + deploy_bin_dir = cfg.deploy_bin_dir, + deploy_lua_dir = cfg.deploy_lua_dir, + deploy_lib_dir = cfg.deploy_lib_dir, + } +end + +local function restore_settings(settings) + cfg.root_dir = settings.root_dir + cfg.rocks_dir = settings.rocks_dir + cfg.deploy_bin_dir = settings.deploy_bin_dir + cfg.deploy_lua_dir = settings.deploy_lua_dir + cfg.deploy_lib_dir = settings.deploy_lib_dir + cfg.variables.ROCKS_TREE = settings.rocks_dir + cfg.variables.SCRIPTS_DIR = settings.deploy_bin_dir + table.remove(cfg.rocks_servers, 1) +end + +local function prepare_sandbox(file) + local root_dir = fs.make_temp_dir(file):gsub("/+$", "") + cfg.root_dir = root_dir + cfg.rocks_dir = path.rocks_dir(root_dir) + cfg.deploy_bin_dir = path.deploy_bin_dir(root_dir) + cfg.variables.ROCKS_TREE = cfg.rocks_dir + cfg.variables.SCRIPTS_DIR = cfg.deploy_bin_dir + return root_dir +end + +local function validate_rockspec(file) + local ok, err, errcode = build.build_rockspec(file, true, "one") + if not ok then + util.printerr(err) + end + return ok, err, errcode +end + +local function validate_src_rock(file) + local ok, err, errcode = build.build_rock(file, false, "one") + if not ok then + util.printerr(err) + end + return ok, err, errcode +end + +local function validate_rock(file) + local ok, err, errcode = install.install_binary_rock(file, "one") + if not ok then + util.printerr(err) + end + return ok, err, errcode +end + +function validate.command(flags, repo) + repo = repo or cfg.rocks_dir + + util.printout("Verifying contents of "..repo) + + local results = { + ok = {} + } + local settings = save_settings(repo) + local sandbox + if flags["quick"] then + sandbox = prepare_sandbox("luarocks_validate") + end + if not fs.exists(repo) then + return nil, repo.." is not a local repository." + end + for file in fs.dir(repo) do for _=1,1 do + if file == "manifest" or file == "index.html" then + break -- continue for + end + local pathname = fs.absolute_name(dir.path(repo, file)) + if not flags["quick"] then + sandbox = prepare_sandbox(file) + end + local ok, err, errcode + util.printout() + util.printout("Verifying "..pathname) + if file:match("%.rockspec$") then + ok, err, errcode = validate_rockspec(pathname, "one") + elseif file:match("%.src%.rock$") then + ok, err, errcode = validate_src_rock(pathname) + elseif file:match("%.rock$") then + ok, err, errcode = validate_rock(pathname) + end + if ok then + table.insert(results.ok, {file=file} ) + else + if not errcode then + errcode = "misc" + end + if not results[errcode] then + results[errcode] = {} + end + table.insert(results[errcode], {file=file, err=err} ) + end + util.run_scheduled_functions() + if not flags["quick"] then + fs.delete(sandbox) + end + repeat until not fs.pop_dir() + end end + if flags["quick"] then + fs.delete(sandbox) + end + restore_settings(settings) + util.title("Results:") + util.printout("OK: "..tostring(#results.ok)) + for _, entry in ipairs(results.ok) do + util.printout(entry.file) + end + for errcode, errors in pairs(results) do + if errcode ~= "ok" then + util.printout() + util.printout(errcode.." errors: "..tostring(#errors)) + for _, entry in ipairs(errors) do + util.printout(entry.file, entry.err) + end + end + end + + util.title("Summary:") + local total = 0 + for errcode, errors in pairs(results) do + util.printout(errcode..": "..tostring(#errors)) + total = total + #errors + end + util.printout("Total: "..total) + return true +end + + +return validate diff --git a/Utils/luarocks/lua/luarocks/write_rockspec.lua b/Utils/luarocks/lua/luarocks/write_rockspec.lua new file mode 100644 index 000000000..33edeb1b4 --- /dev/null +++ b/Utils/luarocks/lua/luarocks/write_rockspec.lua @@ -0,0 +1,375 @@ + +local write_rockspec = {} +package.loaded["luarocks.write_rockspec"] = write_rockspec + +local cfg = require("luarocks.cfg") +local dir = require("luarocks.dir") +local fetch = require("luarocks.fetch") +local fs = require("luarocks.fs") +local path = require("luarocks.path") +local persist = require("luarocks.persist") +local type_check = require("luarocks.type_check") +local util = require("luarocks.util") + +util.add_run_function(write_rockspec) +write_rockspec.help_summary = "Write a template for a rockspec file." +write_rockspec.help_arguments = "[--output= ...] [] [] [|]" +write_rockspec.help = [[ +This command writes an initial version of a rockspec file, +based on a name, a version, and a location (an URL or a local path). +If only two arguments are given, the first one is considered the name and the +second one is the location. +If only one argument is given, it must be the location. +If no arguments are given, current directory is used as location. +LuaRocks will attempt to infer name and version if not given, +using 'scm' as default version. + +Note that the generated file is a _starting point_ for writing a +rockspec, and is not guaranteed to be complete or correct. + +--output= Write the rockspec with the given filename. + If not given, a file is written in the current + directory with a filename based on given name and version. +--license="" A license string, such as "MIT/X11" or "GNU GPL v3". +--summary="" A short one-line description summary. +--detailed="" A longer description string. +--homepage= Project homepage. +--lua-version= Supported Lua versions. Accepted values are "5.1", "5.2", + "5.3", "5.1,5.2", "5.2,5.3", or "5.1,5.2,5.3". +--rockspec-format= Rockspec format version, such as "1.0" or "1.1". +--tag= Tag to use. Will attempt to extract version number from it. +--lib=[,] A comma-separated list of libraries that C files need to + link to. +]] + +local function open_file(name) + return io.open(dir.path(fs.current_dir(), name), "r") +end + +local function get_url(rockspec) + local file, temp_dir, err_code, err_file, err_temp_dir = fetch.fetch_sources(rockspec, false) + if err_code == "source.dir" then + file, temp_dir = err_file, err_temp_dir + elseif not file then + util.warning("Could not fetch sources - "..temp_dir) + return false + end + util.printout("File successfully downloaded. Making checksum and checking base dir...") + if fetch.is_basic_protocol(rockspec.source.protocol) then + rockspec.source.md5 = fs.get_md5(file) + end + local inferred_dir, found_dir = fetch.find_base_dir(file, temp_dir, rockspec.source.url) + return true, found_dir or inferred_dir, temp_dir +end + +local function configure_lua_version(rockspec, luaver) + if luaver == "5.1" then + table.insert(rockspec.dependencies, "lua ~> 5.1") + elseif luaver == "5.2" then + table.insert(rockspec.dependencies, "lua ~> 5.2") + elseif luaver == "5.3" then + table.insert(rockspec.dependencies, "lua ~> 5.3") + elseif luaver == "5.1,5.2" then + table.insert(rockspec.dependencies, "lua >= 5.1, < 5.3") + elseif luaver == "5.2,5.3" then + table.insert(rockspec.dependencies, "lua >= 5.2, < 5.4") + elseif luaver == "5.1,5.2,5.3" then + table.insert(rockspec.dependencies, "lua >= 5.1, < 5.4") + else + util.warning("Please specify supported Lua version with --lua-version=. "..util.see_help("write_rockspec")) + end +end + +local function detect_description() + local fd = open_file("README.md") or open_file("README") + if not fd then return end + local data = fd:read("*a") + fd:close() + local paragraph = data:match("\n\n([^%[].-)\n\n") + if not paragraph then paragraph = data:match("\n\n(.*)") end + local summary, detailed + if paragraph then + detailed = paragraph + + if #paragraph < 80 then + summary = paragraph:gsub("\n", "") + else + summary = paragraph:gsub("\n", " "):match("([^.]*%.) ") + end + end + return summary, detailed +end + +local function detect_mit_license(data) + local strip_copyright = (data:gsub("Copyright [^\n]*\n", "")) + local sum = 0 + for i = 1, #strip_copyright do + local num = string.byte(strip_copyright:sub(i,i)) + if num > 32 and num <= 128 then + sum = sum + num + end + end + return sum == 78656 +end + +local simple_scm_protocols = { + git = true, ["git+http"] = true, ["git+https"] = true, + hg = true, ["hg+http"] = true, ["hg+https"] = true +} + +local function detect_url_from_command(program, args, directory) + local command = fs.Q(cfg.variables[program:upper()]).. " "..args + local pipe = io.popen(fs.command_at(directory, fs.quiet_stderr(command))) + if not pipe then return nil end + local url = pipe:read("*a"):match("^([^\r\n]+)") + pipe:close() + if not url then return nil end + if not util.starts_with(url, program.."://") then + url = program.."+"..url + end + + if simple_scm_protocols[dir.split_url(url)] then + return url + end +end + +local function detect_scm_url(directory) + return detect_url_from_command("git", "config --get remote.origin.url", directory) or + detect_url_from_command("hg", "paths default", directory) +end + +local function show_license(rockspec) + local fd = open_file("COPYING") or open_file("LICENSE") or open_file("MIT-LICENSE.txt") + if not fd then return nil end + local data = fd:read("*a") + fd:close() + local is_mit = detect_mit_license(data) + util.title("License for "..rockspec.package..":") + util.printout(data) + util.printout() + return is_mit +end + +local function get_cmod_name(file) + local fd = open_file(file) + if not fd then return nil end + local data = fd:read("*a") + fd:close() + return (data:match("int%s+luaopen_([a-zA-Z0-9_]+)")) +end + +local luamod_blacklist = { + test = true, + tests = true, +} + +local function fill_as_builtin(rockspec, libs) + rockspec.build.type = "builtin" + rockspec.build.modules = {} + local prefix = "" + + for _, parent in ipairs({"src", "lua"}) do + if fs.is_dir(parent) then + fs.change_dir(parent) + prefix = parent.."/" + break + end + end + + local incdirs, libdirs + if libs then + incdirs, libdirs = {}, {} + for _, lib in ipairs(libs) do + local upper = lib:upper() + incdirs[#incdirs+1] = "$("..upper.."_INCDIR)" + libdirs[#libdirs+1] = "$("..upper.."_LIBDIR)" + end + end + + for _, file in ipairs(fs.find()) do + local luamod = file:match("(.*)%.lua$") + if luamod and not luamod_blacklist[luamod] then + rockspec.build.modules[path.path_to_module(file)] = prefix..file + else + local cmod = file:match("(.*)%.c$") + if cmod then + local modname = get_cmod_name(file) or path.path_to_module(file:gsub("%.c$", ".lua")) + rockspec.build.modules[modname] = { + sources = prefix..file, + libraries = libs, + incdirs = incdirs, + libdirs = libdirs, + } + end + end + end + + for _, directory in ipairs({ "doc", "docs", "samples", "tests" }) do + if fs.is_dir(directory) then + if not rockspec.build.copy_directories then + rockspec.build.copy_directories = {} + end + table.insert(rockspec.build.copy_directories, directory) + end + end + + if prefix ~= "" then + fs.pop_dir() + end +end + +local function rockspec_cleanup(rockspec) + rockspec.source.file = nil + rockspec.source.protocol = nil + rockspec.variables = nil + rockspec.name = nil +end + +function write_rockspec.command(flags, name, version, url_or_dir) + if not name then + url_or_dir = "." + elseif not version then + url_or_dir = name + name = nil + elseif not url_or_dir then + url_or_dir = version + version = nil + end + + if flags["tag"] then + if not version then + version = flags["tag"]:gsub("^v", "") + end + end + + local protocol, pathname = dir.split_url(url_or_dir) + if protocol == "file" then + if pathname == "." then + name = name or dir.base_name(fs.current_dir()) + end + elseif fetch.is_basic_protocol(protocol) then + local filename = dir.base_name(url_or_dir) + local newname, newversion = filename:match("(.*)-([^-]+)") + if newname then + name = name or newname + version = version or newversion:gsub("%.[a-z]+$", ""):gsub("%.tar$", "") + end + else + name = name or dir.base_name(url_or_dir):gsub("%.[^.]+$", "") + end + + if not name then + return nil, "Could not infer rock name. "..util.see_help("write_rockspec") + end + version = version or "scm" + + local filename = flags["output"] or dir.path(fs.current_dir(), name:lower().."-"..version.."-1.rockspec") + + local rockspec = { + rockspec_format = flags["rockspec-format"], + package = name, + name = name:lower(), + version = version.."-1", + source = { + url = "*** please add URL for source tarball, zip or repository here ***", + tag = flags["tag"], + }, + description = { + summary = flags["summary"] or "*** please specify description summary ***", + detailed = flags["detailed"] or "*** please enter a detailed description ***", + homepage = flags["homepage"] or "*** please enter a project homepage ***", + license = flags["license"] or "*** please specify a license ***", + }, + dependencies = {}, + build = {}, + } + path.configure_paths(rockspec) + rockspec.source.protocol = protocol + + configure_lua_version(rockspec, flags["lua-version"]) + + local local_dir = url_or_dir + + if url_or_dir:match("://") then + rockspec.source.url = url_or_dir + rockspec.source.file = dir.base_name(url_or_dir) + rockspec.source.dir = "dummy" + if not fetch.is_basic_protocol(rockspec.source.protocol) then + if version ~= "scm" then + rockspec.source.tag = flags["tag"] or "v" .. version + end + end + rockspec.source.dir = nil + local ok, base_dir, temp_dir = get_url(rockspec) + if ok then + if base_dir ~= dir.base_name(url_or_dir) then + rockspec.source.dir = base_dir + end + end + if base_dir then + local_dir = dir.path(temp_dir, base_dir) + else + local_dir = nil + end + else + rockspec.source.url = detect_scm_url(local_dir) or rockspec.source.url + end + + if not local_dir then + local_dir = "." + end + + if not flags["homepage"] then + local url_protocol, url_path = dir.split_url(rockspec.source.url) + + if simple_scm_protocols[url_protocol] then + for _, domain in ipairs({"github.com", "bitbucket.org", "gitlab.com"}) do + if util.starts_with(url_path, domain) then + rockspec.description.homepage = "https://"..url_path:gsub("%.git$", "") + break + end + end + end + end + + local libs = nil + if flags["lib"] then + libs = {} + rockspec.external_dependencies = {} + for lib in flags["lib"]:gmatch("([^,]+)") do + table.insert(libs, lib) + rockspec.external_dependencies[lib:upper()] = { + library = lib + } + end + end + + local ok, err = fs.change_dir(local_dir) + if not ok then return nil, "Failed reaching files from project - error entering directory "..local_dir end + + if (not flags["summary"]) or (not flags["detailed"]) then + local summary, detailed = detect_description() + rockspec.description.summary = flags["summary"] or summary + rockspec.description.detailed = flags["detailed"] or detailed + end + + local is_mit = show_license(rockspec) + + if is_mit and not flags["license"] then + rockspec.description.license = "MIT" + end + + fill_as_builtin(rockspec, libs) + + rockspec_cleanup(rockspec) + + persist.save_from_table(filename, rockspec, type_check.rockspec_order) + + util.printout() + util.printout("Wrote template at "..filename.." -- you should now edit and finish it.") + util.printout() + + return true +end + +return write_rockspec diff --git a/Utils/luarocks/lua5.1.dll b/Utils/luarocks/lua5.1.dll new file mode 100644 index 000000000..b87f3b661 Binary files /dev/null and b/Utils/luarocks/lua5.1.dll differ diff --git a/Utils/luarocks/lua5.1.exe b/Utils/luarocks/lua5.1.exe new file mode 100644 index 000000000..dc1c2c35c Binary files /dev/null and b/Utils/luarocks/lua5.1.exe differ diff --git a/Utils/luarocks/lua5.1.lib b/Utils/luarocks/lua5.1.lib new file mode 100644 index 000000000..df9882765 Binary files /dev/null and b/Utils/luarocks/lua5.1.lib differ diff --git a/Utils/luarocks/lua51.dll b/Utils/luarocks/lua51.dll new file mode 100644 index 000000000..27ab26542 Binary files /dev/null and b/Utils/luarocks/lua51.dll differ diff --git a/Utils/luarocks/lua51.lib b/Utils/luarocks/lua51.lib new file mode 100644 index 000000000..74bcf3fb4 Binary files /dev/null and b/Utils/luarocks/lua51.lib differ diff --git a/Utils/luarocks/luac5.1.exe b/Utils/luarocks/luac5.1.exe new file mode 100644 index 000000000..6f87524e2 Binary files /dev/null and b/Utils/luarocks/luac5.1.exe differ diff --git a/Utils/luarocks/luarocks-admin.bat b/Utils/luarocks/luarocks-admin.bat new file mode 100644 index 000000000..e7bd4d12b --- /dev/null +++ b/Utils/luarocks/luarocks-admin.bat @@ -0,0 +1,43 @@ +@ECHO OFF +SETLOCAL ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS + +SET "LUA_PATH=C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua\?.lua;C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua\?\init.lua;%LUA_PATH%" +IF NOT "%LUA_PATH_5_2%"=="" ( + SET "LUA_PATH_5_2=C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua\?.lua;C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua\?\init.lua;%LUA_PATH_5_2%" +) +IF NOT "%LUA_PATH_5_3%"=="" ( + SET "LUA_PATH_5_3=C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua\?.lua;C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua\?\init.lua;%LUA_PATH_5_3%" +) +SET "PATH=C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks;%PATH%" +"C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua5.1" "C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\luarocks-admin.lua" %* +SET EXITCODE=%ERRORLEVEL% +IF NOT "%EXITCODE%"=="2" GOTO EXITLR + +REM Permission denied error, try and auto elevate... +REM already an admin? (checking to prevent loops) +NET SESSION >NUL 2>&1 +IF "%ERRORLEVEL%"=="0" GOTO EXITLR + +REM Do we have PowerShell available? +PowerShell /? >NUL 2>&1 +IF NOT "%ERRORLEVEL%"=="0" GOTO EXITLR + +:GETTEMPNAME +SET TMPFILE=%TEMP%\LuaRocks-Elevator-%RANDOM%.bat +IF EXIST "%TMPFILE%" GOTO :GETTEMPNAME + +ECHO @ECHO OFF > "%TMPFILE%" +ECHO CHDIR /D %CD% >> "%TMPFILE%" +ECHO ECHO %0 %* >> "%TMPFILE%" +ECHO ECHO. >> "%TMPFILE%" +ECHO CALL %0 %* >> "%TMPFILE%" +ECHO ECHO. >> "%TMPFILE%" +ECHO ECHO Press any key to close this window... >> "%TMPFILE%" +ECHO PAUSE ^> NUL >> "%TMPFILE%" +ECHO DEL "%TMPFILE%" >> "%TMPFILE%" + +ECHO Now retrying as a privileged user... +PowerShell -Command (New-Object -com 'Shell.Application').ShellExecute('%TMPFILE%', '', '', 'runas') + +:EXITLR +exit /b %EXITCODE% diff --git a/Utils/luarocks/luarocks-admin.lua b/Utils/luarocks/luarocks-admin.lua new file mode 100644 index 000000000..2890d1f19 --- /dev/null +++ b/Utils/luarocks/luarocks-admin.lua @@ -0,0 +1,19 @@ +#!/usr/bin/env lua + +-- this should be loaded first. +local cfg = require("luarocks.cfg") + +local loader = require("luarocks.loader") +local command_line = require("luarocks.command_line") + +program_description = "LuaRocks repository administration interface" + +commands = { + help = "luarocks.help", + make_manifest = "luarocks.make_manifest", + add = "luarocks.add", + remove = "luarocks.admin_remove", + refresh_cache = "luarocks.refresh_cache", +} + +command_line.run_command(...) diff --git a/Utils/luarocks/luarocks.bat b/Utils/luarocks/luarocks.bat new file mode 100644 index 000000000..373068966 --- /dev/null +++ b/Utils/luarocks/luarocks.bat @@ -0,0 +1,43 @@ +@ECHO OFF +SETLOCAL ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS + +SET "LUA_PATH=C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua\?.lua;C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua\?\init.lua;%LUA_PATH%" +IF NOT "%LUA_PATH_5_2%"=="" ( + SET "LUA_PATH_5_2=C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua\?.lua;C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua\?\init.lua;%LUA_PATH_5_2%" +) +IF NOT "%LUA_PATH_5_3%"=="" ( + SET "LUA_PATH_5_3=C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua\?.lua;C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua\?\init.lua;%LUA_PATH_5_3%" +) +SET "PATH=C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks;%PATH%" +"C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\lua5.1" "C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\luarocks.lua" %* +SET EXITCODE=%ERRORLEVEL% +IF NOT "%EXITCODE%"=="2" GOTO EXITLR + +REM Permission denied error, try and auto elevate... +REM already an admin? (checking to prevent loops) +NET SESSION >NUL 2>&1 +IF "%ERRORLEVEL%"=="0" GOTO EXITLR + +REM Do we have PowerShell available? +PowerShell /? >NUL 2>&1 +IF NOT "%ERRORLEVEL%"=="0" GOTO EXITLR + +:GETTEMPNAME +SET TMPFILE=%TEMP%\LuaRocks-Elevator-%RANDOM%.bat +IF EXIST "%TMPFILE%" GOTO :GETTEMPNAME + +ECHO @ECHO OFF > "%TMPFILE%" +ECHO CHDIR /D %CD% >> "%TMPFILE%" +ECHO ECHO %0 %* >> "%TMPFILE%" +ECHO ECHO. >> "%TMPFILE%" +ECHO CALL %0 %* >> "%TMPFILE%" +ECHO ECHO. >> "%TMPFILE%" +ECHO ECHO Press any key to close this window... >> "%TMPFILE%" +ECHO PAUSE ^> NUL >> "%TMPFILE%" +ECHO DEL "%TMPFILE%" >> "%TMPFILE%" + +ECHO Now retrying as a privileged user... +PowerShell -Command (New-Object -com 'Shell.Application').ShellExecute('%TMPFILE%', '', '', 'runas') + +:EXITLR +exit /b %EXITCODE% diff --git a/Utils/luarocks/luarocks.lua b/Utils/luarocks/luarocks.lua new file mode 100644 index 000000000..be6c2b811 --- /dev/null +++ b/Utils/luarocks/luarocks.lua @@ -0,0 +1,33 @@ +#!/usr/bin/env lua + +-- this should be loaded first. +local cfg = require("luarocks.cfg") + +local loader = require("luarocks.loader") +local command_line = require("luarocks.command_line") + +program_description = "LuaRocks main command-line interface" + +commands = { + help = "luarocks.help", + pack = "luarocks.pack", + unpack = "luarocks.unpack", + build = "luarocks.build", + install = "luarocks.install", + search = "luarocks.search", + list = "luarocks.list", + remove = "luarocks.remove", + make = "luarocks.make", + download = "luarocks.download", + path = "luarocks.path_cmd", + show = "luarocks.show", + new_version = "luarocks.new_version", + lint = "luarocks.lint", + write_rockspec = "luarocks.write_rockspec", + purge = "luarocks.purge", + doc = "luarocks.doc", + upload = "luarocks.upload", + config = "luarocks.config_cmd", +} + +command_line.run_command(...) diff --git a/Utils/luarocks/luarocksw.bat b/Utils/luarocks/luarocksw.bat new file mode 100644 index 000000000..8ac029209 --- /dev/null +++ b/Utils/luarocks/luarocksw.bat @@ -0,0 +1,49 @@ +@echo off +setlocal +SET MYPATH=%~dp0 + +IF NOT [%1]==[] GOTO LETSGO +ECHO Same as 'luarocks' command, except this +ECHO command will pause after completion, allowing for +ECHO examination of output. +ECHO. +ECHO For LuaRocks help use: +ECHO LUAROCKS HELP +ECHO. +ECHO OPTIONS specific for LUAROCKSW: +ECHO REMOVEALL is a command specific to this batch file +ECHO the option takes a FULL ROCKSPEC filename and then +ECHO it will strip path, version and extension info from +ECHO it before executing the LUAROCKS REMOVE command +ECHO Example: +ECHO luarocksw remove "c:\somedir\modulename-1.0-1.rockspec" +ECHO will execute: +ECHO luarocks remove "c:\somedir\modulename-1.0-1.rockspec" +ECHO and will only remove the specific version 1.0 from the +ECHO system. +ECHO luarocksw removeall "c:\somedir\modulename-1.0-1.rockspec" +ECHO will execute: +ECHO luarocks remove modulename +ECHO and will remove all versions of this package +ECHO. +GOTO END + +:LETSGO +REM if REMOVEALL command then info must be stripped from the parameter +if [%1]==[removeall] goto REMOVEALL + +REM execute LuaRocks and wait for results +echo executing: luarocks %* +call "%MYPATH%luarocks" %* +pause +goto END + +:REMOVEALL +for /f "delims=-" %%a in ("%~n2") do ( + echo executing: luarocks remove %%a + "%MYPATH%luarocks" remove "%%a" + pause + goto END +) + +:END \ No newline at end of file diff --git a/Utils/luarocks/msvcm80.dll b/Utils/luarocks/msvcm80.dll new file mode 100644 index 000000000..c751385bd Binary files /dev/null and b/Utils/luarocks/msvcm80.dll differ diff --git a/Utils/luarocks/msvcp80.dll b/Utils/luarocks/msvcp80.dll new file mode 100644 index 000000000..f0b52ebf1 Binary files /dev/null and b/Utils/luarocks/msvcp80.dll differ diff --git a/Utils/luarocks/msvcr80.dll b/Utils/luarocks/msvcr80.dll new file mode 100644 index 000000000..53c005efc Binary files /dev/null and b/Utils/luarocks/msvcr80.dll differ diff --git a/Utils/luarocks/rclauncher.c b/Utils/luarocks/rclauncher.c new file mode 100644 index 000000000..77459f464 --- /dev/null +++ b/Utils/luarocks/rclauncher.c @@ -0,0 +1,139 @@ + +/* +** Simple Lua interpreter. +** This program is used to run a Lua file embedded as a resource. +** It creates a Lua state, opens all its standard libraries, and run +** the Lua file in a protected environment just to redirect the error +** messages to stdout and stderr. +** +** $Id: rclauncher.c,v 1.1 2008/06/30 14:29:59 carregal Exp $ +*/ + +#include +#include + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +#include +#include +#include + +/* +** Report error message. +** Assumes that the error message is on top of the stack. +*/ +static int report (lua_State *L) { + fprintf (stderr, "lua: fatal error: `%s'\n", lua_tostring (L, -1)); + fflush (stderr); + printf ("Content-type: text/plain\n\nConfiguration fatal error: see error log!\n"); + printf ("%s", lua_tostring(L, -1)); + return 1; +} + +static int runlua (lua_State *L, const char *lua_string, int argc, char *argv[]) { + int err_func; + int err; + + lua_getglobal(L, "debug"); + lua_pushliteral(L, "traceback"); + lua_gettable(L, -2); + err_func = lua_gettop (L); + err = luaL_loadstring (L, lua_string); + if(!err) { + int i; + // fill global arg table + lua_getglobal(L, "arg"); + for(i = 1; i < argc; i++) + { + lua_pushstring(L, argv[i]); + lua_rawseti(L, -2, i); + } + lua_pop(L, 1); + // fill parameters (in vararg '...') + for(i = 1; i < argc; i++) + lua_pushstring(L, argv[i]); + return lua_pcall (L, argc - 1, LUA_MULTRET, err_func); + } else return err; +} + +static DWORD GetModulePath( HINSTANCE hInst, LPTSTR pszBuffer, DWORD dwSize ) +// +// Return the size of the path in bytes. +{ + DWORD dwLength = GetModuleFileName( hInst, pszBuffer, dwSize ); + if( dwLength ) + { + while( dwLength && pszBuffer[ dwLength ] != '.' ) + { + dwLength--; + } + + if( dwLength ) + pszBuffer[ dwLength ] = '\000'; + } + return dwLength; +} + + +/* +** MAIN +*/ +int main (int argc, char *argv[]) { + char name[ MAX_PATH ]; + DWORD dwLength; + int size; + luaL_Buffer b; + int i; +#ifdef UNICODE + TCHAR lua_wstring[4098]; +#endif + char lua_string[4098]; + lua_State *L = luaL_newstate(); + (void)argc; /* avoid "unused parameter" warning */ + luaL_openlibs(L); + lua_newtable(L); // create arg table + lua_pushstring(L, argv[0]); // add interpreter to arg table + lua_rawseti(L, -2, -1); + dwLength = GetModulePath( NULL, name, MAX_PATH ); + if(dwLength) { /* Optional bootstrap */ + strcat(name, ".lua"); + lua_pushstring(L, name); // add lua script to arg table + lua_rawseti(L, -2, 0); + lua_setglobal(L,"arg"); // set global arg table + if(!luaL_loadfile (L, name)) { + if(lua_pcall (L, 0, LUA_MULTRET, 0)) { + report (L); + lua_close (L); + return EXIT_FAILURE; + } + } + } + else + { + lua_pushstring(L, argv[0]); // no lua script, so add interpreter again, now as lua script + lua_rawseti(L, -2, 0); + lua_setglobal(L,"arg"); // set global arg table + } + + luaL_buffinit(L, &b); + for(i = 1; ; i++) { +#ifdef UNICODE + size = LoadString(GetModuleHandle(NULL), i, lua_wstring, + sizeof(lua_string)/sizeof(TCHAR)); + if(size > 0) wcstombs(lua_string, lua_wstring, size + 1); +#else + size = LoadString(GetModuleHandle(NULL), i, lua_string, + sizeof(lua_string)/sizeof(char)); +#endif + if(size) luaL_addlstring(&b, lua_string, size); else break; + } + luaL_pushresult(&b); + if (runlua (L, lua_tostring(L, -1), argc, argv)) { + report (L); + lua_close (L); + return EXIT_FAILURE; + } + lua_close (L); + return EXIT_SUCCESS; +} diff --git a/Utils/luarocks/systree/bin/luadocumentor.bat b/Utils/luarocks/systree/bin/luadocumentor.bat new file mode 100644 index 000000000..e16f6f7e9 --- /dev/null +++ b/Utils/luarocks/systree/bin/luadocumentor.bat @@ -0,0 +1,3 @@ +@echo off +"C:\Users\Hugues\Documents\GitHub\MOOSE\Utils\luarocks\lua5.1" -e "package.path=\"C:\\Users\\Hugues\\AppData\\Roaming/luarocks/share/lua/5.1/?.lua;C:\\Users\\Hugues\\AppData\\Roaming/luarocks/share/lua/5.1/?/init.lua;C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\\systree/share/lua/5.1/?.lua;C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\\systree/share/lua/5.1/?/init.lua;C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks/lua/?.lua;\"..package.path; package.cpath=\"C:\\Users\\Hugues\\AppData\\Roaming/luarocks/lib/lua/5.1/?.dll;C:/Users/Hugues/Documents/GitHub/MOOSE/Utils/luarocks\\systree/lib/lua/5.1/?.dll;\"..package.cpath" -e "local k,l,_=pcall(require,\"luarocks.loader\") _=k and l.add_context(\"luadocumentor\",\"0.1.5-1\")" "C:\Users\Hugues\Documents\GitHub\MOOSE\Utils\luarocks\systree\lib\luarocks\rocks\luadocumentor\0.1.5-1\bin\luadocumentor" %* +exit /b %ERRORLEVEL% diff --git a/Utils/luarocks/systree/lib/lua/5.1/checks.dll b/Utils/luarocks/systree/lib/lua/5.1/checks.dll new file mode 100644 index 000000000..9959cdc01 Binary files /dev/null and b/Utils/luarocks/systree/lib/lua/5.1/checks.dll differ diff --git a/Utils/luarocks/systree/lib/lua/5.1/lfs.dll b/Utils/luarocks/systree/lib/lua/5.1/lfs.dll new file mode 100644 index 000000000..9249ec0eb Binary files /dev/null and b/Utils/luarocks/systree/lib/lua/5.1/lfs.dll differ diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/checks/1.0-1/checks-1.0-1.rockspec b/Utils/luarocks/systree/lib/luarocks/rocks/checks/1.0-1/checks-1.0-1.rockspec similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/checks/1.0-1/checks-1.0-1.rockspec rename to Utils/luarocks/systree/lib/luarocks/rocks/checks/1.0-1/checks-1.0-1.rockspec diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/checks/1.0-1/rock_manifest b/Utils/luarocks/systree/lib/luarocks/rocks/checks/1.0-1/rock_manifest similarity index 63% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/checks/1.0-1/rock_manifest rename to Utils/luarocks/systree/lib/luarocks/rocks/checks/1.0-1/rock_manifest index 07ae2133a..b45de952e 100644 --- a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/checks/1.0-1/rock_manifest +++ b/Utils/luarocks/systree/lib/luarocks/rocks/checks/1.0-1/rock_manifest @@ -1,6 +1,6 @@ rock_manifest = { ["checks-1.0-1.rockspec"] = "70af2f5dd173774a16b9e7cfdfd12ecd", lib = { - ["checks.dll"] = "5342726d76176ca95ebe25c7b07ca6ac" + ["checks.dll"] = "2f86fd2ab34447e9e211836fd56939ae" } } diff --git a/Utils/luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/bin/luadocumentor b/Utils/luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/bin/luadocumentor new file mode 100644 index 000000000..dccc8df18 --- /dev/null +++ b/Utils/luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/bin/luadocumentor @@ -0,0 +1,178 @@ +#!/usr/bin/lua +-------------------------------------------------------------------------------- +-- Copyright (c) 2012-2014 Sierra Wireless. +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Kevin KIN-FOO +-- - initial API and implementation and initial documentation +-------------------------------------------------------------------------------- + +-- Check interpreter version +if _VERSION ~= "Lua 5.1" then + print("Luadocumentor is only compatible with Lua 5.1") + return +end + +-- +-- Defining help message. +-- + +-- This message is compliant to 'lapp', which will match options and arguments +-- from command line. +local help = [[luadocumentor v0.1.4: tool for Lua Documentation Language + -f, --format (default doc) Define output format : + * doc: Will produce HTML documentation from specified file(s) or directories. + * api: Will produce API file(s) from specified file(s) or directories. + -d, --dir (default docs) Define an output directory. If the given directory doesn't exist, it will be created. + -h, --help Display the help. + -n, --noheuristic Do not use code analysis, use only comments to generate documentation. + -s, --style (default !) The path of your own css file, if you don't want to use the default one. (usefull only for the doc format) + [directories|files] Define the paths or the directories of inputs files. Only Lua or C files containing a @module tag will be considered. +]] +local docgenerator = require 'docgenerator' +local lddextractor = require 'lddextractor' +local lapp = require 'pl.lapp' +local args = lapp( help ) + +if not args or #args < 1 then + print('No directory provided') + return +elseif args.help then + -- Just print help + print( help ) + return +end + +-- +-- define css file name +-- +local cssfilename = "stylesheet.css" + +-- +-- Parse files from given folders +-- + +-- Check if all folders exist +local fs = require 'fs.lfs' +local allpresent, missing = fs.checkdirectory(args) + +-- Some of given directories are absent +if missing then + -- List missing directories + print 'Unable to open' + for _, file in ipairs( missing ) do + print('\t'.. file) + end + return +end + +-- Get files from given directories +local filestoparse, error = fs.filelist( args ) +if not filestoparse then + print ( error ) + return +end + +-- +-- Generate documentation only files +-- +if args.format == 'api' then + for _, filename in ipairs( filestoparse ) do + + -- Loading file content + print('Dealing with "'..filename..'".') + local file, error = io.open(filename, 'r') + if not file then + print ('Unable to open "'..filename.."'.\n"..error) + else + local code = file:read('*all') + file:close() + + -- + -- Creating comment file + -- + local commentfile, error = lddextractor.generatecommentfile(filename, code) + + -- Getting module name + -- Optimize me + local module, moduleerror = lddextractor.generateapimodule(filename, code) + if not commentfile then + print('Unable to create documentation file for "'..filename..'"\n'..error) + elseif not module or not module.name then + local error = moduleerror and '\n'..moduleerror or '' + print('Unable to compute module name for "'..filename..'".'..error) + else + -- + -- Flush documentation file on disk + -- + local path = args.dir..fs.separator..module.name..'.lua' + local status, err = fs.fill(path, commentfile) + if not status then + print(err) + end + end + end + end + print('Done') + return +end + +-- Deal only supported output types +if args.format ~= 'doc' then + print ('"'..args.format..'" format is not handled.') + return +end +-- Generate html form files +local parsedfiles, unparsed = docgenerator.generatedocforfiles(filestoparse, cssfilename,args.noheuristic) + +-- Show warnings on unparsed files +if #unparsed > 0 then + for _, faultyfile in ipairs( unparsed ) do + print( faultyfile ) + end +end +-- This loop is just for counting parsed files +-- TODO: Find a more elegant way to do it +local parsedfilescount = 0 +for _, p in pairs(parsedfiles) do + parsedfilescount = parsedfilescount + 1 +end +print (parsedfilescount .. ' file(s) parsed.') + +-- Create html files +local generated = 0 +for _, apifile in pairs ( parsedfiles ) do + local status, err = fs.fill(args.dir..fs.separator..apifile.name..'.html', apifile.body) + if status then + generated = generated + 1 + else + print( 'Unable to create '..apifile.name..'.html on disk.') + end +end +print (generated .. ' file(s) generated.') + +-- Copying css +local csscontent +if args.style == '!' then + csscontent = require 'defaultcss' +else + local css, error = io.open(args.style, 'r') + if not css then + print('Unable to open "'..args.style .. '".\n'..error) + return + end + csscontent = css:read("*all") + css:close() +end + +local status, error = fs.fill(args.dir..fs.separator..cssfilename, csscontent) +if not status then + print(error) + return +end +print('Adding css') +print('Done') diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/LICENSE b/Utils/luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/LICENSE similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/LICENSE rename to Utils/luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/LICENSE diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/README.md b/Utils/luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/README.md similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/README.md rename to Utils/luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/doc/README.md diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/luadocumentor-0.1.5-1.rockspec b/Utils/luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/luadocumentor-0.1.5-1.rockspec similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/luadocumentor-0.1.5-1.rockspec rename to Utils/luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/luadocumentor-0.1.5-1.rockspec diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/rock_manifest b/Utils/luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/rock_manifest similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luadocumentor/0.1.5-1/rock_manifest rename to Utils/luarocks/systree/lib/luarocks/rocks/luadocumentor/0.1.5-1/rock_manifest diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/doc.css b/Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/doc.css similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/doc.css rename to Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/doc.css diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/examples.html b/Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/examples.html similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/examples.html rename to Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/examples.html diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/index.html b/Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/index.html similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/index.html rename to Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/index.html diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/license.html b/Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/license.html similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/license.html rename to Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/license.html diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/luafilesystem.png b/Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/luafilesystem.png similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/luafilesystem.png rename to Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/luafilesystem.png diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/manual.html b/Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/manual.html similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/manual.html rename to Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/doc/us/manual.html diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/luafilesystem-1.6.3-2.rockspec b/Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/luafilesystem-1.6.3-2.rockspec similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/luafilesystem-1.6.3-2.rockspec rename to Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/luafilesystem-1.6.3-2.rockspec diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/rock_manifest b/Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/rock_manifest similarity index 91% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/rock_manifest rename to Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/rock_manifest index b0f7b6332..1b646b631 100644 --- a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/rock_manifest +++ b/Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/rock_manifest @@ -10,7 +10,7 @@ rock_manifest = { } }, lib = { - ["lfs.dll"] = "165694685cffa6014be4ca8cbb7d920b" + ["lfs.dll"] = "bb5e7f0d82350913063363393fafb3e3" }, ["luafilesystem-1.6.3-2.rockspec"] = "eb0ef7c190516892eb8357af799eea5f", tests = { diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/tests/test.lua b/Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/tests/test.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/luafilesystem/1.6.3-2/tests/test.lua rename to Utils/luarocks/systree/lib/luarocks/rocks/luafilesystem/1.6.3-2/tests/test.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/manifest b/Utils/luarocks/systree/lib/luarocks/rocks/manifest similarity index 99% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/manifest rename to Utils/luarocks/systree/lib/luarocks/rocks/manifest index 3b022bd4f..b3bae3c19 100644 --- a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/manifest +++ b/Utils/luarocks/systree/lib/luarocks/rocks/manifest @@ -31,7 +31,8 @@ dependencies = { } }, name = "lua" - }, { + }, + { constraints = { { op = "~>", @@ -41,7 +42,8 @@ dependencies = { } }, name = "luafilesystem" - }, { + }, + { constraints = { { op = "~>", @@ -51,7 +53,8 @@ dependencies = { } }, name = "markdown" - }, { + }, + { constraints = { { op = "~>", @@ -61,7 +64,8 @@ dependencies = { } }, name = "metalua-compiler" - }, { + }, + { constraints = { { op = "~>", @@ -116,7 +120,8 @@ dependencies = { } }, name = "lua" - }, { + }, + { constraints = { { op = "~>", @@ -126,7 +131,8 @@ dependencies = { } }, name = "luafilesystem" - }, { + }, + { constraints = { { op = ">=", diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/markdown/0.32-2/markdown-0.32-2.rockspec b/Utils/luarocks/systree/lib/luarocks/rocks/markdown/0.32-2/markdown-0.32-2.rockspec similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/markdown/0.32-2/markdown-0.32-2.rockspec rename to Utils/luarocks/systree/lib/luarocks/rocks/markdown/0.32-2/markdown-0.32-2.rockspec diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/markdown/0.32-2/rock_manifest b/Utils/luarocks/systree/lib/luarocks/rocks/markdown/0.32-2/rock_manifest similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/markdown/0.32-2/rock_manifest rename to Utils/luarocks/systree/lib/luarocks/rocks/markdown/0.32-2/rock_manifest diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-compiler.md b/Utils/luarocks/systree/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-compiler.md similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-compiler.md rename to Utils/luarocks/systree/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-compiler.md diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-parser.md b/Utils/luarocks/systree/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-parser.md similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-parser.md rename to Utils/luarocks/systree/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README-parser.md diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README.md b/Utils/luarocks/systree/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README.md similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README.md rename to Utils/luarocks/systree/lib/luarocks/rocks/metalua-compiler/0.7.3-1/doc/README.md diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/metalua-compiler-0.7.3-1.rockspec b/Utils/luarocks/systree/lib/luarocks/rocks/metalua-compiler/0.7.3-1/metalua-compiler-0.7.3-1.rockspec similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/metalua-compiler-0.7.3-1.rockspec rename to Utils/luarocks/systree/lib/luarocks/rocks/metalua-compiler/0.7.3-1/metalua-compiler-0.7.3-1.rockspec diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/rock_manifest b/Utils/luarocks/systree/lib/luarocks/rocks/metalua-compiler/0.7.3-1/rock_manifest similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-compiler/0.7.3-1/rock_manifest rename to Utils/luarocks/systree/lib/luarocks/rocks/metalua-compiler/0.7.3-1/rock_manifest diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-compiler.md b/Utils/luarocks/systree/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-compiler.md similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-compiler.md rename to Utils/luarocks/systree/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-compiler.md diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-parser.md b/Utils/luarocks/systree/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-parser.md similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-parser.md rename to Utils/luarocks/systree/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README-parser.md diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README.md b/Utils/luarocks/systree/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README.md similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README.md rename to Utils/luarocks/systree/lib/luarocks/rocks/metalua-parser/0.7.3-2/doc/README.md diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/metalua-parser-0.7.3-2.rockspec b/Utils/luarocks/systree/lib/luarocks/rocks/metalua-parser/0.7.3-2/metalua-parser-0.7.3-2.rockspec similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/metalua-parser-0.7.3-2.rockspec rename to Utils/luarocks/systree/lib/luarocks/rocks/metalua-parser/0.7.3-2/metalua-parser-0.7.3-2.rockspec diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/rock_manifest b/Utils/luarocks/systree/lib/luarocks/rocks/metalua-parser/0.7.3-2/rock_manifest similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/metalua-parser/0.7.3-2/rock_manifest rename to Utils/luarocks/systree/lib/luarocks/rocks/metalua-parser/0.7.3-2/rock_manifest diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/penlight/0.9.8-1/penlight-0.9.8-1.rockspec b/Utils/luarocks/systree/lib/luarocks/rocks/penlight/0.9.8-1/penlight-0.9.8-1.rockspec similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/penlight/0.9.8-1/penlight-0.9.8-1.rockspec rename to Utils/luarocks/systree/lib/luarocks/rocks/penlight/0.9.8-1/penlight-0.9.8-1.rockspec diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/penlight/0.9.8-1/rock_manifest b/Utils/luarocks/systree/lib/luarocks/rocks/penlight/0.9.8-1/rock_manifest similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/lib/luarocks/rocks/penlight/0.9.8-1/rock_manifest rename to Utils/luarocks/systree/lib/luarocks/rocks/penlight/0.9.8-1/rock_manifest diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/defaultcss.lua b/Utils/luarocks/systree/share/lua/5.1/defaultcss.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/defaultcss.lua rename to Utils/luarocks/systree/share/lua/5.1/defaultcss.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/docgenerator.lua b/Utils/luarocks/systree/share/lua/5.1/docgenerator.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/docgenerator.lua rename to Utils/luarocks/systree/share/lua/5.1/docgenerator.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/extractors.lua b/Utils/luarocks/systree/share/lua/5.1/extractors.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/extractors.lua rename to Utils/luarocks/systree/share/lua/5.1/extractors.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/fs/lfs.lua b/Utils/luarocks/systree/share/lua/5.1/fs/lfs.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/fs/lfs.lua rename to Utils/luarocks/systree/share/lua/5.1/fs/lfs.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/lddextractor.lua b/Utils/luarocks/systree/share/lua/5.1/lddextractor.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/lddextractor.lua rename to Utils/luarocks/systree/share/lua/5.1/lddextractor.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/markdown.lua b/Utils/luarocks/systree/share/lua/5.1/markdown.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/markdown.lua rename to Utils/luarocks/systree/share/lua/5.1/markdown.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/ast_to_src.mlua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/ast_to_src.mlua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/ast_to_src.mlua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/ast_to_src.mlua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/bytecode.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/bytecode.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/compile.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/bytecode/compile.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/compile.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/bytecode/compile.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/lcode.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/bytecode/lcode.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/lcode.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/bytecode/lcode.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/ldump.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/bytecode/ldump.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/ldump.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/bytecode/ldump.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/lopcodes.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/bytecode/lopcodes.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/bytecode/lopcodes.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/bytecode/lopcodes.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/globals.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/globals.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/globals.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/globals.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/annot/generator.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/annot/generator.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/annot/generator.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/annot/generator.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/annot/grammar.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/annot/grammar.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/annot/grammar.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/annot/grammar.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/expr.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/expr.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/expr.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/expr.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/ext.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/ext.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/ext.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/ext.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/lexer.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/lexer.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/lexer.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/lexer.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/meta.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/meta.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/meta.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/meta.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/misc.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/misc.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/misc.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/misc.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/stat.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/stat.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/stat.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/stat.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/table.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/table.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/compiler/parser/table.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/compiler/parser/table.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/extension/comprehension.mlua b/Utils/luarocks/systree/share/lua/5.1/metalua/extension/comprehension.mlua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/extension/comprehension.mlua rename to Utils/luarocks/systree/share/lua/5.1/metalua/extension/comprehension.mlua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/extension/match.mlua b/Utils/luarocks/systree/share/lua/5.1/metalua/extension/match.mlua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/extension/match.mlua rename to Utils/luarocks/systree/share/lua/5.1/metalua/extension/match.mlua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/grammar/generator.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/grammar/generator.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/grammar/generator.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/grammar/generator.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/grammar/lexer.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/grammar/lexer.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/grammar/lexer.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/grammar/lexer.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/loader.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/loader.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/loader.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/loader.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/pprint.lua b/Utils/luarocks/systree/share/lua/5.1/metalua/pprint.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/pprint.lua rename to Utils/luarocks/systree/share/lua/5.1/metalua/pprint.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/repl.mlua b/Utils/luarocks/systree/share/lua/5.1/metalua/repl.mlua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/repl.mlua rename to Utils/luarocks/systree/share/lua/5.1/metalua/repl.mlua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/treequery.mlua b/Utils/luarocks/systree/share/lua/5.1/metalua/treequery.mlua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/treequery.mlua rename to Utils/luarocks/systree/share/lua/5.1/metalua/treequery.mlua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/treequery/walk.mlua b/Utils/luarocks/systree/share/lua/5.1/metalua/treequery/walk.mlua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/metalua/treequery/walk.mlua rename to Utils/luarocks/systree/share/lua/5.1/metalua/treequery/walk.mlua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/apimodel.lua b/Utils/luarocks/systree/share/lua/5.1/models/apimodel.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/apimodel.lua rename to Utils/luarocks/systree/share/lua/5.1/models/apimodel.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/apimodelbuilder.lua b/Utils/luarocks/systree/share/lua/5.1/models/apimodelbuilder.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/apimodelbuilder.lua rename to Utils/luarocks/systree/share/lua/5.1/models/apimodelbuilder.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/internalmodel.lua b/Utils/luarocks/systree/share/lua/5.1/models/internalmodel.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/internalmodel.lua rename to Utils/luarocks/systree/share/lua/5.1/models/internalmodel.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/internalmodelbuilder.mlua b/Utils/luarocks/systree/share/lua/5.1/models/internalmodelbuilder.mlua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/internalmodelbuilder.mlua rename to Utils/luarocks/systree/share/lua/5.1/models/internalmodelbuilder.mlua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/ldparser.lua b/Utils/luarocks/systree/share/lua/5.1/models/ldparser.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/models/ldparser.lua rename to Utils/luarocks/systree/share/lua/5.1/models/ldparser.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Date.lua b/Utils/luarocks/systree/share/lua/5.1/pl/Date.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Date.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/Date.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/List.lua b/Utils/luarocks/systree/share/lua/5.1/pl/List.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/List.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/List.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Map.lua b/Utils/luarocks/systree/share/lua/5.1/pl/Map.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Map.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/Map.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/MultiMap.lua b/Utils/luarocks/systree/share/lua/5.1/pl/MultiMap.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/MultiMap.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/MultiMap.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/OrderedMap.lua b/Utils/luarocks/systree/share/lua/5.1/pl/OrderedMap.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/OrderedMap.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/OrderedMap.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Set.lua b/Utils/luarocks/systree/share/lua/5.1/pl/Set.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/Set.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/Set.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/app.lua b/Utils/luarocks/systree/share/lua/5.1/pl/app.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/app.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/app.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/array2d.lua b/Utils/luarocks/systree/share/lua/5.1/pl/array2d.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/array2d.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/array2d.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/class.lua b/Utils/luarocks/systree/share/lua/5.1/pl/class.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/class.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/class.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/comprehension.lua b/Utils/luarocks/systree/share/lua/5.1/pl/comprehension.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/comprehension.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/comprehension.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/config.lua b/Utils/luarocks/systree/share/lua/5.1/pl/config.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/config.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/config.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/data.lua b/Utils/luarocks/systree/share/lua/5.1/pl/data.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/data.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/data.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/dir.lua b/Utils/luarocks/systree/share/lua/5.1/pl/dir.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/dir.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/dir.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/file.lua b/Utils/luarocks/systree/share/lua/5.1/pl/file.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/file.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/file.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/func.lua b/Utils/luarocks/systree/share/lua/5.1/pl/func.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/func.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/func.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/init.lua b/Utils/luarocks/systree/share/lua/5.1/pl/init.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/init.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/init.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/input.lua b/Utils/luarocks/systree/share/lua/5.1/pl/input.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/input.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/input.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/lapp.lua b/Utils/luarocks/systree/share/lua/5.1/pl/lapp.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/lapp.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/lapp.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/lexer.lua b/Utils/luarocks/systree/share/lua/5.1/pl/lexer.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/lexer.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/lexer.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/luabalanced.lua b/Utils/luarocks/systree/share/lua/5.1/pl/luabalanced.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/luabalanced.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/luabalanced.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/operator.lua b/Utils/luarocks/systree/share/lua/5.1/pl/operator.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/operator.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/operator.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/path.lua b/Utils/luarocks/systree/share/lua/5.1/pl/path.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/path.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/path.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/permute.lua b/Utils/luarocks/systree/share/lua/5.1/pl/permute.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/permute.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/permute.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/platf/luajava.lua b/Utils/luarocks/systree/share/lua/5.1/pl/platf/luajava.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/platf/luajava.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/platf/luajava.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/pretty.lua b/Utils/luarocks/systree/share/lua/5.1/pl/pretty.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/pretty.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/pretty.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/seq.lua b/Utils/luarocks/systree/share/lua/5.1/pl/seq.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/seq.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/seq.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/sip.lua b/Utils/luarocks/systree/share/lua/5.1/pl/sip.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/sip.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/sip.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/strict.lua b/Utils/luarocks/systree/share/lua/5.1/pl/strict.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/strict.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/strict.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/stringio.lua b/Utils/luarocks/systree/share/lua/5.1/pl/stringio.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/stringio.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/stringio.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/stringx.lua b/Utils/luarocks/systree/share/lua/5.1/pl/stringx.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/stringx.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/stringx.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/tablex.lua b/Utils/luarocks/systree/share/lua/5.1/pl/tablex.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/tablex.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/tablex.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/template.lua b/Utils/luarocks/systree/share/lua/5.1/pl/template.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/template.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/template.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/test.lua b/Utils/luarocks/systree/share/lua/5.1/pl/test.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/test.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/test.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/text.lua b/Utils/luarocks/systree/share/lua/5.1/pl/text.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/text.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/text.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/utils.lua b/Utils/luarocks/systree/share/lua/5.1/pl/utils.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/utils.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/utils.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/xml.lua b/Utils/luarocks/systree/share/lua/5.1/pl/xml.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/pl/xml.lua rename to Utils/luarocks/systree/share/lua/5.1/pl/xml.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/file.lua b/Utils/luarocks/systree/share/lua/5.1/template/file.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/file.lua rename to Utils/luarocks/systree/share/lua/5.1/template/file.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/index.lua b/Utils/luarocks/systree/share/lua/5.1/template/index.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/index.lua rename to Utils/luarocks/systree/share/lua/5.1/template/index.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/index/recordtypedef.lua b/Utils/luarocks/systree/share/lua/5.1/template/index/recordtypedef.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/index/recordtypedef.lua rename to Utils/luarocks/systree/share/lua/5.1/template/index/recordtypedef.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/item.lua b/Utils/luarocks/systree/share/lua/5.1/template/item.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/item.lua rename to Utils/luarocks/systree/share/lua/5.1/template/item.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/page.lua b/Utils/luarocks/systree/share/lua/5.1/template/page.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/page.lua rename to Utils/luarocks/systree/share/lua/5.1/template/page.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/recordtypedef.lua b/Utils/luarocks/systree/share/lua/5.1/template/recordtypedef.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/recordtypedef.lua rename to Utils/luarocks/systree/share/lua/5.1/template/recordtypedef.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/usage.lua b/Utils/luarocks/systree/share/lua/5.1/template/usage.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/usage.lua rename to Utils/luarocks/systree/share/lua/5.1/template/usage.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/utils.lua b/Utils/luarocks/systree/share/lua/5.1/template/utils.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/template/utils.lua rename to Utils/luarocks/systree/share/lua/5.1/template/utils.lua diff --git a/Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/templateengine.lua b/Utils/luarocks/systree/share/lua/5.1/templateengine.lua similarity index 100% rename from Moose Development Evironment Setup/LuaFiles/lua/5.1/share/lua/5.1/templateengine.lua rename to Utils/luarocks/systree/share/lua/5.1/templateengine.lua diff --git a/Utils/luarocks/tools/7z.dll b/Utils/luarocks/tools/7z.dll new file mode 100644 index 000000000..c0ff7fbe6 Binary files /dev/null and b/Utils/luarocks/tools/7z.dll differ diff --git a/Utils/luarocks/tools/7z.exe b/Utils/luarocks/tools/7z.exe new file mode 100644 index 000000000..5e3d6f9cf Binary files /dev/null and b/Utils/luarocks/tools/7z.exe differ diff --git a/Utils/luarocks/tools/cp.exe b/Utils/luarocks/tools/cp.exe new file mode 100644 index 000000000..0ef4fe85e Binary files /dev/null and b/Utils/luarocks/tools/cp.exe differ diff --git a/Utils/luarocks/tools/find.exe b/Utils/luarocks/tools/find.exe new file mode 100644 index 000000000..85192fbf4 Binary files /dev/null and b/Utils/luarocks/tools/find.exe differ diff --git a/Utils/luarocks/tools/libeay32.dll b/Utils/luarocks/tools/libeay32.dll new file mode 100644 index 000000000..8d31f8668 Binary files /dev/null and b/Utils/luarocks/tools/libeay32.dll differ diff --git a/Utils/luarocks/tools/libiconv2.dll b/Utils/luarocks/tools/libiconv2.dll new file mode 100644 index 000000000..544dd92f5 Binary files /dev/null and b/Utils/luarocks/tools/libiconv2.dll differ diff --git a/Utils/luarocks/tools/libintl3.dll b/Utils/luarocks/tools/libintl3.dll new file mode 100644 index 000000000..ec11e6b19 Binary files /dev/null and b/Utils/luarocks/tools/libintl3.dll differ diff --git a/Utils/luarocks/tools/libssl32.dll b/Utils/luarocks/tools/libssl32.dll new file mode 100644 index 000000000..a30ff0e9e Binary files /dev/null and b/Utils/luarocks/tools/libssl32.dll differ diff --git a/Utils/luarocks/tools/ls.exe b/Utils/luarocks/tools/ls.exe new file mode 100644 index 000000000..96ff2e574 Binary files /dev/null and b/Utils/luarocks/tools/ls.exe differ diff --git a/Utils/luarocks/tools/md5sum.exe b/Utils/luarocks/tools/md5sum.exe new file mode 100644 index 000000000..4ae9f74ff Binary files /dev/null and b/Utils/luarocks/tools/md5sum.exe differ diff --git a/Utils/luarocks/tools/mkdir.exe b/Utils/luarocks/tools/mkdir.exe new file mode 100644 index 000000000..83e57d97a Binary files /dev/null and b/Utils/luarocks/tools/mkdir.exe differ diff --git a/Utils/luarocks/tools/mv.exe b/Utils/luarocks/tools/mv.exe new file mode 100644 index 000000000..9fb65bb97 Binary files /dev/null and b/Utils/luarocks/tools/mv.exe differ diff --git a/Utils/luarocks/tools/pwd.exe b/Utils/luarocks/tools/pwd.exe new file mode 100644 index 000000000..7dd114de1 Binary files /dev/null and b/Utils/luarocks/tools/pwd.exe differ diff --git a/Utils/luarocks/tools/rmdir.exe b/Utils/luarocks/tools/rmdir.exe new file mode 100644 index 000000000..6a85c3c4b Binary files /dev/null and b/Utils/luarocks/tools/rmdir.exe differ diff --git a/Utils/luarocks/tools/test.exe b/Utils/luarocks/tools/test.exe new file mode 100644 index 000000000..94c95f9e4 Binary files /dev/null and b/Utils/luarocks/tools/test.exe differ diff --git a/Utils/luarocks/tools/uname.exe b/Utils/luarocks/tools/uname.exe new file mode 100644 index 000000000..3e2f4cf8f Binary files /dev/null and b/Utils/luarocks/tools/uname.exe differ diff --git a/Utils/luarocks/tools/wget.exe b/Utils/luarocks/tools/wget.exe new file mode 100644 index 000000000..54b372e6b Binary files /dev/null and b/Utils/luarocks/tools/wget.exe differ diff --git a/Utils/luarocks/wlua5.1.exe b/Utils/luarocks/wlua5.1.exe new file mode 100644 index 000000000..7be8c3a71 Binary files /dev/null and b/Utils/luarocks/wlua5.1.exe differ diff --git a/docs/Documentation/AI_A2A.html b/docs/Documentation/AI_A2A.html new file mode 100644 index 000000000..6d716e063 --- /dev/null +++ b/docs/Documentation/AI_A2A.html @@ -0,0 +1,2242 @@ + + + + + + +

+
+ +
+
+
+
+ +
+

Module AI_A2A

+ +

AI -- AI A2A Air Patrolling or Staging.

+ +
+ +

Author: Sven Van de Velde (FlightControl)

+

Contributions:

+ +
    +
  • Dutch_Baron: Working together with James has resulted in the creation of the AI_BALANCER class.
  • +
+ + +

James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-) + * Pikey: Testing and API concept review.

+ +
+ + +

Global(s)

+ + + + + +
AI_A2A +

AI_A2A class, extends Fsm#FSM_CONTROLLABLE

+ +

The AI_A2A class implements the core functions to operate an AI Group A2A tasking.

+
+

Type AI_A2A

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AI_A2A.CheckStatus + +
AI_A2A:ClearTargetDistance() + +
AI_A2A.DisengageRadius + +
AI_A2A:GetDispatcher() + +
AI_A2A.HomeAirbase + +
AI_A2A.IdleCount + +
AI_A2A:New(AIGroup) +

Creates a new AI_A2A object

+
AI_A2A:OnAfterRTB(Controllable, From, Event, To) +

OnAfter Transition Handler for Event RTB.

+
AI_A2A:OnAfterRefuel(Controllable, From, Event, To) +

Refuel Handler OnAfter for AI_A2A

+
AI_A2A:OnAfterStart(From, Event, To) +

Start Handler OnAfter for AI_A2A

+
AI_A2A:OnAfterStatus(Controllable, From, Event, To) +

OnAfter Transition Handler for Event Status.

+
AI_A2A:OnAfterStop(Controllable, From, Event, To) +

OnAfter Transition Handler for Event Stop.

+
AI_A2A:OnBeforeRTB(Controllable, From, Event, To) +

OnBefore Transition Handler for Event RTB.

+
AI_A2A:OnBeforeRefuel(Controllable, From, Event, To) +

Refuel Handler OnBefore for AI_A2A

+
AI_A2A:OnBeforeStart(From, Event, To) +

Start Handler OnBefore for AI_A2A

+
AI_A2A:OnBeforeStatus(Controllable, From, Event, To) +

OnBefore Transition Handler for Event Status.

+
AI_A2A:OnBeforeStop(Controllable, From, Event, To) +

OnBefore Transition Handler for Event Stop.

+
AI_A2A:OnCrash(EventData) + +
AI_A2A:OnEjection(EventData) + +
AI_A2A:OnEnterReturning(Controllable, From, Event, To) +

OnEnter Transition Handler for State Returning.

+
AI_A2A:OnEnterStopped(Controllable, From, Event, To) +

OnEnter Transition Handler for State Stopped.

+
AI_A2A:OnLeaveReturning(Controllable, From, Event, To) +

OnLeave Transition Handler for State Returning.

+
AI_A2A:OnLeaveStopped(Controllable, From, Event, To) +

OnLeave Transition Handler for State Stopped.

+
AI_A2A:OnPilotDead(EventData) + +
AI_A2A.PatrolCeilingAltitude + +
AI_A2A.PatrolDamageThreshold + +
AI_A2A.PatrolFloorAltitude + +
AI_A2A.PatrolFuelThresholdPercentage + +
AI_A2A.PatrolManageDamage + +
AI_A2A.PatrolManageFuel + +
AI_A2A.PatrolMaxSpeed + +
AI_A2A.PatrolMinSpeed + +
AI_A2A.PatrolOutOfFuelOrbitTime + +
AI_A2A:RTB() +

Synchronous Event Trigger for Event RTB.

+
AI_A2A.RTBHold(AIGroup, Fsm) + +
AI_A2A.RTBRoute(AIGroup, Fsm) + +
AI_A2A:Refuel() +

Refuel Trigger for AI_A2A

+
AI_A2A.Resume(AIGroup, Fsm) + +
AI_A2A:SetAltitude(PatrolFloorAltitude, PatrolCeilingAltitude) +

Sets the floor and ceiling altitude of the patrol.

+
AI_A2A:SetDamageThreshold(PatrolDamageThreshold) +

When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base.

+
AI_A2A:SetDisengageRadius(DisengageRadius) +

Sets the disengage range, that when engaging a target beyond the specified range, the engagement will be cancelled and the plane will RTB.

+
AI_A2A:SetDispatcher(Dispatcher) + +
AI_A2A:SetFuelThreshold(PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime) +

When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.

+
AI_A2A:SetHomeAirbase(HomeAirbase) +

Sets the home airbase.

+
AI_A2A:SetSpeed(PatrolMinSpeed, PatrolMaxSpeed) +

Sets (modifies) the minimum and maximum speed of the patrol.

+
AI_A2A:SetStatusOff() +

Set the status checking off.

+
AI_A2A:SetTanker(TankerName) +

Sets to refuel at the given tanker.

+
AI_A2A:SetTargetDistance(Coordinate) + +
AI_A2A:Start() +

Start Trigger for AI_A2A

+
AI_A2A:Status() +

Synchronous Event Trigger for Event Status.

+
AI_A2A:Stop() +

Synchronous Event Trigger for Event Stop.

+
AI_A2A.TankerName + +
AI_A2A:__RTB(Delay) +

Asynchronous Event Trigger for Event RTB.

+
AI_A2A:__Refuel(Delay) +

Refuel Asynchronous Trigger for AI_A2A

+
AI_A2A:__Start(Delay) +

Start Asynchronous Trigger for AI_A2A

+
AI_A2A:__Status(Delay) +

Asynchronous Event Trigger for Event Status.

+
AI_A2A:__Stop(Delay) +

Asynchronous Event Trigger for Event Stop.

+
AI_A2A:onafterDead() + +
AI_A2A:onafterHold(AIGroup, From, Event, To, HoldTime) + +
AI_A2A:onafterHome(AIGroup, From, Event, To) + +
AI_A2A:onafterRTB(AIGroup, From, Event, To) + +
AI_A2A:onafterRefuel(AIGroup, From, Event, To) + +
AI_A2A:onafterStart(Controllable, From, Event, To) +

Defines a new patrol route using the Process_PatrolZone parameters and settings.

+
AI_A2A:onafterStatus() + +
AI_A2A:onbeforeStatus() + +
+ +

Global(s)

+
+
+ + #AI_A2A + +AI_A2A + +
+
+ +

AI_A2A class, extends Fsm#FSM_CONTROLLABLE

+ +

The AI_A2A class implements the core functions to operate an AI Group A2A tasking.

+ + + + +

AI_A2A constructor

+ + + +

2. AI_A2A is a FSM

+ +

Process

+ +

2.1. AI_A2A States

+ +
    +
  • None ( Group ): The process is not started yet.
  • +
  • Patrolling ( Group ): The AI is patrolling the Patrol Zone.
  • +
  • Returning ( Group ): The AI is returning to Base.
  • +
  • Stopped ( Group ): The process is stopped.
  • +
  • Crashed ( Group ): The AI has crashed or is dead.
  • +
+ +

2.2. AI_A2A Events

+ +
    +
  • Start ( Group ): Start the process.
  • +
  • Stop ( Group ): Stop the process.
  • +
  • Route ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
  • +
  • RTB ( Group ): Route the AI to the home base.
  • +
  • Detect ( Group ): The AI is detecting targets.
  • +
  • Detected ( Group ): The AI has detected new targets.
  • +
  • Status ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
  • +
+ +

3. Set or Get the AI controllable

+ + + + +
+
+

Type AI_A2A

+

Field(s)

+
+
+ + #boolean + +AI_A2A.CheckStatus + +
+
+ + + +
+
+
+
+ + +AI_A2A:ClearTargetDistance() + +
+
+ + + +
+
+
+
+ + + +AI_A2A.DisengageRadius + +
+
+ + + +
+
+
+
+ + +AI_A2A:GetDispatcher() + +
+
+ + + +
+
+
+
+ + + +AI_A2A.HomeAirbase + +
+
+ + + +
+
+
+
+ + +AI_A2A.IdleCount + +
+
+ + + +
+
+
+
+ + +AI_A2A:New(AIGroup) + +
+
+ +

Creates a new AI_A2A object

+ +

Parameter

+ +

Return value

+ +

#AI_A2A:

+ + +
+
+
+
+ + +AI_A2A:OnAfterRTB(Controllable, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event RTB.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A:OnAfterRefuel(Controllable, From, Event, To) + +
+
+ +

Refuel Handler OnAfter for AI_A2A

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From :

    + +
  • +
  • + +

    #string Event :

    + +
  • +
  • + +

    #string To :

    + +
  • +
+
+
+
+
+ + +AI_A2A:OnAfterStart(From, Event, To) + +
+
+ +

Start Handler OnAfter for AI_A2A

+ +

Parameters

+
    +
  • + +

    #string From :

    + +
  • +
  • + +

    #string Event :

    + +
  • +
  • + +

    #string To :

    + +
  • +
+
+
+
+
+ + +AI_A2A:OnAfterStatus(Controllable, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Status.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A:OnAfterStop(Controllable, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Stop.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A:OnBeforeRTB(Controllable, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event RTB.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A:OnBeforeRefuel(Controllable, From, Event, To) + +
+
+ +

Refuel Handler OnBefore for AI_A2A

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From :

    + +
  • +
  • + +

    #string Event :

    + +
  • +
  • + +

    #string To :

    + +
  • +
+

Return value

+ +

#boolean:

+ + +
+
+
+
+ + +AI_A2A:OnBeforeStart(From, Event, To) + +
+
+ +

Start Handler OnBefore for AI_A2A

+ +

Parameters

+
    +
  • + +

    #string From :

    + +
  • +
  • + +

    #string Event :

    + +
  • +
  • + +

    #string To :

    + +
  • +
+

Return value

+ +

#boolean:

+ + +
+
+
+
+ + +AI_A2A:OnBeforeStatus(Controllable, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Status.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A:OnBeforeStop(Controllable, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Stop.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A:OnCrash(EventData) + +
+
+ + + +

Parameter

+ +
+
+
+
+ + +AI_A2A:OnEjection(EventData) + +
+
+ + + +

Parameter

+ +
+
+
+
+ + +AI_A2A:OnEnterReturning(Controllable, From, Event, To) + +
+
+ +

OnEnter Transition Handler for State Returning.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A:OnEnterStopped(Controllable, From, Event, To) + +
+
+ +

OnEnter Transition Handler for State Stopped.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A:OnLeaveReturning(Controllable, From, Event, To) + +
+
+ +

OnLeave Transition Handler for State Returning.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A:OnLeaveStopped(Controllable, From, Event, To) + +
+
+ +

OnLeave Transition Handler for State Stopped.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A:OnPilotDead(EventData) + +
+
+ + + +

Parameter

+ +
+
+
+
+ + + +AI_A2A.PatrolCeilingAltitude + +
+
+ + + +
+
+
+
+ + + +AI_A2A.PatrolDamageThreshold + +
+
+ + + +
+
+
+
+ + + +AI_A2A.PatrolFloorAltitude + +
+
+ + + +
+
+
+
+ + + +AI_A2A.PatrolFuelThresholdPercentage + +
+
+ + + +
+
+
+
+ + #boolean + +AI_A2A.PatrolManageDamage + +
+
+ + + +
+
+
+
+ + #boolean + +AI_A2A.PatrolManageFuel + +
+
+ + + +
+
+
+
+ + + +AI_A2A.PatrolMaxSpeed + +
+
+ + + +
+
+
+
+ + + +AI_A2A.PatrolMinSpeed + +
+
+ + + +
+
+
+
+ + + +AI_A2A.PatrolOutOfFuelOrbitTime + +
+
+ + + +
+
+
+
+ + +AI_A2A:RTB() + +
+
+ +

Synchronous Event Trigger for Event RTB.

+ +
+
+
+
+ + +AI_A2A.RTBHold(AIGroup, Fsm) + +
+
+ + + +

Parameters

+ +
+
+
+
+ + +AI_A2A.RTBRoute(AIGroup, Fsm) + +
+
+ + + +

Parameters

+ +
+
+
+
+ + +AI_A2A:Refuel() + +
+
+ +

Refuel Trigger for AI_A2A

+ +
+
+
+
+ + +AI_A2A.Resume(AIGroup, Fsm) + +
+
+ + + +

Parameters

+ +
+
+
+
+ + +AI_A2A:SetAltitude(PatrolFloorAltitude, PatrolCeilingAltitude) + +
+
+ +

Sets the floor and ceiling altitude of the patrol.

+ +

Parameters

+
    +
  • + +

    Dcs.DCSTypes#Altitude PatrolFloorAltitude : +The lowest altitude in meters where to execute the patrol.

    + +
  • +
  • + +

    Dcs.DCSTypes#Altitude PatrolCeilingAltitude : +The highest altitude in meters where to execute the patrol.

    + +
  • +
+

Return value

+ +

#AI_A2A: +self

+ +
+
+
+
+ + +AI_A2A:SetDamageThreshold(PatrolDamageThreshold) + +
+
+ +

When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base.

+ + +

However, damage cannot be foreseen early on. +Therefore, when the damage treshold is reached, +the AI will return immediately to the home base (RTB). +Note that for groups, the average damage of the complete group will be calculated. +So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25.

+ +

Parameter

+
    +
  • + +

    #number PatrolDamageThreshold : +The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.

    + +
  • +
+

Return value

+ +

#AI_A2A: +self

+ +
+
+
+
+ + +AI_A2A:SetDisengageRadius(DisengageRadius) + +
+
+ +

Sets the disengage range, that when engaging a target beyond the specified range, the engagement will be cancelled and the plane will RTB.

+ +

Parameter

+
    +
  • + +

    #number DisengageRadius : +The disengage range.

    + +
  • +
+

Return value

+ +

#AI_A2A: +self

+ +
+
+
+
+ + +AI_A2A:SetDispatcher(Dispatcher) + +
+
+ + + +

Parameter

+
    +
  • + +

    Dispatcher :

    + +
  • +
+
+
+
+
+ + +AI_A2A:SetFuelThreshold(PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime) + +
+
+ +

When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.

+ + +

Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. +When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_A2A. +Once the time is finished, the old AI will return to the base.

+ +

Parameters

+
    +
  • + +

    #number PatrolFuelThresholdPercentage : +The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.

    + +
  • +
  • + +

    #number PatrolOutOfFuelOrbitTime : +The amount of seconds the out of fuel AIControllable will orbit before returning to the base.

    + +
  • +
+

Return value

+ +

#AI_A2A: +self

+ +
+
+
+
+ + +AI_A2A:SetHomeAirbase(HomeAirbase) + +
+
+ +

Sets the home airbase.

+ +

Parameter

+ +

Return value

+ +

#AI_A2A: +self

+ +
+
+
+
+ + +AI_A2A:SetSpeed(PatrolMinSpeed, PatrolMaxSpeed) + +
+
+ +

Sets (modifies) the minimum and maximum speed of the patrol.

+ +

Parameters

+ +

Return value

+ +

#AI_A2A: +self

+ +
+
+
+
+ + +AI_A2A:SetStatusOff() + +
+
+ +

Set the status checking off.

+ +

Return value

+ +

#AI_A2A: +self

+ +
+
+
+
+ + +AI_A2A:SetTanker(TankerName) + +
+
+ +

Sets to refuel at the given tanker.

+ +

Parameter

+
    +
  • + +

    Wrapper.Group#GROUP TankerName : +The group name of the tanker as defined within the Mission Editor or spawned.

    + +
  • +
+

Return value

+ +

#AI_A2A: +self

+ +
+
+
+
+ + +AI_A2A:SetTargetDistance(Coordinate) + +
+
+ + + +

Parameter

+
    +
  • + +

    Coordinate :

    + +
  • +
+
+
+
+
+ + +AI_A2A:Start() + +
+
+ +

Start Trigger for AI_A2A

+ +
+
+
+
+ + +AI_A2A:Status() + +
+
+ +

Synchronous Event Trigger for Event Status.

+ +
+
+
+
+ + +AI_A2A:Stop() + +
+
+ +

Synchronous Event Trigger for Event Stop.

+ +
+
+
+
+ + + +AI_A2A.TankerName + +
+
+ + + +
+
+
+
+ + +AI_A2A:__RTB(Delay) + +
+
+ +

Asynchronous Event Trigger for Event RTB.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_A2A:__Refuel(Delay) + +
+
+ +

Refuel Asynchronous Trigger for AI_A2A

+ +

Parameter

+
    +
  • + +

    #number Delay :

    + +
  • +
+
+
+
+
+ + +AI_A2A:__Start(Delay) + +
+
+ +

Start Asynchronous Trigger for AI_A2A

+ +

Parameter

+
    +
  • + +

    #number Delay :

    + +
  • +
+
+
+
+
+ + +AI_A2A:__Status(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Status.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_A2A:__Stop(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Stop.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_A2A:onafterDead() + +
+
+ + + +
+
+
+
+ + +AI_A2A:onafterHold(AIGroup, From, Event, To, HoldTime) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup :

    + +
  • +
  • + +

    From :

    + +
  • +
  • + +

    Event :

    + +
  • +
  • + +

    To :

    + +
  • +
  • + +

    HoldTime :

    + +
  • +
+
+
+
+
+ + +AI_A2A:onafterHome(AIGroup, From, Event, To) + +
+
+ + + +

Parameters

+ +
+
+
+
+ + +AI_A2A:onafterRTB(AIGroup, From, Event, To) + +
+
+ + + +

Parameters

+ +
+
+
+
+ + +AI_A2A:onafterRefuel(AIGroup, From, Event, To) + +
+
+ + + +

Parameters

+ +
+
+
+
+ + +AI_A2A:onafterStart(Controllable, From, Event, To) + +
+
+ +

Defines a new patrol route using the Process_PatrolZone parameters and settings.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#AI_A2A: +self

+ +
+
+
+
+ + +AI_A2A:onafterStatus() + +
+
+ + + +
+
+
+
+ + +AI_A2A:onbeforeStatus() + +
+
+ + + +
+
+ +
+ +
+ + diff --git a/docs/Documentation/Cas.html b/docs/Documentation/AI_A2A_Cap.html similarity index 51% rename from docs/Documentation/Cas.html rename to docs/Documentation/AI_A2A_Cap.html index 4ed9fdf8c..b93320257 100644 --- a/docs/Documentation/Cas.html +++ b/docs/Documentation/AI_A2A_Cap.html @@ -17,7 +17,16 @@ index
-

Module Cas

+

Module AI_A2A_Cap

-

SP:Y MP:Y AI:Y HU:N TYP:Air -- This module contains the AICASZONE class.

+

AI -- Execute Combat Air Patrol (CAP).

+ +

Banner Image

+ +
+ +

AI CAP classes makes AI Controllables execute a Combat Air Patrol.

-
- -

1) #AICASZONE class, extends Core.Fsm#FSM_CONTROLLABLE

-

The #AICASZONE class implements the core functions to CAS a Zone by an AIR Controllable Group.

- -

1.1) AICASZONE constructor:

+

There are the following types of CAP classes defined:

- -

1.2) AICASZONE state machine:

-

The AICASZONE is a state machine: it manages the different events and states of the AIControllable it is controlling.

- -

1.2.1) AICASZONE Events:

- - - -

1.2.2) AICASZONE States:

- - -

1.2.3) AICASZONE state transition methods:

- - -

1.3) Manage the AICASZONE parameters:

-

The following methods are available to modify the parameters of an AICASZONE object:

- -
-

API CHANGE HISTORY

- -

The underlying change log documents the API changes. Please read this carefully. The following notation is used:

- -
    -
  • Added parts are expressed in bold type face.
  • -
  • Removed parts are expressed in italic type face.
  • -
- -

Hereby the change log:

- -

2017-01-12: Initial class and API.

- -
- -

AUTHORS and CONTRIBUTIONS

+

Author: Sven Van de Velde (FlightControl)

Contributions:

    -
  • Quax: Concept & Testing.
  • -
  • Pikey: Concept & Testing.
  • -
- -

Authors:

- -
    -
  • FlightControl: Concept, Design & Programming.
  • +
  • Quax: Concept, Advice & Testing.
  • +
  • Pikey: Concept, Advice & Testing.
  • +
  • Gunterlund: Test case revision.
  • +
  • **Whisper: Testing.
  • +
  • **Delta99: Testing.
+

Global(s)

- - - - - +
AI_CAS_ZONE - -
_NewEngageRoute(AIControllable)AI_A2A_CAP +

AIA2ACAP class, extends AICAP#AIPATROL_ZONE

+

The AIA2ACAP class implements the core functions to patrol a Zone by an AI Controllable or Group +and automatically engage any airborne enemies that are within a certain range or within a certain zone.

-

Type AI_CAS_ZONE

+

Type AI_A2A_CAP

- - - - - + - + - + - + - + - + - - - - - + - + - + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - - - - - + - + + + + + - + - + + + + + + + + + - + - + - + - + - + - + - + - + - + - + @@ -392,64 +404,116 @@
- #AI_CAS_ZONE - -AI_CAS_ZONE + #AI_A2A_CAP + +AI_A2A_CAP
+

AIA2ACAP class, extends AICAP#AIPATROL_ZONE

+ +

The AIA2ACAP class implements the core functions to patrol a Zone by an AI Controllable or Group +and automatically engage any airborne enemies that are within a certain range or within a certain zone.

+ + + +

Process

+ +

The AIA2ACAP is assigned a Group and this must be done before the AIA2ACAP process can be started using the Start event.

+ +

Process

+ +

The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. +Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.

+ +

Process

+ +

This cycle will continue.

+ +

Process

+ +

During the patrol, the AI will detect enemy targets, which are reported through the Detected event.

+ +

Process

+ +

When enemies are detected, the AI will automatically engage the enemy.

+ +

Process

+ +

Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.

+ +

Process

+ +

1. AIA2ACAP constructor

+ + + +

2. AIA2ACAP is a FSM

+ +

Process

+ +

2.1 AIA2ACAP States

+ +
    +
  • None ( Group ): The process is not started yet.
  • +
  • Patrolling ( Group ): The AI is patrolling the Patrol Zone.
  • +
  • Engaging ( Group ): The AI is engaging the bogeys.
  • +
  • Returning ( Group ): The AI is returning to Base..
  • +
+ +

2.2 AIA2ACAP Events

+ + + +

3. Set the Range of Engagement

+ +

Range

+ +

An optional range can be set in meters, +that will define when the AI will engage with the detected airborne enemy targets. +The range can be beyond or smaller than the range of the Patrol Zone. +The range is applied at the position of the AI. +Use the method AICAP#AIA2A_CAP.SetEngageRange() to define that range.

+ +

4. Set the Zone of Engagement

+ +

Zone

+ +

An optional Zone can be set, +that will define when the AI will engage with the detected airborne enemy targets. +Use the method AICap#AIA2A_CAP.SetEngageZone() to define that Zone.

+ +
+

Type AI_A2A_Cap

+ +

Type AI_A2A

+ +

Type AI_A2A_CAP

+

Field(s)

- -_NewEngageRoute(AIControllable) - -
-
- - - -

Parameter

- -
-
-

Type Cas

- -

Type AI_CAS_ZONE

- -

AICASZONE class

- -

Field(s)

-
-
- - Wrapper.Controllable#CONTROLLABLE - -AI_CAS_ZONE.AIControllable - -
-
- -

The Controllable patrolling.

- -
-
-
-
- - -AI_CAS_ZONE:Abort() + +AI_A2A_CAP:Abort()
@@ -461,8 +525,8 @@
- -AI_CAS_ZONE:Accomplish() + +AI_A2A_CAP:Accomplish()
@@ -475,8 +539,8 @@
#boolean - -AI_CAS_ZONE.Accomplished + +AI_A2A_CAP.Accomplished
@@ -488,9 +552,35 @@
- #boolean - -AI_CAS_ZONE.CheckStatus + +AI_A2A_CAP.AttackRoute(AIGroup, Fsm) + +
+
+ + + +

Parameters

+ +
+
+
+
+ + Core.Set#SET_UNIT + +AI_A2A_CAP.AttackSetUnit
@@ -502,22 +592,8 @@
- #string - -AI_CAS_ZONE.ClassName - -
-
- - - -
-
-
-
- - -AI_CAS_ZONE:Destroy() + +AI_A2A_CAP:Destroy()
@@ -529,22 +605,8 @@
- #boolean - -AI_CAS_ZONE.DetectUnits - -
-
- - - -
-
-
-
- - -AI_CAS_ZONE:Engage() + +AI_A2A_CAP:Engage()
@@ -557,8 +619,8 @@
- -AI_CAS_ZONE.EngageZone + +AI_A2A_CAP.EngageMaxSpeed
@@ -570,8 +632,64 @@
- -AI_CAS_ZONE:Fired() + + +AI_A2A_CAP.EngageMinSpeed + +
+
+ + + +
+
+
+
+ + + +AI_A2A_CAP.EngageRange + +
+
+ + + +
+
+
+
+ + + +AI_A2A_CAP.EngageZone + +
+
+ + + +
+
+
+
+ + #boolean + +AI_A2A_CAP.Engaging + +
+
+ + + +
+
+
+
+ + +AI_A2A_CAP:Fired()
@@ -583,18 +701,23 @@
- -AI_CAS_ZONE:New(PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone) + +AI_A2A_CAP:New(AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType)
-

Creates a new AICASZONE object

+

Creates a new AIA2ACAP object

Parameters

Return value

-

#AICASZONE: -self

+

#AIA2ACAP:

+
- -AI_CAS_ZONE:OnAfterAbort(Controllable, From, Event, To) + +AI_A2A_CAP:OnAfterAbort(Controllable, From, Event, To)
@@ -679,8 +815,8 @@ The To State string.

- -AI_CAS_ZONE:OnAfterAccomplish(Controllable, From, Event, To) + +AI_A2A_CAP:OnAfterAccomplish(Controllable, From, Event, To)
@@ -719,8 +855,8 @@ The To State string.

- -AI_CAS_ZONE:OnAfterDestroy(Controllable, From, Event, To) + +AI_A2A_CAP:OnAfterDestroy(Controllable, From, Event, To)
@@ -759,8 +895,8 @@ The To State string.

- -AI_CAS_ZONE:OnAfterEngage(Controllable, From, Event, To) + +AI_A2A_CAP:OnAfterEngage(Controllable, From, Event, To)
@@ -799,8 +935,8 @@ The To State string.

- -AI_CAS_ZONE:OnAfterFired(Controllable, From, Event, To) + +AI_A2A_CAP:OnAfterFired(Controllable, From, Event, To)
@@ -839,8 +975,8 @@ The To State string.

- -AI_CAS_ZONE:OnBeforeAbort(Controllable, From, Event, To) + +AI_A2A_CAP:OnBeforeAbort(Controllable, From, Event, To)
@@ -884,8 +1020,8 @@ Return false to cancel Transition.

- -AI_CAS_ZONE:OnBeforeAccomplish(Controllable, From, Event, To) + +AI_A2A_CAP:OnBeforeAccomplish(Controllable, From, Event, To)
@@ -929,8 +1065,8 @@ Return false to cancel Transition.

- -AI_CAS_ZONE:OnBeforeDestroy(Controllable, From, Event, To) + +AI_A2A_CAP:OnBeforeDestroy(Controllable, From, Event, To)
@@ -974,8 +1110,8 @@ Return false to cancel Transition.

- -AI_CAS_ZONE:OnBeforeEngage(Controllable, From, Event, To) + +AI_A2A_CAP:OnBeforeEngage(Controllable, From, Event, To)
@@ -1019,8 +1155,8 @@ Return false to cancel Transition.

- -AI_CAS_ZONE:OnBeforeFired(Controllable, From, Event, To) + +AI_A2A_CAP:OnBeforeFired(Controllable, From, Event, To)
@@ -1064,29 +1200,8 @@ Return false to cancel Transition.

- -AI_CAS_ZONE:OnDead(EventData) - -
-
- - - -

Parameter

- -
-
-
-
- - -AI_CAS_ZONE:OnEnterEngaging(Controllable, From, Event, To) + +AI_A2A_CAP:OnEnterEngaging(Controllable, From, Event, To)
@@ -1125,8 +1240,29 @@ The To State string.

- -AI_CAS_ZONE:OnLeaveEngaging(Controllable, From, Event, To) + +AI_A2A_CAP:OnEventDead(EventData) + +
+
+ + + +

Parameter

+ +
+
+
+
+ + +AI_A2A_CAP:OnLeaveEngaging(Controllable, From, Event, To)
@@ -1170,22 +1306,83 @@ Return false to cancel Transition.

- Core.Zone#ZONE_BASE - -AI_CAS_ZONE.TargetZone + +AI_A2A_CAP.Resume(AIGroup)
-

The Zone where the patrol needs to be executed.

+ + +

Parameter

+ +
+
+
+
+ + +AI_A2A_CAP:SetEngageRange(EngageRange) + +
+
+ +

Set the Engage Range when the AI will engage with airborne enemies.

+ +

Parameter

+
    +
  • + +

    #number EngageRange : +The Engage Range.

    + +
  • +
+

Return value

+ +

#AIA2ACAP: +self

- -AI_CAS_ZONE:__Abort(Delay) + +AI_A2A_CAP:SetEngageZone(EngageZone) + +
+
+ +

Set the Engage Zone which defines where the AI will engage bogies.

+ +

Parameter

+
    +
  • + +

    Core.Zone#ZONE EngageZone : +The zone where the AI is performing CAP.

    + +
  • +
+

Return value

+ +

#AIA2ACAP: +self

+ +
+
+
+
+ + +AI_A2A_CAP:__Abort(Delay)
@@ -1206,8 +1403,8 @@ The delay in seconds.

- -AI_CAS_ZONE:__Accomplish(Delay) + +AI_A2A_CAP:__Accomplish(Delay)
@@ -1228,8 +1425,8 @@ The delay in seconds.

- -AI_CAS_ZONE:__Destroy(Delay) + +AI_A2A_CAP:__Destroy(Delay)
@@ -1250,8 +1447,8 @@ The delay in seconds.

- -AI_CAS_ZONE:__Engage(Delay) + +AI_A2A_CAP:__Engage(Delay)
@@ -1272,8 +1469,8 @@ The delay in seconds.

- -AI_CAS_ZONE:__Fired(Delay) + +AI_A2A_CAP:__Fired(Delay)
@@ -1294,8 +1491,48 @@ The delay in seconds.

- -AI_CAS_ZONE:onafterAccomplish(Controllable, From, Event, To) + +AI_A2A_CAP:onafterAbort(AIGroup, From, Event, To) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE AIGroup : +The AI Group managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_CAP:onafterAccomplish(Controllable, From, Event, To)
@@ -1334,8 +1571,8 @@ The To State string.

- -AI_CAS_ZONE:onafterDestroy(Controllable, From, Event, To, EventData) + +AI_A2A_CAP:onafterDestroy(Controllable, From, Event, To, EventData)
@@ -1379,8 +1616,8 @@ The To State string.

- -AI_CAS_ZONE:onafterEngage(Controllable, From, Event, To) + +AI_A2A_CAP:onafterEngage(AIGroup, From, Event, To, AttackSetUnit)
@@ -1391,7 +1628,7 @@ The To State string.

+
+
+
+
+ + +AI_A2A_CAP:onafterPatrol(AIGroup, From, Event, To) + +
+
+ +

onafter State Transition for Event Patrol.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE AIGroup : +The AI Group managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • +

    #string To : The To State string.

    @@ -1419,8 +1701,8 @@ The To State string.

    - -AI_CAS_ZONE:onafterRTB(Controllable, From, Event, To, EventData) + +AI_A2A_CAP:onbeforeEngage(AIGroup, From, Event, To)
    @@ -1431,92 +1713,7 @@ The To State string.

    • -

      Wrapper.Controllable#CONTROLLABLE Controllable : -The Controllable Object managed by the FSM.

      - -
    • -
    • - -

      #string From : -The From State string.

      - -
    • -
    • - -

      #string Event : -The Event string.

      - -
    • -
    • - -

      #string To : -The To State string.

      - -
    • -
    • - -

      EventData :

      - -
    • -
    -
    -
    -
    -
    - - -AI_CAS_ZONE:onafterStart(Controllable, From, Event, To) - -
    -
    - -

    onafter State Transition for Event Start.

    - -

    Parameters

    -
      -
    • - -

      Wrapper.Controllable#CONTROLLABLE Controllable : -The Controllable Object managed by the FSM.

      - -
    • -
    • - -

      #string From : -The From State string.

      - -
    • -
    • - -

      #string Event : -The Event string.

      - -
    • -
    • - -

      #string To : -The To State string.

      - -
    • -
    -
    -
    -
    -
    - - -AI_CAS_ZONE:onbeforeEngage(Controllable, From, Event, To) - -
    -
    - - - -

    Parameters

    -
      -
    • - -

      Wrapper.Controllable#CONTROLLABLE Controllable : +

      Wrapper.Controllable#CONTROLLABLE AIGroup : The Controllable Object managed by the FSM.

    • diff --git a/docs/Documentation/AI_A2A_Dispatcher.html b/docs/Documentation/AI_A2A_Dispatcher.html new file mode 100644 index 000000000..703bc7751 --- /dev/null +++ b/docs/Documentation/AI_A2A_Dispatcher.html @@ -0,0 +1,5650 @@ + + + + + + +
      +
      + +
      +
      +
      +
      + +
      +

      Module AI_A2A_Dispatcher

      + +

      AI - The AIA2ADISPATCHER creates an automatic A2A defense system based on an EWR network targets and coordinating CAP and GCI.

      + + + +

      Banner Image

      + +
      + +

      QUICK START GUIDE

      + +

      There are basically two classes available to model an A2A defense system.

      + +

      AI_A2A_DISPATCHER is the main A2A defense class that models the A2A defense system. +AI_A2A_GCICAP derives or inherits from AI_A2A_DISPATCHER and is a more noob user friendly class, but is less flexible.

      + +

      Before you start using the AI_A2A_DISPATCHER or AI_A2A_GCICAP ask youself the following questions.

      + +

      0. Do I need AI_A2A_DISPATCHER or do I need AI_A2A_GCICAP?

      + +

      AI_A2A_GCICAP, automates a lot of the below questions using the mission editor and requires minimal lua scripting. +But the AI_A2A_GCICAP provides less flexibility and a lot of options are defaulted. +With AI_A2A_DISPATCHER you can setup a much more fine grained A2A defense mechanism, but some more (easy) lua scripting is required.

      + +

      1. Which Coalition am I modeling an A2A defense system for? blue or red?

      + +

      One AI_A2A_DISPATCHER object can create a defense system for one coalition, which is blue or red. +If you want to create a mutual defense system, for both blue and red, then you need to create two AI_A2A_DISPATCHER objects, +each governing their defense system.

      + + +

      2. Which type of EWR will I setup? Grouping based per AREA, per TYPE or per UNIT? (Later others will follow).

      + +

      The MOOSE framework leverages the Detection classes to perform the EWR detection. +Several types of Detection classes exist, and the most common characteristics of these classes is that they:

      + +
        +
      • Perform detections from multiple FACs as one co-operating entity.
      • +
      • Communicate with a Head Quarters, which consolidates each detection.
      • +
      • Groups detections based on a method (per area, per type or per unit).
      • +
      • Communicates detections.
      • +
      + +

      3. Which EWR units will be used as part of the detection system? Only Ground or also Airborne?

      + +

      Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. +These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). +Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. +The position of these units is very important as they need to provide enough coverage +to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them.

      + +

      4. Is a border required?

      + +

      Is this a cold car or a hot war situation? In case of a cold war situation, a border can be set that will only trigger defenses +if the border is crossed by enemy units.

      + +

      5. What maximum range needs to be checked to allow defenses to engage any attacker?

      + +

      A good functioning defense will have a "maximum range" evaluated to the enemy when CAP will be engaged or GCI will be spawned.

      + +

      6. Which Airbases, Carrier Ships, Farps will take part in the defense system for the Coalition?

      + +

      Carefully plan which airbases will take part in the coalition. Color each airbase in the color of the coalition.

      + +

      7. Which Squadrons will I create and which name will I give each Squadron?

      + +

      The defense system works with Squadrons. Each Squadron must be given a unique name, that forms the key to the defense system. +Several options and activities can be set per Squadron.

      + +

      8. Where will the Squadrons be located? On Airbases? On Carrier Ships? On Farps?

      + +

      Squadrons are placed as the "home base" on an airfield, carrier or farp. +Carefully plan where each Squadron will be located as part of the defense system.

      + +

      9. Which plane models will I assign for each Squadron? Do I need one plane model or more plane models per squadron?

      + +

      Per Squadron, one or multiple plane models can be allocated as Templates. +These are late activated groups with one airplane or helicopter that start with a specific name, called the template prefix. +The A2A defense system will select from the given templates a random template to spawn a new plane (group).

      + +

      10. Which payloads, skills and skins will these plane models have?

      + +

      Per Squadron, even if you have one plane model, you can still allocate multiple templates of one plane model, +each having different payloads, skills and skins. +The A2A defense system will select from the given templates a random template to spawn a new plane (group).

      + +

      11. For each Squadron, which will perform CAP?

      + +

      Per Squadron, evaluate which Squadrons will perform CAP. +Not all Squadrons need to perform CAP.

      + +

      12. For each Squadron doing CAP, in which ZONE(s) will the CAP be performed?

      + +

      Per CAP, evaluate where the CAP will be performed, in other words, define the zone. +Near the border or a bit further away?

      + +

      13. For each Squadron doing CAP, which zone types will I create?

      + +

      Per CAP zone, evaluate whether you want:

      + +
        +
      • simple trigger zones
      • +
      • polygon zones
      • +
      • moving zones
      • +
      + +

      Depending on the type of zone selected, a different Zone object needs to be created from a ZONE_ class.

      + +

      14. For each Squadron doing CAP, what are the time intervals and CAP amounts to be performed?

      + +

      For each CAP:

      + +
        +
      • How many CAP you want to have airborne at the same time?
      • +
      • How frequent you want the defense mechanism to check whether to start a new CAP?
      • +
      + +

      15. For each Squadron, which will perform GCI?

      + +

      For each Squadron, evaluate which Squadrons will perform GCI? +Not all Squadrons need to perform GCI.

      + +

      16. For each Squadron, which takeoff method will I use?

      + +

      For each Squadron, evaluate which takeoff method will be used:

      + +
        +
      • Straight from the air
      • +
      • From the runway
      • +
      • From a parking spot with running engines
      • +
      • From a parking spot with cold engines
      • +
      + +

      The default takeoff method is staight in the air.

      + +

      17. For each Squadron, which landing method will I use?

      + +

      For each Squadron, evaluate which landing method will be used:

      + +
        +
      • Despawn near the airbase when returning
      • +
      • Despawn after landing on the runway
      • +
      • Despawn after engine shutdown after landing
      • +
      + +

      The default landing method is despawn when near the airbase when returning.

      + +

      18. For each Squadron, which overhead will I use?

      + +

      For each Squadron, depending on the airplane type (modern, old) and payload, which overhead is required to provide any defense? +In other words, if X attacker airplanes are detected, how many Y defense airplanes need to be spawned per squadron? +The Y is dependent on the type of airplane (era), payload, fuel levels, skills etc. +The overhead is a factor that will calculate dynamically how many Y defenses will be required based on X attackers detected.

      + +

      The default overhead is 1. A value greater than 1, like 1.5 will increase the overhead with 50%, a value smaller than 1, like 0.5 will decrease the overhead with 50%.

      + +

      19. For each Squadron, which grouping will I use?

      + +

      When multiple targets are detected, how will defense airplanes be grouped when multiple defense airplanes are spawned for multiple attackers? +Per one, two, three, four?

      + +

      The default grouping is 1. That means, that each spawned defender will act individually.

      + +
      + +

      Authors: Sven Van de Velde (FlightControl) rework of GCICAP + introduction of new concepts (squadrons).

      +

      Authors: Stonehouse, SNAFU in terms of the advice, documentation, and the original GCICAP script.

      + + +

      Global(s)

      +
AI_CAS_ZONE.AIControllable -

The Controllable patrolling.

-
AI_CAS_ZONE:Abort()AI_A2A_CAP:Abort()

Synchronous Event Trigger for Event Abort.

AI_CAS_ZONE:Accomplish()AI_A2A_CAP:Accomplish()

Synchronous Event Trigger for Event Accomplish.

AI_CAS_ZONE.AccomplishedAI_A2A_CAP.Accomplished
AI_CAS_ZONE.CheckStatusAI_A2A_CAP.AttackRoute(AIGroup, Fsm)
AI_CAS_ZONE.ClassNameAI_A2A_CAP.AttackSetUnit
AI_CAS_ZONE:Destroy()AI_A2A_CAP:Destroy()

Synchronous Event Trigger for Event Destroy.

AI_CAS_ZONE.DetectUnits - -
AI_CAS_ZONE:Engage()AI_A2A_CAP:Engage()

Synchronous Event Trigger for Event Engage.

AI_CAS_ZONE.EngageZoneAI_A2A_CAP.EngageMaxSpeed
AI_CAS_ZONE:Fired()AI_A2A_CAP.EngageMinSpeed + +
AI_A2A_CAP.EngageRange + +
AI_A2A_CAP.EngageZone + +
AI_A2A_CAP.Engaging + +
AI_A2A_CAP:Fired()

Synchronous Event Trigger for Event Fired.

AI_CAS_ZONE:New(PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone)AI_A2A_CAP:New(AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType) -

Creates a new AICASZONE object

+

Creates a new AIA2ACAP object

AI_CAS_ZONE:OnAfterAbort(Controllable, From, Event, To)AI_A2A_CAP:OnAfterAbort(Controllable, From, Event, To)

OnAfter Transition Handler for Event Abort.

AI_CAS_ZONE:OnAfterAccomplish(Controllable, From, Event, To)AI_A2A_CAP:OnAfterAccomplish(Controllable, From, Event, To)

OnAfter Transition Handler for Event Accomplish.

AI_CAS_ZONE:OnAfterDestroy(Controllable, From, Event, To)AI_A2A_CAP:OnAfterDestroy(Controllable, From, Event, To)

OnAfter Transition Handler for Event Destroy.

AI_CAS_ZONE:OnAfterEngage(Controllable, From, Event, To)AI_A2A_CAP:OnAfterEngage(Controllable, From, Event, To)

OnAfter Transition Handler for Event Engage.

AI_CAS_ZONE:OnAfterFired(Controllable, From, Event, To)AI_A2A_CAP:OnAfterFired(Controllable, From, Event, To)

OnAfter Transition Handler for Event Fired.

AI_CAS_ZONE:OnBeforeAbort(Controllable, From, Event, To)AI_A2A_CAP:OnBeforeAbort(Controllable, From, Event, To)

OnBefore Transition Handler for Event Abort.

AI_CAS_ZONE:OnBeforeAccomplish(Controllable, From, Event, To)AI_A2A_CAP:OnBeforeAccomplish(Controllable, From, Event, To)

OnBefore Transition Handler for Event Accomplish.

AI_CAS_ZONE:OnBeforeDestroy(Controllable, From, Event, To)AI_A2A_CAP:OnBeforeDestroy(Controllable, From, Event, To)

OnBefore Transition Handler for Event Destroy.

AI_CAS_ZONE:OnBeforeEngage(Controllable, From, Event, To)AI_A2A_CAP:OnBeforeEngage(Controllable, From, Event, To)

OnBefore Transition Handler for Event Engage.

AI_CAS_ZONE:OnBeforeFired(Controllable, From, Event, To)AI_A2A_CAP:OnBeforeFired(Controllable, From, Event, To)

OnBefore Transition Handler for Event Fired.

AI_CAS_ZONE:OnDead(EventData) - -
AI_CAS_ZONE:OnEnterEngaging(Controllable, From, Event, To)AI_A2A_CAP:OnEnterEngaging(Controllable, From, Event, To)

OnEnter Transition Handler for State Engaging.

AI_CAS_ZONE:OnLeaveEngaging(Controllable, From, Event, To)AI_A2A_CAP:OnEventDead(EventData) + +
AI_A2A_CAP:OnLeaveEngaging(Controllable, From, Event, To)

OnLeave Transition Handler for State Engaging.

AI_CAS_ZONE.TargetZoneAI_A2A_CAP.Resume(AIGroup) -

The Zone where the patrol needs to be executed.

+
AI_CAS_ZONE:__Abort(Delay)AI_A2A_CAP:SetEngageRange(EngageRange) +

Set the Engage Range when the AI will engage with airborne enemies.

+
AI_A2A_CAP:SetEngageZone(EngageZone) +

Set the Engage Zone which defines where the AI will engage bogies.

+
AI_A2A_CAP:__Abort(Delay)

Asynchronous Event Trigger for Event Abort.

AI_CAS_ZONE:__Accomplish(Delay)AI_A2A_CAP:__Accomplish(Delay)

Asynchronous Event Trigger for Event Accomplish.

AI_CAS_ZONE:__Destroy(Delay)AI_A2A_CAP:__Destroy(Delay)

Asynchronous Event Trigger for Event Destroy.

AI_CAS_ZONE:__Engage(Delay)AI_A2A_CAP:__Engage(Delay)

Asynchronous Event Trigger for Event Engage.

AI_CAS_ZONE:__Fired(Delay)AI_A2A_CAP:__Fired(Delay)

Asynchronous Event Trigger for Event Fired.

AI_CAS_ZONE:onafterAccomplish(Controllable, From, Event, To)AI_A2A_CAP:onafterAbort(AIGroup, From, Event, To)
AI_CAS_ZONE:onafterDestroy(Controllable, From, Event, To, EventData)AI_A2A_CAP:onafterAccomplish(Controllable, From, Event, To)
AI_CAS_ZONE:onafterEngage(Controllable, From, Event, To)AI_A2A_CAP:onafterDestroy(Controllable, From, Event, To, EventData)
AI_CAS_ZONE:onafterRTB(Controllable, From, Event, To, EventData)AI_A2A_CAP:onafterEngage(AIGroup, From, Event, To, AttackSetUnit)
AI_CAS_ZONE:onafterStart(Controllable, From, Event, To)AI_A2A_CAP:onafterPatrol(AIGroup, From, Event, To) -

onafter State Transition for Event Start.

+

onafter State Transition for Event Patrol.

AI_CAS_ZONE:onbeforeEngage(Controllable, From, Event, To)AI_A2A_CAP:onbeforeEngage(AIGroup, From, Event, To)
+ + + + + + + + +
AI_A2A_DISPATCHER +

AI_A2A_DISPATCHER class, extends Tasking#DETECTION_MANAGER

+ +

Banner Image

+ +

The #AIA2ADISPATCHER class is designed to create an automatic air defence system for a coalition.

+
AI_A2A_GCICAP +

AI_A2A_GCICAP class, extends AIA2ADispatcher#AIA2ADISPATCHER

+ +

Banner Image

+ +

The AIA2AGCICAP class is designed to create an automatic air defence system for a coalition setting up GCI and CAP air defenses.

+
+

Type AI_A2A_DISPATCHER

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AI_A2A_DISPATCHER:AddDefenderToSquadron(Squadron, Defender, Size) + +
AI_A2A_DISPATCHER:CAP() +

CAP Trigger for AIA2ADISPATCHER

+
AI_A2A_DISPATCHER:CanCAP(SquadronName) + +
AI_A2A_DISPATCHER:CanGCI(SquadronName) + +
AI_A2A_DISPATCHER:ClearDefenderTask(Defender) + +
AI_A2A_DISPATCHER:ClearDefenderTaskTarget(Defender) + +
AI_A2A_DISPATCHER:CountCapAirborne(SquadronName) + +
AI_A2A_DISPATCHER:CountDefendersEngaged(Target) + +
AI_A2A_DISPATCHER:CountDefendersToBeEngaged(DetectedItem, DefenderCount) + +
AI_A2A_DISPATCHER.DefenderDefault + +
AI_A2A_DISPATCHER.DefenderSpawns + +
AI_A2A_DISPATCHER.DefenderSquadrons + +
AI_A2A_DISPATCHER.DefenderTasks + +
AI_A2A_DISPATCHER.Defenders + +
AI_A2A_DISPATCHER.Detection + +
AI_A2A_DISPATCHER.DisengageRadius + +
AI_A2A_DISPATCHER:ENGAGE() +

ENGAGE Trigger for AIA2ADISPATCHER

+
AI_A2A_DISPATCHER:EvaluateENGAGE(DetectedItem) +

Creates an ENGAGE task when there are human friendlies airborne near the targets.

+
AI_A2A_DISPATCHER:EvaluateGCI(DetectedItem) +

Creates an GCI task when there are targets for it.

+
AI_A2A_DISPATCHER:EvaluateSWEEP(DetectedItem) +

Creates an SWEEP task when there are targets for it.

+
AI_A2A_DISPATCHER:GCI() +

GCI Trigger for AIA2ADISPATCHER

+
AI_A2A_DISPATCHER.GciRadius + +
AI_A2A_DISPATCHER:GetAIFriendliesNearBy(DetectedItem) +

Calculates which AI friendlies are nearby the area

+
AI_A2A_DISPATCHER:GetCAPDelay(SquadronName) + +
AI_A2A_DISPATCHER:GetDefaultLanding() +

Gets the default method at which flights will land and despawn as part of the defense system.

+
AI_A2A_DISPATCHER:GetDefaultTakeoff() +

Gets the default method at which new flights will spawn and take-off as part of the defense system.

+
AI_A2A_DISPATCHER:GetDefenderTask(Defender) + +
AI_A2A_DISPATCHER:GetDefenderTaskFsm(Defender) + +
AI_A2A_DISPATCHER:GetDefenderTaskTarget(Defender) + +
AI_A2A_DISPATCHER:GetDefenderTasks() + +
AI_A2A_DISPATCHER:GetFriendliesNearBy(DetectedItem, Target) +

Calculates which friendlies are nearby the area

+
AI_A2A_DISPATCHER:GetPlayerFriendliesNearBy(DetectedItem) +

Calculates which HUMAN friendlies are nearby the area

+
AI_A2A_DISPATCHER:GetSquadron(SquadronName) +

Get an item from the Squadron table.

+
AI_A2A_DISPATCHER:GetSquadronFromDefender(Defender) + +
AI_A2A_DISPATCHER:GetSquadronLanding(SquadronName) +

Gets the method at which flights will land and despawn as part of the defense system.

+
AI_A2A_DISPATCHER:GetSquadronTakeoff(SquadronName) +

Gets the method at which new flights will spawn and take-off as part of the defense system.

+
AI_A2A_DISPATCHER.Landing +

Defnes Landing location.

+
AI_A2A_DISPATCHER:New(Detection) +

AIA2ADISPATCHER constructor.

+
AI_A2A_DISPATCHER:OnAfterAssign(From, Event, To, Task, TaskUnit, PlayerName) +

OnAfter Transition Handler for Event Assign.

+
AI_A2A_DISPATCHER:OnAfterCAP(From, Event, To) +

CAP Handler OnAfter for AIA2ADISPATCHER

+
AI_A2A_DISPATCHER:OnAfterENGAGE(From, Event, To) +

ENGAGE Handler OnAfter for AIA2ADISPATCHER

+
AI_A2A_DISPATCHER:OnAfterGCI(From, Event, To) +

GCI Handler OnAfter for AIA2ADISPATCHER

+
AI_A2A_DISPATCHER:OnBeforeCAP(From, Event, To) +

CAP Handler OnBefore for AIA2ADISPATCHER

+
AI_A2A_DISPATCHER:OnBeforeENGAGE(From, Event, To) +

ENGAGE Handler OnBefore for AIA2ADISPATCHER

+
AI_A2A_DISPATCHER:OnBeforeGCI(From, Event, To) +

GCI Handler OnBefore for AIA2ADISPATCHER

+
AI_A2A_DISPATCHER:OnEventCrashOrDead(EventData) + +
AI_A2A_DISPATCHER:OnEventEngineShutdown(EventData) + +
AI_A2A_DISPATCHER:OnEventLand(EventData) + +
AI_A2A_DISPATCHER:ProcessDetected(Detection) +

Assigns A2A AI Tasks in relation to the detected items.

+
AI_A2A_DISPATCHER:RemoveDefenderFromSquadron(Squadron, Defender) + +
AI_A2A_DISPATCHER.SchedulerCAP(AI_A2A_DISPATCHER, SquadronName, self) + +
AI_A2A_DISPATCHER:SetBorderZone(BorderZone) +

Define a border area to simulate a cold war scenario.

+
AI_A2A_DISPATCHER:SetDefaultCapLimit(CapLimit) +

Set the default CAP limit for squadrons, which will be used to determine how many CAP can be airborne at the same time for the squadron.

+
AI_A2A_DISPATCHER:SetDefaultCapTimeInterval(CapMinSeconds, CapMaxSeconds) +

Set the default CAP time interval for squadrons, which will be used to determine a random CAP timing.

+
AI_A2A_DISPATCHER:SetDefaultDamageThreshold(DamageThreshold) +

Set the default damage treshold when defenders will RTB.

+
AI_A2A_DISPATCHER:SetDefaultFuelThreshold(FuelThreshold) +

Set the default fuel treshold when defenders will RTB or Refuel in the air.

+
AI_A2A_DISPATCHER:SetDefaultGrouping(Grouping) +

Sets the default grouping of new airplanes spawned.

+
AI_A2A_DISPATCHER:SetDefaultLanding(Landing) +

Defines the default method at which flights will land and despawn as part of the defense system.

+
AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown() +

Sets flights by default to land and despawn at engine shutdown, as part of the defense system.

+
AI_A2A_DISPATCHER:SetDefaultLandingAtRunway() +

Sets flights by default to land and despawn at the runway, as part of the defense system.

+
AI_A2A_DISPATCHER:SetDefaultLandingNearAirbase() +

Sets flights by default to land and despawn near the airbase in the air, as part of the defense system.

+
AI_A2A_DISPATCHER:SetDefaultOverhead(Overhead) +

Defines the default amount of extra planes that will take-off as part of the defense system.

+
AI_A2A_DISPATCHER:SetDefaultTakeoff(Takeoff) +

Defines the default method at which new flights will spawn and take-off as part of the defense system.

+
AI_A2A_DISPATCHER:SetDefaultTakeoffFromParkingCold() +

Sets flights to by default take-off from the airbase at a cold location, as part of the defense system.

+
AI_A2A_DISPATCHER:SetDefaultTakeoffFromParkingHot() +

Sets flights by default to take-off from the airbase at a hot location, as part of the defense system.

+
AI_A2A_DISPATCHER:SetDefaultTakeoffFromRunway() +

Sets flights by default to take-off from the runway, as part of the defense system.

+
AI_A2A_DISPATCHER:SetDefaultTakeoffInAir() +

Sets flights to default take-off in the air, as part of the defense system.

+
AI_A2A_DISPATCHER:SetDefaultTakeoffInAirAltitude(TakeoffAltitude) +

Defines the default altitude where airplanes will spawn in the air and take-off as part of the defense system, when the take-off in the air method has been selected.

+
AI_A2A_DISPATCHER:SetDefaultTanker(TankerName) +

Set the default tanker where defenders will Refuel in the air.

+
AI_A2A_DISPATCHER:SetDefenderTask(SquadronName, Defender, Type, Fsm, Target) + +
AI_A2A_DISPATCHER:SetDefenderTaskTarget(AIGroup, Defender, Target) + +
AI_A2A_DISPATCHER:SetDisengageRadius(DisengageRadius) +

Define the radius to disengage any target when the distance to the home base is larger than the specified meters.

+
AI_A2A_DISPATCHER:SetEngageRadius(EngageRadius) +

Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission.

+
AI_A2A_DISPATCHER:SetGciRadius(GciRadius) +

Define the radius to check if a target can be engaged by an ground controlled intercept.

+
AI_A2A_DISPATCHER:SetIntercept(InterceptDelay) + +
AI_A2A_DISPATCHER:SetSquadron(SquadronName, AirbaseName, TemplatePrefixes, Resources) +

This is the main method to define Squadrons programmatically.

+
AI_A2A_DISPATCHER:SetSquadronCap(SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType) +

Set a CAP for a Squadron.

+
AI_A2A_DISPATCHER:SetSquadronCapInterval(SquadronName, CapLimit, LowInterval, HighInterval, Probability) +

Set the squadron CAP parameters.

+
AI_A2A_DISPATCHER:SetSquadronFuelThreshold(SquadronName, FuelThreshold) +

Set the fuel treshold for the squadron when defenders will RTB or Refuel in the air.

+
AI_A2A_DISPATCHER:SetSquadronGci(SquadronName, EngageMinSpeed, EngageMaxSpeed) + +
AI_A2A_DISPATCHER:SetSquadronGrouping(SquadronName, Grouping) +

Sets the grouping of new airplanes spawned.

+
AI_A2A_DISPATCHER:SetSquadronLanding(SquadronName, Landing) +

Defines the method at which flights will land and despawn as part of the defense system.

+
AI_A2A_DISPATCHER:SetSquadronLandingAtEngineShutdown(SquadronName) +

Sets flights to land and despawn at engine shutdown, as part of the defense system.

+
AI_A2A_DISPATCHER:SetSquadronLandingAtRunway(SquadronName) +

Sets flights to land and despawn at the runway, as part of the defense system.

+
AI_A2A_DISPATCHER:SetSquadronLandingNearAirbase(SquadronName) +

Sets flights to land and despawn near the airbase in the air, as part of the defense system.

+
AI_A2A_DISPATCHER:SetSquadronOverhead(SquadronName, Overhead) +

Defines the amount of extra planes that will take-off as part of the defense system.

+
AI_A2A_DISPATCHER:SetSquadronTakeoff(SquadronName, Takeoff) +

Defines the method at which new flights will spawn and take-off as part of the defense system.

+
AI_A2A_DISPATCHER:SetSquadronTakeoffFromParkingCold(SquadronName) +

Sets flights to take-off from the airbase at a cold location, as part of the defense system.

+
AI_A2A_DISPATCHER:SetSquadronTakeoffFromParkingHot(SquadronName) +

Sets flights to take-off from the airbase at a hot location, as part of the defense system.

+
AI_A2A_DISPATCHER:SetSquadronTakeoffFromRunway(SquadronName) +

Sets flights to take-off from the runway, as part of the defense system.

+
AI_A2A_DISPATCHER:SetSquadronTakeoffInAir(SquadronName, TakeoffAltitude) +

Sets flights to take-off in the air, as part of the defense system.

+
AI_A2A_DISPATCHER:SetSquadronTakeoffInAirAltitude(SquadronName, TakeoffAltitude) +

Defines the default altitude where airplanes will spawn in the air and take-off as part of the defense system, when the take-off in the air method has been selected.

+
AI_A2A_DISPATCHER:SetSquadronTanker(SquadronName, TankerName) +

Set the squadron tanker where defenders will Refuel in the air.

+
AI_A2A_DISPATCHER:SetTacticalDisplay(TacticalDisplay) +

Display a tactical report every 30 seconds about which aircraft are: + * Patrolling + * Engaging + * Returning + * Damaged + * Out of Fuel + * ...

+
AI_A2A_DISPATCHER.TacticalDisplay + +
AI_A2A_DISPATCHER.Takeoff + +
AI_A2A_DISPATCHER:__CAP(Delay) +

CAP Asynchronous Trigger for AIA2ADISPATCHER

+
AI_A2A_DISPATCHER:__ENGAGE(Delay) +

ENGAGE Asynchronous Trigger for AIA2ADISPATCHER

+
AI_A2A_DISPATCHER:__GCI(Delay) +

GCI Asynchronous Trigger for AIA2ADISPATCHER

+
AI_A2A_DISPATCHER:onafterCAP(From, Event, To, SquadronName) + +
AI_A2A_DISPATCHER:onafterENGAGE(From, Event, To, Target, Defenders) + +
AI_A2A_DISPATCHER:onafterGCI(From, Event, To, DetectedItem, DefendersMissing, Friendlies) + +
+ +

Type AI_A2A_GCICAP

+ + + + + + + + + + + + + + + + + +
AI_A2A_GCICAP.CAPTemplates + +
AI_A2A_GCICAP:New(EWRPrefixes, TemplatePrefixes, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, Resources) +

AIA2AGCICAP constructor.

+
AI_A2A_GCICAP:NewWithBorder(EWRPrefixes, TemplatePrefixes, BorderPrefix, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, Resources) +

AIA2AGCICAP constructor with border.

+
AI_A2A_GCICAP.Templates + +
+ +

Global(s)

+
+
+ + #AI_A2A_DISPATCHER + +AI_A2A_DISPATCHER + +
+
+ +

AI_A2A_DISPATCHER class, extends Tasking#DETECTION_MANAGER

+ +

Banner Image

+ +

The #AIA2ADISPATCHER class is designed to create an automatic air defence system for a coalition.

+ + + +
+ +

Demo Missions

+ +

AI_A2A_DISPATCHER Demo Missions

+ +
+ +

YouTube Channel

+ +

DCS WORLD - MOOSE - A2A GCICAP - Build an automatic A2A Defense System

+ +
+ +

Banner Image

+ +

It includes automatic spawning of Combat Air Patrol aircraft (CAP) and Ground Controlled Intercept aircraft (GCI) in response to enemy air movements that are detected by a ground based radar network. +CAP flights will take off and proceed to designated CAP zones where they will remain on station until the ground radars direct them to intercept detected enemy aircraft or they run short of fuel and must return to base (RTB). When a CAP flight leaves their zone to perform an interception or return to base a new CAP flight will spawn to take their place. +If all CAP flights are engaged or RTB then additional GCI interceptors will scramble to intercept unengaged enemy aircraft under ground radar control. +With a little time and with a little work it provides the mission designer with a convincing and completely automatic air defence system. +In short it is a plug in very flexible and configurable air defence module for DCS World.

+ +

Note that in order to create a two way A2A defense system, two AI_A2A_DISPATCHER defense system may need to be created, for each coalition one. +This is a good implementation, because maybe in the future, more coalitions may become available in DCS world.

+ +
+ +

USAGE GUIDE

+ +

1. AI_A2A_DISPATCHER constructor:

+ +

Banner Image

+ + +

The AIA2ADISPATCHER.New() method creates a new AI_A2A_DISPATCHER instance.

+ +

1.1. Define the EWR network:

+ +

As part of the AI_A2A_DISPATCHER :New() constructor, an EWR network must be given as the first parameter. +An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy.

+ +

Banner Image

+ +

Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. +These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). +Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. +The position of these units is very important as they need to provide enough coverage +to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them.

+ +

Banner Image

+ +

Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. +For example if they are a long way forward and can detect enemy planes on the ground and taking off +they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. +Having the radars further back will mean a slower escalation because fewer targets will be detected and +therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. +It all depends on what the desired effect is.

+ +

EWR networks are dynamically constructed, that is, they form part of the Functional#DETECTION_BASE object that is given as the input parameter of the AI_A2A_DISPATCHER class. +By defining in a smart way the names or name prefixes of the groups with EWR capable units, these groups will be automatically added or deleted from the EWR network, +increasing or decreasing the radar coverage of the Early Warning System.

+ +

See the following example to setup an EWR network containing EWR stations and AWACS.

+ +

Banner Image +Banner Image

+ +
-- Define a SET_GROUP object that builds a collection of groups that define the EWR network.
+-- Here we build the network with all the groups that have a name starting with DF CCCP AWACS and DF CCCP EWR.
+DetectionSetGroup = SET_GROUP:New()
+DetectionSetGroup:FilterPrefixes( { "DF CCCP AWACS", "DF CCCP EWR" } )
+DetectionSetGroup:FilterStart()
+
+-- Setup the detection and group targets to a 30km range!
+Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 )
+
+-- Setup the A2A dispatcher, and initialize it.
+A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )
+
+ +

The above example creates a SET_GROUP instance, and stores this in the variable (object) DetectionSetGroup. +DetectionSetGroup is then being configured to filter all active groups with a group name starting with DF CCCP AWACS or DF CCCP EWR to be included in the Set. +DetectionSetGroup is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set.

+ +

Then a new Detection object is created from the class DETECTION_AREAS. A grouping radius of 30000 is choosen, which is 30km. +The Detection object is then passed to the AIA2ADISPATCHER.New() method to indicate the EWR network configuration and setup the A2A defense detection mechanism.

+ +

You could build a mutual defense system like this:

+ +
A2ADispatcher_Red = AI_A2A_DISPATCHER:New( EWR_Red )
+A2ADispatcher_Blue = AI_A2A_DISPATCHER:New( EWR_Blue )
+
+ +

2. Define the detected target grouping radius:

+ +

The target grouping radius is a property of the Detection object, that was passed to the AI_A2A_DISPATCHER object, but can be changed. +The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. +Fast planes like in the 80s, need a larger radius than WWII planes.
+Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft.

+ +

Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate +group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small!

+ +

3. Set the Engage Radius:

+ +

Define the Engage Radius to engage any target by airborne friendlies, +which are executing cap or returning from an intercept mission.

+ +

Banner Image

+ +

If there is a target area detected and reported, +then any friendlies that are airborne near this target area, +will be commanded to (re-)engage that target when available (if no other tasks were commanded).

+ +

For example, if 50000 or 50km is given as a value, then any friendly that is airborne within 50km from the detected target, +will be considered to receive the command to engage that target area.

+ +

You need to evaluate the value of this parameter carefully:

+ +
    +
  • If too small, more intercept missions may be triggered upon detected target areas.
  • +
  • If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far.
  • +
+ +

The default Engage Radius is defined as 100000 or 100km. +Use the method AIA2ADISPATCHER.SetEngageRadius() to set a specific Engage Radius. +The Engage Radius is defined for ALL squadrons which are operational.

+ +

Demonstration Mission: AID-019 - AI_A2A - Engage Range Test

+ +

In this example an Engage Radius is set to various values.

+ +
-- Set 50km as the radius to engage any target by airborne friendlies.
+A2ADispatcher:SetEngageRadius( 50000 )
+
+-- Set 100km as the radius to engage any target by airborne friendlies.
+A2ADispatcher:SetEngageRadius() -- 100000 is the default value.
+
+ + +

4. Set the Ground Controlled Intercept Radius or Gci radius:

+ +

When targets are detected that are still really far off, you don't want the AIA2ADISPATCHER to launch intercepts just yet. +You want it to wait until a certain Gci range is reached, which is the distance of the closest airbase to target +being smaller than the Ground Controlled Intercept radius or Gci radius.

+ +

The default Gci radius is defined as 200000 or 200km. Override the default Gci radius when the era of the warfare is early, or, +when you don't want to let the AIA2ADISPATCHER react immediately when a certain border or area is not being crossed.

+ +

Use the method AIA2ADISPATCHER.SetGciRadius() to set a specific controlled ground intercept radius. +The Ground Controlled Intercept radius is defined for ALL squadrons which are operational.

+ +

Demonstration Mission: AID-013 - AI_A2A - Intercept Test

+ +

In these examples, the Gci Radius is set to various values:

+ +
-- Now Setup the A2A dispatcher, and initialize it using the Detection object.
+A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) 
+
+-- Set 100km as the radius to ground control intercept detected targets from the nearest airbase.
+A2ADispatcher:SetGciRadius( 100000 )
+
+-- Set 200km as the radius to ground control intercept.
+A2ADispatcher:SetGciRadius() -- 200000 is the default value.
+
+ +

5. Set the borders:

+ +

According to the tactical and strategic design of the mission broadly decide the shape and extent of red and blue territories. +They should be laid out such that a border area is created between the two coalitions.

+ +

Banner Image

+ +

Define a border area to simulate a cold war scenario. +Use the method AIA2ADISPATCHER.SetBorderZone() to create a border zone for the dispatcher.

+ +

A cold war is one where CAP aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border. +A hot war is one where CAP aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send CAP and GCI aircraft to attack it.

+ +

Banner Image

+ +

If it’s a cold war then the borders of red and blue territory need to be defined using a zone object derived from Zone#ZONE_BASE. +If a hot war is chosen then no borders actually need to be defined using the helicopter units other than +it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. +In a hot war the borders are effectively defined by the ground based radar coverage of a coalition.

+ +

Demonstration Mission: AID-009 - AI_A2A - Border Test

+ +

In this example a border is set for the CCCP A2A dispatcher:

+ +

Banner Image

+ +
-- Setup the A2A dispatcher, and initialize it.
+A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )
+
+-- Setup the border.
+-- Initialize the dispatcher, setting up a border zone. This is a polygon, 
+-- which takes the waypoints of a late activated group with the name CCCP Border as the boundaries of the border area.
+-- Any enemy crossing this border will be engaged.
+
+CCCPBorderZone = ZONE_POLYGON:New( "CCCP Border", GROUP:FindByName( "CCCP Border" ) )
+A2ADispatcher:SetBorderZone( CCCPBorderZone )
+
+ +

6. Squadrons:

+ +

The AI_A2A_DISPATCHER works with Squadrons, that need to be defined using the different methods available.

+ +

Use the method AIA2ADISPATCHER.SetSquadron() to setup a new squadron active at an airfield, +while defining which plane types are being used by the squadron and how many resources are available.

+ +

Squadrons:

+ +
    +
  • Have name (string) that is the identifier or key of the squadron.
  • +
  • Have specific plane types.
  • +
  • Are located at one airbase.
  • +
  • Optionally have a limited set of resources. The default is that squadrons have unlimited resources.
  • +
+ +

The name of the squadron given acts as the squadron key in the AI_A2A_DISPATCHER:Squadron...() methods.

+ +

Additionally, squadrons have specific configuration options to:

+ +
    +
  • Control how new aircraft are taking off from the airfield (in the air, cold, hot, at the runway).
  • +
  • Control how returning aircraft are landing at the airfield (in the air near the airbase, after landing, after engine shutdown).
  • +
  • Control the grouping of new aircraft spawned at the airfield. If there is more than one aircraft to be spawned, these may be grouped.
  • +
  • Control the overhead or defensive strength of the squadron. Depending on the types of planes and amount of resources, the mission designer can choose to increase or reduce the amount of planes spawned.
  • +
+ +

For performance and bug workaround reasons within DCS, squadrons have different methods to spawn new aircraft or land returning or damaged aircraft.

+ +

This example defines a couple of squadrons. Note the templates defined within the Mission Editor.

+ +

Banner Image +Banner Image

+ +
 -- Setup the squadrons.
+ A2ADispatcher:SetSquadron( "Mineralnye", AIRBASE.Caucasus.Mineralnye_Vody, { "SQ CCCP SU-27" }, 20 )
+ A2ADispatcher:SetSquadron( "Maykop", AIRBASE.Caucasus.Maykop_Khanskaya, { "SQ CCCP MIG-31" }, 20 )
+ A2ADispatcher:SetSquadron( "Mozdok", AIRBASE.Caucasus.Mozdok, { "SQ CCCP MIG-31" }, 20 )
+ A2ADispatcher:SetSquadron( "Sochi", AIRBASE.Caucasus.Sochi_Adler, { "SQ CCCP SU-27" }, 20 )
+ A2ADispatcher:SetSquadron( "Novo", AIRBASE.Caucasus.Novorossiysk, { "SQ CCCP SU-27" }, 20 )
+
+ +

6.1. Set squadron take-off methods

+ +

Use the various SetSquadronTakeoff... methods to control how squadrons are taking-off from the airfield:

+ + + +

The default landing method is to spawn new aircraft directly in the air.

+ +

Use these methods to fine-tune for specific airfields that are known to create bottlenecks, or have reduced airbase efficiency. +The more and the longer aircraft need to taxi at an airfield, the more risk there is that:

+ +
    +
  • aircraft will stop waiting for each other or for a landing aircraft before takeoff.
  • +
  • aircraft may get into a "dead-lock" situation, where two aircraft are blocking each other.
  • +
  • aircraft may collide at the airbase.
  • +
  • aircraft may be awaiting the landing of a plane currently in the air, but never lands ...
  • +
+ +

Currently within the DCS engine, the airfield traffic coordination is erroneous and contains a lot of bugs. +If you experience while testing problems with aircraft take-off or landing, please use one of the above methods as a solution to workaround these issues!

+ +

This example sets the default takeoff method to be from the runway. +And for a couple of squadrons overrides this default method.

+ +
 -- Setup the Takeoff methods
+
+ -- The default takeoff
+ A2ADispatcher:SetDefaultTakeOffFromRunway()
+
+ -- The individual takeoff per squadron
+ A2ADispatcher:SetSquadronTakeoff( "Mineralnye", AI_A2A_DISPATCHER.Takeoff.Air )
+ A2ADispatcher:SetSquadronTakeoffInAir( "Sochi" )
+ A2ADispatcher:SetSquadronTakeoffFromRunway( "Mozdok" )
+ A2ADispatcher:SetSquadronTakeoffFromParkingCold( "Maykop" )
+ A2ADispatcher:SetSquadronTakeoffFromParkingHot( "Novo" )  
+
+ + +

6.1. Set Squadron takeoff altitude when spawning new aircraft in the air.

+ +

In the case of the AIA2ADISPATCHER.SetSquadronTakeoffInAir() there is also an other parameter that can be applied. +That is modifying or setting the altitude from where planes spawn in the air. +Use the method AIA2ADISPATCHER.SetSquadronTakeoffInAirAltitude() to set the altitude for a specific squadron. +The default takeoff altitude can be modified or set using the method AIA2ADISPATCHER.SetSquadronTakeoffInAirAltitude(). +As part of the method AIA2ADISPATCHER.SetSquadronTakeoffInAir() a parameter can be specified to set the takeoff altitude. +If this parameter is not specified, then the default altitude will be used for the squadron.

+ +

6.2. Set squadron landing methods

+ +

In analogy with takeoff, the landing methods are to control how squadrons land at the airfield:

+ + + +

You can use these methods to minimize the airbase coodination overhead and to increase the airbase efficiency. +When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the +A2A defense system, as no new CAP or GCI planes can takeoff. +Note that the method AIA2ADISPATCHER.SetSquadronLandingNearAirbase() will only work for returning aircraft, not for damaged or out of fuel aircraft. +Damaged or out-of-fuel aircraft are returning to the nearest friendly airbase and will land, and are out of control from ground control.

+ +

This example defines the default landing method to be at the runway. +And for a couple of squadrons overrides this default method.

+ +
 -- Setup the Landing methods
+
+ -- The default landing method
+ A2ADispatcher:SetDefaultLandingAtRunway()
+
+ -- The individual landing per squadron
+ A2ADispatcher:SetSquadronLandingAtRunway( "Mineralnye" )
+ A2ADispatcher:SetSquadronLandingNearAirbase( "Sochi" )
+ A2ADispatcher:SetSquadronLandingAtEngineShutdown( "Mozdok" )
+ A2ADispatcher:SetSquadronLandingNearAirbase( "Maykop" )
+ A2ADispatcher:SetSquadronLanding( "Novo", AI_A2A_DISPATCHER.Landing.AtRunway )
+
+ + +

6.3. Set squadron grouping

+ +

Use the method AIA2ADISPATCHER.SetSquadronGrouping() to set the grouping of CAP or GCI flights that will take-off when spawned.

+ +

Banner Image

+ +

In the case of GCI, the AIA2ADISPATCHER.SetSquadronGrouping() method has additional behaviour. When there aren't enough CAP flights airborne, a GCI will be initiated for the remaining +targets to be engaged. Depending on the grouping parameter, the spawned flights for GCI are grouped into this setting.
+For example with a group setting of 2, if 3 targets are detected and cannot be engaged by CAP or any airborne flight, +a GCI needs to be started, the GCI flights will be grouped as follows: Group 1 of 2 flights and Group 2 of one flight!

+ +

Even more ... If one target has been detected, and the overhead is 1.5, grouping is 1, then two groups of planes will be spawned, with one unit each!

+ +

The grouping value is set for a Squadron, and can be dynamically adjusted during mission execution, so to adjust the defense flights grouping when the tactical situation changes.

+ +

6.4. Overhead and Balance the effectiveness of the air defenses in case of GCI.

+ +

The effectiveness can be set with the overhead parameter. This is a number that is used to calculate the amount of Units that dispatching command will allocate to GCI in surplus of detected amount of units. +The default value of the overhead parameter is 1.0, which means equal balance.

+ +

Banner Image

+ +

However, depending on the (type of) aircraft (strength and payload) in the squadron and the amount of resources available, this parameter can be changed.

+ +

The AIA2ADISPATCHER.SetSquadronOverhead() method can be used to tweak the defense strength, +taking into account the plane types of the squadron.

+ +

For example, a MIG-31 with full long-distance A2A missiles payload, may still be less effective than a F-15C with short missiles... +So in this case, one may want to use the AIA2ADISPATCHER.SetOverhead() method to allocate more defending planes as the amount of detected attacking planes. +The overhead must be given as a decimal value with 1 as the neutral value, which means that overhead values:

+ +
    +
  • Higher than 1.0, for example 1.5, will increase the defense unit amounts. For 4 planes detected, 6 planes will be spawned.
  • +
  • Lower than 1, for example 0.75, will decrease the defense unit amounts. For 4 planes detected, only 3 planes will be spawned.
  • +
+ +

The amount of defending units is calculated by multiplying the amount of detected attacking planes as part of the detected group +multiplied by the Overhead and rounded up to the smallest integer.

+ +

For example ... If one target has been detected, and the overhead is 1.5, grouping is 1, then two groups of planes will be spawned, with one unit each!

+ +

The overhead value is set for a Squadron, and can be dynamically adjusted during mission execution, so to adjust the defense overhead when the tactical situation changes.

+ +

6.5. Squadron fuel treshold.

+ +

When an airplane gets out of fuel to a certain %-tage, which is by default 15% (0.15), there are two possible actions that can be taken: + - The defender will go RTB, and will be replaced with a new defender if possible. + - The defender will refuel at a tanker, if a tanker has been specified for the squadron.

+ +

Use the method AIA2ADISPATCHER.SetSquadronFuelThreshold() to set the squadron fuel treshold of spawned airplanes for all squadrons.

+ +

7. Setup a squadron for CAP

+ +

7.1. Set the CAP zones

+ +

CAP zones are patrol areas where Combat Air Patrol (CAP) flights loiter until they either return to base due to low fuel or are assigned an interception task by ground control.

+ +

Banner Image

+ +
    +
  • As the CAP flights wander around within the zone waiting to be tasked, these zones need to be large enough that the aircraft are not constantly turning + but do not have to be big and numerous enough to completely cover a border.

  • +
  • CAP zones can be of any type, and are derived from the Zone#ZONE_BASE class. Zones can be Zone#ZONE, Zone#ZONE_POLYGON, Zone#ZONE_UNIT, Zone#ZONE_GROUP, etc. + This allows to setup static, moving and/or complex zones wherein aircraft will perform the CAP.

  • +
  • Typically 20000-50000 metres width is used and they are spaced so that aircraft in the zone waiting for tasks don’t have to far to travel to protect their coalitions important targets. + These targets are chosen as part of the mission design and might be an important airfield or town etc. + Zone size is also determined somewhat by territory size, plane types + (eg WW2 aircraft might mean smaller zones or more zones because they are slower and take longer to intercept enemy aircraft).

  • +
  • In a cold war it is important to make sure a CAP zone doesn’t intrude into enemy territory as otherwise CAP flights will likely cross borders + and spark a full scale conflict which will escalate rapidly.

  • +
  • CAP flights do not need to be in the CAP zone before they are “on station†and ready for tasking.

  • +
  • Typically if a CAP flight is tasked and therefore leaves their zone empty while they go off and intercept their target another CAP flight will spawn to take their place.

  • +
+ +

Banner Image

+ +

The following example illustrates how CAP zones are coded:

+ +

Banner Image

+ +
 -- CAP Squadron execution.
+ CAPZoneEast = ZONE_POLYGON:New( "CAP Zone East", GROUP:FindByName( "CAP Zone East" ) )
+ A2ADispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 )
+ A2ADispatcher:SetSquadronCapInterval( "Mineralnye", 2, 30, 60, 1 )
+
+ +

Banner Image

+ +
 CAPZoneWest = ZONE_POLYGON:New( "CAP Zone West", GROUP:FindByName( "CAP Zone West" ) )
+ A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" )
+ A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
+
+ +

Banner Image

+ +
 CAPZoneMiddle = ZONE:New( "CAP Zone Middle")
+ A2ADispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" )
+ A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
+
+ +

Note the different Zone MOOSE classes being used to create zones of different types. Please click the Zone link for more information about the different zone types. +Zones can be circles, can be setup in the mission editor using trigger zones, but can also be setup in the mission editor as polygons and in this case GROUP objects are being used!

+ +

7.2. Set the squadron to execute CAP:

+ +

The method AIA2ADISPATCHER.SetSquadronCap() defines a CAP execution for a squadron.

+ +

Setting-up a CAP zone also requires specific parameters:

+ +
    +
  • The minimum and maximum altitude
  • +
  • The minimum speed and maximum patrol speed
  • +
  • The minimum and maximum engage speed
  • +
  • The type of altitude measurement
  • +
+ +

These define how the squadron will perform the CAP while partrolling. Different terrain types requires different types of CAP.

+ +

The AIA2ADISPATCHER.SetSquadronCapInterval() method specifies how much and when CAP flights will takeoff.

+ +

It is recommended not to overload the air defense with CAP flights, as these will decrease the performance of the overall system.

+ +

For example, the following setup will create a CAP for squadron "Sochi":

+ +
 A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" )
+ A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
+
+ +

7.3. Squadron tanker to refuel when executing CAP and defender is out of fuel.

+ +

Instead of sending CAP to RTB when out of fuel, you can let CAP refuel in mid air using a tanker. +This greatly increases the efficiency of your CAP operations.

+ +

In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected. +Then, use the method AIA2ADISPATCHER.SetDefaultTanker() to set the default tanker for the refuelling. +You can also specify a specific tanker for refuelling for a squadron by using the method AIA2ADISPATCHER.SetSquadronTanker().

+ +

When the tanker specified is alive and in the air, the tanker will be used for refuelling.

+ +

For example, the following setup will create a CAP for squadron "Gelend" with a refuel task for the squadron:

+ +

Banner Image

+ +
 -- Define the CAP
+ A2ADispatcher:SetSquadron( "Gelend", AIRBASE.Caucasus.Gelendzhik, { "SQ CCCP SU-30" }, 20 )
+ A2ADispatcher:SetSquadronCap( "Gelend", ZONE:New( "PatrolZoneGelend" ), 4000, 8000, 600, 800, 1000, 1300 )
+ A2ADispatcher:SetSquadronCapInterval( "Gelend", 2, 30, 600, 1 ) 
+ A2ADispatcher:SetSquadronGci( "Gelend", 900, 1200 )
+
+ -- Setup the Refuelling for squadron "Gelend", at tanker (group) "TankerGelend" when the fuel in the tank of the CAP defenders is less than 80%.
+ A2ADispatcher:SetSquadronFuelThreshold( "Gelend", 0.8 )
+ A2ADispatcher:SetSquadronTanker( "Gelend", "TankerGelend" )
+
+ +

8. Setup a squadron for GCI:

+ +

The method AIA2ADISPATCHER.SetSquadronGci() defines a GCI execution for a squadron.

+ +

Setting-up a GCI readiness also requires specific parameters:

+ +
    +
  • The minimum speed and maximum patrol speed
  • +
+ +

Essentially this controls how many flights of GCI aircraft can be active at any time. +Note allowing large numbers of active GCI flights can adversely impact mission performance on low or medium specification hosts/servers. +GCI needs to be setup at strategic airbases. Too far will mean that the aircraft need to fly a long way to reach the intruders, +too short will mean that the intruders may have alraedy passed the ideal interception point!

+ +

For example, the following setup will create a GCI for squadron "Sochi":

+ +
 A2ADispatcher:SetSquadronGci( "Mozdok", 900, 1200 )
+
+ +

9. Other configuration options

+ +

9.1. Set a tactical display panel:

+ +

Every 30 seconds, a tactical display panel can be shown that illustrates what the status is of the different groups controlled by AI_A2A_DISPATCHER. +Use the method AIA2ADISPATCHER.SetTacticalDisplay() to switch on the tactical display panel. The default will not show this panel. +Note that there may be some performance impact if this panel is shown.

+ +

10. Defaults settings.

+ +

This provides a good overview of the different parameters that are setup or hardcoded by default. +For some default settings, a method is available that allows you to tweak the defaults.

+ +

10.1. Default takeoff method.

+ +

The default takeoff method is set to in the air, which means that new spawned airplanes will be spawned directly in the air above the airbase by default.

+ +

The default takeoff method can be set for ALL squadrons that don't have an individual takeoff method configured.

+ + + +

10.2. Default landing method.

+ +

The default landing method is set to near the airbase, which means that returning airplanes will be despawned directly in the air by default.

+ +

The default landing method can be set for ALL squadrons that don't have an individual landing method configured.

+ + + +

10.3. Default overhead.

+ +

The default overhead is set to 1. That essentially means that there isn't any overhead set by default.

+ +

The default overhead value can be set for ALL squadrons that don't have an individual overhead value configured.

+ +

Use the AIA2ADISPATCHER.SetDefaultOverhead() method can be used to set the default overhead or defense strength for ALL squadrons.

+ +

10.4. Default grouping.

+ +

The default grouping is set to one airplane. That essentially means that there won't be any grouping applied by default.

+ +

The default grouping value can be set for ALL squadrons that don't have an individual grouping value configured.

+ +

Use the method AIA2ADISPATCHER.SetDefaultGrouping() to set the default grouping of spawned airplanes for all squadrons.

+ +

10.5. Default RTB fuel treshold.

+ +

When an airplane gets out of fuel to a certain %-tage, which is 15% (0.15), it will go RTB, and will be replaced with a new airplane when applicable.

+ +

Use the method AIA2ADISPATCHER.SetDefaultFuelThreshold() to set the default fuel treshold of spawned airplanes for all squadrons.

+ +

10.6. Default RTB damage treshold.

+ +

When an airplane is damaged to a certain %-tage, which is 40% (0.40), it will go RTB, and will be replaced with a new airplane when applicable.

+ +

Use the method AIA2ADISPATCHER.SetDefaultDamageThreshold() to set the default damage treshold of spawned airplanes for all squadrons.

+ +

10.7. Default settings for CAP.

+ +

10.7.1. Default CAP Time Interval.

+ +

CAP is time driven, and will evaluate in random time intervals if a new CAP needs to be spawned. +The default CAP time interval is between 180 and 600 seconds.

+ +

Use the method AIA2ADISPATCHER.SetDefaultCapTimeInterval() to set the default CAP time interval of spawned airplanes for all squadrons.
+Note that you can still change the CAP limit and CAP time intervals for each CAP individually using the AIA2ADISPATCHER.SetSquadronCapTimeInterval() method.

+ +

10.7.2. Default CAP limit.

+ +

Multiple CAP can be airborne at the same time for one squadron, which is controlled by the CAP limit. +The default CAP limit is 1 CAP per squadron to be airborne at the same time. +Note that the default CAP limit is used when a Squadron CAP is defined, and cannot be changed afterwards. +So, ensure that you set the default CAP limit before you spawn the Squadron CAP.

+ +

Use the method AIA2ADISPATCHER.SetDefaultCapTimeInterval() to set the default CAP time interval of spawned airplanes for all squadrons.
+Note that you can still change the CAP limit and CAP time intervals for each CAP individually using the AIA2ADISPATCHER.SetSquadronCapTimeInterval() method.

+ +

10.7.3. Default tanker for refuelling when executing CAP.

+ +

Instead of sending CAP to RTB when out of fuel, you can let CAP refuel in mid air using a tanker. +This greatly increases the efficiency of your CAP operations.

+ +

In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected. +Then, use the method AIA2ADISPATCHER.SetDefaultTanker() to set the tanker for the dispatcher. +Use the method AIA2ADISPATCHER.SetDefaultFuelTreshold() to set the %-tage left in the defender airplane tanks when a refuel action is needed.

+ +

When the tanker specified is alive and in the air, the tanker will be used for refuelling.

+ +

For example, the following setup will set the default refuel tanker to "Tanker":

+ +

Banner Image

+ +
 -- Define the CAP
+ A2ADispatcher:SetSquadron( "Sochi", AIRBASE.Caucasus.Sochi_Adler, { "SQ CCCP SU-34" }, 20 )
+ A2ADispatcher:SetSquadronCap( "Sochi", ZONE:New( "PatrolZone" ), 4000, 8000, 600, 800, 1000, 1300 )
+ A2ADispatcher:SetSquadronCapInterval("Sochi", 2, 30, 600, 1 ) 
+ A2ADispatcher:SetSquadronGci( "Sochi", 900, 1200 )
+
+ -- Set the default tanker for refuelling to "Tanker", when the default fuel treshold has reached 90% fuel left.
+ A2ADispatcher:SetDefaultFuelThreshold( 0.9 )
+ A2ADispatcher:SetDefaultTanker( "Tanker" )
+
+ +

10.8. Default settings for GCI.

+ +

10.8.1. Optimal intercept point calculation.

+ +

When intruders are detected, the intrusion path of the attackers can be monitored by the EWR.
+Although defender planes might be on standby at the airbase, it can still take some time to get the defenses up in the air if there aren't any defenses airborne. +This time can easily take 2 to 3 minutes, and even then the defenders still need to fly towards the target, which takes also time.

+ +

Therefore, an optimal intercept point is calculated which takes a couple of parameters:

+ +
    +
  • The average bearing of the intruders for an amount of seconds.
  • +
  • The average speed of the intruders for an amount of seconds.
  • +
  • An assumed time it takes to get planes operational at the airbase.
  • +
+ +

The intercept point will determine:

+ +
    +
  • If there are any friendlies close to engage the target. These can be defenders performing CAP or defenders in RTB.
  • +
  • The optimal airbase from where defenders will takeoff for GCI.
  • +
+ +

Use the method AIA2ADISPATCHER.SetIntercept() to modify the assumed intercept delay time to calculate a valid interception.

+ +

10.8.2. Default Disengage Radius.

+ +

The radius to disengage any target when the distance of the defender to the home base is larger than the specified meters. +The default Disengage Radius is 300km (300000 meters). Note that the Disengage Radius is applicable to ALL squadrons!

+ +

Use the method AIA2ADISPATCHER.SetDisengageRadius() to modify the default Disengage Radius to another distance setting.

+ + +

11. Q & A:

+ +

11.1. Which countries will be selected for each coalition?

+ +

Which countries are assigned to a coalition influences which units are available to the coalition. +For example because the mission calls for a EWR radar on the blue side the Ukraine might be chosen as a blue country +so that the 55G6 EWR radar unit is available to blue.
+Some countries assign different tasking to aircraft, for example Germany assigns the CAP task to F-4E Phantoms but the USA does not.
+Therefore if F4s are wanted as a coalition’s CAP or GCI aircraft Germany will need to be assigned to that coalition.

+ +

11.2. Country, type, load out, skill and skins for CAP and GCI aircraft?

+ +
    +
  • Note these can be from any countries within the coalition but must be an aircraft with one of the main tasks being “CAPâ€.
  • +
  • Obviously skins which are selected must be available to all players that join the mission otherwise they will see a default skin.
  • +
  • Load outs should be appropriate to a CAP mission eg perhaps drop tanks for CAP flights and extra missiles for GCI flights.
  • +
  • These decisions will eventually lead to template aircraft units being placed as late activation units that the script will use as templates for spawning CAP and GCI flights. Up to 4 different aircraft configurations can be chosen for each coalition. The spawned aircraft will inherit the characteristics of the template aircraft.
  • +
  • The selected aircraft type must be able to perform the CAP tasking for the chosen country.
  • +
+ + + +
+
+
+
+ + #AI_A2A_GCICAP + +AI_A2A_GCICAP + +
+
+ +

AI_A2A_GCICAP class, extends AIA2ADispatcher#AIA2ADISPATCHER

+ +

Banner Image

+ +

The AIA2AGCICAP class is designed to create an automatic air defence system for a coalition setting up GCI and CAP air defenses.

+ + +

The class derives from AI#AIA2ADISPATCHER and thus, all the methods that are defined in the AI#AIA2ADISPATCHER class, can be used also in AI_A2A_GCICAP.

+ +
+ +

Demo Missions

+ +

AI_A2A_GCICAP for Caucasus

+

AI_A2A_GCICAP for NTTR

+

AI_A2A_GCICAP for Normandy

+ +

AI_A2A_GCICAP for beta testers

+ +
+ +

YouTube Channel

+ +

DCS WORLD - MOOSE - A2A GCICAP - Build an automatic A2A Defense System

+ +
+ +

Banner Image

+ +

AI_A2A_GCICAP includes automatic spawning of Combat Air Patrol aircraft (CAP) and Ground Controlled Intercept aircraft (GCI) in response to enemy +air movements that are detected by an airborne or ground based radar network.

+ +

With a little time and with a little work it provides the mission designer with a convincing and completely automatic air defence system.

+ +

The AIA2AGCICAP provides a lightweight configuration method using the mission editor. Within a very short time, and with very little coding, +the mission designer is able to configure a complete A2A defense system for a coalition using the DCS Mission Editor available functions. +Using the DCS Mission Editor, you define borders of the coalition which are guarded by GCICAP, +configure airbases to belong to the coalition, define squadrons flying certain types of planes or payloads per airbase, and define CAP zones. +Very little lua needs to be applied, a one liner, which is fully explained below, which can be embedded +right in a DO SCRIPT trigger action or in a larger DO SCRIPT FILE trigger action.

+ +

CAP flights will take off and proceed to designated CAP zones where they will remain on station until the ground radars direct them to intercept +detected enemy aircraft or they run short of fuel and must return to base (RTB).

+ +

When a CAP flight leaves their zone to perform a GCI or return to base a new CAP flight will spawn to take its place. +If all CAP flights are engaged or RTB then additional GCI interceptors will scramble to intercept unengaged enemy aircraft under ground radar control.

+ +

In short it is a plug in very flexible and configurable air defence module for DCS World.

+ +
+ +

The following actions need to be followed when using AI_A2A_GCICAP in your mission:

+ +

1) Configure a working AI_A2A_GCICAP defense system for ONE coalition.

+ +

1.1) Define which airbases are for which coalition.

+ +

Mission Editor Action

+ +

Color the airbases red or blue. You can do this by selecting the airbase on the map, and select the coalition blue or red.

+ +

1.2) Place groups of units given a name starting with a EWR prefix of your choice to build your EWR network.

+ +

Mission Editor Action

+ +

All EWR groups starting with the EWR prefix (text) will be included in the detection system.

+ +

An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy. +Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. +These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). +Additionally, ANY other radar capable unit can be part of the EWR network! +Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. +The position of these units is very important as they need to provide enough coverage +to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them.

+ +

Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. +For example if they are a long way forward and can detect enemy planes on the ground and taking off +they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. +Having the radars further back will mean a slower escalation because fewer targets will be detected and +therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. +It all depends on what the desired effect is.

+ +

EWR networks are dynamically maintained. By defining in a smart way the names or name prefixes of the groups with EWR capable units, these groups will be automatically added or deleted from the EWR network, +increasing or decreasing the radar coverage of the Early Warning System.

+ +

1.3) Place Airplane or Helicopter Groups with late activation switched on above the airbases to define Squadrons.

+ +

Mission Editor Action

+ +

These are templates, with a given name starting with a Template prefix above each airbase that you wanna have a squadron. +These templates need to be within 1.5km from the airbase center. They don't need to have a slot at the airplane, they can just be positioned above the airbase, +without a route, and should only have ONE unit.

+ +

Mission Editor Action

+ +

All airplane or helicopter groups that are starting with any of the choosen Template Prefixes will result in a squadron created at the airbase.

+ +

1.4) Place floating helicopters to create the CAP zones defined by its route points.

+ +

Mission Editor Action

+ +

All airplane or helicopter groups that are starting with any of the choosen Template Prefixes will result in a squadron created at the airbase.

+ +

The helicopter indicates the start of the CAP zone. +The route points define the form of the CAP zone polygon.

+ +

Mission Editor Action

+ +

The place of the helicopter is important, as the airbase closest to the helicopter will be the airbase from where the CAP planes will take off for CAP.

+ +

2) There are a lot of defaults set, which can be further modified using the methods in AI#AIA2ADISPATCHER:

+ +

2.1) Planes are taking off in the air from the airbases.

+ +

This prevents airbases to get cluttered with airplanes taking off, it also reduces the risk of human players colliding with taxiiing airplanes, +resulting in the airbase to halt operations.

+ +

You can change the way how planes take off by using the inherited methods from AI_A2A_DISPATCHER:

+ + + +

Use these methods to fine-tune for specific airfields that are known to create bottlenecks, or have reduced airbase efficiency. +The more and the longer aircraft need to taxi at an airfield, the more risk there is that:

+ +
    +
  • aircraft will stop waiting for each other or for a landing aircraft before takeoff.
  • +
  • aircraft may get into a "dead-lock" situation, where two aircraft are blocking each other.
  • +
  • aircraft may collide at the airbase.
  • +
  • aircraft may be awaiting the landing of a plane currently in the air, but never lands ...
  • +
+ +

Currently within the DCS engine, the airfield traffic coordination is erroneous and contains a lot of bugs. +If you experience while testing problems with aircraft take-off or landing, please use one of the above methods as a solution to workaround these issues!

+ +

2.2) Planes return near the airbase or will land if damaged.

+ +

When damaged airplanes return to the airbase, they will be routed and will dissapear in the air when they are near the airbase. +There are exceptions to this rule, airplanes that aren't "listening" anymore due to damage or out of fuel, will return to the airbase and land.

+ +

You can change the way how planes land by using the inherited methods from AI_A2A_DISPATCHER:

+ + + +

You can use these methods to minimize the airbase coodination overhead and to increase the airbase efficiency. +When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the +A2A defense system, as no new CAP or GCI planes can takeoff. +Note that the method AIA2ADISPATCHER.SetSquadronLandingNearAirbase() will only work for returning aircraft, not for damaged or out of fuel aircraft. +Damaged or out-of-fuel aircraft are returning to the nearest friendly airbase and will land, and are out of control from ground control.

+ +

2.3) CAP operations setup for specific airbases, will be executed with the following parameters:

+ +
    +
  • The altitude will range between 6000 and 10000 meters.
  • +
  • The CAP speed will vary between 500 and 800 km/h.
  • +
  • The engage speed between 800 and 1200 km/h.
  • +
+ +

You can change or add a CAP zone by using the inherited methods from AI_A2A_DISPATCHER:

+ +

The method AIA2ADISPATCHER.SetSquadronCap() defines a CAP execution for a squadron.

+ +

Setting-up a CAP zone also requires specific parameters:

+ +
    +
  • The minimum and maximum altitude
  • +
  • The minimum speed and maximum patrol speed
  • +
  • The minimum and maximum engage speed
  • +
  • The type of altitude measurement
  • +
+ +

These define how the squadron will perform the CAP while partrolling. Different terrain types requires different types of CAP.

+ +

The AIA2ADISPATCHER.SetSquadronCapInterval() method specifies how much and when CAP flights will takeoff.

+ +

It is recommended not to overload the air defense with CAP flights, as these will decrease the performance of the overall system.

+ +

For example, the following setup will create a CAP for squadron "Sochi":

+ +

A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" ) + A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )

+ +

2.4) Each airbase will perform GCI when required, with the following parameters:

+ +
    +
  • The engage speed is between 800 and 1200 km/h.
  • +
+ +

You can change or add a GCI parameters by using the inherited methods from AI_A2A_DISPATCHER:

+ +

The method AIA2ADISPATCHER.SetSquadronGci() defines a GCI execution for a squadron.

+ +

Setting-up a GCI readiness also requires specific parameters:

+ +
    +
  • The minimum speed and maximum patrol speed
  • +
+ +

Essentially this controls how many flights of GCI aircraft can be active at any time. +Note allowing large numbers of active GCI flights can adversely impact mission performance on low or medium specification hosts/servers. +GCI needs to be setup at strategic airbases. Too far will mean that the aircraft need to fly a long way to reach the intruders, +too short will mean that the intruders may have alraedy passed the ideal interception point!

+ +

For example, the following setup will create a GCI for squadron "Sochi":

+ +

A2ADispatcher:SetSquadronGci( "Mozdok", 900, 1200 )

+ +

2.5) Grouping or detected targets.

+ +

Detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate +group being detected.

+ +

Targets will be grouped within a radius of 30km by default.

+ +

The radius indicates that detected targets need to be grouped within a radius of 30km. +The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. +Fast planes like in the 80s, need a larger radius than WWII planes.
+Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft.

+ +

3) Additional notes:

+ +

In order to create a two way A2A defense system, two AI_A2A_GCICAP defense systems must need to be created, for each coalition one. +Each defense system needs its own EWR network setup, airplane templates and CAP configurations.

+ +

This is a good implementation, because maybe in the future, more coalitions may become available in DCS world.

+ +

4) Coding examples how to use the AI_A2A_GCICAP class:

+ +

4.1) An easy setup:

+ +
 -- Setup the AI_A2A_GCICAP dispatcher for one coalition, and initialize it.
+ GCI_Red = AI_A2A_GCICAP:New( "EWR CCCP", "SQUADRON CCCP", "CAP CCCP", 2 )
+
+

-- +The following parameters were given to the :New method of AIA2AGCICAP, and mean the following:

+ +
    +
  • "EWR CCCP": Groups of the blue coalition are placed that define the EWR network. These groups start with the name EWR CCCP.
  • +
  • "SQUADRON CCCP": Late activated Groups objects of the red coalition are placed above the relevant airbases that will contain these templates in the squadron. + These late activated Groups start with the name SQUADRON CCCP. Each Group object contains only one Unit, and defines the weapon payload, skin and skill level.
  • +
  • "CAP CCCP": CAP Zones are defined using floating, late activated Helicopter Group objects, where the route points define the route of the polygon of the CAP Zone. + These Helicopter Group objects start with the name CAP CCCP, and will be the locations wherein CAP will be performed.
  • +
  • 2 Defines how many CAP airplanes are patrolling in each CAP zone defined simulateneously.
  • +
+ + +

4.2) A more advanced setup:

+ +
 -- Setup the AI_A2A_GCICAP dispatcher for the blue coalition.
+
+ A2A_GCICAP_Blue = AI_A2A_GCICAP:New( { "BLUE EWR" }, { "104th", "105th", "106th" }, { "104th CAP" }, 4 ) 
+
+ +

The following parameters for the :New method have the following meaning:

+ +
    +
  • { "BLUE EWR" }: An array of the group name prefixes of the groups of the blue coalition are placed that define the EWR network. These groups start with the name BLUE EWR.
  • +
  • { "104th", "105th", "106th" }: An array of the group name prefixes of the Late activated Groups objects of the blue coalition are + placed above the relevant airbases that will contain these templates in the squadron. + These late activated Groups start with the name 104th or 105th or 106th.
  • +
  • { "104th CAP" }: An array of the names of the CAP zones are defined using floating, late activated helicopter group objects, + where the route points define the route of the polygon of the CAP Zone. + These Helicopter Group objects start with the name 104th CAP, and will be the locations wherein CAP will be performed.
  • +
  • 4 Defines how many CAP airplanes are patrolling in each CAP zone defined simulateneously.
  • +
+ + +
+
+

Type AI_A2A_Dispatcher

+ +

Type AI_A2A_DISPATCHER

+ +

AIA2ADISPATCHER class.

+ +

Field(s)

+
+
+ + +AI_A2A_DISPATCHER:AddDefenderToSquadron(Squadron, Defender, Size) + +
+
+ + + +

Parameters

+
    +
  • + +

    Squadron :

    + +
  • +
  • + +

    Defender :

    + +
  • +
  • + +

    Size :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:CAP() + +
+
+ +

CAP Trigger for AIA2ADISPATCHER

+ +
+
+
+
+ + +AI_A2A_DISPATCHER:CanCAP(SquadronName) + +
+
+ + + +

Parameter

+
    +
  • + +

    #string SquadronName : +The squadron name.

    + +
  • +
+

Return value

+ +

#table: +DefenderSquadron

+ +
+
+
+
+ + +AI_A2A_DISPATCHER:CanGCI(SquadronName) + +
+
+ + + +

Parameter

+
    +
  • + +

    #string SquadronName : +The squadron name.

    + +
  • +
+

Return value

+ +

#table: +DefenderSquadron

+ +
+
+
+
+ + +AI_A2A_DISPATCHER:ClearDefenderTask(Defender) + +
+
+ + + +

Parameter

+
    +
  • + +

    Defender :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:ClearDefenderTaskTarget(Defender) + +
+
+ + + +

Parameter

+
    +
  • + +

    Defender :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:CountCapAirborne(SquadronName) + +
+
+ + + +

Parameter

+
    +
  • + +

    SquadronName :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:CountDefendersEngaged(Target) + +
+
+ + + +

Parameter

+
    +
  • + +

    Target :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:CountDefendersToBeEngaged(DetectedItem, DefenderCount) + +
+
+ + + +

Parameters

+
    +
  • + +

    DetectedItem :

    + +
  • +
  • + +

    DefenderCount :

    + +
  • +
+
+
+
+
+ + + +AI_A2A_DISPATCHER.DefenderDefault + +
+
+ + + + +

The Defender Default Settings over all Squadrons.

+ +
+
+
+
+ + + +AI_A2A_DISPATCHER.DefenderSpawns + +
+
+ + + +
+
+
+
+ + + +AI_A2A_DISPATCHER.DefenderSquadrons + +
+
+ + + + +

The Defender Squadrons.

+ +
+
+
+
+ + + +AI_A2A_DISPATCHER.DefenderTasks + +
+
+ + + + +

The Defenders Tasks.

+ +
+
+
+
+ + +AI_A2A_DISPATCHER.Defenders + +
+
+ + + +
+
+
+
+ + Functional.Detection#DETECTION_AREAS + +AI_A2A_DISPATCHER.Detection + +
+
+ + + +
+
+
+
+ + +AI_A2A_DISPATCHER.DisengageRadius + +
+
+ + + +
+
+
+
+ + +AI_A2A_DISPATCHER:ENGAGE() + +
+
+ +

ENGAGE Trigger for AIA2ADISPATCHER

+ +
+
+
+
+ + +AI_A2A_DISPATCHER:EvaluateENGAGE(DetectedItem) + +
+
+ +

Creates an ENGAGE task when there are human friendlies airborne near the targets.

+ +

Parameter

+ +

Return values

+
    +
  1. + +

    Set#SET_UNIT: +TargetSetUnit: The target set of units.

    + +
  2. +
  3. + +

    #nil: +If there are no targets to be set.

    + +
  4. +
+
+
+
+
+ + +AI_A2A_DISPATCHER:EvaluateGCI(DetectedItem) + +
+
+ +

Creates an GCI task when there are targets for it.

+ +

Parameter

+ +

Return values

+
    +
  1. + +

    Set#SET_UNIT: +TargetSetUnit: The target set of units.

    + +
  2. +
  3. + +

    #nil: +If there are no targets to be set.

    + +
  4. +
+
+
+
+
+ + +AI_A2A_DISPATCHER:EvaluateSWEEP(DetectedItem) + +
+
+ +

Creates an SWEEP task when there are targets for it.

+ +

Parameter

+ +

Return values

+
    +
  1. + +

    Set#SET_UNIT: +TargetSetUnit: The target set of units.

    + +
  2. +
  3. + +

    #nil: +If there are no targets to be set.

    + +
  4. +
+
+
+
+
+ + +AI_A2A_DISPATCHER:GCI() + +
+
+ +

GCI Trigger for AIA2ADISPATCHER

+ +
+
+
+
+ + +AI_A2A_DISPATCHER.GciRadius + +
+
+ + + +
+
+
+
+ + +AI_A2A_DISPATCHER:GetAIFriendliesNearBy(DetectedItem) + +
+
+ +

Calculates which AI friendlies are nearby the area

+ +

Parameter

+
    +
  • + +

    DetectedItem :

    + +
  • +
+

Return value

+ +

#number, Core.CommandCenter#REPORT:

+ + +
+
+
+
+ + +AI_A2A_DISPATCHER:GetCAPDelay(SquadronName) + +
+
+ + + +

Parameter

+
    +
  • + +

    #string SquadronName : +The squadron name.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +
+
+
+
+ + +AI_A2A_DISPATCHER:GetDefaultLanding() + +
+
+ +

Gets the default method at which flights will land and despawn as part of the defense system.

+ +

Return value

+ +

#number: +Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown

+ +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights by default despawn near the airbase when returning.
+  local LandingMethod = A2ADispatcher:GetDefaultLanding( AI_A2A_Dispatcher.Landing.NearAirbase )
+  if LandingMethod == AI_A2A_Dispatcher.Landing.NearAirbase then
+   ...
+  end
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:GetDefaultTakeoff() + +
+
+ +

Gets the default method at which new flights will spawn and take-off as part of the defense system.

+ +

Return value

+ +

#number: +Takeoff From the airbase hot, from the airbase cold, in the air, from the runway.

+ +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights by default take-off in the air.
+  local TakeoffMethod = A2ADispatcher:GetDefaultTakeoff()
+  if TakeOffMethod == , AI_A2A_Dispatcher.Takeoff.InAir then
+    ...
+  end
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:GetDefenderTask(Defender) + +
+
+ + + +

Parameter

+
    +
  • + +

    Defender :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:GetDefenderTaskFsm(Defender) + +
+
+ + + +

Parameter

+
    +
  • + +

    Defender :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:GetDefenderTaskTarget(Defender) + +
+
+ + + +

Parameter

+
    +
  • + +

    Defender :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:GetDefenderTasks() + +
+
+ + + +
+
+
+
+ + +AI_A2A_DISPATCHER:GetFriendliesNearBy(DetectedItem, Target) + +
+
+ +

Calculates which friendlies are nearby the area

+ +

Parameters

+
    +
  • + +

    DetectedItem :

    + +
  • +
  • + +

    Target :

    + +
  • +
+

Return value

+ +

#number, Core.CommandCenter#REPORT:

+ + +
+
+
+
+ + +AI_A2A_DISPATCHER:GetPlayerFriendliesNearBy(DetectedItem) + +
+
+ +

Calculates which HUMAN friendlies are nearby the area

+ +

Parameter

+
    +
  • + +

    DetectedItem :

    + +
  • +
+

Return value

+ +

#number, Core.CommandCenter#REPORT:

+ + +
+
+
+
+ + +AI_A2A_DISPATCHER:GetSquadron(SquadronName) + +
+
+ +

Get an item from the Squadron table.

+ +

Parameter

+
    +
  • + +

    SquadronName :

    + +
  • +
+

Return value

+ +

#table:

+ + +
+
+
+
+ + +AI_A2A_DISPATCHER:GetSquadronFromDefender(Defender) + +
+
+ + + +

Parameter

+
    +
  • + +

    Defender :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:GetSquadronLanding(SquadronName) + +
+
+ +

Gets the method at which flights will land and despawn as part of the defense system.

+ +

Parameter

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
+

Return value

+ +

#number: +Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown

+ +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights despawn near the airbase when returning.
+  local LandingMethod = A2ADispatcher:GetSquadronLanding( "SquadronName", AI_A2A_Dispatcher.Landing.NearAirbase )
+  if LandingMethod == AI_A2A_Dispatcher.Landing.NearAirbase then
+   ...
+  end
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:GetSquadronTakeoff(SquadronName) + +
+
+ +

Gets the method at which new flights will spawn and take-off as part of the defense system.

+ +

Parameter

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
+

Return value

+ +

#number: +Takeoff From the airbase hot, from the airbase cold, in the air, from the runway.

+ +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights take-off in the air.
+  local TakeoffMethod = A2ADispatcher:GetSquadronTakeoff( "SquadronName" )
+  if TakeOffMethod == , AI_A2A_Dispatcher.Takeoff.InAir then
+    ...
+  end
+  
+ +
+
+
+
+ + + +AI_A2A_DISPATCHER.Landing + +
+
+ +

Defnes Landing location.

+ +
+
+
+
+ + +AI_A2A_DISPATCHER:New(Detection) + +
+
+ +

AIA2ADISPATCHER constructor.

+ + +

This is defining the A2A DISPATCHER for one coaliton. +The Dispatcher works with a Functional#Detection object that is taking of the detection of targets using the EWR units. +The Detection object is polymorphic, depending on the type of detection object choosen, the detection will work differently.

+ +

Parameter

+ +

Return value

+ +

#AIA2ADISPATCHER: +self

+ +

Usage:

+
  
+  -- Setup the Detection, using DETECTION_AREAS.
+  -- First define the SET of GROUPs that are defining the EWR network.
+  -- Here with prefixes DF CCCP AWACS, DF CCCP EWR.
+  DetectionSetGroup = SET_GROUP:New()
+  DetectionSetGroup:FilterPrefixes( { "DF CCCP AWACS", "DF CCCP EWR" } )
+  DetectionSetGroup:FilterStart()
+  
+  -- Define the DETECTION_AREAS, using the DetectionSetGroup, with a 30km grouping radius.
+  Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 )
+
+  -- Now Setup the A2A dispatcher, and initialize it using the Detection object.
+  A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )  --   
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:OnAfterAssign(From, Event, To, Task, TaskUnit, PlayerName) + +
+
+ +

OnAfter Transition Handler for Event Assign.

+ +

Parameters

+
    +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
  • + +

    Tasking.TaskA2A#AIA2A Task :

    + +
  • +
  • + +

    Wrapper.Unit#UNIT TaskUnit :

    + +
  • +
  • + +

    #string PlayerName :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:OnAfterCAP(From, Event, To) + +
+
+ +

CAP Handler OnAfter for AIA2ADISPATCHER

+ +

Parameters

+
    +
  • + +

    #string From :

    + +
  • +
  • + +

    #string Event :

    + +
  • +
  • + +

    #string To :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:OnAfterENGAGE(From, Event, To) + +
+
+ +

ENGAGE Handler OnAfter for AIA2ADISPATCHER

+ +

Parameters

+
    +
  • + +

    #string From :

    + +
  • +
  • + +

    #string Event :

    + +
  • +
  • + +

    #string To :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:OnAfterGCI(From, Event, To) + +
+
+ +

GCI Handler OnAfter for AIA2ADISPATCHER

+ +

Parameters

+
    +
  • + +

    #string From :

    + +
  • +
  • + +

    #string Event :

    + +
  • +
  • + +

    #string To :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:OnBeforeCAP(From, Event, To) + +
+
+ +

CAP Handler OnBefore for AIA2ADISPATCHER

+ +

Parameters

+
    +
  • + +

    #string From :

    + +
  • +
  • + +

    #string Event :

    + +
  • +
  • + +

    #string To :

    + +
  • +
+

Return value

+ +

#boolean:

+ + +
+
+
+
+ + +AI_A2A_DISPATCHER:OnBeforeENGAGE(From, Event, To) + +
+
+ +

ENGAGE Handler OnBefore for AIA2ADISPATCHER

+ +

Parameters

+
    +
  • + +

    #string From :

    + +
  • +
  • + +

    #string Event :

    + +
  • +
  • + +

    #string To :

    + +
  • +
+

Return value

+ +

#boolean:

+ + +
+
+
+
+ + +AI_A2A_DISPATCHER:OnBeforeGCI(From, Event, To) + +
+
+ +

GCI Handler OnBefore for AIA2ADISPATCHER

+ +

Parameters

+
    +
  • + +

    #string From :

    + +
  • +
  • + +

    #string Event :

    + +
  • +
  • + +

    #string To :

    + +
  • +
+

Return value

+ +

#boolean:

+ + +
+
+
+
+ + +AI_A2A_DISPATCHER:OnEventCrashOrDead(EventData) + +
+
+ + + +

Parameter

+ +
+
+
+
+ + +AI_A2A_DISPATCHER:OnEventEngineShutdown(EventData) + +
+
+ + + +

Parameter

+ +
+
+
+
+ + +AI_A2A_DISPATCHER:OnEventLand(EventData) + +
+
+ + + +

Parameter

+ +
+
+
+
+ + +AI_A2A_DISPATCHER:ProcessDetected(Detection) + +
+
+ +

Assigns A2A AI Tasks in relation to the detected items.

+ +

Parameter

+ +

Return value

+ +

#boolean: +Return true if you want the task assigning to continue... false will cancel the loop.

+ +
+
+
+
+ + +AI_A2A_DISPATCHER:RemoveDefenderFromSquadron(Squadron, Defender) + +
+
+ + + +

Parameters

+
    +
  • + +

    Squadron :

    + +
  • +
  • + +

    Defender :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER.SchedulerCAP(AI_A2A_DISPATCHER, SquadronName, self) + +
+
+ + + +

Parameters

+
    +
  • + +

    AIA2ADISPATCHER :

    + +
  • +
  • + +

    #string SquadronName : +The squadron name.

    + +
  • +
  • + +

    self :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:SetBorderZone(BorderZone) + +
+
+ +

Define a border area to simulate a cold war scenario.

+ + +

A cold war is one where CAP aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border. +A hot war is one where CAP aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send CAP and GCI aircraft to attack it. +If it’s a cold war then the borders of red and blue territory need to be defined using a zone object derived from Zone#ZONE_BASE. This method needs to be used for this. +If a hot war is chosen then no borders actually need to be defined using the helicopter units other than it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. Set the noborders parameter to 1

+ +

Parameter

+
    +
  • + +

    Core.Zone#ZONE_BASE BorderZone : +An object derived from ZONEBASE, or a list of objects derived from ZONEBASE.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+  -- Now Setup the A2A dispatcher, and initialize it using the Detection object.
+  A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )  
+  
+  -- Set one ZONE_POLYGON object as the border for the A2A dispatcher.
+  local BorderZone = ZONE_POLYGON( "CCCP Border", GROUP:FindByName( "CCCP Border" ) ) -- The GROUP object is a late activate helicopter unit.
+  A2ADispatcher:SetBorderZone( BorderZone )
+  
+or
+  
+  -- Set two ZONE_POLYGON objects as the border for the A2A dispatcher.
+  local BorderZone1 = ZONE_POLYGON( "CCCP Border1", GROUP:FindByName( "CCCP Border1" ) ) -- The GROUP object is a late activate helicopter unit.
+  local BorderZone2 = ZONE_POLYGON( "CCCP Border2", GROUP:FindByName( "CCCP Border2" ) ) -- The GROUP object is a late activate helicopter unit.
+  A2ADispatcher:SetBorderZone( { BorderZone1, BorderZone2 } )
+  
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultCapLimit(CapLimit) + +
+
+ +

Set the default CAP limit for squadrons, which will be used to determine how many CAP can be airborne at the same time for the squadron.

+ + +

The default CAP limit is 1 CAP, which means one CAP group being spawned.

+ +

Parameter

+
    +
  • + +

    #number CapLimit : +The maximum amount of CAP that can be airborne at the same time for the squadron.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+  -- Now Setup the A2A dispatcher, and initialize it using the Detection object.
+  A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )  
+  
+  -- Now Setup the default CAP limit.
+  A2ADispatcher:SetDefaultCapLimit( 2 ) -- Maximum 2 CAP per squadron.
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultCapTimeInterval(CapMinSeconds, CapMaxSeconds) + +
+
+ +

Set the default CAP time interval for squadrons, which will be used to determine a random CAP timing.

+ + +

The default CAP time interval is between 180 and 600 seconds.

+ +

Parameters

+
    +
  • + +

    #number CapMinSeconds : +The minimum amount of seconds for the random time interval.

    + +
  • +
  • + +

    #number CapMaxSeconds : +The maximum amount of seconds for the random time interval.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+  -- Now Setup the A2A dispatcher, and initialize it using the Detection object.
+  A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )  
+  
+  -- Now Setup the default CAP time interval.
+  A2ADispatcher:SetDefaultCapTimeInterval( 300, 1200 ) -- Between 300 and 1200 seconds.
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultDamageThreshold(DamageThreshold) + +
+
+ +

Set the default damage treshold when defenders will RTB.

+ + +

The default damage treshold is by default set to 40%, which means that when the airplane is 40% damaged, it will go RTB.

+ +

Parameter

+
    +
  • + +

    #number DamageThreshold : +A decimal number between 0 and 1, that expresses the %-tage of the damage treshold before going RTB.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+  -- Now Setup the A2A dispatcher, and initialize it using the Detection object.
+  A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )  
+  
+  -- Now Setup the default damage treshold.
+  A2ADispatcher:SetDefaultDamageThreshold( 0.90 ) -- Go RTB when the airplane 90% damaged.
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultFuelThreshold(FuelThreshold) + +
+
+ +

Set the default fuel treshold when defenders will RTB or Refuel in the air.

+ + +

The fuel treshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed.

+ +

Parameter

+
    +
  • + +

    #number FuelThreshold : +A decimal number between 0 and 1, that expresses the %-tage of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+  -- Now Setup the A2A dispatcher, and initialize it using the Detection object.
+  A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )  
+  
+  -- Now Setup the default fuel treshold.
+  A2ADispatcher:SetDefaultRefuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultGrouping(Grouping) + +
+
+ +

Sets the default grouping of new airplanes spawned.

+ + +

Grouping will trigger how new airplanes will be grouped if more than one airplane is spawned for defense.

+ +

Parameter

+
    +
  • + +

    #number Grouping : +The level of grouping that will be applied of the CAP or GCI defenders.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Set a grouping by default per 2 airplanes.
+  A2ADispatcher:SetDefaultGrouping( 2 )
+
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultLanding(Landing) + +
+
+ +

Defines the default method at which flights will land and despawn as part of the defense system.

+ +

Parameter

+
    +
  • + +

    #number Landing : +The landing method which can be NearAirbase, AtRunway, AtEngineShutdown

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights by default despawn near the airbase when returning.
+  A2ADispatcher:SetDefaultLanding( AI_A2A_Dispatcher.Landing.NearAirbase )
+  
+  -- Let new flights by default despawn after landing land at the runway.
+  A2ADispatcher:SetDefaultLanding( AI_A2A_Dispatcher.Landing.AtRunway )
+  
+  -- Let new flights by default despawn after landing and parking, and after engine shutdown.
+  A2ADispatcher:SetDefaultLanding( AI_A2A_Dispatcher.Landing.AtEngineShutdown )
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown() + +
+
+ +

Sets flights by default to land and despawn at engine shutdown, as part of the defense system.

+ +

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let flights by default land and despawn at engine shutdown.
+  A2ADispatcher:SetDefaultLandingAtEngineShutdown()
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultLandingAtRunway() + +
+
+ +

Sets flights by default to land and despawn at the runway, as part of the defense system.

+ +

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let flights by default land at the runway and despawn.
+  A2ADispatcher:SetDefaultLandingAtRunway()
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultLandingNearAirbase() + +
+
+ +

Sets flights by default to land and despawn near the airbase in the air, as part of the defense system.

+ +

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let flights by default to land near the airbase and despawn.
+  A2ADispatcher:SetDefaultLandingNearAirbase()
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultOverhead(Overhead) + +
+
+ +

Defines the default amount of extra planes that will take-off as part of the defense system.

+ +

Parameter

+
    +
  • + +

    #number Overhead : +The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units. +The default overhead is 1, so equal balance. The AIA2ADISPATCHER.SetOverhead() method can be used to tweak the defense strength, +taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance A2A missiles payload, may still be less effective than a F-15C with short missiles... +So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes. +The overhead must be given as a decimal value with 1 as the neutral value, which means that Overhead values:

    + +
      +
    • Higher than 1, will increase the defense unit amounts.
    • +
    • Lower than 1, will decrease the defense unit amounts.
    • +
    + +

    The amount of defending units is calculated by multiplying the amount of detected attacking planes as part of the detected group +multiplied by the Overhead and rounded up to the smallest integer.

    + +

    The Overhead value set for a Squadron, can be programmatically adjusted (by using this SetOverhead method), to adjust the defense overhead during mission execution.

    + +

    See example below. +

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- An overhead of 1,5 with 1 planes detected, will allocate 2 planes ( 1 * 1,5 ) = 1,5 => rounded up gives 2.
+  -- An overhead of 1,5 with 2 planes detected, will allocate 3 planes ( 2 * 1,5 ) = 3 =>  rounded up gives 3.
+  -- An overhead of 1,5 with 3 planes detected, will allocate 5 planes ( 3 * 1,5 ) = 4,5 => rounded up gives 5 planes.
+  -- An overhead of 1,5 with 4 planes detected, will allocate 6 planes ( 4 * 1,5 ) = 6  => rounded up gives 6 planes.
+  
+  A2ADispatcher:SetDefaultOverhead( 1.5 )
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultTakeoff(Takeoff) + +
+
+ +

Defines the default method at which new flights will spawn and take-off as part of the defense system.

+ +

Parameter

+
    +
  • + +

    #number Takeoff : +From the airbase hot, from the airbase cold, in the air, from the runway.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights by default take-off in the air.
+  A2ADispatcher:SetDefaultTakeoff( AI_A2A_Dispatcher.Takeoff.Air )
+  
+  -- Let new flights by default take-off from the runway.
+  A2ADispatcher:SetDefaultTakeoff( AI_A2A_Dispatcher.Takeoff.Runway )
+  
+  -- Let new flights by default take-off from the airbase hot.
+  A2ADispatcher:SetDefaultTakeoff( AI_A2A_Dispatcher.Takeoff.Hot )
+
+  -- Let new flights by default take-off from the airbase cold.
+  A2ADispatcher:SetDefaultTakeoff( AI_A2A_Dispatcher.Takeoff.Cold )
+
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultTakeoffFromParkingCold() + +
+
+ +

Sets flights to by default take-off from the airbase at a cold location, as part of the defense system.

+ +

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights take-off from a cold parking spot.
+  A2ADispatcher:SetDefaultTakeoffFromParkingCold()
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultTakeoffFromParkingHot() + +
+
+ +

Sets flights by default to take-off from the airbase at a hot location, as part of the defense system.

+ +

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights by default take-off at a hot parking spot.
+  A2ADispatcher:SetDefaultTakeoffFromParkingHot()
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultTakeoffFromRunway() + +
+
+ +

Sets flights by default to take-off from the runway, as part of the defense system.

+ +

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights by default take-off from the runway.
+  A2ADispatcher:SetDefaultTakeoffFromRunway()
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultTakeoffInAir() + +
+
+ +

Sets flights to default take-off in the air, as part of the defense system.

+ +

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights by default take-off in the air.
+  A2ADispatcher:SetDefaultTakeoffInAir()
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultTakeoffInAirAltitude(TakeoffAltitude) + +
+
+ +

Defines the default altitude where airplanes will spawn in the air and take-off as part of the defense system, when the take-off in the air method has been selected.

+ +

Parameter

+
    +
  • + +

    #number TakeoffAltitude : +The altitude in meters above the ground.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Set the default takeoff altitude when taking off in the air.
+  A2ADispatcher:SetDefaultTakeoffInAirAltitude( 2000 )  -- This makes planes start at 2000 meters above the ground.
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefaultTanker(TankerName) + +
+
+ +

Set the default tanker where defenders will Refuel in the air.

+ +

Parameter

+
    +
  • + +

    #strig TankerName : +A string defining the group name of the Tanker as defined within the Mission Editor.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+  -- Now Setup the A2A dispatcher, and initialize it using the Detection object.
+  A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )  
+  
+  -- Now Setup the default fuel treshold.
+  A2ADispatcher:SetDefaultRefuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
+  
+  -- Now Setup the default tanker.
+  A2ADispatcher:SetDefaultTanker( "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor.
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefenderTask(SquadronName, Defender, Type, Fsm, Target) + +
+
+ + + +

Parameters

+
    +
  • + +

    SquadronName :

    + +
  • +
  • + +

    Defender :

    + +
  • +
  • + +

    Type :

    + +
  • +
  • + +

    Fsm :

    + +
  • +
  • + +

    Target :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:SetDefenderTaskTarget(AIGroup, Defender, Target) + +
+
+ + + +

Parameters

+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetDisengageRadius(DisengageRadius) + +
+
+ +

Define the radius to disengage any target when the distance to the home base is larger than the specified meters.

+ +

Parameter

+
    +
  • + +

    #number DisengageRadius : +(Optional, Default = 300000) The radius to disengage a target when too far from the home base.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+  -- Set 50km as the Disengage Radius.
+  A2ADispatcher:SetDisengageRadius( 50000 )
+  
+  -- Set 100km as the Disengage Radius.
+  A2ADispatcher:SetDisngageRadius() -- 300000 is the default value.
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetEngageRadius(EngageRadius) + +
+
+ +

Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission.

+ + +

If there is a target area detected and reported, then any friendlies that are airborne near this target area, +will be commanded to (re-)engage that target when available (if no other tasks were commanded).

+ +

For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, +will be considered to receive the command to engage that target area.

+ +

You need to evaluate the value of this parameter carefully:

+ +
    +
  • If too small, more intercept missions may be triggered upon detected target areas.
  • +
  • If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far.
  • +
+ +

**Use the method AIA2ADISPATCHER.SetEngageRadius() to modify the default Engage Radius for ALL squadrons.**

+ +

Demonstration Mission: AID-019 - AI_A2A - Engage Range Test

+ + +

Parameter

+
    +
  • + +

    #number EngageRadius : +(Optional, Default = 100000) The radius to report friendlies near the target.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+  -- Set 50km as the radius to engage any target by airborne friendlies.
+  A2ADispatcher:SetEngageRadius( 50000 )
+  
+  -- Set 100km as the radius to engage any target by airborne friendlies.
+  A2ADispatcher:SetEngageRadius() -- 100000 is the default value.
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetGciRadius(GciRadius) + +
+
+ +

Define the radius to check if a target can be engaged by an ground controlled intercept.

+ + +

When targets are detected that are still really far off, you don't want the AIA2ADISPATCHER to launch intercepts just yet. +You want it to wait until a certain Gci range is reached, which is the distance of the closest airbase to target +being smaller than the Ground Controlled Intercept radius or Gci radius.

+ +

The default Gci radius is defined as 200000 or 200km. Override the default Gci radius when the era of the warfare is early, or, +when you don't want to let the AIA2ADISPATCHER react immediately when a certain border or area is not being crossed.

+ +

Use the method AIA2ADISPATCHER.SetGciRadius() to set a specific controlled ground intercept radius. +The Ground Controlled Intercept radius is defined for ALL squadrons which are operational.

+ +

Demonstration Mission: AID-013 - AI_A2A - Intercept Test

+ + +

Parameter

+
    +
  • + +

    #number GciRadius : +(Optional, Default = 200000) The radius to ground control intercept detected targets from the nearest airbase.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+  -- Now Setup the A2A dispatcher, and initialize it using the Detection object.
+  A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) 
+  
+  -- Set 100km as the radius to ground control intercept detected targets from the nearest airbase.
+  A2ADispatcher:SetGciRadius( 100000 )
+  
+  -- Set 200km as the radius to ground control intercept.
+  A2ADispatcher:SetGciRadius() -- 200000 is the default value.
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetIntercept(InterceptDelay) + +
+
+ + + +

Parameter

+
    +
  • + +

    InterceptDelay :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadron(SquadronName, AirbaseName, TemplatePrefixes, Resources) + +
+
+ +

This is the main method to define Squadrons programmatically.

+ + +

Squadrons:

+ +
    +
  • Have a name or key that is the identifier or key of the squadron.
  • +
  • Have specific plane types defined by templates.
  • +
  • Are located at one specific airbase. Multiple squadrons can be located at one airbase through.
  • +
  • Optionally have a limited set of resources. The default is that squadrons have unlimited resources.
  • +
+ +

The name of the squadron given acts as the squadron key in the AI_A2A_DISPATCHER:Squadron...() methods.

+ +

Additionally, squadrons have specific configuration options to:

+ +
    +
  • Control how new aircraft are taking off from the airfield (in the air, cold, hot, at the runway).
  • +
  • Control how returning aircraft are landing at the airfield (in the air near the airbase, after landing, after engine shutdown).
  • +
  • Control the grouping of new aircraft spawned at the airfield. If there is more than one aircraft to be spawned, these may be grouped.
  • +
  • Control the overhead or defensive strength of the squadron. Depending on the types of planes and amount of resources, the mission designer can choose to increase or reduce the amount of planes spawned.
  • +
+ +

For performance and bug workaround reasons within DCS, squadrons have different methods to spawn new aircraft or land returning or damaged aircraft.

+ + +

Parameters

+
    +
  • + +

    #string SquadronName : +A string (text) that defines the squadron identifier or the key of the Squadron. +It can be any name, for example "104th Squadron" or "SQ SQUADRON1", whatever. +As long as you remember that this name becomes the identifier of your squadron you have defined. +You need to use this name in other methods too!

    + + +
  • +
  • + +

    #string AirbaseName : +The airbase name where you want to have the squadron located. +You need to specify here EXACTLY the name of the airbase as you see it in the mission editor. +Examples are "Batumi" or "Tbilisi-Lochini". +EXACTLY the airbase name, between quotes "". +To ease the airbase naming when using the LDT editor and IntelliSense, the Airbase#AIRBASE class contains enumerations of the airbases of each map.

    + + + + +
  • +
  • + +

    #string TemplatePrefixes : +A string or an array of strings specifying the prefix names of the templates (not going to explain what is templates here again). +Examples are { "104th", "105th" } or "104th" or "Template 1" or "BLUE PLANES". +Just remember that your template (groups late activated) need to start with the prefix you have specified in your code. +If you have only one prefix name for a squadron, you don't need to use the { }, otherwise you need to use the brackets.

    + + +
  • +
  • + +

    #number Resources : +(optional) A number that specifies how many resources are in stock of the squadron. If not specified, the squadron will have infinite resources available.

    + + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usages:

+
    +
  •   -- Now Setup the A2A dispatcher, and initialize it using the Detection object.
    +  A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )  
    +  
  • +
  •   -- This will create squadron "Squadron1" at "Batumi" airbase, and will use plane types "SQ1" and has 40 planes in stock...  
    +  A2ADispatcher:SetSquadron( "Squadron1", "Batumi", "SQ1", 40 )
    +  
  • +
  •   -- This will create squadron "Sq 1" at "Batumi" airbase, and will use plane types "Mig-29" and "Su-27" and has 20 planes in stock...
    +  -- Note that in this implementation, the A2A dispatcher will select a random plane type when a new plane (group) needs to be spawned for defenses.
    +  -- Note the usage of the {} for the airplane templates list.
    +  A2ADispatcher:SetSquadron( "Sq 1", "Batumi", { "Mig-29", "Su-27" }, 40 )
    +  
  • +
  •   -- This will create 2 squadrons "104th" and "23th" at "Batumi" airbase, and will use plane types "Mig-29" and "Su-27" respectively and each squadron has 10 planes in stock...
    +  A2ADispatcher:SetSquadron( "104th", "Batumi", "Mig-29", 10 )
    +  A2ADispatcher:SetSquadron( "23th", "Batumi", "Su-27", 10 )
    +  
  • +
  •   -- This is an example like the previous, but now with infinite resources.
    +  -- The Resources parameter is not given in the SetSquadron method.
    +  A2ADispatcher:SetSquadron( "104th", "Batumi", "Mig-29" )
    +  A2ADispatcher:SetSquadron( "23th", "Batumi", "Su-27" )
    +  
    +  
  • +
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronCap(SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType) + +
+
+ +

Set a CAP for a Squadron.

+ +

Parameters

+
    +
  • + +

    #string SquadronName : +The squadron name.

    + +
  • +
  • + +

    Core.Zone#ZONE_BASE Zone : +The Zone object derived from Zone#ZONE_BASE that defines the zone wherein the CAP will be executed.

    + +
  • +
  • + +

    #number FloorAltitude : +The minimum altitude at which the cap can be executed.

    + +
  • +
  • + +

    #number CeilingAltitude : +the maximum altitude at which the cap can be executed.

    + +
  • +
  • + +

    #number PatrolMinSpeed : +The minimum speed at which the cap can be executed.

    + +
  • +
  • + +

    #number PatrolMaxSpeed : +The maximum speed at which the cap can be executed.

    + +
  • +
  • + +

    #number EngageMinSpeed : +The minimum speed at which the engage can be executed.

    + +
  • +
  • + +

    #number EngageMaxSpeed : +The maximum speed at which the engage can be executed.

    + +
  • +
  • + +

    #number AltType : +The altitude type, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+       -- CAP Squadron execution.
+       CAPZoneEast = ZONE_POLYGON:New( "CAP Zone East", GROUP:FindByName( "CAP Zone East" ) )
+       A2ADispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 )
+       A2ADispatcher:SetSquadronCapInterval( "Mineralnye", 2, 30, 60, 1 )
+       
+       CAPZoneWest = ZONE_POLYGON:New( "CAP Zone West", GROUP:FindByName( "CAP Zone West" ) )
+       A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" )
+       A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
+       
+       CAPZoneMiddle = ZONE:New( "CAP Zone Middle")
+       A2ADispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" )
+       A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronCapInterval(SquadronName, CapLimit, LowInterval, HighInterval, Probability) + +
+
+ +

Set the squadron CAP parameters.

+ + +

+ +

Parameters

+
    +
  • + +

    #string SquadronName : +The squadron name.

    + +
  • +
  • + +

    #number CapLimit : +(optional) The maximum amount of CAP groups to be spawned. Note that a CAP is a group, so can consist out of 1 to 4 airplanes. The default is 1 CAP group.

    + +
  • +
  • + +

    #number LowInterval : +(optional) The minimum time boundary in seconds when a new CAP will be spawned. The default is 180 seconds.

    + +
  • +
  • + +

    #number HighInterval : +(optional) The maximum time boundary in seconds when a new CAP will be spawned. The default is 600 seconds.

    + +
  • +
  • + +

    #number Probability : +Is not in use, you can skip this parameter.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+       -- CAP Squadron execution.
+       CAPZoneEast = ZONE_POLYGON:New( "CAP Zone East", GROUP:FindByName( "CAP Zone East" ) )
+       A2ADispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 )
+       A2ADispatcher:SetSquadronCapInterval( "Mineralnye", 2, 30, 60, 1 )
+       
+       CAPZoneWest = ZONE_POLYGON:New( "CAP Zone West", GROUP:FindByName( "CAP Zone West" ) )
+       A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" )
+       A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
+       
+       CAPZoneMiddle = ZONE:New( "CAP Zone Middle")
+       A2ADispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" )
+       A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronFuelThreshold(SquadronName, FuelThreshold) + +
+
+ +

Set the fuel treshold for the squadron when defenders will RTB or Refuel in the air.

+ + +

The fuel treshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed.

+ +

Parameters

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
  • + +

    #number FuelThreshold : +A decimal number between 0 and 1, that expresses the %-tage of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+  -- Now Setup the A2A dispatcher, and initialize it using the Detection object.
+  A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )  
+  
+  -- Now Setup the default fuel treshold.
+  A2ADispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronGci(SquadronName, EngageMinSpeed, EngageMaxSpeed) + +
+
+ + + +

Parameters

+
    +
  • + +

    #string SquadronName : +The squadron name.

    + +
  • +
  • + +

    #number EngageMinSpeed : +The minimum speed at which the gci can be executed.

    + +
  • +
  • + +

    #number EngageMaxSpeed : +The maximum speed at which the gci can be executed.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+       -- GCI Squadron execution.
+       A2ADispatcher:SetSquadronGci( "Mozdok", 900, 1200 )
+       A2ADispatcher:SetSquadronGci( "Novo", 900, 2100 )
+       A2ADispatcher:SetSquadronGci( "Maykop", 900, 1200 )
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronGrouping(SquadronName, Grouping) + +
+
+ +

Sets the grouping of new airplanes spawned.

+ + +

Grouping will trigger how new airplanes will be grouped if more than one airplane is spawned for defense.

+ +

Parameters

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
  • + +

    #number Grouping : +The level of grouping that will be applied of the CAP or GCI defenders.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Set a grouping per 2 airplanes.
+  A2ADispatcher:SetSquadronGrouping( "SquadronName", 2 )
+
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronLanding(SquadronName, Landing) + +
+
+ +

Defines the method at which flights will land and despawn as part of the defense system.

+ +

Parameters

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
  • + +

    #number Landing : +The landing method which can be NearAirbase, AtRunway, AtEngineShutdown

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights despawn near the airbase when returning.
+  A2ADispatcher:SetSquadronLanding( "SquadronName", AI_A2A_Dispatcher.Landing.NearAirbase )
+  
+  -- Let new flights despawn after landing land at the runway.
+  A2ADispatcher:SetSquadronLanding( "SquadronName", AI_A2A_Dispatcher.Landing.AtRunway )
+  
+  -- Let new flights despawn after landing and parking, and after engine shutdown.
+  A2ADispatcher:SetSquadronLanding( "SquadronName", AI_A2A_Dispatcher.Landing.AtEngineShutdown )
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronLandingAtEngineShutdown(SquadronName) + +
+
+ +

Sets flights to land and despawn at engine shutdown, as part of the defense system.

+ +

Parameter

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let flights land and despawn at engine shutdown.
+  A2ADispatcher:SetSquadronLandingAtEngineShutdown( "SquadronName" )
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronLandingAtRunway(SquadronName) + +
+
+ +

Sets flights to land and despawn at the runway, as part of the defense system.

+ +

Parameter

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let flights land at the runway and despawn.
+  A2ADispatcher:SetSquadronLandingAtRunway( "SquadronName" )
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronLandingNearAirbase(SquadronName) + +
+
+ +

Sets flights to land and despawn near the airbase in the air, as part of the defense system.

+ +

Parameter

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let flights to land near the airbase and despawn.
+  A2ADispatcher:SetSquadronLandingNearAirbase( "SquadronName" )
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronOverhead(SquadronName, Overhead) + +
+
+ +

Defines the amount of extra planes that will take-off as part of the defense system.

+ +

Parameters

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
  • + +

    #number Overhead : +The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units. +The default overhead is 1, so equal balance. The AIA2ADISPATCHER.SetOverhead() method can be used to tweak the defense strength, +taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance A2A missiles payload, may still be less effective than a F-15C with short missiles... +So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes. +The overhead must be given as a decimal value with 1 as the neutral value, which means that Overhead values:

    + +
      +
    • Higher than 1, will increase the defense unit amounts.
    • +
    • Lower than 1, will decrease the defense unit amounts.
    • +
    + +

    The amount of defending units is calculated by multiplying the amount of detected attacking planes as part of the detected group +multiplied by the Overhead and rounded up to the smallest integer.

    + +

    The Overhead value set for a Squadron, can be programmatically adjusted (by using this SetOverhead method), to adjust the defense overhead during mission execution.

    + +

    See example below. +

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- An overhead of 1,5 with 1 planes detected, will allocate 2 planes ( 1 * 1,5 ) = 1,5 => rounded up gives 2.
+  -- An overhead of 1,5 with 2 planes detected, will allocate 3 planes ( 2 * 1,5 ) = 3 =>  rounded up gives 3.
+  -- An overhead of 1,5 with 3 planes detected, will allocate 5 planes ( 3 * 1,5 ) = 4,5 => rounded up gives 5 planes.
+  -- An overhead of 1,5 with 4 planes detected, will allocate 6 planes ( 4 * 1,5 ) = 6  => rounded up gives 6 planes.
+  
+  A2ADispatcher:SetSquadronOverhead( "SquadronName", 1.5 )
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronTakeoff(SquadronName, Takeoff) + +
+
+ +

Defines the method at which new flights will spawn and take-off as part of the defense system.

+ +

Parameters

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
  • + +

    #number Takeoff : +From the airbase hot, from the airbase cold, in the air, from the runway.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights take-off in the air.
+  A2ADispatcher:SetSquadronTakeoff( "SquadronName", AI_A2A_Dispatcher.Takeoff.Air )
+  
+  -- Let new flights take-off from the runway.
+  A2ADispatcher:SetSquadronTakeoff( "SquadronName", AI_A2A_Dispatcher.Takeoff.Runway )
+  
+  -- Let new flights take-off from the airbase hot.
+  A2ADispatcher:SetSquadronTakeoff( "SquadronName", AI_A2A_Dispatcher.Takeoff.Hot )
+
+  -- Let new flights take-off from the airbase cold.
+  A2ADispatcher:SetSquadronTakeoff( "SquadronName", AI_A2A_Dispatcher.Takeoff.Cold )
+
+
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronTakeoffFromParkingCold(SquadronName) + +
+
+ +

Sets flights to take-off from the airbase at a cold location, as part of the defense system.

+ +

Parameter

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights take-off from a cold parking spot.
+  A2ADispatcher:SetSquadronTakeoffFromParkingCold( "SquadronName" )
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronTakeoffFromParkingHot(SquadronName) + +
+
+ +

Sets flights to take-off from the airbase at a hot location, as part of the defense system.

+ +

Parameter

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights take-off in the air.
+  A2ADispatcher:SetSquadronTakeoffFromParkingHot( "SquadronName" )
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronTakeoffFromRunway(SquadronName) + +
+
+ +

Sets flights to take-off from the runway, as part of the defense system.

+ +

Parameter

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights take-off from the runway.
+  A2ADispatcher:SetSquadronTakeoffFromRunway( "SquadronName" )
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronTakeoffInAir(SquadronName, TakeoffAltitude) + +
+
+ +

Sets flights to take-off in the air, as part of the defense system.

+ +

Parameters

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
  • + +

    #number TakeoffAltitude : +(optional) The altitude in meters above the ground. If not given, the default takeoff altitude will be used.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Let new flights take-off in the air.
+  A2ADispatcher:SetSquadronTakeoffInAir( "SquadronName" )
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronTakeoffInAirAltitude(SquadronName, TakeoffAltitude) + +
+
+ +

Defines the default altitude where airplanes will spawn in the air and take-off as part of the defense system, when the take-off in the air method has been selected.

+ +

Parameters

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
  • + +

    #number TakeoffAltitude : +The altitude in meters above the ground.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+
+  local A2ADispatcher = AI_A2A_DISPATCHER:New( ... )
+  
+  -- Set the default takeoff altitude when taking off in the air.
+  A2ADispatcher:SetSquadronTakeoffInAirAltitude( "SquadronName", 2000 ) -- This makes planes start at 2000 meters above the ground.
+  
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetSquadronTanker(SquadronName, TankerName) + +
+
+ +

Set the squadron tanker where defenders will Refuel in the air.

+ +

Parameters

+
    +
  • + +

    #string SquadronName : +The name of the squadron.

    + +
  • +
  • + +

    #strig TankerName : +A string defining the group name of the Tanker as defined within the Mission Editor.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+  -- Now Setup the A2A dispatcher, and initialize it using the Detection object.
+  A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )  
+  
+  -- Now Setup the squadron fuel treshold.
+  A2ADispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
+  
+  -- Now Setup the squadron tanker.
+  A2ADispatcher:SetSquadronTanker( "SquadronName", "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor.
+ +
+
+
+
+ + +AI_A2A_DISPATCHER:SetTacticalDisplay(TacticalDisplay) + +
+
+ +

Display a tactical report every 30 seconds about which aircraft are: + * Patrolling + * Engaging + * Returning + * Damaged + * Out of Fuel + * ...

+ +

Parameter

+
    +
  • + +

    #boolean TacticalDisplay : +Provide a value of true to display every 30 seconds a tactical overview.

    + +
  • +
+

Return value

+ +

#AIA2ADISPATCHER:

+ + +

Usage:

+

+  -- Now Setup the A2A dispatcher, and initialize it using the Detection object.
+  A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )  
+  
+  -- Now Setup the Tactical Display for debug mode.
+  A2ADispatcher:SetTacticalDisplay( true )
+  
+ +
+
+
+
+ + + +AI_A2A_DISPATCHER.TacticalDisplay + +
+
+ + + +
+
+
+
+ + #AI_A2A_DISPATCHER.Takeoff + +AI_A2A_DISPATCHER.Takeoff + +
+
+ + + +
+
+
+
+ + +AI_A2A_DISPATCHER:__CAP(Delay) + +
+
+ +

CAP Asynchronous Trigger for AIA2ADISPATCHER

+ +

Parameter

+
    +
  • + +

    #number Delay :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:__ENGAGE(Delay) + +
+
+ +

ENGAGE Asynchronous Trigger for AIA2ADISPATCHER

+ +

Parameter

+
    +
  • + +

    #number Delay :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:__GCI(Delay) + +
+
+ +

GCI Asynchronous Trigger for AIA2ADISPATCHER

+ +

Parameter

+
    +
  • + +

    #number Delay :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:onafterCAP(From, Event, To, SquadronName) + +
+
+ + + +

Parameters

+
    +
  • + +

    From :

    + +
  • +
  • + +

    Event :

    + +
  • +
  • + +

    To :

    + +
  • +
  • + +

    SquadronName :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:onafterENGAGE(From, Event, To, Target, Defenders) + +
+
+ + + +

Parameters

+
    +
  • + +

    From :

    + +
  • +
  • + +

    Event :

    + +
  • +
  • + +

    To :

    + +
  • +
  • + +

    Target :

    + +
  • +
  • + +

    Defenders :

    + +
  • +
+
+
+
+
+ + +AI_A2A_DISPATCHER:onafterGCI(From, Event, To, DetectedItem, DefendersMissing, Friendlies) + +
+
+ + + +

Parameters

+
    +
  • + +

    From :

    + +
  • +
  • + +

    Event :

    + +
  • +
  • + +

    To :

    + +
  • +
  • + +

    DetectedItem :

    + +
  • +
  • + +

    DefendersMissing :

    + +
  • +
  • + +

    Friendlies :

    + +
  • +
+
+
+ +

Type AI_A2A_DISPATCHER.Takeoff

+ +

Enumerator for spawns at airbases

+ + +

Type AI_A2A_GCICAP

+

Field(s)

+
+
+ + + +AI_A2A_GCICAP.CAPTemplates + +
+
+ + + +
+
+
+
+ + +AI_A2A_GCICAP:New(EWRPrefixes, TemplatePrefixes, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, Resources) + +
+
+ +

AIA2AGCICAP constructor.

+ +

Parameters

+
    +
  • + +

    #string EWRPrefixes : +A list of prefixes that of groups that setup the Early Warning Radar network.

    + +
  • +
  • + +

    #string TemplatePrefixes : +A list of template prefixes.

    + +
  • +
  • + +

    #string CapPrefixes : +A list of CAP zone prefixes (polygon zones).

    + +
  • +
  • + +

    #number CapLimit : +A number of how many CAP maximum will be spawned.

    + +
  • +
  • + +

    #number GroupingRadius : +The radius in meters wherein detected planes are being grouped as one target area. +For airplanes, 6000 (6km) is recommended, and is also the default value of this parameter.

    + +
  • +
  • + +

    #number EngageRadius : +The radius in meters wherein detected airplanes will be engaged by airborne defenders without a task.

    + +
  • +
  • + +

    #number GciRadius : +The radius in meters wherein detected airplanes will GCI.

    + +
  • +
  • + +

    #number Resources : +The amount of resources that will be allocated to each squadron.

    + +
  • +
+

Return value

+ +

#AIA2AGCICAP:

+ + +

Usages:

+
    +
  •   
    +  -- Setup a new GCICAP dispatcher object. Each squadron has unlimited resources.
    +  -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network.
    +  -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates.
    +  -- The CAP Zone prefix is "CAP Zone".
    +  -- The CAP Limit is 2.
    +  A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2 )  
    +  
  • +
  •   
    +  -- Setup a new GCICAP dispatcher object. Each squadron has unlimited resources.
    +  -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network.
    +  -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates.
    +  -- The CAP Zone prefix is "CAP Zone".
    +  -- The CAP Limit is 2.
    +  -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets.
    +  A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000 )  
    +  
  • +
  •   
    +  -- Setup a new GCICAP dispatcher object. Each squadron has unlimited resources.
    +  -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network.
    +  -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates.
    +  -- The CAP Zone prefix is "CAP Zone".
    +  -- The CAP Limit is 2.
    +  -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets.
    +  -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, 
    +  -- will be considered a defense task if the target is within 60km from the defender.
    +  A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000, 60000 )  
    +  
  • +
  •   
    +  -- Setup a new GCICAP dispatcher object. Each squadron has unlimited resources.
    +  -- The EWR network group prefix is DF CCCP. All groups starting with DF CCCP will be part of the EWR network.
    +  -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates.
    +  -- The CAP Zone prefix is "CAP Zone".
    +  -- The CAP Limit is 2.
    +  -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets.
    +  -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, 
    +  -- will be considered a defense task if the target is within 60km from the defender.
    +  -- The GCI Radius is set to 150000. Any target detected within 150km will be considered for GCI engagement.
    +  A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000, 60000, 150000 )  
    +  
  • +
  •   
    +  -- Setup a new GCICAP dispatcher object. Each squadron has 30 resources.
    +  -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network.
    +  -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates.
    +  -- The CAP Zone prefix is "CAP Zone".
    +  -- The CAP Limit is 2.
    +  -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets.
    +  -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, 
    +  -- will be considered a defense task if the target is within 60km from the defender.
    +  -- The GCI Radius is set to 150000. Any target detected within 150km will be considered for GCI engagement.
    +  -- The amount of resources for each squadron is set to 30. Thus about 30 resources are allocated to each squadron created.
    +
    +  A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000, 60000, 150000, 30 )  
    +  
  • +
  •   
    +  -- Setup a new GCICAP dispatcher object. Each squadron has 30 resources.
    +  -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network.
    +  -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates.
    +  -- The CAP Zone prefix is nil. No CAP is created.
    +  -- The CAP Limit is nil.
    +  -- The Grouping Radius is nil. The default range of 6km radius will be grouped as a group of targets.
    +  -- The Engage Radius is set nil. The default Engage Radius will be used to consider a defenser being assigned to a task.
    +  -- The GCI Radius is nil. Any target detected within the default GCI Radius will be considered for GCI engagement.
    +  -- The amount of resources for each squadron is set to 30. Thus about 30 resources are allocated to each squadron created.
    +
    +  A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, nil, nil, nil, nil, nil, 30 )  
    +  
  • +
+ +
+
+
+
+ + +AI_A2A_GCICAP:NewWithBorder(EWRPrefixes, TemplatePrefixes, BorderPrefix, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, Resources) + +
+
+ +

AIA2AGCICAP constructor with border.

+ +

Parameters

+
    +
  • + +

    #string EWRPrefixes : +A list of prefixes that of groups that setup the Early Warning Radar network.

    + +
  • +
  • + +

    #string TemplatePrefixes : +A list of template prefixes.

    + +
  • +
  • + +

    #string BorderPrefix : +A Border Zone Prefix.

    + +
  • +
  • + +

    #string CapPrefixes : +A list of CAP zone prefixes (polygon zones).

    + +
  • +
  • + +

    #number CapLimit : +A number of how many CAP maximum will be spawned.

    + +
  • +
  • + +

    #number GroupingRadius : +The radius in meters wherein detected planes are being grouped as one target area. +For airplanes, 6000 (6km) is recommended, and is also the default value of this parameter.

    + +
  • +
  • + +

    #number EngageRadius : +The radius in meters wherein detected airplanes will be engaged by airborne defenders without a task.

    + +
  • +
  • + +

    #number GciRadius : +The radius in meters wherein detected airplanes will GCI.

    + +
  • +
  • + +

    #number Resources : +The amount of resources that will be allocated to each squadron.

    + +
  • +
+

Return value

+ +

#AIA2AGCICAP:

+ + +

Usages:

+
    +
  •   
    +  -- Setup a new GCICAP dispatcher object with a border. Each squadron has unlimited resources.
    +  -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network.
    +  -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates.
    +  -- The CAP Zone prefix is "CAP Zone".
    +  -- The CAP Limit is 2.
    +
    +  A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2 )  
    +  
  • +
  •   
    +  -- Setup a new GCICAP dispatcher object with a border. Each squadron has unlimited resources.
    +  -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network.
    +  -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates.
    +  -- The Border prefix is "Border". This will setup a border using the group defined within the mission editor with the name Border.
    +  -- The CAP Zone prefix is "CAP Zone".
    +  -- The CAP Limit is 2.
    +  -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets.
    +
    +  A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000 )  
    +  
  • +
  •   
    +  -- Setup a new GCICAP dispatcher object with a border. Each squadron has unlimited resources.
    +  -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network.
    +  -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates.
    +  -- The Border prefix is "Border". This will setup a border using the group defined within the mission editor with the name Border.
    +  -- The CAP Zone prefix is "CAP Zone".
    +  -- The CAP Limit is 2.
    +  -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets.
    +  -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, 
    +  -- will be considered a defense task if the target is within 60km from the defender.
    +
    +  A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000, 60000 )  
    +  
  • +
  •   
    +  -- Setup a new GCICAP dispatcher object with a border. Each squadron has unlimited resources.
    +  -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network.
    +  -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates.
    +  -- The Border prefix is "Border". This will setup a border using the group defined within the mission editor with the name Border.
    +  -- The CAP Zone prefix is "CAP Zone".
    +  -- The CAP Limit is 2.
    +  -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets.
    +  -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, 
    +  -- will be considered a defense task if the target is within 60km from the defender.
    +  -- The GCI Radius is set to 150000. Any target detected within 150km will be considered for GCI engagement.
    +
    +  A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000, 60000, 150000 )  
    +  
  • +
  •   
    +  -- Setup a new GCICAP dispatcher object with a border. Each squadron has 30 resources.
    +  -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network.
    +  -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates.
    +  -- The Border prefix is "Border". This will setup a border using the group defined within the mission editor with the name Border.
    +  -- The CAP Zone prefix is "CAP Zone".
    +  -- The CAP Limit is 2.
    +  -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets.
    +  -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, 
    +  -- will be considered a defense task if the target is within 60km from the defender.
    +  -- The GCI Radius is set to 150000. Any target detected within 150km will be considered for GCI engagement.
    +  -- The amount of resources for each squadron is set to 30. Thus about 30 resources are allocated to each squadron created.
    +
    +  A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000, 60000, 150000, 30 )  
    +  
  • +
  •   
    +  -- Setup a new GCICAP dispatcher object with a border. Each squadron has 30 resources.
    +  -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network.
    +  -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates.
    +  -- The Border prefix is "Border". This will setup a border using the group defined within the mission editor with the name Border.
    +  -- The CAP Zone prefix is nil. No CAP is created.
    +  -- The CAP Limit is nil.
    +  -- The Grouping Radius is nil. The default range of 6km radius will be grouped as a group of targets.
    +  -- The Engage Radius is set nil. The default Engage Radius will be used to consider a defenser being assigned to a task.
    +  -- The GCI Radius is nil. Any target detected within the default GCI Radius will be considered for GCI engagement.
    +  -- The amount of resources for each squadron is set to 30. Thus about 30 resources are allocated to each squadron created.
    +
    +  A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", nil, nil, nil, nil, nil, 30 )  
    +  
  • +
+ +
+
+
+
+ + + +AI_A2A_GCICAP.Templates + +
+
+ + + +
+
+ +

Type list

+ +

Type strig

+ +
+ + + + diff --git a/docs/Documentation/AI_A2A_GCI.html b/docs/Documentation/AI_A2A_GCI.html new file mode 100644 index 000000000..7a6e7ed7b --- /dev/null +++ b/docs/Documentation/AI_A2A_GCI.html @@ -0,0 +1,1576 @@ + + + + + + +
+
+ +
+
+
+
+ +
+

Module AI_A2A_GCI

+ +

AI -- Execute Ground Controlled Interception (GCI).

+ +

Banner Image

+ +
+ +

AI A2A_INTEREPT class makes AI Groups execute an Intercept.

+ + + +

There are the following types of GCI classes defined:

+ + + +
+ +

Author: Sven Van de Velde (FlightControl)

+ +

Contributions:

+ +
+ + +

Global(s)

+ + + + + +
AI_A2A_GCI +

AIA2AGCI class, extends AIA2A#AIA2A

+ +

The AIA2AGCI class implements the core functions to intercept intruders.

+
+

Type AI_A2A_GCI

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AI_A2A_GCI:Abort() +

Synchronous Event Trigger for Event Abort.

+
AI_A2A_GCI:Accomplish() +

Synchronous Event Trigger for Event Accomplish.

+
AI_A2A_GCI.Accomplished + +
AI_A2A_GCI.AttackSetUnit + +
AI_A2A_GCI:Destroy() +

Synchronous Event Trigger for Event Destroy.

+
AI_A2A_GCI:Engage() +

Synchronous Event Trigger for Event Engage.

+
AI_A2A_GCI.EngageMaxSpeed + +
AI_A2A_GCI.EngageMinSpeed + +
AI_A2A_GCI.Engaging + +
AI_A2A_GCI:Fired() +

Synchronous Event Trigger for Event Fired.

+
AI_A2A_GCI.InterceptRoute(AIControllable, AIGroup, Fsm) + +
AI_A2A_GCI:New(AIGroup, EngageMinSpeed, EngageMaxSpeed) +

Creates a new AIA2AGCI object

+
AI_A2A_GCI:OnAfterAbort(AIGroup, From, Event, To) +

OnAfter Transition Handler for Event Abort.

+
AI_A2A_GCI:OnAfterAccomplish(AIGroup, From, Event, To) +

OnAfter Transition Handler for Event Accomplish.

+
AI_A2A_GCI:OnAfterDestroy(AIGroup, From, Event, To) +

OnAfter Transition Handler for Event Destroy.

+
AI_A2A_GCI:OnAfterEngage(AIGroup, From, Event, To) +

OnAfter Transition Handler for Event Engage.

+
AI_A2A_GCI:OnAfterFired(AIGroup, From, Event, To) +

OnAfter Transition Handler for Event Fired.

+
AI_A2A_GCI:OnBeforeAbort(AIGroup, From, Event, To) +

OnBefore Transition Handler for Event Abort.

+
AI_A2A_GCI:OnBeforeAccomplish(AIGroup, From, Event, To) +

OnBefore Transition Handler for Event Accomplish.

+
AI_A2A_GCI:OnBeforeDestroy(AIGroup, From, Event, To) +

OnBefore Transition Handler for Event Destroy.

+
AI_A2A_GCI:OnBeforeEngage(AIGroup, From, Event, To) +

OnBefore Transition Handler for Event Engage.

+
AI_A2A_GCI:OnBeforeFired(AIGroup, From, Event, To) +

OnBefore Transition Handler for Event Fired.

+
AI_A2A_GCI:OnEnterEngaging(AIGroup, From, Event, To) +

OnEnter Transition Handler for State Engaging.

+
AI_A2A_GCI:OnEventDead(EventData) + +
AI_A2A_GCI:OnLeaveEngaging(AIGroup, From, Event, To) +

OnLeave Transition Handler for State Engaging.

+
AI_A2A_GCI.PatrolAltType + +
AI_A2A_GCI.PatrolMaxSpeed + +
AI_A2A_GCI.PatrolMinSpeed + +
AI_A2A_GCI:__Abort(Delay) +

Asynchronous Event Trigger for Event Abort.

+
AI_A2A_GCI:__Accomplish(Delay) +

Asynchronous Event Trigger for Event Accomplish.

+
AI_A2A_GCI:__Destroy(Delay) +

Asynchronous Event Trigger for Event Destroy.

+
AI_A2A_GCI:__Engage(Delay) +

Asynchronous Event Trigger for Event Engage.

+
AI_A2A_GCI:__Fired(Delay) +

Asynchronous Event Trigger for Event Fired.

+
AI_A2A_GCI:onafterAbort(AIGroup, From, Event, To) + +
AI_A2A_GCI:onafterAccomplish(AIGroup, From, Event, To) + +
AI_A2A_GCI:onafterDestroy(AIGroup, From, Event, To, EventData) + +
AI_A2A_GCI:onafterEngage(AIGroup, From, Event, To) +

onafter State Transition for Event Patrol.

+
AI_A2A_GCI:onbeforeEngage(AIGroup, From, Event, To) + +
+ +

Global(s)

+
+
+ + #AI_A2A_GCI + +AI_A2A_GCI + +
+
+ +

AIA2AGCI class, extends AIA2A#AIA2A

+ +

The AIA2AGCI class implements the core functions to intercept intruders.

+ + +

The Engage function will intercept intruders.

+ +

Process

+ +

The AIA2AGCI is assigned a Group and this must be done before the AIA2AGCI process can be started using the Start event.

+ +

Process

+ +

The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. +Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.

+ +

Process

+ +

This cycle will continue.

+ +

Process

+ +

During the patrol, the AI will detect enemy targets, which are reported through the Detected event.

+ +

Process

+ +

When enemies are detected, the AI will automatically engage the enemy.

+ +

Process

+ +

Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.

+ +

Process

+ +

1. AIA2AGCI constructor

+ + + +

2. AIA2AGCI is a FSM

+ +

Process

+ +

2.1 AIA2AGCI States

+ +
    +
  • None ( Group ): The process is not started yet.
  • +
  • Patrolling ( Group ): The AI is patrolling the Patrol Zone.
  • +
  • Engaging ( Group ): The AI is engaging the bogeys.
  • +
  • Returning ( Group ): The AI is returning to Base..
  • +
+ +

2.2 AIA2AGCI Events

+ + + +

3. Set the Range of Engagement

+ +

Range

+ +

An optional range can be set in meters, +that will define when the AI will engage with the detected airborne enemy targets. +The range can be beyond or smaller than the range of the Patrol Zone. +The range is applied at the position of the AI. +Use the method AIGCI#AIA2A_GCI.SetEngageRange() to define that range.

+ +

4. Set the Zone of Engagement

+ +

Zone

+ +

An optional Zone can be set, +that will define when the AI will engage with the detected airborne enemy targets. +Use the method AICap#AIA2A_GCI.SetEngageZone() to define that Zone.

+ +
+ + +
+
+

Type AI_A2A_GCI

+

Field(s)

+
+
+ + +AI_A2A_GCI:Abort() + +
+
+ +

Synchronous Event Trigger for Event Abort.

+ +
+
+
+
+ + +AI_A2A_GCI:Accomplish() + +
+
+ +

Synchronous Event Trigger for Event Accomplish.

+ +
+
+
+
+ + #boolean + +AI_A2A_GCI.Accomplished + +
+
+ + + +
+
+
+
+ + Core.Set#SET_UNIT + +AI_A2A_GCI.AttackSetUnit + +
+
+ + + +
+
+
+
+ + +AI_A2A_GCI:Destroy() + +
+
+ +

Synchronous Event Trigger for Event Destroy.

+ +
+
+
+
+ + +AI_A2A_GCI:Engage() + +
+
+ +

Synchronous Event Trigger for Event Engage.

+ +
+
+
+
+ + + +AI_A2A_GCI.EngageMaxSpeed + +
+
+ + + +
+
+
+
+ + + +AI_A2A_GCI.EngageMinSpeed + +
+
+ + + +
+
+
+
+ + #boolean + +AI_A2A_GCI.Engaging + +
+
+ + + +
+
+
+
+ + +AI_A2A_GCI:Fired() + +
+
+ +

Synchronous Event Trigger for Event Fired.

+ +
+
+
+
+ + +AI_A2A_GCI.InterceptRoute(AIControllable, AIGroup, Fsm) + +
+
+ + + +

Parameters

+ +
+
+
+
+ + +AI_A2A_GCI:New(AIGroup, EngageMinSpeed, EngageMaxSpeed) + +
+
+ +

Creates a new AIA2AGCI object

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup :

    + +
  • +
  • + +

    EngageMinSpeed :

    + +
  • +
  • + +

    EngageMaxSpeed :

    + +
  • +
+

Return value

+ +

#AIA2AGCI:

+ + +
+
+
+
+ + +AI_A2A_GCI:OnAfterAbort(AIGroup, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Abort.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:OnAfterAccomplish(AIGroup, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Accomplish.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:OnAfterDestroy(AIGroup, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Destroy.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:OnAfterEngage(AIGroup, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Engage.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:OnAfterFired(AIGroup, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Fired.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:OnBeforeAbort(AIGroup, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Abort.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A_GCI:OnBeforeAccomplish(AIGroup, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Accomplish.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A_GCI:OnBeforeDestroy(AIGroup, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Destroy.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A_GCI:OnBeforeEngage(AIGroup, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Engage.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A_GCI:OnBeforeFired(AIGroup, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Fired.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A_GCI:OnEnterEngaging(AIGroup, From, Event, To) + +
+
+ +

OnEnter Transition Handler for State Engaging.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:OnEventDead(EventData) + +
+
+ + + +

Parameter

+ +
+
+
+
+ + +AI_A2A_GCI:OnLeaveEngaging(AIGroup, From, Event, To) + +
+
+ +

OnLeave Transition Handler for State Engaging.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + #string + +AI_A2A_GCI.PatrolAltType + +
+
+ + + +
+
+
+
+ + + +AI_A2A_GCI.PatrolMaxSpeed + +
+
+ + + +
+
+
+
+ + + +AI_A2A_GCI.PatrolMinSpeed + +
+
+ + + +
+
+
+
+ + +AI_A2A_GCI:__Abort(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Abort.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:__Accomplish(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Accomplish.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:__Destroy(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Destroy.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:__Engage(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Engage.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:__Fired(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Fired.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:onafterAbort(AIGroup, From, Event, To) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AI Group managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:onafterAccomplish(AIGroup, From, Event, To) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:onafterDestroy(AIGroup, From, Event, To, EventData) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
  • + +

    Core.Event#EVENTDATA EventData :

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:onafterEngage(AIGroup, From, Event, To) + +
+
+ +

onafter State Transition for Event Patrol.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AI Group managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_GCI:onbeforeEngage(AIGroup, From, Event, To) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+ +
+ +
+ + diff --git a/docs/Documentation/AI_A2A_Patrol.html b/docs/Documentation/AI_A2A_Patrol.html new file mode 100644 index 000000000..3e8626ebe --- /dev/null +++ b/docs/Documentation/AI_A2A_Patrol.html @@ -0,0 +1,1127 @@ + + + + + + +
+
+ +
+
+
+
+ +
+

Module AI_A2A_Patrol

+ +

AI -- Air Patrolling or Staging.

+ +

Banner Image

+ +
+ +

AI PATROL classes makes AI Controllables execute an Patrol.

+ + + +

There are the following types of PATROL classes defined:

+ + + +
+ +

Demo Missions

+ +

AI_PATROL Demo Missions source code

+ +

AI_PATROL Demo Missions, only for beta testers

+ +

ALL Demo Missions pack of the last release

+ +
+ +

YouTube Channel

+ +

AI_PATROL YouTube Channel

+ +
+ +

Author: Sven Van de Velde (FlightControl)

+

Contributions:

+ +
    +
  • Dutch_Baron: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
  • +
  • Pikey: Testing and API concept review.
  • +
+ +
+ + +

Global(s)

+ + + + + +
AI_A2A_PATROL +

AIA2APATROL class, extends Fsm#FSM_CONTROLLABLE

+ +

The AIA2APATROL class implements the core functions to patrol a Zone by an AI Controllable or Group.

+
+

Type AI_A2A_PATROL

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AI_A2A_PATROL:New(AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType) +

Creates a new AIA2APATROL object

+
AI_A2A_PATROL:OnAfterPatrol(Controllable, From, Event, To) +

OnAfter Transition Handler for Event Patrol.

+
AI_A2A_PATROL:OnAfterRoute(Controllable, From, Event, To) +

OnAfter Transition Handler for Event Route.

+
AI_A2A_PATROL:OnBeforePatrol(Controllable, From, Event, To) +

OnBefore Transition Handler for Event Patrol.

+
AI_A2A_PATROL:OnBeforeRoute(Controllable, From, Event, To) +

OnBefore Transition Handler for Event Route.

+
AI_A2A_PATROL:OnEnterPatrolling(Controllable, From, Event, To) +

OnEnter Transition Handler for State Patrolling.

+
AI_A2A_PATROL:OnLeavePatrolling(Controllable, From, Event, To) +

OnLeave Transition Handler for State Patrolling.

+
AI_A2A_PATROL:Patrol() +

Synchronous Event Trigger for Event Patrol.

+
AI_A2A_PATROL.PatrolAltType + +
AI_A2A_PATROL.PatrolCeilingAltitude + +
AI_A2A_PATROL.PatrolFloorAltitude + +
AI_A2A_PATROL.PatrolMaxSpeed + +
AI_A2A_PATROL.PatrolMinSpeed + +
AI_A2A_PATROL.PatrolRoute(AIGroup, Fsm) + +
AI_A2A_PATROL.PatrolZone + +
AI_A2A_PATROL.Resume(AIGroup) + +
AI_A2A_PATROL:Route() +

Synchronous Event Trigger for Event Route.

+
AI_A2A_PATROL:SetAltitude(PatrolFloorAltitude, PatrolCeilingAltitude) +

Sets the floor and ceiling altitude of the patrol.

+
AI_A2A_PATROL:SetSpeed(PatrolMinSpeed, PatrolMaxSpeed) +

Sets (modifies) the minimum and maximum speed of the patrol.

+
AI_A2A_PATROL:__Patrol(Delay) +

Asynchronous Event Trigger for Event Patrol.

+
AI_A2A_PATROL:__Route(Delay) +

Asynchronous Event Trigger for Event Route.

+
AI_A2A_PATROL:onafterPatrol(Controllable, From, Event, To) +

Defines a new patrol route using the Process_PatrolZone parameters and settings.

+
AI_A2A_PATROL:onafterRoute(AIGroup, From, Event, To) +

Defines a new patrol route using the Process_PatrolZone parameters and settings.

+
+ +

Global(s)

+
+
+ + #AI_A2A_PATROL + +AI_A2A_PATROL + +
+
+ +

AIA2APATROL class, extends Fsm#FSM_CONTROLLABLE

+ +

The AIA2APATROL class implements the core functions to patrol a Zone by an AI Controllable or Group.

+ + + +

Process

+ +

The AIA2APATROL is assigned a Group and this must be done before the AIA2APATROL process can be started using the Start event.

+ +

Process

+ +

The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. +Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.

+ +

Process

+ +

This cycle will continue.

+ +

Process

+ +

During the patrol, the AI will detect enemy targets, which are reported through the Detected event.

+ +

Process

+ +

-- Note that the enemy is not engaged! To model enemy engagement, either tailor the Detected event, or +use derived AI_ classes to model AI offensive or defensive behaviour.

+ +

Process

+ +

Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. +When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.

+ +

Process

+ +

1. AIA2APATROL constructor

+ + + +

2. AIA2APATROL is a FSM

+ +

Process

+ +

2.1. AIA2APATROL States

+ +
    +
  • None ( Group ): The process is not started yet.
  • +
  • Patrolling ( Group ): The AI is patrolling the Patrol Zone.
  • +
  • Returning ( Group ): The AI is returning to Base.
  • +
  • Stopped ( Group ): The process is stopped.
  • +
  • Crashed ( Group ): The AI has crashed or is dead.
  • +
+ +

2.2. AIA2APATROL Events

+ +
    +
  • Start ( Group ): Start the process.
  • +
  • Stop ( Group ): Stop the process.
  • +
  • Route ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
  • +
  • RTB ( Group ): Route the AI to the home base.
  • +
  • Detect ( Group ): The AI is detecting targets.
  • +
  • Detected ( Group ): The AI has detected new targets.
  • +
  • Status ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
  • +
+ +

3. Set or Get the AI controllable

+ + + +

4. Set the Speed and Altitude boundaries of the AI controllable

+ + + +

5. Manage the detection process of the AI controllable

+ +

The detection process of the AI controllable can be manipulated. +Detection requires an amount of CPU power, which has an impact on your mission performance. +Only put detection on when absolutely necessary, and the frequency of the detection can also be set.

+ + + +

The detection frequency can be set with AIA2APATROL.SetRefreshTimeInterval( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. +Use the method AIA2APATROL.GetDetectedUnits() to obtain a list of the Units detected by the AI.

+ +

The detection can be filtered to potential targets in a specific zone. +Use the method AIA2APATROL.SetDetectionZone() to set the zone where targets need to be detected. +Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected +according the weather conditions.

+ +

6. Manage the "out of fuel" in the AIA2APATROL

+ +

When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. +Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. +When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, +while a new AI is targetted to the AIA2APATROL. +Once the time is finished, the old AI will return to the base. +Use the method AIA2APATROL.ManageFuel() to have this proces in place.

+ +

7. Manage "damage" behaviour of the AI in the AIA2APATROL

+ +

When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on. +Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). +Use the method AIA2APATROL.ManageDamage() to have this proces in place.

+ +
+ + +
+
+

Type AI_A2A_Patrol

+ +

Type AI_A2A

+ +

Type AI_A2A_PATROL

+

Field(s)

+
+
+ + +AI_A2A_PATROL:New(AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType) + +
+
+ +

Creates a new AIA2APATROL object

+ +

Parameters

+ +

Return value

+ +

#AIA2APATROL: +self

+ +

Usage:

+
-- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol an AIControllable within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
+PatrolZone = ZONE:New( 'PatrolZone' )
+PatrolSpawn = SPAWN:New( 'Patrol Group' )
+PatrolArea = AI_A2A_PATROL:New( PatrolZone, 3000, 6000, 600, 900 )
+ +
+
+
+
+ + +AI_A2A_PATROL:OnAfterPatrol(Controllable, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Patrol.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_PATROL:OnAfterRoute(Controllable, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Route.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_PATROL:OnBeforePatrol(Controllable, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Patrol.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A_PATROL:OnBeforeRoute(Controllable, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Route.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A_PATROL:OnEnterPatrolling(Controllable, From, Event, To) + +
+
+ +

OnEnter Transition Handler for State Patrolling.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_A2A_PATROL:OnLeavePatrolling(Controllable, From, Event, To) + +
+
+ +

OnLeave Transition Handler for State Patrolling.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_A2A_PATROL:Patrol() + +
+
+ +

Synchronous Event Trigger for Event Patrol.

+ +
+
+
+
+ + +AI_A2A_PATROL.PatrolAltType + +
+
+ + + + +

defafult PatrolAltType to "RADIO" if not specified

+ +
+
+
+
+ + + +AI_A2A_PATROL.PatrolCeilingAltitude + +
+
+ + + +
+
+
+
+ + + +AI_A2A_PATROL.PatrolFloorAltitude + +
+
+ + + +
+
+
+
+ + + +AI_A2A_PATROL.PatrolMaxSpeed + +
+
+ + + +
+
+
+
+ + + +AI_A2A_PATROL.PatrolMinSpeed + +
+
+ + + +
+
+
+
+ + +AI_A2A_PATROL.PatrolRoute(AIGroup, Fsm) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +This statis method is called from the route path within the last task at the last waaypoint of the Controllable. +Note that this method is required, as triggers the next route when patrolling for the Controllable.

    + +
  • +
  • + +

    Fsm :

    + +
  • +
+
+
+
+
+ + + +AI_A2A_PATROL.PatrolZone + +
+
+ + + +
+
+
+
+ + +AI_A2A_PATROL.Resume(AIGroup) + +
+
+ + + +

Parameter

+ +
+
+
+
+ + +AI_A2A_PATROL:Route() + +
+
+ +

Synchronous Event Trigger for Event Route.

+ +
+
+
+
+ + +AI_A2A_PATROL:SetAltitude(PatrolFloorAltitude, PatrolCeilingAltitude) + +
+
+ +

Sets the floor and ceiling altitude of the patrol.

+ +

Parameters

+
    +
  • + +

    Dcs.DCSTypes#Altitude PatrolFloorAltitude : +The lowest altitude in meters where to execute the patrol.

    + +
  • +
  • + +

    Dcs.DCSTypes#Altitude PatrolCeilingAltitude : +The highest altitude in meters where to execute the patrol.

    + +
  • +
+

Return value

+ +

#AIA2APATROL: +self

+ +
+
+
+
+ + +AI_A2A_PATROL:SetSpeed(PatrolMinSpeed, PatrolMaxSpeed) + +
+
+ +

Sets (modifies) the minimum and maximum speed of the patrol.

+ +

Parameters

+ +

Return value

+ +

#AIA2APATROL: +self

+ +
+
+
+
+ + +AI_A2A_PATROL:__Patrol(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Patrol.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_A2A_PATROL:__Route(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Route.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_A2A_PATROL:onafterPatrol(Controllable, From, Event, To) + +
+
+ +

Defines a new patrol route using the Process_PatrolZone parameters and settings.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#AIA2APATROL: +self

+ +
+
+
+
+ + +AI_A2A_PATROL:onafterRoute(AIGroup, From, Event, To) + +
+
+ +

Defines a new patrol route using the Process_PatrolZone parameters and settings.

+ +

Parameters

+
    +
  • + +

    Wrapper.Group#GROUP AIGroup : +The AIGroup managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+ +
+ +
+ + diff --git a/docs/Documentation/AI_BAI.html b/docs/Documentation/AI_BAI.html new file mode 100644 index 000000000..afa707ccf --- /dev/null +++ b/docs/Documentation/AI_BAI.html @@ -0,0 +1,1981 @@ + + + + + + +
+
+ +
+
+
+
+ +
+

Module AI_Bai

+ +

AI -- Provide Battlefield Air Interdiction (bombing).

+ +

Banner Image

+ +
+ +

AI_BAI classes makes AI Controllables execute bombing tasks.

+ + + +

There are the following types of BAI classes defined:

+ + + +
+ +

Demo Missions

+ +

AI_BAI Demo Missions source code

+ +

AI_BAI Demo Missions, only for beta testers

+ +

ALL Demo Missions pack of the last release

+ +
+ +

YouTube Channel

+ +

AI_BAI YouTube Channel

+ +
+ +

Author: Sven Van de Velde (FlightControl)

+ +

Contributions:

+ + + +
+ + +

Global(s)

+ + + + + + + + + +
AI_BAI_ZONE +

AIBAIZONE class, extends AIPatrol#AIPATROL_ZONE

+ +

AIBAIZONE derives from the AIPatrol#AIPATROL_ZONE, inheriting its methods and behaviour.

+
_NewEngageRoute(AIControllable) + +
+

Type AI_BAI_ZONE

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AI_BAI_ZONE.AIControllable +

The Controllable patrolling.

+
AI_BAI_ZONE:Abort() +

Synchronous Event Trigger for Event Abort.

+
AI_BAI_ZONE:Accomplish() +

Synchronous Event Trigger for Event Accomplish.

+
AI_BAI_ZONE.Accomplished + +
AI_BAI_ZONE:Destroy() +

Synchronous Event Trigger for Event Destroy.

+
AI_BAI_ZONE:Engage(EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection) +

Synchronous Event Trigger for Event Engage.

+
AI_BAI_ZONE.EngageAltitude + +
AI_BAI_ZONE.EngageAttackQty + +
AI_BAI_ZONE.EngageDirection + +
AI_BAI_ZONE.EngageSpeed + +
AI_BAI_ZONE.EngageWeaponExpend + +
AI_BAI_ZONE.EngageZone + +
AI_BAI_ZONE:Fired() +

Synchronous Event Trigger for Event Fired.

+
AI_BAI_ZONE:New(PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType) +

Creates a new AIBAIZONE object

+
AI_BAI_ZONE:OnAfterAbort(Controllable, From, Event, To) +

OnAfter Transition Handler for Event Abort.

+
AI_BAI_ZONE:OnAfterAccomplish(Controllable, From, Event, To) +

OnAfter Transition Handler for Event Accomplish.

+
AI_BAI_ZONE:OnAfterDestroy(Controllable, From, Event, To) +

OnAfter Transition Handler for Event Destroy.

+
AI_BAI_ZONE:OnAfterEngage(Controllable, From, Event, To) +

OnAfter Transition Handler for Event Engage.

+
AI_BAI_ZONE:OnAfterFired(Controllable, From, Event, To) +

OnAfter Transition Handler for Event Fired.

+
AI_BAI_ZONE:OnBeforeAbort(Controllable, From, Event, To) +

OnBefore Transition Handler for Event Abort.

+
AI_BAI_ZONE:OnBeforeAccomplish(Controllable, From, Event, To) +

OnBefore Transition Handler for Event Accomplish.

+
AI_BAI_ZONE:OnBeforeDestroy(Controllable, From, Event, To) +

OnBefore Transition Handler for Event Destroy.

+
AI_BAI_ZONE:OnBeforeEngage(Controllable, From, Event, To) +

OnBefore Transition Handler for Event Engage.

+
AI_BAI_ZONE:OnBeforeFired(Controllable, From, Event, To) +

OnBefore Transition Handler for Event Fired.

+
AI_BAI_ZONE:OnEnterEngaging(Controllable, From, Event, To) +

OnEnter Transition Handler for State Engaging.

+
AI_BAI_ZONE:OnEventDead(EventData) + +
AI_BAI_ZONE:OnLeaveEngaging(Controllable, From, Event, To) +

OnLeave Transition Handler for State Engaging.

+
AI_BAI_ZONE.Search + +
AI_BAI_ZONE:SearchOff() +

If Search is Off, the current zone coordinate will be the center of the bombing.

+
AI_BAI_ZONE:SearchOn() +

If Search is On, BAI will search for potential targets in the zone.

+
AI_BAI_ZONE:SearchOnOff(Search) +

Specifies whether to search for potential targets in the zone, or let the center of the zone be the bombing coordinate.

+
AI_BAI_ZONE:SetEngageZone(EngageZone) +

Set the Engage Zone where the AI is performing BOMB.

+
AI_BAI_ZONE.TargetZone +

The Zone where the patrol needs to be executed.

+
AI_BAI_ZONE:__Abort(Delay) +

Asynchronous Event Trigger for Event Abort.

+
AI_BAI_ZONE:__Accomplish(Delay) +

Asynchronous Event Trigger for Event Accomplish.

+
AI_BAI_ZONE:__Destroy(Delay) +

Asynchronous Event Trigger for Event Destroy.

+
AI_BAI_ZONE:__Engage(Delay, EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection) +

Asynchronous Event Trigger for Event Engage.

+
AI_BAI_ZONE:__Fired(Delay) +

Asynchronous Event Trigger for Event Fired.

+
AI_BAI_ZONE:onafterAbort(Controllable, From, Event, To) + +
AI_BAI_ZONE:onafterAccomplish(Controllable, From, Event, To) + +
AI_BAI_ZONE:onafterDestroy(Controllable, From, Event, To, EventData) + +
AI_BAI_ZONE:onafterEngage(Controllable, From, Event, To, EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection) + +
AI_BAI_ZONE:onafterStart(Controllable, From, Event, To) +

onafter State Transition for Event Start.

+
AI_BAI_ZONE:onafterTarget(Controllable, From, Event, To) + +
AI_BAI_ZONE:onbeforeEngage(Controllable, From, Event, To) + +
+ +

Global(s)

+
+
+ + #AI_BAI_ZONE + +AI_BAI_ZONE + +
+
+ +

AIBAIZONE class, extends AIPatrol#AIPATROL_ZONE

+ +

AIBAIZONE derives from the AIPatrol#AIPATROL_ZONE, inheriting its methods and behaviour.

+ + +

+The AIBAIZONE class implements the core functions to provide BattleGround Air Interdiction in an Engage Zone by an AIR Controllable or Group. +The AIBAIZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone.

+ +

HoldAndEngage

+ +

The AIBAIZONE is assigned a Group and this must be done before the AIBAIZONE process can be started through the Start event.

+ +

Start Event

+ +

Upon started, The AI will Route itself towards the random 3D point within a patrol zone, +using a random speed within the given altitude and speed limits. +Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. +This cycle will continue until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.

+ +

Route Event

+ +

When the AI is commanded to provide BattleGround Air Interdiction (through the event Engage), the AI will fly towards the Engage Zone. +Any target that is detected in the Engage Zone will be reported and will be destroyed by the AI.

+ +

Engage Event

+ +

The AI will detect the targets and will only destroy the targets within the Engage Zone.

+ +

Engage Event

+ +

Every target that is destroyed, is reported< by the AI.

+ +

Engage Event

+ +

Note that the AI does not know when the Engage Zone is cleared, and therefore will keep circling in the zone.

+ +

Engage Event

+ +

Until it is notified through the event Accomplish, which is to be triggered by an observing party:

+ +
    +
  • a FAC
  • +
  • a timed event
  • +
  • a menu option selected by a human
  • +
  • a condition
  • +
  • others ...
  • +
+ +

Engage Event

+ +

When the AI has accomplished the Bombing, it will fly back to the Patrol Zone.

+ +

Engage Event

+ +

It will keep patrolling there, until it is notified to RTB or move to another BOMB Zone. +It can be notified to go RTB through the RTB event.

+ +

When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.

+ +

Engage Event

+ +

1. AIBAIZONE constructor

+ + + +

2. AIBAIZONE is a FSM

+ +

Process

+ +

2.1. AIBAIZONE States

+ +
    +
  • None ( Group ): The process is not started yet.
  • +
  • Patrolling ( Group ): The AI is patrolling the Patrol Zone.
  • +
  • Engaging ( Group ): The AI is engaging the targets in the Engage Zone, executing BOMB.
  • +
  • Returning ( Group ): The AI is returning to Base..
  • +
+ +

2.2. AIBAIZONE Events

+ + + +

3. Modify the Engage Zone behaviour to pinpoint a map object or scenery object

+ +

Use the method AIBAIZONE.SearchOff() to specify that the EngageZone is not to be searched for potential targets (UNITs), but that the center of the zone +is the point where a map object is to be destroyed (like a bridge).

+ +

Example:

+ +
 -- Tell the BAI not to search for potential targets in the BAIEngagementZone, but rather use the center of the BAIEngagementZone as the bombing location.
+ AIBAIZone:SearchOff()
+
+ +

Searching can be switched back on with the method AIBAIZONE.SearchOn(). Use the method AIBAIZONE.SearchOnOff() to flexibily switch searching on or off.

+ +
+ + +
+
+
+
+ + +_NewEngageRoute(AIControllable) + +
+
+ + + +

Parameter

+ +
+
+

Type AI_Bai

+ +

Type AI_BAI_ZONE

+ +

AIBAIZONE class

+ +

Field(s)

+
+
+ + Wrapper.Controllable#CONTROLLABLE + +AI_BAI_ZONE.AIControllable + +
+
+ +

The Controllable patrolling.

+ +
+
+
+
+ + +AI_BAI_ZONE:Abort() + +
+
+ +

Synchronous Event Trigger for Event Abort.

+ +
+
+
+
+ + +AI_BAI_ZONE:Accomplish() + +
+
+ +

Synchronous Event Trigger for Event Accomplish.

+ +
+
+
+
+ + #boolean + +AI_BAI_ZONE.Accomplished + +
+
+ + + +
+
+
+
+ + +AI_BAI_ZONE:Destroy() + +
+
+ +

Synchronous Event Trigger for Event Destroy.

+ +
+
+
+
+ + +AI_BAI_ZONE:Engage(EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection) + +
+
+ +

Synchronous Event Trigger for Event Engage.

+ +

Parameters

+
    +
  • + +

    #number EngageSpeed : +(optional) The speed the Group will hold when engaging to the target zone.

    + +
  • +
  • + +

    Dcs.DCSTypes#Distance EngageAltitude : +(optional) Desired altitude to perform the unit engagement.

    + +
  • +
  • + +

    Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend : +(optional) Determines how much weapon will be released at each attack. +If parameter is not defined the unit / controllable will choose expend on its own discretion. +Use the structure DCSTypes#AI.Task.WeaponExpend to define the amount of weapons to be release at each attack.

    + +
  • +
  • + +

    #number EngageAttackQty : +(optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.

    + +
  • +
  • + +

    Dcs.DCSTypes#Azimuth EngageDirection : +(optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE.EngageAltitude + +
+
+ + + +
+
+
+
+ + + +AI_BAI_ZONE.EngageAttackQty + +
+
+ + + +
+
+
+
+ + + +AI_BAI_ZONE.EngageDirection + +
+
+ + + +
+
+
+
+ + +AI_BAI_ZONE.EngageSpeed + +
+
+ + + +
+
+
+
+ + + +AI_BAI_ZONE.EngageWeaponExpend + +
+
+ + + +
+
+
+
+ + + +AI_BAI_ZONE.EngageZone + +
+
+ + + +
+
+
+
+ + +AI_BAI_ZONE:Fired() + +
+
+ +

Synchronous Event Trigger for Event Fired.

+ +
+
+
+
+ + +AI_BAI_ZONE:New(PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType) + +
+
+ +

Creates a new AIBAIZONE object

+ +

Parameters

+ +

Return value

+ +

#AIBAIZONE: +self

+ +
+
+
+
+ + +AI_BAI_ZONE:OnAfterAbort(Controllable, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Abort.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:OnAfterAccomplish(Controllable, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Accomplish.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:OnAfterDestroy(Controllable, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Destroy.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:OnAfterEngage(Controllable, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Engage.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:OnAfterFired(Controllable, From, Event, To) + +
+
+ +

OnAfter Transition Handler for Event Fired.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:OnBeforeAbort(Controllable, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Abort.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_BAI_ZONE:OnBeforeAccomplish(Controllable, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Accomplish.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_BAI_ZONE:OnBeforeDestroy(Controllable, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Destroy.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_BAI_ZONE:OnBeforeEngage(Controllable, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Engage.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:OnBeforeFired(Controllable, From, Event, To) + +
+
+ +

OnBefore Transition Handler for Event Fired.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + +AI_BAI_ZONE:OnEnterEngaging(Controllable, From, Event, To) + +
+
+ +

OnEnter Transition Handler for State Engaging.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:OnEventDead(EventData) + +
+
+ + + +

Parameter

+ +
+
+
+
+ + +AI_BAI_ZONE:OnLeaveEngaging(Controllable, From, Event, To) + +
+
+ +

OnLeave Transition Handler for State Engaging.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+

Return value

+ +

#boolean: +Return false to cancel Transition.

+ +
+
+
+
+ + + +AI_BAI_ZONE.Search + +
+
+ + + +
+
+
+
+ + +AI_BAI_ZONE:SearchOff() + +
+
+ +

If Search is Off, the current zone coordinate will be the center of the bombing.

+ +

Return value

+ +

#AIBAIZONE:

+ + +
+
+
+
+ + +AI_BAI_ZONE:SearchOn() + +
+
+ +

If Search is On, BAI will search for potential targets in the zone.

+ +

Return value

+ +

#AIBAIZONE:

+ + +
+
+
+
+ + +AI_BAI_ZONE:SearchOnOff(Search) + +
+
+ +

Specifies whether to search for potential targets in the zone, or let the center of the zone be the bombing coordinate.

+ + +

AIBAIZONE will search for potential targets by default.

+ +

Parameter

+
    +
  • + +

    Search :

    + +
  • +
+

Return value

+ +

#AIBAIZONE:

+ + +
+
+
+
+ + +AI_BAI_ZONE:SetEngageZone(EngageZone) + +
+
+ +

Set the Engage Zone where the AI is performing BOMB.

+ + +

Note that if the EngageZone is changed, the AI needs to re-detect targets.

+ +

Parameter

+
    +
  • + +

    Core.Zone#ZONE EngageZone : +The zone where the AI is performing BOMB.

    + +
  • +
+

Return value

+ +

#AIBAIZONE: +self

+ +
+
+
+
+ + Core.Zone#ZONE_BASE + +AI_BAI_ZONE.TargetZone + +
+
+ +

The Zone where the patrol needs to be executed.

+ +
+
+
+
+ + +AI_BAI_ZONE:__Abort(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Abort.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:__Accomplish(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Accomplish.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:__Destroy(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Destroy.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:__Engage(Delay, EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection) + +
+
+ +

Asynchronous Event Trigger for Event Engage.

+ +

Parameters

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
  • + +

    #number EngageSpeed : +(optional) The speed the Group will hold when engaging to the target zone.

    + +
  • +
  • + +

    Dcs.DCSTypes#Distance EngageAltitude : +(optional) Desired altitude to perform the unit engagement.

    + +
  • +
  • + +

    Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend : +(optional) Determines how much weapon will be released at each attack. +If parameter is not defined the unit / controllable will choose expend on its own discretion. +Use the structure DCSTypes#AI.Task.WeaponExpend to define the amount of weapons to be release at each attack.

    + +
  • +
  • + +

    #number EngageAttackQty : +(optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.

    + +
  • +
  • + +

    Dcs.DCSTypes#Azimuth EngageDirection : +(optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:__Fired(Delay) + +
+
+ +

Asynchronous Event Trigger for Event Fired.

+ +

Parameter

+
    +
  • + +

    #number Delay : +The delay in seconds.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:onafterAbort(Controllable, From, Event, To) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:onafterAccomplish(Controllable, From, Event, To) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:onafterDestroy(Controllable, From, Event, To, EventData) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
  • + +

    Core.Event#EVENTDATA EventData :

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:onafterEngage(Controllable, From, Event, To, EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
  • + +

    #number EngageSpeed : +(optional) The speed the Group will hold when engaging to the target zone.

    + +
  • +
  • + +

    Dcs.DCSTypes#Distance EngageAltitude : +(optional) Desired altitude to perform the unit engagement.

    + +
  • +
  • + +

    Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend : +(optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.

    + +
  • +
  • + +

    #number EngageAttackQty : +(optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.

    + +
  • +
  • + +

    Dcs.DCSTypes#Azimuth EngageDirection : +(optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:onafterStart(Controllable, From, Event, To) + +
+
+ +

onafter State Transition for Event Start.

+ +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:onafterTarget(Controllable, From, Event, To) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+
+
+ + +AI_BAI_ZONE:onbeforeEngage(Controllable, From, Event, To) + +
+
+ + + +

Parameters

+
    +
  • + +

    Wrapper.Controllable#CONTROLLABLE Controllable : +The Controllable Object managed by the FSM.

    + +
  • +
  • + +

    #string From : +The From State string.

    + +
  • +
  • + +

    #string Event : +The Event string.

    + +
  • +
  • + +

    #string To : +The To State string.

    + +
  • +
+
+
+ +
+ +
+ + diff --git a/docs/Documentation/AI_Balancer.html b/docs/Documentation/AI_Balancer.html index 2b5f48f01..2f53c4846 100644 --- a/docs/Documentation/AI_Balancer.html +++ b/docs/Documentation/AI_Balancer.html @@ -17,9 +17,16 @@ index

Module AI_Balancer

-

Single-Player:No / Multi-Player:Yes / AI:Yes / Human:No / Types:All -- AI Balancing will replace in multi player missions +

AI -- AI Balancing will replace in multi player missions non-occupied human slots with AI groups, in order to provide an engaging simulation environment, even when there are hardly any players in the mission.

@@ -81,103 +113,33 @@ even when there are hardly any players in the mission.


-

1) AIBalancer#AIBALANCER class, extends Fsm#FSM_SET

+

Demo Missions

-

The AIBalancer#AIBALANCER class monitors and manages as many replacement AI groups as there are -CLIENTS in a SET_CLIENT collection, which are not occupied by human players.

+

AI_BALANCER Demo Missions source code

- -

In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions.

+

AI_BALANCER Demo Missions, only for beta testers

-

The parent class Fsm#FSM_SET manages the functionality to control the Finite State Machine (FSM). -The mission designer can tailor the behaviour of the AI_BALANCER, by defining event and state transition methods. -An explanation about state and event transition methods can be found in the FSM module documentation.

- -

The mission designer can tailor the AI_BALANCER behaviour, by implementing a state or event handling method for the following:

- - - -

1.1) AI_BALANCER construction

- -

Create a new AI_BALANCER object with the AI_BALANCER.New() method:

- -

1.2) AI_BALANCER is a FSM

- -

Process

- -

1.2.1) AI_BALANCER States

- -
    -
  • Monitoring ( Set ): Monitoring the Set if all AI is spawned for the Clients.
  • -
  • Spawning ( Set, ClientName ): There is a new AI group spawned with ClientName as the name of reference.
  • -
  • Spawned ( Set, AIGroup ): A new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
  • -
  • Destroying ( Set, AIGroup ): The AI is being destroyed.
  • -
  • Returning ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. Handle this state to customize the return behaviour of the AI, if any.
  • -
- -

1.2.2) AI_BALANCER Events

- -
    -
  • Monitor ( Set ): Every 10 seconds, the Monitor event is triggered to monitor the Set.
  • -
  • Spawn ( Set, ClientName ): Triggers when there is a new AI group to be spawned with ClientName as the name of reference.
  • -
  • Spawned ( Set, AIGroup ): Triggers when a new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
  • -
  • Destroy ( Set, AIGroup ): The AI is being destroyed.
  • -
  • Return ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods.
  • -
- -

1.3) AI_BALANCER spawn interval for replacement AI

- -

Use the method AI_BALANCER.InitSpawnInterval() to set the earliest and latest interval in seconds that is waited until a new replacement AI is spawned.

- -

1.4) AI_BALANCER returns AI to Airbases

- -

By default, When a human player joins a slot that is AI_BALANCED, the AI group will be destroyed by default. -However, there are 2 additional options that you can use to customize the destroy behaviour. -When a human player joins a slot, you can configure to let the AI return to:

- - - -

Note that when AI returns to an airbase, the AIBALANCER will trigger the Return event and the AI will return, -otherwise the AIBALANCER will trigger a Destroy event, and the AI will be destroyed.

+

ALL Demo Missions pack of the last release


-

API CHANGE HISTORY

+

YouTube Channel

-

The underlying change log documents the API changes. Please read this carefully. The following notation is used:

- -
    -
  • Added parts are expressed in bold type face.
  • -
  • Removed parts are expressed in italic type face.
  • -
- -

Hereby the change log:

- -

2017-01-17: There is still a problem with AI being destroyed, but not respawned. Need to check further upon that.

- -

2017-01-08: AI_BALANCER:InitSpawnInterval( Earliest, Latest ) added.

+

AI_BALANCER YouTube Channel


-

AUTHORS and CONTRIBUTIONS

- +

Author: Sven Van de Velde (FlightControl)

Contributions:

    -
  • Dutch_Baron: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
  • -
  • SNAFU: Had a couple of mails with the guys to validate, if the same concept in the GCI/CAP script could be reworked within MOOSE. None of the script code has been used however within the new AI_BALANCER moose class.
  • +
  • Dutch_Baron: Working together with James has resulted in the creation of the AI_BALANCER class.
-

Authors:

+ +

James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)

-
    -
  • FlightControl: Framework Design & Programming and Documentation.
  • -
+

Global(s)

@@ -185,25 +147,16 @@ otherwise the AIBALANCER will trigger a Destroy event, and AI_BALANCER +

AI_BALANCER class, extends Fsm#FSM_SET

+

The AIBALANCER class monitors and manages as many replacement AI groups as there are +CLIENTS in a SETCLIENT collection, which are not occupied by human players.

Type AI_BALANCER

- - - - - - - - - - - - @@ -240,21 +187,21 @@ otherwise the AIBALANCER will trigger a Destroy event, and - + + + + + - + - - - - @@ -324,6 +271,69 @@ otherwise the AIBALANCER will trigger a Destroy event, and
+

AI_BALANCER class, extends Fsm#FSM_SET

+ +

The AIBALANCER class monitors and manages as many replacement AI groups as there are +CLIENTS in a SETCLIENT collection, which are not occupied by human players.

+ + +

In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions.

+ +

The parent class Fsm#FSM_SET manages the functionality to control the Finite State Machine (FSM). +The mission designer can tailor the behaviour of the AI_BALANCER, by defining event and state transition methods. +An explanation about state and event transition methods can be found in the FSM module documentation.

+ +

The mission designer can tailor the AI_BALANCER behaviour, by implementing a state or event handling method for the following:

+ + + +

1. AI_BALANCER construction

+ +

Create a new AI_BALANCER object with the AI_BALANCER.New() method:

+ +

2. AI_BALANCER is a FSM

+ +

Process

+ +

2.1. AI_BALANCER States

+ +
    +
  • Monitoring ( Set ): Monitoring the Set if all AI is spawned for the Clients.
  • +
  • Spawning ( Set, ClientName ): There is a new AI group spawned with ClientName as the name of reference.
  • +
  • Spawned ( Set, AIGroup ): A new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
  • +
  • Destroying ( Set, AIGroup ): The AI is being destroyed.
  • +
  • Returning ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. Handle this state to customize the return behaviour of the AI, if any.
  • +
+ +

2.2. AI_BALANCER Events

+ +
    +
  • Monitor ( Set ): Every 10 seconds, the Monitor event is triggered to monitor the Set.
  • +
  • Spawn ( Set, ClientName ): Triggers when there is a new AI group to be spawned with ClientName as the name of reference.
  • +
  • Spawned ( Set, AIGroup ): Triggers when a new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
  • +
  • Destroy ( Set, AIGroup ): The AI is being destroyed.
  • +
  • Return ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods.
  • +
+ +

3. AI_BALANCER spawn interval for replacement AI

+ +

Use the method AI_BALANCER.InitSpawnInterval() to set the earliest and latest interval in seconds that is waited until a new replacement AI is spawned.

+ +

4. AI_BALANCER returns AI to Airbases

+ +

By default, When a human player joins a slot that is AI_BALANCED, the AI group will be destroyed by default. +However, there are 2 additional options that you can use to customize the destroy behaviour. +When a human player joins a slot, you can configure to let the AI return to:

+ + + +

Note that when AI returns to an airbase, the AIBALANCER will trigger the Return event and the AI will return, +otherwise the AIBALANCER will trigger a Destroy event, and the AI will be destroyed.

@@ -331,38 +341,7 @@ otherwise the AIBALANCER will trigger a Destroy event, and

Type AI_Balancer

Type AI_BALANCER

- -

AI_BALANCER class

- -

Field(s)

-
-
- - - -AI_BALANCER.AIGroups - -
-
- - - -
-
-
-
- - #string - -AI_BALANCER.ClassName - -
-
- - - -
-
+

Field(s)

@@ -458,20 +437,6 @@ The default Spawn object to spawn new AI Groups when needed.

#AI_BALANCER:

- -
-
-
- - - -AI_BALANCER.PatrolZones - -
-
- - -
@@ -486,13 +451,27 @@ The default Spawn object to spawn new AI Groups when needed.

+ +
+
+
+ + + +AI_BALANCER.ReturnThresholdRange + +
+
+ + +
-AI_BALANCER:ReturnToHomeAirbase(ReturnTresholdRange) +AI_BALANCER:ReturnToHomeAirbase(ReturnThresholdRange)
@@ -503,8 +482,8 @@ The default Spawn object to spawn new AI Groups when needed.

@@ -514,7 +493,7 @@ If there is an enemy Client#CLIENT within th
-AI_BALANCER:ReturnToNearestAirbases(ReturnTresholdRange, ReturnAirbaseSet) +AI_BALANCER:ReturnToNearestAirbases(ReturnThresholdRange, ReturnAirbaseSet)
@@ -525,8 +504,8 @@ If there is an enemy Client#CLIENT within th -
-
-
-
- - - -AI_BALANCER.ReturnTresholdRange - -
-
- - -
diff --git a/docs/Documentation/AI_Cap.html b/docs/Documentation/AI_Cap.html index 0657d3fa0..715305e43 100644 --- a/docs/Documentation/AI_Cap.html +++ b/docs/Documentation/AI_Cap.html @@ -17,9 +17,16 @@ index

Module AI_Cap

-

AI - Execute Combat Air Patrol (CAP).

+

AI -- Execute Combat Air Patrol (CAP).

Banner Image

@@ -91,22 +123,23 @@
-

API CHANGE HISTORY

+

Demo Missions

-

The underlying change log documents the API changes. Please read this carefully. The following notation is used:

+

AI_CAP Demo Missions source code

-
    -
  • Added parts are expressed in bold type face.
  • -
  • Removed parts are expressed in italic type face.
  • -
+

AI_CAP Demo Missions, only for beta testers

-

Hereby the change log:

- -

2017-01-15: Initial class and API.

+

ALL Demo Missions pack of the last release


-

AUTHORS and CONTRIBUTIONS

+

YouTube Channel

+ +

AI_CAP YouTube Channel

+ +
+ +

Author: Sven Van de Velde (FlightControl)

Contributions:

@@ -118,11 +151,7 @@
  • **Delta99: Testing.
  • -

    Authors:

    - -
      -
    • FlightControl: Concept, Design & Programming.
    • -
    +

    Global(s)

    @@ -130,9 +159,9 @@
    @@ -392,9 +421,9 @@ and automatically engage any airborne enemies that are within a certain range or
    -

    1) #AICAPZONE class, extends AICAP#AIPATROL_ZONE

    +

    AICAPZONE class, extends AICAP#AIPATROL_ZONE

    -

    The #AICAPZONE class implements the core functions to patrol a Zone by an AI Controllable or Group +

    The AICAPZONE class implements the core functions to patrol a Zone by an AI Controllable or Group and automatically engage any airborne enemies that are within a certain range or within a certain zone.

    @@ -427,17 +456,17 @@ When the fuel treshold has been reached, the airplane will fly towards the neare

    Process

    -

    1.1) AICAPZONE constructor

    +

    1. AICAPZONE constructor

    -

    1.2) AICAPZONE is a FSM

    +

    2. AICAPZONE is a FSM

    Process

    -

    1.2.1) AICAPZONE States

    +

    2.1 AICAPZONE States

    • None ( Group ): The process is not started yet.
    • @@ -446,7 +475,7 @@ When the fuel treshold has been reached, the airplane will fly towards the neare
    • Returning ( Group ): The AI is returning to Base..
    -

    1.2.2) AICAPZONE Events

    +

    2.2 AICAPZONE Events

    • **AIPatrol#AIPATROL_ZONE.Start**: Start the process.
    • @@ -461,7 +490,7 @@ When the fuel treshold has been reached, the airplane will fly towards the neare
    • Status ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
    -

    1.3) Set the Range of Engagement

    +

    3. Set the Range of Engagement

    Range

    @@ -471,7 +500,7 @@ The range can be beyond or smaller than the range of the Patrol Zone. The range is applied at the position of the AI. Use the method AICAP#AICAP_ZONE.SetEngageRange() to define that range.

    -

    1.4) Set the Zone of Engagement

    +

    4. Set the Zone of Engagement

    Zone

    diff --git a/docs/Documentation/AI_Cas.html b/docs/Documentation/AI_Cas.html index 10992a925..068b80a80 100644 --- a/docs/Documentation/AI_Cas.html +++ b/docs/Documentation/AI_Cas.html @@ -17,9 +17,16 @@ index @@ -91,22 +123,24 @@
    -

    API CHANGE HISTORY

    +

    Demo Missions

    -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    +

    AI_CAS Demo Missions source code

    -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    +

    AI_CAS Demo Missions, only for beta testers

    -

    Hereby the change log:

    - -

    2017-01-15: Initial class and API.

    +

    ALL Demo Missions pack of the last release


    -

    AUTHORS and CONTRIBUTIONS

    +

    YouTube Channel

    + +

    AI_CAS YouTube Channel

    + +
    + + +

    Author: Sven Van de Velde (FlightControl)

    Contributions:

    @@ -116,11 +150,7 @@
  • Gunterlund: Test case revision.
  • -

    Authors:

    - -
      -
    • FlightControl: Concept, Design & Programming.
    • -
    +

    Global(s)

    @@ -128,9 +158,9 @@
    @@ -401,13 +431,13 @@
    -

    1) #AICASZONE class, extends AIPatrol#AIPATROL_ZONE

    +

    AICASZONE class, extends AIPatrol#AIPATROL_ZONE

    -

    #AICASZONE derives from the AIPatrol#AIPATROL_ZONE, inheriting its methods and behaviour.

    +

    AICASZONE derives from the AIPatrol#AIPATROL_ZONE, inheriting its methods and behaviour.

    -The #AICASZONE class implements the core functions to provide Close Air Support in an Engage Zone by an AIR Controllable or Group. +The AICASZONE class implements the core functions to provide Close Air Support in an Engage Zone by an AIR Controllable or Group. The AICASZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone.

    HoldAndEngage

    @@ -463,17 +493,17 @@ It can be notified to go RTB through the RTB event.

    Engage Event

    -

    1.1) AICASZONE constructor

    +

    AICASZONE constructor

    -

    1.2) AICASZONE is a FSM

    +

    AICASZONE is a FSM

    Process

    -

    1.2.1) AICASZONE States

    +

    2.1. AICASZONE States

    • None ( Group ): The process is not started yet.
    • @@ -482,7 +512,7 @@ It can be notified to go RTB through the RTB event.

    • Returning ( Group ): The AI is returning to Base..
    -

    1.2.2) AICASZONE Events

    +

    2.2. AICASZONE Events

    AI_BALANCER.AIGroups - -
    AI_BALANCER.ClassName - -
    AI_BALANCER.Earliest @@ -225,12 +178,6 @@ otherwise the AIBALANCER will trigger a Destroy event, and AI_BALANCER:New(SetClient, SpawnAI)

    Creates a new AI_BALANCER object

    -
    AI_BALANCER.PatrolZones -
    AI_BALANCER:ReturnToHomeAirbase(ReturnTresholdRange)AI_BALANCER.ReturnThresholdRange + +
    AI_BALANCER:ReturnToHomeAirbase(ReturnThresholdRange)

    Returns the AI to the home Airbase#AIRBASE.

    AI_BALANCER:ReturnToNearestAirbases(ReturnTresholdRange, ReturnAirbaseSet)AI_BALANCER:ReturnToNearestAirbases(ReturnThresholdRange, ReturnAirbaseSet)

    Returns the AI to the nearest friendly Airbase#AIRBASE.

    -
    AI_BALANCER.ReturnTresholdRange -
    AI_CAP_ZONE -

    1) #AICAPZONE class, extends AICAP#AIPATROL_ZONE

    +

    AICAPZONE class, extends AICAP#AIPATROL_ZONE

    -

    The #AICAPZONE class implements the core functions to patrol a Zone by an AI Controllable or Group +

    The AICAPZONE class implements the core functions to patrol a Zone by an AI Controllable or Group and automatically engage any airborne enemies that are within a certain range or within a certain zone.

    AI_CAS_ZONE -

    1) #AICASZONE class, extends AIPatrol#AIPATROL_ZONE

    +

    AICASZONE class, extends AIPatrol#AIPATROL_ZONE

    -

    #AICASZONE derives from the AIPatrol#AIPATROL_ZONE, inheriting its methods and behaviour.

    +

    AICASZONE derives from the AIPatrol#AIPATROL_ZONE, inheriting its methods and behaviour.

    + + + + +
    AI_DESIGNATE +

    AI_DESIGNATE class, extends Fsm#FSM

    + +

    AI_DESIGNATE is orchestrating the designation of potential targets executed by a Recce group, +and communicates these to a dedicated attacking group of players, +so that following a dynamically generated menu system, +each detected set of potential targets can be lased or smoked...

    +
    +

    Type AI_DESIGNATE

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AI_DESIGNATE:ActivateAutoLase() +

    Coordinates the Auto Lase.

    +
    AI_DESIGNATE.AttackSet + +
    AI_DESIGNATE.AutoLase + +
    AI_DESIGNATE.Designating + +
    AI_DESIGNATE:Detect() +

    Detect Trigger for AI_DESIGNATE

    +
    AI_DESIGNATE.Detection + +
    AI_DESIGNATE.FlashStatusMenu + +
    AI_DESIGNATE:GenerateLaserCodes() +

    Generate an array of possible laser codes.

    +
    AI_DESIGNATE:Illuminate() +

    Illuminate Trigger for AI_DESIGNATE

    +
    AI_DESIGNATE.LaseDuration + +
    AI_DESIGNATE:LaseOff() +

    LaseOff Trigger for AI_DESIGNATE

    +
    AI_DESIGNATE:LaseOn() +

    LaseOn Trigger for AI_DESIGNATE

    +
    AI_DESIGNATE.LaserCodes + +
    AI_DESIGNATE.LaserCodesUsed + +
    AI_DESIGNATE:MenuAutoLase(AutoLase) + +
    AI_DESIGNATE:MenuFlashStatus(AttackGroup, Flash) + +
    AI_DESIGNATE:MenuIlluminate(Index) + +
    AI_DESIGNATE:MenuLaseOff(Index, Duration) + +
    AI_DESIGNATE:MenuLaseOn(Index, Duration) + +
    AI_DESIGNATE:MenuSmoke(Index, Color) + +
    AI_DESIGNATE:MenuStatus(AttackGroup) + +
    AI_DESIGNATE:New(Detection, AttackSet) +

    AI_DESIGNATE Constructor.

    +
    AI_DESIGNATE:OnAfterDetect(From, Event, To) +

    Detect Handler OnAfter for AI_DESIGNATE

    +
    AI_DESIGNATE:OnAfterIlluminate(From, Event, To) +

    Illuminate Handler OnAfter for AI_DESIGNATE

    +
    AI_DESIGNATE:OnAfterLaseOff(From, Event, To) +

    LaseOff Handler OnAfter for AI_DESIGNATE

    +
    AI_DESIGNATE:OnAfterLaseOn(From, Event, To) +

    LaseOn Handler OnAfter for AI_DESIGNATE

    +
    AI_DESIGNATE:OnAfterSmoke(From, Event, To) +

    Smoke Handler OnAfter for AI_DESIGNATE

    +
    AI_DESIGNATE:OnAfterStatus(From, Event, To) +

    Status Handler OnAfter for AI_DESIGNATE

    +
    AI_DESIGNATE:OnBeforeDetect(From, Event, To) +

    Detect Handler OnBefore for AI_DESIGNATE

    +
    AI_DESIGNATE:OnBeforeIlluminate(From, Event, To) +

    Illuminate Handler OnBefore for AI_DESIGNATE

    +
    AI_DESIGNATE:OnBeforeLaseOff(From, Event, To) +

    LaseOff Handler OnBefore for AI_DESIGNATE

    +
    AI_DESIGNATE:OnBeforeLaseOn(From, Event, To) +

    LaseOn Handler OnBefore for AI_DESIGNATE

    +
    AI_DESIGNATE:OnBeforeSmoke(From, Event, To) +

    Smoke Handler OnBefore for AI_DESIGNATE

    +
    AI_DESIGNATE:OnBeforeStatus(From, Event, To) +

    Status Handler OnBefore for AI_DESIGNATE

    +
    AI_DESIGNATE.RecceSet + +
    AI_DESIGNATE.Recces + +
    AI_DESIGNATE:SendStatus() +

    Sends the status to the Attack Groups.

    +
    AI_DESIGNATE:SetAutoLase(AutoLase) +

    Set auto lase.

    +
    AI_DESIGNATE:SetDesignateMenu() +

    Sets the Designate Menu.

    +
    AI_DESIGNATE:SetFlashStatusMenu(FlashMenu) +

    Set the flashing of the status menu.

    +
    AI_DESIGNATE:SetLaserCodes(<, LaserCodes) +

    Set an array of possible laser codes.

    +
    AI_DESIGNATE:SetThreatLevelPrioritization(Prioritize) +

    Set priorization of Targets based on the Threat Level of the Target in an Air to Ground context.

    +
    AI_DESIGNATE:Smoke() +

    Smoke Trigger for AI_DESIGNATE

    +
    AI_DESIGNATE:Status() +

    Status Trigger for AI_DESIGNATE

    +
    AI_DESIGNATE.ThreatLevelPrioritization + +
    AI_DESIGNATE:__Detect(Delay) +

    Detect Asynchronous Trigger for AI_DESIGNATE

    +
    AI_DESIGNATE:__Illuminate(Delay) +

    Illuminate Asynchronous Trigger for AI_DESIGNATE

    +
    AI_DESIGNATE:__LaseOff(Delay) +

    LaseOff Asynchronous Trigger for AI_DESIGNATE

    +
    AI_DESIGNATE:__LaseOn(Delay) +

    LaseOn Asynchronous Trigger for AI_DESIGNATE

    +
    AI_DESIGNATE:__Smoke(Delay) +

    Smoke Asynchronous Trigger for AI_DESIGNATE

    +
    AI_DESIGNATE:__Status(Delay) +

    Status Asynchronous Trigger for AI_DESIGNATE

    +
    AI_DESIGNATE:onafterDetect() + +
    AI_DESIGNATE:onafterDone(From, Event, To, Index) +

    Done

    +
    AI_DESIGNATE:onafterIlluminate(From, Event, To, Index) +

    Illuminating

    +
    AI_DESIGNATE:onafterLaseOff(From, Event, To, Index) + +
    AI_DESIGNATE:onafterLaseOn(From, Event, To, Index, Duration) + +
    AI_DESIGNATE:onafterLasing(From, Event, To, Index, Duration) + +
    AI_DESIGNATE:onafterSmoke(From, Event, To, Index, Color) + +
    + +

    Global(s)

    +
    +
    + + #AI_DESIGNATE + +AI_DESIGNATE + +
    +
    + +

    AI_DESIGNATE class, extends Fsm#FSM

    + +

    AI_DESIGNATE is orchestrating the designation of potential targets executed by a Recce group, +and communicates these to a dedicated attacking group of players, +so that following a dynamically generated menu system, +each detected set of potential targets can be lased or smoked...

    + + + +

    Targets can be:

    + +
      +
    • Lased for a period of time.
    • +
    • Smoked. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.)
    • +
    • Illuminated through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.
    • +
    + +

    The following terminology is being used throughout this document:

    + +
      +
    • The DesignateObject is the object of the AI_DESIGNATE class, which is this class explained in the document.
    • +
    • The DetectionObject is the object of a DETECTION_ class (DETECTIONTYPES, DETECTIONAREAS, DETECTION_UNITS), which is executing the detection and grouping of Targets into DetectionItems.
    • +
    • DetectionItems is the list of detected target groupings by the DetectionObject. Each DetectionItem contains a TargetSet.
    • +
    • DetectionItem is one element of the DetectionItems list, and contains a TargetSet.
    • +
    • The TargetSet is a SET_UNITS collection of Targets, that have been detected by the DetectionObject.
    • +
    • A Target is a detected UNIT object by the DetectionObject.
    • +
    • A Threat Level is a number from 0 to 10 that is calculated based on the threat of the Target in an Air to Ground battle scenario.
    • +
    • The RecceSet is a SET_GROUP collection that contains the RecceGroups.
    • +
    • A RecceGroup is a GROUP object containing the Recces.
    • +
    • A Recce is a UNIT object executing the reconnaissance as part the DetectionObject. A Recce can be of any UNIT type.
    • +
    • An AttackGroup is a GROUP object that contain Players.
    • +
    • A Player is an active CLIENT object containing a human player.
    • +
    • A Designate Menu is the menu that is dynamically created during the designation process for each AttackGroup.
    • +
    + +

    The RecceSet is continuously detecting for potential Targets, executing its task as part of the DetectionObject. +Once Targets have been detected, the DesignateObject will trigger the Detect Event.

    + +

    As part of the Detect Event, the DetectionItems list is used by the DesignateObject to provide the Players with:

    + +
      +
    • The RecceGroups are reporting to each AttackGroup, sending Messages containing the Threat Level and the TargetSet composition.
    • +
    • Menu options are created and updated for each AttackGroup, containing the Threat Level and the TargetSet composition.
    • +
    + +

    A Player can then select an action from the Designate Menu.

    + +

    Note that each selected action will be executed for a TargetSet, thus the Target grouping done by the DetectionObject.

    + +

    Each Menu Option in the Designate Menu has two modes:

    + +
      +
    1. If the TargetSet is not being designated, then the Designate Menu option for the target Set will provide options to Lase or Smoke the targets.
    2. +
    3. If the Target Set is being designated, then the Designate Menu option will provide an option to stop or cancel the designation.
    4. +
    + +

    While designating, the RecceGroups will report any change in TargetSet composition or Target presence.

    + +

    The following logic is executed when a TargetSet is selected to be lased from the Designation Menu:

    + +
      +
    • The RecceSet is searched for any Recce that is within designation distance from a Target in the TargetSet that is currently not being designated.
    • +
    • If there is a Recce found that is currently no designating a target, and is within designation distance from the Target, then that Target will be designated.
    • +
    • During designation, any Recce that does not have Line of Sight (LOS) and is not within disignation distance from the Target, will stop designating the Target, and a report is given.
    • +
    • When a Recce is designating a Target, and that Target is destroyed, then the Recce will stop designating the Target, and will report the event.
    • +
    • When a Recce is designating a Target, and that Recce is destroyed, then the Recce will be removed from the RecceSet and designation will stop without reporting.
    • +
    • When all RecceGroups are destroyed from the RecceSet, then the DesignationObject will stop functioning, and nothing will be reported.
    • +
    + +

    In this way, the DesignationObject assists players to designate ground targets for a coordinated attack!

    + +

    Have FUN!

    + +

    1. AI_DESIGNATE constructor

    + + + +

    2. AI_DESIGNATE is a FSM

    + +

    Process

    + +

    2.1 AI_DESIGNATE States

    + +
      +
    • Designating ( Group ): The process is not started yet.
    • +
    + +

    2.2 AI_DESIGNATE Events

    + + + +

    3. Laser codes

    + +

    3.1 Set possible laser codes

    + +

    An array of laser codes can be provided, that will be used by the AI_DESIGNATE when lasing. +The laser code is communicated by the Recce when it is lasing a larget. +Note that the default laser code is 1113. +Working known laser codes are: 1113,1462,1483,1537,1362,1214,1131,1182,1644,1614,1515,1411,1621,1138,1542,1678,1573,1314,1643,1257,1467,1375,1341,1275,1237

    + +

    Use the method AI_DESIGNATE.SetLaserCodes() to set the possible laser codes to be selected from. +One laser code can be given or an sequence of laser codes through an table...

    + +
    AIDesignate:SetLaserCodes( 1214 )
    +
    + +

    The above sets one laser code with the value 1214.

    + +
    AIDesignate:SetLaserCodes( { 1214, 1131, 1614, 1138 } )
    +
    + +

    The above sets a collection of possible laser codes that can be assigned. Note the { } notation!

    + +

    3.2 Auto generate laser codes

    + +

    Use the method AI_DESIGNATE.GenerateLaserCodes() to generate all possible laser codes. Logic implemented and advised by Ciribob!

    + +

    4. Autolase to automatically lase detected targets.

    + +

    DetectionItems can be auto lased once detected by Recces. As such, there is almost no action required from the Players using the Designate Menu. +The auto lase function can be activated through the Designation Menu. +Use the method AI_DESIGNATE.SetAutoLase() to activate or deactivate the auto lase function programmatically. +Note that autolase will automatically activate lasing for ALL DetectedItems. Individual items can be switched-off if required using the Designation Menu.

    + +
    AIDesignate:SetAutoLase( true )
    +
    + +

    Activate the auto lasing.

    + +

    5. Target prioritization on threat level

    + +

    Targets can be detected of different types in one DetectionItem. Depending on the type of the Target, a different threat level applies in an Air to Ground combat context. +SAMs are of a higher threat than normal tanks. So, if the Target type was recognized, the Recces will select those targets that form the biggest threat first, +and will continue this until the remaining vehicles with the lowest threat have been reached.

    + +

    This threat level prioritization can be activated using the method AI_DESIGNATE.SetThreatLevelPrioritization(). +If not activated, Targets will be selected in a random order, but most like those first which are the closest to the Recce marking the Target.

    + +

    6. Status Report

    + +

    A status report is available that displays the current Targets detected, grouped per DetectionItem, and a list of which Targets are currently being marked.

    + +
      +
    • The status report can be shown by selecting "Status" -> "Report Status" from the Designation menu .
    • +
    • The status report can be automatically flashed by selecting "Status" -> "Flash Status On".
    • +
    • The automatic flashing of the status report can be deactivated by selecting "Status" -> "Flash Status Off".
    • +
    • The flashing of the status menu is disabled by default.
    • +
    • The method AI_DESIGNATE.FlashStatusMenu() can be used to enable or disable to flashing of the status menu.
    • +
    + + +
    +
    +

    Type AI_Designate

    + +

    Type AI_DESIGNATE

    +

    Field(s)

    +
    +
    + + +AI_DESIGNATE:ActivateAutoLase() + +
    +
    + +

    Coordinates the Auto Lase.

    + +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + + +AI_DESIGNATE.AttackSet + +
    +
    + + + +
    +
    +
    +
    + + + +AI_DESIGNATE.AutoLase + +
    +
    + + + +
    +
    +
    +
    + + + +AI_DESIGNATE.Designating + +
    +
    + + + +
    +
    +
    +
    + + +AI_DESIGNATE:Detect() + +
    +
    + +

    Detect Trigger for AI_DESIGNATE

    + +
    +
    +
    +
    + + + +AI_DESIGNATE.Detection + +
    +
    + + + +
    +
    +
    +
    + + + +AI_DESIGNATE.FlashStatusMenu + +
    +
    + + + +
    +
    +
    +
    + + +AI_DESIGNATE:GenerateLaserCodes() + +
    +
    + +

    Generate an array of possible laser codes.

    + + +

    Each new lase will select a code from this table. +The entered value can range from 1111 - 1788, +-- but the first digit of the series must be a 1 or 2 +-- and the last three digits must be between 1 and 8. + The range used to be bugged so its not 1 - 8 but 0 - 7. +function below will use the range 1-7 just in case

    + +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:Illuminate() + +
    +
    + +

    Illuminate Trigger for AI_DESIGNATE

    + +
    +
    +
    +
    + + #number + +AI_DESIGNATE.LaseDuration + +
    +
    + + + +
    +
    +
    +
    + + +AI_DESIGNATE:LaseOff() + +
    +
    + +

    LaseOff Trigger for AI_DESIGNATE

    + +
    +
    +
    +
    + + +AI_DESIGNATE:LaseOn() + +
    +
    + +

    LaseOn Trigger for AI_DESIGNATE

    + +
    +
    +
    +
    + + + +AI_DESIGNATE.LaserCodes + +
    +
    + + + +
    +
    +
    +
    + + + +AI_DESIGNATE.LaserCodesUsed + +
    +
    + + + +
    +
    +
    +
    + + +AI_DESIGNATE:MenuAutoLase(AutoLase) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      AutoLase :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:MenuFlashStatus(AttackGroup, Flash) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      AttackGroup :

      + +
    • +
    • + +

      Flash :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:MenuIlluminate(Index) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      Index :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:MenuLaseOff(Index, Duration) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      Index :

      + +
    • +
    • + +

      Duration :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:MenuLaseOn(Index, Duration) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      Index :

      + +
    • +
    • + +

      Duration :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:MenuSmoke(Index, Color) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      Index :

      + +
    • +
    • + +

      Color :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:MenuStatus(AttackGroup) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      AttackGroup :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:New(Detection, AttackSet) + +
    +
    + +

    AI_DESIGNATE Constructor.

    + + +

    This class is an abstract class and should not be instantiated.

    + +

    Parameters

    + +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:OnAfterDetect(From, Event, To) + +
    +
    + +

    Detect Handler OnAfter for AI_DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:OnAfterIlluminate(From, Event, To) + +
    +
    + +

    Illuminate Handler OnAfter for AI_DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:OnAfterLaseOff(From, Event, To) + +
    +
    + +

    LaseOff Handler OnAfter for AI_DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:OnAfterLaseOn(From, Event, To) + +
    +
    + +

    LaseOn Handler OnAfter for AI_DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:OnAfterSmoke(From, Event, To) + +
    +
    + +

    Smoke Handler OnAfter for AI_DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:OnAfterStatus(From, Event, To) + +
    +
    + +

    Status Handler OnAfter for AI_DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:OnBeforeDetect(From, Event, To) + +
    +
    + +

    Detect Handler OnBefore for AI_DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:OnBeforeIlluminate(From, Event, To) + +
    +
    + +

    Illuminate Handler OnBefore for AI_DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:OnBeforeLaseOff(From, Event, To) + +
    +
    + +

    LaseOff Handler OnBefore for AI_DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:OnBeforeLaseOn(From, Event, To) + +
    +
    + +

    LaseOn Handler OnBefore for AI_DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:OnBeforeSmoke(From, Event, To) + +
    +
    + +

    Smoke Handler OnBefore for AI_DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:OnBeforeStatus(From, Event, To) + +
    +
    + +

    Status Handler OnBefore for AI_DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + + +AI_DESIGNATE.RecceSet + +
    +
    + + + +
    +
    +
    +
    + + + +AI_DESIGNATE.Recces + +
    +
    + + + +
    +
    +
    +
    + + +AI_DESIGNATE:SendStatus() + +
    +
    + +

    Sends the status to the Attack Groups.

    + +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:SetAutoLase(AutoLase) + +
    +
    + +

    Set auto lase.

    + + +

    Auto lase will start lasing targets immediately when these are in range.

    + +

    Parameter

    +
      +
    • + +

      #boolean AutoLase :

      + +
    • +
    +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:SetDesignateMenu() + +
    +
    + +

    Sets the Designate Menu.

    + +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:SetFlashStatusMenu(FlashMenu) + +
    +
    + +

    Set the flashing of the status menu.

    + +

    Parameter

    +
      +
    • + +

      #boolean FlashMenu : +true: the status menu will be flashed every detection run; false: no flashing of the menu.

      + +
    • +
    +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:SetLaserCodes(<, LaserCodes) + +
    +
    + +

    Set an array of possible laser codes.

    + + +

    Each new lase will select a code from this table.

    + +

    Parameters

    +
      +
    • + +

      #list < : +number> LaserCodes

      + +
    • +
    • + +

      LaserCodes :

      + +
    • +
    +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:SetThreatLevelPrioritization(Prioritize) + +
    +
    + +

    Set priorization of Targets based on the Threat Level of the Target in an Air to Ground context.

    + +

    Parameter

    +
      +
    • + +

      #boolean Prioritize :

      + +
    • +
    +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:Smoke() + +
    +
    + +

    Smoke Trigger for AI_DESIGNATE

    + +
    +
    +
    +
    + + +AI_DESIGNATE:Status() + +
    +
    + +

    Status Trigger for AI_DESIGNATE

    + +
    +
    +
    +
    + + + +AI_DESIGNATE.ThreatLevelPrioritization + +
    +
    + + + +
    +
    +
    +
    + + +AI_DESIGNATE:__Detect(Delay) + +
    +
    + +

    Detect Asynchronous Trigger for AI_DESIGNATE

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:__Illuminate(Delay) + +
    +
    + +

    Illuminate Asynchronous Trigger for AI_DESIGNATE

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:__LaseOff(Delay) + +
    +
    + +

    LaseOff Asynchronous Trigger for AI_DESIGNATE

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:__LaseOn(Delay) + +
    +
    + +

    LaseOn Asynchronous Trigger for AI_DESIGNATE

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:__Smoke(Delay) + +
    +
    + +

    Smoke Asynchronous Trigger for AI_DESIGNATE

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:__Status(Delay) + +
    +
    + +

    Status Asynchronous Trigger for AI_DESIGNATE

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:onafterDetect() + +
    +
    + + + +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:onafterDone(From, Event, To, Index) + +
    +
    + +

    Done

    + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:onafterIlluminate(From, Event, To, Index) + +
    +
    + +

    Illuminating

    + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:onafterLaseOff(From, Event, To, Index) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:onafterLaseOn(From, Event, To, Index, Duration) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      Index :

      + +
    • +
    • + +

      Duration :

      + +
    • +
    +
    +
    +
    +
    + + +AI_DESIGNATE:onafterLasing(From, Event, To, Index, Duration) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      Index :

      + +
    • +
    • + +

      Duration :

      + +
    • +
    +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    +
    +
    + + +AI_DESIGNATE:onafterSmoke(From, Event, To, Index, Color) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      Index :

      + +
    • +
    • + +

      Color :

      + +
    • +
    +

    Return value

    + +

    #AI_DESIGNATE:

    + + +
    +
    + +

    Type list

    + +
    + +
    + + diff --git a/docs/Documentation/AI_Follow.html b/docs/Documentation/AI_Follow.html new file mode 100644 index 000000000..a09fbf172 --- /dev/null +++ b/docs/Documentation/AI_Follow.html @@ -0,0 +1,1935 @@ + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +

    Module AI_Follow

    + +

    AI -- (R2.1) Build large formations of AI Groups flying together.

    + + + +

    Banner Image

    + +
    + +

    AI_FORMATION makes AI GROUPs fly in formation of various compositions.

    + +

    There are the following types of classes defined:

    + + + +
    + +

    Demo Missions

    + +

    AI_FORMATION Demo Missions source code

    + +

    AI_FORMATION Demo Missions, only for beta testers

    + +

    ALL Demo Missions pack of the last release

    + +
    + +

    YouTube Channel

    + + + +
    + +

    AUTHORS and CONTRIBUTIONS

    + +

    Contributions:

    + +

    Authors:

    + +
      +
    • FlightControl: Concept, Design & Programming. +
    • +
    + +

    Global(s)

    + + + + + +
    AI_FORMATION +

    AI_FORMATION class, extends Fsm#FSM_SET

    + +

    The #AI_FORMATION class allows you to build large formations, make AI follow a Client#CLIENT (player) leader or a Unit#UNIT (AI) leader.

    +
    +

    Type AI_FORMATION

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AI_FORMATION.FollowDistance +

    The current follow distance.

    +
    AI_FORMATION.FollowGroupSet + +
    AI_FORMATION.FollowMenuResumeMission + +
    AI_FORMATION.FollowMode +

    The mode the escort is in.

    +
    AI_FORMATION.FollowName + +
    AI_FORMATION.FollowScheduler +

    The instance of the SCHEDULER class.

    +
    AI_FORMATION.FollowUnit + +
    AI_FORMATION:FormationLeftLine(XStart, YStart, ZStart, ZSpace) +

    FormationLeftLine Trigger for AI_FORMATION

    +
    AI_FORMATION:FormationLeftWing(XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationLeftWing Trigger for AI_FORMATION

    +
    AI_FORMATION:FormationRightLine(XStart, YStart, ZStart, ZSpace) +

    FormationRightLine Trigger for AI_FORMATION

    +
    AI_FORMATION:FormationRightWing(XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationRightWing Trigger for AI_FORMATION

    +
    AI_FORMATION:New(FollowUnit, FollowGroupSet, FollowName, FollowBriefing) +

    AI_FORMATION class constructor for an AI group

    +
    AI_FORMATION:OnAfterFormationLeftLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) +

    FormationLeftLine Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnAfterFormationLeftWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationLeftWing Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnAfterFormationRightLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) +

    FormationRightLine Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnAfterFormationRightWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationRightWing Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationLeftLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) +

    FormationLeftLine Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationLeftWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationLeftWing Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationRightLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) +

    FormationRightLine Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationRightWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationRightWing Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION.OptionReactionOnThreat +

    Which REACTIONONTHREAT is set to the FollowGroup.

    +
    AI_FORMATION.ReportTargets +

    If true, nearby targets are reported.

    +
    AI_FORMATION.SmokeDirectionVector + +
    AI_FORMATION:TestSmokeDirectionVector(SmokeDirection) +

    This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.

    +
    AI_FORMATION:__FormationLeftLine(Delay, XStart, YStart, ZStart, ZSpace) +

    FormationLeftLine Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:__FormationLeftWing(Delay, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationLeftWing Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:__FormationRightLine(Delay, XStart, YStart, ZStart, ZSpace) +

    FormationRightLine Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:__FormationRightWing(Delay, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationRightWing Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:onafterFormationLeftLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    AI_FORMATION:onafterFormationLeftWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    AI_FORMATION:onafterFormationRightLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    AI_FORMATION:onafterFormationRightWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    AI_FORMATION:onenterFollowing(FollowGroupSet) + +
    + +

    Type AI_FORMATION.MODE

    + + + + + + + + + +
    AI_FORMATION.MODE.FOLLOW + +
    AI_FORMATION.MODE.MISSION + +
    + +

    Type MENUPARAM

    + + + + + + + + + + + + + + + + + +
    MENUPARAM.ParamDistance + +
    MENUPARAM.ParamFunction + +
    MENUPARAM.ParamMessage + +
    MENUPARAM.ParamSelf + +
    + +

    Global(s)

    +
    +
    + + #AI_FORMATION + +AI_FORMATION + +
    +
    + +

    AI_FORMATION class, extends Fsm#FSM_SET

    + +

    The #AI_FORMATION class allows you to build large formations, make AI follow a Client#CLIENT (player) leader or a Unit#UNIT (AI) leader.

    + + + +

    AI_FORMATION construction

    + +

    Create a new SPAWN object with the AI_FORMATION.New method:

    + + + +

    Initialization methods

    + +

    The following menus are created within the RADIO MENU of an active unit hosted by a player:

    + + + + + +

    Usage:

    +
    -- Declare a new FollowPlanes object as follows:
    +
    +-- First find the GROUP object and the CLIENT object.
    +local FollowUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor.
    +local FollowGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Follow Client.
    +
    +-- Now use these 2 objects to construct the new FollowPlanes object.
    +FollowPlanes = AI_FORMATION:New( FollowUnit, FollowGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
    +
    + +
    +
    +

    Type AI_Follow

    + +

    Type AI_FORMATION

    + +

    AI_FORMATION class

    + +

    Field(s)

    +
    +
    + + #number + +AI_FORMATION.FollowDistance + +
    +
    + +

    The current follow distance.

    + +
    +
    +
    +
    + + Set#SET_GROUP + +AI_FORMATION.FollowGroupSet + +
    +
    + + + +
    +
    +
    +
    + + Menu#MENU_CLIENT + +AI_FORMATION.FollowMenuResumeMission + +
    +
    + + + +
    +
    +
    +
    + + #AI_FORMATION.MODE + +AI_FORMATION.FollowMode + +
    +
    + +

    The mode the escort is in.

    + +
    +
    +
    +
    + + #string + +AI_FORMATION.FollowName + +
    +
    + + + +
    +
    +
    +
    + + Scheduler#SCHEDULER + +AI_FORMATION.FollowScheduler + +
    +
    + +

    The instance of the SCHEDULER class.

    + +
    +
    +
    +
    + + Unit#UNIT + +AI_FORMATION.FollowUnit + +
    +
    + + + +
    +
    +
    +
    + + +AI_FORMATION:FormationLeftLine(XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftLine Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:FormationLeftWing(XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftWing Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:FormationRightLine(XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightLine Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:FormationRightWing(XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightWing Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:New(FollowUnit, FollowGroupSet, FollowName, FollowBriefing) + +
    +
    + +

    AI_FORMATION class constructor for an AI group

    + +

    Parameters

    +
      +
    • + +

      Unit#UNIT FollowUnit : +The UNIT leading the FolllowGroupSet.

      + +
    • +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string FollowName : +Name of the escort.

      + +
    • +
    • + +

      FollowBriefing :

      + +
    • +
    +

    Return value

    + +

    #AI_FORMATION: +self

    + +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationLeftLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftLine Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationLeftWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftWing Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationRightLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightLine Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationRightWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightWing Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationLeftLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftLine Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationLeftWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftWing Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationRightLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightLine Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationRightWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightWing Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT + +AI_FORMATION.OptionReactionOnThreat + +
    +
    + +

    Which REACTIONONTHREAT is set to the FollowGroup.

    + +
    +
    +
    +
    + + #boolean + +AI_FORMATION.ReportTargets + +
    +
    + +

    If true, nearby targets are reported.

    + +
    +
    +
    +
    + + +AI_FORMATION.SmokeDirectionVector + +
    +
    + + + +
    +
    +
    +
    + + +AI_FORMATION:TestSmokeDirectionVector(SmokeDirection) + +
    +
    + +

    This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.

    + + +

    This allows to visualize where the escort is flying to.

    + +

    Parameter

    +
      +
    • + +

      #boolean SmokeDirection : +If true, then the direction vector will be smoked.

      + +
    • +
    +

    Return value

    + +

    #AI_FORMATION:

    + + +
    +
    +
    +
    + + +AI_FORMATION:__FormationLeftLine(Delay, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftLine Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:__FormationLeftWing(Delay, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftWing Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:__FormationRightLine(Delay, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightLine Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:__FormationRightWing(Delay, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightWing Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationLeftLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      FollowGroupSet :

      + +
    • +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      XStart :

      + +
    • +
    • + +

      YStart :

      + +
    • +
    • + +

      ZStart :

      + +
    • +
    • + +

      ZSpace :

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationLeftWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      FollowGroupSet :

      + +
    • +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      XStart :

      + +
    • +
    • + +

      XSpace :

      + +
    • +
    • + +

      YStart :

      + +
    • +
    • + +

      ZStart :

      + +
    • +
    • + +

      ZSpace :

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationRightLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      FollowGroupSet :

      + +
    • +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      XStart :

      + +
    • +
    • + +

      YStart :

      + +
    • +
    • + +

      ZStart :

      + +
    • +
    • + +

      ZSpace :

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationRightWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      FollowGroupSet :

      + +
    • +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      XStart :

      + +
    • +
    • + +

      XSpace :

      + +
    • +
    • + +

      YStart :

      + +
    • +
    • + +

      ZStart :

      + +
    • +
    • + +

      ZSpace :

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:onenterFollowing(FollowGroupSet) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      FollowGroupSet :

      + +
    • +
    +
    +
    + +

    Type AI_FORMATION.MODE

    + +

    AI_FORMATION.Mode class

    + +

    Field(s)

    +
    +
    + + #number + +AI_FORMATION.MODE.FOLLOW + +
    +
    + + + +
    +
    +
    +
    + + #number + +AI_FORMATION.MODE.MISSION + +
    +
    + + + +
    +
    + +

    Type Distance

    + +

    Type MENUPARAM

    + +

    MENUPARAM type

    + +

    Field(s)

    +
    +
    + + #Distance + +MENUPARAM.ParamDistance + +
    +
    + + + +
    +
    +
    +
    + + #function + +MENUPARAM.ParamFunction + +
    +
    + + + +
    +
    +
    +
    + + #string + +MENUPARAM.ParamMessage + +
    +
    + + + +
    +
    +
    +
    + + #AI_FORMATION + +MENUPARAM.ParamSelf + +
    +
    + + + +
    +
    + +

    Type nubmer

    + +
    + +
    + + diff --git a/docs/Documentation/AI_Formation.html b/docs/Documentation/AI_Formation.html new file mode 100644 index 000000000..76e0fbc33 --- /dev/null +++ b/docs/Documentation/AI_Formation.html @@ -0,0 +1,4103 @@ + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +

    Module AI_Formation

    + +

    AI -- Build large formations of AI Groups flying together.

    + + + +

    Banner Image

    + +
    + +

    AI_FORMATION makes AI GROUPs fly in formation of various compositions. +The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! +The purpose of the class is to:

    + +
      +
    • Make formation building a process that can be managed while in flight, rather than a task.
    • +
    • Human players can guide formations, consisting of larget planes.
    • +
    • Build large formations (like a large bomber field).
    • +
    • Form formations that DCS does not support off the shelve.
    • +
    + +

    A few remarks:

    + +
      +
    • Depending on the type of plane, the change in direction by the leader may result in the formation getting disentangled while in flight and needs to be rebuild.
    • +
    • Formations are vulnerable to collissions, but is depending on the type of plane, the distance between the planes and the speed and angle executed by the leader.
    • +
    • Formations may take a while to build up.
    • +
    + +

    As a result, the AI_FORMATION is not perfect, but is very useful to:

    + +
      +
    • Model large formations when flying straight line.
    • +
    • Make humans guide a large formation, when the planes are wide from each other.
    • +
    + +

    There are the following types of classes defined:

    + + + +
    + +

    Demo Missions

    + +

    AI_FORMATION Demo Missions source code

    + +

    AI_FORMATION Demo Missions, only for beta testers

    + +

    ALL Demo Missions pack of the last release

    + +
    + +

    YouTube Channel

    + + + +
    + +

    Author: Sven Van de Velde (FlightControl)

    + +

    Contributions:

    + +
    +

    + +

    Global(s)

    + + + + + +
    AI_FORMATION +

    AI_FORMATION class, extends Fsm#FSM_SET

    + +

    The #AI_FORMATION class allows you to build large formations, make AI follow a Client#CLIENT (player) leader or a Unit#UNIT (AI) leader.

    +
    +

    Type AI_FORMATION

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AI_FORMATION.FlightRandomization + +
    AI_FORMATION.FollowDistance +

    The current follow distance.

    +
    AI_FORMATION.FollowGroupSet + +
    AI_FORMATION.FollowMenuResumeMission + +
    AI_FORMATION.FollowMode +

    The mode the escort is in.

    +
    AI_FORMATION.FollowName + +
    AI_FORMATION.FollowScheduler +

    The instance of the SCHEDULER class.

    +
    AI_FORMATION.FollowUnit + +
    AI_FORMATION:FormationBox(XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels) +

    FormationBox Trigger for AI_FORMATION

    +
    AI_FORMATION:FormationCenterWing(XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationCenterWing Trigger for AI_FORMATION

    +
    AI_FORMATION:FormationLeftLine(XStart, YStart, ZStart, ZSpace) +

    FormationLeftLine Trigger for AI_FORMATION

    +
    AI_FORMATION:FormationLeftWing(XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationLeftWing Trigger for AI_FORMATION

    +
    AI_FORMATION:FormationLine(XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationLine Trigger for AI_FORMATION

    +
    AI_FORMATION:FormationRightLine(XStart, YStart, ZStart, ZSpace) +

    FormationRightLine Trigger for AI_FORMATION

    +
    AI_FORMATION:FormationRightWing(XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationRightWing Trigger for AI_FORMATION

    +
    AI_FORMATION:FormationStack(XStart, XSpace, YStart, YSpace) +

    FormationStack Trigger for AI_FORMATION

    +
    AI_FORMATION:FormationTrail(XStart, XSpace, YStart) +

    FormationTrail Trigger for AI_FORMATION

    +
    AI_FORMATION:FormationVic(XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationVic Trigger for AI_FORMATION

    +
    AI_FORMATION:New(FollowUnit, FollowGroupSet, FollowName, FollowBriefing) +

    AI_FORMATION class constructor for an AI group

    +
    AI_FORMATION:OnAfterFormationBox(From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels) +

    FormationBox Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnAfterFormationCenterWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationCenterWing Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnAfterFormationLeftLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) +

    FormationLeftLine Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnAfterFormationLeftWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationLeftWing Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnAfterFormationLine(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationLine Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnAfterFormationRightLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) +

    FormationRightLine Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnAfterFormationRightWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationRightWing Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnAfterFormationStack(From, Event, To, XStart, XSpace, YStart, YSpace) +

    FormationStack Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnAfterFormationTrail(From, Event, To, XStart, XSpace, YStart) +

    FormationTrail Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnAfterFormationVic(From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationVic Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationBox(From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels) +

    FormationBox Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationCenterWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationCenterWing Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationLeftLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) +

    FormationLeftLine Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationLeftWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationLeftWing Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationLine(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationLine Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationRightLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) +

    FormationRightLine Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationRightWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationRightWing Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationStack(From, Event, To, XStart, XSpace, YStart, YSpace) +

    FormationStack Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationTrail(From, Event, To, XStart, XSpace, YStart) +

    FormationTrail Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION:OnBeforeFormationVic(From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationVic Handler OnBefore for AI_FORMATION

    +
    AI_FORMATION.OptionReactionOnThreat +

    Which REACTIONONTHREAT is set to the FollowGroup.

    +
    AI_FORMATION.ReportTargets +

    If true, nearby targets are reported.

    +
    AI_FORMATION:SetFlightRandomization(FlightRandomization) +

    Use the method AIFormation#AIFORMATION.SetFlightRandomization() to make the air units in your formation randomize their flight a bit while in formation.

    +
    AI_FORMATION.SmokeDirectionVector + +
    AI_FORMATION:TestSmokeDirectionVector(SmokeDirection) +

    This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.

    +
    AI_FORMATION:__FormationBox(Delay, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels) +

    FormationBox Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:__FormationCenterWing(Delay, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationCenterWing Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:__FormationLeftLine(Delay, XStart, YStart, ZStart, ZSpace) +

    FormationLeftLine Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:__FormationLeftWing(Delay, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationLeftWing Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:__FormationLine(Delay, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationLine Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:__FormationRightLine(Delay, XStart, YStart, ZStart, ZSpace) +

    FormationRightLine Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:__FormationRightWing(Delay, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationRightWing Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:__FormationStack(Delay, XStart, XSpace, YStart, YSpace) +

    FormationStack Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:__FormationTrail(Delay, XStart, XSpace, YStart) +

    FormationTrail Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:__FormationVic(Delay, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationVic Asynchronous Trigger for AI_FORMATION

    +
    AI_FORMATION:onafterFormationBox(From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels, FollowGroupSet) +

    FormationBox Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:onafterFormationCenterWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationCenterWing Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:onafterFormationLeftLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) +

    FormationLeftLine Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:onafterFormationLeftWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) +

    FormationLeftWing Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:onafterFormationLine(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) +

    FormationLine Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:onafterFormationRightLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) +

    FormationRightLine Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:onafterFormationRightWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    AI_FORMATION:onafterFormationStack(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace) +

    FormationStack Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:onafterFormationTrail(FollowGroupSet, From, Event, To, XStart, XSpace, YStart) +

    FormationTrail Handler OnAfter for AI_FORMATION

    +
    AI_FORMATION:onafterFormationVic(From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, FollowGroupSet) +

    FormationVic Handle for AI_FORMATION

    +
    AI_FORMATION:onenterFollowing(FollowGroupSet) + +
    + +

    Type AI_FORMATION.MODE

    + + + + + + + + + +
    AI_FORMATION.MODE.FOLLOW + +
    AI_FORMATION.MODE.MISSION + +
    + +

    Type MENUPARAM

    + + + + + + + + + + + + + + + + + +
    MENUPARAM.ParamDistance + +
    MENUPARAM.ParamFunction + +
    MENUPARAM.ParamMessage + +
    MENUPARAM.ParamSelf + +
    + +

    Global(s)

    +
    +
    + + #AI_FORMATION + +AI_FORMATION + +
    +
    + +

    AI_FORMATION class, extends Fsm#FSM_SET

    + +

    The #AI_FORMATION class allows you to build large formations, make AI follow a Client#CLIENT (player) leader or a Unit#UNIT (AI) leader.

    + + + +

    AI_FORMATION makes AI GROUPs fly in formation of various compositions. +The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! +The purpose of the class is to:

    + +
      +
    • Make formation building a process that can be managed while in flight, rather than a task.
    • +
    • Human players can guide formations, consisting of larget planes.
    • +
    • Build large formations (like a large bomber field).
    • +
    • Form formations that DCS does not support off the shelve.
    • +
    + +

    A few remarks:

    + +
      +
    • Depending on the type of plane, the change in direction by the leader may result in the formation getting disentangled while in flight and needs to be rebuild.
    • +
    • Formations are vulnerable to collissions, but is depending on the type of plane, the distance between the planes and the speed and angle executed by the leader.
    • +
    • Formations may take a while to build up.
    • +
    + +

    As a result, the AI_FORMATION is not perfect, but is very useful to:

    + +
      +
    • Model large formations when flying straight line. You can build close formations when doing this.
    • +
    • Make humans guide a large formation, when the planes are wide from each other.
    • +
    + +

    AI_FORMATION construction

    + +

    Create a new SPAWN object with the AI_FORMATION.New method:

    + + + +

    Formation methods

    + +

    The following methods can be used to set or change the formation:

    + + + +

    Randomization

    + +

    Use the method AIFormation#AIFORMATION.SetFlightRandomization() to simulate the formation flying errors that pilots make while in formation. Is a range set in meters.

    + + +

    Usage:

    +
    local FollowGroupSet = SET_GROUP:New():FilterCategories("plane"):FilterCoalitions("blue"):FilterPrefixes("Follow"):FilterStart()
    +FollowGroupSet:Flush()
    +local LeaderUnit = UNIT:FindByName( "Leader" )
    +local LargeFormation = AI_FORMATION:New( LeaderUnit, FollowGroupSet, "Center Wing Formation", "Briefing" )
    +LargeFormation:FormationCenterWing( 500, 50, 0, 250, 250 )
    +LargeFormation:__Start( 1 )
    +
    + +
    +
    +

    Type AI_Formation

    + +

    Type AI_FORMATION

    + +

    AI_FORMATION class

    + +

    Field(s)

    +
    +
    + + + +AI_FORMATION.FlightRandomization + +
    +
    + + + +
    +
    +
    +
    + + #number + +AI_FORMATION.FollowDistance + +
    +
    + +

    The current follow distance.

    + +
    +
    +
    +
    + + Set#SET_GROUP + +AI_FORMATION.FollowGroupSet + +
    +
    + + + +
    +
    +
    +
    + + Menu#MENU_CLIENT + +AI_FORMATION.FollowMenuResumeMission + +
    +
    + + + +
    +
    +
    +
    + + #AI_FORMATION.MODE + +AI_FORMATION.FollowMode + +
    +
    + +

    The mode the escort is in.

    + +
    +
    +
    +
    + + #string + +AI_FORMATION.FollowName + +
    +
    + + + +
    +
    +
    +
    + + Scheduler#SCHEDULER + +AI_FORMATION.FollowScheduler + +
    +
    + +

    The instance of the SCHEDULER class.

    + +
    +
    +
    +
    + + Unit#UNIT + +AI_FORMATION.FollowUnit + +
    +
    + + + +
    +
    +
    +
    + + +AI_FORMATION:FormationBox(XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels) + +
    +
    + +

    FormationBox Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    • + +

      #number ZLevels : +The amount of levels on the Z-axis.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:FormationCenterWing(XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationCenterWing Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:FormationLeftLine(XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftLine Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:FormationLeftWing(XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftWing Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:FormationLine(XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationLine Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:FormationRightLine(XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightLine Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:FormationRightWing(XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightWing Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:FormationStack(XStart, XSpace, YStart, YSpace) + +
    +
    + +

    FormationStack Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:FormationTrail(XStart, XSpace, YStart) + +
    +
    + +

    FormationTrail Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:FormationVic(XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationVic Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:New(FollowUnit, FollowGroupSet, FollowName, FollowBriefing) + +
    +
    + +

    AI_FORMATION class constructor for an AI group

    + +

    Parameters

    +
      +
    • + +

      Unit#UNIT FollowUnit : +The UNIT leading the FolllowGroupSet.

      + +
    • +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string FollowName : +Name of the escort.

      + +
    • +
    • + +

      FollowBriefing :

      + +
    • +
    +

    Return value

    + +

    #AI_FORMATION: +self

    + +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationBox(From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels) + +
    +
    + +

    FormationBox Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    • + +

      #number ZLevels : +The amount of levels on the Z-axis.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationCenterWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationCenterWing Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationLeftLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftLine Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationLeftWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftWing Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationLine(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationLine Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationRightLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightLine Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationRightWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightWing Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationStack(From, Event, To, XStart, XSpace, YStart, YSpace) + +
    +
    + +

    FormationStack Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationTrail(From, Event, To, XStart, XSpace, YStart) + +
    +
    + +

    FormationTrail Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnAfterFormationVic(From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationVic Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationBox(From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels) + +
    +
    + +

    FormationBox Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    • + +

      #number ZLevels : +The amount of levels on the Z-axis.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationCenterWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationCenterWing Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationLeftLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftLine Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationLeftWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftWing Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationLine(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationLine Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationRightLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightLine Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationRightWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightWing Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationStack(From, Event, To, XStart, XSpace, YStart, YSpace) + +
    +
    + +

    FormationStack Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationTrail(From, Event, To, XStart, XSpace, YStart) + +
    +
    + +

    FormationTrail Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +AI_FORMATION:OnBeforeFormationVic(From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationVic Handler OnBefore for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT + +AI_FORMATION.OptionReactionOnThreat + +
    +
    + +

    Which REACTIONONTHREAT is set to the FollowGroup.

    + +
    +
    +
    +
    + + #boolean + +AI_FORMATION.ReportTargets + +
    +
    + +

    If true, nearby targets are reported.

    + +
    +
    +
    +
    + + +AI_FORMATION:SetFlightRandomization(FlightRandomization) + +
    +
    + +

    Use the method AIFormation#AIFORMATION.SetFlightRandomization() to make the air units in your formation randomize their flight a bit while in formation.

    + +

    Parameter

    +
      +
    • + +

      #number FlightRandomization : +The formation flying errors that pilots can make while in formation. Is a range set in meters.

      + +
    • +
    +

    Return value

    + +

    #AI_FORMATION:

    + + +
    +
    +
    +
    + + +AI_FORMATION.SmokeDirectionVector + +
    +
    + + + +
    +
    +
    +
    + + +AI_FORMATION:TestSmokeDirectionVector(SmokeDirection) + +
    +
    + +

    This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.

    + + +

    This allows to visualize where the escort is flying to.

    + +

    Parameter

    +
      +
    • + +

      #boolean SmokeDirection : +If true, then the direction vector will be smoked.

      + +
    • +
    +

    Return value

    + +

    #AI_FORMATION:

    + + +
    +
    +
    +
    + + +AI_FORMATION:__FormationBox(Delay, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels) + +
    +
    + +

    FormationBox Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    • + +

      #number ZLevels : +The amount of levels on the Z-axis.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:__FormationCenterWing(Delay, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationCenterWing Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:__FormationLeftLine(Delay, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftLine Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:__FormationLeftWing(Delay, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftWing Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:__FormationLine(Delay, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationLine Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:__FormationRightLine(Delay, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightLine Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:__FormationRightWing(Delay, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightWing Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:__FormationStack(Delay, XStart, XSpace, YStart, YSpace) + +
    +
    + +

    FormationStack Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:__FormationTrail(Delay, XStart, XSpace, YStart) + +
    +
    + +

    FormationTrail Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:__FormationVic(Delay, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationVic Asynchronous Trigger for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #number Delay :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationBox(From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels, FollowGroupSet) + +
    +
    + +

    FormationBox Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    • + +

      #number ZLevels : +The amount of levels on the Z-axis.

      + +
    • +
    • + +

      FollowGroupSet :

      + +
    • +
    +

    Return value

    + +

    #AI_FORMATION:

    + + +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationCenterWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationCenterWing Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationLeftLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftLine Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #AI_FORMATION:

    + + +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationLeftWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationLeftWing Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationLine(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace) + +
    +
    + +

    FormationLine Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #AI_FORMATION:

    + + +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationRightLine(FollowGroupSet, From, Event, To, XStart, YStart, ZStart, ZSpace) + +
    +
    + +

    FormationRightLine Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #AI_FORMATION:

    + + +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationRightWing(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, ZStart, ZSpace) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      FollowGroupSet :

      + +
    • +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      XStart :

      + +
    • +
    • + +

      XSpace :

      + +
    • +
    • + +

      YStart :

      + +
    • +
    • + +

      ZStart :

      + +
    • +
    • + +

      ZSpace :

      + +
    • +
    +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationStack(FollowGroupSet, From, Event, To, XStart, XSpace, YStart, YSpace) + +
    +
    + +

    FormationStack Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    +

    Return value

    + +

    #AI_FORMATION:

    + + +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationTrail(FollowGroupSet, From, Event, To, XStart, XSpace, YStart) + +
    +
    + +

    FormationTrail Handler OnAfter for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      Core.Set#SET_GROUP FollowGroupSet : +The group AI escorting the FollowUnit.

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    +

    Return value

    + +

    #AI_FORMATION:

    + + +
    +
    +
    +
    + + +AI_FORMATION:onafterFormationVic(From, Event, To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, FollowGroupSet) + +
    +
    + +

    FormationVic Handle for AI_FORMATION

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      #number XStart : +The start position on the X-axis in meters for the first group.

      + +
    • +
    • + +

      #number XSpace : +The space between groups on the X-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer YStart : +The start position on the Y-axis in meters for the first group.

      + +
    • +
    • + +

      #number YSpace : +The space between groups on the Y-axis in meters for each sequent group.

      + +
    • +
    • + +

      #nubmer ZStart : +The start position on the Z-axis in meters for the first group.

      + +
    • +
    • + +

      #number ZSpace : +The space between groups on the Z-axis in meters for each sequent group.

      + +
    • +
    • + +

      FollowGroupSet :

      + +
    • +
    +

    Return value

    + +

    #AI_FORMATION:

    + + +
    +
    +
    +
    + + +AI_FORMATION:onenterFollowing(FollowGroupSet) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      FollowGroupSet :

      + +
    • +
    +
    +
    + +

    Type AI_FORMATION.MODE

    + +

    AI_FORMATION.Mode class

    + +

    Field(s)

    +
    +
    + + #number + +AI_FORMATION.MODE.FOLLOW + +
    +
    + + + +
    +
    +
    +
    + + #number + +AI_FORMATION.MODE.MISSION + +
    +
    + + + +
    +
    + +

    Type Distance

    + +

    Type MENUPARAM

    + +

    MENUPARAM type

    + +

    Field(s)

    +
    +
    + + #Distance + +MENUPARAM.ParamDistance + +
    +
    + + + +
    +
    +
    +
    + + #function + +MENUPARAM.ParamFunction + +
    +
    + + + +
    +
    +
    +
    + + #string + +MENUPARAM.ParamMessage + +
    +
    + + + +
    +
    +
    +
    + + #AI_FORMATION + +MENUPARAM.ParamSelf + +
    +
    + + + +
    +
    + +

    Type nubmer

    + +
    + +
    + + diff --git a/docs/Documentation/AI_Patrol.html b/docs/Documentation/AI_Patrol.html index 20cd2b005..0892b0b07 100644 --- a/docs/Documentation/AI_Patrol.html +++ b/docs/Documentation/AI_Patrol.html @@ -17,9 +17,16 @@ index
    @@ -91,38 +123,23 @@
    -

    OPEN ISSUES

    +

    Demo Missions

    -

    2017-01-17: When Spawned AI is located at an airbase, it will be routed first back to the airbase after take-off.

    +

    AI_PATROL Demo Missions source code

    -

    2016-01-17: - -- Fixed problem with AI returning to base too early and unexpected. - -- ReSpawning of AI will reset the AI_PATROL and derived classes. - -- Checked the correct workings of SCHEDULER, and it DOES work correctly.

    +

    AI_PATROL Demo Missions, only for beta testers

    + +

    ALL Demo Missions pack of the last release


    -

    API CHANGE HISTORY

    +

    YouTube Channel

    -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    - -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    - -

    Hereby the change log:

    - -

    2017-01-17: Rename of class: AI_PATROL_ZONE is the new name for the old AI_PATROLZONE.

    - -

    2017-01-15: Complete revision. AIPATROLZONE is the base class for other AI_PATROL like classes.

    - -

    2016-09-01: Initial class and API.

    +

    AI_PATROL YouTube Channel


    -

    AUTHORS and CONTRIBUTIONS

    - +

    Author: Sven Van de Velde (FlightControl)

    Contributions:

      @@ -130,11 +147,7 @@
    • Pikey: Testing and API concept review.
    -

    Authors:

    - -
      -
    • FlightControl: Design & Programming.
    • -
    +

    Global(s)

    @@ -142,9 +155,9 @@ AI_PATROL_ZONE -

    1) #AIPATROLZONE class, extends Fsm#FSM_CONTROLLABLE

    +

    AIPATROLZONE class, extends Fsm#FSM_CONTROLLABLE

    -

    The #AIPATROLZONE class implements the core functions to patrol a Zone by an AI Controllable or Group.

    +

    The AIPATROLZONE class implements the core functions to patrol a Zone by an AI Controllable or Group.

    @@ -223,13 +236,13 @@ - AI_PATROL_ZONE:ManageDamage(PatrolDamageTreshold) + AI_PATROL_ZONE:ManageDamage(PatrolDamageThreshold)

    When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base.

    - AI_PATROL_ZONE:ManageFuel(PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime) + AI_PATROL_ZONE:ManageFuel(PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime)

    When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.

    @@ -391,7 +404,7 @@ - AI_PATROL_ZONE.PatrolDamageTreshold + AI_PATROL_ZONE.PatrolDamageThreshold @@ -403,7 +416,7 @@ - AI_PATROL_ZONE.PatrolFuelTresholdPercentage + AI_PATROL_ZONE.PatrolFuelThresholdPercentage @@ -472,12 +485,6 @@ AI_PATROL_ZONE:SetDetectionDeactivated()

    Deactivate the detection.

    - - - - AI_PATROL_ZONE:SetDetectionInterval(Seconds) - -

    Set the interval in seconds between each detection executed by the AI.

    @@ -496,6 +503,12 @@ AI_PATROL_ZONE:SetDetectionZone(DetectionZone)

    Set the detection zone where the AI is detecting targets.

    + + + + AI_PATROL_ZONE:SetRefreshTimeInterval(Seconds) + +

    Set the interval in seconds between each detection executed by the AI.

    @@ -637,9 +650,9 @@
    -

    1) #AIPATROLZONE class, extends Fsm#FSM_CONTROLLABLE

    +

    AIPATROLZONE class, extends Fsm#FSM_CONTROLLABLE

    -

    The #AIPATROLZONE class implements the core functions to patrol a Zone by an AI Controllable or Group.

    +

    The AIPATROLZONE class implements the core functions to patrol a Zone by an AI Controllable or Group.

    @@ -672,17 +685,17 @@ When the fuel treshold has been reached, the airplane will fly towards the neare

    Process

    -

    1.1) AIPATROLZONE constructor

    +

    1. AIPATROLZONE constructor

    -

    1.2) AIPATROLZONE is a FSM

    +

    2. AIPATROLZONE is a FSM

    Process

    -

    1.2.1) AIPATROLZONE States

    +

    2.1. AIPATROLZONE States

    • None ( Group ): The process is not started yet.
    • @@ -692,7 +705,7 @@ When the fuel treshold has been reached, the airplane will fly towards the neare
    • Crashed ( Group ): The AI has crashed or is dead.
    -

    1.2.2) AIPATROLZONE Events

    +

    2.2. AIPATROLZONE Events

    • Start ( Group ): Start the process.
    • @@ -704,21 +717,21 @@ When the fuel treshold has been reached, the airplane will fly towards the neare
    • Status ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
    -

    1.3) Set or Get the AI controllable

    +

    3. Set or Get the AI controllable

    -

    1.4) Set the Speed and Altitude boundaries of the AI controllable

    +

    4. Set the Speed and Altitude boundaries of the AI controllable

    -

    1.5) Manage the detection process of the AI controllable

    +

    5. Manage the detection process of the AI controllable

    The detection process of the AI controllable can be manipulated. Detection requires an amount of CPU power, which has an impact on your mission performance. @@ -729,7 +742,7 @@ Only put detection on when absolutely necessary, and the frequency of the detect

  • AIPATROLZONE.SetDetectionOff(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
  • -

    The detection frequency can be set with AIPATROLZONE.SetDetectionInterval( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. +

    The detection frequency can be set with AIPATROLZONE.SetRefreshTimeInterval( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. Use the method AIPATROLZONE.GetDetectedUnits() to obtain a list of the Units detected by the AI.

    The detection can be filtered to potential targets in a specific zone. @@ -737,7 +750,7 @@ Use the method AIPATROLZO Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected according the weather conditions.

    -

    1.6) Manage the "out of fuel" in the AIPATROLZONE

    +

    6. Manage the "out of fuel" in the AIPATROLZONE

    When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. @@ -746,7 +759,7 @@ while a new AI is targetted to the AIPATROLZONE. Once the time is finished, the old AI will return to the base. Use the method AIPATROLZONE.ManageFuel() to have this proces in place.

    -

    1.7) Manage "damage" behaviour of the AI in the AIPATROLZONE

    +

    7. Manage "damage" behaviour of the AI in the AIPATROLZONE

    When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on. Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). @@ -944,7 +957,7 @@ The list of Unit#UNITs

    -AI_PATROL_ZONE:ManageDamage(PatrolDamageTreshold) +AI_PATROL_ZONE:ManageDamage(PatrolDamageThreshold)
    @@ -962,7 +975,7 @@ So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold
    @@ -191,6 +223,18 @@ ACT_ACCOUNT_DEADS:New(TargetSetUnit, TaskName)

    Creates a new DESTROY process.

    + + + + ACT_ACCOUNT_DEADS:OnEventHit(EventData) + + + + + + ACT_ACCOUNT_DEADS.PlayerHits + + @@ -200,19 +244,19 @@ - ACT_ACCOUNT_DEADS.TaskName - - - - - - ACT_ACCOUNT_DEADS:onafterEvent(ProcessUnit, Event, From, To, Task) + ACT_ACCOUNT_DEADS:onafterEvent(ProcessClient, Task, From, Event, To, EventData)

    StateMachine callback function

    - ACT_ACCOUNT_DEADS:onenterAccount(ProcessUnit, Event, From, To, Task, EventData) + ACT_ACCOUNT_DEADS:onenterAccountForOther(ProcessClient, Task, From, Event, To, EventData) + +

    StateMachine callback function

    + + + + ACT_ACCOUNT_DEADS:onenterAccountForPlayer(ProcessClient, Task, From, Event, To, EventData)

    StateMachine callback function

    @@ -221,6 +265,12 @@ ACT_ACCOUNT_DEADS:onenterReport(ProcessUnit, Event, From, To, Task)

    StateMachine callback function

    + + + + ACT_ACCOUNT_DEADS:onfuncEventCrash(EventData) + + @@ -642,6 +692,40 @@ Each successful dead will trigger an Account state transition that can be scored + + +
    +
    + + +ACT_ACCOUNT_DEADS:OnEventHit(EventData) + +
    +
    + + + +

    Parameter

    + +
    +
    +
    +
    + + +ACT_ACCOUNT_DEADS.PlayerHits + +
    +
    + + +
    @@ -656,27 +740,13 @@ Each successful dead will trigger an Account state transition that can be scored - -
    -
    -
    - - - -ACT_ACCOUNT_DEADS.TaskName - -
    -
    - - -
    -ACT_ACCOUNT_DEADS:onafterEvent(ProcessUnit, Event, From, To, Task) +ACT_ACCOUNT_DEADS:onafterEvent(ProcessClient, Task, From, Event, To, EventData)
    @@ -687,12 +757,12 @@ Each successful dead will trigger an Account state transition that can be scored @@ -716,8 +791,8 @@ Each successful dead will trigger an Account state transition that can be scored
    - -ACT_ACCOUNT_DEADS:onenterAccount(ProcessUnit, Event, From, To, Task, EventData) + +ACT_ACCOUNT_DEADS:onenterAccountForOther(ProcessClient, Task, From, Event, To, EventData)
    @@ -728,12 +803,12 @@ Each successful dead will trigger an Account state transition that can be scored +
    +
    +
    +
    + + +ACT_ACCOUNT_DEADS:onenterAccountForPlayer(ProcessClient, Task, From, Event, To, EventData) + +
    +
    + +

    StateMachine callback function

    + +

    Parameters

    + @@ -803,6 +924,27 @@ Each successful dead will trigger an Account state transition that can be scored
    + +ACT_ACCOUNT_DEADS:onfuncEventCrash(EventData) + +
    +
    + + + +

    Parameter

    + +
    +
    +
    +
    + ACT_ACCOUNT_DEADS:onfuncEventDead(EventData) diff --git a/docs/Documentation/Airbase.html b/docs/Documentation/Airbase.html index 328b41564..d176bb9fc 100644 --- a/docs/Documentation/Airbase.html +++ b/docs/Documentation/Airbase.html @@ -17,9 +17,16 @@ index

    Module Airbase

    -

    This module contains the AIRBASE classes.

    +

    Wrapper -- AIRBASE is a wrapper class to handle the DCS Airbase objects.


    -

    1) Airbase#AIRBASE class, extends Positionable#POSITIONABLE

    -

    The AIRBASE class is a wrapper class to handle the DCS Airbase objects:

    +

    Author: Sven Van de Velde (FlightControl)

    -
      -
    • Support all DCS Airbase APIs.
    • -
    • Enhance with Airbase specific APIs not in the DCS Airbase API set.
    • -
    +

    Contributions:

    - -

    1.1) AIRBASE reference methods

    -

    For each DCS Airbase object alive within a running mission, a AIRBASE wrapper object (instance) will be created within the _DATABASE object. -This is done at the beginning of the mission (when the mission starts).

    - -

    The AIRBASE class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference -using the DCS Airbase or the DCS AirbaseName.

    - -

    Another thing to know is that AIRBASE objects do not "contain" the DCS Airbase object. -The AIRBASE methods will reference the DCS Airbase object by name when it is needed during API execution. -If the DCS Airbase object does not exist or is nil, the AIRBASE methods will return nil and log an exception in the DCS.log file.

    - -

    The AIRBASE class provides the following functions to retrieve quickly the relevant AIRBASE instance:

    - -
      -
    • AIRBASE.Find(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase object.
    • -
    • AIRBASE.FindByName(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase name.
    • -
    - -

    IMPORTANT: ONE SHOULD NEVER SANATIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil).

    - -

    1.2) DCS AIRBASE APIs

    -

    The DCS Airbase APIs are used extensively within MOOSE. The AIRBASE class has for each DCS Airbase API a corresponding method. -To be able to distinguish easily in your code the difference between a AIRBASE API call and a DCS Airbase API call, -the first letter of the method is also capitalized. So, by example, the DCS Airbase method DCSWrapper.Airbase#Airbase.getName() -is implemented in the AIRBASE class as AIRBASE.GetName().

    - -

    More functions will be added

    -

    During the MOOSE development, more functions will be added.

    +

    Global(s)

    @@ -123,20 +123,20 @@ is implemented in the AIRBASE class as AIRBASE.Get AIRBASE +

    AIRBASE class, extends Positionable#POSITIONABLE

    +

    AIRBASE is a wrapper class to handle the DCS Airbase objects:

    + +
      +
    • Support all DCS Airbase APIs.
    • +

    Type AIRBASE

    - - - - - + @@ -157,6 +157,24 @@ is implemented in the AIRBASE class as AIRBASE.Get + + + + + + + + + + + + @@ -178,6 +196,46 @@ is implemented in the AIRBASE class as AIRBASE.Get
    +

    AIRBASE class, extends Positionable#POSITIONABLE

    + +

    AIRBASE is a wrapper class to handle the DCS Airbase objects:

    + +
      +
    • Support all DCS Airbase APIs.
    • +
    + + +
      +
    • Enhance with Airbase specific APIs not in the DCS Airbase API set.
    • +
    + +

    AIRBASE reference methods

    + +

    For each DCS Airbase object alive within a running mission, a AIRBASE wrapper object (instance) will be created within the _DATABASE object. +This is done at the beginning of the mission (when the mission starts).

    + +

    The AIRBASE class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference +using the DCS Airbase or the DCS AirbaseName.

    + +

    Another thing to know is that AIRBASE objects do not "contain" the DCS Airbase object. +The AIRBASE methods will reference the DCS Airbase object by name when it is needed during API execution. +If the DCS Airbase object does not exist or is nil, the AIRBASE methods will return nil and log an exception in the DCS.log file.

    + +

    The AIRBASE class provides the following functions to retrieve quickly the relevant AIRBASE instance:

    + +
      +
    • AIRBASE.Find(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase object.
    • +
    • AIRBASE.FindByName(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase name.
    • +
    + +

    IMPORTANT: ONE SHOULD NEVER SANATIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil).

    + +

    DCS Airbase APIs

    + +

    The DCS Airbase APIs are used extensively within MOOSE. The AIRBASE class has for each DCS Airbase API a corresponding method. +To be able to distinguish easily in your code the difference between a AIRBASE API call and a DCS Airbase API call, +the first letter of the method is also capitalized. So, by example, the DCS Airbase method DCSWrapper.Airbase#Airbase.getName() +is implemented in the AIRBASE class as AIRBASE.GetName().

    @@ -185,30 +243,13 @@ is implemented in the AIRBASE class as AIRBASE.Get

    Type Airbase

    Type AIRBASE

    - -

    The AIRBASE class

    - -

    Field(s)

    +

    Field(s)

    - -AIRBASE.CategoryName - -
    -
    - - - -
    -
    -
    -
    - - #string - -AIRBASE.ClassName + +AIRBASE.Caucasus
    @@ -282,6 +323,52 @@ self

    +
    +
    +
    +
    + + +AIRBASE:GetZone() + +
    +
    + +

    Get the airbase zone.

    + +

    Return value

    + +

    Core.Zone#ZONE_RADIUS: +The zone radius of the airbase.

    + +
    +
    +
    +
    + + + +AIRBASE.Nevada + +
    +
    + + + +
    +
    +
    +
    + + + +AIRBASE.Normandy + +
    +
    + + +
    diff --git a/docs/Documentation/AirbasePolice.html b/docs/Documentation/AirbasePolice.html index 93fdd0ae2..218a7d247 100644 --- a/docs/Documentation/AirbasePolice.html +++ b/docs/Documentation/AirbasePolice.html @@ -17,9 +17,16 @@ index

    Module AirbasePolice

    -

    This module contains the AIRBASEPOLICE classes.

    +

    Functional -- This module monitors airbases traffic.

    diff --git a/docs/Documentation/Assign.html b/docs/Documentation/Assign.html index e352ed51e..d96b1fe01 100644 --- a/docs/Documentation/Assign.html +++ b/docs/Documentation/Assign.html @@ -17,9 +17,16 @@ index
    diff --git a/docs/Documentation/Base.html b/docs/Documentation/Base.html index 6a32f5c84..01653547f 100644 --- a/docs/Documentation/Base.html +++ b/docs/Documentation/Base.html @@ -17,9 +17,16 @@ index

    Module Base

    -

    Core - BASE forms the basis of the MOOSE framework.

    +

    Core -- BASE forms the basis of the MOOSE framework.

    Each class within the MOOSE framework derives from BASE.

    @@ -82,39 +114,10 @@
    -

    The #BASE class is the core root class from where every other class in moose is derived.

    - -
    - -

    API CHANGE HISTORY

    - -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    - -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    - -

    YYYY-MM-DD: CLASS:NewFunction( Params ) replaces CLASS:OldFunction( Params ) -YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - -

    Hereby the change log:

    - -
    - -

    AUTHORS and CONTRIBUTIONS

    - +

    Author: Sven Van de Velde (FlightControl)

    Contributions:

    -
      -
    • None.
    • -
    - -

    Authors:

    - -
      -
    • FlightControl: Design & Programming
    • -
    +

    Global(s)

    @@ -239,7 +242,7 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - + @@ -254,6 +257,12 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    + + + + @@ -290,6 +299,12 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    + + + + @@ -463,30 +478,24 @@ When Moose is loaded statically, (as one file), tracing is switched off by defau - - - - - - - - + + + + @@ -503,6 +512,12 @@ When Moose is loaded statically, (as one file), tracing is switched off by defau + + + +
    AIRBASE.CategoryName - -
    AIRBASE.ClassNameAIRBASE.Caucasus AIRBASE:GetDCSObject() +
    AIRBASE:GetZone() +

    Get the airbase zone.

    +
    AIRBASE.Nevada + +
    AIRBASE.Normandy +
    BASE:GetState(Object, Key, Value)BASE:GetState(Object, Key)

    Get a Value given a Key from the Object.

    BASE:Inherit(Child, Parent)

    This is the worker method to inherit from a parent class.

    +
    BASE:IsInstanceOf(ClassName) +

    This is the worker method to check if an object is an (sub)instance of a class.

    BASE:OnEventCrash(EventData)

    Occurs when any aircraft crashes into the ground and is completely destroyed.

    +
    BASE:OnEventDead(EventData) +

    Occurs when an object is dead.

    BASE:UnHandleEvent(Event)

    UnSubscribe to a DCS event.

    -
    BASE:_Destructor() -
    BASE:_F(Arguments, DebugInfoCurrentParam, DebugInfoFromParam)

    Trace a function call.

    -
    BASE:_SetDestructor() -
    BASE:_T(Arguments, DebugInfoCurrentParam, DebugInfoFromParam)

    Trace a function logic.

    +
    BASE.__ +
    FORMATION.Cone

    A cone formation.

    +
    FORMATION.Vee +
    @@ -1116,7 +1131,7 @@ is the Child class from which the Parent class needs to be retrieved.

    -BASE:GetState(Object, Key, Value) +BASE:GetState(Object, Key)
    @@ -1139,12 +1154,6 @@ The object that holds the Value set by the Key.

    Key : The key that is used to retrieve the value. Note that the key can be a #string, but it can also be any other type!

    - -
  • - -

    Value : -The value to is stored in the Object.

    -
  • Return value

    @@ -1217,6 +1226,46 @@ is the Parent class that the Child inherits from.

    #BASE: Child

    +
    + +
    +
    + + +BASE:IsInstanceOf(ClassName) + +
    +
    + +

    This is the worker method to check if an object is an (sub)instance of a class.

    + + + +

    Examples:

    + +
      +
    • ZONE:New( 'some zone' ):IsInstanceOf( ZONE ) will return true

    • +
    • ZONE:New( 'some zone' ):IsInstanceOf( 'ZONE' ) will return true

    • +
    • ZONE:New( 'some zone' ):IsInstanceOf( 'zone' ) will return true

    • +
    • ZONE:New( 'some zone' ):IsInstanceOf( 'BASE' ) will return true

    • +
    • ZONE:New( 'some zone' ):IsInstanceOf( 'GROUP' ) will return false

    • +
    + + +

    Parameter

    +
      +
    • + +

      ClassName : +is the name of the class or the class itself to run the check against

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + +
    @@ -1370,6 +1419,31 @@ The EventData structure.

    + +BASE:OnEventDead(EventData) + +
    +
    + +

    Occurs when an object is dead.

    + + +

    initiator : The unit that is dead.

    + +

    Parameter

    + +
    +
    +
    +
    + BASE:OnEventEjection(EventData) @@ -2100,19 +2174,6 @@ BASE:TraceOnOff( false )

    #BASE:

    - -
    -
    -
    - - -BASE:_Destructor() - -
    -
    - - -
    @@ -2153,22 +2214,6 @@ A #table or any field.

    - -BASE:_SetDestructor() - -
    -
    - - - - -

    THIS IS WHY WE NEED LUA 5.2 ...

    - -
    -
    -
    -
    - BASE:_T(Arguments, DebugInfoCurrentParam, DebugInfoFromParam) @@ -2196,6 +2241,20 @@ A #table or any field.

    + +
    +
    +
    + + #BASE.__ + +BASE.__ + +
    +
    + + +
    @@ -2226,6 +2285,8 @@ A #table or any field.

    +

    Type BASE.__

    +

    Type FORMATION

    The Formation Class

    @@ -2242,6 +2303,20 @@ A #table or any field.

    A cone formation.

    + +
    +
    +
    + + #string + +FORMATION.Vee + +
    +
    + + +
    diff --git a/docs/Documentation/Cargo.html b/docs/Documentation/Cargo.html index d0b6aea75..a11fd4dba 100644 --- a/docs/Documentation/Cargo.html +++ b/docs/Documentation/Cargo.html @@ -17,9 +17,16 @@ index

    Module Cargo

    -

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Ground --
    -Management of logical cargo objects, that can be transported from and to transportation carriers.

    +

    Core -- Management of CARGO logistics, that can be transported from and to transportation carriers.

    -

    Banner Image

    + + +

    Banner Image


    Cargo can be of various forms, always are composed out of ONE object ( one unit or one static or one slingload crate ):

      -
    • AICARGOUNIT, represented by a Unit in a Group: Cargo can be represented by a Unit in a Group.
    • +
    • CARGO_UNIT, represented by a Unit in a singleton Group: Cargo can be represented by a Unit in a Group. a CARGO_UNIT is representable...
    • +
    • CARGO_GROUP, represented by a Group. A CARGO_GROUP is reportable...
    - -

    Destruction of the Unit will mean that the cargo is lost. - * CARGO_STATIC, represented by a Static: Cargo can be represented by a Static. Destruction of the Static will mean that the cargo is lost. - * AICARGOPACKAGE, contained in a Unit of a Group: Cargo can be contained within a Unit of a Group. The cargo can be delivered by the Unit. If the Unit is destroyed, the cargo will be destroyed also. - * AICARGOPACKAGE, Contained in a Static: Cargo can be contained within a Static. The cargo can be collected from the @Static. If the Static is destroyed, the cargo will be destroyed. - * CARGO_SLINGLOAD, represented by a Cargo that is transportable: Cargo can be represented by a Cargo object that is transportable. Destruction of the Cargo will mean that the cargo is lost.

    +

    This module is still under construction, but is described above works already, and will keep working ...

    + +
    + +

    Demo Missions

    + +

    CARGO Demo Missions source code

    + +

    CARGO Demo Missions, only for beta testers

    + +

    ALL Demo Missions pack of the last release

    + +
    + +

    YouTube Channel

    + +

    CARGO YouTube Channel

    + +
    + +

    Author: Sven Van de Velde (FlightControl)

    +

    Contributions:

    + +
    + + +

    Global(s)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CARGO +

    (R2.1) CARGO class, extends Fsm#FSM_PROCESS

    + +

    The CARGO class defines the core functions that defines a cargo object within MOOSE.

    +
    CARGOS + +
    CARGO_GROUP +

    CARGO_GROUP class

    + +

    The CARGO_GROUP class defines a cargo that is represented by a Group object within the simulator, and can be transported by a carrier.

    +
    CARGO_PACKAGE + +
    CARGO_REPORTABLE + +
    CARGO_REPRESENTABLE + +
    CARGO_UNIT +

    CARGO_UNIT class, extends #CARGO_REPRESENTABLE

    + +

    The CARGO_UNIT class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.

    +
    +

    Type CARGO

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CARGO:Board(ToCarrier, NearRadius) +

    Boards the cargo to a Carrier.

    +
    CARGO.CargoCarrier +

    The alive DCS object carrying the cargo. This value can be nil, meaning, that the cargo is not contained anywhere...

    +
    CARGO.CargoObject +

    The alive DCS object representing the cargo. This value can be nil, meaning, that the cargo is not represented anywhere...

    +
    CARGO.CargoScheduler + +
    CARGO.Containable +

    This flag defines if the cargo can be contained within a DCS Unit.

    +
    CARGO.Deployed + +
    CARGO:Destroy() +

    Destroy the cargo.

    +
    CARGO:Flare(FlareColor) +

    Signal a flare at the position of the CARGO.

    +
    CARGO:FlareGreen() +

    Signal a green flare at the position of the CARGO.

    +
    CARGO:FlareRed() +

    Signal a red flare at the position of the CARGO.

    +
    CARGO:FlareWhite() +

    Signal a white flare at the position of the CARGO.

    +
    CARGO:FlareYellow() +

    Signal a yellow flare at the position of the CARGO.

    +
    CARGO:GetCoordinate() +

    Get the current coordinates of the Cargo.

    +
    CARGO:GetName() +

    Get the name of the Cargo.

    +
    CARGO:GetObjectName() +

    Get the object name of the Cargo.

    +
    CARGO:GetPointVec2() +

    Get the current PointVec2 of the cargo.

    +
    CARGO:GetType() +

    Get the type of the Cargo.

    +
    CARGO:IsAlive() +

    Check if cargo is alive.

    +
    CARGO:IsDeployed() +

    Is the cargo deployed

    +
    CARGO:IsDestroyed() +

    Check if cargo is destroyed.

    +
    CARGO:IsInZone(Zone) +

    Check if Cargo is the given Zone.

    +
    CARGO:IsLoaded() +

    Check if cargo is loaded.

    +
    CARGO:IsNear(PointVec2, NearRadius) +

    Check if CargoCarrier is near the Cargo to be Loaded.

    +
    CARGO:IsUnLoaded() +

    Check if cargo is unloaded.

    +
    CARGO:Load(ToCarrier) +

    Loads the cargo to a Carrier.

    +
    CARGO.Moveable +

    This flag defines if the cargo is moveable.

    +
    CARGO.Name +

    A string defining the name of the cargo. The name is the unique identifier of the cargo.

    +
    CARGO.NearRadius +

    (optional) A number defining the radius in meters when the cargo is near to a Carrier, so that it can be loaded.

    +
    CARGO:New(Type, Name, Weight, NearRadius) +

    CARGO Constructor.

    +
    CARGO:OnEnterBoarding(Controllable, NearRadius) + +
    CARGO:OnEnterLoaded(Controllable) + +
    CARGO:OnEnterUnBoarding(Controllable) + +
    CARGO:OnEnterUnLoaded(Controllable) + +
    CARGO:OnLeaveBoarding(Controllable) + +
    CARGO:OnLeaveLoaded(Controllable) + +
    CARGO:OnLeaveUnBoarding(Controllable) + +
    CARGO:OnLeaveUnLoaded(Controllable) + +
    CARGO.Representable +

    This flag defines if the cargo can be represented by a DCS Unit.

    +
    CARGO:SetDeployed(Deployed) +

    Set the cargo as deployed

    +
    CARGO:SetWeight(Weight) +

    Set the weight of the cargo.

    +
    CARGO.Slingloadable +

    This flag defines if the cargo can be slingloaded.

    +
    CARGO:Smoke(SmokeColor, Range) +

    Smoke the CARGO.

    +
    CARGO:SmokeBlue() +

    Smoke the CARGO Blue.

    +
    CARGO:SmokeGreen() +

    Smoke the CARGO Green.

    +
    CARGO:SmokeOrange() +

    Smoke the CARGO Orange.

    +
    CARGO:SmokeRed() +

    Smoke the CARGO Red.

    +
    CARGO:SmokeWhite() +

    Smoke the CARGO White.

    +
    CARGO:Spawn(PointVec2) +

    Template method to spawn a new representation of the CARGO in the simulator.

    +
    CARGO.Type +

    A string defining the type of the cargo. eg. Engineers, Equipment, Screwdrivers.

    +
    CARGO:UnBoard(ToPointVec2) +

    UnBoards the cargo to a Carrier.

    +
    CARGO:UnLoad(ToPointVec2) +

    UnLoads the cargo to a Carrier.

    +
    CARGO.Weight +

    A number defining the weight of the cargo. The weight is expressed in kg.

    +
    CARGO:__Board(DelaySeconds, ToCarrier, NearRadius) +

    Boards the cargo to a Carrier.

    +
    CARGO:__Load(DelaySeconds, ToCarrier) +

    Loads the cargo to a Carrier.

    +
    CARGO:__UnBoard(DelaySeconds, ToPointVec2) +

    UnBoards the cargo to a Carrier.

    +
    CARGO:__UnLoad(DelaySeconds, ToPointVec2) +

    UnLoads the cargo to a Carrier.

    +
    + +

    Type CARGO_GROUP

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CARGO_GROUP.CargoCarrier + +
    CARGO_GROUP.CargoGroup + +
    CARGO_GROUP.CargoObject + +
    CARGO_GROUP:GetCount() +

    Get the amount of cargo units in the group.

    +
    CARGO_GROUP:New(CargoGroup, Type, Name, ReportRadius, NearRadius) +

    CARGO_GROUP constructor.

    +
    CARGO_GROUP:OnEventCargoDead(EventData) + +
    CARGO_GROUP:RespawnOnDestroyed(RespawnDestroyed) +

    Respawn the cargo when destroyed

    +
    CARGO_GROUP:onafterBoarding(CargoCarrier, Event, From, To, NearRadius, ...) +

    Leave Boarding State.

    +
    CARGO_GROUP:onafterUnBoarding(ToPointVec2, Event, From, To, NearRadius, ...) +

    UnBoard Event.

    +
    CARGO_GROUP:onenterBoarding(CargoCarrier, Event, From, To, NearRadius, ...) +

    Enter Boarding State.

    +
    CARGO_GROUP:onenterDestroyed() + +
    CARGO_GROUP:onenterLoaded(CargoCarrier, Event, From, To, ...) +

    Enter Loaded State.

    +
    CARGO_GROUP:onenterUnBoarding(ToPointVec2, Event, From, To, NearRadius, ...) +

    Enter UnBoarding State.

    +
    CARGO_GROUP:onenterUnLoaded(Core, Event, From, To, ToPointVec2, ...) +

    Enter UnLoaded State.

    +
    CARGO_GROUP:onleaveUnBoarding(ToPointVec2, Event, From, To, NearRadius, ...) +

    Leave UnBoarding State.

    +
    + +

    Type CARGO_PACKAGE

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CARGO_PACKAGE.CargoCarrier + +
    CARGO_PACKAGE.CargoInAir + +
    CARGO_PACKAGE.ClassName + +
    CARGO_PACKAGE:IsNear(CargoCarrier) +

    Check if CargoCarrier is near the Cargo to be Loaded.

    +
    CARGO_PACKAGE:New(CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius) +

    CARGO_PACKAGE Constructor.

    +
    CARGO_PACKAGE:onafterLoad(Event, From, To, CargoCarrier, Speed, LoadDistance, Angle) +

    Load Event.

    +
    CARGO_PACKAGE:onafterOnBoard(Event, From, To, CargoCarrier, Speed, BoardDistance, Angle, LoadDistance) +

    Board Event.

    +
    CARGO_PACKAGE:onafterOnBoarded(Event, From, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle) +

    Boarded Event.

    +
    CARGO_PACKAGE:onafterUnBoard(Event, From, To, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle, CargoCarrier) +

    UnBoard Event.

    +
    CARGO_PACKAGE:onafterUnBoarded(Event, From, To, CargoCarrier, Speed) +

    UnBoarded Event.

    +
    CARGO_PACKAGE:onafterUnLoad(Event, From, To, Distance, Angle, CargoCarrier, Speed) +

    UnLoad Event.

    +
    + +

    Type CARGO_REPORTABLE

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CARGO_REPORTABLE.CargoObject + +
    CARGO_REPORTABLE.CargoSet + +
    CARGO_REPORTABLE.ClassName + +
    CARGO_REPORTABLE:GetBoardingRange() +

    Get the range till cargo will board.

    +
    CARGO_REPORTABLE:IsInRadius(PointVec2) +

    Check if CargoCarrier is in the ReportRadius for the Cargo to be Loaded.

    +
    CARGO_REPORTABLE:MessageToGroup(Message, TaskGroup, Name) +

    Send a CC message to a GROUP.

    +
    CARGO_REPORTABLE:New(CargoObject, Type, Name, Weight, ReportRadius, NearRadius) +

    CARGO_REPORTABLE Constructor.

    +
    CARGO_REPORTABLE.ReportRadius + +
    CARGO_REPORTABLE:Respawn() +

    Respawn the cargo.

    +
    + +

    Type CARGO_REPRESENTABLE

    + + + + + + + + + + + + + +
    CARGO_REPRESENTABLE:New(Type, Name, Weight, ReportRadius, NearRadius, CargoObject) +

    CARGO_REPRESENTABLE Constructor.

    +
    CARGO_REPRESENTABLE:RouteTo(ToPointVec2, Speed) +

    Route a cargo unit to a PointVec2.

    +
    CARGO_REPRESENTABLE.test + +
    + +

    Type CARGO_UNIT

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CARGO_UNIT.CargoCarrier + +
    CARGO_UNIT.CargoInAir + +
    CARGO_UNIT.CargoObject + +
    CARGO_UNIT:Destroy() +

    CARGO_UNIT Destructor.

    +
    CARGO_UNIT:New(CargoUnit, Type, Name, Weight, ReportRadius, NearRadius) +

    CARGO_UNIT Constructor.

    +
    CARGO_UNIT.OnUnLoadedCallBack + +
    CARGO_UNIT.RunCount + +
    CARGO_UNIT:onafterBoard(Event, From, To, CargoCarrier, NearRadius, ...) +

    Board Event.

    +
    CARGO_UNIT:onafterBoarding(Event, From, To, CargoCarrier, NearRadius, ...) +

    Boarding Event.

    +
    CARGO_UNIT:onafterUnBoarding(Event, From, To, ToPointVec2, NearRadius) +

    UnBoard Event.

    +
    CARGO_UNIT:onenterBoarding(Event, From, To, CargoCarrier, NearRadius, ...) +

    Enter Boarding State.

    +
    CARGO_UNIT:onenterLoaded(Event, From, To, CargoCarrier) +

    Loaded State.

    +
    CARGO_UNIT:onenterUnBoarding(Event, From, To, ToPointVec2, NearRadius) +

    Enter UnBoarding State.

    +
    CARGO_UNIT:onenterUnLoaded(Event, From, To, Core, ToPointVec2) +

    Enter UnLoaded State.

    +
    CARGO_UNIT:onleaveUnBoarding(Event, From, To, ToPointVec2, NearRadius) +

    Leave UnBoarding State.

    +
    + +

    Global(s)

    +
    +
    + + #CARGO + +CARGO + +
    +
    + +

    (R2.1) CARGO class, extends Fsm#FSM_PROCESS

    + +

    The CARGO class defines the core functions that defines a cargo object within MOOSE.

    + + +

    A cargo is a logical object defined that is available for transport, and has a life status within a simulation.

    + +

    The CARGO is a state machine: it manages the different events and states of the cargo. +All derived classes from CARGO follow the same state machine, expose the same cargo event functions, and provide the same cargo states.

    + +

    CARGO Events:

      -
    • AICARGOGROUPED, represented by a Group of CARGO_UNITs.
    • +
    • CARGO.Board( ToCarrier ): Boards the cargo to a carrier.
    • +
    • CARGO.Load( ToCarrier ): Loads the cargo into a carrier, regardless of its position.
    • +
    • CARGO.UnBoard( ToPointVec2 ): UnBoard the cargo from a carrier. This will trigger a movement of the cargo to the option ToPointVec2.
    • +
    • CARGO.UnLoad( ToPointVec2 ): UnLoads the cargo from a carrier.
    • +
    • CARGO.Dead( Controllable ): The cargo is dead. The cargo process will be ended.
    -

    1) #AI_CARGO class, extends Fsm#FSM_PROCESS

    - -

    The #AI_CARGO class defines the core functions that defines a cargo object within MOOSE. -A cargo is a logical object defined that is available for transport, and has a life status within a simulation.

    - -

    The AICARGO is a state machine: it manages the different events and states of the cargo. -All derived classes from AICARGO follow the same state machine, expose the same cargo event functions, and provide the same cargo states.

    - -

    1.2.1) AI_CARGO Events:

    - -
      -
    • AI_CARGO.Board( ToCarrier ): Boards the cargo to a carrier.
    • -
    • AI_CARGO.Load( ToCarrier ): Loads the cargo into a carrier, regardless of its position.
    • -
    • AI_CARGO.UnBoard( ToPointVec2 ): UnBoard the cargo from a carrier. This will trigger a movement of the cargo to the option ToPointVec2.
    • -
    • AI_CARGO.UnLoad( ToPointVec2 ): UnLoads the cargo from a carrier.
    • -
    • AI_CARGO.Dead( Controllable ): The cargo is dead. The cargo process will be ended.
    • -
    - -

    1.2.2) AI_CARGO States:

    +

    CARGO States:

    • UnLoaded: The cargo is unloaded from a carrier.
    • @@ -126,7 +917,7 @@ All derived classes from AICARGO follow the same state machine, expose the
    • End: The process has come to an end.
    -

    1.2.3) AI_CARGO state transition methods:

    +

    CARGO state transition methods:

    State transition functions can be set by the mission designer customizing or improving the behaviour of the state. There are 2 moments when state transition methods will be called by the state machine:

    @@ -142,602 +933,6 @@ There are 2 moments when state transition methods will be called by the state ma These state transition methods need to provide a return value, which is specified at the function description.

    -

    2) #AICARGOUNIT class

    - -

    The AICARGOUNIT class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. -Use the event functions as described above to Load, UnLoad, Board, UnBoard the AICARGOUNIT objects to and from carriers.

    - -

    5) #AICARGOGROUPED class

    - -

    The AICARGOGROUPED class defines a cargo that is represented by a group of UNIT objects within the simulator, and can be transported by a carrier. -Use the event functions as described above to Load, UnLoad, Board, UnBoard the AICARGOUNIT objects to and from carriers.

    - -

    This module is still under construction, but is described above works already, and will keep working ...

    - - -

    Global(s)

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AI_CARGO - -
    AI_CARGO_GROUP - -
    AI_CARGO_GROUPED - -
    AI_CARGO_PACKAGE - -
    AI_CARGO_REPRESENTABLE - -
    AI_CARGO_UNIT - -
    CARGOS - -
    -

    Type AI_CARGO

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AI_CARGO:Board(ToCarrier) -

    Boards the cargo to a Carrier.

    -
    AI_CARGO.CargoCarrier -

    The alive DCS object carrying the cargo. This value can be nil, meaning, that the cargo is not contained anywhere...

    -
    AI_CARGO.CargoObject -

    The alive DCS object representing the cargo. This value can be nil, meaning, that the cargo is not represented anywhere...

    -
    AI_CARGO.ClassName - -
    AI_CARGO.Containable -

    This flag defines if the cargo can be contained within a DCS Unit.

    -
    AI_CARGO:IsNear(PointVec2) -

    Check if CargoCarrier is near the Cargo to be Loaded.

    -
    AI_CARGO:Load(ToCarrier) -

    Loads the cargo to a Carrier.

    -
    AI_CARGO.Moveable -

    This flag defines if the cargo is moveable.

    -
    AI_CARGO.Name -

    A string defining the name of the cargo. The name is the unique identifier of the cargo.

    -
    AI_CARGO.NearRadius -

    (optional) A number defining the radius in meters when the cargo is near to a Carrier, so that it can be loaded.

    -
    AI_CARGO:New(Type, Name, Weight, ReportRadius, NearRadius) -

    AI_CARGO Constructor.

    -
    AI_CARGO:OnEnterBoarding(Controllable) - -
    AI_CARGO:OnEnterLoaded(Controllable) - -
    AI_CARGO:OnEnterUnBoarding(Controllable) - -
    AI_CARGO:OnEnterUnLoaded(Controllable) - -
    AI_CARGO:OnLeaveBoarding(Controllable) - -
    AI_CARGO:OnLeaveLoaded(Controllable) - -
    AI_CARGO:OnLeaveUnBoarding(Controllable) - -
    AI_CARGO:OnLeaveUnLoaded(Controllable) - -
    AI_CARGO.ReportRadius -

    (optional) A number defining the radius in meters when the cargo is signalling or reporting to a Carrier.

    -
    AI_CARGO.Representable -

    This flag defines if the cargo can be represented by a DCS Unit.

    -
    AI_CARGO.Slingloadable -

    This flag defines if the cargo can be slingloaded.

    -
    AI_CARGO:Spawn(PointVec2) -

    Template method to spawn a new representation of the AI_CARGO in the simulator.

    -
    AI_CARGO.Type -

    A string defining the type of the cargo. eg. Engineers, Equipment, Screwdrivers.

    -
    AI_CARGO:UnBoard(ToPointVec2) -

    UnBoards the cargo to a Carrier.

    -
    AI_CARGO:UnLoad(ToPointVec2) -

    UnLoads the cargo to a Carrier.

    -
    AI_CARGO.Weight -

    A number defining the weight of the cargo. The weight is expressed in kg.

    -
    AI_CARGO:__Board(DelaySeconds, ToCarrier) -

    Boards the cargo to a Carrier.

    -
    AI_CARGO:__Load(DelaySeconds, ToCarrier) -

    Loads the cargo to a Carrier.

    -
    AI_CARGO:__UnBoard(DelaySeconds, ToPointVec2) -

    UnBoards the cargo to a Carrier.

    -
    AI_CARGO:__UnLoad(DelaySeconds, ToPointVec2) -

    UnLoads the cargo to a Carrier.

    -
    - -

    Type AI_CARGO_GROUP

    - - - - - - - - - - - - - - - - - -
    AI_CARGO_GROUP.CargoSet -

    A set of cargo objects.

    -
    AI_CARGO_GROUP.ClassName - -
    AI_CARGO_GROUP.Name -

    A string defining the name of the cargo group. The name is the unique identifier of the cargo.

    -
    AI_CARGO_GROUP:New(CargoSet, Type, Name, Weight, ReportRadius, NearRadius) -

    AICARGOGROUP constructor.

    -
    - -

    Type AI_CARGO_GROUPED

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AI_CARGO_GROUPED.ClassName - -
    AI_CARGO_GROUPED:New(CargoSet, Type, Name, Weight, ReportRadius, NearRadius) -

    AICARGOGROUPED constructor.

    -
    AI_CARGO_GROUPED:onafterUnBoarding(ToPointVec2, Event, From, To) -

    UnBoard Event.

    -
    AI_CARGO_GROUPED:onenterBoarding(CargoCarrier, Event, From, To) -

    Enter Boarding State.

    -
    AI_CARGO_GROUPED:onenterLoaded(CargoCarrier, Event, From, To) -

    Enter Loaded State.

    -
    AI_CARGO_GROUPED:onenterUnBoarding(ToPointVec2, Event, From, To) -

    Enter UnBoarding State.

    -
    AI_CARGO_GROUPED:onenterUnLoaded(Core, Event, From, To, ToPointVec2) -

    Enter UnLoaded State.

    -
    AI_CARGO_GROUPED:onleaveBoarding(CargoCarrier, Event, From, To) -

    Leave Boarding State.

    -
    AI_CARGO_GROUPED:onleaveUnBoarding(ToPointVec2, Event, From, To) -

    Leave UnBoarding State.

    -
    - -

    Type AI_CARGO_PACKAGE

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AI_CARGO_PACKAGE.CargoCarrier - -
    AI_CARGO_PACKAGE.CargoInAir - -
    AI_CARGO_PACKAGE.ClassName - -
    AI_CARGO_PACKAGE:IsNear(CargoCarrier) -

    Check if CargoCarrier is near the Cargo to be Loaded.

    -
    AI_CARGO_PACKAGE:New(CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius) -

    AICARGOPACKAGE Constructor.

    -
    AI_CARGO_PACKAGE:onafterLoad(Event, From, To, CargoCarrier, Speed, LoadDistance, Angle) -

    Load Event.

    -
    AI_CARGO_PACKAGE:onafterOnBoard(Event, From, To, CargoCarrier, Speed, BoardDistance, Angle, LoadDistance) -

    Board Event.

    -
    AI_CARGO_PACKAGE:onafterOnBoarded(Event, From, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle) -

    Boarded Event.

    -
    AI_CARGO_PACKAGE:onafterUnBoard(Event, From, To, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle, CargoCarrier) -

    UnBoard Event.

    -
    AI_CARGO_PACKAGE:onafterUnBoarded(Event, From, To, CargoCarrier, Speed) -

    UnBoarded Event.

    -
    AI_CARGO_PACKAGE:onafterUnLoad(Event, From, To, Distance, Angle, CargoCarrier, Speed) -

    UnLoad Event.

    -
    - -

    Type AI_CARGO_REPRESENTABLE

    - - - - - - - - - - - - - -
    AI_CARGO_REPRESENTABLE.ClassName - -
    AI_CARGO_REPRESENTABLE:New(CargoObject, Type, Name, Weight, ReportRadius, NearRadius) -

    AICARGOREPRESENTABLE Constructor.

    -
    AI_CARGO_REPRESENTABLE:RouteTo(ToPointVec2, Speed) -

    Route a cargo unit to a PointVec2.

    -
    - -

    Type AI_CARGO_UNIT

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AI_CARGO_UNIT.CargoCarrier - -
    AI_CARGO_UNIT.CargoInAir - -
    AI_CARGO_UNIT.CargoObject - -
    AI_CARGO_UNIT.ClassName - -
    AI_CARGO_UNIT:New(CargoUnit, Type, Name, Weight, ReportRadius, NearRadius) -

    AICARGOUNIT Constructor.

    -
    AI_CARGO_UNIT.OnUnLoadedCallBack - -
    AI_CARGO_UNIT:onafterBoard(Event, From, To, CargoCarrier) -

    Board Event.

    -
    AI_CARGO_UNIT:onafterUnBoarding(Event, From, To, ToPointVec2) -

    UnBoard Event.

    -
    AI_CARGO_UNIT:onenterBoarding(Event, From, To, CargoCarrier) -

    Enter Boarding State.

    -
    AI_CARGO_UNIT:onenterLoaded(Event, From, To, CargoCarrier) -

    Loaded State.

    -
    AI_CARGO_UNIT:onenterUnBoarding(Event, From, To, ToPointVec2) -

    Enter UnBoarding State.

    -
    AI_CARGO_UNIT:onenterUnLoaded(Event, From, To, Core, ToPointVec2) -

    Enter UnLoaded State.

    -
    AI_CARGO_UNIT:onleaveBoarding(Event, From, To, CargoCarrier) -

    Leave Boarding State.

    -
    AI_CARGO_UNIT:onleaveUnBoarding(Event, From, To, ToPointVec2) -

    Leave UnBoarding State.

    -
    - -

    Global(s)

    -
    -
    - - #AI_CARGO - -AI_CARGO - -
    -
    - - - -
    -
    -
    -
    - - #AI_CARGO_GROUP - -AI_CARGO_GROUP - -
    -
    - - - -
    -
    -
    -
    - - #AI_CARGO_GROUPED - -AI_CARGO_GROUPED - -
    -
    - - - -
    -
    -
    -
    - - #AI_CARGO_PACKAGE - -AI_CARGO_PACKAGE - -
    -
    - - - -
    -
    -
    -
    - - #AI_CARGO_REPRESENTABLE - -AI_CARGO_REPRESENTABLE - -
    -
    - - - -
    -
    -
    -
    - - #AI_CARGO_UNIT - -AI_CARGO_UNIT - -
    -
    - -
    @@ -753,17 +948,101 @@ Use the event functions as described above to Load, UnLoad, Board, UnBoard the A +
    +
    +
    +
    + + #CARGO_GROUP + +CARGO_GROUP + +
    +
    + +

    CARGO_GROUP class

    + +

    The CARGO_GROUP class defines a cargo that is represented by a Group object within the simulator, and can be transported by a carrier.

    + + +

    Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO_GROUP to and from carrier.

    + + +
    +
    +
    +
    + + #CARGO_PACKAGE + +CARGO_PACKAGE + +
    +
    + + + +
    +
    +
    +
    + + #CARGO_REPORTABLE + +CARGO_REPORTABLE + +
    +
    + + + +
    +
    +
    +
    + + #CARGO_REPRESENTABLE + +CARGO_REPRESENTABLE + +
    +
    + + + +
    +
    +
    +
    + + #CARGO_UNIT + +CARGO_UNIT + +
    +
    + +

    CARGO_UNIT class, extends #CARGO_REPRESENTABLE

    + +

    The CARGO_UNIT class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.

    + + +

    Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO_UNIT objects to and from carriers.

    + +
    + +

    Type Cargo

    -

    Type AI_CARGO

    +

    Type CARGO

    Field(s)

    - -AI_CARGO:Board(ToCarrier) + +CARGO:Board(ToCarrier, NearRadius)
    @@ -774,13 +1053,19 @@ Use the event functions as described above to Load, UnLoad, Board, UnBoard the A

    The event will create a movement (= running or driving) of the cargo to the Carrier. The cargo must be in the UnLoaded state.

    -

    Parameter

    +

    Parameters

    • Wrapper.Controllable#CONTROLLABLE ToCarrier : The Carrier that will hold the cargo.

      +
    • +
    • + +

      #number NearRadius : +The radius when the cargo will board the Carrier (to avoid collision).

      +
    @@ -788,9 +1073,9 @@ The Carrier that will hold the cargo.

    - Wrapper.Controllable#CONTROLLABLE - -AI_CARGO.CargoCarrier + Wrapper.Client#CLIENT + +CARGO.CargoCarrier
    @@ -803,8 +1088,8 @@ The Carrier that will hold the cargo.

    Wrapper.Controllable#CONTROLLABLE - -AI_CARGO.CargoObject + +CARGO.CargoObject
    @@ -816,9 +1101,9 @@ The Carrier that will hold the cargo.

    - #string - -AI_CARGO.ClassName + + +CARGO.CargoScheduler
    @@ -831,8 +1116,8 @@ The Carrier that will hold the cargo.

    #boolean - -AI_CARGO.Containable + +CARGO.Containable
    @@ -844,20 +1129,314 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:IsNear(PointVec2) + + +CARGO.Deployed + +
    +
    + + + +
    +
    +
    +
    + + +CARGO:Destroy() + +
    +
    + +

    Destroy the cargo.

    + +
    +
    +
    +
    + + +CARGO:Flare(FlareColor) + +
    +
    + +

    Signal a flare at the position of the CARGO.

    + +

    Parameter

    + +
    +
    +
    +
    + + +CARGO:FlareGreen() + +
    +
    + +

    Signal a green flare at the position of the CARGO.

    + +
    +
    +
    +
    + + +CARGO:FlareRed() + +
    +
    + +

    Signal a red flare at the position of the CARGO.

    + +
    +
    +
    +
    + + +CARGO:FlareWhite() + +
    +
    + +

    Signal a white flare at the position of the CARGO.

    + +
    +
    +
    +
    + + +CARGO:FlareYellow() + +
    +
    + +

    Signal a yellow flare at the position of the CARGO.

    + +
    +
    +
    +
    + + +CARGO:GetCoordinate() + +
    +
    + +

    Get the current coordinates of the Cargo.

    + +

    Return value

    + +

    Core.Point#COORDINATE: +The coordinates of the Cargo.

    + +
    +
    +
    +
    + + +CARGO:GetName() + +
    +
    + +

    Get the name of the Cargo.

    + +

    Return value

    + +

    #string: +The name of the Cargo.

    + +
    +
    +
    +
    + + +CARGO:GetObjectName() + +
    +
    + +

    Get the object name of the Cargo.

    + +

    Return value

    + +

    #string: +The object name of the Cargo.

    + +
    +
    +
    +
    + + +CARGO:GetPointVec2() + +
    +
    + +

    Get the current PointVec2 of the cargo.

    + +

    Return value

    + +

    Core.Point#POINT_VEC2:

    + + +
    +
    +
    +
    + + +CARGO:GetType() + +
    +
    + +

    Get the type of the Cargo.

    + +

    Return value

    + +

    #string: +The type of the Cargo.

    + +
    +
    +
    +
    + + +CARGO:IsAlive() + +
    +
    + +

    Check if cargo is alive.

    + +

    Return value

    + +

    #boolean: +true if unloaded

    + +
    +
    +
    +
    + + +CARGO:IsDeployed() + +
    +
    + +

    Is the cargo deployed

    + +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +CARGO:IsDestroyed() + +
    +
    + +

    Check if cargo is destroyed.

    + +

    Return value

    + +

    #boolean: +true if destroyed

    + +
    +
    +
    +
    + + +CARGO:IsInZone(Zone) + +
    +
    + +

    Check if Cargo is the given Zone.

    + +

    Parameter

    + +

    Return value

    + +

    #boolean: +true if cargo is in the Zone, false if cargo is not in the Zone.

    + +
    +
    +
    +
    + + +CARGO:IsLoaded() + +
    +
    + +

    Check if cargo is loaded.

    + +

    Return value

    + +

    #boolean: +true if loaded

    + +
    +
    +
    +
    + + +CARGO:IsNear(PointVec2, NearRadius)

    Check if CargoCarrier is near the Cargo to be Loaded.

    -

    Parameter

    +

    Parameters

    • Core.Point#POINT_VEC2 PointVec2 :

      +
    • +
    • + +

      #number NearRadius : +The radius when the cargo will board the Carrier (to avoid collision).

      +

    Return value

    @@ -870,8 +1449,26 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:Load(ToCarrier) + +CARGO:IsUnLoaded() + +
    +
    + +

    Check if cargo is unloaded.

    + +

    Return value

    + +

    #boolean: +true if unloaded

    + +
    +
    +
    +
    + + +CARGO:Load(ToCarrier)
    @@ -897,8 +1494,8 @@ The Carrier that will hold the cargo.

    #boolean - -AI_CARGO.Moveable + +CARGO.Moveable
    @@ -911,8 +1508,8 @@ The Carrier that will hold the cargo.

    #string - -AI_CARGO.Name + +CARGO.Name
    @@ -925,8 +1522,8 @@ The Carrier that will hold the cargo.

    #number - -AI_CARGO.NearRadius + +CARGO.NearRadius
    @@ -938,13 +1535,13 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:New(Type, Name, Weight, ReportRadius, NearRadius) + +CARGO:New(Type, Name, Weight, NearRadius)
    -

    AI_CARGO Constructor.

    +

    CARGO Constructor.

    This class is an abstract class and should not be instantiated.

    @@ -968,12 +1565,6 @@ The Carrier that will hold the cargo.

  • -

    #number ReportRadius : -(optional)

    - -
  • -
  • -

    #number NearRadius : (optional)

    @@ -981,7 +1572,7 @@ The Carrier that will hold the cargo.

    Return value

    -

    #AI_CARGO:

    +

    #CARGO:

  • @@ -989,8 +1580,35 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:OnEnterBoarding(Controllable) + +CARGO:OnEnterBoarding(Controllable, NearRadius) + +
    +
    + + + +

    Parameters

    + +
    +
    +
    +
    + + +CARGO:OnEnterLoaded(Controllable)
    @@ -1010,8 +1628,8 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:OnEnterLoaded(Controllable) + +CARGO:OnEnterUnBoarding(Controllable)
    @@ -1031,8 +1649,8 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:OnEnterUnBoarding(Controllable) + +CARGO:OnEnterUnLoaded(Controllable)
    @@ -1052,29 +1670,8 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:OnEnterUnLoaded(Controllable) - -
    -
    - - - -

    Parameter

    - -
    -
    -
    -
    - - -AI_CARGO:OnLeaveBoarding(Controllable) + +CARGO:OnLeaveBoarding(Controllable)
    @@ -1099,8 +1696,8 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:OnLeaveLoaded(Controllable) + +CARGO:OnLeaveLoaded(Controllable)
    @@ -1125,8 +1722,8 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:OnLeaveUnBoarding(Controllable) + +CARGO:OnLeaveUnBoarding(Controllable)
    @@ -1151,8 +1748,8 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:OnLeaveUnLoaded(Controllable) + +CARGO:OnLeaveUnLoaded(Controllable)
    @@ -1172,42 +1769,76 @@ The Carrier that will hold the cargo.

    #boolean:

    -
    -
    -
    -
    - - #number - -AI_CARGO.ReportRadius - -
    -
    - -

    (optional) A number defining the radius in meters when the cargo is signalling or reporting to a Carrier.

    -
    #boolean - -AI_CARGO.Representable + +CARGO.Representable

    This flag defines if the cargo can be represented by a DCS Unit.

    +
    +
    +
    +
    + + +CARGO:SetDeployed(Deployed) + +
    +
    + +

    Set the cargo as deployed

    + +

    Parameter

    +
      +
    • + +

      Deployed :

      + +
    • +
    +
    +
    +
    +
    + + +CARGO:SetWeight(Weight) + +
    +
    + +

    Set the weight of the cargo.

    + +

    Parameter

    +
      +
    • + +

      #number Weight : +The weight in kg.

      + +
    • +
    +

    Return value

    + +

    #CARGO:

    + +
    #boolean - -AI_CARGO.Slingloadable + +CARGO.Slingloadable
    @@ -1219,13 +1850,104 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:Spawn(PointVec2) + +CARGO:Smoke(SmokeColor, Range)
    -

    Template method to spawn a new representation of the AI_CARGO in the simulator.

    +

    Smoke the CARGO.

    + +

    Parameters

    +
      +
    • + +

      SmokeColor :

      + +
    • +
    • + +

      Range :

      + +
    • +
    +
    +
    +
    +
    + + +CARGO:SmokeBlue() + +
    +
    + +

    Smoke the CARGO Blue.

    + +
    +
    +
    +
    + + +CARGO:SmokeGreen() + +
    +
    + +

    Smoke the CARGO Green.

    + +
    +
    +
    +
    + + +CARGO:SmokeOrange() + +
    +
    + +

    Smoke the CARGO Orange.

    + +
    +
    +
    +
    + + +CARGO:SmokeRed() + +
    +
    + +

    Smoke the CARGO Red.

    + +
    +
    +
    +
    + + +CARGO:SmokeWhite() + +
    +
    + +

    Smoke the CARGO White.

    + +
    +
    +
    +
    + + +CARGO:Spawn(PointVec2) + +
    +
    + +

    Template method to spawn a new representation of the CARGO in the simulator.

    Parameter

      @@ -1237,7 +1959,7 @@ The Carrier that will hold the cargo.

    Return value

    -

    #AI_CARGO:

    +

    #CARGO:

    @@ -1246,8 +1968,8 @@ The Carrier that will hold the cargo.

    #string - -AI_CARGO.Type + +CARGO.Type
    @@ -1259,8 +1981,8 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:UnBoard(ToPointVec2) + +CARGO:UnBoard(ToPointVec2)
    @@ -1285,8 +2007,8 @@ The cargo must be in the Loaded state.

    - -AI_CARGO:UnLoad(ToPointVec2) + +CARGO:UnLoad(ToPointVec2)
    @@ -1312,8 +2034,8 @@ The cargo must be in the Loaded state.

    #number - -AI_CARGO.Weight + +CARGO.Weight
    @@ -1325,8 +2047,8 @@ The cargo must be in the Loaded state.

    - -AI_CARGO:__Board(DelaySeconds, ToCarrier) + +CARGO:__Board(DelaySeconds, ToCarrier, NearRadius)
    @@ -1350,6 +2072,12 @@ The amount of seconds to delay the action.

    Wrapper.Controllable#CONTROLLABLE ToCarrier : The Carrier that will hold the cargo.

    + +
  • + +

    #number NearRadius : +The radius when the cargo will board the Carrier (to avoid collision).

    +
  • @@ -1357,8 +2085,8 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:__Load(DelaySeconds, ToCarrier) + +CARGO:__Load(DelaySeconds, ToCarrier)
    @@ -1389,8 +2117,8 @@ The Carrier that will hold the cargo.

    - -AI_CARGO:__UnBoard(DelaySeconds, ToPointVec2) + +CARGO:__UnBoard(DelaySeconds, ToPointVec2)
    @@ -1421,8 +2149,8 @@ The amount of seconds to delay the action.

    - -AI_CARGO:__UnLoad(DelaySeconds, ToPointVec2) + +CARGO:__UnLoad(DelaySeconds, ToPointVec2)
    @@ -1451,30 +2179,33 @@ The amount of seconds to delay the action.

    -

    Type AI_CARGO.CargoObjects

    +

    Type CARGO.CargoObjects

    -

    Type AI_CARGO_GROUP

    +

    Type CARGO_GROUP

    Field(s)

    - Set#SET_BASE - -AI_CARGO_GROUP.CargoSet + + +CARGO_GROUP.CargoCarrier
    -

    A set of cargo objects.

    + + + +

    self.CargoObject:Destroy()

    - #string - -AI_CARGO_GROUP.ClassName + + +CARGO_GROUP.CargoGroup
    @@ -1486,33 +2217,51 @@ The amount of seconds to delay the action.

    - #string - -AI_CARGO_GROUP.Name + + +CARGO_GROUP.CargoObject
    -

    A string defining the name of the cargo group. The name is the unique identifier of the cargo.

    +
    - -AI_CARGO_GROUP:New(CargoSet, Type, Name, Weight, ReportRadius, NearRadius) + +CARGO_GROUP:GetCount()
    -

    AICARGOGROUP constructor.

    +

    Get the amount of cargo units in the group.

    + +

    Return value

    + +

    #CARGO_GROUP:

    + + +
    +
    +
    +
    + + +CARGO_GROUP:New(CargoGroup, Type, Name, ReportRadius, NearRadius) + +
    +
    + +

    CARGO_GROUP constructor.

    Parameters

    • -

      Core.Set#Set_BASE CargoSet :

      +

      Wrapper.Group#GROUP CargoGroup :

    • @@ -1527,11 +2276,6 @@ The amount of seconds to delay the action.

    • -

      #number Weight :

      - -
    • -
    • -

      #number ReportRadius : (optional)

      @@ -1545,24 +2289,7 @@ The amount of seconds to delay the action.

    Return value

    -

    #AICARGOGROUP:

    - - -
    -
    - -

    Type AI_CARGO_GROUPED

    -

    Field(s)

    -
    -
    - - #string - -AI_CARGO_GROUPED.ClassName - -
    -
    - +

    #CARGO_GROUP:

    @@ -1570,61 +2297,96 @@ The amount of seconds to delay the action.

    - -AI_CARGO_GROUPED:New(CargoSet, Type, Name, Weight, ReportRadius, NearRadius) + +CARGO_GROUP:OnEventCargoDead(EventData)
    -

    AICARGOGROUPED constructor.

    + + +

    Parameter

    + +
    +
    +
    +
    + + +CARGO_GROUP:RespawnOnDestroyed(RespawnDestroyed) + +
    +
    + +

    Respawn the cargo when destroyed

    + +

    Parameter

    +
      +
    • + +

      #boolean RespawnDestroyed :

      + +
    • +
    +
    +
    +
    +
    + + +CARGO_GROUP:onafterBoarding(CargoCarrier, Event, From, To, NearRadius, ...) + +
    +
    + +

    Leave Boarding State.

    Parameters

    • -

      Core.Set#Set_BASE CargoSet :

      +

      Wrapper.Unit#UNIT CargoCarrier :

    • -

      #string Type :

      +

      #string Event :

    • -

      #string Name :

      +

      #string From :

    • -

      #number Weight :

      +

      #string To :

    • -

      #number ReportRadius : -(optional)

      +

      NearRadius :

    • -

      #number NearRadius : -(optional)

      +

      ... :

    -

    Return value

    - -

    #AICARGOGROUPED:

    - -
    - -AI_CARGO_GROUPED:onafterUnBoarding(ToPointVec2, Event, From, To) + +CARGO_GROUP:onafterUnBoarding(ToPointVec2, Event, From, To, NearRadius, ...)
    @@ -1652,6 +2414,16 @@ The amount of seconds to delay the action.

    #string To :

    + +
  • + +

    NearRadius :

    + +
  • +
  • + +

    ... :

    +
  • @@ -1659,8 +2431,8 @@ The amount of seconds to delay the action.

    - -AI_CARGO_GROUPED:onenterBoarding(CargoCarrier, Event, From, To) + +CARGO_GROUP:onenterBoarding(CargoCarrier, Event, From, To, NearRadius, ...)
    @@ -1688,6 +2460,16 @@ The amount of seconds to delay the action.

    #string To :

    + +
  • + +

    NearRadius :

    + +
  • +
  • + +

    ... :

    +
  • @@ -1695,8 +2477,21 @@ The amount of seconds to delay the action.

    - -AI_CARGO_GROUPED:onenterLoaded(CargoCarrier, Event, From, To) + +CARGO_GROUP:onenterDestroyed() + +
    +
    + + + +
    +
    +
    +
    + + +CARGO_GROUP:onenterLoaded(CargoCarrier, Event, From, To, ...)
    @@ -1724,6 +2519,11 @@ The amount of seconds to delay the action.

    #string To :

    + +
  • + +

    ... :

    +
  • @@ -1731,8 +2531,8 @@ The amount of seconds to delay the action.

    - -AI_CARGO_GROUPED:onenterUnBoarding(ToPointVec2, Event, From, To) + +CARGO_GROUP:onenterUnBoarding(ToPointVec2, Event, From, To, NearRadius, ...)
    @@ -1760,6 +2560,16 @@ The amount of seconds to delay the action.

    #string To :

    + +
  • + +

    NearRadius :

    + +
  • +
  • + +

    ... :

    +
  • @@ -1767,8 +2577,8 @@ The amount of seconds to delay the action.

    - -AI_CARGO_GROUPED:onenterUnLoaded(Core, Event, From, To, ToPointVec2) + +CARGO_GROUP:onenterUnLoaded(Core, Event, From, To, ToPointVec2, ...)
    @@ -1803,40 +2613,9 @@ Point#POINT_VEC2

    ToPointVec2 :

    - -
    -
    -
    -
    - - -AI_CARGO_GROUPED:onleaveBoarding(CargoCarrier, Event, From, To) - -
    -
    - -

    Leave Boarding State.

    - -

    Parameters

    -
    • -

      Wrapper.Unit#UNIT CargoCarrier :

      - -
    • -
    • - -

      #string Event :

      - -
    • -
    • - -

      #string From :

      - -
    • -
    • - -

      #string To :

      +

      ... :

    @@ -1845,8 +2624,8 @@ Point#POINT_VEC2

    - -AI_CARGO_GROUPED:onleaveUnBoarding(ToPointVec2, Event, From, To) + +CARGO_GROUP:onleaveUnBoarding(ToPointVec2, Event, From, To, NearRadius, ...)
    @@ -1874,19 +2653,29 @@ Point#POINT_VEC2

    #string To :

    + +
  • + +

    NearRadius :

    + +
  • +
  • + +

    ... :

    +
  • -

    Type AI_CARGO_PACKAGE

    +

    Type CARGO_PACKAGE

    Field(s)

    - -AI_CARGO_PACKAGE.CargoCarrier + +CARGO_PACKAGE.CargoCarrier
    @@ -1899,8 +2688,8 @@ Point#POINT_VEC2

    - -AI_CARGO_PACKAGE.CargoInAir + +CARGO_PACKAGE.CargoInAir
    @@ -1913,8 +2702,8 @@ Point#POINT_VEC2

    #string - -AI_CARGO_PACKAGE.ClassName + +CARGO_PACKAGE.ClassName
    @@ -1926,8 +2715,8 @@ Point#POINT_VEC2

    - -AI_CARGO_PACKAGE:IsNear(CargoCarrier) + +CARGO_PACKAGE:IsNear(CargoCarrier)
    @@ -1952,13 +2741,13 @@ Point#POINT_VEC2

    - -AI_CARGO_PACKAGE:New(CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius) + +CARGO_PACKAGE:New(CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius)
    -

    AICARGOPACKAGE Constructor.

    +

    CARGO_PACKAGE Constructor.

    Parameters

      @@ -1998,7 +2787,7 @@ The UNIT carrying the package.

    Return value

    -

    #AICARGOPACKAGE:

    +

    #CARGO_PACKAGE:

    @@ -2006,8 +2795,8 @@ The UNIT carrying the package.

    - -AI_CARGO_PACKAGE:onafterLoad(Event, From, To, CargoCarrier, Speed, LoadDistance, Angle) + +CARGO_PACKAGE:onafterLoad(Event, From, To, CargoCarrier, Speed, LoadDistance, Angle)
    @@ -2057,8 +2846,8 @@ The UNIT carrying the package.

    - -AI_CARGO_PACKAGE:onafterOnBoard(Event, From, To, CargoCarrier, Speed, BoardDistance, Angle, LoadDistance) + +CARGO_PACKAGE:onafterOnBoard(Event, From, To, CargoCarrier, Speed, BoardDistance, Angle, LoadDistance)
    @@ -2113,8 +2902,8 @@ The UNIT carrying the package.

    - -AI_CARGO_PACKAGE:onafterOnBoarded(Event, From, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle) + +CARGO_PACKAGE:onafterOnBoarded(Event, From, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle)
    @@ -2169,8 +2958,8 @@ The UNIT carrying the package.

    - -AI_CARGO_PACKAGE:onafterUnBoard(Event, From, To, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle, CargoCarrier) + +CARGO_PACKAGE:onafterUnBoard(Event, From, To, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle, CargoCarrier)
    @@ -2230,8 +3019,8 @@ The UNIT carrying the package.

    - -AI_CARGO_PACKAGE:onafterUnBoarded(Event, From, To, CargoCarrier, Speed) + +CARGO_PACKAGE:onafterUnBoarded(Event, From, To, CargoCarrier, Speed)
    @@ -2271,8 +3060,8 @@ The UNIT carrying the package.

    - -AI_CARGO_PACKAGE:onafterUnLoad(Event, From, To, Distance, Angle, CargoCarrier, Speed) + +CARGO_PACKAGE:onafterUnLoad(Event, From, To, Distance, Angle, CargoCarrier, Speed)
    @@ -2320,14 +3109,14 @@ The UNIT carrying the package.

    -

    Type AI_CARGO_REPRESENTABLE

    +

    Type CARGO_REPORTABLE

    Field(s)

    - #string - -AI_CARGO_REPRESENTABLE.ClassName + + +CARGO_REPORTABLE.CargoObject
    @@ -2339,13 +3128,117 @@ The UNIT carrying the package.

    - -AI_CARGO_REPRESENTABLE:New(CargoObject, Type, Name, Weight, ReportRadius, NearRadius) + Core.Set#SET_CARGO + +CARGO_REPORTABLE.CargoSet
    -

    AICARGOREPRESENTABLE Constructor.

    + + +
    +
    +
    +
    + + #string + +CARGO_REPORTABLE.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +CARGO_REPORTABLE:GetBoardingRange() + +
    +
    + +

    Get the range till cargo will board.

    + +

    Return value

    + +

    #number: +The range till cargo will board.

    + +
    +
    +
    +
    + + +CARGO_REPORTABLE:IsInRadius(PointVec2) + +
    +
    + +

    Check if CargoCarrier is in the ReportRadius for the Cargo to be Loaded.

    + +

    Parameter

    + +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +CARGO_REPORTABLE:MessageToGroup(Message, TaskGroup, Name) + +
    +
    + +

    Send a CC message to a GROUP.

    + +

    Parameters

    +
      +
    • + +

      #string Message :

      + +
    • +
    • + +

      Wrapper.Group#GROUP TaskGroup :

      + +
    • +
    • + +

      #sring Name : +(optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown.

      + +
    • +
    +
    +
    +
    +
    + + +CARGO_REPORTABLE:New(CargoObject, Type, Name, Weight, ReportRadius, NearRadius) + +
    +
    + +

    CARGO_REPORTABLE Constructor.

    Parameters

      @@ -2384,7 +3277,7 @@ The UNIT carrying the package.

    Return value

    -

    #AICARGOREPRESENTABLE:

    +

    #CARGO_REPORTABLE:

    @@ -2392,8 +3285,90 @@ The UNIT carrying the package.

    - -AI_CARGO_REPRESENTABLE:RouteTo(ToPointVec2, Speed) + +CARGO_REPORTABLE.ReportRadius + +
    +
    + + + +
    +
    +
    +
    + + +CARGO_REPORTABLE:Respawn() + +
    +
    + +

    Respawn the cargo.

    + +
    +
    + +

    Type CARGO_REPRESENTABLE

    +

    Field(s)

    +
    +
    + + +CARGO_REPRESENTABLE:New(Type, Name, Weight, ReportRadius, NearRadius, CargoObject) + +
    +
    + +

    CARGO_REPRESENTABLE Constructor.

    + +

    Parameters

    +
      +
    • + +

      #string Type :

      + +
    • +
    • + +

      #string Name :

      + +
    • +
    • + +

      #number Weight :

      + +
    • +
    • + +

      #number ReportRadius : +(optional)

      + +
    • +
    • + +

      #number NearRadius : +(optional)

      + +
    • +
    • + +

      CargoObject :

      + +
    • +
    +

    Return value

    + +

    #CARGO_REPRESENTABLE:

    + + +
    +
    +
    +
    + + +CARGO_REPRESENTABLE:RouteTo(ToPointVec2, Speed)
    @@ -2415,19 +3390,36 @@ The UNIT carrying the package.

    Return value

    -

    #AICARGOREPRESENTABLE:

    +

    #CARGO_REPRESENTABLE:

    + + +
    +
    +
    +
    + + +CARGO_REPRESENTABLE.test + +
    +
    +
    -

    Type AI_CARGO_UNIT

    -

    Field(s)

    +

    Type CARGO_UNIT

    + +

    Hello

    + +

    Field(s)

    - -AI_CARGO_UNIT.CargoCarrier + + +CARGO_UNIT.CargoCarrier
    @@ -2440,8 +3432,8 @@ The UNIT carrying the package.

    - -AI_CARGO_UNIT.CargoInAir + +CARGO_UNIT.CargoInAir
    @@ -2454,8 +3446,8 @@ The UNIT carrying the package.

    - -AI_CARGO_UNIT.CargoObject + +CARGO_UNIT.CargoObject
    @@ -2467,13 +3459,17 @@ The UNIT carrying the package.

    - #string - -AI_CARGO_UNIT.ClassName + +CARGO_UNIT:Destroy()
    +

    CARGO_UNIT Destructor.

    + +

    Return value

    + +

    #CARGO_UNIT:

    @@ -2481,13 +3477,13 @@ The UNIT carrying the package.

    - -AI_CARGO_UNIT:New(CargoUnit, Type, Name, Weight, ReportRadius, NearRadius) + +CARGO_UNIT:New(CargoUnit, Type, Name, Weight, ReportRadius, NearRadius)
    -

    AICARGOUNIT Constructor.

    +

    CARGO_UNIT Constructor.

    Parameters

      @@ -2526,7 +3522,7 @@ The UNIT carrying the package.

    Return value

    -

    #AICARGOUNIT:

    +

    #CARGO_UNIT:

    @@ -2534,8 +3530,8 @@ The UNIT carrying the package.

    - -AI_CARGO_UNIT.OnUnLoadedCallBack + +CARGO_UNIT.OnUnLoadedCallBack
    @@ -2547,8 +3543,21 @@ The UNIT carrying the package.

    - -AI_CARGO_UNIT:onafterBoard(Event, From, To, CargoCarrier) + +CARGO_UNIT.RunCount + +
    +
    + + + +
    +
    +
    +
    + + +CARGO_UNIT:onafterBoard(Event, From, To, CargoCarrier, NearRadius, ...)
    @@ -2576,6 +3585,16 @@ The UNIT carrying the package.

    CargoCarrier :

    + +
  • + +

    NearRadius :

    + +
  • +
  • + +

    ... :

    +
  • @@ -2583,8 +3602,54 @@ The UNIT carrying the package.

    - -AI_CARGO_UNIT:onafterUnBoarding(Event, From, To, ToPointVec2) + +CARGO_UNIT:onafterBoarding(Event, From, To, CargoCarrier, NearRadius, ...) + +
    +
    + +

    Boarding Event.

    + +

    Parameters

    +
      +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string From :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    • + +

      Wrapper.Unit#UNIT CargoCarrier :

      + +
    • +
    • + +

      #number NearRadius :

      + +
    • +
    • + +

      ... :

      + +
    • +
    +
    +
    +
    +
    + + +CARGO_UNIT:onafterUnBoarding(Event, From, To, ToPointVec2, NearRadius)
    @@ -2612,6 +3677,11 @@ The UNIT carrying the package.

    Core.Point#POINT_VEC2 ToPointVec2 :

    + +
  • + +

    NearRadius :

    +
  • @@ -2619,8 +3689,8 @@ The UNIT carrying the package.

    - -AI_CARGO_UNIT:onenterBoarding(Event, From, To, CargoCarrier) + +CARGO_UNIT:onenterBoarding(Event, From, To, CargoCarrier, NearRadius, ...)
    @@ -2648,6 +3718,16 @@ The UNIT carrying the package.

    Wrapper.Unit#UNIT CargoCarrier :

    + +
  • + +

    NearRadius :

    + +
  • +
  • + +

    ... :

    +
  • @@ -2655,8 +3735,8 @@ The UNIT carrying the package.

    - -AI_CARGO_UNIT:onenterLoaded(Event, From, To, CargoCarrier) + +CARGO_UNIT:onenterLoaded(Event, From, To, CargoCarrier)
    @@ -2691,8 +3771,8 @@ The UNIT carrying the package.

    - -AI_CARGO_UNIT:onenterUnBoarding(Event, From, To, ToPointVec2) + +CARGO_UNIT:onenterUnBoarding(Event, From, To, ToPointVec2, NearRadius)
    @@ -2720,6 +3800,11 @@ The UNIT carrying the package.

    Core.Point#POINT_VEC2 ToPointVec2 :

    + +
  • + +

    NearRadius :

    +
  • @@ -2727,8 +3812,8 @@ The UNIT carrying the package.

    - -AI_CARGO_UNIT:onenterUnLoaded(Event, From, To, Core, ToPointVec2) + +CARGO_UNIT:onenterUnLoaded(Event, From, To, Core, ToPointVec2)
    @@ -2769,44 +3854,8 @@ Point#POINT_VEC2

    - -AI_CARGO_UNIT:onleaveBoarding(Event, From, To, CargoCarrier) - -
    -
    - -

    Leave Boarding State.

    - -

    Parameters

    -
      -
    • - -

      #string Event :

      - -
    • -
    • - -

      #string From :

      - -
    • -
    • - -

      #string To :

      - -
    • -
    • - -

      Wrapper.Unit#UNIT CargoCarrier :

      - -
    • -
    -
    -
    -
    -
    - - -AI_CARGO_UNIT:onleaveUnBoarding(Event, From, To, ToPointVec2) + +CARGO_UNIT:onleaveUnBoarding(Event, From, To, ToPointVec2, NearRadius)
    @@ -2834,11 +3883,18 @@ Point#POINT_VEC2

    Core.Point#POINT_VEC2 ToPointVec2 :

    + +
  • + +

    NearRadius :

    +
  • +

    Type sring

    +
    diff --git a/docs/Documentation/CleanUp.html b/docs/Documentation/CleanUp.html index 60829d2bc..42bd588ce 100644 --- a/docs/Documentation/CleanUp.html +++ b/docs/Documentation/CleanUp.html @@ -17,9 +17,16 @@ index

    Module CleanUp

    -

    The CLEANUP class keeps an area clean of crashing or colliding airplanes.

    +

    Functional -- The CLEANUP_AIRBASE class keeps an area clean of crashing or colliding airplanes.

    It also prevents airplanes from firing within this area.

    +
    + +

    Author: Sven Van de Velde (FlightControl)

    +

    Contributions:

    + +
    + +

    Global(s)

    - +
    CLEANUPCLEANUP_AIRBASE +

    CLEANUP_AIRBASE, extends Base#BASE

    +

    Banner Image

    + +

    The CLEANUP_AIRBASE class keeps airbases clean, and tries to guarantee continuous airbase operations, even under combat.

    -

    Type CLEANUP

    +

    Type CLEANUP_AIRBASE

    - + - - - - - - - - - + - + + + + + + + + + - - -
    CLEANUP.ClassNameCLEANUP_AIRBASE:AddAirbase(AirbaseName) - +

    Adds an airbase to the airbase validation list.

    CLEANUP.CleanUpList - -
    CLEANUP.CleanUpScheduler - -
    CLEANUP:New(ZoneNames, TimeInterval)CLEANUP_AIRBASE:New(<, AirbaseNames)

    Creates the main object which is handling the cleaning of the debris within the given Zone Names.

    CLEANUP.TimeIntervalCLEANUP_AIRBASE:RemoveAirbase(AirbaseName) +

    Removes an airbase from the airbase validation list.

    +
    CLEANUP_AIRBASE:SetCleanMissiles(CleanMissiles) +

    Enables or disables the cleaning of missiles within the airbase zones.

    +
    CLEANUP_AIRBASE.__
    CLEANUP.ZoneNames +
    - - +

    Type CLEANUP_AIRBASE.__

    + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CLEANUP:_AddForCleanUp(CleanUpUnit, CleanUpUnitName)CLEANUP_AIRBASE.__.< -

    Add the DCSWrapper.Unit#Unit to the CleanUpList for CleanUp.

    -
    CLEANUP:_CleanUpScheduler() -

    At the defined time interval, CleanUp the Groups within the CleanUpList.

    -
    CLEANUP:_DestroyGroup(GroupObject, CleanUpGroupName) -

    Destroys a group from the simulator, but checks first if it is still existing!

    -
    CLEANUP:_DestroyMissile(MissileObject) - -
    CLEANUP:_DestroyUnit(CleanUpUnit, CleanUpUnitName) -

    Destroys a DCSWrapper.Unit#Unit from the simulator, but checks first if it is still existing!

    -
    CLEANUP:_EventAddForCleanUp(event, Event) -

    Detects if the Unit has an SEVENTENGINESHUTDOWN or an SEVENT_HIT within the given ZoneNames.

    -
    CLEANUP:_EventCrash(event, Event) -

    Detects if a crash event occurs.

    -
    CLEANUP:_EventHitCleanUp(event, Event) -

    Detects if the Unit has an SEVENTHIT within the given ZoneNames.

    -
    CLEANUP:_EventShot(event, Event) -

    Detects if a unit shoots a missile.

    -
    CLEANUP:_OnEventBirth(EventData) - +

    string,Wrapper.Airbase#AIRBASE> Airbases Map of Airbases.

    @@ -191,34 +179,100 @@
    - #CLEANUP - -CLEANUP + #CLEANUP_AIRBASE + +CLEANUP_AIRBASE
    +

    CLEANUP_AIRBASE, extends Base#BASE

    + +

    Banner Image

    + +

    The CLEANUP_AIRBASE class keeps airbases clean, and tries to guarantee continuous airbase operations, even under combat.

    + + +

    Specific airbases need to be provided that need to be guarded. Each airbase registered, will be guarded within a zone of 8 km around the airbase. +Any unit that fires a missile, or shoots within the zone of an airbase, will be monitored by CLEANUP_AIRBASE. +Within the 8km zone, units cannot fire any missile, which prevents the airbase runway to receive missile or bomb hits. +Any airborne or ground unit that is on the runway below 30 meters (default value) will be automatically removed if it is damaged.

    + +

    This is not a full 100% secure implementation. It is still possible that CLEANUP_AIRBASE cannot prevent (in-time) to keep the airbase clean. +The following situations may happen that will still stop the runway of an airbase:

    + +
      +
    • A damaged unit is not removed on time when above the runway, and crashes on the runway.
    • +
    • A bomb or missile is still able to dropped on the runway.
    • +
    • Units collide on the airbase, and could not be removed on time.
    • +
    + +

    When a unit is within the airbase zone and needs to be monitored, +its status will be checked every 0.25 seconds! This is required to ensure that the airbase is kept clean. +But as a result, there is more CPU overload.

    + +

    So as an advise, I suggest you use the CLEANUP_AIRBASE class with care:

    + +
      +
    • Only monitor airbases that really need to be monitored!
    • +
    • Try not to monitor airbases that are likely to be invaded by enemy troops. + For these airbases, there is little use to keep them clean, as they will be invaded anyway...
    • +
    + +

    By following the above guidelines, you can add airbase cleanup with acceptable CPU overhead.

    + +

    1. CLEANUP_AIRBASE Constructor

    + +

    Creates the main object which is preventing the airbase to get polluted with debris on the runway, which halts the airbase.

    + +
     -- Clean these Zones.
    + CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi )
    +
    + -- or
    + CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi )
    + CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
    +
    + +

    2. Add or Remove airbases

    + +

    The method CLEANUP_AIRBASE.AddAirbase() to add an airbase to the cleanup validation process. +The method CLEANUP_AIRBASE.RemoveAirbase() removes an airbase from the cleanup validation process.

    + +

    3. Clean missiles and bombs within the airbase zone.

    + +

    When missiles or bombs hit the runway, the airbase operations stop. +Use the method CLEANUP_AIRBASE.SetCleanMissiles() to control the cleaning of missiles, which will prevent airbases to stop. +Note that this method will not allow anymore airbases to be attacked, so there is a trade-off here to do.

    Type CleanUp

    -

    Type CLEANUP

    +

    Type CLEANUP_AIRBASE

    +

    Field(s)

    +
    +
    + + +CLEANUP_AIRBASE:AddAirbase(AirbaseName) + +
    +
    + +

    Adds an airbase to the airbase validation list.

    + +

    Parameter

    +
      +
    • -

      The CLEANUP class.

      +

      #string AirbaseName :

      -

      Field(s)

      -
      -
      - - #string - -CLEANUP.ClassName - -
      -
      - +
    • +
    +

    Return value

    + +

    #CLEANUP_AIRBASE:

    @@ -226,36 +280,8 @@
    - - -CLEANUP.CleanUpList - -
    -
    - - - -
    -
    -
    -
    - - - -CLEANUP.CleanUpScheduler - -
    -
    - - - -
    -
    -
    -
    - - -CLEANUP:New(ZoneNames, TimeInterval) + +CLEANUP_AIRBASE:New(<, AirbaseNames)
    @@ -266,41 +292,87 @@
    • -

      #table ZoneNames : -Is a table of zone names where the debris should be cleaned. Also a single string can be passed with one zone name.

      +

      #list < : +string> AirbaseNames Is a table of airbase names where the debris should be cleaned. Also a single string can be passed with one airbase name.

    • -

      #number TimeInterval : -The interval in seconds when the clean activity takes place. The default is 300 seconds, thus every 5 minutes.

      +

      AirbaseNames :

    Return value

    -

    #CLEANUP:

    +

    #CLEANUP_AIRBASE:

    Usage:

     -- Clean these Zones.
    -CleanUpAirports = CLEANUP:New( { 'CLEAN Tbilisi', 'CLEAN Kutaisi' }, 150 )
    +CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi )
     or
    -CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 )
    -CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 )
    +CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi ) +CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
    - - -CLEANUP.TimeInterval + +CLEANUP_AIRBASE:RemoveAirbase(AirbaseName)
    +

    Removes an airbase from the airbase validation list.

    + +

    Parameter

    +
      +
    • + +

      #string AirbaseName :

      + +
    • +
    +

    Return value

    + +

    #CLEANUP_AIRBASE:

    + + +
    +
    +
    +
    + + +CLEANUP_AIRBASE:SetCleanMissiles(CleanMissiles) + +
    +
    + +

    Enables or disables the cleaning of missiles within the airbase zones.

    + + +

    Airbase operations stop when a missile or bomb is dropped at a runway. +Note that when this method is used, the airbase operations won't stop if +the missile or bomb was cleaned within the airbase zone, which is 8km from the center of the airbase. +However, there is a trade-off to make. Attacks on airbases won't be possible anymore if this method is used. +Note, one can also use the method CLEANUP_AIRBASE.RemoveAirbase() to remove the airbase from the control process as a whole, +when an enemy unit is near. That is also an option...

    + +

    Parameter

    +
      +
    • + +

      #string CleanMissiles : +(Default=true) If true, missiles fired are immediately destroyed. If false missiles are not controlled.

      + +
    • +
    +

    Return value

    + +

    #CLEANUP_AIRBASE:

    @@ -309,276 +381,43 @@ CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 )
    - -CLEANUP.ZoneNames + +CLEANUP_AIRBASE.__
    + +

    @field #CLEANUPAIRBASE._

    +
    + +

    Type CLEANUP_AIRBASE.__

    +

    Field(s)

    - -CLEANUP:_AddForCleanUp(CleanUpUnit, CleanUpUnitName) + #map + +CLEANUP_AIRBASE.__.<
    -

    Add the DCSWrapper.Unit#Unit to the CleanUpList for CleanUp.

    - -

    Parameters

    -
      -
    • - -

      CleanUpUnit :

      - -
    • -
    • - -

      CleanUpUnitName :

      - -
    • -
    -
    -
    -
    -
    - - -CLEANUP:_CleanUpScheduler() - -
    -
    - -

    At the defined time interval, CleanUp the Groups within the CleanUpList.

    +

    string,Wrapper.Airbase#AIRBASE> Airbases Map of Airbases.

    -
    -
    -
    -
    - - -CLEANUP:_DestroyGroup(GroupObject, CleanUpGroupName) - -
    -
    - -

    Destroys a group from the simulator, but checks first if it is still existing!

    - -

    Parameters

    -
      -
    • - -

      Dcs.DCSWrapper.Group#Group GroupObject : -The object to be destroyed.

      - -
    • -
    • - -

      #string CleanUpGroupName : -The groupname...

      - -
    • -
    -
    -
    -
    -
    - - -CLEANUP:_DestroyMissile(MissileObject) - -
    -
    - - - - -

    TODO check Dcs.DCSTypes#Weapon -- Destroys a missile from the simulator, but checks first if it is still existing! - @param #CLEANUP self - @param Dcs.DCSTypes#Weapon MissileObject

    - -

    Parameter

    -
      -
    • - -

      MissileObject :

      - -
    • -
    -
    -
    -
    -
    - - -CLEANUP:_DestroyUnit(CleanUpUnit, CleanUpUnitName) - -
    -
    - -

    Destroys a DCSWrapper.Unit#Unit from the simulator, but checks first if it is still existing!

    - -

    Parameters

    -
      -
    • - -

      Dcs.DCSWrapper.Unit#Unit CleanUpUnit : -The object to be destroyed.

      - -
    • -
    • - -

      #string CleanUpUnitName : -The Unit name ...

      - -
    • -
    -
    -
    -
    -
    - - -CLEANUP:_EventAddForCleanUp(event, Event) - -
    -
    - -

    Detects if the Unit has an SEVENTENGINESHUTDOWN or an SEVENT_HIT within the given ZoneNames.

    - - -

    If this is the case, add the Group to the CLEANUP List.

    - -

    Parameters

    - -
    -
    -
    -
    - - -CLEANUP:_EventCrash(event, Event) - -
    -
    - -

    Detects if a crash event occurs.

    - - -

    Crashed units go into a CleanUpList for removal.

    - -

    Parameters

    - -
    -
    -
    -
    - - -CLEANUP:_EventHitCleanUp(event, Event) - -
    -
    - -

    Detects if the Unit has an SEVENTHIT within the given ZoneNames.

    - - -

    If this is the case, destroy the unit.

    - -

    Parameters

    - -
    -
    -
    -
    - - -CLEANUP:_EventShot(event, Event) - -
    -
    - -

    Detects if a unit shoots a missile.

    - - -

    If this occurs within one of the zones, then the weapon used must be destroyed.

    - -

    Parameters

    - -
    -
    -
    -
    - - -CLEANUP:_OnEventBirth(EventData) - -
    -
    - - - -

    Parameter

    -
    +

    Type CLEANUP_AIRBASE.__.Airbases

    + +

    Type list

    + +

    Type map

    +
    diff --git a/docs/Documentation/Client.html b/docs/Documentation/Client.html index 565f34b9b..357ccad2a 100644 --- a/docs/Documentation/Client.html +++ b/docs/Documentation/Client.html @@ -17,9 +17,16 @@ index

    Module Client

    -

    This module contains the CLIENT class.

    +

    Wrapper -- CLIENT wraps DCS Unit objects acting as a Client or Player within a mission.

    -

    1) Client#CLIENT class, extends Unit#UNIT

    -

    Clients are those Units defined within the Mission Editor that have the skillset defined as Client or Player. -Note that clients are NOT the same as Units, they are NOT necessarily alive. -The Client#CLIENT class is a wrapper class to handle the DCS Unit objects that have the skillset defined as Client or Player:

    +
    -
      -
    • Wraps the DCS Unit objects with skill level set to Player or Client.
    • -
    • Support all DCS Unit APIs.
    • -
    • Enhance with Unit specific APIs not in the DCS Group API set.
    • -
    • When player joins Unit, execute alive init logic.
    • -
    • Handles messages to players.
    • -
    • Manage the "state" of the DCS Unit.
    • -
    +

    Author: Sven Van de Velde (FlightControl)

    -

    Clients are being used by the MISSION class to follow players and register their successes.

    +

    Contributions:

    -

    1.1) CLIENT reference methods

    -

    For each DCS Unit having skill level Player or Client, a CLIENT wrapper object (instance) will be created within the _DATABASE object. -This is done at the beginning of the mission (when the mission starts).

    - -

    The CLIENT class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference -using the DCS Unit or the DCS UnitName.

    - -

    Another thing to know is that CLIENT objects do not "contain" the DCS Unit object. -The CLIENT methods will reference the DCS Unit object by name when it is needed during API execution. -If the DCS Unit object does not exist or is nil, the CLIENT methods will return nil and log an exception in the DCS.log file.

    - -

    The CLIENT class provides the following functions to retrieve quickly the relevant CLIENT instance:

    - -
      -
    • CLIENT.Find(): Find a CLIENT instance from the _DATABASE object using a DCS Unit object.
    • -
    • CLIENT.FindByName(): Find a CLIENT instance from the _DATABASE object using a DCS Unit name.
    • -
    - -

    IMPORTANT: ONE SHOULD NEVER SANATIZE these CLIENT OBJECT REFERENCES! (make the CLIENT object references nil).

    +

    Global(s)

    @@ -119,7 +123,9 @@ If the DCS Unit object does not exist or is nil, the CLIENT methods will return CLIENT +

    CLIENT class, extends Unit#UNIT

    +

    Clients are those Units defined within the Mission Editor that have the skillset defined as Client or Player.

    @@ -135,18 +141,6 @@ If the DCS Unit object does not exist or is nil, the CLIENT methods will return CLIENT:Alive(CallBackFunction, ...)

    Checks for a client alive event and calls a function on a continuous basis.

    - - - - CLIENT.ClassName - - - - - - CLIENT.ClientAlive - - @@ -189,12 +183,6 @@ If the DCS Unit object does not exist or is nil, the CLIENT methods will return CLIENT.ClientGroupUnit - - - - CLIENT.ClientName - - @@ -267,18 +255,6 @@ If the DCS Unit object does not exist or is nil, the CLIENT methods will return CLIENT:Message(Message, MessageDuration, MessageCategory, MessageInterval, MessageID)

    The main message driver for the CLIENT.

    - - - - CLIENT.Messages - - - - - - CLIENT.ONBOARDSIDE - - @@ -333,12 +309,6 @@ If the DCS Unit object does not exist or is nil, the CLIENT methods will return CLIENT._Menus - - - - CLIENT._Tasks - - @@ -354,6 +324,45 @@ If the DCS Unit object does not exist or is nil, the CLIENT methods will return
    +

    CLIENT class, extends Unit#UNIT

    + +

    Clients are those Units defined within the Mission Editor that have the skillset defined as Client or Player.

    + + +

    Note that clients are NOT the same as Units, they are NOT necessarily alive. +The CLIENT class is a wrapper class to handle the DCS Unit objects that have the skillset defined as Client or Player:

    + +
      +
    • Wraps the DCS Unit objects with skill level set to Player or Client.
    • +
    • Support all DCS Unit APIs.
    • +
    • Enhance with Unit specific APIs not in the DCS Group API set.
    • +
    • When player joins Unit, execute alive init logic.
    • +
    • Handles messages to players.
    • +
    • Manage the "state" of the DCS Unit.
    • +
    + +

    Clients are being used by the MISSION class to follow players and register their successes.

    + +

    CLIENT reference methods

    + +

    For each DCS Unit having skill level Player or Client, a CLIENT wrapper object (instance) will be created within the _DATABASE object. +This is done at the beginning of the mission (when the mission starts).

    + +

    The CLIENT class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference +using the DCS Unit or the DCS UnitName.

    + +

    Another thing to know is that CLIENT objects do not "contain" the DCS Unit object. +The CLIENT methods will reference the DCS Unit object by name when it is needed during API execution. +If the DCS Unit object does not exist or is nil, the CLIENT methods will return nil and log an exception in the DCS.log file.

    + +

    The CLIENT class provides the following functions to retrieve quickly the relevant CLIENT instance:

    + +
      +
    • CLIENT.Find(): Find a CLIENT instance from the _DATABASE object using a DCS Unit object.
    • +
    • CLIENT.FindByName(): Find a CLIENT instance from the _DATABASE object using a DCS Unit name.
    • +
    + +

    IMPORTANT: ONE SHOULD NEVER SANATIZE these CLIENT OBJECT REFERENCES! (make the CLIENT object references nil).

    @@ -422,34 +431,6 @@ Create a function that will be called when a player joins the slot.

    #CLIENT:

    - - -
    -
    - - #string - -CLIENT.ClassName - -
    -
    - - - -
    -
    -
    -
    - - #boolean - -CLIENT.ClientAlive - -
    -
    - - -
    @@ -547,19 +528,6 @@ Create a function that will be called when a player joins the slot.

    - -
    -
    -
    - - -CLIENT.ClientName - -
    -
    - - -
    @@ -872,34 +840,6 @@ is the identifier of the message when displayed with intervals.

    - -
    -
    -
    - - - -CLIENT.Messages - -
    -
    - - - -
    -
    -
    -
    - - - -CLIENT.ONBOARDSIDE - -
    -
    - - -
    @@ -1084,20 +1024,6 @@ self

    - -
    -
    -
    - - - -CLIENT._Tasks - -
    -
    - - -
    diff --git a/docs/Documentation/CommandCenter.html b/docs/Documentation/CommandCenter.html index 3384b0fa4..c707cb7f5 100644 --- a/docs/Documentation/CommandCenter.html +++ b/docs/Documentation/CommandCenter.html @@ -17,9 +17,16 @@ index

    Module CommandCenter

    -

    A COMMANDCENTER is the owner of multiple missions within MOOSE.

    +

    Tasking -- A COMMANDCENTER is the owner of multiple missions within MOOSE.

    A COMMANDCENTER governs multiple missions, the tasking and the reporting.

    +
    + +

    Author: Sven Van de Velde (FlightControl)

    + +

    Contributions:

    + +
    + +

    Global(s)

    - - - -
    COMMANDCENTER +

    COMMANDCENTER class, extends Base#BASE

    -
    REPORT - +

    The COMMANDCENTER class governs multiple missions, the tasking and the reporting.

    @@ -99,12 +136,6 @@ COMMANDCENTER:AddMission(Mission)

    Add a MISSION to be governed by the HQ command center.

    - - - - COMMANDCENTER.ClassName - - @@ -120,13 +151,7 @@ - COMMANDCENTER.CommandCenterName - - - - - - COMMANDCENTER.CommandCenterPositionable + COMMANDCENTER.CommunicationMode @@ -165,6 +190,12 @@ COMMANDCENTER:HasGroup(Wrapper, MissionGroup)

    Checks of the COMMANDCENTER has a GROUP.

    + + + + COMMANDCENTER:IsModeWWII() + +

    Returns if the commandcenter operations is in WWII mode

    @@ -183,12 +214,6 @@ COMMANDCENTER:MessageToGroup(Message, TaskGroup, Name)

    Send a CC message to a GROUP.

    - - - - COMMANDCENTER.Name - - @@ -210,7 +235,13 @@ - COMMANDCENTER:ReportSummary(ReportGroup) + COMMANDCENTER:ReportMissionsPlayers(ReportGroup) + +

    Report the players of all MISSIONs to a GROUP.

    + + + + COMMANDCENTER:ReportMissionsStatus(ReportGroup)

    Report the status of all MISSIONs to a GROUP.

    @@ -221,32 +252,18 @@

    Sets the menu structure of the Missions governed by the HQ command center.

    - - -

    Type REPORT

    - - + - + - - - - - - - -
    REPORT:Add(Text)COMMANDCENTER:SetModeWWII() -

    Add a new line to a REPORT.

    +

    Set the commandcenter operations in WWII mode +This will disable LL, MGRS, BRA, BULLS navigatin messages sent by the Command Center, +and will be replaced by a navigation using Reference Zones.

    REPORT.ClassNameCOMMANDCENTER:SetReferenceZones(ReferenceZonePrefix) - -
    REPORT:New(Title) -

    Create a new REPORT.

    -
    REPORT:Text(Delimiter) -

    Produces the text of the report, taking into account an optional delimeter, which is \n by default.

    +

    Set special Reference Zones known by the Command Center to guide airborne pilots during WWII.

    @@ -262,20 +279,57 @@
    +

    COMMANDCENTER class, extends Base#BASE

    +

    The COMMANDCENTER class governs multiple missions, the tasking and the reporting.

    -
    - -
    -
    - - #REPORT - -REPORT - -
    -
    +

    + The commandcenter communicates important messages between the various groups of human players executing tasks in missions.

    + +

    COMMANDCENTER constructor

    + + + +

    Mission Management

    + + + +

    Reference Zones

    + +

    Command Centers may be aware of certain Reference Zones within the battleground. These Reference Zones can refer to +known areas, recognizable buildings or sites, or any other point of interest. +Command Centers will use these Reference Zones to help pilots with defining coordinates in terms of navigation +during the WWII era. +The Reference Zones are related to the WWII mode that the Command Center will operate in. +Use the method COMMANDCENTER.SetModeWWII() to set the mode of communication to the WWII mode.

    + +

    In WWII mode, the Command Center will receive detected targets, and will select for each target the closest +nearby Reference Zone. This allows pilots to navigate easier through the battle field readying for combat.

    + +

    The Reference Zones need to be set by the Mission Designer in the Mission Editor. +Reference Zones are set by normal trigger zones. One can color the zones in a specific color, +and the radius of the zones doesn't matter, only the point is important. Place the center of these Reference Zones at +specific scenery objects or points of interest (like cities, rivers, hills, crossing etc). +The trigger zones indicating a Reference Zone need to follow a specific syntax. +The name of each trigger zone expressing a Reference Zone need to start with a classification name of the object, +followed by a #, followed by a symbolic name of the Reference Zone. +A few examples:

    + +
      +
    • A church at Tskinvali would be indicated as: Church#Tskinvali
    • +
    • A train station near Kobuleti would be indicated as: Station#Kobuleti
    • +
    + +

    The COMMANDCENTER class contains a method to indicate which trigger zones need to be used as Reference Zones. +This is done by using the method COMMANDCENTER.SetReferenceZones(). +For the moment, only one Reference Zone class can be specified, but in the future, more classes will become possible.

    @@ -311,20 +365,6 @@

    Tasking.Mission#MISSION:

    - -
    -
    -
    - - #string - -COMMANDCENTER.ClassName - -
    -
    - - -
    @@ -358,21 +398,8 @@
    #string - -COMMANDCENTER.CommandCenterName - -
    -
    - - - -
    -
    -
    -
    - - -COMMANDCENTER.CommandCenterPositionable + +COMMANDCENTER.CommunicationMode
    @@ -497,6 +524,24 @@ Group#GROUP

    #boolean:

    +
    +
    +
    +
    + + +COMMANDCENTER:IsModeWWII() + +
    +
    + +

    Returns if the commandcenter operations is in WWII mode

    + +

    Return value

    + +

    #boolean: +true if in WWII mode.

    +
    @@ -571,20 +616,6 @@ Group#GROUP

    - -
    -
    -
    - - #string - -COMMANDCENTER.Name - -
    -
    - - -
    @@ -679,8 +710,32 @@ Group#GROUP

    - -COMMANDCENTER:ReportSummary(ReportGroup) + +COMMANDCENTER:ReportMissionsPlayers(ReportGroup) + +
    +
    + +

    Report the players of all MISSIONs to a GROUP.

    + + +

    Each Mission is listed, with an indication how many Tasks are still to be completed.

    + +

    Parameter

    +
      +
    • + +

      ReportGroup :

      + +
    • +
    +
    +
    +
    +
    + + +COMMANDCENTER:ReportMissionsStatus(ReportGroup)
    @@ -713,34 +768,25 @@ Group#GROUP

    - -

    Type REPORT

    - -

    The REPORT class

    - -

    Field(s)

    - -REPORT:Add(Text) + +COMMANDCENTER:SetModeWWII()
    -

    Add a new line to a REPORT.

    +

    Set the commandcenter operations in WWII mode +This will disable LL, MGRS, BRA, BULLS navigatin messages sent by the Command Center, +and will be replaced by a navigation using Reference Zones.

    -

    Parameter

    -
      -
    • - -

      #string Text :

      + +

      It will also disable the settings at the settings menu for these.

      -
    • -

    Return value

    -

    #REPORT:

    +

    #COMMANDCENTER:

    @@ -748,68 +794,53 @@ Group#GROUP

    - #string - -REPORT.ClassName + +COMMANDCENTER:SetReferenceZones(ReferenceZonePrefix)
    +

    Set special Reference Zones known by the Command Center to guide airborne pilots during WWII.

    - -
    -
    -
    -
    - - -REPORT:New(Title) - -
    -
    -

    Create a new REPORT.

    + +

    These Reference Zones are normal trigger zones, with a special naming. +The Reference Zones need to be set by the Mission Designer in the Mission Editor. +Reference Zones are set by normal trigger zones. One can color the zones in a specific color, +and the radius of the zones doesn't matter, only the center of the zone is important. Place the center of these Reference Zones at +specific scenery objects or points of interest (like cities, rivers, hills, crossing etc). +The trigger zones indicating a Reference Zone need to follow a specific syntax. +The name of each trigger zone expressing a Reference Zone need to start with a classification name of the object, +followed by a #, followed by a symbolic name of the Reference Zone. +A few examples:

    + +
      +
    • A church at Tskinvali would be indicated as: Church#Tskinvali
    • +
    • A train station near Kobuleti would be indicated as: Station#Kobuleti
    • +
    + +

    Taking the above example, this is how this method would be used:

    + +
    CC:SetReferenceZones( "Church" )
    +CC:SetReferenceZones( "Station" )
    +
    + +

    Parameter

    • -

      #string Title :

      +

      #string ReferenceZonePrefix : +The name before the #-mark indicating the class of the Reference Zones.

    Return value

    -

    #REPORT:

    +

    #COMMANDCENTER:

    -
    -
    -
    -
    - - -REPORT:Text(Delimiter) - -
    -
    - -

    Produces the text of the report, taking into account an optional delimeter, which is \n by default.

    - -

    Parameter

    -
      -
    • - -

      #string Delimiter : -(optional) A delimiter text.

      - -
    • -
    -

    Return value

    - -

    #string: -The report text.

    -
    diff --git a/docs/Documentation/Controllable.html b/docs/Documentation/Controllable.html index 22bfaf97f..2d5323c07 100644 --- a/docs/Documentation/Controllable.html +++ b/docs/Documentation/Controllable.html @@ -17,9 +17,16 @@ index

    Module Controllable

    -

    This module contains the CONTROLLABLE class.

    +

    Wrapper -- CONTROLLABLE is an intermediate class wrapping Group and Unit classes "controllers".

    -

    1) Controllable#CONTROLLABLE class, extends Positionable#POSITIONABLE

    -

    The Controllable#CONTROLLABLE class is a wrapper class to handle the DCS Controllable objects:

    +
    -
      -
    • Support all DCS Controllable APIs.
    • -
    • Enhance with Controllable specific APIs not in the DCS Controllable API set.
    • -
    • Handle local Controllable Controller.
    • -
    • Manage the "state" of the DCS Controllable.
    • -
    +

    Author: Sven Van de Velde (FlightControl)

    -

    1.1) CONTROLLABLE constructor

    -

    The CONTROLLABLE class provides the following functions to construct a CONTROLLABLE instance:

    - - - -

    1.2) CONTROLLABLE task methods

    -

    Several controllable task methods are available that help you to prepare tasks. -These methods return a string consisting of the task description, which can then be given to either a Controllable#CONTROLLABLE.PushTask or Controllable#SetTask method to assign the task to the CONTROLLABLE. -Tasks are specific for the category of the CONTROLLABLE, more specific, for AIR, GROUND or AIR and GROUND. -Each task description where applicable indicates for which controllable category the task is valid. -There are 2 main subdivisions of tasks: Assigned tasks and EnRoute tasks.

    - -

    1.2.1) Assigned task methods

    - -

    Assigned task methods make the controllable execute the task where the location of the (possible) targets of the task are known before being detected. -This is different from the EnRoute tasks, where the targets of the task need to be detected before the task can be executed.

    - -

    Find below a list of the assigned task methods:

    - - - -

    1.2.2) EnRoute task methods

    - -

    EnRoute tasks require the targets of the task need to be detected by the controllable (using its sensors) before the task can be executed:

    - - - -

    1.2.3) Preparation task methods

    - -

    There are certain task methods that allow to tailor the task behaviour:

    - - - -

    1.2.4) Obtain the mission from controllable templates

    - -

    Controllable templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a controllable and assign it to another:

    - - - -

    1.3) CONTROLLABLE Command methods

    -

    Controllable command methods prepare the execution of commands using the CONTROLLABLE.SetCommand method:

    - - - -

    1.4) CONTROLLABLE Option methods

    -

    Controllable Option methods change the behaviour of the Controllable while being alive.

    - -

    1.4.1) Rule of Engagement:

    - - - -

    To check whether an ROE option is valid for a specific controllable, use:

    - - - -

    1.4.2) Rule on thread:

    - - - -

    To test whether an ROT option is valid for a specific controllable, use:

    - - +

    Contributions:


    @@ -221,19 +123,19 @@ This is different from the EnRoute tasks, where the targets of the task need to CONTROLLABLE +

    CONTROLLABLE class, extends Positionable#POSITIONABLE

    +

    CONTROLLABLE is a wrapper class to handle the "DCS Controllable objects", which are Groups and Units:

    + +
      +
    • Support all DCS Controllable APIs.
    • +

    Type CONTROLLABLE

    - - - - + + + + @@ -327,12 +235,30 @@ This is different from the EnRoute tasks, where the targets of the task need to + + + + + + + + + + + + @@ -360,7 +286,19 @@ This is different from the EnRoute tasks, where the targets of the task need to - + + + + + + + + + @@ -465,6 +403,18 @@ This is different from the EnRoute tasks, where the targets of the task need to + + + + + + + + @@ -480,9 +430,21 @@ This is different from the EnRoute tasks, where the targets of the task need to - + + + + + + + + + @@ -507,6 +469,12 @@ This is different from the EnRoute tasks, where the targets of the task need to + + + + @@ -516,7 +484,7 @@ This is different from the EnRoute tasks, where the targets of the task need to - + @@ -528,7 +496,7 @@ This is different from the EnRoute tasks, where the targets of the task need to - + @@ -594,9 +562,9 @@ This is different from the EnRoute tasks, where the targets of the task need to - + @@ -651,12 +619,24 @@ This is different from the EnRoute tasks, where the targets of the task need to + + + + + + + + @@ -675,12 +655,6 @@ This is different from the EnRoute tasks, where the targets of the task need to - - - - @@ -699,6 +673,22 @@ This is different from the EnRoute tasks, where the targets of the task need to + +
    CONTROLLABLE.ClassName - -
    CONTROLLABLE:ClearTasks()

    Clear all tasks from the controllable.

    @@ -297,6 +199,12 @@ This is different from the EnRoute tasks, where the targets of the task need to
    CONTROLLABLE:EnRouteTaskEngageTargets(Distance, TargetTypes, Priority)

    (AIR) Engaging targets of defined types.

    +
    CONTROLLABLE:EnRouteTaskEngageTargetsInZone(Vec2, Radius, TargetTypes, Priority) +

    (AIR) Engaging a targets of defined types at circle-shaped zone.

    CONTROLLABLE:GetDetectedTargets(DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK)

    Return the detected targets of the controllable.

    +
    CONTROLLABLE:GetFuel() +

    Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks.

    CONTROLLABLE:GetLife()

    Returns the health.

    +
    CONTROLLABLE:GetLife0() +

    Returns the initial health.

    +
    CONTROLLABLE:GetSize() +
    CONTROLLABLE:IsTargetDetected(DCSObject)CONTROLLABLE:HasTask() +

    Checking the Task Queue of the controllable.

    +
    CONTROLLABLE:IsAirPlane() +

    Returns if the Controllable contains AirPlanes.

    +
    CONTROLLABLE:IsTargetDetected(DCSObject, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) CONTROLLABLE:OptionROTVerticalPossible()

    Can the CONTROLLABLE evade on fire using vertical manoeuvres?

    +
    CONTROLLABLE:OptionRTBAmmo(WeaponsFlag) +

    Set RTB on ammo.

    +
    CONTROLLABLE:OptionRTBBingoFuel(RTB) +

    Set RTB on bingo fuel.

    CONTROLLABLE:Route(GoPoints)CONTROLLABLE:Route(Route, DelaySeconds)

    Make the controllable to follow a given route.

    +
    CONTROLLABLE:RouteAirTo(ToCoordinate, AltType, Type, Action, Speed, DelaySeconds) +

    Make the AIR Controllable fly towards a specific point.

    +
    CONTROLLABLE:RouteGroundTo(ToCoordinate, Speed, Formation, DelaySeconds) +

    Make the GROUND Controllable to drive towards a specific point.

    CONTROLLABLE:SetTask(DCSTask, WaitTime)

    Clearing the Task Queue and Setting the Task on the queue from the controllable.

    +
    CONTROLLABLE:SetTaskWaypoint(Waypoint, Task) +

    Set a Task at a Waypoint using a Route list.

    CONTROLLABLE:TaskAttackMapObject(Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack)CONTROLLABLE:TaskAttackMapObject(Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType)

    (AIR) Attacking the map object (building, structure, e.t.c).

    CONTROLLABLE:TaskBombing(Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack)CONTROLLABLE:TaskBombing(Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType)

    (AIR) Delivering weapon at the point on the ground.

    CONTROLLABLE:TaskFunction(WayPoint, WayPointIndex, FunctionString, FunctionArguments)CONTROLLABLE:TaskFunction(FunctionString, ...) - +

    This creates a Task element, with an action to call a function as part of a Wrapped Task.

    CONTROLLABLE:TaskRoute(Points)

    Return a Misson task to follow a given route defined by Points.

    +
    CONTROLLABLE:TaskRouteToVec2(Vec2, Speed, Formation) +

    (GROUND) Route the controllable to a given Vec2.

    CONTROLLABLE:TaskRouteToZone(Zone, Randomize, Speed, Formation)

    (AIR + GROUND) Route the controllable to a given zone.

    +
    CONTROLLABLE.TaskScheduler +
    CONTROLLABLE:WayPointFunction(WayPoint, WayPointIndex, WayPointFunction, ...)

    Registers a waypoint function that will be executed when the controllable moves over the WayPoint.

    -
    CONTROLLABLE.WayPointFunctions -
    CONTROLLABLE:_GetController()

    Get the controller for the CONTROLLABLE.

    +
    + +

    Type Vec2

    + + + + + + + +
    Vec2.x + +
    Vec2.y +
    @@ -714,6 +704,177 @@ This is different from the EnRoute tasks, where the targets of the task need to
    +

    CONTROLLABLE class, extends Positionable#POSITIONABLE

    + +

    CONTROLLABLE is a wrapper class to handle the "DCS Controllable objects", which are Groups and Units:

    + +
      +
    • Support all DCS Controllable APIs.
    • +
    + + +
      +
    • Enhance with Controllable specific APIs not in the DCS Controllable API set.
    • +
    • Handle local Controllable Controller.
    • +
    • Manage the "state" of the DCS Controllable.
    • +
    + +

    CONTROLLABLE constructor

    + +

    The CONTROLLABLE class provides the following functions to construct a CONTROLLABLE instance:

    + + + +

    CONTROLLABLE Task methods

    + +

    Several controllable task methods are available that help you to prepare tasks. +These methods return a string consisting of the task description, which can then be given to either a Controllable#CONTROLLABLE.PushTask or Controllable#SetTask method to assign the task to the CONTROLLABLE. +Tasks are specific for the category of the CONTROLLABLE, more specific, for AIR, GROUND or AIR and GROUND. +Each task description where applicable indicates for which controllable category the task is valid. +There are 2 main subdivisions of tasks: Assigned tasks and EnRoute tasks.

    + +

    Task assignment

    + +

    Assigned task methods make the controllable execute the task where the location of the (possible) targets of the task are known before being detected. +This is different from the EnRoute tasks, where the targets of the task need to be detected before the task can be executed.

    + +

    Find below a list of the assigned task methods:

    + + + +

    EnRoute assignment

    + +

    EnRoute tasks require the targets of the task need to be detected by the controllable (using its sensors) before the task can be executed:

    + + + +

    Task preparation

    + +

    There are certain task methods that allow to tailor the task behaviour:

    + + + +

    Call a function as a Task

    + +

    A function can be called which is part of a Task. The method CONTROLLABLE.TaskFunction() prepares +a Task that can call a GLOBAL function from within the Controller execution. +This method can also be used to embed a function call when a certain waypoint has been reached. +See below the Tasks at Waypoints section.

    + +

    Demonstration Mission: GRP-502 - Route at waypoint to random point

    + +

    Tasks at Waypoints

    + +

    Special Task methods are available to set tasks at certain waypoints. +The method CONTROLLABLE.SetTaskAtWaypoint() helps preparing a Route, embedding a Task at the Waypoint of the Route.

    + +

    This creates a Task element, with an action to call a function as part of a Wrapped Task.

    + +

    Obtain the mission from controllable templates

    + +

    Controllable templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a controllable and assign it to another:

    + + + +

    CONTROLLABLE Command methods

    + +

    Controllable command methods prepare the execution of commands using the CONTROLLABLE.SetCommand method:

    + + + +

    Routing of Controllables

    + +

    Different routing methods exist to route GROUPs and UNITs to different locations:

    + + + +

    Option methods

    + +

    Controllable Option methods change the behaviour of the Controllable while being alive.

    + +

    Rule of Engagement:

    + + + +

    To check whether an ROE option is valid for a specific controllable, use:

    + + + +

    Rule on thread:

    + + + +

    To test whether an ROT option is valid for a specific controllable, use:

    + +
    @@ -721,24 +882,7 @@ This is different from the EnRoute tasks, where the targets of the task need to

    Type Controllable

    Type CONTROLLABLE

    - -

    The CONTROLLABLE class

    - -

    Field(s)

    -
    -
    - - #string - -CONTROLLABLE.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -1090,6 +1234,51 @@ The DCS task structure.

    + +CONTROLLABLE:EnRouteTaskEngageTargetsInZone(Vec2, Radius, TargetTypes, Priority) + +
    +
    + +

    (AIR) Engaging a targets of defined types at circle-shaped zone.

    + +

    Parameters

    +
      +
    • + +

      Dcs.DCSTypes#Vec2 Vec2 : +2D-coordinates of the zone.

      + +
    • +
    • + +

      Dcs.DCSTypes#Distance Radius : +Radius of the zone.

      + +
    • +
    • + +

      Dcs.DCSTypes#AttributeNameArray TargetTypes : +Array of target categories allowed to engage.

      + +
    • +
    • + +

      #number Priority : +All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first.

      + +
    • +
    +

    Return value

    + +

    Dcs.DCSTasking.Task#Task: +The DCS task structure.

    + +
    +
    +
    +
    + CONTROLLABLE:EnRouteTaskEngageUnit(EngageUnit, Priority, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack) @@ -1339,6 +1528,27 @@ DetectedTargets

    + +CONTROLLABLE:GetFuel() + +
    +
    + +

    Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks.

    + + +

    This method returns nil to ensure polymorphic behaviour! This method needs to be overridden by GROUP or UNIT.

    + +

    Return value

    + +

    #nil: +The CONTROLLABLE is not existing or alive.

    + +
    +
    +
    +
    + CONTROLLABLE:GetLife() @@ -1365,6 +1575,47 @@ The controllable is not existing or alive.

    + +
    +
    +
    + + +CONTROLLABLE:GetLife0() + +
    +
    + +

    Returns the initial health.

    + +

    Return values

    +
      +
    1. + +

      #number: +The controllable health value (unit or group average).

      + +
    2. +
    3. + +

      #nil: +The controllable is not existing or alive.

      + +
    4. +
    +
    +
    +
    +
    + + +CONTROLLABLE:GetSize() + +
    +
    + + +
    @@ -1443,20 +1694,89 @@ WayPoints If WayPoints is given, then return the WayPoints structure.

    + +CONTROLLABLE:HasTask() + +
    +
    + +

    Checking the Task Queue of the controllable.

    + + +

    Returns false if no task is on the queue. true if there is a task.

    + +

    Return value

    + +

    Wrapper.Controllable#CONTROLLABLE: +self

    + +
    +
    +
    +
    + + +CONTROLLABLE:IsAirPlane() + +
    +
    + +

    Returns if the Controllable contains AirPlanes.

    + +

    Return value

    + +

    #boolean: +true if Controllable contains AirPlanes.

    + +
    +
    +
    +
    + -CONTROLLABLE:IsTargetDetected(DCSObject) +CONTROLLABLE:IsTargetDetected(DCSObject, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK)
    -

    Parameter

    +

    Parameters

    • DCSObject :

      +
    • +
    • + +

      DetectVisual :

      + +
    • +
    • + +

      DetectOptical :

      + +
    • +
    • + +

      DetectRadar :

      + +
    • +
    • + +

      DetectIRST :

      + +
    • +
    • + +

      DetectRWR :

      + +
    • +
    • + +

      DetectDLINK :

      +
    @@ -1774,6 +2094,61 @@ self

    #boolean:

    + +
    +
    +
    + + +CONTROLLABLE:OptionRTBAmmo(WeaponsFlag) + +
    +
    + +

    Set RTB on ammo.

    + +

    Parameter

    +
      +
    • + +

      #boolean WeaponsFlag : +Weapons.flag enumerator.

      + +
    • +
    +

    Return value

    + +

    #CONTROLLABLE: +self

    + +
    +
    +
    +
    + + +CONTROLLABLE:OptionRTBBingoFuel(RTB) + +
    +
    + +

    Set RTB on bingo fuel.

    + +

    Parameter

    +
      +
    • + +

      #boolean RTB : +true if RTB on bingo fuel (default), false if no RTB on bingo fuel. +Warning! When you switch this option off, the airborne group will continue to fly until all fuel has been consumed, and will crash.

      + +
    • +
    +

    Return value

    + +

    #CONTROLLABLE: +self

    +
    @@ -1829,26 +2204,134 @@ self

    -CONTROLLABLE:Route(GoPoints) +CONTROLLABLE:Route(Route, DelaySeconds)

    Make the controllable to follow a given route.

    -

    Parameter

    +

    Parameters

    • -

      #table GoPoints : +

      #table Route : A table of Route Points.

      +
    • +
    • + +

      #number DelaySeconds : +Wait for the specified seconds before executing the Route.

      +

    Return value

    #CONTROLLABLE: -self

    +The CONTROLLABLE.

    + +
    +
    +
    +
    + + +CONTROLLABLE:RouteAirTo(ToCoordinate, AltType, Type, Action, Speed, DelaySeconds) + +
    +
    + +

    Make the AIR Controllable fly towards a specific point.

    + +

    Parameters

    + +

    Return value

    + +

    #CONTROLLABLE: +The CONTROLLABLE.

    + +
    +
    +
    +
    + + +CONTROLLABLE:RouteGroundTo(ToCoordinate, Speed, Formation, DelaySeconds) + +
    +
    + +

    Make the GROUND Controllable to drive towards a specific point.

    + +

    Parameters

    +
      +
    • + +

      Core.Point#COORDINATE ToCoordinate : +A Coordinate to drive to.

      + +
    • +
    • + +

      #number Speed : +(optional) Speed in km/h. The default speed is 999 km/h.

      + +
    • +
    • + +

      #string Formation : +(optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right".

      + +
    • +
    • + +

      #number DelaySeconds : +Wait for the specified seconds before executing the Route.

      + +
    • +
    +

    Return value

    + +

    #CONTROLLABLE: +The CONTROLLABLE.

    @@ -1973,6 +2456,39 @@ self

    Wrapper.Controllable#CONTROLLABLE: self

    + +
    +
    +
    + + +CONTROLLABLE:SetTaskWaypoint(Waypoint, Task) + +
    +
    + +

    Set a Task at a Waypoint using a Route list.

    + +

    Parameters

    +
      +
    • + +

      #table Waypoint : +The Waypoint!

      + +
    • +
    • + +

      Dcs.DCSTasking.Task#Task Task : +The Task structure to be executed!

      + +
    • +
    +

    Return value

    + +

    Dcs.DCSTasking.Task#Task:

    + +
    @@ -2042,7 +2558,7 @@ The DCS task structure.

    -CONTROLLABLE:TaskAttackMapObject(Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack) +CONTROLLABLE:TaskAttackMapObject(Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType)
    @@ -2054,13 +2570,13 @@ The DCS task structure.

  • Dcs.DCSTypes#Vec2 Vec2 : -2D-coordinates of the point the map object is closest to. The distance between the point and the map object must not be greater than 2000 meters. Object id is not used here because Mission Editor doesn't support map object identificators.

    +2D-coordinates of the point to deliver weapon at.

  • -

    #number WeaponType : -(optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage.

    +

    #boolean GroupAttack : +(optional) If true, all units in the group will attack the Unit when found.

  • @@ -2083,8 +2599,14 @@ The DCS task structure.

  • -

    #boolean ControllableAttack : -(optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft.

    +

    #number Altitude : +(optional) The altitude from where to attack.

    + +
  • +
  • + +

    #number WeaponType : +(optional) The WeaponType.

  • @@ -2168,7 +2690,7 @@ The DCS task structure.

    -CONTROLLABLE:TaskBombing(Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack) +CONTROLLABLE:TaskBombing(Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType)
    @@ -2185,8 +2707,8 @@ The DCS task structure.

  • -

    #number WeaponType : -(optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage.

    +

    #boolean GroupAttack : +(optional) If true, all units in the group will attack the Unit when found.

  • @@ -2198,7 +2720,7 @@ The DCS task structure.

  • #number AttackQty : -(optional) Desired quantity of passes. The parameter is not the same in AttackGroup and AttackUnit tasks.

    +(optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.

  • @@ -2209,8 +2731,14 @@ The DCS task structure.

  • -

    #boolean ControllableAttack : -(optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft.

    +

    #number Altitude : +(optional) The altitude from where to attack.

    + +
  • +
  • + +

    #number WeaponType : +(optional) The WeaponType.

  • @@ -2655,36 +3183,79 @@ The DCS task structure.

    -CONTROLLABLE:TaskFunction(WayPoint, WayPointIndex, FunctionString, FunctionArguments) +CONTROLLABLE:TaskFunction(FunctionString, ...)
    +

    This creates a Task element, with an action to call a function as part of a Wrapped Task.

    + +

    This Task can then be embedded at a Waypoint by calling the method CONTROLLABLE.SetTaskAtWaypoint.

    Parameters

    • -

      WayPoint :

      +

      #string FunctionString : +The function name embedded as a string that will be called.

    • -

      WayPointIndex :

      - -
    • -
    • - -

      FunctionString :

      - -
    • -
    • - -

      FunctionArguments :

      +

      ... : +The variable arguments passed to the function when called! These arguments can be of any type!

    +

    Return value

    + +

    #CONTROLLABLE:

    + + +

    Usage:

    +
    
    + local ZoneList = { 
    +   ZONE:New( "ZONE1" ), 
    +   ZONE:New( "ZONE2" ), 
    +   ZONE:New( "ZONE3" ), 
    +   ZONE:New( "ZONE4" ), 
    +   ZONE:New( "ZONE5" ) 
    + }
    + 
    + GroundGroup = GROUP:FindByName( "Vehicle" )
    + 
    + --- @param Wrapper.Group#GROUP GroundGroup
    + function RouteToZone( Vehicle, ZoneRoute )
    + 
    +   local Route = {}
    +   
    +   Vehicle:E( { ZoneRoute = ZoneRoute } )
    +   
    +   Vehicle:MessageToAll( "Moving to zone " .. ZoneRoute:GetName(), 10 )
    + 
    +   -- Get the current coordinate of the Vehicle
    +   local FromCoord = Vehicle:GetCoordinate()
    +   
    +   -- Select a random Zone and get the Coordinate of the new Zone.
    +   local RandomZone = ZoneList[ math.random( 1, #ZoneList ) ] -- Core.Zone#ZONE
    +   local ToCoord = RandomZone:GetCoordinate()
    +   
    +   -- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
    +   Route[#Route+1] = FromCoord:WaypointGround( 72 )
    +   Route[#Route+1] = ToCoord:WaypointGround( 60, "Vee" )
    +   
    +   local TaskRouteToZone = Vehicle:TaskFunction( "RouteToZone", RandomZone )
    +   
    +   Vehicle:SetTaskAtWaypoint( Route, #Route, TaskRouteToZone ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
    + 
    +   Vehicle:Route( Route, math.random( 10, 20 ) ) -- Move after a random seconds to the Route. See the Route method for details.
    +   
    + end
    +   
    +   RouteToZone( GroundGroup, ZoneList[1] )
    +
    +
    @@ -2956,6 +3527,44 @@ A table of route points.

    + +CONTROLLABLE:TaskRouteToVec2(Vec2, Speed, Formation) + +
    +
    + +

    (GROUND) Route the controllable to a given Vec2.

    + + +

    A speed can be given in km/h. +A given formation can be given.

    + +

    Parameters

    +
      +
    • + +

      #Vec2 Vec2 : +The Vec2 where to route to.

      + +
    • +
    • + +

      #number Speed : +The speed.

      + +
    • +
    • + +

      Base#FORMATION Formation : +The formation string.

      + +
    • +
    +
    +
    +
    +
    + CONTROLLABLE:TaskRouteToZone(Zone, Randomize, Speed, Formation) @@ -2996,6 +3605,20 @@ The formation string.

    + +
    +
    +
    + + + +CONTROLLABLE.TaskScheduler + +
    +
    + + +
    @@ -3108,20 +3731,6 @@ The waypoint function to be called when the controllable moves over the waypoint

    #CONTROLLABLE:

    - -
    -
    -
    - - - -CONTROLLABLE.WayPointFunctions - -
    -
    - - -
    @@ -3193,6 +3802,37 @@ If WayPoints is given, then use the route.

    Type DCSStopCondition

    +

    Type Vec2

    +

    Field(s)

    +
    +
    + + + +Vec2.x + +
    +
    + + + +
    +
    +
    +
    + + + +Vec2.y + +
    +
    + + + +
    +
    +

    Type list

    diff --git a/docs/Documentation/DCSAirbase.html b/docs/Documentation/DCSAirbase.html index d47a0d2d4..dcf1f5e56 100644 --- a/docs/Documentation/DCSAirbase.html +++ b/docs/Documentation/DCSAirbase.html @@ -17,7 +17,17 @@ index

    Module Database

    -

    This module contains the DATABASE class, managing the database of mission objects.

    +

    Core -- DATABASE manages the database of mission objects.

    @@ -83,12 +115,14 @@

    Mission designers can use the DATABASE class to refer to:

      +
    • STATICS
    • UNITS
    • GROUPS
    • CLIENTS
    • -
    • AIRPORTS
    • +
    • AIRBASES
    • PLAYERSJOINED
    • PLAYERS
    • +
    • CARGOS

    On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor.

    @@ -113,12 +147,23 @@ The following iterator methods are currently available within the DATABASE:


    +

    Author: Sven Van de Velde (FlightControl)

    +

    Contributions:

    + +
    +

    Global(s)

    + + + +
    DATABASE +
    PlayerCheckSchedule +
    @@ -131,9 +176,15 @@ The following iterator methods are currently available within the DATABASE:

    - DATABASE:AddAirbase(DCSAirbaseName) + DATABASE:AddAirbase(AirbaseName)

    Adds a Airbase based on the Airbase Name in the DATABASE.

    + + + + DATABASE:AddCargo(CargoName, Cargo) + +

    Adds a Cargo based on the Cargo Name in the DATABASE.

    @@ -164,6 +215,12 @@ The following iterator methods are currently available within the DATABASE:

    DATABASE:AddUnit(DCSUnitName)

    Adds a Unit based on the Unit Name in the DATABASE.

    + + + + DATABASE.CARGOS + + @@ -191,13 +248,19 @@ The following iterator methods are currently available within the DATABASE:

    - DATABASE:DeleteAirbase(DCSAirbaseName) + DATABASE:DeleteAirbase(AirbaseName)

    Deletes a Airbase from the DATABASE based on the Airbase Name.

    - DATABASE:DeletePlayer(PlayerName) + DATABASE:DeleteCargo(CargoName) + +

    Deletes a Cargo from the DATABASE based on the Cargo Name.

    + + + + DATABASE:DeletePlayer(UnitName, PlayerName)

    Deletes a player from the DATABASE based on the Player Name.

    @@ -218,6 +281,12 @@ The following iterator methods are currently available within the DATABASE:

    DATABASE:FindAirbase(AirbaseName)

    Finds a AIRBASE based on the AirbaseName.

    + + + + DATABASE:FindCargo(CargoName) + +

    Finds an CARGO based on the CargoName.

    @@ -248,6 +317,12 @@ The following iterator methods are currently available within the DATABASE:

    DATABASE:ForEach(IteratorFunction, FinalizeFunction, arg, Set)

    Iterate the DATABASE and call an iterator function for the given set, providing the Object for each element within the set and optional parameters.

    + + + + DATABASE:ForEachCargo(IteratorFunction, ...) + +

    Iterate the DATABASE and call an iterator function for each CARGO, providing the CARGO object to the function and optional parameters.

    @@ -257,21 +332,33 @@ The following iterator methods are currently available within the DATABASE:

    - DATABASE:ForEachGroup(IteratorFunction, ...) + DATABASE:ForEachGroup(IteratorFunction, FinalizeFunction, ...)

    Iterate the DATABASE and call an iterator function for each alive GROUP, providing the GROUP and optional parameters.

    - DATABASE:ForEachPlayer(IteratorFunction, ...) + DATABASE:ForEachPlayer(IteratorFunction, FinalizeFunction, ...)

    Iterate the DATABASE and call an iterator function for each ALIVE player, providing the player name and optional parameters.

    - DATABASE:ForEachPlayerJoined(IteratorFunction, ...) + DATABASE:ForEachPlayerJoined(IteratorFunction, FinalizeFunction, ...)

    Iterate the DATABASE and call an iterator function for each player who has joined the mission, providing the Unit of the player and optional parameters.

    + + + + DATABASE:ForEachPlayerUnit(IteratorFunction, FinalizeFunction, ...) + +

    Iterate the DATABASE and call an iterator function for each ALIVE player UNIT, providing the player UNIT and optional parameters.

    + + + + DATABASE:ForEachStatic(IteratorFunction, FinalizeFunction, ...) + +

    Iterate the DATABASE and call an iterator function for each alive STATIC, providing the STATIC and optional parameters.

    @@ -332,6 +419,18 @@ The following iterator methods are currently available within the DATABASE:

    DATABASE:GetGroupTemplateFromUnitName(UnitName) + + + + DATABASE:GetPlayerSettings(PlayerName) + +

    Gets the player settings

    + + + + DATABASE:GetStaticUnitTemplate(StaticName) + + @@ -344,30 +443,66 @@ The following iterator methods are currently available within the DATABASE:

    DATABASE.NavPoints + + + + DATABASE.Navpoints + + DATABASE:New()

    Creates a new DATABASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names.

    + + + + DATABASE:OnEventDeleteCargo(EventData) + +

    Handles the OnEventDeleteCargo.

    + + + + DATABASE:OnEventNewCargo(EventData) + +

    Handles the OnEventNewCargo event.

    DATABASE.PLAYERS + + + + DATABASE.PLAYERSETTINGS + + DATABASE.PLAYERSJOINED + + + + DATABASE.PLAYERUNITS + + DATABASE.STATICS + + + + DATABASE:SetPlayerSettings(PlayerName, Settings) + +

    Sets the player settings

    @@ -392,6 +527,24 @@ The following iterator methods are currently available within the DATABASE:

    DATABASE.UNITS + + + + DATABASE.UNITS_Index + + + + + + DATABASE.UNITS_Position + + + + + + DATABASE.ZONENAMES + + @@ -428,6 +581,12 @@ The following iterator methods are currently available within the DATABASE:

    DATABASE:_RegisterClients()

    Private method that registers all Units of skill Client or Player within in the mission.

    + + + + DATABASE:_RegisterGroupTemplate(GroupTemplate, CoalitionID, CategoryID, CountryID) + +

    Private method that registers new Group Templates within the DATABASE Object.

    @@ -443,15 +602,15 @@ The following iterator methods are currently available within the DATABASE:

    - DATABASE:_RegisterStatics() + DATABASE:_RegisterStaticTemplate(GroupTemplate, StaticTemplate, CoalitionID, CategoryID, CountryID) - +

    Private method that registers new Static Templates within the DATABASE Object.

    - DATABASE:_RegisterTemplate(GroupTemplate, CoalitionID, CategoryID, CountryID) + DATABASE:_RegisterStatics() -

    Private method that registers new Group Templates within the DATABASE Object.

    + @@ -475,6 +634,20 @@ The following iterator methods are currently available within the DATABASE:

    + + +
    +
    + + + +PlayerCheckSchedule + +
    +
    + + +

    Type Database

    @@ -502,7 +675,7 @@ The following iterator methods are currently available within the DATABASE:

    -DATABASE:AddAirbase(DCSAirbaseName) +DATABASE:AddAirbase(AirbaseName)
    @@ -513,7 +686,35 @@ The following iterator methods are currently available within the DATABASE:

    • -

      DCSAirbaseName :

      +

      #string AirbaseName : +The name of the airbase

      + +
    • +
    +
    + +
    +
    + + +DATABASE:AddCargo(CargoName, Cargo) + +
    +
    + +

    Adds a Cargo based on the Cargo Name in the DATABASE.

    + +

    Parameters

    +
      +
    • + +

      #string CargoName : +The name of the airbase

      + +
    • +
    • + +

      Cargo :

    @@ -627,6 +828,20 @@ The following iterator methods are currently available within the DATABASE:

    +
    +
    +
    +
    + + + +DATABASE.CARGOS + +
    +
    + + +
    @@ -689,7 +904,7 @@ The following iterator methods are currently available within the DATABASE:

    -DATABASE:DeleteAirbase(DCSAirbaseName) +DATABASE:DeleteAirbase(AirbaseName)
    @@ -700,7 +915,30 @@ The following iterator methods are currently available within the DATABASE:

    • -

      DCSAirbaseName :

      +

      #string AirbaseName : +The name of the airbase

      + +
    • +
    +
    +
    +
    +
    + + +DATABASE:DeleteCargo(CargoName) + +
    +
    + +

    Deletes a Cargo from the DATABASE based on the Cargo Name.

    + +

    Parameter

    +
      +
    • + +

      #string CargoName : +The name of the airbase

    @@ -710,17 +948,22 @@ The following iterator methods are currently available within the DATABASE:

    -DATABASE:DeletePlayer(PlayerName) +DATABASE:DeletePlayer(UnitName, PlayerName)

    Deletes a player from the DATABASE based on the Player Name.

    -

    Parameter

    +

    Parameters

    +

    Return value

    + +

    #DATABASE: +self

    + +
    +
    +
    +
    + + +DATABASE:ForEachPlayerUnit(IteratorFunction, FinalizeFunction, ...) + +
    +
    + +

    Iterate the DATABASE and call an iterator function for each ALIVE player UNIT, providing the player UNIT and optional parameters.

    + +

    Parameters

    +
      +
    • + +

      #function IteratorFunction : +The function that will be called for each object in the database. The function needs to accept the player name.

      + +
    • +
    • + +

      FinalizeFunction :

      + +
    • +
    • + +

      ... :

      + +
    • +
    +

    Return value

    + +

    #DATABASE: +self

    + +
    +
    +
    +
    + + +DATABASE:ForEachStatic(IteratorFunction, FinalizeFunction, ...) + +
    +
    + +

    Iterate the DATABASE and call an iterator function for each alive STATIC, providing the STATIC and optional parameters.

    + +

    Parameters

    +
      +
    • + +

      #function IteratorFunction : +The function that will be called for each object in the database. The function needs to accept a STATIC parameter.

      + +
    • +
    • + +

      FinalizeFunction :

    • @@ -1085,7 +1475,7 @@ self

    • #function IteratorFunction : -The function that will be called when there is an alive UNIT in the database. The function needs to accept a UNIT parameter.

      +The function that will be called for each object in the database. The function needs to accept a UNIT parameter.

    • @@ -1291,6 +1681,53 @@ self

      + +DATABASE:GetPlayerSettings(PlayerName) + +
      +
      + +

      Gets the player settings

      + +

      Parameter

      +
        +
      • + +

        #string PlayerName :

        + +
      • +
      +

      Return value

      + +

      Core.Settings#SETTINGS:

      + + +
      +
      +
      +
      + + +DATABASE:GetStaticUnitTemplate(StaticName) + +
      +
      + + + +

      Parameter

      +
        +
      • + +

        StaticName :

        + +
      • +
      +
      +
      +
      +
      + DATABASE:GetStatusGroup(GroupName) @@ -1321,6 +1758,20 @@ self

      +
    +
    +
    +
    + + + +DATABASE.Navpoints + +
    +
    + + +
    @@ -1343,6 +1794,48 @@ self

    -- Define a new DATABASE Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE.
     DBObject = DATABASE:New()
    + +
    +
    +
    + + +DATABASE:OnEventDeleteCargo(EventData) + +
    +
    + +

    Handles the OnEventDeleteCargo.

    + +

    Parameter

    + +
    +
    +
    +
    + + +DATABASE:OnEventNewCargo(EventData) + +
    +
    + +

    Handles the OnEventNewCargo event.

    + +

    Parameter

    +
    @@ -1357,6 +1850,20 @@ DBObject = DATABASE:New() + +
    +
    +
    + + + +DATABASE.PLAYERSETTINGS + +
    +
    + + +
    @@ -1371,6 +1878,20 @@ DBObject = DATABASE:New() + +
    +
    +
    + + + +DATABASE.PLAYERUNITS + +
    +
    + + +
    @@ -1385,6 +1906,37 @@ DBObject = DATABASE:New() + +
    +
    +
    + + +DATABASE:SetPlayerSettings(PlayerName, Settings) + +
    +
    + +

    Sets the player settings

    + +

    Parameters

    + +

    Return value

    + +

    Core.Settings#SETTINGS:

    + +
    @@ -1470,6 +2022,48 @@ self

    + +
    +
    +
    + + + +DATABASE.UNITS_Index + +
    +
    + + + +
    +
    +
    +
    + + #number + +DATABASE.UNITS_Position + +
    +
    + + + +
    +
    +
    +
    + + + +DATABASE.ZONENAMES + +
    +
    + + +
    @@ -1590,57 +2184,8 @@ self

    - -DATABASE:_RegisterGroupsAndUnits() - -
    -
    - -

    Private method that registers all Groups and Units within in the mission.

    - -

    Return value

    - -

    #DATABASE: -self

    - -
    -
    -
    -
    - - -DATABASE:_RegisterPlayers() - -
    -
    - -

    Private method that registers all alive players in the mission.

    - -

    Return value

    - -

    #DATABASE: -self

    - -
    -
    -
    -
    - - -DATABASE:_RegisterStatics() - -
    -
    - - - -
    -
    -
    -
    - - -DATABASE:_RegisterTemplate(GroupTemplate, CoalitionID, CategoryID, CountryID) + +DATABASE:_RegisterGroupTemplate(GroupTemplate, CoalitionID, CategoryID, CountryID)
    @@ -1675,6 +2220,101 @@ self

    #DATABASE: self

    +
    +
    +
    +
    + + +DATABASE:_RegisterGroupsAndUnits() + +
    +
    + +

    Private method that registers all Groups and Units within in the mission.

    + +

    Return value

    + +

    #DATABASE: +self

    + +
    +
    +
    +
    + + +DATABASE:_RegisterPlayers() + +
    +
    + +

    Private method that registers all alive players in the mission.

    + +

    Return value

    + +

    #DATABASE: +self

    + +
    +
    +
    +
    + + +DATABASE:_RegisterStaticTemplate(GroupTemplate, StaticTemplate, CoalitionID, CategoryID, CountryID) + +
    +
    + +

    Private method that registers new Static Templates within the DATABASE Object.

    + +

    Parameters

    +
      +
    • + +

      #table GroupTemplate :

      + +
    • +
    • + +

      StaticTemplate :

      + +
    • +
    • + +

      CoalitionID :

      + +
    • +
    • + +

      CategoryID :

      + +
    • +
    • + +

      CountryID :

      + +
    • +
    +

    Return value

    + +

    #DATABASE: +self

    + +
    +
    +
    +
    + + +DATABASE:_RegisterStatics() + +
    +
    + + +
    diff --git a/docs/Documentation/Designate.html b/docs/Documentation/Designate.html new file mode 100644 index 000000000..31210aea8 --- /dev/null +++ b/docs/Documentation/Designate.html @@ -0,0 +1,2846 @@ + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +

    Module Designate

    + +

    Functional -- Management of target Designation.

    + + +

    Lase, smoke and illuminate targets.

    + +

    --Banner Image

    + +
    + +

    DESIGNATE is orchestrating the designation of potential targets executed by a Recce group, +and communicates these to a dedicated attacking group of players, +so that following a dynamically generated menu system, +each detected set of potential targets can be lased or smoked...

    + +

    Targets can be:

    + +
      +
    • Lased for a period of time.
    • +
    • Smoked. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.)
    • +
    • Illuminated through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.
    • +
    + +
    + +

    AUTHORS and CONTRIBUTIONS

    + +

    Contributions:

    + +
      +
    • Ciribob: Showing the way how to lase targets + how laser codes work!!! Explained the autolase script.
    • +
    • EasyEB: Ideas and Beta Testing
    • +
    • Wingthor: Beta Testing
    • +
    + + +

    Authors:

    + +
      +
    • FlightControl: Design & Programming
    • +
    + + +

    Global(s)

    + + + + + +
    DESIGNATE +

    DESIGNATE class, extends Fsm#FSM

    + +

    DESIGNATE is orchestrating the designation of potential targets executed by a Recce group, +and communicates these to a dedicated attacking group of players, +so that following a dynamically generated menu system, +each detected set of potential targets can be lased or smoked...

    +
    +

    Type DESIGNATE

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DESIGNATE:AddMenuLaserCode(LaserCode, MenuText) +

    Add a specific lase code to the designate lase menu to lase targets with a specific laser code.

    +
    DESIGNATE.AttackSet + +
    DESIGNATE.AutoLase + +
    DESIGNATE.CC + +
    DESIGNATE:CoordinateLase() +

    Coordinates the Auto Lase.

    +
    DESIGNATE.DesignateName + +
    DESIGNATE.Designating + +
    DESIGNATE:DesignationScope() +

    Adapt the designation scope according the detected items.

    +
    DESIGNATE:Detect() +

    Detect Trigger for DESIGNATE

    +
    DESIGNATE.Detection + +
    DESIGNATE.FlashStatusMenu + +
    DESIGNATE:GenerateLaserCodes() +

    Generate an array of possible laser codes.

    +
    DESIGNATE:Illuminate() +

    Illuminate Trigger for DESIGNATE

    +
    DESIGNATE.LaseDuration + +
    DESIGNATE:LaseOff() +

    LaseOff Trigger for DESIGNATE

    +
    DESIGNATE:LaseOn() +

    LaseOn Trigger for DESIGNATE

    +
    DESIGNATE.LaserCodes + +
    DESIGNATE.LaserCodesUsed + +
    DESIGNATE.MarkScheduler + +
    DESIGNATE.MaximumDesignations + +
    DESIGNATE.MaximumDistanceAirDesignation + +
    DESIGNATE.MaximumDistanceDesignations + +
    DESIGNATE.MaximumDistanceGroundDesignation + +
    DESIGNATE.MaximumMarkings + +
    DESIGNATE:MenuAutoLase(AutoLase) + +
    DESIGNATE.MenuDesignate + +
    DESIGNATE:MenuFlashStatus(AttackGroup, Flash) + +
    DESIGNATE:MenuForget(Index) + +
    DESIGNATE:MenuIlluminate(Index) + +
    DESIGNATE:MenuLaseCode(Index, Duration, LaserCode) + +
    DESIGNATE:MenuLaseOff(Index, Duration) + +
    DESIGNATE:MenuLaseOn(Index, Duration) + +
    DESIGNATE.MenuLaserCodes + +
    DESIGNATE:MenuSmoke(Index, Color) + +
    DESIGNATE:MenuStatus(AttackGroup, Duration) + +
    DESIGNATE.Mission + +
    DESIGNATE:New(CC, Detection, AttackSet, Mission) +

    DESIGNATE Constructor.

    +
    DESIGNATE:OnAfterDetect(From, Event, To) +

    Detect Handler OnAfter for DESIGNATE

    +
    DESIGNATE:OnAfterIlluminate(From, Event, To) +

    Illuminate Handler OnAfter for DESIGNATE

    +
    DESIGNATE:OnAfterLaseOff(From, Event, To) +

    LaseOff Handler OnAfter for DESIGNATE

    +
    DESIGNATE:OnAfterLaseOn(From, Event, To) +

    LaseOn Handler OnAfter for DESIGNATE

    +
    DESIGNATE:OnAfterSmoke(From, Event, To) +

    Smoke Handler OnAfter for DESIGNATE

    +
    DESIGNATE:OnAfterStatus(From, Event, To) +

    Status Handler OnAfter for DESIGNATE

    +
    DESIGNATE:OnBeforeDetect(From, Event, To) +

    Detect Handler OnBefore for DESIGNATE

    +
    DESIGNATE:OnBeforeIlluminate(From, Event, To) +

    Illuminate Handler OnBefore for DESIGNATE

    +
    DESIGNATE:OnBeforeLaseOff(From, Event, To) +

    LaseOff Handler OnBefore for DESIGNATE

    +
    DESIGNATE:OnBeforeLaseOn(From, Event, To) +

    LaseOn Handler OnBefore for DESIGNATE

    +
    DESIGNATE:OnBeforeSmoke(From, Event, To) +

    Smoke Handler OnBefore for DESIGNATE

    +
    DESIGNATE:OnBeforeStatus(From, Event, To) +

    Status Handler OnBefore for DESIGNATE

    +
    DESIGNATE.RecceSet + +
    DESIGNATE.Recces + +
    DESIGNATE:RemoveMenuLaserCode(LaserCode) +

    Removes a specific lase code from the designate lase menu.

    +
    DESIGNATE:SendStatus(AttackGroup, Duration, MenuAttackGroup) +

    Sends the status to the Attack Groups.

    +
    DESIGNATE:SetAutoLase(AutoLase, Message) +

    Set auto lase.

    +
    DESIGNATE:SetDesignateMenu() +

    Sets the Designate Menu.

    +
    DESIGNATE:SetDesignateName(DesignateName) +

    Set the name of the designation.

    +
    DESIGNATE:SetFlashStatusMenu(FlashMenu) +

    Set the flashing of the status menu.

    +
    DESIGNATE:SetLaserCodes(<, LaserCodes) +

    Set an array of possible laser codes.

    +
    DESIGNATE:SetMaximumDesignations(MaximumDesignations) +

    Set the maximum amount of designations.

    +
    DESIGNATE:SetMaximumDistanceAirDesignation(MaximumDistanceAirDesignation) +

    Set the maximum air designation distance.

    +
    DESIGNATE:SetMaximumDistanceDesignations(MaximumDistanceDesignations) +

    Set the overall maximum distance when designations can be accepted.

    +
    DESIGNATE:SetMaximumDistanceGroundDesignation(MaximumDistanceGroundDesignation) +

    Set the maximum ground designation distance.

    +
    DESIGNATE:SetMaximumMarkings(MaximumMarkings) +

    Set the maximum amount of markings FACs will do, per designated target group.

    +
    DESIGNATE:SetMission(Mission) +

    Set the MISSION object for which designate will function.

    +
    DESIGNATE:SetThreatLevelPrioritization(Prioritize) +

    Set priorization of Targets based on the Threat Level of the Target in an Air to Ground context.

    +
    DESIGNATE:Smoke() +

    Smoke Trigger for DESIGNATE

    +
    DESIGNATE:Status() +

    Status Trigger for DESIGNATE

    +
    DESIGNATE.ThreatLevelPrioritization + +
    DESIGNATE:__Detect(Delay) +

    Detect Asynchronous Trigger for DESIGNATE

    +
    DESIGNATE:__Illuminate(Delay) +

    Illuminate Asynchronous Trigger for DESIGNATE

    +
    DESIGNATE:__LaseOff(Delay) +

    LaseOff Asynchronous Trigger for DESIGNATE

    +
    DESIGNATE:__LaseOn(Delay) +

    LaseOn Asynchronous Trigger for DESIGNATE

    +
    DESIGNATE:__Smoke(Delay) +

    Smoke Asynchronous Trigger for DESIGNATE

    +
    DESIGNATE:__Status(Delay) +

    Status Asynchronous Trigger for DESIGNATE

    +
    DESIGNATE:onafterDetect() + +
    DESIGNATE:onafterDone(From, Event, To, Index) +

    Done

    +
    DESIGNATE:onafterIlluminate(From, Event, To, Index) +

    Illuminating

    +
    DESIGNATE:onafterLaseOff(From, Event, To, Index) + +
    DESIGNATE:onafterLaseOn(From, Event, To, Index, Duration, LaserCode) + +
    DESIGNATE:onafterLasing(From, Event, To, Index, Duration, LaserCodeRequested) + +
    DESIGNATE:onafterSmoke(From, Event, To, Index, Color) + +
    + +

    Global(s)

    +
    +
    + + #DESIGNATE + +DESIGNATE + +
    +
    + +

    DESIGNATE class, extends Fsm#FSM

    + +

    DESIGNATE is orchestrating the designation of potential targets executed by a Recce group, +and communicates these to a dedicated attacking group of players, +so that following a dynamically generated menu system, +each detected set of potential targets can be lased or smoked...

    + + + +

    Targets can be:

    + +
      +
    • Lased for a period of time.
    • +
    • Smoked. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.)
    • +
    • Illuminated through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.
    • +
    + +

    The following terminology is being used throughout this document:

    + +
      +
    • The DesignateObject is the object of the DESIGNATE class, which is this class explained in the document.
    • +
    • The DetectionObject is the object of a DETECTION_ class (DETECTIONTYPES, DETECTIONAREAS, DETECTION_UNITS), which is executing the detection and grouping of Targets into DetectionItems.
    • +
    • DetectionItems is the list of detected target groupings by the DetectionObject. Each DetectionItem contains a TargetSet.
    • +
    • DetectionItem is one element of the DetectionItems list, and contains a TargetSet.
    • +
    • The TargetSet is a SET_UNITS collection of Targets, that have been detected by the DetectionObject.
    • +
    • A Target is a detected UNIT object by the DetectionObject.
    • +
    • A Threat Level is a number from 0 to 10 that is calculated based on the threat of the Target in an Air to Ground battle scenario.
    • +
    • The RecceSet is a SET_GROUP collection that contains the RecceGroups.
    • +
    • A RecceGroup is a GROUP object containing the Recces.
    • +
    • A Recce is a UNIT object executing the reconnaissance as part the DetectionObject. A Recce can be of any UNIT type.
    • +
    • An AttackGroup is a GROUP object that contain Players.
    • +
    • A Player is an active CLIENT object containing a human player.
    • +
    • A Designate Menu is the menu that is dynamically created during the designation process for each AttackGroup.
    • +
    + +

    The RecceSet is continuously detecting for potential Targets, executing its task as part of the DetectionObject. +Once Targets have been detected, the DesignateObject will trigger the Detect Event.

    + +

    In order to prevent an overflow in the DesignateObject of detected targets, there is a maximum +amount of DetectionItems that can be put in scope of the DesignateObject. +We call this the MaximumDesignations term.

    + +

    As part of the Detect Event, the DetectionItems list is used by the DesignateObject to provide the Players with:

    + +
      +
    • The RecceGroups are reporting to each AttackGroup, sending Messages containing the Threat Level and the TargetSet composition.
    • +
    • Menu options are created and updated for each AttackGroup, containing the Detection ID and the Coordinates.
    • +
    + +

    A Player can then select an action from the Designate Menu.

    + +

    Note that each selected action will be executed for a TargetSet, thus the Target grouping done by the DetectionObject.

    + +

    Each Menu Option in the Designate Menu has two modes:

    + +
      +
    1. If the TargetSet is not being designated, then the Designate Menu option for the target Set will provide options to Lase or Smoke the targets.
    2. +
    3. If the Target Set is being designated, then the Designate Menu option will provide an option to stop or cancel the designation.
    4. +
    + +

    While designating, the RecceGroups will report any change in TargetSet composition or Target presence.

    + +

    The following logic is executed when a TargetSet is selected to be lased from the Designation Menu:

    + +
      +
    • The RecceSet is searched for any Recce that is within designation distance from a Target in the TargetSet that is currently not being designated.
    • +
    • If there is a Recce found that is currently no designating a target, and is within designation distance from the Target, then that Target will be designated.
    • +
    • During designation, any Recce that does not have Line of Sight (LOS) and is not within disignation distance from the Target, will stop designating the Target, and a report is given.
    • +
    • When a Recce is designating a Target, and that Target is destroyed, then the Recce will stop designating the Target, and will report the event.
    • +
    • When a Recce is designating a Target, and that Recce is destroyed, then the Recce will be removed from the RecceSet and designation will stop without reporting.
    • +
    • When all RecceGroups are destroyed from the RecceSet, then the DesignationObject will stop functioning, and nothing will be reported.
    • +
    + +

    In this way, the DesignationObject assists players to designate ground targets for a coordinated attack!

    + +

    Have FUN!

    + +

    1. DESIGNATE constructor

    + + + +

    2. DESIGNATE is a FSM

    + +

    Process

    + +

    2.1 DESIGNATE States

    + +
      +
    • Designating ( Group ): The designation process.
    • +
    + +

    2.2 DESIGNATE Events

    + + + +

    3. Maximum Designations

    + +

    In order to prevent an overflow of designations due to many Detected Targets, there is a +Maximum Designations scope that is set in the DesignationObject.

    + +

    The method DESIGNATE.SetMaximumDesignations() will put a limit on the amount of designations put in scope of the DesignationObject. +Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected.

    + +

    4. Laser codes

    + +

    4.1. Set possible laser codes

    + +

    An array of laser codes can be provided, that will be used by the DESIGNATE when lasing. +The laser code is communicated by the Recce when it is lasing a larget. +Note that the default laser code is 1113. +Working known laser codes are: 1113,1462,1483,1537,1362,1214,1131,1182,1644,1614,1515,1411,1621,1138,1542,1678,1573,1314,1643,1257,1467,1375,1341,1275,1237

    + +

    Use the method DESIGNATE.SetLaserCodes() to set the possible laser codes to be selected from. +One laser code can be given or an sequence of laser codes through an table...

    + +
    Designate:SetLaserCodes( 1214 )
    +
    + +

    The above sets one laser code with the value 1214.

    + +
    Designate:SetLaserCodes( { 1214, 1131, 1614, 1138 } )
    +
    + +

    The above sets a collection of possible laser codes that can be assigned. Note the { } notation!

    + +

    4.2. Auto generate laser codes

    + +

    Use the method DESIGNATE.GenerateLaserCodes() to generate all possible laser codes. Logic implemented and advised by Ciribob!

    + +

    4.3. Add specific lase codes to the lase menu

    + +

    Certain plane types can only drop laser guided ordonnance when targets are lased with specific laser codes. +The SU-25T needs targets to be lased using laser code 1113. +The A-10A needs targets to be lased using laser code 1680.

    + +

    The method DESIGNATE.AddMenuLaserCode() to allow a player to lase a target using a specific laser code. +Remove such a lase menu option using DESIGNATE.RemoveMenuLaserCode().

    + +

    5. Autolase to automatically lase detected targets.

    + +

    DetectionItems can be auto lased once detected by Recces. As such, there is almost no action required from the Players using the Designate Menu. +The auto lase function can be activated through the Designation Menu. +Use the method DESIGNATE.SetAutoLase() to activate or deactivate the auto lase function programmatically. +Note that autolase will automatically activate lasing for ALL DetectedItems. Individual items can be switched-off if required using the Designation Menu.

    + +
    Designate:SetAutoLase( true )
    +
    + +

    Activate the auto lasing.

    + +

    6. Target prioritization on threat level

    + +

    Targets can be detected of different types in one DetectionItem. Depending on the type of the Target, a different threat level applies in an Air to Ground combat context. +SAMs are of a higher threat than normal tanks. So, if the Target type was recognized, the Recces will select those targets that form the biggest threat first, +and will continue this until the remaining vehicles with the lowest threat have been reached.

    + +

    This threat level prioritization can be activated using the method DESIGNATE.SetThreatLevelPrioritization(). +If not activated, Targets will be selected in a random order, but most like those first which are the closest to the Recce marking the Target.

    + +
    Designate:SetThreatLevelPrioritization( true )
    +
    + +

    The example will activate the threat level prioritization for this the Designate object. Threats will be marked based on the threat level of the Target.

    + +

    6. Designate Menu Location for a Mission

    + +

    You can make DESIGNATE work for a Mission#MISSION object. In this way, the designate menu will not appear in the root of the radio menu, but in the menu of the Mission. +Use the method DESIGNATE.SetMission() to set the Mission object for the designate function.

    + +

    7. Status Report

    + +

    A status report is available that displays the current Targets detected, grouped per DetectionItem, and a list of which Targets are currently being marked.

    + +
      +
    • The status report can be shown by selecting "Status" -> "Report Status" from the Designation menu .

    • +
    • The status report can be automatically flashed by selecting "Status" -> "Flash Status On".

    • +
    • The automatic flashing of the status report can be deactivated by selecting "Status" -> "Flash Status Off".

    • +
    • The flashing of the status menu is disabled by default.

    • +
    • The method DESIGNATE.FlashStatusMenu() can be used to enable or disable to flashing of the status menu.

      + +

      Designate:FlashStatusMenu( true )

    • +
    + +

    The example will activate the flashing of the status menu for this Designate object.

    + + +
    +
    +

    Type Designate

    + +

    Type DESIGNATE

    +

    Field(s)

    +
    +
    + + +DESIGNATE:AddMenuLaserCode(LaserCode, MenuText) + +
    +
    + +

    Add a specific lase code to the designate lase menu to lase targets with a specific laser code.

    + + +

    The MenuText will appear in the lase menu.

    + +

    Parameters

    +
      +
    • + +

      #number LaserCode : +The specific laser code to be added to the lase menu.

      + +
    • +
    • + +

      #string MenuText : +The text to be shown to the player. If you specify a %d in the MenuText, the %d will be replaced with the LaserCode specified.

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +

    Usage:

    +
      RecceDesignation:AddMenuLaserCode( 1113, "Lase with %d for Su-25T" )
    +  RecceDesignation:AddMenuLaserCode( 1680, "Lase with %d for A-10A" )
    +
    + +
    +
    +
    +
    + + + +DESIGNATE.AttackSet + +
    +
    + + + +
    +
    +
    +
    + + +DESIGNATE.AutoLase + +
    +
    + + + +
    +
    +
    +
    + + + +DESIGNATE.CC + +
    +
    + + + +
    +
    +
    +
    + + +DESIGNATE:CoordinateLase() + +
    +
    + +

    Coordinates the Auto Lase.

    + +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE.DesignateName + +
    +
    + + + +
    +
    +
    +
    + + + +DESIGNATE.Designating + +
    +
    + + + +
    +
    +
    +
    + + +DESIGNATE:DesignationScope() + +
    +
    + +

    Adapt the designation scope according the detected items.

    + +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:Detect() + +
    +
    + +

    Detect Trigger for DESIGNATE

    + +
    +
    +
    +
    + + + +DESIGNATE.Detection + +
    +
    + + + +
    +
    +
    +
    + + + +DESIGNATE.FlashStatusMenu + +
    +
    + + + +
    +
    +
    +
    + + +DESIGNATE:GenerateLaserCodes() + +
    +
    + +

    Generate an array of possible laser codes.

    + + +

    Each new lase will select a code from this table. +The entered value can range from 1111 - 1788, +-- but the first digit of the series must be a 1 or 2 +-- and the last three digits must be between 1 and 8. + The range used to be bugged so its not 1 - 8 but 0 - 7. +function below will use the range 1-7 just in case

    + +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:Illuminate() + +
    +
    + +

    Illuminate Trigger for DESIGNATE

    + +
    +
    +
    +
    + + #number + +DESIGNATE.LaseDuration + +
    +
    + + + +
    +
    +
    +
    + + +DESIGNATE:LaseOff() + +
    +
    + +

    LaseOff Trigger for DESIGNATE

    + +
    +
    +
    +
    + + +DESIGNATE:LaseOn() + +
    +
    + +

    LaseOn Trigger for DESIGNATE

    + +
    +
    +
    +
    + + +DESIGNATE.LaserCodes + +
    +
    + + + +
    +
    +
    +
    + + + +DESIGNATE.LaserCodesUsed + +
    +
    + + + +
    +
    +
    +
    + + + +DESIGNATE.MarkScheduler + +
    +
    + + + +
    +
    +
    +
    + + + +DESIGNATE.MaximumDesignations + +
    +
    + + + +
    +
    +
    +
    + + + +DESIGNATE.MaximumDistanceAirDesignation + +
    +
    + + + +
    +
    +
    +
    + + + +DESIGNATE.MaximumDistanceDesignations + +
    +
    + + + +
    +
    +
    +
    + + + +DESIGNATE.MaximumDistanceGroundDesignation + +
    +
    + + + +
    +
    +
    +
    + + + +DESIGNATE.MaximumMarkings + +
    +
    + + + +
    +
    +
    +
    + + +DESIGNATE:MenuAutoLase(AutoLase) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      AutoLase :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE.MenuDesignate + +
    +
    + + + +
    +
    +
    +
    + + +DESIGNATE:MenuFlashStatus(AttackGroup, Flash) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      AttackGroup :

      + +
    • +
    • + +

      Flash :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:MenuForget(Index) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      Index :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:MenuIlluminate(Index) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      Index :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:MenuLaseCode(Index, Duration, LaserCode) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      Index :

      + +
    • +
    • + +

      Duration :

      + +
    • +
    • + +

      LaserCode :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:MenuLaseOff(Index, Duration) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      Index :

      + +
    • +
    • + +

      Duration :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:MenuLaseOn(Index, Duration) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      Index :

      + +
    • +
    • + +

      Duration :

      + +
    • +
    +
    +
    +
    +
    + + + +DESIGNATE.MenuLaserCodes + +
    +
    + + + + +

    This map contains the laser codes that will be shown in the designate menu to lase with specific laser codes.

    + +
    +
    +
    +
    + + +DESIGNATE:MenuSmoke(Index, Color) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      Index :

      + +
    • +
    • + +

      Color :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:MenuStatus(AttackGroup, Duration) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      AttackGroup :

      + +
    • +
    • + +

      Duration :

      + +
    • +
    +
    +
    +
    +
    + + + +DESIGNATE.Mission + +
    +
    + + + +
    +
    +
    +
    + + +DESIGNATE:New(CC, Detection, AttackSet, Mission) + +
    +
    + +

    DESIGNATE Constructor.

    + + +

    This class is an abstract class and should not be instantiated.

    + +

    Parameters

    + +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:OnAfterDetect(From, Event, To) + +
    +
    + +

    Detect Handler OnAfter for DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:OnAfterIlluminate(From, Event, To) + +
    +
    + +

    Illuminate Handler OnAfter for DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:OnAfterLaseOff(From, Event, To) + +
    +
    + +

    LaseOff Handler OnAfter for DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:OnAfterLaseOn(From, Event, To) + +
    +
    + +

    LaseOn Handler OnAfter for DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:OnAfterSmoke(From, Event, To) + +
    +
    + +

    Smoke Handler OnAfter for DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:OnAfterStatus(From, Event, To) + +
    +
    + +

    Status Handler OnAfter for DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:OnBeforeDetect(From, Event, To) + +
    +
    + +

    Detect Handler OnBefore for DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +DESIGNATE:OnBeforeIlluminate(From, Event, To) + +
    +
    + +

    Illuminate Handler OnBefore for DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +DESIGNATE:OnBeforeLaseOff(From, Event, To) + +
    +
    + +

    LaseOff Handler OnBefore for DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +DESIGNATE:OnBeforeLaseOn(From, Event, To) + +
    +
    + +

    LaseOn Handler OnBefore for DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +DESIGNATE:OnBeforeSmoke(From, Event, To) + +
    +
    + +

    Smoke Handler OnBefore for DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +DESIGNATE:OnBeforeStatus(From, Event, To) + +
    +
    + +

    Status Handler OnBefore for DESIGNATE

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + + +DESIGNATE.RecceSet + +
    +
    + + + +
    +
    +
    +
    + + + +DESIGNATE.Recces + +
    +
    + + + +
    +
    +
    +
    + + +DESIGNATE:RemoveMenuLaserCode(LaserCode) + +
    +
    + +

    Removes a specific lase code from the designate lase menu.

    + +

    Parameter

    +
      +
    • + +

      #number LaserCode : +The specific laser code that was set to be added to the lase menu.

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +

    Usage:

    +
      RecceDesignation:RemoveMenuLaserCode( 1113 )
    +  
    + +
    +
    +
    +
    + + +DESIGNATE:SendStatus(AttackGroup, Duration, MenuAttackGroup) + +
    +
    + +

    Sends the status to the Attack Groups.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Group#GROUP AttackGroup :

      + +
    • +
    • + +

      #number Duration : +The time in seconds the report should be visible.

      + +
    • +
    • + +

      MenuAttackGroup :

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:SetAutoLase(AutoLase, Message) + +
    +
    + +

    Set auto lase.

    + + +

    Auto lase will start lasing targets immediately when these are in range.

    + +

    Parameters

    +
      +
    • + +

      #boolean AutoLase : +(optional) true sets autolase on, false off. Default is off.

      + +
    • +
    • + +

      #boolean Message : +(optional) true is send message, false or nil won't send a message. Default is no message sent.

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:SetDesignateMenu() + +
    +
    + +

    Sets the Designate Menu.

    + +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:SetDesignateName(DesignateName) + +
    +
    + +

    Set the name of the designation.

    + + +

    The name will appear in the menu. +This method can be used to control different designations for different plane types.

    + +

    Parameter

    +
      +
    • + +

      #string DesignateName :

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:SetFlashStatusMenu(FlashMenu) + +
    +
    + +

    Set the flashing of the status menu.

    + +

    Parameter

    +
      +
    • + +

      #boolean FlashMenu : +true: the status menu will be flashed every detection run; false: no flashing of the menu.

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:SetLaserCodes(<, LaserCodes) + +
    +
    + +

    Set an array of possible laser codes.

    + + +

    Each new lase will select a code from this table.

    + +

    Parameters

    +
      +
    • + +

      #list < : +number> LaserCodes

      + +
    • +
    • + +

      LaserCodes :

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:SetMaximumDesignations(MaximumDesignations) + +
    +
    + +

    Set the maximum amount of designations.

    + +

    Parameter

    +
      +
    • + +

      #number MaximumDesignations :

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:SetMaximumDistanceAirDesignation(MaximumDistanceAirDesignation) + +
    +
    + +

    Set the maximum air designation distance.

    + +

    Parameter

    +
      +
    • + +

      #number MaximumDistanceAirDesignation : +Maximum air designation distance in meters.

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:SetMaximumDistanceDesignations(MaximumDistanceDesignations) + +
    +
    + +

    Set the overall maximum distance when designations can be accepted.

    + +

    Parameter

    +
      +
    • + +

      #number MaximumDistanceDesignations : +Maximum distance in meters to accept designations.

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:SetMaximumDistanceGroundDesignation(MaximumDistanceGroundDesignation) + +
    +
    + +

    Set the maximum ground designation distance.

    + +

    Parameter

    +
      +
    • + +

      #number MaximumDistanceGroundDesignation : +Maximum ground designation distance in meters.

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:SetMaximumMarkings(MaximumMarkings) + +
    +
    + +

    Set the maximum amount of markings FACs will do, per designated target group.

    + +

    Parameter

    +
      +
    • + +

      #number MaximumMarkings : +Maximum markings FACs will do, per designated target group.

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:SetMission(Mission) + +
    +
    + +

    Set the MISSION object for which designate will function.

    + + +

    When a MISSION object is assigned, the menu for the designation will be located at the Mission Menu.

    + +

    Parameter

    + +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:SetThreatLevelPrioritization(Prioritize) + +
    +
    + +

    Set priorization of Targets based on the Threat Level of the Target in an Air to Ground context.

    + +

    Parameter

    +
      +
    • + +

      #boolean Prioritize :

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:Smoke() + +
    +
    + +

    Smoke Trigger for DESIGNATE

    + +
    +
    +
    +
    + + +DESIGNATE:Status() + +
    +
    + +

    Status Trigger for DESIGNATE

    + +
    +
    +
    +
    + + + +DESIGNATE.ThreatLevelPrioritization + +
    +
    + + + +
    +
    +
    +
    + + +DESIGNATE:__Detect(Delay) + +
    +
    + +

    Detect Asynchronous Trigger for DESIGNATE

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:__Illuminate(Delay) + +
    +
    + +

    Illuminate Asynchronous Trigger for DESIGNATE

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:__LaseOff(Delay) + +
    +
    + +

    LaseOff Asynchronous Trigger for DESIGNATE

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:__LaseOn(Delay) + +
    +
    + +

    LaseOn Asynchronous Trigger for DESIGNATE

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:__Smoke(Delay) + +
    +
    + +

    Smoke Asynchronous Trigger for DESIGNATE

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:__Status(Delay) + +
    +
    + +

    Status Asynchronous Trigger for DESIGNATE

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:onafterDetect() + +
    +
    + + + +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:onafterDone(From, Event, To, Index) + +
    +
    + +

    Done

    + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:onafterIlluminate(From, Event, To, Index) + +
    +
    + +

    Illuminating

    + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:onafterLaseOff(From, Event, To, Index) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      Index :

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:onafterLaseOn(From, Event, To, Index, Duration, LaserCode) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      Index :

      + +
    • +
    • + +

      Duration :

      + +
    • +
    • + +

      LaserCode :

      + +
    • +
    +
    +
    +
    +
    + + +DESIGNATE:onafterLasing(From, Event, To, Index, Duration, LaserCodeRequested) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      Index :

      + +
    • +
    • + +

      Duration :

      + +
    • +
    • + +

      LaserCodeRequested :

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    +
    +
    + + +DESIGNATE:onafterSmoke(From, Event, To, Index, Color) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    • + +

      Index :

      + +
    • +
    • + +

      Color :

      + +
    • +
    +

    Return value

    + +

    #DESIGNATE:

    + + +
    +
    + +

    Type list

    + +
    + +
    + + diff --git a/docs/Documentation/Detection.html b/docs/Documentation/Detection.html index 7002da0ba..e055abe7c 100644 --- a/docs/Documentation/Detection.html +++ b/docs/Documentation/Detection.html @@ -17,9 +17,16 @@ index

    Module Detection

    -

    Functional - DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods.

    +

    Functional -- DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods.

    @@ -84,8 +116,25 @@

    DETECTION classes facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units). DETECTION uses the in-built detection capabilities of DCS World, but adds new functionalities.

    -

    Please watch this youtube video that explains the detection concepts.

    +

    Find the DETECTION classes documentation further in this document in the globals section.

    +
    + +

    Demo Missions

    + +

    DETECTION Demo Missions and Source Code

    + +

    DETECTION Demo Missions, only for Beta Testers

    + +

    ALL Demo Missions pack of the Latest Release

    + +
    + +

    YouTube Channel

    + +

    DETECTION YouTube Channel

    + +

    Contributions:

    @@ -111,7 +160,9 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_BASE +

    DETECTION_BASE class, extends Fsm#FSM

    +

    The DETECTION_BASE class defines the core functions to administer detected objects.

    @@ -136,9 +187,9 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_AREAS:CalculateThreatLevelA2G(DetectedItem) + DETECTION_AREAS:CalculateIntercept(DetectedItem) -

    Calculate the maxium A2G threat level of the DetectedItem.

    +

    Calculate the optimal intercept point of the DetectedItem.

    @@ -148,13 +199,25 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_AREAS:CreateDetectionSets() + DETECTION_AREAS.CountryID + + + + + + DETECTION_AREAS:CreateDetectionItems()

    Make a DetectionSet table.

    - DETECTION_AREAS:DetectedItemReportSummary(Index) + DETECTION_AREAS:DetectedItemMenu(Index, AttackGroup) + +

    Menu of a detected item using a given numeric index.

    + + + + DETECTION_AREAS:DetectedItemReportSummary(Index, AttackGroup, Settings)

    Report summary of a detected item using a given numeric index.

    @@ -163,6 +226,12 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_AREAS.DetectedItems

    A list of areas containing the set of Units, Zones, the center Unit within the zone, and ID of each area that was detected within a DetectionZoneRange.

    + + + + DETECTION_AREAS:DetectedReportDetailed(AttackGroup) + +

    Report detailed of a detection result.

    @@ -187,18 +256,6 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_AREAS:GetChangeText(DetectedItem)

    Make text documenting the changes of the detected zone.

    - - - - DETECTION_AREAS:GetTreatLevelA2G(DetectedItem) - -

    Returns the A2G threat level of the units in the DetectedItem

    - - - - DETECTION_AREAS:IsFriendliesNearBy(DetectedItem) - -

    Returns if there are friendlies nearby the FAC units ...

    @@ -290,7 +347,7 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_BASE:AddDetectedItem(DetectedItemIndex, Set) + DETECTION_BASE:AddDetectedItem(ItemPrefix, DetectedItemIndex, Set)

    Adds a new DetectedItem to the DetectedItems list.

    @@ -308,19 +365,13 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_BASE.ClassName + DETECTION_BASE:CleanDetectionItem(DetectedItem, DetectedItemID) - DETECTION_BASE.CountryID - - - - - - DETECTION_BASE:CreateDetectionSets() + DETECTION_BASE:CreateDetectionItems()

    Make a DetectionSet table.

    @@ -386,7 +437,13 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_BASE:DetectedItemReportSummary(Index) + DETECTION_BASE:DetectedItemMenu(Index, AttackGroup) + +

    Menu of a detected item using a given numeric index.

    + + + + DETECTION_BASE:DetectedItemReportSummary(Index, AttackGroup, Settings)

    Report summary of a detected item using a given numeric index.

    @@ -410,7 +467,7 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_BASE:DetectedReportDetailed() + DETECTION_BASE:DetectedReportDetailed(AttackGroup)

    Report detailed of a detectedion result.

    @@ -419,12 +476,6 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_BASE.DetectionCount - - - - DETECTION_BASE.DetectionInterval - - @@ -455,12 +506,89 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_BASE.DistanceProbability + + + + DETECTION_BASE:FilterCategories(<, FilterCategories) + +

    Filter the detected units based on Unit.Category
    +The different values of Unit.Category can be:

    + +
      +
    • Unit.Category.AIRPLANE
    • +
    • Unit.Category.GROUND_UNIT
    • +
    • Unit.Category.HELICOPTER
    • +
    • Unit.Category.SHIP
    • +
    • Unit.Category.STRUCTURE
    • +
    + +

    Multiple Unit.Category entries can be given as a table and then these will be evaluated as an OR expression.

    + + + + DETECTION_BASE:FilterFriendliesCategory(FriendliesCategory) + +

    Filters friendly units by unit category.

    + + + + DETECTION_BASE:ForgetDetectedUnit(UnitName) + +

    Forget a Unit from a DetectionItem

    + + + + DETECTION_BASE.FriendliesCategory + + + + + + DETECTION_BASE.FriendliesRange + + + + + + DETECTION_BASE.FriendlyPrefixes + + + + + + DETECTION_BASE:GetDetectedID(Index) + +

    Get a detected ID using a given numeric index.

    DETECTION_BASE:GetDetectedItem(Index)

    Get a detected item using a given numeric index.

    + + + + DETECTION_BASE:GetDetectedItemCoordinate(Index) + +

    Get the detected item coordinate.

    + + + + DETECTION_BASE:GetDetectedItemID(Index) + +

    Get a detected ItemID using a given numeric index.

    + + + + DETECTION_BASE:GetDetectedItemThreatLevel(Index) + +

    Get the detected item coordinate.

    + + + + DETECTION_BASE:GetDetectedItemZone(Index) + +

    Get the Zone#ZONE_UNIT of a detection area using a given numeric index.

    @@ -488,15 +616,39 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_BASE:GetDetectedZone(Index) + DETECTION_BASE:GetDetectedUnitTypeName(DetectedUnit) -

    Get the Zone#ZONE_UNIT of a detection area using a given numeric index.

    +

    Gets a detected unit type name, taking into account the detection results.

    DETECTION_BASE:GetDetectionSetGroup()

    Get the detection Groups.

    + + + + DETECTION_BASE:GetFriendliesDistance(DetectedItem) + +

    Returns the distance used to identify friendlies near the deteted item ...

    + + + + DETECTION_BASE:GetFriendliesNearBy(DetectedItem) + +

    Returns friendly units nearby the FAC units ...

    + + + + DETECTION_BASE:GetFriendliesNearIntercept(DetectedItem) + +

    Returns friendly units nearby the intercept point ...

    + + + + DETECTION_BASE:GetPlayersNearBy(DetectedItem) + +

    Returns friendly units nearby the FAC units ...

    @@ -539,6 +691,24 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_BASE:InitDetectVisual(DetectVisual)

    Detect Visual.

    + + + + DETECTION_BASE.Intercept + + + + + + DETECTION_BASE.InterceptDelay + + + + + + DETECTION_BASE:IsDetectedItemDetected(DetectedItem) + +

    Checks if there is at least one UNIT detected in the Set of the the DetectedItem.

    @@ -551,6 +721,18 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_BASE:IsFriendliesNearBy(DetectedItem)

    Returns if there are friendlies nearby the FAC units ...

    + + + + DETECTION_BASE:IsFriendliesNearIntercept(DetectedItem) + +

    Returns if there are friendlies nearby the intercept ...

    + + + + DETECTION_BASE:IsPlayersNearBy(DetectedItem) + +

    Returns if there are friendlies nearby the FAC units ...

    @@ -629,6 +811,12 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_BASE:OnLeaveStopped(From, Event, To)

    OnLeave Transition Handler for State Stopped.

    + + + + DETECTION_BASE.RefreshTimeInterval + + @@ -686,15 +874,45 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_BASE:SetDetectionInterval(DetectionInterval) + DETECTION_BASE:SetDetectedItemCoordinate(The, Coordinate, DetectedItemUnit, DetectedItem) -

    Set the detection interval time in seconds.

    +

    Set the detected item coordinate.

    + + + + DETECTION_BASE:SetDetectedItemThreatLevel(The, DetectedItem) + +

    Set the detected item threatlevel.

    DETECTION_BASE:SetDistanceProbability(DistanceProbability)

    Upon a visual detection, the further away a detected object is, the less likely it is to be detected properly.

    + + + + DETECTION_BASE:SetFriendliesRange(FriendliesRange) + +

    Set the radius in meters to validate if friendlies are nearby.

    + + + + DETECTION_BASE:SetFriendlyPrefixes(FriendlyPrefixes) + +

    This will allow during friendly search any recce or detection unit to be also considered as a friendly.

    + + + + DETECTION_BASE:SetIntercept(Intercept, IntereptDelay, InterceptDelay) + +

    Set the parameters to calculate to optimal intercept point.

    + + + + DETECTION_BASE:SetRefreshTimeInterval(RefreshTimeInterval) + +

    Set the detection interval time in seconds.

    @@ -731,6 +949,12 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_BASE:UnIdentifyDetectedObject(DetectedObject)

    UnIdentify a detected object during detection processing.

    + + + + DETECTION_BASE:UpdateDetectedItemDetection(DetectedItem) + +

    Set IsDetected flag for all DetectedItems.

    @@ -770,7 +994,7 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_BASE:onafterDetectionGroup(From, Event, To, DetectionGroup) + DETECTION_BASE:onafterDetectionGroup(From, Event, To, DetectionGroup, DetectionTimeStamp) @@ -795,6 +1019,18 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_BASE.DetectedItem.Changes

    A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes).

    + + + + DETECTION_BASE.DetectedItem.Coordinate + +

    The last known coordinate of the DetectedItem.

    + + + + DETECTION_BASE.DetectedItem.DistanceRecce + + @@ -804,13 +1040,13 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_BASE.DetectedItem.ItemID + DETECTION_BASE.DetectedItem.ID

    -- The identifier of the detected area.

    - DETECTION_BASE.DetectedItem.MaxThreatLevelA2G + DETECTION_BASE.DetectedItem.InterceptCoord @@ -825,6 +1061,12 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_BASE.DetectedItem.Set

    -- The Set of Units in the detected area.

    + + + + DETECTION_BASE.DetectedItem.ThreatLevel + + @@ -847,6 +1089,42 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_BASE.DetectedObject.Identified + + + + DETECTION_BASE.DetectedObject.IsVisible + + + + + + DETECTION_BASE.DetectedObject.KnowDistance + + + + + + DETECTION_BASE.DetectedObject.KnowType + + + + + + DETECTION_BASE.DetectedObject.LastPos + + + + + + DETECTION_BASE.DetectedObject.LastTime + + + + + + DETECTION_BASE.DetectedObject.LastVelocity + + @@ -859,12 +1137,6 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu DETECTION_BASE.DetectedObject.Type - - - - DETECTION_BASE.DetectedObject.Visible - - @@ -878,19 +1150,25 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_TYPES:CreateDetectionSets() + DETECTION_TYPES:CreateDetectionItems()

    Create the DetectedItems list from the DetectedObjects table.

    - DETECTION_TYPES:DetectedItemReportSummary(Index, DetectedTypeName) + DETECTION_TYPES:DetectedItemMenu(Index, DetectedTypeName, AttackGroup) + +

    Menu of a DetectedItem using a given numeric index.

    + + + + DETECTION_TYPES:DetectedItemReportSummary(Index, AttackGroup, Settings, DetectedTypeName)

    Report summary of a DetectedItem using a given numeric index.

    - DETECTION_TYPES:DetectedReportDetailed() + DETECTION_TYPES:DetectedReportDetailed(AttackGroup)

    Report detailed of a detection result.

    @@ -954,19 +1232,25 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu - DETECTION_UNITS:CreateDetectionSets() + DETECTION_UNITS:CreateDetectionItems()

    Create the DetectedItems list from the DetectedObjects table.

    - DETECTION_UNITS:DetectedItemReportSummary(Index) + DETECTION_UNITS:DetectedItemMenu(Index, AttackGroup) + +

    Menu of a DetectedItem using a given numeric index.

    + + + + DETECTION_UNITS:DetectedItemReportSummary(Index, AttackGroup, Settings)

    Report summary of a DetectedItem using a given numeric index.

    - DETECTION_UNITS:DetectedReportDetailed() + DETECTION_UNITS:DetectedReportDetailed(AttackGroup)

    Report detailed of a detection result.

    @@ -1046,7 +1330,238 @@ DETECTION uses the in-built detection capabilities of DCS World, but adds new fu
    +

    DETECTION_BASE class, extends Fsm#FSM

    +

    The DETECTION_BASE class defines the core functions to administer detected objects.

    + + +

    The DETECTION_BASE class will detect objects within the battle zone for a list of Groups detecting targets following (a) detection method(s).

    + +

    DETECTION_BASE constructor

    + +

    Construct a new DETECTION_BASE instance using the DETECTION_BASE.New() method.

    + +

    Initialization

    + +

    By default, detection will return detected objects with all the detection sensors available. +However, you can ask how the objects were found with specific detection methods. +If you use one of the below methods, the detection will work with the detection method specified. +You can specify to apply multiple detection methods.

    + +

    Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK:

    + + + +

    Filter detected units based on category of the unit

    + +

    Filter the detected units based on Unit.Category using the method DETECTION_BASE.FilterCategories().
    +The different values of Unit.Category can be:

    + +
      +
    • Unit.Category.AIRPLANE
    • +
    • Unit.Category.GROUND_UNIT
    • +
    • Unit.Category.HELICOPTER
    • +
    • Unit.Category.SHIP
    • +
    • Unit.Category.STRUCTURE
    • +
    + +

    Multiple Unit.Category entries can be given as a table and then these will be evaluated as an OR expression.

    + +

    Example to filter a single category (Unit.Category.AIRPLANE).

    + +
    DetectionObject:FilterCategories( Unit.Category.AIRPLANE ) 
    +
    + +

    Example to filter multiple categories (Unit.Category.AIRPLANE, Unit.Category.HELICOPTER). Note the {}.

    + +
    DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
    +
    + + +

    DETECTION_ derived classes group the detected units into a DetectedItems[] list

    + +

    DETECTIONBASE derived classes build a list called DetectedItems[], which is essentially a first later +of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains +a SETUNIT object that contains the detected units that belong to that group.

    + +

    Derived classes will apply different methods to group the detected units. +Examples are per area, per quadrant, per distance, per type. +See further the derived DETECTION classes on which grouping methods are currently supported.

    + +

    Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class:

    + +
      +
    • The method Detection#DETECTION_BASE.GetDetectedItems() retrieves the DetectedItems[] list.
    • +
    • A DetectedItem from the DetectedItems[] list can be retrieved using the method Detection#DETECTION_BASE.GetDetectedItem( DetectedItemIndex ). + Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information + about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem.
    • +
    • A DetectedSet from the DetectedItems[] list can be retrieved using the method Detection#DETECTION_BASE.GetDetectedSet( DetectedItemIndex ). + This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ).
    • +
    + +

    Visual filters to fine-tune the probability of the detected objects

    + +

    By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. +That being said, the DCS World detection algorithm can sometimes be unrealistic. +Especially for a visual detection, DCS World is able to report within 1 second a detailed detection of a group of 20 units (including types of the units) that are 10 kilometers away, using only visual capabilities. +Additionally, trees and other obstacles are not accounted during the DCS World detection.

    + +

    Therefore, an additional (optional) filtering has been built into the DETECTION_BASE class, that can be set for visual detected units. +For electronic detection, this filtering is not applied, only for visually detected targets.

    + +

    The following additional filtering can be applied for visual filtering:

    + +
      +
    • A probability factor per kilometer distance.
    • +
    • A probability factor based on the alpha angle between the detected object and the unit detecting. + A detection from a higher altitude allows for better detection than when on the ground.
    • +
    • Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. + The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone.
    • +
    + +

    I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. +Only when you experience unrealistic behaviour in your missions, these filters could be applied.

    + + +

    Distance visual detection probability

    + +

    Upon a visual detection, the further away a detected object is, the less likely it is to be detected properly. +Also, the speed of accurate detection plays a role.

    + +

    A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance.

    + +

    For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: +1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%.

    + +

    Note that based on this probability factor, not only the detection but also the type of the unit will be applied!

    + +

    Use the method Detection#DETECTION_BASE.SetDistanceProbability() to set the probability factor upon a 10 km distance.

    + +

    Alpha Angle visual detection probability

    + +

    Upon a visual detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. +A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct.

    + +

    A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle.

    + +

    For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: +0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100%

    + +

    Use the method Detection#DETECTION_BASE.SetAlphaAngleProbability() to set the probability factor if 0°.

    + +

    Cloudy Zones detection probability

    + +

    Upon a visual detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. +The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission +zones that reflect cloudy areas where detected units may not be so easily visually detected.

    + +

    Use the method Detection#DETECTION_BASE.SetZoneProbability() to set for a defined number of zones, the probability factors.

    + +

    Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take +from the DETECTIONBASE to calculate the presence of the detected unit within each zone. +Expecially for ZONEPOLYGON, try to limit the amount of nodes of the polygon!

    + +

    Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for +AI not to detect so easily targets within a forrest or village rich area.

    + +

    Accept / Reject detected units

    + +

    DETECTION_BASE can accept or reject successful detections based on the location of the detected object, +if it is located in range or located inside or outside of specific zones.

    + +

    Detection acceptance of within range limit

    + +

    A range can be set that will limit a successful detection for a unit. +Use the method Detection#DETECTION_BASE.SetAcceptRange() to apply a range in meters till where detected units will be accepted.

    + +
     local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
    +
    + -- Build a detect object.
    + local Detection = DETECTION_UNITS:New( SetGroup )
    +
    + -- This will accept detected units if the range is below 5000 meters.
    + Detection:SetAcceptRange( 5000 ) 
    +
    + -- Start the Detection.
    + Detection:Start()
    +
    + + +

    Detection acceptance if within zone(s).

    + +

    Specific ZONEBASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONEBASE object(s). +Use the method Detection#DETECTION_BASE.SetAcceptZones() will accept detected units if they are within the specified zones.

    + +
     local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
    +
    + -- Search fo the zones where units are to be accepted.
    + local ZoneAccept1 = ZONE:New( "AcceptZone1" )
    + local ZoneAccept2 = ZONE:New( "AcceptZone2" )
    +
    + -- Build a detect object.
    + local Detection = DETECTION_UNITS:New( SetGroup )
    +
    + -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2.
    + Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) 
    +
    + -- Start the Detection.
    + Detection:Start()
    +
    + +

    Detection rejectance if within zone(s).

    + +

    Specific ZONEBASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONEBASE object(s). +Use the method Detection#DETECTION_BASE.SetRejectZones() will reject detected units if they are within the specified zones. +An example of how to use the method is shown below.

    + +
     local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
    +
    + -- Search fo the zones where units are to be rejected.
    + local ZoneReject1 = ZONE:New( "RejectZone1" )
    + local ZoneReject2 = ZONE:New( "RejectZone2" )
    +
    + -- Build a detect object.
    + local Detection = DETECTION_UNITS:New( SetGroup )
    +
    + -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2.
    + Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) 
    +
    + -- Start the Detection.
    + Detection:Start()
    +
    + +

    Detection of Friendlies Nearby

    + +

    Use the method Detection#DETECTION_BASE.SetFriendliesRange() to set the range what will indicate when friendlies are nearby +a DetectedItem. The default range is 6000 meters. For air detections, it is advisory to use about 30.000 meters.

    + +

    DETECTION_BASE is a Finite State Machine

    + +

    Various Events and State Transitions can be tailored using DETECTION_BASE.

    + +

    DETECTION_BASE States

    + +
      +
    • Detecting: The detection is running.
    • +
    • Stopped: The detection is stopped.
    • +
    + +

    DETECTION_BASE Events

    + +
      +
    • Start: Start the detection process.
    • +
    • Detect: Detect new units.
    • +
    • Detected: New units have been detected.
    • +
    • Stop: Stop the detection process. +
    • +
    @@ -1141,13 +1656,13 @@ self

    - -DETECTION_AREAS:CalculateThreatLevelA2G(DetectedItem) + +DETECTION_AREAS:CalculateIntercept(DetectedItem)
    -

    Calculate the maxium A2G threat level of the DetectedItem.

    +

    Calculate the optimal intercept point of the DetectedItem.

    Parameter

      @@ -1176,8 +1691,22 @@ self

      - -DETECTION_AREAS:CreateDetectionSets() + + +DETECTION_AREAS.CountryID + +
      +
      + + + +
      +
      +
      +
      + + +DETECTION_AREAS:CreateDetectionItems()
      @@ -1197,20 +1726,25 @@ self

      - -DETECTION_AREAS:DetectedItemReportSummary(Index) + +DETECTION_AREAS:DetectedItemMenu(Index, AttackGroup)
      -

      Report summary of a detected item using a given numeric index.

      +

      Menu of a detected item using a given numeric index.

      -

      Parameter

      +

      Parameters

      • Index :

        +
      • +
      • + +

        AttackGroup :

        +

      Return value

      @@ -1218,6 +1752,44 @@ self

      #string:

      +
      +
      +
      +
      + + +DETECTION_AREAS:DetectedItemReportSummary(Index, AttackGroup, Settings) + +
      +
      + +

      Report summary of a detected item using a given numeric index.

      + +

      Parameters

      + +

      Return value

      + +

      Core.Report#REPORT: +The report of the detection items.

      +
      @@ -1232,6 +1804,33 @@ self

      A list of areas containing the set of Units, Zones, the center Unit within the zone, and ID of each area that was detected within a DetectionZoneRange.

      +
      +
      +
      +
      + + +DETECTION_AREAS:DetectedReportDetailed(AttackGroup) + +
      +
      + +

      Report detailed of a detection result.

      + +

      Parameter

      + +

      Return value

      + +

      #string:

      + +
      @@ -1313,58 +1912,6 @@ The Changes text

      - -DETECTION_AREAS:GetTreatLevelA2G(DetectedItem) - -
      -
      - -

      Returns the A2G threat level of the units in the DetectedItem

      - -

      Parameter

      - -

      Return value

      - -

      #number: -a scale from 0 to 10.

      - -
      -
      -
      -
      - - -DETECTION_AREAS:IsFriendliesNearBy(DetectedItem) - -
      -
      - -

      Returns if there are friendlies nearby the FAC units ...

      - -

      Parameter

      -
        -
      • - -

        DetectedItem :

        - -
      • -
      -

      Return value

      - -

      #boolean: -trhe if there are friendlies nearby

      - -
      -
      -
      -
      - DETECTION_AREAS:NearestFAC(DetectedItem) @@ -1529,209 +2076,7 @@ self

      Type DETECTION_BASE

      - -

      1) DETECTION_BASE class, extends Fsm#FSM

      - -

      The DETECTION_BASE class defines the core functions to administer detected objects.

      - - -

      The DETECTION_BASE class will detect objects within the battle zone for a list of Groups detecting targets following (a) detection method(s).

      - -

      1.1) DETECTION_BASE constructor

      - -

      Construct a new DETECTION_BASE instance using the DETECTION_BASE.New() method.

      - -

      1.2) DETECTION_BASE initialization

      - -

      By default, detection will return detected objects with all the detection sensors available. -However, you can ask how the objects were found with specific detection methods. -If you use one of the below methods, the detection will work with the detection method specified. -You can specify to apply multiple detection methods.

      - -

      Use the following functions to report the objects it detected using the methods Visual, Optical, Radar, IRST, RWR, DLINK:

      - - - -

      1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list

      - -

      DETECTIONBASE derived classes build a list called DetectedItems[], which is essentially a first later -of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains -a SETUNIT object that contains the detected units that belong to that group.

      - -

      Derived classes will apply different methods to group the detected units. -Examples are per area, per quadrant, per distance, per type. -See further the derived DETECTION classes on which grouping methods are currently supported.

      - -

      Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class:

      - -
        -
      • The method Detection#DETECTION_BASE.GetDetectedItems() retrieves the DetectedItems[] list.
      • -
      • A DetectedItem from the DetectedItems[] list can be retrieved using the method Detection#DETECTION_BASE.GetDetectedItem( DetectedItemIndex ). - Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information - about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem.
      • -
      • A DetectedSet from the DetectedItems[] list can be retrieved using the method Detection#DETECTION_BASE.GetDetectedSet( DetectedItemIndex ). - This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ).
      • -
      - -

      1.4) Apply additional Filters to fine-tune the detected objects

      - -

      By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means. -That being said, the DCS World detection algorithm can sometimes be unrealistic. -Especially for a visual detection, DCS World is able to report within 1 second a detailed detection of a group of 20 units (including types of the units) that are 10 kilometers away, using only visual capabilities. -Additionally, trees and other obstacles are not accounted during the DCS World detection.

      - -

      Therefore, an additional (optional) filtering has been built into the DETECTION_BASE class, that can be set for visual detected units. -For electronic detection, this filtering is not applied, only for visually detected targets.

      - -

      The following additional filtering can be applied for visual filtering:

      - -
        -
      • A probability factor per kilometer distance.
      • -
      • A probability factor based on the alpha angle between the detected object and the unit detecting. - A detection from a higher altitude allows for better detection than when on the ground.
      • -
      • Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. - The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone.
      • -
      - -

      I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. -Only when you experience unrealistic behaviour in your missions, these filters could be applied.

      - -

      1.4.1 ) Distance visual detection probability

      - -

      Upon a visual detection, the further away a detected object is, the less likely it is to be detected properly. -Also, the speed of accurate detection plays a role.

      - -

      A distance probability factor between 0 and 1 can be given, that will model a linear extrapolated probability over 10 km distance.

      - -

      For example, if a probability factor of 0.6 (60%) is given, the extrapolated probabilities over 15 kilometers would like like: -1 km: 96%, 2 km: 92%, 3 km: 88%, 4 km: 84%, 5 km: 80%, 6 km: 76%, 7 km: 72%, 8 km: 68%, 9 km: 64%, 10 km: 60%, 11 km: 56%, 12 km: 52%, 13 km: 48%, 14 km: 44%, 15 km: 40%.

      - -

      Note that based on this probability factor, not only the detection but also the type of the unit will be applied!

      - -

      Use the method Detection#DETECTION_BASE.SetDistanceProbability() to set the probability factor upon a 10 km distance.

      - -

      1.4.2 ) Alpha Angle visual detection probability

      - -

      Upon a visual detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. -A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct.

      - -

      A probability factor between 0 and 1 can be given, that will model a progressive extrapolated probability if the target would be detected at a 0° angle.

      - -

      For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: -0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100%

      - -

      Use the method Detection#DETECTION_BASE.SetAlphaAngleProbability() to set the probability factor if 0°.

      - -

      1.4.3 ) Cloudy Zones detection probability

      - -

      Upon a visual detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully. -The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission -zones that reflect cloudy areas where detected units may not be so easily visually detected.

      - -

      Use the method Detection#DETECTION_BASE.SetZoneProbability() to set for a defined number of zones, the probability factors.

      - -

      Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take -from the DETECTIONBASE to calculate the presence of the detected unit within each zone. -Expecially for ZONEPOLYGON, try to limit the amount of nodes of the polygon!

      - -

      Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for -AI not to detect so easily targets within a forrest or village rich area.

      - -

      1.5 ) Accept / Reject detected units

      - -

      DETECTION_BASE can accept or reject successful detections based on the location of the detected object, -if it is located in range or located inside or outside of specific zones.

      - -

      1.5.1 ) Detection acceptance of within range limit

      - -

      A range can be set that will limit a successful detection for a unit. -Use the method Detection#DETECTION_BASE.SetAcceptRange() to apply a range in meters till where detected units will be accepted.

      - -
       local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
      -
      - -- Build a detect object.
      - local Detection = DETECTION_BASE:New( SetGroup )
      -
      - -- This will accept detected units if the range is below 5000 meters.
      - Detection:SetAcceptRange( 5000 ) 
      -
      - -- Start the Detection.
      - Detection:Start()
      -
      - - -

      1.5.2 ) Detection acceptance if within zone(s).

      - -

      Specific ZONEBASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONEBASE object(s). -Use the method Detection#DETECTION_BASE.SetAcceptZones() will accept detected units if they are within the specified zones.

      - -
       local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
      -
      - -- Search fo the zones where units are to be accepted.
      - local ZoneAccept1 = ZONE:New( "AcceptZone1" )
      - local ZoneAccept2 = ZONE:New( "AcceptZone2" )
      -
      - -- Build a detect object.
      - local Detection = DETECTION_BASE:New( SetGroup )
      -
      - -- This will accept detected units by Detection when the unit is within ZoneAccept1 OR ZoneAccept2.
      - Detection:SetAcceptZones( { ZoneAccept1, ZoneAccept2 } ) 
      -
      - -- Start the Detection.
      - Detection:Start()
      -
      - -

      1.5.3 ) Detection rejectance if within zone(s).

      - -

      Specific ZONEBASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONEBASE object(s). -Use the method Detection#DETECTION_BASE.SetRejectZones() will reject detected units if they are within the specified zones. -An example of how to use the method is shown below.

      - -
       local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers.
      -
      - -- Search fo the zones where units are to be rejected.
      - local ZoneReject1 = ZONE:New( "RejectZone1" )
      - local ZoneReject2 = ZONE:New( "RejectZone2" )
      -
      - -- Build a detect object.
      - local Detection = DETECTION_BASE:New( SetGroup )
      -
      - -- This will reject detected units by Detection when the unit is within ZoneReject1 OR ZoneReject2.
      - Detection:SetRejectZones( { ZoneReject1, ZoneReject2 } ) 
      -
      - -- Start the Detection.
      - Detection:Start()
      -
      - -

      1.6) DETECTION_BASE is a Finite State Machine

      - -

      Various Events and State Transitions can be tailored using DETECTION_BASE.

      - -

      1.6.1) DETECTION_BASE States

      - -
        -
      • Detecting: The detection is running.
      • -
      • Stopped: The detection is stopped.
      • -
      - -

      1.6.2) DETECTION_BASE Events

      - -
        -
      • Start: Start the detection process.
      • -
      • Detect: Detect new units.
      • -
      • Detected: New units have been detected.
      • -
      • Stop: Stop the detection process.
      • -
      - - -

      Field(s)

      +

      Field(s)

      @@ -1862,7 +2207,7 @@ self

      -DETECTION_BASE:AddDetectedItem(DetectedItemIndex, Set) +DETECTION_BASE:AddDetectedItem(ItemPrefix, DetectedItemIndex, Set)
      @@ -1876,6 +2221,11 @@ self

      +
      +
      +
      + + +DETECTION_BASE:FilterCategories(<, FilterCategories) + +
      +
      + +

      Filter the detected units based on Unit.Category
      +The different values of Unit.Category can be:

      + +
        +
      • Unit.Category.AIRPLANE
      • +
      • Unit.Category.GROUND_UNIT
      • +
      • Unit.Category.HELICOPTER
      • +
      • Unit.Category.SHIP
      • +
      • Unit.Category.STRUCTURE
      • +
      + +

      Multiple Unit.Category entries can be given as a table and then these will be evaluated as an OR expression.

      + + + +

      Example to filter a single category (Unit.Category.AIRPLANE).

      + +
      DetectionObject:FilterCategories( Unit.Category.AIRPLANE ) 
      +
      + +

      Example to filter multiple categories (Unit.Category.AIRPLANE, Unit.Category.HELICOPTER). Note the {}.

      + +
      DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
      +
      + + +

      Parameters

      +
        +
      • + +

        #list < : +cs.DCSUnit#Unit> FilterCategories The Categories entries

        + +
      • +
      • + +

        FilterCategories :

        + +
      • +
      +

      Return value

      + +

      #DETECTION_BASE: +self

      + +
      +
      +
      +
      + + +DETECTION_BASE:FilterFriendliesCategory(FriendliesCategory) + +
      +
      + +

      Filters friendly units by unit category.

      + +

      Parameter

      +
        +
      • + +

        FriendliesCategory :

        + +
      • +
      +

      Return value

      + +

      #DETECTION_BASE:

      + + +
      +
      +
      +
      + + +DETECTION_BASE:ForgetDetectedUnit(UnitName) + +
      +
      + +

      Forget a Unit from a DetectionItem

      + +

      Parameter

      +
        +
      • + +

        #string UnitName : +The UnitName that needs to be forgotten from the DetectionItem Sets.

        + +
      • +
      +

      Return value

      + +

      #DETECTION_BASE:

      + + +
      +
      +
      +
      + + + +DETECTION_BASE.FriendliesCategory + +
      +
      + + + +
      +
      +
      +
      + + + +DETECTION_BASE.FriendliesRange + +
      +
      + + + +
      +
      +
      +
      + + +DETECTION_BASE.FriendlyPrefixes + +
      +
      + + + +
      +
      +
      +
      + + +DETECTION_BASE:GetDetectedID(Index) + +
      +
      + +

      Get a detected ID using a given numeric index.

      + +

      Parameter

      +
        +
      • + +

        #number Index :

        + +
      • +
      +

      Return value

      + +

      #string: +DetectedItemID

      +
      @@ -2345,6 +2910,110 @@ self

      #DETECTION_BASE.DetectedItem:

      +
    +
    +
    +
    + + +DETECTION_BASE:GetDetectedItemCoordinate(Index) + +
    +
    + +

    Get the detected item coordinate.

    + +

    Parameter

    +
      +
    • + +

      #number Index :

      + +
    • +
    +

    Return value

    + +

    Core.Point#COORDINATE:

    + + +
    +
    +
    +
    + + +DETECTION_BASE:GetDetectedItemID(Index) + +
    +
    + +

    Get a detected ItemID using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      #number Index :

      + +
    • +
    +

    Return value

    + +

    #string: +DetectedItemID

    + +
    +
    +
    +
    + + +DETECTION_BASE:GetDetectedItemThreatLevel(Index) + +
    +
    + +

    Get the detected item coordinate.

    + +

    Parameter

    +
      +
    • + +

      #number Index :

      + +
    • +
    +

    Return value

    + +

    #number: +ThreatLevel

    + +
    +
    +
    +
    + + +DETECTION_BASE:GetDetectedItemZone(Index) + +
    +
    + +

    Get the Zone#ZONE_UNIT of a detection area using a given numeric index.

    + +

    Parameter

    +
      +
    • + +

      #number Index :

      + +
    • +
    +

    Return value

    + +

    Core.Zone#ZONE_UNIT: +DetectedZone

    +
    @@ -2379,7 +3048,7 @@ self

    Return value

    #number: -Count

    +The amount of detected items. Note that the amount of detected items can differ with the reality, because detections are not real-time but doen in intervals!

    @@ -2438,26 +3107,26 @@ DetectedSet

    - -DETECTION_BASE:GetDetectedZone(Index) + +DETECTION_BASE:GetDetectedUnitTypeName(DetectedUnit)
    -

    Get the Zone#ZONE_UNIT of a detection area using a given numeric index.

    +

    Gets a detected unit type name, taking into account the detection results.

    Parameter

    Return value

    -

    Core.Zone#ZONE_UNIT: -DetectedZone

    +

    #string: +The type name

    @@ -2474,9 +3143,113 @@ DetectedZone

    Return value

    -

    Wrapper.Group#GROUP:

    +

    Core.Set#SET_GROUP:

    + +
    +
    +
    + + +DETECTION_BASE:GetFriendliesDistance(DetectedItem) + +
    +
    + +

    Returns the distance used to identify friendlies near the deteted item ...

    + +

    Parameter

    +
      +
    • + +

      DetectedItem :

      + +
    • +
    +

    Return value

    + +

    #number: +The distance.

    + +
    +
    +
    +
    + + +DETECTION_BASE:GetFriendliesNearBy(DetectedItem) + +
    +
    + +

    Returns friendly units nearby the FAC units ...

    + +

    Parameter

    +
      +
    • + +

      DetectedItem :

      + +
    • +
    +

    Return value

    + +

    #map:

    +

    string,Wrapper.Unit#UNIT> The map of Friendly UNITs.

    + +
    +
    +
    +
    + + +DETECTION_BASE:GetFriendliesNearIntercept(DetectedItem) + +
    +
    + +

    Returns friendly units nearby the intercept point ...

    + +

    Parameter

    +
      +
    • + +

      DetectedItem :

      + +
    • +
    +

    Return value

    + +

    #map:

    +

    string,Wrapper.Unit#UNIT> The map of Friendly UNITs.

    + +
    +
    +
    +
    + + +DETECTION_BASE:GetPlayersNearBy(DetectedItem) + +
    +
    + +

    Returns friendly units nearby the FAC units ...

    + +

    Parameter

    +
      +
    • + +

      DetectedItem :

      + +
    • +
    +

    Return value

    + +

    #map:

    +

    string,Wrapper.Unit#UNIT> The map of Friendly UNITs.

    +
    @@ -2654,6 +3427,60 @@ self

    #DETECTION_BASE: self

    + +
    +
    +
    + + + +DETECTION_BASE.Intercept + +
    +
    + + + +
    +
    +
    +
    + + + +DETECTION_BASE.InterceptDelay + +
    +
    + + + +
    +
    +
    +
    + + +DETECTION_BASE:IsDetectedItemDetected(DetectedItem) + +
    +
    + +

    Checks if there is at least one UNIT detected in the Set of the the DetectedItem.

    + +

    Parameter

    +
      +
    • + +

      DetectedItem :

      + +
    • +
    +

    Return value

    + +

    #boolean: +true if at least one UNIT is detected from the DetectedSet, false if no UNIT was detected from the DetectedSet.

    +
    @@ -2691,6 +3518,58 @@ true if already identified.

    +

    Returns if there are friendlies nearby the FAC units ...

    + +

    Parameter

    +
      +
    • + +

      DetectedItem :

      + +
    • +
    +

    Return value

    + +

    #boolean: +true if there are friendlies nearby

    + +
    +
    +
    +
    + + +DETECTION_BASE:IsFriendliesNearIntercept(DetectedItem) + +
    +
    + +

    Returns if there are friendlies nearby the intercept ...

    + +

    Parameter

    +
      +
    • + +

      DetectedItem :

      + +
    • +
    +

    Return value

    + +

    #boolean: +trhe if there are friendlies near the intercept.

    + +
    +
    +
    +
    + + +DETECTION_BASE:IsPlayersNearBy(DetectedItem) + +
    +
    +

    Returns if there are friendlies nearby the FAC units ...

    Parameter

    @@ -3171,6 +4050,20 @@ The To State string.

    #boolean: Return false to cancel Transition.

    +
    +
    +
    +
    + + #number + +DETECTION_BASE.RefreshTimeInterval + +
    +
    + + +
    @@ -3336,7 +4229,7 @@ self

    @@ -3800,6 +4908,34 @@ The To State string.

    A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes).

    + +
    +
    +
    + + Core.Point#COORDINATE + +DETECTION_BASE.DetectedItem.Coordinate + +
    +
    + +

    The last known coordinate of the DetectedItem.

    + +
    +
    +
    +
    + + + +DETECTION_BASE.DetectedItem.DistanceRecce + +
    +
    + + +
    @@ -3820,8 +4956,8 @@ The To State string.

    #number - -DETECTION_BASE.DetectedItem.ItemID + +DETECTION_BASE.DetectedItem.ID
    @@ -3834,8 +4970,8 @@ The To State string.

    - -DETECTION_BASE.DetectedItem.MaxThreatLevelA2G + +DETECTION_BASE.DetectedItem.InterceptCoord
    @@ -3870,6 +5006,20 @@ The To State string.

    -- The Set of Units in the detected area.

    +
    +
    +
    +
    + + + +DETECTION_BASE.DetectedItem.ThreatLevel + +
    +
    + + +
    @@ -3917,6 +5067,90 @@ The To State string.

    + +
    +
    +
    + + #boolean + +DETECTION_BASE.DetectedObject.IsVisible + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_BASE.DetectedObject.KnowDistance + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_BASE.DetectedObject.KnowType + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +DETECTION_BASE.DetectedObject.LastPos + +
    +
    + + + +
    +
    +
    +
    + + #number + +DETECTION_BASE.DetectedObject.LastTime + +
    +
    + + + +
    +
    +
    +
    + + #number + +DETECTION_BASE.DetectedObject.LastVelocity + +
    +
    + + +
    @@ -3945,20 +5179,6 @@ The To State string.

    - -
    -
    -
    - - #boolean - -DETECTION_BASE.DetectedObject.Visible - -
    -
    - - -
    @@ -3994,8 +5214,8 @@ Beware that when the amount of different types detected is large, the DetectedIt
    - -DETECTION_TYPES:CreateDetectionSets() + +DETECTION_TYPES:CreateDetectionItems()
    @@ -4010,13 +5230,49 @@ Beware that when the amount of different types detected is large, the DetectedIt

    #DETECTION_TYPES: self

    +
    +
    +
    +
    + + +DETECTION_TYPES:DetectedItemMenu(Index, DetectedTypeName, AttackGroup) + +
    +
    + +

    Menu of a DetectedItem using a given numeric index.

    + +

    Parameters

    +
      +
    • + +

      Index :

      + +
    • +
    • + +

      DetectedTypeName :

      + +
    • +
    • + +

      AttackGroup :

      + +
    • +
    +

    Return value

    + +

    #string:

    + +
    -DETECTION_TYPES:DetectedItemReportSummary(Index, DetectedTypeName) +DETECTION_TYPES:DetectedItemReportSummary(Index, AttackGroup, Settings, DetectedTypeName)
    @@ -4032,14 +5288,26 @@ self

  • +

    Wrapper.Group#GROUP AttackGroup : +The group to generate the report for.

    + +
  • +
  • + +

    Core.Settings#SETTINGS Settings : +Message formatting settings to use.

    + +
  • +
  • +

    DetectedTypeName :

  • Return value

    -

    #string:

    - +

    Core.Report#REPORT: +The report of the detection items.

    @@ -4047,13 +5315,22 @@ self

    -DETECTION_TYPES:DetectedReportDetailed() +DETECTION_TYPES:DetectedReportDetailed(AttackGroup)

    Report detailed of a detection result.

    +

    Parameter

    +

    Return value

    #string:

    @@ -4202,7 +5479,7 @@ self

    Type DETECTION_UNITS

    -

    2) DETECTION_UNITS class, extends Detection#DETECTION_BASE

    +

    DETECTION_UNITS class, extends Detection#DETECTION_BASE

    The DETECTION_UNITS class will detect units within the battle zone.

    @@ -4229,8 +5506,8 @@ Beware that when the amount of units detected is large, the DetectedItems list w
    - -DETECTION_UNITS:CreateDetectionSets() + +DETECTION_UNITS:CreateDetectionItems()
    @@ -4250,20 +5527,25 @@ self

    - -DETECTION_UNITS:DetectedItemReportSummary(Index) + +DETECTION_UNITS:DetectedItemMenu(Index, AttackGroup)
    -

    Report summary of a DetectedItem using a given numeric index.

    +

    Menu of a DetectedItem using a given numeric index.

    -

    Parameter

    +

    Parameters

    • Index :

      +
    • +
    • + +

      AttackGroup :

      +

    Return value

    @@ -4271,19 +5553,66 @@ self

    #string:

    +
    +
    +
    +
    + + +DETECTION_UNITS:DetectedItemReportSummary(Index, AttackGroup, Settings) + +
    +
    + +

    Report summary of a DetectedItem using a given numeric index.

    + +

    Parameters

    + +

    Return value

    + +

    Core.Report#REPORT: +The report of the detection items.

    +
    -DETECTION_UNITS:DetectedReportDetailed() +DETECTION_UNITS:DetectedReportDetailed(AttackGroup)

    Report detailed of a detection result.

    +

    Parameter

    +

    Return value

    #string:

    @@ -4431,6 +5760,10 @@ self

    Type DETECTION_UNITS.DetectedItem

    +

    Type list

    + +

    Type map

    +
    diff --git a/docs/Documentation/DetectionManager.html b/docs/Documentation/DetectionManager.html index e31707159..f6ee82ce5 100644 --- a/docs/Documentation/DetectionManager.html +++ b/docs/Documentation/DetectionManager.html @@ -17,9 +17,16 @@ index @@ -89,7 +121,7 @@ Reportings can be done in several manners, and it is up to the derived classes i

    1.2) DETECTION_MANAGER reporting:

    Derived DETECTION_MANAGER classes will reports detected units using the method DetectionManager#DETECTION_MANAGER.ReportDetected(). This method implements polymorphic behaviour.

    -

    The time interval in seconds of the reporting can be changed using the methods DetectionManager#DETECTION_MANAGER.SetReportInterval(). +

    The time interval in seconds of the reporting can be changed using the methods DetectionManager#DETECTION_MANAGER.SetRefreshTimeInterval(). To control how long a reporting message is displayed, use DetectionManager#DETECTION_MANAGER.SetReportDisplayTime(). Derived classes need to implement the method DetectionManager#DETECTION_MANAGER.GetReportDisplayTime() to use the correct display time for displayed messages during a report.

    @@ -152,6 +184,30 @@ If an ad-hoc report is requested, use the method DETECTION_MANAGER:New(SetGroup, Detection)

    FAC constructor.

    + + + + DETECTION_MANAGER:OnAfterStart(From, Event, To) + +

    Start Handler OnAfter for DETECTION_MANAGER

    + + + + DETECTION_MANAGER:OnAfterStop(From, Event, To) + +

    Stop Handler OnAfter for DETECTION_MANAGER

    + + + + DETECTION_MANAGER:OnBeforeStart(From, Event, To) + +

    Start Handler OnBefore for DETECTION_MANAGER

    + + + + DETECTION_MANAGER:OnBeforeStop(From, Event, To) + +

    Stop Handler OnBefore for DETECTION_MANAGER

    @@ -164,6 +220,12 @@ If an ad-hoc report is requested, use the method DETECTION_MANAGER.SetGroup

    The groups to which the FAC will report to.

    + + + + DETECTION_MANAGER:SetRefreshTimeInterval(RefreshTimeInterval) + +

    Set the reporting time interval.

    @@ -173,9 +235,21 @@ If an ad-hoc report is requested, use the method DETECTION_MANAGER:SetReportInterval(ReportInterval) + DETECTION_MANAGER:Start() -

    Set the reporting time interval.

    +

    Start Trigger for DETECTION_MANAGER

    + + + + DETECTION_MANAGER:Stop() + +

    Stop Trigger for DETECTION_MANAGER

    + + + + DETECTION_MANAGER._RefreshTimeInterval + + @@ -185,9 +259,15 @@ If an ad-hoc report is requested, use the method DETECTION_MANAGER._ReportInterval + DETECTION_MANAGER:__Start(Delay) - +

    Start Asynchronous Trigger for DETECTION_MANAGER

    + + + + DETECTION_MANAGER:__Stop(Delay) + +

    Stop Asynchronous Trigger for DETECTION_MANAGER

    @@ -355,6 +435,140 @@ ReportDisplayTime The display time in seconds when a report needs to be done.

    #DETECTION_MANAGER: self

    + + +
    +
    + + +DETECTION_MANAGER:OnAfterStart(From, Event, To) + +
    +
    + +

    Start Handler OnAfter for DETECTION_MANAGER

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_MANAGER:OnAfterStop(From, Event, To) + +
    +
    + +

    Stop Handler OnAfter for DETECTION_MANAGER

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +DETECTION_MANAGER:OnBeforeStart(From, Event, To) + +
    +
    + +

    Start Handler OnBefore for DETECTION_MANAGER

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +DETECTION_MANAGER:OnBeforeStop(From, Event, To) + +
    +
    + +

    Stop Handler OnBefore for DETECTION_MANAGER

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + +
    @@ -400,6 +614,33 @@ self

    + +DETECTION_MANAGER:SetRefreshTimeInterval(RefreshTimeInterval) + +
    +
    + +

    Set the reporting time interval.

    + +

    Parameter

    +
      +
    • + +

      #number RefreshTimeInterval : +The interval in seconds when a report needs to be done.

      + +
    • +
    +

    Return value

    + +

    #DETECTION_MANAGER: +self

    + +
    +
    +
    +
    + DETECTION_MANAGER:SetReportDisplayTime(ReportDisplayTime) @@ -427,27 +668,40 @@ self

    - -DETECTION_MANAGER:SetReportInterval(ReportInterval) + +DETECTION_MANAGER:Start()
    -

    Set the reporting time interval.

    +

    Start Trigger for DETECTION_MANAGER

    -

    Parameter

    -
      -
    • - -

      #number ReportInterval : -The interval in seconds when a report needs to be done.

      +
    +
    +
    +
    + + +DETECTION_MANAGER:Stop() + +
    +
    + +

    Stop Trigger for DETECTION_MANAGER

    + +
    +
    +
    +
    + + + +DETECTION_MANAGER._RefreshTimeInterval + +
    +
    + - - -

    Return value

    - -

    #DETECTION_MANAGER: -self

    @@ -468,15 +722,43 @@ self

    - - -DETECTION_MANAGER._ReportInterval + +DETECTION_MANAGER:__Start(Delay)
    +

    Start Asynchronous Trigger for DETECTION_MANAGER

    +

    Parameter

    +
      +
    • + +

      #number Delay :

      +
    • +
    +
    +
    +
    +
    + + +DETECTION_MANAGER:__Stop(Delay) + +
    +
    + +

    Stop Asynchronous Trigger for DETECTION_MANAGER

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    diff --git a/docs/Documentation/Escort.html b/docs/Documentation/Escort.html index d2890d3e0..ebf59fcc1 100644 --- a/docs/Documentation/Escort.html +++ b/docs/Documentation/Escort.html @@ -17,9 +17,16 @@ index

    Module Escort

    -

    Taking the lead of AI escorting your flight.

    +

    Functional -- Taking the lead of AI escorting your flight.

    +
    +

    #ESCORT class

    The #ESCORT class allows you to interact with escorting AI on your flight and take the lead. Each escorting group can be commanded with a whole set of radio commands (radio menu in your flight, and then F10).

    diff --git a/docs/Documentation/Event.html b/docs/Documentation/Event.html index da27fb360..64b4bf9a0 100644 --- a/docs/Documentation/Event.html +++ b/docs/Documentation/Event.html @@ -17,9 +17,16 @@ index

    Module Event

    -

    Core - EVENT models DCS event dispatching using a publish-subscribe model.

    +

    Core -- EVENT models DCS event dispatching using a publish-subscribe model.

    @@ -244,36 +276,10 @@ Example code snippet:


    -

    API CHANGE HISTORY

    - -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    - -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    - -

    YYYY-MM-DD: CLASS:NewFunction( Params ) replaces CLASS:OldFunction( Params ) -YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - -

    Hereby the change log:

    - -
      -
    • 2017-03-07: Added the correct event dispatching in case the event is subscribed by a GROUP.

    • -
    • 2017-02-07: Did a complete revision of the Event Handing API and underlying mechanisms.

    • -
    - -
    - -

    AUTHORS and CONTRIBUTIONS

    - +

    Author: Sven Van de Velde (FlightControl)

    Contributions:

    -

    Authors:

    - - +

    Global(s)

    @@ -312,9 +318,21 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - EVENT:EventText(EventID) + EVENT:CreateEventDeleteCargo(Cargo) - +

    Creation of a Cargo Deletion Event.

    + + + + EVENT:CreateEventNewCargo(Cargo) + +

    Creation of a New Cargo Event.

    + + + + EVENT:CreateEventPlayerEnterUnit(PlayerUnit) + +

    Creation of a SEVENTPLAYERENTERUNIT Event.

    @@ -393,12 +411,6 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    EVENT:OnTakeOffForTemplate(EventTemplate, EventFunction, EventClass) - - - - EVENT:Remove(EventClass, EventID) - -

    Removes an Events entry

    @@ -408,15 +420,15 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - EVENT:RemoveForGroup(GroupName, EventClass, EventID) + EVENT:RemoveEvent(EventClass, EventID) -

    Removes an Events entry for a GROUP.

    +

    Removes a subscription

    - EVENT:RemoveForUnit(UnitName, EventClass, EventID) + EVENT:Reset(EventClass, EventID, EventObject) -

    Removes an Events entry for a UNIT.

    +

    Resets subscriptions

    @@ -440,6 +452,18 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    Type EVENTDATA

    + + + + + + + + + + + + @@ -766,6 +796,12 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    + + + + @@ -917,19 +953,63 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - -EVENT:EventText(EventID) + +EVENT:CreateEventDeleteCargo(Cargo)
    - +

    Creation of a Cargo Deletion Event.

    Parameter

    +
    +
    +
    +
    + + +EVENT:CreateEventNewCargo(Cargo) + +
    +
    + +

    Creation of a New Cargo Event.

    + +

    Parameter

    + +
    +
    +
    +
    + + +EVENT:CreateEventPlayerEnterUnit(PlayerUnit) + +
    +
    + +

    Creation of a SEVENTPLAYERENTERUNIT Event.

    + +

    Parameter

    + @@ -1399,38 +1479,6 @@ The self instance of the class for which the event is captured. When the event h
    - -EVENT:Remove(EventClass, EventID) - -
    -
    - -

    Removes an Events entry

    - -

    Parameters

    - -

    Return value

    - -

    #EVENT.Events:

    - - -
    -
    -
    -
    - EVENT:RemoveAll(EventObject) @@ -1452,24 +1500,18 @@ The self instance of the class for which the event is.

    - -EVENT:RemoveForGroup(GroupName, EventClass, EventID) + +EVENT:RemoveEvent(EventClass, EventID)
    -

    Removes an Events entry for a GROUP.

    +

    Removes a subscription

    Parameters

    +
    +
    +
    + + + +EVENTS.NewCargo + +
    +
    + + +
    diff --git a/docs/Documentation/Fsm.html b/docs/Documentation/Fsm.html index 40679fc47..177f34281 100644 --- a/docs/Documentation/Fsm.html +++ b/docs/Documentation/Fsm.html @@ -17,9 +17,16 @@ index

    Module Fsm

    -

    Core - The FSM (Finite State Machine) class and derived FSM_ classes +

    Core -- The FSM (Finite State Machine) class and derived FSM_ classes are design patterns allowing efficient (long-lasting) processes and workflows.

    @@ -82,6 +114,8 @@ are design patterns allowing efficient (long-lasting) processes and workflows. +

    A Finite State Machine (FSM) models a process flow that transitions between various States through triggered Events.

    +

    A FSM can only be in one of a finite number of states. The machine is in only one state at a time; the state it is in at any given time is called the current state. It can change from one state to another when initiated by an internal or external triggering event, which is called a transition. @@ -139,39 +173,11 @@ Additionally, I've added extendability and created an API that allows seamless F


    -

    API CHANGE HISTORY

    - -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    - -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    - -

    YYYY-MM-DD: CLASS:NewFunction( Params ) replaces CLASS:OldFunction( Params ) -YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - -

    Hereby the change log:

    - -
      -
    • 2016-12-18: Released.
    • -
    - -
    - -

    AUTHORS and CONTRIBUTIONS

    +

    Author: Sven Van de Velde (FlightControl)

    Contributions:

    -
      -
    • Pikey: Review of documentation & advice for improvements.
    • -
    - -

    Authors:

    - - +

    Global(s)

    @@ -179,11 +185,9 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    @@ -629,12 +633,6 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    - - - - @@ -706,14 +704,52 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    -

    1) FSM class, extends Base#BASE

    +

    FSM class, extends Base#BASE

    + +

    A Finite State Machine (FSM) models a process flow that transitions between various States through triggered Events.

    + + + +

    A FSM can only be in one of a finite number of states. +The machine is in only one state at a time; the state it is in at any given time is called the current state. +It can change from one state to another when initiated by an internal or external triggering event, which is called a transition. +An FSM implementation is defined by a list of its states, its initial state, and the triggering events for each possible transition. +An FSM implementation is composed out of two parts, a set of state transition rules, and an implementation set of state transition handlers, implementing those transitions.

    + +

    The FSM class supports a hierarchical implementation of a Finite State Machine, +that is, it allows to embed existing FSM implementations in a master FSM. +FSM hierarchies allow for efficient FSM re-use, not having to re-invent the wheel every time again when designing complex processes.

    + +

    Workflow Example

    + +

    The above diagram shows a graphical representation of a FSM implementation for a Task, which guides a Human towards a Zone, +orders him to destroy x targets and account the results. +Other examples of ready made FSM could be:

    + +
      +
    • route a plane to a zone flown by a human
    • +
    • detect targets by an AI and report to humans
    • +
    • account for destroyed targets by human players
    • +
    • handle AI infantry to deploy from or embark to a helicopter or airplane or vehicle
    • +
    • let an AI patrol a zone
    • +
    + +

    The MOOSE framework uses extensively the FSM class and derived FSM_ classes, +because the goal of MOOSE is to simplify mission design complexity for mission building. +By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes. +Ready made FSM-based implementations classes exist within the MOOSE framework that can easily be re-used, +and tailored by mission designers through the implementation of Transition Handlers. +Each of these FSM implementation classes start either with:

    + +
      +
    • an acronym AI_, which indicates an FSM implementation directing AI controlled GROUP and/or UNIT. These AI_ classes derive the #FSM_CONTROLLABLE class.
    • +
    • an acronym TASK_, which indicates an FSM implementation executing a TASK executed by Groups of players. These TASK_ classes derive the #FSM_TASK class.
    • +
    • an acronym ACT_, which indicates an Sub-FSM implementation, directing Humans actions that need to be done in a TASK, seated in a CLIENT (slot) or a UNIT (CA join). These ACT_ classes derive the #FSM_PROCESS class.
    • +

    Transition Rules and Transition Handlers and Event Triggers

    -

    The FSM class is the base class of all FSM_ derived classes.

    - - -

    It implements the main functionality to define and execute Finite State Machines. +

    The FSM class is the base class of all FSM_ derived classes. It implements the main functionality to define and execute Finite State Machines. The derived FSM_ classes extend the Finite State Machine functionality to run a workflow process for a specific purpose or component.

    Finite State Machines have Transition Rules, Transition Handlers and Event Triggers.

    @@ -733,13 +769,13 @@ Most of the time, these Event Triggers are used within the Transition Handler me

    As explained above, a FSM supports Linear State Transitions and Hierarchical State Transitions, and both can be mixed to make a comprehensive FSM implementation. The below documentation has a seperate chapter explaining both transition modes, taking into account the Transition Rules, Transition Handlers and Event Triggers.

    -

    1.1) FSM Linear Transitions

    +

    FSM Linear Transitions

    Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible From state(s) towards a To state upon a Triggered Event. The Lineair transition rule evaluation will always be done from the current state of the FSM. If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop.

    -

    1.1.1) FSM Transition Rules

    +

    FSM Transition Rules

    The FSM has transition rules that it follows and validates, as it walks the process. These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event.

    @@ -767,7 +803,7 @@ These rules define when an FSM can transition from a specific state towards an o
  • Note that once the Switch is On or Middle, it can only be switched Off.
  • -

    Some additional comments:

    +

    Some additional comments:

    Note that Linear Transition Rules can be declared in a few variations:

    @@ -781,7 +817,7 @@ These rules define when an FSM can transition from a specific state towards an o
     FsmSwitch:AddTransition( { "On",  "Middle" }, "SwitchOff", "Off" )
     
    -

    1.1.2) Transition Handling

    +

    Transition Handling

    Transition Handlers

    @@ -807,7 +843,7 @@ These parameters are on the correct order: From, Event, To:

    On top, each of these methods can have a variable amount of parameters passed. See the example in section 1.1.3.

    -

    1.1.3) Event Triggers

    +

    Event Triggers

    Event Triggers

    @@ -850,7 +886,7 @@ Event will be processed after 5 seconds, and Amount is given as a parameter.

    Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing!

    -

    1.1.4) Linear Transition Example

    +

    Linear Transition Example

    This example is fully implemented in the MOOSE test mission on GITHUB: FSM-100 - Transition Explanation

    @@ -940,7 +976,7 @@ The transition for event Stop can be executed if the current state of the FSM is

    So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped. And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt.

    -

    1.5) FSM Hierarchical Transitions

    +

    FSM Hierarchical Transitions

    Hierarchical Transitions allow to re-use readily available and implemented FSMs. This becomes in very useful for mission building, where mission designers build complex processes and workflows, @@ -2576,27 +2612,6 @@ self

    -

    Parameter

    -
      -
    • - -

      ProcessUnit :

      - -
    • -
    -
    - -
    -
    - - -FSM_PROCESS:onenterSuccess(ProcessUnit) - -
    -
    - - -

    Parameter

    • diff --git a/docs/Documentation/Group.html b/docs/Documentation/Group.html index 7d74f1dbd..7a3c2368b 100644 --- a/docs/Documentation/Group.html +++ b/docs/Documentation/Group.html @@ -17,9 +17,16 @@ index

    Module Group

    -

    Wrapper -- GROUP is a wrapper class for the DCS Class Group.

    +

    Wrapper -- GROUP wraps the DCS Class Group objects.

    @@ -94,31 +126,7 @@
    -

    API CHANGE HISTORY

    - -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    - -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    - -

    Hereby the change log:

    - -

    2017-03-26: GROUP:RouteRTB( RTBAirbase, Speed ) added.

    - -

    2017-03-07: GROUP:HandleEvent( Event, EventFunction ) added.
    -2017-03-07: GROUP:UnHandleEvent( Event ) added.

    - -

    2017-01-24: GROUP:SetAIOnOff( AIOnOff ) added.

    - -

    2017-01-24: GROUP:SetAIOn() added.

    - -

    2017-01-24: GROUP:SetAIOff() added.

    - -
    - -

    AUTHORS and CONTRIBUTIONS

    +

    Author: Sven Van de Velde (FlightControl)

    Contributions:

    @@ -126,11 +134,7 @@
  • Entropy, Afinegan: Came up with the requirement for AIOnOff().
  • -

    Authors:

    - -
      -
    • FlightControl: Design & Programming
    • -
    +

    Global(s)

    @@ -141,6 +145,12 @@

    GROUP class, extends Controllable#CONTROLLABLE

    For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _DATABASE object.

    + +
    + + +
    EVENTDATA.Cargo + +
    EVENTDATA.CargoName + +
    EVENTDATA.IniCategory

    (UNIT) The category of the initiator.

    @@ -718,6 +742,12 @@ YYYY-MM-DD: CLASS:NewFunction( Params ) added

    EVENTS.Dead +
    EVENTS.DeleteCargo +
    EVENTS.MissionStart +
    EVENTS.NewCargo +
    FSM -

    1) FSM class, extends Base#BASE

    +

    FSM class, extends Base#BASE

    -

    Transition Rules and Transition Handlers and Event Triggers

    - -

    The FSM class is the base class of all FSM_ derived classes.

    +

    A Finite State Machine (FSM) models a process flow that transitions between various States through triggered Events.

    FSM_PROCESS:onenterFailed(ProcessUnit) -
    FSM_PROCESS:onenterSuccess(ProcessUnit) -
    GROUPTEMPLATE +
    @@ -168,6 +178,12 @@ GROUP:CopyRoute(Begin, End, Randomize, Radius)

    Return the route of a group by using the Database#DATABASE class.

    + + + + GROUP:CountInZone(Zone) + +

    Returns the number of UNITs that are in the Zone

    @@ -210,6 +226,12 @@ GROUP:GetCoalition()

    Returns the coalition of the DCS Group.

    + + + + GROUP:GetCoordinate() + +

    Returns a COORDINATE object indicating the point of the first UNIT of the GROUP within the mission.

    @@ -234,6 +256,18 @@ GROUP:GetDCSUnits()

    Returns the DCS Units of the DCS Group.

    + + + + GROUP:GetFuel() + +

    Returns relative amount of fuel (from 0.0 to 1.0) the group has in its internal tanks.

    + + + + GROUP:GetHeading() + +

    Returns the mean heading of every UNIT in the GROUP in degrees

    @@ -260,18 +294,36 @@ GROUP:GetMinHeight()

    Returns the current minimum height of the group.

    + + + + GROUP:GetPlayerName() + +

    Gets the player name of the group.

    GROUP:GetPlayerNames()

    Get player names

    + + + + GROUP:GetPointVec2() + +

    Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission.

    GROUP:GetPositionVec3()

    Returns the DCSTypes#Position3 position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.

    + + + + GROUP:GetRandomVec3(Radius) + +

    Returns a random DCSTypes#Vec3 vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP.

    @@ -404,6 +456,12 @@ GROUP:Register(GroupName)

    Create a new GROUP from a DCSGroup

    + + + + GROUP:ResetEvents() + +

    Reset the subscriptions.

    @@ -452,12 +510,46 @@ GROUP:SetTemplateCountry(CountryID, Template)

    Sets the CountryID of the group in a Template.

    + + + + GROUP.Takeoff + + GROUP:UnHandleEvent(Event)

    UnSubscribe to a DCS event.

    + + + + +

    Type GROUP.Takeoff

    + + + + + + + + + + + + + + + +
    GROUP.Takeoff.Air + +
    GROUP.Takeoff.Cold + +
    GROUP.Takeoff.Hot + +
    GROUP.Takeoff.Runway +
    @@ -538,6 +630,20 @@ Use the following Zone validation methods on the group:

    + +
    +
    +
    + + + +GROUPTEMPLATE + +
    +
    + + +

    Type Group

    @@ -634,6 +740,33 @@ When randomization is on, the randomization is within the radius.

    + +GROUP:CountInZone(Zone) + +
    +
    + +

    Returns the number of UNITs that are in the Zone

    + +

    Parameter

    + +

    Return value

    + +

    #number: +The number of UNITs that are in the Zone

    + +
    +
    +
    +
    + GROUP:Destroy() @@ -777,6 +910,24 @@ The coalition side of the DCS Group.

    + +GROUP:GetCoordinate() + +
    +
    + +

    Returns a COORDINATE object indicating the point of the first UNIT of the GROUP within the mission.

    + +

    Return value

    + +

    Core.Point#COORDINATE: +The COORDINATE of the GROUP.

    + +
    +
    +
    +
    + GROUP:GetCountry() @@ -871,6 +1022,65 @@ The DCS Units.

    + +GROUP:GetFuel() + +
    +
    + +

    Returns relative amount of fuel (from 0.0 to 1.0) the group has in its internal tanks.

    + + +

    If there are additional fuel tanks the value may be greater than 1.0.

    + +

    Return values

    +
      +
    1. + +

      #number: +The relative amount of fuel (from 0.0 to 1.0).

      + +
    2. +
    3. + +

      #nil: +The GROUP is not existing or alive.

      + +
    4. +
    +
    +
    +
    +
    + + +GROUP:GetHeading() + +
    +
    + +

    Returns the mean heading of every UNIT in the GROUP in degrees

    + +

    Return values

    +
      +
    1. + +

      #number: +mean heading of the GROUP

      + +
    2. +
    3. + +

      #nil: +The first UNIT is not existing or alive.

      + +
    4. +
    +
    +
    +
    +
    + GROUP:GetInitialSize() @@ -957,6 +1167,24 @@ Minimum height found.

    + +GROUP:GetPlayerName() + +
    +
    + +

    Gets the player name of the group.

    + +

    Return value

    + +

    #string: +The player name of the group.

    + +
    +
    +
    +
    + GROUP:GetPlayerNames() @@ -985,6 +1213,34 @@ The group has no players

    + +GROUP:GetPointVec2() + +
    +
    + +

    Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission.

    + +

    Return values

    +
      +
    1. + +

      Core.Point#POINT_VEC2: +The 2D point vector of the first DCS Unit of the GROUP.

      + +
    2. +
    3. + +

      #nil: +The first UNIT is not existing or alive.

      + +
    4. +
    +
    +
    +
    +
    + GROUP:GetPositionVec3() @@ -1013,6 +1269,46 @@ The POSITIONABLE is not existing or alive.

    + +GROUP:GetRandomVec3(Radius) + +
    +
    + +

    Returns a random DCSTypes#Vec3 vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP.

    + +

    Parameter

    +
      +
    • + +

      #number Radius :

      + +
    • +
    +

    Return values

    +
      +
    1. + +

      Dcs.DCSTypes#Vec3: +The random 3D point vector around the first UNIT of the GROUP.

      + +
    2. +
    3. + +

      #nil: +The GROUP is invalid or empty

      + +
    4. +
    +

    Usage:

    +
    
    +-- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP
    + +
    +
    +
    +
    + GROUP:GetSize() @@ -1414,7 +1710,7 @@ The zone to test.

    Return value

    #boolean: -Returns true if the Group is completely within the Zone#ZONE_BASE

    +Returns true if the Group is not within the Zone#ZONE_BASE

    @@ -1441,7 +1737,7 @@ The zone to test.

    Return value

    #boolean: -Returns true if the Group is completely within the Zone#ZONE_BASE

    +Returns true if the Group is partially within the Zone#ZONE_BASE

    @@ -1509,6 +1805,24 @@ The DCS Group name

    #GROUP: self

    + +
    +
    +
    + + +GROUP:ResetEvents() + +
    +
    + +

    Reset the subscriptions.

    + +

    Return value

    + +

    #GROUP:

    + +
    @@ -1757,6 +2071,20 @@ The country ID.

    #table:

    + +
    +
    +
    + + #GROUP.Takeoff + +GROUP.Takeoff + +
    +
    + + +
    @@ -1783,6 +2111,68 @@ The country ID.

    #GROUP:

    + +
    + +

    Type GROUP.Takeoff

    + +

    Enumerator for location at airbases

    + +

    Field(s)

    +
    +
    + + #number + +GROUP.Takeoff.Air + +
    +
    + + + +
    +
    +
    +
    + + #number + +GROUP.Takeoff.Cold + +
    +
    + + + +
    +
    +
    +
    + + #number + +GROUP.Takeoff.Hot + +
    +
    + + + +
    +
    +
    +
    + + #number + +GROUP.Takeoff.Runway + +
    +
    + + +
    diff --git a/docs/Documentation/Identifiable.html b/docs/Documentation/Identifiable.html index 26302176c..bb4e93bce 100644 --- a/docs/Documentation/Identifiable.html +++ b/docs/Documentation/Identifiable.html @@ -17,9 +17,16 @@ index

    Module Identifiable

    -

    This module contains the IDENTIFIABLE class.

    +

    Wrapper -- IDENTIFIABLE is an intermediate class wrapping DCS Object class derived Objects.

    -

    1) #IDENTIFIABLE class, extends Object#OBJECT

    -

    The #IDENTIFIABLE class is a wrapper class to handle the DCS Identifiable objects:

    +
    -
      -
    • Support all DCS Identifiable APIs.
    • -
    • Enhance with Identifiable specific APIs not in the DCS Identifiable API set.
    • -
    • Manage the "state" of the DCS Identifiable.
    • -
    - -

    1.1) IDENTIFIABLE constructor:

    -

    The IDENTIFIABLE class provides the following functions to construct a IDENTIFIABLE instance:

    - - - -

    1.2) IDENTIFIABLE methods:

    -

    The following methods can be used to identify an identifiable object:

    - - +

    Author: Sven Van de Velde (FlightControl)

    +

    Contributions:


    @@ -114,19 +123,19 @@ IDENTIFIABLE +

    IDENTIFIABLE class, extends Object#OBJECT

    +

    The IDENTIFIABLE class is a wrapper class to handle the DCS Identifiable objects:

    + +
      +
    • Support all DCS Identifiable APIs.
    • +

    Type IDENTIFIABLE

    - - - -
    IDENTIFIABLE.ClassName - -
    IDENTIFIABLE:GetCallsign()

    Gets the CallSign of the IDENTIFIABLE, which is a blank by default.

    @@ -211,6 +220,27 @@
    +

    IDENTIFIABLE class, extends Object#OBJECT

    + +

    The IDENTIFIABLE class is a wrapper class to handle the DCS Identifiable objects:

    + +
      +
    • Support all DCS Identifiable APIs.
    • +
    + + +
      +
    • Enhance with Identifiable specific APIs not in the DCS Identifiable API set.
    • +
    • Manage the "state" of the DCS Identifiable.
    • +
    + +

    IDENTIFIABLE constructor

    + +

    The IDENTIFIABLE class provides the following functions to construct a IDENTIFIABLE instance:

    + +
    @@ -218,24 +248,7 @@

    Type Identifiable

    Type IDENTIFIABLE

    - -

    The IDENTIFIABLE class

    - -

    Field(s)

    -
    -
    - - #string - -IDENTIFIABLE.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    diff --git a/docs/Documentation/Menu.html b/docs/Documentation/Menu.html index 898011d4b..edded7fd7 100644 --- a/docs/Documentation/Menu.html +++ b/docs/Documentation/Menu.html @@ -17,9 +17,16 @@ index @@ -107,175 +139,103 @@ On top, MOOSE implements variable parameter passing for command
    - -

    The above menus classes are derived from 2 main abstract classes defined within the MOOSE framework (so don't use these):

    - -

    1) MENU_ BASE abstract base classes (don't use them)

    -

    The underlying base menu classes are NOT to be used within your missions. -These are simply abstract base classes defining a couple of fields that are used by the -derived MENU_ classes to manage menus.

    - -

    1.1) #MENU_BASE class, extends Base#BASE

    -

    The #MENU_BASE class defines the main MENU class where other MENU classes are derived from.

    - -

    1.2) #MENUCOMMANDBASE class, extends Base#BASE

    -

    The #MENUCOMMANDBASE class defines the main MENU class where other MENU COMMAND_ classes are derived from, in order to set commands.

    +

    -

    +

    Author: Sven Van de Velde (FlightControl)

    +

    Contributions:


    - -

    The next menus define the MENU classes that you can use within your missions.

    - -

    2) MENU MISSION classes

    -

    The underlying classes manage the menus for a complete mission file.

    - -

    2.1) #MENU_MISSION class, extends Menu#MENU_BASE

    -

    The Menu#MENU_MISSION class manages the main menus for a complete mission.
    -You can add menus with the MENU_MISSION.New method, which constructs a MENU_MISSION object and returns you the object reference. -Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENU_MISSION.Remove.

    - -

    2.2) #MENUMISSIONCOMMAND class, extends Menu#MENUCOMMANDBASE

    -

    The Menu#MENUMISSIONCOMMAND class manages the command menus for a complete mission, which allow players to execute functions during mission execution.
    -You can add menus with the MENUMISSIONCOMMAND.New method, which constructs a MENUMISSIONCOMMAND object and returns you the object reference. -Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENUMISSIONCOMMAND.Remove.

    - -
    - -

    3) MENU COALITION classes

    -

    The underlying classes manage the menus for whole coalitions.

    - -

    3.1) #MENU_COALITION class, extends Menu#MENU_BASE

    -

    The Menu#MENU_COALITION class manages the main menus for coalitions.
    -You can add menus with the MENU_COALITION.New method, which constructs a MENU_COALITION object and returns you the object reference. -Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENU_COALITION.Remove.

    - -

    3.2) Menu#MENUCOALITIONCOMMAND class, extends Menu#MENUCOMMANDBASE

    -

    The Menu#MENUCOALITIONCOMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.
    -You can add menus with the MENUCOALITIONCOMMAND.New method, which constructs a MENUCOALITIONCOMMAND object and returns you the object reference. -Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENUCOALITIONCOMMAND.Remove.

    - -
    - -

    4) MENU GROUP classes

    -

    The underlying classes manage the menus for groups. Note that groups can be inactive, alive or can be destroyed.

    - -

    4.1) Menu#MENU_GROUP class, extends Menu#MENU_BASE

    -

    The Menu#MENU_GROUP class manages the main menus for coalitions.
    -You can add menus with the MENU_GROUP.New method, which constructs a MENU_GROUP object and returns you the object reference. -Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENU_GROUP.Remove.

    - -

    4.2) Menu#MENUGROUPCOMMAND class, extends Menu#MENUCOMMANDBASE

    -

    The Menu#MENUGROUPCOMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.
    -You can add menus with the MENUGROUPCOMMAND.New method, which constructs a MENUGROUPCOMMAND object and returns you the object reference. -Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENUGROUPCOMMAND.Remove.

    - -
    - -

    5) MENU CLIENT classes

    -

    The underlying classes manage the menus for units with skill level client or player.

    - -

    5.1) Menu#MENU_CLIENT class, extends Menu#MENU_BASE

    -

    The Menu#MENU_CLIENT class manages the main menus for coalitions.
    -You can add menus with the MENU_CLIENT.New method, which constructs a MENU_CLIENT object and returns you the object reference. -Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENU_CLIENT.Remove.

    - -

    5.2) Menu#MENUCLIENTCOMMAND class, extends Menu#MENUCOMMANDBASE

    -

    The Menu#MENUCLIENTCOMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.
    -You can add menus with the MENUCLIENTCOMMAND.New method, which constructs a MENUCLIENTCOMMAND object and returns you the object reference. -Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENUCLIENTCOMMAND.Remove.

    - -
    - -

    Contributions: -

    -

    Authors: FlightControl : Design & Programming

    - +

    Global(s)

    MENU_BASE - +

    MENU_BASE class, extends Base#BASE

    +

    The MENU_BASE class defines the main MENU class where other MENU classes are derived from.

    MENU_CLIENT +

    MENU_CLIENT class, extends Menu#MENU_BASE

    +

    The MENU_CLIENT class manages the main menus for coalitions.

    MENU_CLIENT_COMMAND +

    MENUCLIENTCOMMAND class, extends Menu#MENUCOMMANDBASE

    +

    The MENUCLIENTCOMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.

    MENU_COALITION +

    MENU_COALITION class, extends Menu#MENU_BASE

    +

    The Menu#MENU_COALITION class manages the main menus for coalitions.

    MENU_COALITION_COMMAND +

    MENUCOALITIONCOMMAND class, extends Menu#MENUCOMMANDBASE

    +

    The MENUCOALITIONCOMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.

    MENU_COMMAND_BASE +

    MENUCOMMANDBASE class, extends Base#BASE

    +
    +

    The MENUCOMMANDBASE class defines the main MENU class where other MENU COMMAND_ +classes are derived from, in order to set commands.

    MENU_GROUP +

    MENU_GROUP class, extends Menu#MENU_BASE

    +

    The MENU_GROUP class manages the main menus for coalitions.

    MENU_GROUP_COMMAND +

    MENUGROUPCOMMAND class, extends Menu#MENUCOMMANDBASE

    +

    The Menu#MENUGROUPCOMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.

    MENU_MISSION +

    MENU_MISSION class, extends Menu#MENU_BASE

    +

    The MENU_MISSION class manages the main menus for a complete mission.

    MENU_MISSION_COMMAND +

    MENUMISSIONCOMMAND class, extends Menu#MENUCOMMANDBASE

    +

    The MENUMISSIONCOMMAND class manages the command menus for a complete mission, which allow players to execute functions during mission execution.

    Type MENU_BASE

    - - - - - - - - - - - - @@ -285,7 +245,7 @@ Using this object reference, you can then remove ALL the menus and submenus unde - + @@ -306,6 +266,12 @@ Using this object reference, you can then remove ALL the menus and submenus unde + + + + @@ -319,12 +285,6 @@ Using this object reference, you can then remove ALL the menus and submenus unde

    Type MENU_CLIENT

    MENU_BASE.ClassName - -
    MENU_BASE:GetMenu(MenuText)

    Gets a Menu from a parent Menu

    -
    MENU_BASE.MenuParentPath - -
    MENU_BASE.MenuPath -
    MENU_BASE.MenuTextMENU_BASE.MenuTag MENU_BASE:SetRemoveParent(RemoveParent)

    Sets a Menu to remove automatically the parent menu when the menu removed is the last child menu of that parent Menu.

    +
    MENU_BASE:SetTag(MenuTag) +

    Sets a tag for later selection of menu refresh.

    - - - - + + + + @@ -450,12 +475,6 @@ - - - - @@ -617,15 +636,13 @@
    -

    3) ZONE class, extends Zone#ZONE_RADIUS

    +

    ZONE class, extends Zone#ZONE_RADIUS

    The ZONE class, defined by the zone name as defined within the Mission Editor.

    This class implements the inherited functions from #ZONE_RADIUS taking into account the own zone format and properties.

    -
    -
    @@ -639,54 +656,60 @@
    -

    1) ZONE_BASE class, extends Base#BASE

    +

    ZONE_BASE class, extends Base#BASE

    This class is an abstract BASE class for derived classes, and is not meant to be instantiated.

    -

    1.1) Each zone has a name:

    +

    Each zone has a name:

    -

    1.2) Each zone implements two polymorphic functions defined in Zone#ZONE_BASE:

    +

    Each zone implements two polymorphic functions defined in Zone#ZONE_BASE:

    -

    1.3) A zone has a probability factor that can be set to randomize a selection between zones:

    +

    A zone has a probability factor that can be set to randomize a selection between zones:

      -
    • ZONE_BASE.SetRandomizeProbability(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% )
    • -
    • ZONE_BASE.GetRandomizeProbability(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% )
    • +
    • ZONE_BASE.SetZoneProbability(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% )
    • +
    • ZONE_BASE.GetZoneProbability(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% )
    • ZONE_BASE.GetZoneMaybe(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate.
    -

    1.4) A zone manages Vectors:

    +

    A zone manages vectors:

    -

    1.5) A zone has a bounding square:

    +

    A zone has a bounding square:

    -

    1.6) A zone can be marked:

    +

    A zone can be marked:

    -
    @@ -700,7 +723,7 @@
    -

    5) #ZONE_GROUP class, extends Zone#ZONE_RADIUS

    +

    ZONE_GROUP class, extends Zone#ZONE_RADIUS

    The ZONE_GROUP class defines by a zone around a Group#GROUP with a radius.

    @@ -708,8 +731,6 @@

    The current leader of the group defines the center of the zone. This class implements the inherited functions from Zone#ZONE_RADIUS taking into account the own zone format and properties.

    -
    -
    @@ -723,15 +744,13 @@ This class implements the inherited functions from Zone#ZONEPOLYGONBASE +

    ZONE_POLYGON class, extends Zone#ZONEPOLYGONBASE

    The ZONE_POLYGON class defined by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon.

    This class implements the inherited functions from Zone#ZONE_RADIUS taking into account the own zone format and properties.

    -
    - @@ -745,7 +764,7 @@ This class implements the inherited functions from Zone#ZONE_BASE +

    ZONEPOLYGONBASE class, extends Zone#ZONE_BASE

    The ZONEPOLYGONBASE class defined by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon.

    @@ -753,7 +772,7 @@ This class implements the inherited functions from Zone#ZONE_RADIUS taking into account the own zone format and properties. This class is an abstract BASE class for derived classes, and is not meant to be instantiated.

    -

    6.1) Zone point randomization

    +

    Zone point randomization

    Various functions exist to find random points within the zone.

    @@ -763,8 +782,6 @@ This class is an abstract BASE class for derived classes, and is not meant to be
  • ZONEPOLYGONBASE.GetRandomPointVec3(): Return a Point#POINT_VEC3 object representing a random 3D point at landheight within the zone.
  • -
    - @@ -778,27 +795,27 @@ This class is an abstract BASE class for derived classes, and is not meant to be
    -

    2) Zone#ZONE_RADIUS class, extends Zone#ZONE_BASE

    +

    ZONE_RADIUS class, extends Zone#ZONE_BASE

    The ZONE_RADIUS class defined by a zone name, a location and a radius.

    This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties.

    -

    2.1) Zone#ZONE_RADIUS constructor

    +

    ZONE_RADIUS constructor

    -

    2.2) Manage the radius of the zone

    +

    Manage the radius of the zone

    -

    2.3) Manage the location of the zone

    +

    Manage the location of the zone

    -

    2.4) Zone point randomization

    +

    Zone point randomization

    Various functions exist to find random points within the zone.

    @@ -816,8 +833,6 @@ This class is an abstract BASE class for derived classes, and is not meant to be
  • ZONE_RADIUS.GetRandomPointVec3(): Gets a Point#POINT_VEC3 object representing a random 3D point in the zone. Note that the height of the point is at landheight.
  • -
    -
    @@ -831,15 +846,13 @@ This class is an abstract BASE class for derived classes, and is not meant to be
    -

    4) #ZONE_UNIT class, extends Zone#ZONE_RADIUS

    +

    ZONE_UNIT class, extends Zone#ZONE_RADIUS

    The ZONE_UNIT class defined by a zone around a Unit#UNIT with a radius.

    This class implements the inherited functions from #ZONE_RADIUS taking into account the own zone format and properties.

    -
    -
    @@ -876,10 +889,7 @@ The name of the zone as defined within the mission editor.

    Type ZONE_BASE

    - -

    The ZONE_BASE class

    - -

    Field(s)

    +

    Field(s)

    @@ -914,6 +924,24 @@ The bounding square.

    + +ZONE_BASE:GetCoordinate() + +
    +
    + +

    Returns a Point#COORDINATE of the zone.

    + +

    Return value

    + +

    Core.Point#COORDINATE: +The Coordinate of the zone.

    + +
    +
    +
    +
    + ZONE_BASE:GetName() @@ -1131,27 +1159,81 @@ A value between 0 and 1. 0 = 0% and 1 = 100% probability.

    - -ZONE_BASE:IsVec2InZone(Vec2) + +ZONE_BASE:IsPointVec2InZone(PointVec2)
    -

    Returns if a location is within the zone.

    +

    Returns if a PointVec2 is within the zone.

    Parameter

    Return value

    #boolean: -true if the location is within the zone.

    +true if the PointVec2 is within the zone.

    + +
    +
    +
    +
    + + +ZONE_BASE:IsPointVec3InZone(PointVec3) + +
    +
    + +

    Returns if a PointVec3 is within the zone.

    + +

    Parameter

    + +

    Return value

    + +

    #boolean: +true if the PointVec3 is within the zone.

    + +
    +
    +
    +
    + + +ZONE_BASE:IsVec2InZone(Vec2) + +
    +
    + +

    Returns if a Vec2 is within the zone.

    + +

    Parameter

    + +

    Return value

    + +

    #boolean: +true if the Vec2 is within the zone.

    @@ -1164,7 +1246,7 @@ true if the location is within the zone.

    -

    Returns if a point is within the zone.

    +

    Returns if a Vec3 is within the zone.

    Parameter

      @@ -1178,7 +1260,7 @@ The point to test.

      Return value

      #boolean: -true if the point is within the zone.

      +true if the Vec3 is within the zone.

    @@ -1349,6 +1431,39 @@ The smoke color.

    + +ZONE_GROUP:GetRandomPointVec2(inner, outer) + +
    +
    + +

    Returns a Point#POINT_VEC2 object reflecting a random 2D location within the zone.

    + +

    Parameters

    +
      +
    • + +

      #number inner : +(optional) Minimal distance from the center of the zone. Default is 0.

      + +
    • +
    • + +

      #number outer : +(optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.

      + +
    • +
    +

    Return value

    + +

    Core.Point#POINT_VEC2: +The Point#POINT_VEC2 object reflecting the random 3D location within the zone.

    + +
    +
    +
    +
    + ZONE_GROUP:GetRandomVec2() @@ -1419,20 +1534,6 @@ The radius of the zone.

    #ZONE_GROUP: self

    - -
    -
    -
    - - Wrapper.Group#GROUP - -ZONE_GROUP.ZoneGROUP - -
    -
    - - -
    @@ -1597,6 +1698,24 @@ The Vec2 coordinate.

    + +ZONE_POLYGON_BASE:GetVec2() + +
    +
    + +

    Returns the center location of the polygon.

    + +

    Return value

    + +

    Dcs.DCSTypes#Vec2: +The location of the zone based on the Group location.

    + +
    +
    +
    +
    + ZONE_POLYGON_BASE:IsVec2InZone(Vec2) @@ -1658,20 +1777,6 @@ An array of DCSTypes#Vec2, forming a polygon

    #ZONEPOLYGONBASE: self

    - -
    -
    -
    - - #ZONE_POLYGON_BASE.ListVec2 - -ZONE_POLYGON_BASE.Polygon - -
    -
    - -

    The polygon defined by an array of DCSTypes#Vec2.

    -
    @@ -2163,10 +2268,7 @@ self

    Type ZONE_UNIT

    - -

    The ZONE_UNIT class defined by a zone around a Unit#UNIT with a radius.

    - -

    Field(s)

    +

    Field(s)

    diff --git a/docs/Documentation/env.html b/docs/Documentation/env.html index 985b449dc..09dbbfcd0 100644 --- a/docs/Documentation/env.html +++ b/docs/Documentation/env.html @@ -17,7 +17,17 @@ index

    Module

    MENU_CLIENT.ClassName - -
    MENU_CLIENT:New(Client, MenuText, ParentMenu)

    MENU_CLIENT constructor.

    @@ -347,12 +307,6 @@ Using this object reference, you can then remove ALL the menus and submenus unde

    Type MENU_CLIENT_COMMAND

    - - - - @@ -162,7 +163,7 @@ @@ -170,7 +171,7 @@ @@ -178,7 +179,7 @@ @@ -186,7 +187,7 @@ @@ -194,7 +195,7 @@ @@ -202,7 +203,7 @@ @@ -230,6 +231,12 @@ + + + + @@ -290,18 +297,30 @@ + + + + + + + + @@ -367,6 +386,12 @@

    Type ZONE_GROUP

    MENU_CLIENT_COMMAND.ClassName - -
    MENU_CLIENT_COMMAND:New(Client, MenuText, ParentMenu, CommandMenuFunction, ...)

    MENUCLIENTCOMMAND constructor.

    @@ -369,12 +323,6 @@ Using this object reference, you can then remove ALL the menus and submenus unde

    Type MENU_COALITION

    - - - - - + + + + + @@ -169,9 +168,9 @@ Use the method TASK.AddScore() to add scores whe - + @@ -181,9 +180,21 @@ Use the method TASK.AddScore() to add scores whe - + + + + + + + + + @@ -208,6 +219,12 @@ Use the method TASK.AddScore() to add scores whe + + + + @@ -232,6 +249,24 @@ Use the method TASK.AddScore() to add scores whe + + + + + + + + + + + + @@ -256,6 +291,12 @@ Use the method TASK.AddScore() to add scores whe + + + + @@ -280,6 +321,12 @@ Use the method TASK.AddScore() to add scores whe + + + + @@ -301,7 +348,7 @@ Use the method TASK.AddScore() to add scores whe - + @@ -361,13 +408,19 @@ Use the method TASK.AddScore() to add scores whe - + - + + + + + @@ -397,9 +450,15 @@ Use the method TASK.AddScore() to add scores whe - + + + + + @@ -421,21 +480,15 @@ Use the method TASK.AddScore() to add scores whe - + - + - - - - @@ -448,12 +501,6 @@ Use the method TASK.AddScore() to add scores whe - - - - @@ -469,21 +516,21 @@ Use the method TASK.AddScore() to add scores whe - + - + - + @@ -502,6 +549,12 @@ Use the method TASK.AddScore() to add scores whe + + + + @@ -514,12 +567,24 @@ Use the method TASK.AddScore() to add scores whe + + + + + + + + @@ -544,6 +609,24 @@ Use the method TASK.AddScore() to add scores whe + + + + + + + + + + + + @@ -634,12 +717,24 @@ Use the method TASK.AddScore() to add scores whe + + + + + + + + @@ -694,6 +789,12 @@ Use the method TASK.AddScore() to add scores whe + + + + @@ -730,6 +831,12 @@ Use the method TASK.AddScore() to add scores whe + + + + @@ -769,6 +876,57 @@ Use the method TASK.AddScore() to add scores whe
    +

    TASK class, extends Base#BASE

    + +

    The TASK class implements the methods for task orchestration within MOOSE.

    + + + +

    The class provides a couple of methods to:

    + + + +

    1.2) Set and enquire task status (beyond the task state machine processing).

    + +

    A task needs to implement as a minimum the following task states:

    + +
      +
    • Success: Expresses the successful execution and finalization of the task.
    • +
    • Failed: Expresses the failure of a task.
    • +
    • Planned: Expresses that the task is created, but not yet in execution and is not assigned yet.
    • +
    • Assigned: Expresses that the task is assigned to a Group of players, and that the task is in execution mode.
    • +
    + +

    A task may also implement the following task states:

    + +
      +
    • Rejected: Expresses that the task is rejected by a player, who was requested to accept the task.
    • +
    • Cancelled: Expresses that the task is cancelled by HQ or through a logical situation where a cancellation of the task is required.
    • +
    + +

    A task can implement more statusses than the ones outlined above. Please consult the documentation of the specific tasks to understand the different status modelled.

    + +

    The status of tasks can be set by the methods State followed by the task status. An example is StateAssigned(). +The status of tasks can be enquired by the methods IsState followed by the task status name. An example is if IsStateAssigned() then.

    + +

    1.3) Add scoring when reaching a certain task status:

    + +

    Upon reaching a certain task status in a task, additional scoring can be given. If the Mission has a scoring system attached, the scores will be added to the mission scoring. +Use the method TASK.AddScore() to add scores when a status is reached.

    + +

    1.4) Task briefing:

    + +

    A task briefing can be given that is shown to the player when he is assigned to the task.

    @@ -776,10 +934,7 @@ Use the method TASK.AddScore() to add scores whe

    Type Task

    Type TASK

    - -

    The TASK class

    - -

    Field(s)

    +

    Field(s)

    @@ -799,8 +954,8 @@ Use the method TASK.AddScore() to add scores whe
    - -TASK:AbortUnit(PlayerUnit) + +TASK:AbortGroup(PlayerUnit, PlayerGroup)
    @@ -811,19 +966,68 @@ Use the method TASK.AddScore() to add scores whe

    If the Unit was not part of the Task, false is returned. If the Unit is part of the Task, true is returned.

    -

    Parameter

    +

    Parameters

    • Wrapper.Unit#UNIT PlayerUnit : The CLIENT or UNIT of the Player aborting the Task.

      +
    • +
    • + +

      PlayerGroup :

      +

    Return value

    -

    #boolean: -true if Unit is part of the Task.

    +

    #TASK:

    + + +
    +
    +
    +
    + + +TASK:AddProgress(PlayerName, ProgressText, ProgressTime, ProgressPoints) + +
    +
    + +

    Add Task Progress for a Player Name

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #string ProgressText : +The text that explains the Progress achieved.

      + +
    • +
    • + +

      #number ProgressTime : +The time the progress was achieved.

      + +
    • +
    • + +

      ProgressPoints :

      + +
    • +
    +

    Return value

    + +

    #TASK:

    +
    @@ -898,13 +1102,25 @@ self

    - #string - -TASK.ClassName + +TASK:ClearGroupAssignment(TaskGroup)
    +

    Clear the Group assignment from the Task.

    + +

    Parameter

    + +

    Return value

    + +

    #TASK:

    @@ -926,8 +1142,8 @@ self

    - -TASK:CrashUnit(PlayerUnit) + +TASK:CrashGroup(PlayerUnit, PlayerGroup)
    @@ -939,19 +1155,52 @@ self

    If the Unit was not part of the Task, false is returned. If the Unit is part of the Task, true is returned.

    -

    Parameter

    +

    Parameters

    • Wrapper.Unit#UNIT PlayerUnit : The CLIENT or UNIT of the Player aborting the Task.

      +
    • +
    • + +

      PlayerGroup :

      +

    Return value

    -

    #boolean: -true if Unit is part of the Task.

    +

    #TASK:

    + + +
    +
    +
    +
    + + + +TASK.DetectedItemIndex + +
    +
    + + + +
    +
    +
    +
    + + + +TASK.Detection + +
    +
    + +
    @@ -1025,6 +1274,24 @@ true if Unit is part of the Task.

    + +
    +
    +
    + + +TASK:GetBriefing() + +
    +
    + +

    Gets the Task briefing.

    + +

    Return value

    + +

    #string: +The briefing text.

    +
    @@ -1102,6 +1369,63 @@ The Task Name

    + +TASK:GetPlayerCount() + +
    +
    + +

    Create a count of the players in the Task.

    + +

    Return value

    + +

    #number: +The total number of players in the task.

    + +
    +
    +
    +
    + + +TASK:GetPlayerNames() + +
    +
    + +

    Create a list of the players in the Task.

    + +

    Return value

    + +

    #map:

    +

    string,Wrapper.Group#GROUP> A map of the players

    + +
    +
    +
    +
    + + +TASK:GetPlayerProgress(PlayerName) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      PlayerName :

      + +
    • +
    +
    +
    +
    +
    + TASK:GetProcessTemplate(ProcessName) @@ -1185,6 +1509,24 @@ Scoring

    + +TASK:GetTaskBriefing() + +
    +
    + +

    Returns the Task briefing.

    + +

    Return value

    + +

    #string: +Task briefing.

    + +
    +
    +
    +
    + TASK:GetTaskIndex() @@ -1260,6 +1602,19 @@ TaskType

    Core.Fsm#FSM_PROCESS:

    + +
    +
    +
    + + +TASK:Goal() + +
    +
    + +

    Goal Trigger for TASK

    +
    @@ -1335,8 +1690,8 @@ self

    - -TASK:IsAssignedToGroup(TaskGroup) + +TASK:IsGroupAssigned(TaskGroup)
    @@ -1474,7 +1829,7 @@ self

    Add a PlayerUnit to join the Task.

    -

    For each Group within the Task, the Unit is check if it can join the Task. +

    For each Group within the Task, the Unit is checked if it can join the Task. If the Unit was not part of the Task, false is returned. If the Unit is part of the Task, true is returned.

    @@ -1503,9 +1858,29 @@ true if Unit is part of the Task.

    - - -TASK.Menu + +TASK:MenuAssignToGroup(TaskGroup) + +
    +
    + + + +

    Parameter

    + +
    +
    +
    +
    + + +TASK.MenuAssigned
    @@ -1517,22 +1892,14 @@ true if Unit is part of the Task.

    - -TASK.MenuAssignToGroup(MenuParam) + +TASK.MenuPlanned
    -

    Parameter

    -
      -
    • - -

      MenuParam :

      - -
    • -
    @@ -1616,7 +1983,7 @@ true if Unit is part of the Task.

    -TASK:New(Mission, SetGroupAssign, TaskName, TaskType) +TASK:New(Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing)
    @@ -1651,6 +2018,11 @@ The name of the Task

    #string TaskType : The type of the Task

    + +
  • + +

    TaskBriefing :

    +
  • Return value

    @@ -1663,6 +2035,42 @@ self

    + +TASK:OnAfterGoal(Controllable, From, Event, To) + +
    +
    + +

    Goal Handler OnAfter for TASK

    + +

    Parameters

    + +
    +
    +
    +
    + TASK:OnAfterPlayerAborted(PlayerUnit, PlayerName) @@ -1747,12 +2155,40 @@ The name of the Player.

    - -TASK.Players + +TASK:OnBeforeGoal(Controllable, From, Event, To)
    +

    Goal Handler OnBefore for TASK

    + +

    Parameters

    + +

    Return value

    + +

    #boolean:

    @@ -1760,28 +2196,31 @@ The name of the Player.

    - - -TASK.ProcessClasses + +TASK:RefreshMenus(TaskGroup, MenuTime)
    +

    Remove the menu option of the Task for a Group.

    +

    Parameters

    +
    -
    -
    -
    - - - -TASK.Processes - -
    -
    - + +
  • + +

    #number MenuTime :

    +
  • + +

    Return value

    + +

    #TASK: +self

    @@ -1840,37 +2279,6 @@ self

    #TASK:

    -
    -
    -
    -
    - - -TASK:RemovePlannedMenuForGroup(TaskGroup, MenuTime) - -
    -
    - -

    Remove the menu option of the Task for a Group.

    - -

    Parameters

    - -

    Return value

    - -

    #TASK: -self

    -
    @@ -1919,7 +2327,7 @@ self

    -TASK:ReportDetails() +TASK:ReportDetails(TaskGroup, ReportGroup)
    @@ -1929,6 +2337,48 @@ self

    List the Task Status, and the Players assigned to the Task.

    +

    Parameters

    + +

    Return value

    + +

    #string:

    + + +
    +
    +
    +
    + + +TASK:ReportOverview(ReportGroup) + +
    +
    + +

    Create an overiew report of the Task.

    + + +

    List the Task Name and Status

    + +

    Parameter

    +
      +
    • + +

      ReportGroup :

      + +
    • +

    Return value

    #string:

    @@ -1940,7 +2390,7 @@ self

    -TASK:ReportSummary() +TASK:ReportSummary(ReportGroup)
    @@ -1950,25 +2400,19 @@ self

    List the Task Name and Status

    +

    Parameter

    +

    Return value

    #string:

    -
    -
    -
    -
    - - - -TASK.Scores - -
    -
    - - -
    @@ -2039,6 +2483,37 @@ self

    #TASK: self

    +
    +
    +
    +
    + + +TASK:SetDetection(Detection, DetectedItemIndex) + +
    +
    + +

    Set detection of a task

    + +

    Parameters

    + +

    Return value

    + +

    #TASK:

    + +
    @@ -2079,6 +2554,32 @@ self

    The Set of Groups assigned to the Task

    +
    +
    +
    +
    + + +TASK:SetGroupAssigned(TaskGroup) + +
    +
    + +

    Set Group assigned to the Task.

    + +

    Parameter

    + +

    Return value

    + +

    #TASK:

    + +
    @@ -2105,6 +2606,40 @@ self

    + +TASK:SetInfo(TaskInfo, TaskInfoText, TaskInfoOrder) + +
    +
    + +

    Sets the Information on the Task

    + +

    Parameters

    +
      +
    • + +

      #string TaskInfo : +The key and title of the task information.

      + +
    • +
    • + +

      #string TaskInfoText : +The Task info text.

      + +
    • +
    • + +

      #number TaskInfoOrder : +The ordering, a number between 0 and 99.

      + +
    • +
    +
    +
    +
    +
    + TASK:SetMenu(MenuTime) @@ -2215,6 +2750,120 @@ The menu text.

    #TASK: self

    + +
    +
    +
    + + +TASK:SetScoreOnFail(PlayerName, Penalty, TaskUnit) + +
    +
    + +

    Set a penalty when the A2A attack has failed.

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #number Penalty : +The penalty in points, must be a negative value!

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK:

    + + +
    +
    +
    +
    + + +TASK:SetScoreOnProgress(PlayerName, Score, TaskUnit) + +
    +
    + +

    Set a score when progress has been made by the player.

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #number Score : +The score in points to be granted when task process has been achieved.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK:

    + + +
    +
    +
    +
    + + +TASK:SetScoreOnSuccess(PlayerName, Score, TaskUnit) + +
    +
    + +

    Set a score when all the targets in scope of the A2A attack, have been destroyed.

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #number Score : +The score in points.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK:

    + +
    @@ -2472,6 +3121,19 @@ Fsm#FSM_PROCESS

    + +
    +
    +
    + + +TASK.TaskInfo + +
    +
    + + +
    @@ -2486,6 +3148,19 @@ Fsm#FSM_PROCESS

    + +
    +
    +
    + + +TASK.TaskProgress + +
    +
    + + +
    @@ -2545,7 +3220,7 @@ Fsm#FSM_PROCESS

    @@ -2641,6 +3316,27 @@ self

    + +TASK:__Goal(Delay) + +
    +
    + +

    Goal Asynchronous Trigger for TASK

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + TASK:__Replan() @@ -2807,6 +3503,37 @@ self

    + +TASK:onenterCancelled(From, Event, To) + +
    +
    + +

    FSM function for a TASK

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + TASK:onenterFailed(From, Event, To) @@ -2931,6 +3658,8 @@ self

    Type integer

    +

    Type map

    + diff --git a/docs/Documentation/Task_A2A.html b/docs/Documentation/Task_A2A.html new file mode 100644 index 000000000..53005ee57 --- /dev/null +++ b/docs/Documentation/Task_A2A.html @@ -0,0 +1,1652 @@ + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +

    Module Task_A2A

    + +

    Tasking - The TASK_A2A models tasks for players in Air to Air engagements.

    + + + +

    Banner Image

    + +
    + +

    Author: Sven Van de Velde (FlightControl)

    + +

    Contributions:

    + +
    +

    + +

    Global(s)

    +
    MENU_COALITION.ClassName - -
    MENU_COALITION:New(Coalition, MenuText, ParentMenu)

    MENU_COALITION constructor.

    @@ -397,12 +345,6 @@ Using this object reference, you can then remove ALL the menus and submenus unde

    Type MENU_COALITION_COMMAND

    - - - -
    MENU_COALITION_COMMAND.ClassName - -
    MENU_COALITION_COMMAND:New(Coalition, MenuText, ParentMenu, CommandMenuFunction, CommandMenuArgument, ...)

    MENU_COALITION constructor.

    @@ -419,24 +361,6 @@ Using this object reference, you can then remove ALL the menus and submenus unde

    Type MENU_COMMAND_BASE

    - - - - - - - - - - - - + + + + + + + +
    MENU_COMMAND_BASE.ClassName - -
    MENU_COMMAND_BASE.CommandMenuArgument - -
    MENU_COMMAND_BASE.CommandMenuFunction - -
    MENU_COMMAND_BASE.MenuCallHandler @@ -446,6 +370,24 @@ Using this object reference, you can then remove ALL the menus and submenus unde MENU_COMMAND_BASE.New(#, self, MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments)

    Constructor

    +
    MENU_COMMAND_BASE.SetCommandMenuArguments(#, self, CommandMenuArguments) +

    This sets the new command arguments of a menu, +so that if a menu is regenerated, or if command arguments change, +that the arguments set for the menu are loosely coupled with the menu itself!!! +If the arguments change, no new menu needs to be generated if the menu text is the same!!!

    +
    MENU_COMMAND_BASE.SetCommandMenuFunction(#, self, CommandMenuFunction) +

    This sets the new command function of a menu, +so that if a menu is regenerated, or if command function changes, +that the function set for the menu is loosely coupled with the menu itself!!! +If the function changes, no new menu needs to be generated if the menu text is the same!!!

    @@ -453,12 +395,6 @@ Using this object reference, you can then remove ALL the menus and submenus unde

    Type MENU_GROUP

    - - - - - + - + @@ -517,12 +453,6 @@ Using this object reference, you can then remove ALL the menus and submenus unde

    Type MENU_GROUP_COMMAND

    MENU_GROUP.ClassName - -
    MENU_GROUP.MenuGroup @@ -501,13 +437,13 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    MENU_GROUP:Remove(MenuTime)MENU_GROUP:Remove(MenuTime, MenuTag)

    Removes the main menu and sub menus recursively of this MENU_GROUP.

    MENU_GROUP:RemoveSubMenus(MenuTime)MENU_GROUP:RemoveSubMenus(MenuTime, MenuTag)

    Removes the sub menus recursively of this MENU_GROUP.

    - - - - - + @@ -575,12 +505,6 @@ Using this object reference, you can then remove ALL the menus and submenus unde

    Type MENU_MISSION

    MENU_GROUP_COMMAND.ClassName - -
    MENU_GROUP_COMMAND.MenuGroup @@ -565,7 +495,7 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    MENU_GROUP_COMMAND:Remove(MenuTime)MENU_GROUP_COMMAND:Remove(MenuTime, MenuTag)

    Removes a menu structure for a group.

    - - - - + + + + + + + + @@ -330,6 +226,12 @@ A coding example is provided at the description of the SPAWN:GetSpawnIndexFromGroup(SpawnGroup) + + + + @@ -360,6 +262,30 @@ A coding example is provided at the description of the SPAWN:InitCleanUp(SpawnCleanUpInterval) + + + + + + + + + + + + + + + + @@ -494,6 +420,12 @@ and any spaces before and after the resulting name are removed.

    + + + + @@ -554,12 +486,24 @@ and any spaces before and after the resulting name are removed.

    + + + + + + + + @@ -746,6 +690,12 @@ and any spaces before and after the resulting name are removed.

    + + + + @@ -872,6 +822,16 @@ and any spaces before and after the resulting name are removed.

    + +
    MENU_MISSION.ClassName - -
    MENU_MISSION:New(MenuText, ParentMenu)

    MENU_MISSION constructor.

    @@ -603,12 +527,6 @@ Using this object reference, you can then remove ALL the menus and submenus unde

    Type MENU_MISSION_COMMAND

    - - - -
    MENU_MISSION_COMMAND.ClassName - -
    MENU_MISSION_COMMAND:New(MenuText, ParentMenu, CommandMenuFunction, CommandMenuArgument, ...)

    MENU_MISSION constructor.

    @@ -633,7 +551,11 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    +

    MENU_BASE class, extends Base#BASE

    +

    The MENU_BASE class defines the main MENU class where other MENU classes are derived from.

    + +

    This is an abstract class, so don't use it.

    @@ -647,7 +569,65 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    +

    MENU_CLIENT class, extends Menu#MENU_BASE

    +

    The MENU_CLIENT class manages the main menus for coalitions.

    + + +

    You can add menus with the MENU_CLIENT.New method, which constructs a MENU_CLIENT object and returns you the object reference. +Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENU_CLIENT.Remove.

    + + +

    Usage:

    +
     -- This demo creates a menu structure for the two clients of planes.
    + -- Each client will receive a different menu structure.
    + -- To test, join the planes, then look at the other radio menus (Option F10).
    + -- Then switch planes and check if the menu is still there.
    + -- And play with the Add and Remove menu options.
    + 
    + -- Note that in multi player, this will only work after the DCS clients bug is solved.
    +
    + local function ShowStatus( PlaneClient, StatusText, Coalition )
    +
    +   MESSAGE:New( Coalition, 15 ):ToRed()
    +   PlaneClient:Message( StatusText, 15 )
    + end
    +
    + local MenuStatus = {}
    +
    + local function RemoveStatusMenu( MenuClient )
    +   local MenuClientName = MenuClient:GetName()
    +   MenuStatus[MenuClientName]:Remove()
    + end
    +
    + --- @param Wrapper.Client#CLIENT MenuClient
    + local function AddStatusMenu( MenuClient )
    +   local MenuClientName = MenuClient:GetName()
    +   -- This would create a menu for the red coalition under the MenuCoalitionRed menu object.
    +   MenuStatus[MenuClientName] = MENU_CLIENT:New( MenuClient, "Status for Planes" )
    +   MENU_CLIENT_COMMAND:New( MenuClient, "Show Status", MenuStatus[MenuClientName], ShowStatus, MenuClient, "Status of planes is ok!", "Message to Red Coalition" )
    + end
    +
    + SCHEDULER:New( nil,
    +   function()
    +     local PlaneClient = CLIENT:FindByName( "Plane 1" )
    +     if PlaneClient and PlaneClient:IsAlive() then
    +       local MenuManage = MENU_CLIENT:New( PlaneClient, "Manage Menus" )
    +       MENU_CLIENT_COMMAND:New( PlaneClient, "Add Status Menu Plane 1", MenuManage, AddStatusMenu, PlaneClient )
    +       MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 1", MenuManage, RemoveStatusMenu, PlaneClient )
    +     end
    +   end, {}, 10, 10 )
    +
    + SCHEDULER:New( nil,
    +   function()
    +     local PlaneClient = CLIENT:FindByName( "Plane 2" )
    +     if PlaneClient and PlaneClient:IsAlive() then
    +       local MenuManage = MENU_CLIENT:New( PlaneClient, "Manage Menus" )
    +       MENU_CLIENT_COMMAND:New( PlaneClient, "Add Status Menu Plane 2", MenuManage, AddStatusMenu, PlaneClient )
    +       MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneClient )
    +     end
    +   end, {}, 10, 10 )
    +   
    @@ -661,6 +641,13 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    +

    MENUCLIENTCOMMAND class, extends Menu#MENUCOMMANDBASE

    + +

    The MENUCLIENTCOMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.

    + + +

    You can add menus with the MENUCLIENTCOMMAND.New method, which constructs a MENUCLIENTCOMMAND object and returns you the object reference. +Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENUCLIENTCOMMAND.Remove.

    @@ -675,7 +662,53 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    +

    MENU_COALITION class, extends Menu#MENU_BASE

    +

    The Menu#MENU_COALITION class manages the main menus for coalitions.

    + + +

    You can add menus with the MENU_COALITION.New method, which constructs a MENU_COALITION object and returns you the object reference. +Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENU_COALITION.Remove.

    + + + +

    Usage:

    +
     -- This demo creates a menu structure for the planes within the red coalition.
    + -- To test, join the planes, then look at the other radio menus (Option F10).
    + -- Then switch planes and check if the menu is still there.
    +
    + local Plane1 = CLIENT:FindByName( "Plane 1" )
    + local Plane2 = CLIENT:FindByName( "Plane 2" )
    +
    +
    + -- This would create a menu for the red coalition under the main DCS "Others" menu.
    + local MenuCoalitionRed = MENU_COALITION:New( coalition.side.RED, "Manage Menus" )
    +
    +
    + local function ShowStatus( StatusText, Coalition )
    +
    +   MESSAGE:New( Coalition, 15 ):ToRed()
    +   Plane1:Message( StatusText, 15 )
    +   Plane2:Message( StatusText, 15 )
    + end
    +
    + local MenuStatus -- Menu#MENU_COALITION
    + local MenuStatusShow -- Menu#MENU_COALITION_COMMAND
    +
    + local function RemoveStatusMenu()
    +   MenuStatus:Remove()
    + end
    +
    + local function AddStatusMenu()
    +   
    +   -- This would create a menu for the red coalition under the MenuCoalitionRed menu object.
    +   MenuStatus = MENU_COALITION:New( coalition.side.RED, "Status for Planes" )
    +   MenuStatusShow = MENU_COALITION_COMMAND:New( coalition.side.RED, "Show Status", MenuStatus, ShowStatus, "Status of planes is ok!", "Message to Red Coalition" )
    + end
    +
    + local MenuAdd = MENU_COALITION_COMMAND:New( coalition.side.RED, "Add Status Menu", MenuCoalitionRed, AddStatusMenu )
    + local MenuRemove = MENU_COALITION_COMMAND:New( coalition.side.RED, "Remove Status Menu", MenuCoalitionRed, RemoveStatusMenu )
    + 
    @@ -689,6 +722,13 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    +

    MENUCOALITIONCOMMAND class, extends Menu#MENUCOMMANDBASE

    + +

    The MENUCOALITIONCOMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.

    + + +

    You can add menus with the MENUCOALITIONCOMMAND.New method, which constructs a MENUCOALITIONCOMMAND object and returns you the object reference. +Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENUCOALITIONCOMMAND.Remove.

    @@ -703,6 +743,10 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    +

    MENUCOMMANDBASE class, extends Base#BASE

    +
    +

    The MENUCOMMANDBASE class defines the main MENU class where other MENU COMMAND_ +classes are derived from, in order to set commands.

    @@ -717,7 +761,65 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    +

    MENU_GROUP class, extends Menu#MENU_BASE

    +

    The MENU_GROUP class manages the main menus for coalitions.

    + + +

    You can add menus with the MENU_GROUP.New method, which constructs a MENU_GROUP object and returns you the object reference. +Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENU_GROUP.Remove.

    + + +

    Usage:

    +
     -- This demo creates a menu structure for the two groups of planes.
    + -- Each group will receive a different menu structure.
    + -- To test, join the planes, then look at the other radio menus (Option F10).
    + -- Then switch planes and check if the menu is still there.
    + -- And play with the Add and Remove menu options.
    + 
    + -- Note that in multi player, this will only work after the DCS groups bug is solved.
    +
    + local function ShowStatus( PlaneGroup, StatusText, Coalition )
    +
    +   MESSAGE:New( Coalition, 15 ):ToRed()
    +   PlaneGroup:Message( StatusText, 15 )
    + end
    +
    + local MenuStatus = {}
    +
    + local function RemoveStatusMenu( MenuGroup )
    +   local MenuGroupName = MenuGroup:GetName()
    +   MenuStatus[MenuGroupName]:Remove()
    + end
    +
    + --- @param Wrapper.Group#GROUP MenuGroup
    + local function AddStatusMenu( MenuGroup )
    +   local MenuGroupName = MenuGroup:GetName()
    +   -- This would create a menu for the red coalition under the MenuCoalitionRed menu object.
    +   MenuStatus[MenuGroupName] = MENU_GROUP:New( MenuGroup, "Status for Planes" )
    +   MENU_GROUP_COMMAND:New( MenuGroup, "Show Status", MenuStatus[MenuGroupName], ShowStatus, MenuGroup, "Status of planes is ok!", "Message to Red Coalition" )
    + end
    +
    + SCHEDULER:New( nil,
    +   function()
    +     local PlaneGroup = GROUP:FindByName( "Plane 1" )
    +     if PlaneGroup and PlaneGroup:IsAlive() then
    +       local MenuManage = MENU_GROUP:New( PlaneGroup, "Manage Menus" )
    +       MENU_GROUP_COMMAND:New( PlaneGroup, "Add Status Menu Plane 1", MenuManage, AddStatusMenu, PlaneGroup )
    +       MENU_GROUP_COMMAND:New( PlaneGroup, "Remove Status Menu Plane 1", MenuManage, RemoveStatusMenu, PlaneGroup )
    +     end
    +   end, {}, 10, 10 )
    +
    + SCHEDULER:New( nil,
    +   function()
    +     local PlaneGroup = GROUP:FindByName( "Plane 2" )
    +     if PlaneGroup and PlaneGroup:IsAlive() then
    +       local MenuManage = MENU_GROUP:New( PlaneGroup, "Manage Menus" )
    +       MENU_GROUP_COMMAND:New( PlaneGroup, "Add Status Menu Plane 2", MenuManage, AddStatusMenu, PlaneGroup )
    +       MENU_GROUP_COMMAND:New( PlaneGroup, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneGroup )
    +     end
    +   end, {}, 10, 10 )
    +
    @@ -731,6 +833,13 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    +

    MENUGROUPCOMMAND class, extends Menu#MENUCOMMANDBASE

    + +

    The Menu#MENUGROUPCOMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.

    + + +

    You can add menus with the MENUGROUPCOMMAND.New method, which constructs a MENUGROUPCOMMAND object and returns you the object reference. +Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENUGROUPCOMMAND.Remove.

    @@ -745,7 +854,13 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    +

    MENU_MISSION class, extends Menu#MENU_BASE

    +

    The MENU_MISSION class manages the main menus for a complete mission.

    + + +

    You can add menus with the MENU_MISSION.New method, which constructs a MENU_MISSION object and returns you the object reference. +Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENU_MISSION.Remove.

    @@ -759,6 +874,13 @@ Using this object reference, you can then remove ALL the menus and submenus unde
    +

    MENUMISSIONCOMMAND class, extends Menu#MENUCOMMANDBASE

    + +

    The MENUMISSIONCOMMAND class manages the command menus for a complete mission, which allow players to execute functions during mission execution.

    + + +

    You can add menus with the MENUMISSIONCOMMAND.New method, which constructs a MENUMISSIONCOMMAND object and returns you the object reference. +Using this object reference, you can then remove ALL the menus and submenus underlying automatically with MENUMISSIONCOMMAND.Remove.

    @@ -766,24 +888,7 @@ Using this object reference, you can then remove ALL the menus and submenus unde

    Type Menu

    Type MENU_BASE

    - -

    The MENU_BASE class

    - -

    Field(s)

    -
    -
    - - #string - -MENU_BASE.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -809,32 +914,6 @@ The text of the child menu.

    #MENU_BASE:

    - -
    -
    -
    - - -MENU_BASE.MenuParentPath - -
    -
    - - - -
    -
    -
    -
    - - -MENU_BASE.MenuPath - -
    -
    - - -
    @@ -854,9 +933,9 @@ The text of the child menu.

    - #string - -MENU_BASE.MenuText + + +MENU_BASE.MenuTag
    @@ -946,6 +1025,33 @@ If true, the parent menu is automatically removed when this menu is the last chi

    #MENU_BASE:

    +
    +
    +
    +
    + + +MENU_BASE:SetTag(MenuTag) + +
    +
    + +

    Sets a tag for later selection of menu refresh.

    + +

    Parameter

    +
      +
    • + +

      #string MenuTag : +A Tag or Key that will filter only menu items set with this key.

      + +
    • +
    +

    Return value

    + +

    #MENU_BASE:

    + +
    @@ -982,74 +1088,10 @@ If true, the parent menu is automatically removed when this menu is the last chi

    Creates a new radio command item for a coalition, which can invoke a function with parameters.

    -

    Usage:

    -
     -- This demo creates a menu structure for the two clients of planes.
    - -- Each client will receive a different menu structure.
    - -- To test, join the planes, then look at the other radio menus (Option F10).
    - -- Then switch planes and check if the menu is still there.
    - -- And play with the Add and Remove menu options.
    - 
    - -- Note that in multi player, this will only work after the DCS clients bug is solved.
    -
    - local function ShowStatus( PlaneClient, StatusText, Coalition )
    -
    -   MESSAGE:New( Coalition, 15 ):ToRed()
    -   PlaneClient:Message( StatusText, 15 )
    - end
    -
    - local MenuStatus = {}
    -
    - local function RemoveStatusMenu( MenuClient )
    -   local MenuClientName = MenuClient:GetName()
    -   MenuStatus[MenuClientName]:Remove()
    - end
    -
    - --- @param Wrapper.Client#CLIENT MenuClient
    - local function AddStatusMenu( MenuClient )
    -   local MenuClientName = MenuClient:GetName()
    -   -- This would create a menu for the red coalition under the MenuCoalitionRed menu object.
    -   MenuStatus[MenuClientName] = MENU_CLIENT:New( MenuClient, "Status for Planes" )
    -   MENU_CLIENT_COMMAND:New( MenuClient, "Show Status", MenuStatus[MenuClientName], ShowStatus, MenuClient, "Status of planes is ok!", "Message to Red Coalition" )
    - end
    -
    - SCHEDULER:New( nil,
    -   function()
    -     local PlaneClient = CLIENT:FindByName( "Plane 1" )
    -     if PlaneClient and PlaneClient:IsAlive() then
    -       local MenuManage = MENU_CLIENT:New( PlaneClient, "Manage Menus" )
    -       MENU_CLIENT_COMMAND:New( PlaneClient, "Add Status Menu Plane 1", MenuManage, AddStatusMenu, PlaneClient )
    -       MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 1", MenuManage, RemoveStatusMenu, PlaneClient )
    -     end
    -   end, {}, 10, 10 )
    -
    - SCHEDULER:New( nil,
    -   function()
    -     local PlaneClient = CLIENT:FindByName( "Plane 2" )
    -     if PlaneClient and PlaneClient:IsAlive() then
    -       local MenuManage = MENU_CLIENT:New( PlaneClient, "Manage Menus" )
    -       MENU_CLIENT_COMMAND:New( PlaneClient, "Add Status Menu Plane 2", MenuManage, AddStatusMenu, PlaneClient )
    -       MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneClient )
    -     end
    -   end, {}, 10, 10 )
    -

    Field(s)

    - #string - -MENU_CLIENT.ClassName - -
    -
    - - - -
    -
    -
    -
    - MENU_CLIENT:New(Client, MenuText, ParentMenu) @@ -1127,24 +1169,7 @@ self

    Type MENU_CLIENT_COMMAND

    - -

    The MENUCLIENTCOMMAND class

    - -

    Field(s)

    -
    -
    - - #string - -MENU_CLIENT_COMMAND.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -1218,61 +1243,7 @@ self

    Type MENU_COALITION

    - -

    The MENU_COALITION class

    - -

    Usage:

    -
     -- This demo creates a menu structure for the planes within the red coalition.
    - -- To test, join the planes, then look at the other radio menus (Option F10).
    - -- Then switch planes and check if the menu is still there.
    -
    - local Plane1 = CLIENT:FindByName( "Plane 1" )
    - local Plane2 = CLIENT:FindByName( "Plane 2" )
    -
    -
    - -- This would create a menu for the red coalition under the main DCS "Others" menu.
    - local MenuCoalitionRed = MENU_COALITION:New( coalition.side.RED, "Manage Menus" )
    -
    -
    - local function ShowStatus( StatusText, Coalition )
    -
    -   MESSAGE:New( Coalition, 15 ):ToRed()
    -   Plane1:Message( StatusText, 15 )
    -   Plane2:Message( StatusText, 15 )
    - end
    -
    - local MenuStatus -- Menu#MENU_COALITION
    - local MenuStatusShow -- Menu#MENU_COALITION_COMMAND
    -
    - local function RemoveStatusMenu()
    -   MenuStatus:Remove()
    - end
    -
    - local function AddStatusMenu()
    -   
    -   -- This would create a menu for the red coalition under the MenuCoalitionRed menu object.
    -   MenuStatus = MENU_COALITION:New( coalition.side.RED, "Status for Planes" )
    -   MenuStatusShow = MENU_COALITION_COMMAND:New( coalition.side.RED, "Show Status", MenuStatus, ShowStatus, "Status of planes is ok!", "Message to Red Coalition" )
    - end
    -
    - local MenuAdd = MENU_COALITION_COMMAND:New( coalition.side.RED, "Add Status Menu", MenuCoalitionRed, AddStatusMenu )
    - local MenuRemove = MENU_COALITION_COMMAND:New( coalition.side.RED, "Remove Status Menu", MenuCoalitionRed, RemoveStatusMenu )
    - -

    Field(s)

    -
    -
    - - #string - -MENU_COALITION.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -1356,24 +1327,7 @@ self

    Type MENU_COALITION_COMMAND

    - -

    The MENUCOALITIONCOMMAND class

    - -

    Field(s)

    -
    -
    - - #string - -MENU_COALITION_COMMAND.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -1453,50 +1407,7 @@ An argument for the function. There can only be ONE argument given. So multiple

    Type MENU_COMMAND_BASE

    - -

    The MENUCOMMANDBASE class

    - -

    Field(s)

    -
    -
    - - #string - -MENU_COMMAND_BASE.ClassName - -
    -
    - - - -
    -
    -
    -
    - - -MENU_COMMAND_BASE.CommandMenuArgument - -
    -
    - - - -
    -
    -
    -
    - - -MENU_COMMAND_BASE.CommandMenuFunction - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -1563,73 +1474,42 @@ ENUCOMMANDBASE

    - -

    Type MENU_GROUP

    - -

    The MENU_GROUP class

    - -

    Usage:

    -
     -- This demo creates a menu structure for the two groups of planes.
    - -- Each group will receive a different menu structure.
    - -- To test, join the planes, then look at the other radio menus (Option F10).
    - -- Then switch planes and check if the menu is still there.
    - -- And play with the Add and Remove menu options.
    - 
    - -- Note that in multi player, this will only work after the DCS groups bug is solved.
    -
    - local function ShowStatus( PlaneGroup, StatusText, Coalition )
    -
    -   MESSAGE:New( Coalition, 15 ):ToRed()
    -   PlaneGroup:Message( StatusText, 15 )
    - end
    -
    - local MenuStatus = {}
    -
    - local function RemoveStatusMenu( MenuGroup )
    -   local MenuGroupName = MenuGroup:GetName()
    -   MenuStatus[MenuGroupName]:Remove()
    - end
    -
    - --- @param Wrapper.Group#GROUP MenuGroup
    - local function AddStatusMenu( MenuGroup )
    -   local MenuGroupName = MenuGroup:GetName()
    -   -- This would create a menu for the red coalition under the MenuCoalitionRed menu object.
    -   MenuStatus[MenuGroupName] = MENU_GROUP:New( MenuGroup, "Status for Planes" )
    -   MENU_GROUP_COMMAND:New( MenuGroup, "Show Status", MenuStatus[MenuGroupName], ShowStatus, MenuGroup, "Status of planes is ok!", "Message to Red Coalition" )
    - end
    -
    - SCHEDULER:New( nil,
    -   function()
    -     local PlaneGroup = GROUP:FindByName( "Plane 1" )
    -     if PlaneGroup and PlaneGroup:IsAlive() then
    -       local MenuManage = MENU_GROUP:New( PlaneGroup, "Manage Menus" )
    -       MENU_GROUP_COMMAND:New( PlaneGroup, "Add Status Menu Plane 1", MenuManage, AddStatusMenu, PlaneGroup )
    -       MENU_GROUP_COMMAND:New( PlaneGroup, "Remove Status Menu Plane 1", MenuManage, RemoveStatusMenu, PlaneGroup )
    -     end
    -   end, {}, 10, 10 )
    -
    - SCHEDULER:New( nil,
    -   function()
    -     local PlaneGroup = GROUP:FindByName( "Plane 2" )
    -     if PlaneGroup and PlaneGroup:IsAlive() then
    -       local MenuManage = MENU_GROUP:New( PlaneGroup, "Manage Menus" )
    -       MENU_GROUP_COMMAND:New( PlaneGroup, "Add Status Menu Plane 2", MenuManage, AddStatusMenu, PlaneGroup )
    -       MENU_GROUP_COMMAND:New( PlaneGroup, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneGroup )
    -     end
    -   end, {}, 10, 10 )
    -
    - -

    Field(s)

    - #string - -MENU_GROUP.ClassName + +MENU_COMMAND_BASE.SetCommandMenuArguments(#, self, CommandMenuArguments)
    +

    This sets the new command arguments of a menu, +so that if a menu is regenerated, or if command arguments change, +that the arguments set for the menu are loosely coupled with the menu itself!!! +If the arguments change, no new menu needs to be generated if the menu text is the same!!!

    + +

    Parameters

    +
      +
    • + +

      # : +ENUCOMMANDBASE

      + +
    • +
    • + +

      self :

      + +
    • +
    • + +

      CommandMenuArguments :

      + +
    • +
    +

    Return value

    + +

    #MENUCOMMANDBASE:

    @@ -1637,6 +1517,49 @@ ENUCOMMANDBASE

    + +MENU_COMMAND_BASE.SetCommandMenuFunction(#, self, CommandMenuFunction) + +
    +
    + +

    This sets the new command function of a menu, +so that if a menu is regenerated, or if command function changes, +that the function set for the menu is loosely coupled with the menu itself!!! +If the function changes, no new menu needs to be generated if the menu text is the same!!!

    + +

    Parameters

    +
      +
    • + +

      # : +ENUCOMMANDBASE

      + +
    • +
    • + +

      self :

      + +
    • +
    • + +

      CommandMenuFunction :

      + +
    • +
    +

    Return value

    + +

    #MENUCOMMANDBASE:

    + + +
    +
    + +

    Type MENU_GROUP

    +

    Field(s)

    +
    +
    + MENU_GROUP.MenuGroup @@ -1764,19 +1687,25 @@ self

    -MENU_GROUP:Remove(MenuTime) +MENU_GROUP:Remove(MenuTime, MenuTag)

    Removes the main menu and sub menus recursively of this MENU_GROUP.

    -

    Parameter

    +

    Parameters

    • MenuTime :

      +
    • +
    • + +

      MenuTag : +A Tag or Key to filter the menus to be refreshed with the Tag set.

      +

    Return value

    @@ -1790,19 +1719,25 @@ self

    -MENU_GROUP:RemoveSubMenus(MenuTime) +MENU_GROUP:RemoveSubMenus(MenuTime, MenuTag)

    Removes the sub menus recursively of this MENU_GROUP.

    -

    Parameter

    +

    Parameters

    • MenuTime :

      +
    • +
    • + +

      MenuTag : +A Tag or Key to filter the menus to be refreshed with the Tag set.

      +

    Return value

    @@ -1814,24 +1749,7 @@ self

    Type MENU_GROUP_COMMAND

    - -

    The MENUGROUPCOMMAND class

    - -

    Field(s)

    -
    -
    - - #string - -MENU_GROUP_COMMAND.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -1970,25 +1888,34 @@ An argument for the function.

    + +

    self:E({Path=Path})

    +
    -MENU_GROUP_COMMAND:Remove(MenuTime) +MENU_GROUP_COMMAND:Remove(MenuTime, MenuTag)

    Removes a menu structure for a group.

    -

    Parameter

    +

    Parameters

    • MenuTime :

      +
    • +
    • + +

      MenuTag : +A Tag or Key to filter the menus to be refreshed with the Tag set.

      +

    Return value

    @@ -2000,24 +1927,7 @@ An argument for the function.

    Type MENU_MISSION

    - -

    The MENU_MISSION class

    - -

    Field(s)

    -
    -
    - - #string - -MENU_MISSION.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    @@ -2095,24 +2005,7 @@ The parent menu. This parameter can be ignored if you want the menu to be locate

    Type MENU_MISSION_COMMAND

    - -

    The MENUMISSIONCOMMAND class

    - -

    Field(s)

    -
    -
    - - #string - -MENU_MISSION_COMMAND.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    diff --git a/docs/Documentation/Message.html b/docs/Documentation/Message.html index db044781f..d4364cb07 100644 --- a/docs/Documentation/Message.html +++ b/docs/Documentation/Message.html @@ -17,9 +17,16 @@ index

    Module Message

    -

    Core - MESSAGE class takes are of the real-time notifications and messages to players during a simulation.

    +

    Core -- MESSAGE class takes are of the real-time notifications and messages to players during a simulation.

    @@ -81,71 +113,21 @@
    -

    1) Message#MESSAGE class, extends Base#BASE

    - -

    Message System to display Messages to Clients, Coalitions or All. -Messages are shown on the display panel for an amount of seconds, and will then disappear. -Messages can contain a category which is indicating the category of the message.

    - -

    1.1) MESSAGE construction

    - -

    Messages are created with Message#MESSAGE.New. Note that when the MESSAGE object is created, no message is sent yet. -To send messages, you need to use the To functions.

    - -

    1.2) Send messages to an audience

    - -

    Messages are sent:

    - - - -

    1.3) Send conditionally to an audience

    - -

    Messages can be sent conditionally to an audience (when a condition is true):

    - - - -

    Global(s)

    MESSAGE +

    MESSAGE class, extends Base#BASE

    +

    Message System to display Messages to Clients, Coalitions or All.

    Type MESSAGE

    - - - - - - - - - - - - + + + + + + + + @@ -923,6 +1194,72 @@ The default "time interval" is after 0.001 seconds.

    You can set the "yield interval", and the "time interval". (See above).

    + + +
    +
    + + #SET_CARGO + +SET_CARGO + +
    +
    + +

    (R2.1) SET_CARGO class, extends Set#SET_BASE

    + +

    Mission designers can use the Set#SET_CARGO class to build sets of cargos optionally belonging to certain:

    + +
      +
    • Coalitions
    • +
    • Types
    • +
    • Name or Prefix
    • +
    + +

    SET_CARGO constructor

    + +

    Create a new SET_CARGO object with the SET_CARGO.New method:

    + + + + +

    +

    Add or Remove CARGOs from SET_CARGO

    + +

    CARGOs can be added and removed using the Set#SET_CARGO.AddCargosByName and Set#SET_CARGO.RemoveCargosByName respectively. +These methods take a single CARGO name or an array of CARGO names to be added or removed from SET_CARGO.

    + +

    SET_CARGO filter criteria

    + +

    You can set filter criteria to automatically maintain the SET_CARGO contents. +Filter criteria are defined by:

    + + + +

    Once the filter criteria have been set for the SET_CARGO, you can start filtering using:

    + + + +

    SET_CARGO iterators

    + +

    Once the filters have been defined and the SETCARGO has been built, you can iterate the SETCARGO with the available iterator methods. +The iterator methods will walk the SETCARGO set, and call for each cargo within the set a function that you provide. +The following iterator methods are currently available within the SETCARGO:

    + + + +
    @@ -1011,7 +1348,7 @@ The following iterator methods are currently available within the SETCLIENT
    -

    2) SET_GROUP class, extends Set#SET_BASE

    +

    SET_GROUP class, extends Set#SET_BASE

    Mission designers can use the Set#SET_GROUP class to build sets of groups belonging to certain:

    @@ -1024,7 +1361,7 @@ The following iterator methods are currently available within the SETCLIENT

    -

    2.1) SET_GROUP constructor

    +

    1. SET_GROUP constructor

    Create a new SET_GROUP object with the SET_GROUP.New method:

    @@ -1032,12 +1369,12 @@ The following iterator methods are currently available within the SETCLIENT
  • SET_GROUP.New: Creates a new SET_GROUP object.
  • -

    2.2) Add or Remove GROUP(s) from SET_GROUP

    +

    2. Add or Remove GROUP(s) from SET_GROUP

    GROUPS can be added and removed using the Set#SET_GROUP.AddGroupsByName and Set#SET_GROUP.RemoveGroupsByName respectively. These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP.

    -

    2.3) SET_GROUP filter criteria

    +

    3. SET_GROUP filter criteria

    You can set filter criteria to define the set of groups within the SET_GROUP. Filter criteria are defined by:

    @@ -1049,6 +1386,17 @@ Filter criteria are defined by:

  • SET_GROUP.FilterPrefixes: Builds the SET_GROUP with the groups starting with the same prefix string(s).
  • +

    For the Category Filter, extra methods have been added:

    + + + +

    Once the filter criteria have been set for the SET_GROUP, you can start filtering using:

    -

    2.4) SET_GROUP iterators

    +

    4. SET_GROUP iterators

    Once the filters have been defined and the SETGROUP has been built, you can iterate the SETGROUP with the available iterator methods. The iterator methods will walk the SETGROUP set, and call for each element within the set a function that you provide. @@ -1640,6 +1988,42 @@ Count

    +
    +
    +
    +
    + + +SET_BASE:FilterCrashes() + +
    +
    + +

    Starts the filtering of the Crash events for the collection.

    + +

    Return value

    + +

    #SET_BASE: +self

    + +
    +
    +
    +
    + + +SET_BASE:FilterDeads() + +
    +
    + +

    Starts the filtering of the Dead events for the collection.

    + +

    Return value

    + +

    #SET_BASE: +self

    +
    @@ -1833,6 +2217,24 @@ self

    Core.Base#BASE:

    + +
    +
    +
    + + +SET_BASE:GetObjectNames() + +
    +
    + +

    Gets a string with all the object names.

    + +

    Return value

    + +

    #string: +A string with the names of the objects.

    +
    @@ -2189,6 +2591,454 @@ self

    Core.Base#BASE: The Object found.

    + +
    + +

    Type SET_CARGO

    +

    Field(s)

    +
    +
    + + +SET_CARGO:AddCargosByName(AddCargoNames) + +
    +
    + +

    (R2.1) Add CARGOs to SET_CARGO.

    + +

    Parameter

    +
      +
    • + +

      #string AddCargoNames : +A single name or an array of CARGO names.

      + +
    • +
    +

    Return value

    + + +

    self

    + +
    +
    +
    +
    + + +SET_CARGO:AddInDatabase(Event) + +
    +
    + +

    (R2.1) Handles the Database to check on an event (birth) that the Object was added in the Database.

    + + +

    This is required, because sometimes the DATABASE birth event gets called later than the SETBASE birth event!

    + +

    Parameter

    + +

    Return values

    +
      +
    1. + +

      #string: +The name of the CARGO

      + +
    2. +
    3. + +

      #table: +The CARGO

      + +
    4. +
    +
    +
    +
    +
    + + +SET_CARGO:FilterCoalitions(Coalitions) + +
    +
    + +

    (R2.1) Builds a set of cargos of coalitions.

    + + +

    Possible current coalitions are red, blue and neutral.

    + +

    Parameter

    +
      +
    • + +

      #string Coalitions : +Can take the following values: "red", "blue", "neutral".

      + +
    • +
    +

    Return value

    + +

    #SET_CARGO: +self

    + +
    +
    +
    +
    + + +SET_CARGO:FilterCountries(Countries) + +
    +
    + +

    (R2.1) Builds a set of cargos of defined countries.

    + + +

    Possible current countries are those known within DCS world.

    + +

    Parameter

    +
      +
    • + +

      #string Countries : +Can take those country strings known within DCS world.

      + +
    • +
    +

    Return value

    + +

    #SET_CARGO: +self

    + +
    +
    +
    +
    + + +SET_CARGO:FilterPrefixes(Prefixes) + +
    +
    + +

    (R2.1) Builds a set of cargos of defined cargo prefixes.

    + + +

    All the cargos starting with the given prefixes will be included within the set.

    + +

    Parameter

    +
      +
    • + +

      #string Prefixes : +The prefix of which the cargo name starts with.

      + +
    • +
    +

    Return value

    + +

    #SET_CARGO: +self

    + +
    +
    +
    +
    + + +SET_CARGO:FilterStart() + +
    +
    + +

    (R2.1) Starts the filtering.

    + +

    Return value

    + +

    #SET_CARGO: +self

    + +
    +
    +
    +
    + + +SET_CARGO:FilterTypes(Types) + +
    +
    + +

    (R2.1) Builds a set of cargos of defined cargo types.

    + + +

    Possible current types are those types known within DCS world.

    + +

    Parameter

    +
      +
    • + +

      #string Types : +Can take those type strings known within DCS world.

      + +
    • +
    +

    Return value

    + +

    #SET_CARGO: +self

    + +
    +
    +
    +
    + + +SET_CARGO:FindCargo(CargoName) + +
    +
    + +

    (R2.1) Finds a Cargo based on the Cargo Name.

    + +

    Parameter

    +
      +
    • + +

      #string CargoName :

      + +
    • +
    +

    Return value

    + +

    Wrapper.Cargo#CARGO: +The found Cargo.

    + +
    +
    +
    +
    + + +SET_CARGO:FindInDatabase(Event) + +
    +
    + +

    (R2.1) Handles the Database to check on any event that Object exists in the Database.

    + + +

    This is required, because sometimes the DATABASE event gets called later than the SETBASE event or vise versa!

    + +

    Parameter

    + +

    Return values

    +
      +
    1. + +

      #string: +The name of the CARGO

      + +
    2. +
    3. + +

      #table: +The CARGO

      + +
    4. +
    +
    +
    +
    +
    + + +SET_CARGO:FindNearestCargoFromPointVec2(PointVec2) + +
    +
    + +

    (R2.1) Iterate the SET_CARGO while identifying the nearest Cargo#CARGO from a Point#POINT_VEC2.

    + +

    Parameter

    + +

    Return value

    + +

    Wrapper.Cargo#CARGO: +The closest Cargo#CARGO.

    + +
    +
    +
    +
    + + +SET_CARGO:ForEachCargo(IteratorFunction, ...) + +
    +
    + +

    (R2.1) Iterate the SET_CARGO and call an interator function for each CARGO, providing the CARGO and optional parameters.

    + +

    Parameters

    +
      +
    • + +

      #function IteratorFunction : +The function that will be called when there is an alive CARGO in the SET_CARGO. The function needs to accept a CARGO parameter.

      + +
    • +
    • + +

      ... :

      + +
    • +
    +

    Return value

    + +

    #SET_CARGO: +self

    + +
    +
    +
    +
    + + +SET_CARGO:IsIncludeObject(MCargo) + +
    +
    + +

    (R2.1)

    + +

    Parameter

    + +

    Return value

    + +

    #SET_CARGO: +self

    + +
    +
    +
    +
    + + +SET_CARGO:New() + +
    +
    + +

    (R2.1) Creates a new SET_CARGO object, building a set of cargos belonging to a coalitions and categories.

    + +

    Return value

    + +

    #SET_CARGO:

    + + +

    Usage:

    +
    -- Define a new SET_CARGO Object. The DatabaseSet will contain a reference to all Cargos.
    +DatabaseSet = SET_CARGO:New()
    + +
    +
    +
    +
    + + +SET_CARGO:OnEventDeleteCargo(EventData) + +
    +
    + +

    (R2.1) Handles the OnDead or OnCrash event for alive units set.

    + +

    Parameter

    + +
    +
    +
    +
    + + +SET_CARGO:OnEventNewCargo(EventData) + +
    +
    + +

    (R2.1) Handles the OnEventNewCargo event for the Set.

    + +

    Parameter

    + +
    +
    +
    +
    + + +SET_CARGO:RemoveCargosByName(RemoveCargoNames) + +
    +
    + +

    (R2.1) Remove CARGOs from SET_CARGO.

    + +

    Parameter

    + +

    Return value

    + + +

    self

    +
    @@ -2748,6 +3598,261 @@ The GROUP

    + +SET_GROUP:AllCompletelyInZone(ZoneObject, Zone) + +
    +
    + +

    Iterate the SET_GROUP and return true if all the Wrapper.Group#GROUP are completely in the Core.Zone#ZONE

    + +

    Parameters

    +
      +
    • + +

      Core.Zone#ZONE ZoneObject : +The Zone to be tested for.

      + +
    • +
    • + +

      Zone :

      + +
    • +
    +

    Return value

    + +

    #boolean: +true if all the Wrapper.Group#GROUP are completly in the Core.Zone#ZONE, false otherwise

    + +

    Usage:

    +
    local MyZone = ZONE:New("Zone1")
    +local MySetGroup = SET_GROUP:New()
    +MySetGroup:AddGroupsByName({"Group1", "Group2"})
    +
    +if MySetGroup:AllCompletelyInZone(MyZone) then
    +  MESSAGE:New("All the SET's GROUP are in zone !", 10):ToAll()
    +else
    +  MESSAGE:New("Some or all SET's GROUP are outside zone !", 10):ToAll()
    +end
    + +
    +
    +
    +
    + + +SET_GROUP:AnyCompletelyInZone(ZoneObject, Zone) + +
    +
    + +

    Iterate the SET_GROUP and return true if at least one of the Wrapper.Group#GROUP is completely inside the Core.Zone#ZONE

    + +

    Parameters

    +
      +
    • + +

      Core.Zone#ZONE ZoneObject : +The Zone to be tested for.

      + +
    • +
    • + +

      Zone :

      + +
    • +
    +

    Return value

    + +

    #boolean: +true if at least one of the Wrapper.Group#GROUP is completly inside the Core.Zone#ZONE, false otherwise.

    + +

    Usage:

    +
    local MyZone = ZONE:New("Zone1")
    +local MySetGroup = SET_GROUP:New()
    +MySetGroup:AddGroupsByName({"Group1", "Group2"})
    +
    +if MySetGroup:AnyCompletelyInZone(MyZone) then
    +  MESSAGE:New("At least one GROUP is completely in zone !", 10):ToAll()
    +else
    +  MESSAGE:New("No GROUP is completely in zone !", 10):ToAll()
    +end
    + +
    +
    +
    +
    + + +SET_GROUP:AnyInZone(ZoneObject, Zone) + +
    +
    + +

    Iterate the SET_GROUP and return true if at least one #UNIT of one GROUP of the SET_GROUP is in ZONE

    + +

    Parameters

    +
      +
    • + +

      Core.Zone#ZONE ZoneObject : +The Zone to be tested for.

      + +
    • +
    • + +

      Zone :

      + +
    • +
    +

    Return value

    + +

    #boolean: +true if at least one of the Wrapper.Group#GROUP is partly or completly inside the Core.Zone#ZONE, false otherwise.

    + +

    Usage:

    +
    local MyZone = ZONE:New("Zone1")
    +local MySetGroup = SET_GROUP:New()
    +MySetGroup:AddGroupsByName({"Group1", "Group2"})
    +
    +if MySetGroup:AnyPartlyInZone(MyZone) then
    +  MESSAGE:New("At least one GROUP has at least one UNIT in zone !", 10):ToAll()
    +else
    +  MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll()
    +end
    + +
    +
    +
    +
    + + +SET_GROUP:AnyPartlyInZone(ZoneObject, Zone) + +
    +
    + +

    Iterate the SET_GROUP and return true if at least one GROUP of the SET_GROUP is partly in ZONE.

    + + +

    Will return false if a GROUP is fully in the ZONE

    + +

    Parameters

    +
      +
    • + +

      Core.Zone#ZONE ZoneObject : +The Zone to be tested for.

      + +
    • +
    • + +

      Zone :

      + +
    • +
    +

    Return value

    + +

    #boolean: +true if at least one of the Wrapper.Group#GROUP is partly or completly inside the Core.Zone#ZONE, false otherwise.

    + +

    Usage:

    +
    local MyZone = ZONE:New("Zone1")
    +local MySetGroup = SET_GROUP:New()
    +MySetGroup:AddGroupsByName({"Group1", "Group2"})
    +
    +if MySetGroup:AnyPartlyInZone(MyZone) then
    +  MESSAGE:New("At least one GROUP is partially in the zone, but none are fully in it !", 10):ToAll()
    +else
    +  MESSAGE:New("No GROUP are in zone, or one (or more) GROUP is completely in it !", 10):ToAll()
    +end
    + +
    +
    +
    +
    + + +SET_GROUP:CountInZone(ZoneObject, Zone) + +
    +
    + +

    Iterate the SETGROUP and count how many GROUPs are completely in the Zone +That could easily be done with SETGROUP:ForEachGroupCompletelyInZone(), but this function +provides an easy to use shortcut...

    + +

    Parameters

    +
      +
    • + +

      Core.Zone#ZONE ZoneObject : +The Zone to be tested for.

      + +
    • +
    • + +

      Zone :

      + +
    • +
    +

    Return value

    + +

    #number: +the number of GROUPs completely in the Zone

    + +

    Usage:

    +
    local MyZone = ZONE:New("Zone1")
    +local MySetGroup = SET_GROUP:New()
    +MySetGroup:AddGroupsByName({"Group1", "Group2"})
    +
    +MESSAGE:New("There are " .. MySetGroup:CountInZone(MyZone) .. " GROUPs in the Zone !", 10):ToAll()
    + +
    +
    +
    +
    + + +SET_GROUP:CountUnitInZone(ZoneObject, Zone) + +
    +
    + +

    Iterate the SET_GROUP and count how many UNITs are completely in the Zone

    + +

    Parameters

    +
      +
    • + +

      Core.Zone#ZONE ZoneObject : +The Zone to be tested for.

      + +
    • +
    • + +

      Zone :

      + +
    • +
    +

    Return value

    + +

    #number: +the number of GROUPs completely in the Zone

    + +

    Usage:

    +
    local MyZone = ZONE:New("Zone1")
    +local MySetGroup = SET_GROUP:New()
    +MySetGroup:AddGroupsByName({"Group1", "Group2"})
    +
    +MESSAGE:New("There are " .. MySetGroup:CountUnitInZone(MyZone) .. " UNITs in the Zone !", 10):ToAll()
    + +
    +
    +
    +
    + SET_GROUP:FilterCategories(Categories) @@ -2778,6 +3883,96 @@ self

    + +SET_GROUP:FilterCategoryAirplane() + +
    +
    + +

    Builds a set of groups out of airplane category.

    + +

    Return value

    + +

    #SET_GROUP: +self

    + +
    +
    +
    +
    + + +SET_GROUP:FilterCategoryGround() + +
    +
    + +

    Builds a set of groups out of ground category.

    + +

    Return value

    + +

    #SET_GROUP: +self

    + +
    +
    +
    +
    + + +SET_GROUP:FilterCategoryHelicopter() + +
    +
    + +

    Builds a set of groups out of helicopter category.

    + +

    Return value

    + +

    #SET_GROUP: +self

    + +
    +
    +
    +
    + + +SET_GROUP:FilterCategoryShip() + +
    +
    + +

    Builds a set of groups out of ship category.

    + +

    Return value

    + +

    #SET_GROUP: +self

    + +
    +
    +
    +
    + + +SET_GROUP:FilterCategoryStructure() + +
    +
    + +

    Builds a set of groups out of structure category.

    + +

    Return value

    + +

    #SET_GROUP: +self

    + +
    +
    +
    +
    + SET_GROUP:FilterCoalitions(Coalitions) @@ -2951,6 +4146,33 @@ The GROUP

    + +SET_GROUP:FindNearestGroupFromPointVec2(PointVec2) + +
    +
    + +

    Iterate the SET_GROUP while identifying the nearest object from a Point#POINT_VEC2.

    + +

    Parameter

    + +

    Return value

    + +

    Wrapper.Group#GROUP: +The closest group.

    + +
    +
    +
    +
    + SET_GROUP:ForEachGroup(IteratorFunction, ...) @@ -3145,6 +4367,51 @@ DBObject = SET_GROUP:New()
    + +SET_GROUP:NoneInZone(ZoneObject, Zone) + +
    +
    + +

    Iterate the SET_GROUP and return true if no GROUP of the SET_GROUP is in ZONE +This could also be achieved with not SET_GROUP:AnyPartlyInZone(Zone), but it's easier for the +mission designer to add a dedicated method

    + +

    Parameters

    +
      +
    • + +

      Core.Zone#ZONE ZoneObject : +The Zone to be tested for.

      + +
    • +
    • + +

      Zone :

      + +
    • +
    +

    Return value

    + +

    #boolean: +true if no Wrapper.Group#GROUP is inside the Core.Zone#ZONE in any way, false otherwise.

    + +

    Usage:

    +
    local MyZone = ZONE:New("Zone1")
    +local MySetGroup = SET_GROUP:New()
    +MySetGroup:AddGroupsByName({"Group1", "Group2"})
    +
    +if MySetGroup:NoneInZone(MyZone) then
    +  MESSAGE:New("No GROUP is completely in zone !", 10):ToAll()
    +else
    +  MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll()
    +end
    + +
    +
    +
    +
    + SET_GROUP:RemoveGroupsByName(RemoveGroupNames) @@ -3167,6 +4434,30 @@ A single name or an array of GROUP names.

    self

    + +
    +
    +
    + + +SET_GROUP:_EventOnDeadOrCrash(Event) + +
    +
    + +

    Handles the OnDead or OnCrash event for alive groups set.

    + + +

    Note: The GROUP object in the SET_GROUP collection will only be removed if the last unit is destroyed of the GROUP.

    + +

    Parameter

    +
    @@ -3276,6 +4567,11 @@ self

    Calculate the maxium A2G threat level of the SET_UNIT.

    +

    Return value

    + +

    #number: +The maximum threatlevel

    +
    @@ -3670,6 +4966,79 @@ self

    + +SET_UNIT:ForEachUnitPerThreatLevel(FromThreatLevel, ToThreatLevel, IteratorFunction, ...) + +
    +
    + +

    Iterate the SET_UNIT sorted *per Threat Level and call an interator function for each alive UNIT, providing the UNIT and optional parameters.

    + + +

    Parameters

    +
      +
    • + +

      #number FromThreatLevel : +The TreatLevel to start the evaluation From (this must be a value between 0 and 10).

      + +
    • +
    • + +

      #number ToThreatLevel : +The TreatLevel to stop the evaluation To (this must be a value between 0 and 10).

      + +
    • +
    • + +

      #function IteratorFunction : +The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter.

      + +
    • +
    • + +

      ... :

      + +
    • +
    +

    Return value

    + +

    #SET_UNIT: +self

    + +

    Usage:

    +
    
    +    UnitSet:ForEachUnitPerThreatLevel( 10, 0,
    +      -- @param Wrapper.Unit#UNIT UnitObject The UNIT object in the UnitSet, that will be passed to the local function for evaluation.
    +      function( UnitObject )
    +        .. logic ..
    +      end
    +    )
    +
    + +
    +
    +
    +
    + + +SET_UNIT:GetFirst() + +
    +
    + +

    Get the first unit from the set.

    + +

    Return value

    + +

    Wrapper.Unit#UNIT: +The UNIT object.

    + +
    +
    +
    +
    + SET_UNIT:GetTypeNames(Delimiter) diff --git a/docs/Documentation/Settings.html b/docs/Documentation/Settings.html new file mode 100644 index 000000000..992d1808d --- /dev/null +++ b/docs/Documentation/Settings.html @@ -0,0 +1,1490 @@ + + + + + + +
    MESSAGE.ClassName - -
    MESSAGE.MessageCategory - -
    MESSAGE.MessageID - -
    MESSAGE:New(MessageText, MessageDuration, MessageCategory)

    Creates a new MESSAGE object.

    @@ -212,6 +194,47 @@ To send messages, you need to use the To functions.

    +

    MESSAGE class, extends Base#BASE

    + +

    Message System to display Messages to Clients, Coalitions or All.

    + + +

    Messages are shown on the display panel for an amount of seconds, and will then disappear. +Messages can contain a category which is indicating the category of the message.

    + +

    MESSAGE construction

    + +

    Messages are created with Message#MESSAGE.New. Note that when the MESSAGE object is created, no message is sent yet. +To send messages, you need to use the To functions.

    + +

    Send messages to an audience

    + +

    Messages are sent:

    + + + +

    Send conditionally to an audience

    + +

    Messages can be sent conditionally to an audience (when a condition is true):

    + + + +
    + +

    Author: Sven Van de Velde (FlightControl)

    +

    Contributions:

    + +
    @@ -226,48 +249,6 @@ To send messages, you need to use the To functions.

    - #string - -MESSAGE.ClassName - -
    -
    - - - -
    -
    -
    -
    - - #number - -MESSAGE.MessageCategory - -
    -
    - - - -
    -
    -
    -
    - - #number - -MESSAGE.MessageID - -
    -
    - - - -
    -
    -
    -
    - MESSAGE:New(MessageText, MessageDuration, MessageCategory) diff --git a/docs/Documentation/MissileTrainer.html b/docs/Documentation/MissileTrainer.html index 028f28dff..17e2909ef 100644 --- a/docs/Documentation/MissileTrainer.html +++ b/docs/Documentation/MissileTrainer.html @@ -17,9 +17,16 @@ index

    Module MissileTrainer

    -

    This module contains the MISSILETRAINER class.

    +

    Functional -- MISSILETRAINER helps you to train missile avoidance.

    diff --git a/docs/Documentation/Mission.html b/docs/Documentation/Mission.html index bb21a844f..ce693c0a0 100644 --- a/docs/Documentation/Mission.html +++ b/docs/Documentation/Mission.html @@ -17,9 +17,16 @@ index

    Module Mission

    -

    A MISSION is the main owner of a Mission orchestration within MOOSE .

    +

    Tasking -- A MISSION is the main owner of a Mission orchestration within MOOSE.

    -

    The Mission framework orchestrates CLIENTs, TASKs, STAGEs etc. -A CLIENT needs to be registered within the MISSION through the function AddClient. A TASK needs to be registered within the MISSION through the function AddTask.

    + +
    + +

    Author: Sven Van de Velde (FlightControl)

    + +

    Contributions:

    + +
    +

    Global(s)

    @@ -94,6 +133,12 @@ A CLIENT needs to be registered within the MISSION:AbortUnit(PlayerUnit) + + + + @@ -106,12 +151,24 @@ A CLIENT needs to be registered within the MISSION:AddTask(Task) + + + + + + + + @@ -147,7 +204,7 @@ A CLIENT needs to be registered within the MISSION:GetMenu(TaskGroup) @@ -160,6 +217,18 @@ A CLIENT needs to be registered within the MISSION:GetNextTaskID(Task) + + + + + + + + @@ -172,12 +241,24 @@ A CLIENT needs to be registered within the MISSION.GetTask(TaskName, self) + + + + + + + + @@ -187,45 +268,93 @@ A CLIENT needs to be registered within the MISSION:IsCompleted() + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -262,6 +391,12 @@ A CLIENT needs to be registered within the MISSION:OnAfterFail(From, Event, To) + + + + @@ -286,6 +421,12 @@ A CLIENT needs to be registered within the MISSION:OnBeforeFail(From, Event, To) + + + + @@ -301,51 +442,51 @@ A CLIENT needs to be registered within the MISSION:OnEnterCompleted(From, Event, To) + - + - + - + - + - + - + - + @@ -367,19 +508,43 @@ A CLIENT needs to be registered within the MISSION:ReportDetails() + + + + + - + - + + + + + + + + + + + + + @@ -388,6 +553,12 @@ A CLIENT needs to be registered within the MISSION.Scoring + + + + @@ -424,6 +595,12 @@ A CLIENT needs to be registered within the MISSION:__Fail(Delay) + + + + @@ -439,13 +616,7 @@ A CLIENT needs to be registered within the MISSION:onbeforeComplete(From, Event, To) - - - - + @@ -501,9 +672,30 @@ The CLIENT or UNIT of the Player joining the Mission.

    Return value

    -

    #boolean: -true if Unit is part of a Task in the Mission.

    +

    #MISSION:

    + + + +
    +
    + + +MISSION:AddPlayerName(PlayerName) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      PlayerName :

      + +
    • +
    @@ -561,6 +753,20 @@ is the Task object.

    Tasking.Task#TASK: The task added.

    + +
    +
    +
    + + + +MISSION.AssignedGroups + +
    +
    + + +
    @@ -575,6 +781,32 @@ The task added.

    + +
    +
    +
    + + +MISSION:ClearGroupAssignment(MissionGroup) + +
    +
    + +

    Clear the Group assignment from the Mission.

    + +

    Parameter

    + +

    Return value

    + +

    #MISSION:

    + +
    @@ -617,8 +849,8 @@ The CLIENT or UNIT of the Player crashing.

    Return value

    -

    #boolean: -true if Unit is part of a Task in the Mission.

    +

    #MISSION:

    +
    @@ -680,13 +912,13 @@ true if Unit is part of a Task in the Mission.

    -

    Gets the mission menu for the coalition.

    +

    Gets the mission menu for the TaskGroup.

    Parameter

    @@ -745,6 +977,45 @@ The task added.

    + +MISSION:GetPlayerNames() + +
    +
    + + + +
    +
    +
    +
    + + +MISSION:GetRootMenu(TaskGroup) + +
    +
    + +

    Gets the root mission menu for the TaskGroup.

    + +

    Parameter

    +
      +
    • + +

      TaskGroup :

      + +
    • +
    +

    Return value

    + +

    Core.Menu#MENU_COALITION: +self

    + +
    +
    +
    +
    + MISSION:GetScoring() @@ -808,6 +1079,24 @@ Returns nil if no task was found.

    + +MISSION:GetTaskTypes() + +
    +
    + + + +

    Return value

    + +

    #number:

    + + +
    +
    +
    +
    + MISSION:GetTasks() @@ -829,6 +1118,24 @@ Returns nil if no task was found.

    Tasks = Mission:GetTasks() env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" ) +
    + +
    +
    + + +MISSION:GetTasksRemaining() + +
    +
    + + + +

    Return value

    + +

    #number:

    + +
    @@ -871,13 +1178,13 @@ true if the Mission has a Group.

    - -MISSION:IsCompleted() + +MISSION:IsCOMPLETED()
    -

    Is the Mission Completed.

    +

    Is the Mission COMPLETED.

    Return value

    @@ -889,13 +1196,13 @@ true if the Mission has a Group.

    - -MISSION:IsFailed() + +MISSION:IsENGAGED()
    -

    Is the Mission Failed.

    +

    Is the Mission ENGAGED.

    Return value

    @@ -907,13 +1214,13 @@ true if the Mission has a Group.

    - -MISSION:IsHold() + +MISSION:IsFAILED()
    -

    Is the Mission Hold.

    +

    Is the Mission FAILED.

    Return value

    @@ -925,13 +1232,39 @@ true if the Mission has a Group.

    - -MISSION:IsIdle() + +MISSION:IsGroupAssigned(MissionGroup)
    -

    Is the Mission Idle.

    +

    Returns if the Mission is assigned to the Group.

    + +

    Parameter

    + +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +MISSION:IsHOLD() + +
    +
    + +

    Is the Mission HOLD.

    Return value

    @@ -943,13 +1276,13 @@ true if the Mission has a Group.

    - -MISSION:IsOngoing() + +MISSION:IsIDLE()
    -

    Is the Mission Ongoing.

    +

    Is the Mission IDLE.

    Return value

    @@ -994,6 +1327,118 @@ The GROUP of the player joining the Mission.

    #boolean: true if Unit is part of a Task in the Mission.

    +
    +
    +
    +
    + + +MISSION:MenuReportBriefing(ReportGroup) + +
    +
    + +

    Reports the briefing.

    + +

    Parameter

    +
      +
    • + +

      Wrapper.Group#GROUP ReportGroup : +The group to which the report needs to be sent.

      + +
    • +
    +
    +
    +
    +
    + + +MISSION:MenuReportPlayersPerTask(ReportGroup) + +
    +
    + + + +

    Parameter

    + +
    +
    +
    +
    + + +MISSION:MenuReportPlayersProgress(ReportGroup) + +
    +
    + + + +

    Parameter

    + +
    +
    +
    +
    + + +MISSION:MenuReportTasksPerStatus(TaskStatus, ReportGroup) + +
    +
    + + + +

    Parameters

    + +
    +
    +
    +
    + + +MISSION:MenuReportTasksSummary(ReportGroup) + +
    +
    + +

    Report the task summary.

    + +

    Parameter

    +
    @@ -1008,6 +1453,32 @@ true if Unit is part of a Task in the Mission.

    +
    +
    +
    +
    + + +MISSION:MissionGoals() + +
    +
    + +

    MissionGoals Trigger for MISSION

    + +
    +
    +
    +
    + + +MISSION.MissionGroupMenu + +
    +
    + + +
    @@ -1176,6 +1647,37 @@ The To State string.

    + +MISSION:OnAfterMissionGoals(From, Event, To) + +
    +
    + +

    MissionGoals Handler OnAfter for MISSION

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + MISSION:OnAfterStart(From, Event, To) @@ -1317,6 +1819,42 @@ The To State string.

    #boolean: Return false to cancel Transition.

    +
    +
    +
    +
    + + +MISSION:OnBeforeMissionGoals(From, Event, To) + +
    +
    + +

    MissionGoals Handler OnBefore for MISSION

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + +
    @@ -1400,13 +1938,13 @@ Return false to cancel Transition.

    - -MISSION:OnEnterCompleted(From, Event, To) + +MISSION:OnEnterCOMPLETED(From, Event, To)
    -

    OnEnter Transition Handler for State Completed.

    +

    OnEnter Transition Handler for State COMPLETED.

    Parameters

    Aborts a PlayerUnit from the Mission.

    +
    MISSION:AddPlayerName(PlayerName) +

    Register a Task to be completed within the Mission.

    +
    MISSION.AssignedGroups +
    MISSION.ClassName +
    MISSION:ClearGroupAssignment(MissionGroup) +

    Clear the Group assignment from the Mission.

    -

    Gets the mission menu for the coalition.

    +

    Gets the mission menu for the TaskGroup.

    Return the next Task ID to be completed within the Mission.

    +
    MISSION:GetPlayerNames() + +
    MISSION:GetRootMenu(TaskGroup) +

    Gets the root mission menu for the TaskGroup.

    Get the TASK identified by the TaskNumber from the Mission.

    +
    MISSION:GetTaskTypes() +
    MISSION:GetTasks()

    Get all the TASKs from the Mission.

    +
    MISSION:GetTasksRemaining() +
    MISSION:IsCOMPLETED() -

    Is the Mission Completed.

    +

    Is the Mission COMPLETED.

    MISSION:IsFailed()MISSION:IsENGAGED() -

    Is the Mission Failed.

    +

    Is the Mission ENGAGED.

    MISSION:IsHold()MISSION:IsFAILED() -

    Is the Mission Hold.

    +

    Is the Mission FAILED.

    MISSION:IsIdle()MISSION:IsGroupAssigned(MissionGroup) -

    Is the Mission Idle.

    +

    Returns if the Mission is assigned to the Group.

    MISSION:IsOngoing()MISSION:IsHOLD() -

    Is the Mission Ongoing.

    +

    Is the Mission HOLD.

    +
    MISSION:IsIDLE() +

    Is the Mission IDLE.

    MISSION:JoinUnit(PlayerUnit, PlayerGroup)

    Add a Unit to join the Mission.

    +
    MISSION:MenuReportBriefing(ReportGroup) +

    Reports the briefing.

    +
    MISSION:MenuReportPlayersPerTask(ReportGroup) + +
    MISSION:MenuReportPlayersProgress(ReportGroup) + +
    MISSION:MenuReportTasksPerStatus(TaskStatus, ReportGroup) + +
    MISSION:MenuReportTasksSummary(ReportGroup) +

    Report the task summary.

    MISSION.MissionBriefing +
    MISSION:MissionGoals() +

    MissionGoals Trigger for MISSION

    +
    MISSION.MissionGroupMenu +

    OnAfter Transition Handler for Event Fail.

    +
    MISSION:OnAfterMissionGoals(From, Event, To) +

    MissionGoals Handler OnAfter for MISSION

    OnBefore Transition Handler for Event Fail.

    +
    MISSION:OnBeforeMissionGoals(From, Event, To) +

    MissionGoals Handler OnBefore for MISSION

    MISSION:OnEnterCOMPLETED(From, Event, To) -

    OnEnter Transition Handler for State Completed.

    +

    OnEnter Transition Handler for State COMPLETED.

    MISSION:OnEnterFailed(From, Event, To)MISSION:OnEnterENGAGED(From, Event, To) -

    OnEnter Transition Handler for State Failed.

    +

    OnEnter Transition Handler for State ENGAGED.

    MISSION:OnEnterIdle(From, Event, To)MISSION:OnEnterFAILED(From, Event, To) -

    OnEnter Transition Handler for State Idle.

    +

    OnEnter Transition Handler for State FAILED.

    MISSION:OnEnterOngoing(From, Event, To)MISSION:OnEnterIDLE(From, Event, To) -

    OnEnter Transition Handler for State Ongoing.

    +

    OnEnter Transition Handler for State IDLE.

    MISSION:OnLeaveCompleted(From, Event, To)MISSION:OnLeaveCOMPLETED(From, Event, To) -

    OnLeave Transition Handler for State Completed.

    +

    OnLeave Transition Handler for State COMPLETED.

    MISSION:OnLeaveFailed(From, Event, To)MISSION:OnLeaveENGAGED(From, Event, To) -

    OnLeave Transition Handler for State Failed.

    +

    OnLeave Transition Handler for State ENGAGED.

    MISSION:OnLeaveIdle(From, Event, To)MISSION:OnLeaveFAILED(From, Event, To) -

    OnLeave Transition Handler for State Idle.

    +

    OnLeave Transition Handler for State FAILED.

    MISSION:OnLeaveOngoing(From, Event, To)MISSION:OnLeaveIDLE(From, Event, To) -

    OnLeave Transition Handler for State Ongoing.

    +

    OnLeave Transition Handler for State IDLE.

    MISSION:ReportBriefing() +

    Create a briefing report of the Mission.

    +
    MISSION:ReportDetails(ReportGroup)

    Create a detailed report of the Mission, listing all the details of the Task.

    MISSION:ReportOverview()MISSION:ReportOverview(ReportGroup, TaskStatus)

    Create a overview report of the Mission (multiple lines).

    MISSION:ReportSummary()MISSION:ReportPlayersPerTask(ReportGroup) +

    Create an active player report of the Mission.

    +
    MISSION:ReportPlayersProgress(ReportGroup) +

    Create an Mission Progress report of the Mission.

    +
    MISSION:ReportStatus() +

    Create a status report of the Mission.

    +
    MISSION:ReportSummary(ReportGroup)

    Create a summary report of the Mission (one line).

    +
    MISSION:SetGroupAssigned(MissionGroup) +

    Set Group assigned to the Mission.

    Asynchronous Event Trigger for Event Fail.

    +
    MISSION:__MissionGoals(Delay) +

    MissionGoals Asynchronous Trigger for MISSION

    - -
    MISSION:onenterCompleted(From, Event, To)MISSION:onenterCOMPLETED(From, Event, To)
    OBJECT +

    OBJECT class, extends Base#BASE

    +

    OBJECT handles the DCS Object objects:

    + +
      +
    • Support all DCS Object APIs.
    • +
    @@ -125,12 +146,6 @@

    Type OBJECT

    - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -613,6 +851,12 @@ + + + + @@ -649,12 +893,26 @@ + + + + + + + +
    OBJECT.ClassName - -
    OBJECT:Destroy()

    Destroys the OBJECT.

    @@ -167,6 +182,27 @@
    +

    OBJECT class, extends Base#BASE

    + +

    OBJECT handles the DCS Object objects:

    + +
      +
    • Support all DCS Object APIs.
    • +
    + + +
      +
    • Enhance with Object specific APIs not in the DCS Object API set.
    • +
    • Manage the "state" of the DCS Object.
    • +
    + +

    OBJECT constructor:

    + +

    The OBJECT class provides the following functions to construct a OBJECT instance:

    + +
    @@ -193,24 +229,7 @@

    Type OBJECT

    - -

    The OBJECT class

    - -

    Field(s)

    -
    -
    - - #string - -OBJECT.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    diff --git a/docs/Documentation/Patrol.html b/docs/Documentation/Patrol.html deleted file mode 100644 index 1e60bd6a1..000000000 --- a/docs/Documentation/Patrol.html +++ /dev/null @@ -1,771 +0,0 @@ - - - - - - -
    -
    - -
    -
    -
    -
    - -
    -

    Module Patrol

    - -

    (AI) (FSM) Make AI patrol routes or zones.

    - - - -
    - -

    1) #AI_PATROLZONE class, extends Core.Fsm#FSM_CONTROLLABLE

    -

    The #AI_PATROLZONE class implements the core functions to patrol a Zone by an AIR Controllable Group. -The patrol algorithm works that for each airplane patrolling, upon arrival at the patrol zone, -a random point is selected as the route point within the 3D space, within the given boundary limits. -The airplane will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. -Upon arrival at the random 3D point, a new 3D random point will be selected within the patrol zone using the given limits. -This cycle will continue until a fuel treshold has been reached by the airplane. -When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.

    - -

    1.1) AI_PATROLZONE constructor:

    - - - -

    1.2) AI_PATROLZONE state machine:

    -

    The AI_PATROLZONE is a state machine: it manages the different events and states of the AIControllable it is controlling.

    - -

    1.2.1) AI_PATROLZONE Events:

    - -
      -
    • AI_PATROLZONE.Route( AIControllable ): A new 3D route point is selected and the AIControllable will fly towards that point with the given speed.
    • -
    • AI_PATROLZONE.Patrol( AIControllable ): The AIControllable reports it is patrolling. This event is called every 30 seconds.
    • -
    • AI_PATROLZONE.RTB( AIControllable ): The AIControllable will report return to base.
    • -
    • AI_PATROLZONE.End( AIControllable ): The end of the AI_PATROLZONE process.
    • -
    • AI_PATROLZONE.Dead( AIControllable ): The AIControllable is dead. The AI_PATROLZONE process will be ended.
    • -
    - -

    1.2.2) AI_PATROLZONE States:

    - -
      -
    • Route: A new 3D route point is selected and the AIControllable will fly towards that point with the given speed.
    • -
    • Patrol: The AIControllable is patrolling. This state is set every 30 seconds, so every 30 seconds, a state transition method can be used.
    • -
    • RTB: The AIControllable reports it wants to return to the base.
    • -
    • Dead: The AIControllable is dead ...
    • -
    • End: The process has come to an end.
    • -
    - -

    1.2.3) AI_PATROLZONE state transition methods:

    - -

    State transition functions can be set by the mission designer customizing or improving the behaviour of the state. -There are 2 moments when state transition methods will be called by the state machine:

    - -
      -
    • Before the state transition. - The state transition method needs to start with the name OnBefore + the name of the state. - If the state transition method returns false, then the processing of the state transition will not be done! - If you want to change the behaviour of the AIControllable at this event, return false, - but then you'll need to specify your own logic using the AIControllable!

    • -
    • After the state transition. - The state transition method needs to start with the name OnAfter + the name of the state. - These state transition methods need to provide a return value, which is specified at the function description.

    • -
    - -

    An example how to manage a state transition for an AI_PATROLZONE object Patrol for the state RTB:

    - -
     local PatrolZoneGroup = GROUP:FindByName( "Patrol Zone" )
    - local PatrolZone = ZONE_POLYGON:New( "PatrolZone", PatrolZoneGroup )
    -
    - local PatrolSpawn = SPAWN:New( "Patrol Group" )
    - local PatrolGroup = PatrolSpawn:Spawn()
    -
    - local Patrol = AI_PATROLZONE:New( PatrolZone, 3000, 6000, 300, 600 )
    - Patrol:SetControllable( PatrolGroup )
    - Patrol:ManageFuel( 0.2, 60 )
    -
    - -

    OnBeforeRTB( AIGroup ) will be called by the AIPATROLZONE object when the AIGroup reports RTB, but before the RTB default action is processed by the AIPATROLZONE object.

    - -
     --- State transition function for the AI_PATROLZONE **Patrol** object
    - -- @param #AI_PATROLZONE self 
    - -- @param Wrapper.Controllable#CONTROLLABLE AIGroup
    - -- @return #boolean If false is returned, then the OnAfter state transition method will not be called.
    - function Patrol:OnBeforeRTB( AIGroup )
    -   AIGroup:MessageToRed( "Returning to base", 20 )
    - end
    -
    - -

    OnAfterRTB( AIGroup ) will be called by the AIPATROLZONE object when the AIGroup reports RTB, but after the RTB default action was processed by the AIPATROLZONE object.

    - -
     --- State transition function for the AI_PATROLZONE **Patrol** object
    - -- @param #AI_PATROLZONE self 
    - -- @param Wrapper.Controllable#CONTROLLABLE AIGroup
    - -- @return #Wrapper.Controllable#CONTROLLABLE The new AIGroup object that is set to be patrolling the zone.
    - function Patrol:OnAfterRTB( AIGroup )
    -   return PatrolSpawn:Spawn()
    - end 
    -
    - -

    1.3) Manage the AI_PATROLZONE parameters:

    -

    The following methods are available to modify the parameters of a AI_PATROLZONE object:

    - - - -

    1.3) Manage the out of fuel in the AI_PATROLZONE:

    -

    When the AIControllable is out of fuel, it is required that a new AIControllable is started, before the old AIControllable can return to the home base. -Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. -When the fuel treshold is reached, the AIControllable will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROLZONE. -Once the time is finished, the old AIControllable will return to the base. -Use the method AI_PATROLZONE.ManageFuel() to have this proces in place.

    - -
    - -

    API CHANGE HISTORY

    - -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    - -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    - -

    Hereby the change log:

    - -

    2016-09-01: Initial class and API.

    - -
    - -

    AUTHORS and CONTRIBUTIONS

    - -

    Contributions:

    - -
      -
    • DutchBaron: Testing.
    • -
    • Pikey: Testing and API concept review.
    • -
    - -

    Authors:

    - -
      -
    • FlightControl: Design & Programming.
    • -
    - - - -

    Global(s)

    - - - - - - - - - -
    AI_PATROLZONE - -
    _NewPatrolRoute(AIControllable) - -
    -

    Type AI_PATROLZONE

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AI_PATROLZONE.AIControllable -

    The Controllable patrolling.

    -
    AI_PATROLZONE.ClassName - -
    AI_PATROLZONE:ManageFuel(PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime) -

    When the AIControllable is out of fuel, it is required that a new AIControllable is started, before the old AIControllable can return to the home base.

    -
    AI_PATROLZONE:New(PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed) -

    Creates a new AI_PATROLZONE object

    -
    AI_PATROLZONE:OnAfterRoute(Controllable) -

    OnAfter State Transition Function

    -
    AI_PATROLZONE:OnBeforeRoute(Controllable) -

    OnBefore State Transition Function

    -
    AI_PATROLZONE.PatrolCeilingAltitude -

    The highest altitude in meters where to execute the patrol.

    -
    AI_PATROLZONE.PatrolFloorAltitude -

    The lowest altitude in meters where to execute the patrol.

    -
    AI_PATROLZONE.PatrolFuelTresholdPercentage - -
    AI_PATROLZONE.PatrolManageFuel - -
    AI_PATROLZONE.PatrolMaxSpeed -

    The maximum speed of the Controllable in km/h.

    -
    AI_PATROLZONE.PatrolMinSpeed -

    The minimum speed of the Controllable in km/h.

    -
    AI_PATROLZONE.PatrolOutOfFuelOrbitTime - -
    AI_PATROLZONE.PatrolZone -

    The Zone where the patrol needs to be executed.

    -
    AI_PATROLZONE:SetAltitude(PatrolFloorAltitude, PatrolCeilingAltitude) -

    Sets the floor and ceiling altitude of the patrol.

    -
    AI_PATROLZONE:SetSpeed(PatrolMinSpeed, PatrolMaxSpeed) -

    Sets (modifies) the minimum and maximum speed of the patrol.

    -
    AI_PATROLZONE:onenterPatrol() - -
    AI_PATROLZONE:onenterRoute() -

    Defines a new patrol route using the Process_PatrolZone parameters and settings.

    -
    - -

    Global(s)

    -
    -
    - - #AI_PATROLZONE - -AI_PATROLZONE - -
    -
    - - - -
    -
    -
    -
    - - -_NewPatrolRoute(AIControllable) - -
    -
    - - - -

    Parameter

    - -
    -
    -

    Type Patrol

    - -

    Type AI_PATROLZONE

    - -

    AI_PATROLZONE class

    - -

    Field(s)

    -
    -
    - - Wrapper.Controllable#CONTROLLABLE - -AI_PATROLZONE.AIControllable - -
    -
    - -

    The Controllable patrolling.

    - -
    -
    -
    -
    - - #string - -AI_PATROLZONE.ClassName - -
    -
    - - - -
    -
    -
    -
    - - -AI_PATROLZONE:ManageFuel(PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime) - -
    -
    - -

    When the AIControllable is out of fuel, it is required that a new AIControllable is started, before the old AIControllable can return to the home base.

    - - -

    Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. -When the fuel treshold is reached, the AIControllable will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROLZONE. -Once the time is finished, the old AIControllable will return to the base.

    - -

    Parameters

    -
      -
    • - -

      #number PatrolFuelTresholdPercentage : -The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.

      - -
    • -
    • - -

      #number PatrolOutOfFuelOrbitTime : -The amount of seconds the out of fuel AIControllable will orbit before returning to the base.

      - -
    • -
    -

    Return value

    - -

    #AI_PATROLZONE: -self

    - -
    -
    -
    -
    - - -AI_PATROLZONE:New(PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed) - -
    -
    - -

    Creates a new AI_PATROLZONE object

    - -

    Parameters

    - -

    Return value

    - -

    #AI_PATROLZONE: -self

    - -

    Usage:

    -
    -- Define a new AI_PATROLZONE Object. This PatrolArea will patrol an AIControllable within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
    -PatrolZone = ZONE:New( 'PatrolZone' )
    -PatrolSpawn = SPAWN:New( 'Patrol Group' )
    -PatrolArea = AI_PATROLZONE:New( PatrolZone, 3000, 6000, 600, 900 )
    - -
    -
    -
    -
    - - -AI_PATROLZONE:OnAfterRoute(Controllable) - -
    -
    - -

    OnAfter State Transition Function

    - -

    Parameter

    - -
    -
    -
    -
    - - -AI_PATROLZONE:OnBeforeRoute(Controllable) - -
    -
    - -

    OnBefore State Transition Function

    - -

    Parameter

    - -

    Return value

    - -

    #boolean:

    - - -
    -
    -
    -
    - - Dcs.DCSTypes#Altitude - -AI_PATROLZONE.PatrolCeilingAltitude - -
    -
    - -

    The highest altitude in meters where to execute the patrol.

    - -
    -
    -
    -
    - - Dcs.DCSTypes#Altitude - -AI_PATROLZONE.PatrolFloorAltitude - -
    -
    - -

    The lowest altitude in meters where to execute the patrol.

    - -
    -
    -
    -
    - - - -AI_PATROLZONE.PatrolFuelTresholdPercentage - -
    -
    - - - -
    -
    -
    -
    - - #boolean - -AI_PATROLZONE.PatrolManageFuel - -
    -
    - - - -
    -
    -
    -
    - - Dcs.DCSTypes#Speed - -AI_PATROLZONE.PatrolMaxSpeed - -
    -
    - -

    The maximum speed of the Controllable in km/h.

    - -
    -
    -
    -
    - - Dcs.DCSTypes#Speed - -AI_PATROLZONE.PatrolMinSpeed - -
    -
    - -

    The minimum speed of the Controllable in km/h.

    - -
    -
    -
    -
    - - - -AI_PATROLZONE.PatrolOutOfFuelOrbitTime - -
    -
    - - - -
    -
    -
    -
    - - Core.Zone#ZONE_BASE - -AI_PATROLZONE.PatrolZone - -
    -
    - -

    The Zone where the patrol needs to be executed.

    - -
    -
    -
    -
    - - -AI_PATROLZONE:SetAltitude(PatrolFloorAltitude, PatrolCeilingAltitude) - -
    -
    - -

    Sets the floor and ceiling altitude of the patrol.

    - -

    Parameters

    -
      -
    • - -

      Dcs.DCSTypes#Altitude PatrolFloorAltitude : -The lowest altitude in meters where to execute the patrol.

      - -
    • -
    • - -

      Dcs.DCSTypes#Altitude PatrolCeilingAltitude : -The highest altitude in meters where to execute the patrol.

      - -
    • -
    -

    Return value

    - -

    #AI_PATROLZONE: -self

    - -
    -
    -
    -
    - - -AI_PATROLZONE:SetSpeed(PatrolMinSpeed, PatrolMaxSpeed) - -
    -
    - -

    Sets (modifies) the minimum and maximum speed of the patrol.

    - -

    Parameters

    - -

    Return value

    - -

    #AI_PATROLZONE: -self

    - -
    -
    -
    -
    - - -AI_PATROLZONE:onenterPatrol() - -
    -
    - - - -
    -
    -
    -
    - - -AI_PATROLZONE:onenterRoute() - -
    -
    - -

    Defines a new patrol route using the Process_PatrolZone parameters and settings.

    - -

    Return value

    - -

    #AI_PATROLZONE: -self

    - -
    -
    - -
    - -
    - - diff --git a/docs/Documentation/Point.html b/docs/Documentation/Point.html index 2f03970b6..4c79fc8f9 100644 --- a/docs/Documentation/Point.html +++ b/docs/Documentation/Point.html @@ -17,9 +17,16 @@ index

    Module Point

    -

    Core - POINT_VEC classes define an extensive API to manage 3D points in the simulation space.

    +

    Core -- POINT_VEC classes define an extensive API to manage 3D points in the simulation space.

    -

    1) Point#POINT_VEC3 class, extends Base#BASE

    -

    The Point#POINT_VEC3 class defines a 3D point in the simulator.

    - -

    Important Note: Most of the functions in this section were taken from MIST, and reworked to OO concepts. -In order to keep the credibility of the the author, I want to emphasize that the of the MIST framework was created by Grimes, who you can find on the Eagle Dynamics Forums.

    - -

    1.1) POINT_VEC3 constructor

    - -

    A new POINT_VEC3 instance can be created with:

    - - - -

    1.2) Manupulate the X, Y, Z coordinates of the point

    - -

    A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate. -Methods exist to manupulate these coordinates.

    - -

    The current X, Y, Z axis can be retrieved with the methods POINT_VEC3.GetX(), POINT_VEC3.GetY(), POINT_VEC3.GetZ() respectively. -The methods POINT_VEC3.SetX(), POINT_VEC3.SetY(), POINT_VEC3.SetZ() change the respective axis with a new value. -The current axis values can be changed by using the methods POINT_VEC3.AddX(), POINT_VEC3.AddY(), POINT_VEC3.AddZ() -to add or substract a value from the current respective axis value. -Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example:

    - -
     local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3()
    -
    - -

    1.3) Create waypoints for routes

    - -

    A POINT_VEC3 can prepare waypoints for Ground, Air and Naval groups to be embedded into a Route.

    - - -

    1.5) Smoke, flare, explode, illuminate

    - -

    At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods:

    - -

    1.5.1) Smoke

    - - - -

    1.5.2) Flare

    - - - -

    1.5.3) Explode

    - - - -

    1.5.4) Illuminate

    - - - - -

    2) Point#POINT_VEC2 class, extends Point#POINT_VEC3

    -

    The Point#POINT_VEC2 class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified.

    - -

    2.1) POINT_VEC2 constructor

    -

    A new POINT_VEC2 instance can be created with:

    - - - -

    1.2) Manupulate the X, Altitude, Y coordinates of the 2D point

    - -

    A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate. -Methods exist to manupulate these coordinates.

    - -

    The current X, Altitude, Y axis can be retrieved with the methods POINT_VEC2.GetX(), POINT_VEC2.GetAlt(), POINT_VEC2.GetY() respectively. -The methods POINT_VEC2.SetX(), POINT_VEC2.SetAlt(), POINT_VEC2.SetY() change the respective axis with a new value. -The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods POINT_VEC2.GetLat(), POINT_VEC2.GetAlt(), POINT_VEC2.GetLon() respectively. -The current axis values can be changed by using the methods POINT_VEC2.AddX(), POINT_VEC2.AddAlt(), POINT_VEC2.AddY() -to add or substract a value from the current respective axis value. -Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example:

    - -
     local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2()
    -
    +

    Banner Image


    -

    API CHANGE HISTORY

    +

    Demo Missions

    -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    +

    POINT_VEC Demo Missions source code

    -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    +

    POINT_VEC Demo Missions, only for beta testers

    -

    Hereby the change log:

    +

    ALL Demo Missions pack of the last release

    -

    2017-03-03: POINT_VEC3:Explosion( ExplosionIntensity ) added.
    -2017-03-03: POINT_VEC3:IlluminationBomb() added.

    +
    -

    2017-02-18: POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd ) added.

    +

    YouTube Channel

    -

    2016-08-12: POINT_VEC3:Translate( Distance, Angle ) added.

    +

    POINT_VEC YouTube Channel

    -

    2016-08-06: Made PointVec3 and Vec3, PointVec2 and Vec2 terminology used in the code consistent.

    - -
      -
    • Replaced method PointVec3() to Vec3() where the code manages a Vec3. Replaced all references to the method.
    • -
    • Replaced method PointVec2() to Vec2() where the code manages a Vec2. Replaced all references to the method.
    • -
    • Replaced method RandomPointVec3() to RandomVec3() where the code manages a Vec3. Replaced all references to the method. - .
    • -
    +

    Authors:

    @@ -215,19 +140,337 @@ Note that the Set and Add methods return the current POINT_VEC2 object, so these

    Global(s)

    + + + + + +
    COORDINATE +

    COORDINATE class, extends Base#BASE

    + +

    COORDINATE defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space.

    +
    POINT_VEC2 +

    POINT_VEC2 class, extends Point#COORDINATE

    +

    The Point#POINT_VEC2 class defines a 2D point in the simulator.

    POINT_VEC3 +

    POINT_VEC3 class, extends Point#COORDINATE

    + +

    POINT_VEC3 defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space.

    +
    +

    Type COORDINATE

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    COORDINATE:DistanceFromVec2(Vec2Reference) + +
    COORDINATE:Explosion(ExplosionIntensity) +

    Creates an explosion at the point of a certain intensity.

    +
    COORDINATE:Flare(FlareColor, (, Azimuth) +

    Flares the point in a color.

    +
    COORDINATE:FlareGreen((, Azimuth) +

    Flare the COORDINATE Green.

    +
    COORDINATE:FlareRed(Azimuth) +

    Flare the COORDINATE Red.

    +
    COORDINATE:FlareWhite((, Azimuth) +

    Flare the COORDINATE White.

    +
    COORDINATE:FlareYellow((, Azimuth) +

    Flare the COORDINATE Yellow.

    +
    COORDINATE:Get2DDistance(TargetCoordinate) +

    Return the 2D distance in meters between the target COORDINATE and the COORDINATE.

    +
    COORDINATE:Get3DDistance(TargetCoordinate) +

    Return the 3D distance in meters between the target COORDINATE and the COORDINATE.

    +
    COORDINATE:GetAltitudeText(Settings) +

    Return the altitude text of the COORDINATE.

    +
    COORDINATE:GetAngleDegrees(DirectionVec3) +

    Return an angle in degrees from the COORDINATE using a direction vector in Vec3 format.

    +
    COORDINATE:GetAngleRadians(DirectionVec3) +

    Return an angle in radians from the COORDINATE using a direction vector in Vec3 format.

    +
    COORDINATE:GetBRAText(AngleRadians, Distance, Settings) +

    Provides a Bearing / Range / Altitude string

    +
    COORDINATE:GetBRText(AngleRadians, Distance, Settings) +

    Provides a Bearing / Range string

    +
    COORDINATE:GetBearingText(AngleRadians, Precision, Settings) +

    Provides a bearing text in degrees.

    +
    COORDINATE:GetDirectionVec3(TargetCoordinate) +

    Return a direction vector Vec3 from COORDINATE to the COORDINATE.

    +
    COORDINATE:GetDistanceText(Distance, Settings) +

    Provides a distance text expressed in the units of measurement.

    +
    COORDINATE:GetLandHeight() +

    Return the height of the land at the coordinate.

    +
    COORDINATE:GetNorthCorrectionRadians() +

    Get a correction in radians of the real magnetic north of the COORDINATE.

    +
    COORDINATE:GetRandomVec2InRadius(OuterRadius, InnerRadius) +

    Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.

    +
    COORDINATE:GetRandomVec3InRadius(OuterRadius, InnerRadius) +

    Return a random Vec3 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.

    +
    COORDINATE:GetVec2() +

    Return the coordinates of the COORDINATE in Vec2 format.

    +
    COORDINATE:GetVec3() +

    Return the coordinates of the COORDINATE in Vec3 format.

    +
    COORDINATE:IlluminationBomb() +

    Creates an illumination bomb at the point.

    +
    COORDINATE:IsLOS(ToCoordinate) +

    Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate.

    +
    COORDINATE:New(x, y, z) +

    COORDINATE constructor.

    +
    COORDINATE:NewFromVec2(Vec2, LandHeightAdd) +

    Create a new COORDINATE object from Vec2 coordinates.

    +
    COORDINATE:NewFromVec3(Vec3) +

    Create a new COORDINATE object from Vec3 coordinates.

    +
    COORDINATE:SetHeading(Heading) + +
    COORDINATE:Smoke(SmokeColor) +

    Smokes the point in a color.

    +
    COORDINATE:SmokeBlue() +

    Smoke the COORDINATE Blue.

    +
    COORDINATE:SmokeGreen() +

    Smoke the COORDINATE Green.

    +
    COORDINATE:SmokeOrange() +

    Smoke the COORDINATE Orange.

    +
    COORDINATE:SmokeRed() +

    Smoke the COORDINATE Red.

    +
    COORDINATE:SmokeWhite() +

    Smoke the COORDINATE White.

    +
    COORDINATE:ToString(Controllable, Settings, Task) +

    Provides a coordinate string of the point, based on a coordinate format system: + * Uses default settings in COORDINATE.

    +
    COORDINATE:ToStringAspect(TargetCoordinate) +

    Return an aspect string from a COORDINATE to the Angle of the object.

    +
    COORDINATE:ToStringBR(TargetCoordinate, FromCoordinate, Settings) +

    Return a BR string from a COORDINATE to the COORDINATE.

    +
    COORDINATE:ToStringBRA(TargetCoordinate, FromCoordinate, Settings) +

    Return a BRAA string from a COORDINATE to the COORDINATE.

    +
    COORDINATE:ToStringBULLS(Coalition, Settings) +

    Return a BULLS string from a COORDINATE to the BULLS of the coalition.

    +
    COORDINATE:ToStringFromRP(Controllable, Settings, ReferenceCoord, ReferenceName) +

    Provides a coordinate string of the point, based on a coordinate format system: + * Uses default settings in COORDINATE.

    +
    COORDINATE:ToStringLLDDM(Settings) +

    Provides a Lat Lon string in Degree Decimal Minute format.

    +
    COORDINATE:ToStringLLDMS(Settings) +

    Provides a Lat Lon string in Degree Minute Second format.

    +
    COORDINATE:ToStringMGRS(Settings) +

    Provides a MGRS string

    +
    COORDINATE:Translate(Distance, Angle) +

    Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE.

    +
    COORDINATE:WaypointAir(AltType, Type, Action, Speed, SpeedLocked) +

    Build an air type route point.

    +
    COORDINATE:WaypointGround(Speed, Formation) +

    Build an ground type route point.

    +
    COORDINATE.x + +
    COORDINATE.y + +
    COORDINATE.z
    +

    Type POINT_VEC2

    @@ -246,36 +489,18 @@ Note that the Set and Add methods return the current POINT_VEC2 object, so these - - - - - - - - - - - - @@ -288,6 +513,12 @@ Note that the Set and Add methods return the current POINT_VEC2 object, so these + + + + @@ -348,12 +579,6 @@ Note that the Set and Add methods return the current POINT_VEC2 object, so these - - - - @@ -394,132 +619,18 @@ Note that the Set and Add methods return the current POINT_VEC2 object, so these - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -538,24 +649,6 @@ Note that the Set and Add methods return the current POINT_VEC2 object, so these - - - - - - - - - - - - @@ -580,36 +673,18 @@ Note that the Set and Add methods return the current POINT_VEC2 object, so these - - - - - - - - - - - - @@ -628,66 +703,12 @@ Note that the Set and Add methods return the current POINT_VEC2 object, so these - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -756,6 +777,142 @@ Note that the Set and Add methods return the current POINT_VEC2 object, so these
    + #COORDINATE + +COORDINATE + +
    +
    + +

    COORDINATE class, extends Base#BASE

    + +

    COORDINATE defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space.

    + + + +

    COORDINATE constructor

    + +

    A new COORDINATE object can be created with:

    + + + +

    Create waypoints for routes

    + +

    A COORDINATE can prepare waypoints for Ground and Air groups to be embedded into a Route.

    + + + +

    Route points can be used in the Route methods of the Group#GROUP class.

    + + +

    Smoke, flare, explode, illuminate

    + +

    At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods:

    + +

    Smoke

    + + + +

    Flare

    + + + +

    Explode

    + + + +

    Illuminate

    + + + + +

    3D calculation methods

    + +

    Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method:

    + +

    Distance

    + + + +

    Angle

    + + + +

    Translation

    + +
      +
    • COORDINATE.Translate(): Translate the current 3D point towards an other 3D point using the given Distance and Angle.
    • +
    + +

    Get the North correction of the current location

    + + + + +

    Point Randomization

    + +

    Various methods exist to calculate random locations around a given 3D point.

    + + + + +

    Metric system

    + + + + +

    Coorinate text generation

    + + + + +
    +
    +
    +
    + #POINT_VEC2 POINT_VEC2 @@ -763,6 +920,36 @@ Note that the Set and Add methods return the current POINT_VEC2 object, so these
    +

    POINT_VEC2 class, extends Point#COORDINATE

    + +

    The Point#POINT_VEC2 class defines a 2D point in the simulator.

    + + +

    The height coordinate (if needed) will be the land height + an optional added height specified.

    + +

    POINT_VEC2 constructor

    + +

    A new POINT_VEC2 instance can be created with:

    + + + +

    Manupulate the X, Altitude, Y coordinates of the 2D point

    + +

    A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate. +Methods exist to manupulate these coordinates.

    + +

    The current X, Altitude, Y axis can be retrieved with the methods POINT_VEC2.GetX(), POINT_VEC2.GetAlt(), POINT_VEC2.GetY() respectively. +The methods POINT_VEC2.SetX(), POINT_VEC2.SetAlt(), POINT_VEC2.SetY() change the respective axis with a new value. +The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods POINT_VEC2.GetLat(), POINT_VEC2.GetAlt(), POINT_VEC2.GetLon() respectively. +The current axis values can be changed by using the methods POINT_VEC2.AddX(), POINT_VEC2.AddAlt(), POINT_VEC2.AddY() +to add or substract a value from the current respective axis value. +Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example:

    + +
     local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2()
    +
    @@ -777,17 +964,1405 @@ Note that the Set and Add methods return the current POINT_VEC2 object, so these
    +

    POINT_VEC3 class, extends Point#COORDINATE

    + +

    POINT_VEC3 defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space.

    + + + +

    Important Note: Most of the functions in this section were taken from MIST, and reworked to OO concepts. +In order to keep the credibility of the the author, +I want to emphasize that the formulas embedded in the MIST framework were created by Grimes or previous authors, +who you can find on the Eagle Dynamics Forums.

    + + +

    POINT_VEC3 constructor

    + +

    A new POINT_VEC3 object can be created with:

    + + + + +

    Manupulate the X, Y, Z coordinates of the POINT_VEC3

    + +

    A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate. +Methods exist to manupulate these coordinates.

    + +

    The current X, Y, Z axis can be retrieved with the methods POINT_VEC3.GetX(), POINT_VEC3.GetY(), POINT_VEC3.GetZ() respectively. +The methods POINT_VEC3.SetX(), POINT_VEC3.SetY(), POINT_VEC3.SetZ() change the respective axis with a new value. +The current axis values can be changed by using the methods POINT_VEC3.AddX(), POINT_VEC3.AddY(), POINT_VEC3.AddZ() +to add or substract a value from the current respective axis value. +Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example:

    + +
     local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3()
    +
    + + +

    3D calculation methods

    + +

    Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method:

    + + +

    Point Randomization

    + +

    Various methods exist to calculate random locations around a given 3D point.

    + + +

    Type Point

    -

    Type POINT_VEC2

    - -

    The POINT_VEC2 class

    +

    Type COORDINATE

    +

    Field(s)

    +
    +
    -

    Field(s)

    + +COORDINATE:DistanceFromVec2(Vec2Reference) + +
    +
    + + + + +

    TODO: check this to replace +- Calculate the distance from a reference DCSTypes#Vec2. + @param #COORDINATE self + @param Dcs.DCSTypes#Vec2 Vec2Reference The reference DCSTypes#Vec2. + @return Dcs.DCSTypes#Distance The distance from the reference DCSTypes#Vec2 in meters.

    + +

    Parameter

    +
      +
    • + +

      Vec2Reference :

      + +
    • +
    +
    +
    +
    +
    + + +COORDINATE:Explosion(ExplosionIntensity) + +
    +
    + +

    Creates an explosion at the point of a certain intensity.

    + +

    Parameter

    +
      +
    • + +

      #number ExplosionIntensity :

      + +
    • +
    +
    +
    +
    +
    + + +COORDINATE:Flare(FlareColor, (, Azimuth) + +
    +
    + +

    Flares the point in a color.

    + +

    Parameters

    + +
    +
    +
    +
    + + +COORDINATE:FlareGreen((, Azimuth) + +
    +
    + +

    Flare the COORDINATE Green.

    + +

    Parameters

    +
      +
    • + +

      Dcs.DCSTypes#Azimuth ( : +ptional) Azimuth The azimuth of the flare direction. The default azimuth is 0.

      + +
    • +
    • + +

      Azimuth :

      + +
    • +
    +
    +
    +
    +
    + + +COORDINATE:FlareRed(Azimuth) + +
    +
    + +

    Flare the COORDINATE Red.

    + +

    Parameter

    +
      +
    • + +

      Azimuth :

      + +
    • +
    +
    +
    +
    +
    + + +COORDINATE:FlareWhite((, Azimuth) + +
    +
    + +

    Flare the COORDINATE White.

    + +

    Parameters

    +
      +
    • + +

      Dcs.DCSTypes#Azimuth ( : +ptional) Azimuth The azimuth of the flare direction. The default azimuth is 0.

      + +
    • +
    • + +

      Azimuth :

      + +
    • +
    +
    +
    +
    +
    + + +COORDINATE:FlareYellow((, Azimuth) + +
    +
    + +

    Flare the COORDINATE Yellow.

    + +

    Parameters

    +
      +
    • + +

      Dcs.DCSTypes#Azimuth ( : +ptional) Azimuth The azimuth of the flare direction. The default azimuth is 0.

      + +
    • +
    • + +

      Azimuth :

      + +
    • +
    +
    +
    +
    +
    + + +COORDINATE:Get2DDistance(TargetCoordinate) + +
    +
    + +

    Return the 2D distance in meters between the target COORDINATE and the COORDINATE.

    + +

    Parameter

    +
      +
    • + +

      #COORDINATE TargetCoordinate : +The target COORDINATE.

      + +
    • +
    +

    Return value

    + +

    Dcs.DCSTypes#Distance: +Distance The distance in meters.

    + +
    +
    +
    +
    + + +COORDINATE:Get3DDistance(TargetCoordinate) + +
    +
    + +

    Return the 3D distance in meters between the target COORDINATE and the COORDINATE.

    + +

    Parameter

    +
      +
    • + +

      #COORDINATE TargetCoordinate : +The target COORDINATE.

      + +
    • +
    +

    Return value

    + +

    Dcs.DCSTypes#Distance: +Distance The distance in meters.

    + +
    +
    +
    +
    + + +COORDINATE:GetAltitudeText(Settings) + +
    +
    + +

    Return the altitude text of the COORDINATE.

    + +

    Parameter

    +
      +
    • + +

      Settings :

      + +
    • +
    +

    Return value

    + +

    #string: +Altitude text.

    + +
    +
    +
    +
    + + +COORDINATE:GetAngleDegrees(DirectionVec3) + +
    +
    + +

    Return an angle in degrees from the COORDINATE using a direction vector in Vec3 format.

    + +

    Parameter

    + +

    Return value

    + +

    #number: +DirectionRadians The angle in degrees.

    + +
    +
    +
    +
    + + +COORDINATE:GetAngleRadians(DirectionVec3) + +
    +
    + +

    Return an angle in radians from the COORDINATE using a direction vector in Vec3 format.

    + +

    Parameter

    + +

    Return value

    + +

    #number: +DirectionRadians The angle in radians.

    + +
    +
    +
    +
    + + +COORDINATE:GetBRAText(AngleRadians, Distance, Settings) + +
    +
    + +

    Provides a Bearing / Range / Altitude string

    + +

    Parameters

    +
      +
    • + +

      #number AngleRadians : +The angle in randians

      + +
    • +
    • + +

      #number Distance : +The distance

      + +
    • +
    • + +

      Core.Settings#SETTINGS Settings :

      + +
    • +
    +

    Return value

    + +

    #string: +The BRA Text

    + +
    +
    +
    +
    + + +COORDINATE:GetBRText(AngleRadians, Distance, Settings) + +
    +
    + +

    Provides a Bearing / Range string

    + +

    Parameters

    +
      +
    • + +

      #number AngleRadians : +The angle in randians

      + +
    • +
    • + +

      #number Distance : +The distance

      + +
    • +
    • + +

      Core.Settings#SETTINGS Settings :

      + +
    • +
    +

    Return value

    + +

    #string: +The BR Text

    + +
    +
    +
    +
    + + +COORDINATE:GetBearingText(AngleRadians, Precision, Settings) + +
    +
    + +

    Provides a bearing text in degrees.

    + +

    Parameters

    +
      +
    • + +

      #number AngleRadians : +The angle in randians.

      + +
    • +
    • + +

      #number Precision : +The precision.

      + +
    • +
    • + +

      Core.Settings#SETTINGS Settings :

      + +
    • +
    +

    Return value

    + +

    #string: +The bearing text in degrees.

    + +
    +
    +
    +
    + + +COORDINATE:GetDirectionVec3(TargetCoordinate) + +
    +
    + +

    Return a direction vector Vec3 from COORDINATE to the COORDINATE.

    + +

    Parameter

    +
      +
    • + +

      #COORDINATE TargetCoordinate : +The target COORDINATE.

      + +
    • +
    +

    Return value

    + +

    Dcs.DCSTypes#Vec3: +DirectionVec3 The direction vector in Vec3 format.

    + +
    +
    +
    +
    + + +COORDINATE:GetDistanceText(Distance, Settings) + +
    +
    + +

    Provides a distance text expressed in the units of measurement.

    + +

    Parameters

    + +

    Return value

    + +

    #string: +The distance text expressed in the units of measurement.

    + +
    +
    +
    +
    + + +COORDINATE:GetLandHeight() + +
    +
    + +

    Return the height of the land at the coordinate.

    + +

    Return value

    + +

    #number:

    + + +
    +
    +
    +
    + + +COORDINATE:GetNorthCorrectionRadians() + +
    +
    + +

    Get a correction in radians of the real magnetic north of the COORDINATE.

    + +

    Return value

    + +

    #number: +CorrectionRadians The correction in radians.

    + +
    +
    +
    +
    + + +COORDINATE:GetRandomVec2InRadius(OuterRadius, InnerRadius) + +
    +
    + +

    Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.

    + +

    Parameters

    + +

    Return value

    + +

    Dcs.DCSTypes#Vec2: +Vec2

    + +
    +
    +
    +
    + + +COORDINATE:GetRandomVec3InRadius(OuterRadius, InnerRadius) + +
    +
    + +

    Return a random Vec3 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.

    + +

    Parameters

    + +

    Return value

    + +

    Dcs.DCSTypes#Vec3: +Vec3

    + +
    +
    +
    +
    + + +COORDINATE:GetVec2() + +
    +
    + +

    Return the coordinates of the COORDINATE in Vec2 format.

    + +

    Return value

    + +

    Dcs.DCSTypes#Vec2: +The Vec2 format coordinate.

    + +
    +
    +
    +
    + + +COORDINATE:GetVec3() + +
    +
    + +

    Return the coordinates of the COORDINATE in Vec3 format.

    + +

    Return value

    + +

    Dcs.DCSTypes#Vec3: +The Vec3 format coordinate.

    + +
    +
    +
    +
    + + +COORDINATE:IlluminationBomb() + +
    +
    + +

    Creates an illumination bomb at the point.

    + +
    +
    +
    +
    + + +COORDINATE:IsLOS(ToCoordinate) + +
    +
    + +

    Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate.

    + +

    Parameter

    + +

    Return value

    + +

    #boolean: +true If the ToCoordinate has LOS with the Coordinate, otherwise false.

    + +
    +
    +
    +
    + + +COORDINATE:New(x, y, z) + +
    +
    + +

    COORDINATE constructor.

    + +

    Parameters

    + +

    Return value

    + +

    #COORDINATE:

    + + +
    +
    +
    +
    + + +COORDINATE:NewFromVec2(Vec2, LandHeightAdd) + +
    +
    + +

    Create a new COORDINATE object from Vec2 coordinates.

    + +

    Parameters

    +
      +
    • + +

      Dcs.DCSTypes#Vec2 Vec2 : +The Vec2 point.

      + +
    • +
    • + +

      Dcs.DCSTypes#Distance LandHeightAdd : +(optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height.

      + +
    • +
    +

    Return value

    + +

    #COORDINATE:

    + + +
    +
    +
    +
    + + +COORDINATE:NewFromVec3(Vec3) + +
    +
    + +

    Create a new COORDINATE object from Vec3 coordinates.

    + +

    Parameter

    + +

    Return value

    + +

    #COORDINATE:

    + + +
    +
    +
    +
    + + +COORDINATE:SetHeading(Heading) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      Heading :

      + +
    • +
    +
    +
    +
    +
    + + +COORDINATE:Smoke(SmokeColor) + +
    +
    + +

    Smokes the point in a color.

    + +

    Parameter

    + +
    +
    +
    +
    + + +COORDINATE:SmokeBlue() + +
    +
    + +

    Smoke the COORDINATE Blue.

    + +
    +
    +
    +
    + + +COORDINATE:SmokeGreen() + +
    +
    + +

    Smoke the COORDINATE Green.

    + +
    +
    +
    +
    + + +COORDINATE:SmokeOrange() + +
    +
    + +

    Smoke the COORDINATE Orange.

    + +
    +
    +
    +
    + + +COORDINATE:SmokeRed() + +
    +
    + +

    Smoke the COORDINATE Red.

    + +
    +
    +
    +
    + + +COORDINATE:SmokeWhite() + +
    +
    + +

    Smoke the COORDINATE White.

    + +
    +
    +
    +
    + + +COORDINATE:ToString(Controllable, Settings, Task) + +
    +
    + +

    Provides a coordinate string of the point, based on a coordinate format system: + * Uses default settings in COORDINATE.

    + + +
      +
    • Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
    • +
    + +

    Parameters

    + +

    Return value

    + +

    #string: +The coordinate Text in the configured coordinate system.

    + +
    +
    +
    +
    + + +COORDINATE:ToStringAspect(TargetCoordinate) + +
    +
    + +

    Return an aspect string from a COORDINATE to the Angle of the object.

    + +

    Parameter

    +
      +
    • + +

      #COORDINATE TargetCoordinate : +The target COORDINATE.

      + +
    • +
    +

    Return value

    + +

    #string: +The Aspect string, which is Hot, Cold or Flanking.

    + +
    +
    +
    +
    + + +COORDINATE:ToStringBR(TargetCoordinate, FromCoordinate, Settings) + +
    +
    + +

    Return a BR string from a COORDINATE to the COORDINATE.

    + +

    Parameters

    +
      +
    • + +

      #COORDINATE TargetCoordinate : +The target COORDINATE.

      + +
    • +
    • + +

      FromCoordinate :

      + +
    • +
    • + +

      Settings :

      + +
    • +
    +

    Return value

    + +

    #string: +The BR text.

    + +
    +
    +
    +
    + + +COORDINATE:ToStringBRA(TargetCoordinate, FromCoordinate, Settings) + +
    +
    + +

    Return a BRAA string from a COORDINATE to the COORDINATE.

    + +

    Parameters

    +
      +
    • + +

      #COORDINATE TargetCoordinate : +The target COORDINATE.

      + +
    • +
    • + +

      FromCoordinate :

      + +
    • +
    • + +

      Settings :

      + +
    • +
    +

    Return value

    + +

    #string: +The BR text.

    + +
    +
    +
    +
    + + +COORDINATE:ToStringBULLS(Coalition, Settings) + +
    +
    + +

    Return a BULLS string from a COORDINATE to the BULLS of the coalition.

    + +

    Parameters

    + +

    Return value

    + +

    #string: +The BR text.

    + +
    +
    +
    +
    + + +COORDINATE:ToStringFromRP(Controllable, Settings, ReferenceCoord, ReferenceName) + +
    +
    + +

    Provides a coordinate string of the point, based on a coordinate format system: + * Uses default settings in COORDINATE.

    + + +
      +
    • Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
    • +
    + +

    Parameters

    + +

    Return value

    + +

    #string: +The coordinate Text in the configured coordinate system.

    + +
    +
    +
    +
    + + +COORDINATE:ToStringLLDDM(Settings) + +
    +
    + +

    Provides a Lat Lon string in Degree Decimal Minute format.

    + +

    Parameter

    + +

    Return value

    + +

    #string: +The LL DDM Text

    + +
    +
    +
    +
    + + +COORDINATE:ToStringLLDMS(Settings) + +
    +
    + +

    Provides a Lat Lon string in Degree Minute Second format.

    + +

    Parameter

    + +

    Return value

    + +

    #string: +The LL DMS Text

    + +
    +
    +
    +
    + + +COORDINATE:ToStringMGRS(Settings) + +
    +
    + +

    Provides a MGRS string

    + +

    Parameter

    + +

    Return value

    + +

    #string: +The MGRS Text

    + +
    +
    +
    +
    + + +COORDINATE:Translate(Distance, Angle) + +
    +
    + +

    Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE.

    + +

    Parameters

    + +

    Return value

    + +

    #COORDINATE: +The new calculated COORDINATE.

    + +
    +
    +
    +
    + + +COORDINATE:WaypointAir(AltType, Type, Action, Speed, SpeedLocked) + +
    +
    + +

    Build an air type route point.

    + +

    Parameters

    + +

    Return value

    + +

    #table: +The route point.

    + +
    +
    +
    +
    + + +COORDINATE:WaypointGround(Speed, Formation) + +
    +
    + +

    Build an ground type route point.

    + +

    Parameters

    +
      +
    • + +

      #number Speed : +(optional) Speed in km/h. The default speed is 999 km/h.

      + +
    • +
    • + +

      #string Formation : +(optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right".

      + +
    • +
    +

    Return value

    + +

    #table: +The route point.

    + +
    +
    +
    +
    + + + +COORDINATE.x + +
    +
    + + + +
    +
    +
    +
    + + + +COORDINATE.y + +
    +
    + + + +
    +
    +
    +
    + + + +COORDINATE.z + +
    +
    + + + +
    +
    + +

    Type COORDINATE.RoutePointAction

    + +

    Type COORDINATE.RoutePointAltType

    + +

    Type COORDINATE.RoutePointType

    + +

    Type POINT_VEC2

    +

    Field(s)

    @@ -867,20 +2442,6 @@ The y coordinate.

    #POINT_VEC2:

    - -
    -
    -
    - - #string - -POINT_VEC2.ClassName - -
    -
    - - -
    @@ -892,49 +2453,23 @@ The y coordinate.

    -

    Calculate the distance from a reference #POINT_VEC2.

    -

    Parameter

    - -

    Return value

    - -

    Dcs.DCSTypes#Distance: -The distance from the reference #POINT_VEC2 in meters.

    - -
    -
    -
    -
    - - -POINT_VEC2:DistanceFromVec2(Vec2Reference) - -
    -
    -

    Calculate the distance from a reference DCSTypes#Vec2.

    +

    TODO: Check this to replace +- Calculate the distance from a reference #POINT_VEC2. + @param #POINTVEC2 self + @param #POINTVEC2 PointVec2Reference The reference #POINT_VEC2. + @return Dcs.DCSTypes#Distance The distance from the reference #POINT_VEC2 in meters.

    Parameter

    -

    Return value

    - -

    Dcs.DCSTypes#Distance: -The distance from the reference DCSTypes#Vec2 in meters.

    -
    @@ -958,24 +2493,6 @@ The land altitude.

    - -POINT_VEC2:GetAltitudeText() - -
    -
    - -

    Return no text for the altitude of the POINT_VEC2.

    - -

    Return value

    - -

    #string: -Empty string.

    - -
    -
    -
    -
    - POINT_VEC2:GetLat() @@ -1007,6 +2524,37 @@ The x coodinate.

    #number: The y coodinate.

    + +
    +
    +
    + + +POINT_VEC2:GetRandomPointVec2InRadius(OuterRadius, InnerRadius) + +
    +
    + +

    Return a random POINTVEC2 within an Outer Radius and optionally NOT within an Inner Radius of the POINTVEC2.

    + +

    Parameters

    + +

    Return value

    + +

    #POINT_VEC2:

    + +
    @@ -1281,39 +2829,6 @@ The y coordinate.

    #POINT_VEC2:

    - -
    -
    -
    - - -POINT_VEC2:Translate(Distance, Angle) - -
    -
    - -

    Add a Distance in meters from the POINTVEC2 orthonormal plane, with the given angle, and calculate the new POINTVEC2.

    - -

    Parameters

    - -

    Return value

    - -

    #POINT_VEC2: -The new calculated POINT_VEC2.

    -
    @@ -1347,6 +2862,7 @@ The new calculated POINT_VEC2.

    + POINT_VEC2.z @@ -1442,73 +2958,6 @@ The z coordinate value to add to the current z coodinate.

    #POINT_VEC3:

    - -
    -
    -
    - - #string - -POINT_VEC3.ClassName - -
    -
    - - - -
    -
    -
    -
    - - -POINT_VEC3:Explosion(ExplosionIntensity) - -
    -
    - -

    Creates an explosion at the point of a certain intensity.

    - -

    Parameter

    -
      -
    • - -

      #number ExplosionIntensity :

      - -
    • -
    -
    -
    -
    -
    - - -POINT_VEC3:Flare(FlareColor, (, Azimuth) - -
    -
    - -

    Flares the point in a color.

    - -

    Parameters

    -
    @@ -1523,310 +2972,6 @@ ptional) Azimuth The azimuth of the flare direction. The default azimuth is 0. -
    -
    -
    - - -POINT_VEC3:FlareGreen((, Azimuth) - -
    -
    - -

    Flare the POINT_VEC3 Green.

    - -

    Parameters

    -
      -
    • - -

      Dcs.DCSTypes#Azimuth ( : -ptional) Azimuth The azimuth of the flare direction. The default azimuth is 0.

      - -
    • -
    • - -

      Azimuth :

      - -
    • -
    -
    -
    -
    -
    - - -POINT_VEC3:FlareRed(Azimuth) - -
    -
    - -

    Flare the POINT_VEC3 Red.

    - -

    Parameter

    -
      -
    • - -

      Azimuth :

      - -
    • -
    -
    -
    -
    -
    - - -POINT_VEC3:FlareWhite((, Azimuth) - -
    -
    - -

    Flare the POINT_VEC3 White.

    - -

    Parameters

    -
      -
    • - -

      Dcs.DCSTypes#Azimuth ( : -ptional) Azimuth The azimuth of the flare direction. The default azimuth is 0.

      - -
    • -
    • - -

      Azimuth :

      - -
    • -
    -
    -
    -
    -
    - - -POINT_VEC3:FlareYellow((, Azimuth) - -
    -
    - -

    Flare the POINT_VEC3 Yellow.

    - -

    Parameters

    -
      -
    • - -

      Dcs.DCSTypes#Azimuth ( : -ptional) Azimuth The azimuth of the flare direction. The default azimuth is 0.

      - -
    • -
    • - -

      Azimuth :

      - -
    • -
    -
    -
    -
    -
    - - -POINT_VEC3:Get2DDistance(TargetPointVec3) - -
    -
    - -

    Return the 2D distance in meters between the target POINTVEC3 and the POINTVEC3.

    - -

    Parameter

    -
      -
    • - -

      #POINT_VEC3 TargetPointVec3 : -The target POINT_VEC3.

      - -
    • -
    -

    Return value

    - -

    Dcs.DCSTypes#Distance: -Distance The distance in meters.

    - -
    -
    -
    -
    - - -POINT_VEC3:Get3DDistance(TargetPointVec3) - -
    -
    - -

    Return the 3D distance in meters between the target POINTVEC3 and the POINTVEC3.

    - -

    Parameter

    -
      -
    • - -

      #POINT_VEC3 TargetPointVec3 : -The target POINT_VEC3.

      - -
    • -
    -

    Return value

    - -

    Dcs.DCSTypes#Distance: -Distance The distance in meters.

    - -
    -
    -
    -
    - - -POINT_VEC3:GetAltitudeText() - -
    -
    - -

    Return the altitude text of the POINT_VEC3.

    - -

    Return value

    - -

    #string: -Altitude text.

    - -
    -
    -
    -
    - - -POINT_VEC3:GetBRText(TargetPointVec3) - -
    -
    - -

    Return a BR string from a POINTVEC3 to the POINTVEC3.

    - -

    Parameter

    -
      -
    • - -

      #POINT_VEC3 TargetPointVec3 : -The target POINT_VEC3.

      - -
    • -
    -

    Return value

    - -

    #string: -The BR text.

    - -
    -
    -
    -
    - - -POINT_VEC3:GetDirectionRadians(DirectionVec3) - -
    -
    - -

    Return a direction in radians from the POINT_VEC3 using a direction vector in Vec3 format.

    - -

    Parameter

    - -

    Return value

    - -

    #number: -DirectionRadians The direction in radians.

    - -
    -
    -
    -
    - - -POINT_VEC3:GetDirectionVec3(TargetPointVec3) - -
    -
    - -

    Return a direction vector Vec3 from POINTVEC3 to the POINTVEC3.

    - -

    Parameter

    -
      -
    • - -

      #POINT_VEC3 TargetPointVec3 : -The target POINT_VEC3.

      - -
    • -
    -

    Return value

    - -

    Dcs.DCSTypes#Vec3: -DirectionVec3 The direction vector in Vec3 format.

    - -
    -
    -
    -
    - - -POINT_VEC3:GetNorthCorrectionRadians() - -
    -
    - -

    Get a correction in radians of the real magnetic north of the POINT_VEC3.

    - -

    Return value

    - -

    #number: -CorrectionRadians The correction in radians.

    - -
    -
    -
    -
    - - -POINT_VEC3:GetRandomPointVec2InRadius(OuterRadius, InnerRadius) - -
    -
    - -

    Return a random POINTVEC2 within an Outer Radius and optionally NOT within an Inner Radius of the POINTVEC3.

    - -

    Parameters

    - -

    Return value

    - -

    #POINT_VEC2:

    - -
    @@ -1858,104 +3003,6 @@ CorrectionRadians The correction in radians.

    #POINT_VEC3:

    - -
    -
    -
    - - -POINT_VEC3:GetRandomVec2InRadius(OuterRadius, InnerRadius) - -
    -
    - -

    Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3.

    - -

    Parameters

    - -

    Return value

    - -

    Dcs.DCSTypes#Vec2: -Vec2

    - -
    -
    -
    -
    - - -POINT_VEC3:GetRandomVec3InRadius(OuterRadius, InnerRadius) - -
    -
    - -

    Return a random Vec3 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3.

    - -

    Parameters

    - -

    Return value

    - -

    Dcs.DCSTypes#Vec3: -Vec3

    - -
    -
    -
    -
    - - -POINT_VEC3:GetVec2() - -
    -
    - -

    Return the coordinates of the POINT_VEC3 in Vec2 format.

    - -

    Return value

    - -

    Dcs.DCSTypes#Vec2: -The Vec2 coodinate.

    - -
    -
    -
    -
    - - -POINT_VEC3:GetVec3() - -
    -
    - -

    Return the coordinates of the POINT_VEC3 in Vec3 format.

    - -

    Return value

    - -

    Dcs.DCSTypes#Vec3: -The Vec3 coodinate.

    -
    @@ -2010,51 +3057,6 @@ The y coodinate.

    #number: The z coodinate.

    - -
    -
    -
    - - -POINT_VEC3:IlluminationBomb() - -
    -
    - -

    Creates an illumination bomb at the point.

    - -
    -
    -
    -
    - - -POINT_VEC3:IsMetric() - -
    -
    - -

    Gets if the POINT_VEC3 is metric or NM.

    - -

    Return value

    - -

    #boolean: -Metric true means metric, false means NM.

    - -
    -
    -
    -
    - - - -POINT_VEC3.Metric - -
    -
    - - -
    @@ -2091,8 +3093,8 @@ The z coordinate of the Vec3 point, pointing to the Right.

    Return value

    -

    Core.Point#POINT_VEC3: -self

    +

    Core.Point#POINT_VEC3:

    +
    @@ -2117,7 +3119,8 @@ The Vec2 point.

  • -

    LandHeightAdd :

    +

    Dcs.DCSTypes#Distance LandHeightAdd : +(optional) Add a landheight.

  • @@ -2167,57 +3170,6 @@ self

    - -
    -
    -
    - - -POINT_VEC3:RoutePointAir(AltType, Type, Action, Speed, SpeedLocked) - -
    -
    - -

    Build an air type route point.

    - -

    Parameters

    - -

    Return value

    - -

    #table: -The route point.

    -
    @@ -2232,39 +3184,6 @@ The route point.

    - -
    -
    -
    - - -POINT_VEC3:RoutePointGround(Speed, Formation) - -
    -
    - -

    Build an ground type route point.

    - -

    Parameters

    - -

    Return value

    - -

    #table: -The route point.

    -
    @@ -2279,28 +3198,6 @@ The route point.

    - -
    -
    -
    - - -POINT_VEC3:SetMetric(Metric) - -
    -
    - -

    Sets the POINT_VEC3 metric or NM.

    - -

    Parameter

    -
      -
    • - -

      #boolean Metric : -true means metric, false means NM.

      - -
    • -
    @@ -2382,40 +3279,6 @@ The z coordinate.

    #POINT_VEC3:

    - -
    -
    -
    - - -POINT_VEC3:Smoke(SmokeColor) - -
    -
    - -

    Smokes the point in a color.

    - -

    Parameter

    - -
    -
    -
    -
    - - -POINT_VEC3:SmokeBlue() - -
    -
    - -

    Smoke the POINT_VEC3 Blue.

    -
    @@ -2430,167 +3293,6 @@ The z coordinate.

    - -
    -
    -
    - - -POINT_VEC3:SmokeGreen() - -
    -
    - -

    Smoke the POINT_VEC3 Green.

    - -
    -
    -
    -
    - - -POINT_VEC3:SmokeOrange() - -
    -
    - -

    Smoke the POINT_VEC3 Orange.

    - -
    -
    -
    -
    - - -POINT_VEC3:SmokeRed() - -
    -
    - -

    Smoke the POINT_VEC3 Red.

    - -
    -
    -
    -
    - - -POINT_VEC3:SmokeWhite() - -
    -
    - -

    Smoke the POINT_VEC3 White.

    - -
    -
    -
    -
    - - -POINT_VEC3:ToStringBR(AngleRadians, Distance) - -
    -
    - -

    Provides a Bearing / Range string

    - -

    Parameters

    -
      -
    • - -

      #number AngleRadians : -The angle in randians

      - -
    • -
    • - -

      #number Distance : -The distance

      - -
    • -
    -

    Return value

    - -

    #string: -The BR Text

    - -
    -
    -
    -
    - - -POINT_VEC3:ToStringLL(AngleRadians, Distance, acc, DMS) - -
    -
    - -

    Provides a Bearing / Range string

    - -

    Parameters

    -
      -
    • - -

      #number AngleRadians : -The angle in randians

      - -
    • -
    • - -

      #number Distance : -The distance

      - -
    • -
    • - -

      acc :

      - -
    • -
    • - -

      DMS :

      - -
    • -
    -

    Return value

    - -

    #string: -The BR Text

    - -
    -
    -
    -
    - - -POINT_VEC3:Translate(Distance, Angle) - -
    -
    - -

    Add a Distance in meters from the POINTVEC3 horizontal plane, with the given angle, and calculate the new POINTVEC3.

    - -

    Parameters

    - -

    Return value

    - -

    #POINT_VEC3: -The new calculated POINT_VEC3.

    -
    diff --git a/docs/Documentation/Positionable.html b/docs/Documentation/Positionable.html index 6ac05da3e..f5b1c4a9e 100644 --- a/docs/Documentation/Positionable.html +++ b/docs/Documentation/Positionable.html @@ -17,9 +17,16 @@ index

    Module Positionable

    -

    This module contains the POSITIONABLE class.

    +

    Wrapper -- POSITIONABLE wraps DCS classes that are "positionable".

    -

    1) Positionable#POSITIONABLE class, extends Identifiable#IDENTIFIABLE

    -

    The Positionable#POSITIONABLE class is a wrapper class to handle the POSITIONABLE objects:

    +
    -
      -
    • Support all DCS APIs.
    • -
    • Enhance with POSITIONABLE specific APIs not in the DCS API set.
    • -
    • Manage the "state" of the POSITIONABLE.
    • -
    +

    Author: Sven Van de Velde (FlightControl)

    -

    1.1) POSITIONABLE constructor:

    -

    The POSITIONABLE class provides the following functions to construct a POSITIONABLE instance:

    - - - -

    1.2) POSITIONABLE methods:

    -

    The following methods can be used to identify an measurable object:

    - - +

    Contributions:


    @@ -109,7 +123,13 @@
    POINT_VEC2:AddY(y)

    Add to the y coordinate of the POINT_VEC2.

    -
    POINT_VEC2.ClassName -
    POINT_VEC2:DistanceFromPointVec2(PointVec2Reference) -

    Calculate the distance from a reference #POINT_VEC2.

    -
    POINT_VEC2:DistanceFromVec2(Vec2Reference) -

    Calculate the distance from a reference DCSTypes#Vec2.

    +
    POINT_VEC2:GetAlt()

    Return the altitude (height) of the land at the POINT_VEC2.

    -
    POINT_VEC2:GetAltitudeText() -

    Return no text for the altitude of the POINT_VEC2.

    POINT_VEC2:GetLon()

    Return the Lon(gitude) coordinate of the POINTVEC2 (ie: (parent)POINTVEC3.z).

    +
    POINT_VEC2:GetRandomPointVec2InRadius(OuterRadius, InnerRadius) +

    Return a random POINTVEC2 within an Outer Radius and optionally NOT within an Inner Radius of the POINTVEC2.

    POINT_VEC2:SetY(y)

    Set the y coordinate of the POINT_VEC2.

    -
    POINT_VEC2:Translate(Distance, Angle) -

    Add a Distance in meters from the POINTVEC2 orthonormal plane, with the given angle, and calculate the new POINTVEC2.

    POINT_VEC3:AddZ(z)

    Add to the z coordinate of the POINT_VEC3.

    -
    POINT_VEC3.ClassName - -
    POINT_VEC3:Explosion(ExplosionIntensity) -

    Creates an explosion at the point of a certain intensity.

    -
    POINT_VEC3:Flare(FlareColor, (, Azimuth) -

    Flares the point in a color.

    POINT_VEC3.FlareColor -
    POINT_VEC3:FlareGreen((, Azimuth) -

    Flare the POINT_VEC3 Green.

    -
    POINT_VEC3:FlareRed(Azimuth) -

    Flare the POINT_VEC3 Red.

    -
    POINT_VEC3:FlareWhite((, Azimuth) -

    Flare the POINT_VEC3 White.

    -
    POINT_VEC3:FlareYellow((, Azimuth) -

    Flare the POINT_VEC3 Yellow.

    -
    POINT_VEC3:Get2DDistance(TargetPointVec3) -

    Return the 2D distance in meters between the target POINTVEC3 and the POINTVEC3.

    -
    POINT_VEC3:Get3DDistance(TargetPointVec3) -

    Return the 3D distance in meters between the target POINTVEC3 and the POINTVEC3.

    -
    POINT_VEC3:GetAltitudeText() -

    Return the altitude text of the POINT_VEC3.

    -
    POINT_VEC3:GetBRText(TargetPointVec3) -

    Return a BR string from a POINTVEC3 to the POINTVEC3.

    -
    POINT_VEC3:GetDirectionRadians(DirectionVec3) -

    Return a direction in radians from the POINT_VEC3 using a direction vector in Vec3 format.

    -
    POINT_VEC3:GetDirectionVec3(TargetPointVec3) -

    Return a direction vector Vec3 from POINTVEC3 to the POINTVEC3.

    -
    POINT_VEC3:GetNorthCorrectionRadians() -

    Get a correction in radians of the real magnetic north of the POINT_VEC3.

    -
    POINT_VEC3:GetRandomPointVec2InRadius(OuterRadius, InnerRadius) -

    Return a random POINTVEC2 within an Outer Radius and optionally NOT within an Inner Radius of the POINTVEC3.

    POINT_VEC3:GetRandomPointVec3InRadius(OuterRadius, InnerRadius)

    Return a random POINTVEC3 within an Outer Radius and optionally NOT within an Inner Radius of the POINTVEC3.

    -
    POINT_VEC3:GetRandomVec2InRadius(OuterRadius, InnerRadius) -

    Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3.

    -
    POINT_VEC3:GetRandomVec3InRadius(OuterRadius, InnerRadius) -

    Return a random Vec3 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3.

    -
    POINT_VEC3:GetVec2() -

    Return the coordinates of the POINT_VEC3 in Vec2 format.

    -
    POINT_VEC3:GetVec3() -

    Return the coordinates of the POINT_VEC3 in Vec3 format.

    POINT_VEC3:GetZ()

    Return the z coordinate of the POINT_VEC3.

    -
    POINT_VEC3:IlluminationBomb() -

    Creates an illumination bomb at the point.

    -
    POINT_VEC3:IsMetric() -

    Gets if the POINT_VEC3 is metric or NM.

    -
    POINT_VEC3.Metric -
    POINT_VEC3.RoutePointAction -
    POINT_VEC3:RoutePointAir(AltType, Type, Action, Speed, SpeedLocked) -

    Build an air type route point.

    POINT_VEC3.RoutePointAltType -
    POINT_VEC3:RoutePointGround(Speed, Formation) -

    Build an ground type route point.

    POINT_VEC3.RoutePointType -
    POINT_VEC3:SetMetric(Metric) -

    Sets the POINT_VEC3 metric or NM.

    POINT_VEC3:SetZ(z)

    Set the z coordinate of the POINT_VEC3.

    -
    POINT_VEC3:Smoke(SmokeColor) -

    Smokes the point in a color.

    -
    POINT_VEC3:SmokeBlue() -

    Smoke the POINT_VEC3 Blue.

    POINT_VEC3.SmokeColor -
    POINT_VEC3:SmokeGreen() -

    Smoke the POINT_VEC3 Green.

    -
    POINT_VEC3:SmokeOrange() -

    Smoke the POINT_VEC3 Orange.

    -
    POINT_VEC3:SmokeRed() -

    Smoke the POINT_VEC3 Red.

    -
    POINT_VEC3:SmokeWhite() -

    Smoke the POINT_VEC3 White.

    -
    POINT_VEC3:ToStringBR(AngleRadians, Distance) -

    Provides a Bearing / Range string

    -
    POINT_VEC3:ToStringLL(AngleRadians, Distance, acc, DMS) -

    Provides a Bearing / Range string

    -
    POINT_VEC3:Translate(Distance, Angle) -

    Add a Distance in meters from the POINTVEC3 horizontal plane, with the given angle, and calculate the new POINTVEC3.

    POSITIONABLE +

    POSITIONABLE class, extends Identifiable#IDENTIFIABLE

    +

    The POSITIONABLE class is a wrapper class to handle the POSITIONABLE objects:

    + +
      +
    • Support all DCS APIs.
    • +
    @@ -126,27 +146,105 @@

    Type POSITIONABLE

    - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -177,6 +275,12 @@ + + + + @@ -201,6 +305,18 @@ + + + + + + + + @@ -213,6 +329,30 @@ + + + + + + + + + + + + + + + + @@ -240,7 +380,7 @@ - + @@ -255,6 +395,12 @@ + + + + @@ -264,9 +410,67 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    POSITIONABLE.ClassNamePOSITIONABLE:AddCargo(Cargo) - +

    Add cargo.

    +
    POSITIONABLE:CargoItemCount() +

    Get cargo item count.

    +
    POSITIONABLE:ClearCargo() +

    Clear all cargo.

    +
    POSITIONABLE:Flare(FlareColor) +

    Signal a flare at the position of the POSITIONABLE.

    +
    POSITIONABLE:FlareGreen() +

    Signal a green flare at the position of the POSITIONABLE.

    +
    POSITIONABLE:FlareRed() +

    Signal a red flare at the position of the POSITIONABLE.

    +
    POSITIONABLE:FlareWhite() +

    Signal a white flare at the position of the POSITIONABLE.

    +
    POSITIONABLE:FlareYellow() +

    Signal a yellow flare at the position of the POSITIONABLE.

    POSITIONABLE:GetAltitude()

    Returns the altitude of the POSITIONABLE.

    +
    POSITIONABLE:GetBeacon() +

    Create a Radio#BEACON, to allow this POSITIONABLE to broadcast beacon signals

    +
    POSITIONABLE:GetBoundingBox() +

    Get the bounding box of the underlying POSITIONABLE DCS Object.

    +
    POSITIONABLE:GetCoordinate() +

    Returns a COORDINATE object indicating the point in 3D of the POSITIONABLE within the mission.

    POSITIONABLE:GetHeading()

    Returns the POSITIONABLE heading in degrees.

    +
    POSITIONABLE:GetHeight() +

    Returns the POSITIONABLE height in meters.

    +
    POSITIONABLE:GetLaserCode() +

    Get the last assigned laser code

    POSITIONABLE:GetMessage(Message, Duration, Name)

    Returns a message with the callsign embedded (if there is one).

    +
    POSITIONABLE:GetMessageText(Message, Name) +

    Returns the message text with the callsign embedded (if there is one).

    POSITIONABLE:GetRandomVec3(Radius)

    Returns a random DCSTypes#Vec3 vector within a range, indicating the point in 3D of the POSITIONABLE within the mission.

    +
    POSITIONABLE:GetSpot() +

    Get the Spot

    POSITIONABLE:GetVelocityKMH()

    Returns the POSITIONABLE velocity in km/h.

    +
    POSITIONABLE:GetVelocityMPS() +

    Returns the POSITIONABLE velocity in meters per second.

    +
    POSITIONABLE:HasCargo(Cargo) +

    Returns if carrier has given cargo.

    POSITIONABLE:IsAboveRunway()

    Returns if the Positionable is located above a runway.

    +
    POSITIONABLE:IsLasing() +

    Check if the POSITIONABLE is lasing a target

    +
    POSITIONABLE:LaseOff() +

    Stop Lasing a POSITIONABLE

    +
    POSITIONABLE:LaseUnit(Target, LaserCode, Duration) +

    Start Lasing a POSITIONABLE

    +
    POSITIONABLE.LaserCode +
    POSITIONABLE:MessageToCoalition(Message, Duration, MessageCoalition, Name)POSITIONABLE:MessageToCoalition(Message, Duration, MessageCoalition)

    Send a message to a coalition.

    POSITIONABLE:MessageToRed(Message, Duration, Name)

    Send a message to the red coalition.

    +
    POSITIONABLE:MessageToSetGroup(Message, Duration, MessageSetGroup, Name) +

    Send a message to a Set#SET_GROUP.

    POSITIONABLE.PositionableNamePOSITIONABLE:RemoveCargo(Cargo) -

    The name of the measurable.

    +

    Remove cargo.

    +
    POSITIONABLE:Smoke(SmokeColor, Range, AddHeight) +

    Smoke the POSITIONABLE.

    +
    POSITIONABLE:SmokeBlue() +

    Smoke the POSITIONABLE Blue.

    +
    POSITIONABLE:SmokeGreen() +

    Smoke the POSITIONABLE Green.

    +
    POSITIONABLE:SmokeOrange() +

    Smoke the POSITIONABLE Orange.

    +
    POSITIONABLE:SmokeRed() +

    Smoke the POSITIONABLE Red.

    +
    POSITIONABLE:SmokeWhite() +

    Smoke the POSITIONABLE White.

    +
    POSITIONABLE.Spot + +
    POSITIONABLE.__ + +
    + +

    Type POSITIONABLE.__

    + + + +
    POSITIONABLE.__.Cargo +
    @@ -282,6 +486,38 @@
    +

    POSITIONABLE class, extends Identifiable#IDENTIFIABLE

    + +

    The POSITIONABLE class is a wrapper class to handle the POSITIONABLE objects:

    + +
      +
    • Support all DCS APIs.
    • +
    + + +
      +
    • Enhance with POSITIONABLE specific APIs not in the DCS API set.
    • +
    • Manage the "state" of the POSITIONABLE.
    • +
    + +

    POSITIONABLE constructor

    + +

    The POSITIONABLE class provides the following functions to construct a POSITIONABLE instance:

    + + + +

    Get the current speed

    + +

    There are 3 methods that can be used to determine the speed. +Use POSITIONABLE.GetVelocityKMH() to retrieve the current speed in km/h. Use POSITIONABLE.GetVelocityMPS() to retrieve the speed in meters per second. +The method POSITIONABLE.GetVelocity() returns the speed vector (a Vec3).

    + +

    Get the current altitude

    + +

    Altitude can be retrieved using the method POSITIONABLE.GetHeight() and returns the current altitude in meters from the orthonormal plane.

    +
    @@ -308,21 +544,134 @@

    Type POSITIONABLE

    - -

    The POSITIONABLE class

    - -

    Field(s)

    +

    Field(s)

    - #string - -POSITIONABLE.ClassName + +POSITIONABLE:AddCargo(Cargo)
    +

    Add cargo.

    +

    Parameter

    + +

    Return value

    + +

    #POSITIONABLE:

    + + +
    +
    +
    +
    + + +POSITIONABLE:CargoItemCount() + +
    +
    + +

    Get cargo item count.

    + +

    Return value

    + +

    Core.Cargo#CARGO: +Cargo

    + +
    +
    +
    +
    + + +POSITIONABLE:ClearCargo() + +
    +
    + +

    Clear all cargo.

    + +
    +
    +
    +
    + + +POSITIONABLE:Flare(FlareColor) + +
    +
    + +

    Signal a flare at the position of the POSITIONABLE.

    + +

    Parameter

    + +
    +
    +
    +
    + + +POSITIONABLE:FlareGreen() + +
    +
    + +

    Signal a green flare at the position of the POSITIONABLE.

    + +
    +
    +
    +
    + + +POSITIONABLE:FlareRed() + +
    +
    + +

    Signal a red flare at the position of the POSITIONABLE.

    + +
    +
    +
    +
    + + +POSITIONABLE:FlareWhite() + +
    +
    + +

    Signal a white flare at the position of the POSITIONABLE.

    + +
    +
    +
    +
    + + +POSITIONABLE:FlareYellow() + +
    +
    + +

    Signal a yellow flare at the position of the POSITIONABLE.

    @@ -357,6 +706,70 @@ The POSITIONABLE is not existing or alive.

    + +POSITIONABLE:GetBeacon() + +
    +
    + +

    Create a Radio#BEACON, to allow this POSITIONABLE to broadcast beacon signals

    + +

    Return value

    + +

    #RADIO: +Radio

    + +
    +
    +
    +
    + + +POSITIONABLE:GetBoundingBox() + +
    +
    + +

    Get the bounding box of the underlying POSITIONABLE DCS Object.

    + +

    Return values

    +
      +
    1. + +

      Dcs.DCSTypes#Distance: +The bounding box of the POSITIONABLE.

      + +
    2. +
    3. + +

      #nil: +The POSITIONABLE is not existing or alive.

      + +
    4. +
    +
    +
    +
    +
    + + +POSITIONABLE:GetCoordinate() + +
    +
    + +

    Returns a COORDINATE object indicating the point in 3D of the POSITIONABLE within the mission.

    + +

    Return value

    + +

    Core.Point#COORDINATE: +The COORDINATE of the POSITIONABLE.

    + +
    +
    +
    +
    + POSITIONABLE:GetHeading() @@ -365,11 +778,67 @@ The POSITIONABLE is not existing or alive.

    Returns the POSITIONABLE heading in degrees.

    -

    Return value

    +

    Return values

    +
      +
    1. #number: The POSTIONABLE heading

      +
    2. +
    3. + +

      #nil: +The POSITIONABLE is not existing or alive.

      + +
    4. +
    + +
    +
    +
    + + +POSITIONABLE:GetHeight() + +
    +
    + +

    Returns the POSITIONABLE height in meters.

    + +

    Return values

    +
      +
    1. + +

      Dcs.DCSTypes#Vec3: +The height of the positionable.

      + +
    2. +
    3. + +

      #nil: +The POSITIONABLE is not existing or alive.

      + +
    4. +
    +
    +
    +
    +
    + + +POSITIONABLE:GetLaserCode() + +
    +
    + +

    Get the last assigned laser code

    + +

    Return value

    + +

    #number: +The laser code

    +
    @@ -409,6 +878,39 @@ The duration of the message.

    Core.Message#MESSAGE:

    + +
    +
    +
    + + +POSITIONABLE:GetMessageText(Message, Name) + +
    +
    + +

    Returns the message text with the callsign embedded (if there is one).

    + +

    Parameters

    +
      +
    • + +

      #string Message : +The message text

      + +
    • +
    • + +

      #string Name : +(optional) The Name of the sender. If not provided, the Name is the type of the Positionable.

      + +
    • +
    +

    Return value

    + +

    #string: +The message text

    +
    @@ -531,7 +1033,7 @@ Radio

    • -

      Radius :

      +

      #number Radius :

    @@ -550,6 +1052,28 @@ The POSITIONABLE is not existing or alive.

    +

    Usage:

    +
    
    +-- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP
    + + +
    +
    +
    + + +POSITIONABLE:GetSpot() + +
    +
    + +

    Get the Spot

    + +

    Return value

    + +

    Core.Spot#SPOT: +The Spot

    +
    @@ -647,21 +1171,55 @@ The POSITIONABLE is not existing or alive.

    Returns the POSITIONABLE velocity in km/h.

    -

    Return values

    -
      -
    1. +

      Return value

      #number: The velocity in km/h

      -
    2. + +
    +
    +
    + + +POSITIONABLE:GetVelocityMPS() + +
    +
    + +

    Returns the POSITIONABLE velocity in meters per second.

    + +

    Return value

    + +

    #number: +The velocity in meters per second.

    + +
    +
    +
    +
    + + +POSITIONABLE:HasCargo(Cargo) + +
    +
    + +

    Returns if carrier has given cargo.

    + +

    Parameter

    +
    • -

      #nil: -The POSITIONABLE is not existing or alive.

      +

      Cargo :

    • - +
    +

    Return value

    + +

    Core.Cargo#CARGO: +Cargo

    +
    @@ -721,6 +1279,92 @@ The POSITIONABLE is not existing or alive.

    + +
    +
    +
    + + +POSITIONABLE:IsLasing() + +
    +
    + +

    Check if the POSITIONABLE is lasing a target

    + +

    Return value

    + +

    #boolean: +true if it is lasing a target

    + +
    +
    +
    +
    + + +POSITIONABLE:LaseOff() + +
    +
    + +

    Stop Lasing a POSITIONABLE

    + +

    Return value

    + +

    #POSITIONABLE:

    + + +
    +
    +
    +
    + + +POSITIONABLE:LaseUnit(Target, LaserCode, Duration) + +
    +
    + +

    Start Lasing a POSITIONABLE

    + +

    Parameters

    +
      +
    • + +

      #POSITIONABLE Target :

      + +
    • +
    • + +

      #number LaserCode :

      + +
    • +
    • + +

      #number Duration :

      + +
    • +
    +

    Return value

    + +

    Core.Spot#SPOT:

    + + +
    +
    +
    +
    + + + +POSITIONABLE.LaserCode + +
    +
    + + +
    @@ -881,7 +1525,7 @@ The client object receiving the message.

    -POSITIONABLE:MessageToCoalition(Message, Duration, MessageCoalition, Name) +POSITIONABLE:MessageToCoalition(Message, Duration, MessageCoalition)
    @@ -910,12 +1554,6 @@ The duration of the message.

    Dcs.DCScoalition#coalition MessageCoalition : The Coalition receiving the message.

    - -
  • - -

    #string Name : -(optional) The Name of the sender. If not provided, the Name is the type of the Positionable.

    -
  • @@ -1003,6 +1641,49 @@ The duration of the message.

    + +POSITIONABLE:MessageToSetGroup(Message, Duration, MessageSetGroup, Name) + +
    +
    + +

    Send a message to a Set#SET_GROUP.

    + + +

    The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.

    + +

    Parameters

    +
      +
    • + +

      #string Message : +The message text

      + +
    • +
    • + +

      Dcs.DCSTypes#Duration Duration : +The duration of the message.

      + +
    • +
    • + +

      Core.Set#SET_GROUP MessageSetGroup : +The SET_GROUP collection receiving the message.

      + +
    • +
    • + +

      #string Name : +(optional) The Name of the sender. If not provided, the Name is the type of the Positionable.

      + +
    • +
    +
    +
    +
    +
    + POSITIONABLE:New(PositionableName) @@ -1030,18 +1711,176 @@ self

    - #string - -POSITIONABLE.PositionableName + +POSITIONABLE:RemoveCargo(Cargo)
    -

    The name of the measurable.

    +

    Remove cargo.

    + +

    Parameter

    + +

    Return value

    + +

    #POSITIONABLE:

    + + +
    +
    +
    +
    + + +POSITIONABLE:Smoke(SmokeColor, Range, AddHeight) + +
    +
    + +

    Smoke the POSITIONABLE.

    + +

    Parameters

    +
      +
    • + +

      Utilities.Utils#SMOKECOLOR SmokeColor : +The color to smoke to positionable.

      + +
    • +
    • + +

      #number Range : +The range in meters to randomize the smoking around the positionable.

      + +
    • +
    • + +

      #number AddHeight : +The height in meters to add to the altitude of the positionable.

      + +
    • +
    +
    +
    +
    +
    + + +POSITIONABLE:SmokeBlue() + +
    +
    + +

    Smoke the POSITIONABLE Blue.

    + +
    +
    +
    +
    + + +POSITIONABLE:SmokeGreen() + +
    +
    + +

    Smoke the POSITIONABLE Green.

    + +
    +
    +
    +
    + + +POSITIONABLE:SmokeOrange() + +
    +
    + +

    Smoke the POSITIONABLE Orange.

    + +
    +
    +
    +
    + + +POSITIONABLE:SmokeRed() + +
    +
    + +

    Smoke the POSITIONABLE Red.

    + +
    +
    +
    +
    + + +POSITIONABLE:SmokeWhite() + +
    +
    + +

    Smoke the POSITIONABLE White.

    + +
    +
    +
    +
    + + Core.Spot#SPOT + +POSITIONABLE.Spot + +
    +
    + + + +
    +
    +
    +
    + + #POSITIONABLE.__ + +POSITIONABLE.__ + +
    +
    + +
    +

    Type POSITIONABLE.__

    +

    Field(s)

    +
    +
    + + #POSITIONABLE.__.Cargo + +POSITIONABLE.__.Cargo + +
    +
    + + + +
    +
    + +

    Type POSITIONABLE.__.Cargo

    +

    Type RADIO

    diff --git a/docs/Documentation/Process_JTAC.html b/docs/Documentation/Process_JTAC.html index 92c909e8f..666b88494 100644 --- a/docs/Documentation/Process_JTAC.html +++ b/docs/Documentation/Process_JTAC.html @@ -17,9 +17,16 @@ index diff --git a/docs/Documentation/Process_Pickup.html b/docs/Documentation/Process_Pickup.html index a031bed16..a74b64401 100644 --- a/docs/Documentation/Process_Pickup.html +++ b/docs/Documentation/Process_Pickup.html @@ -17,9 +17,16 @@ index diff --git a/docs/Documentation/Radio.html b/docs/Documentation/Radio.html index 3b47dc1f1..dd150fc26 100644 --- a/docs/Documentation/Radio.html +++ b/docs/Documentation/Radio.html @@ -17,9 +17,16 @@ index

    Module Radio

    -

    Core - The RADIO class is responsible for transmitting radio communications.

    +

    Core -- The RADIO Module is responsible for everything that is related to radio transmission and you can hear in DCS, be it TACAN beacons, Radio transmissions...

    -

    --- bitmap

    +

    Banner Image


    +

    The Radio contains 2 classes : RADIO and BEACON

    +

    What are radio communications in DCS ?

    SET_GROUP:AddInDatabase(Event)

    Handles the Database to check on an event (birth) that the Object was added in the Database.

    +
    SET_GROUP:AllCompletelyInZone(ZoneObject, Zone) +

    Iterate the SET_GROUP and return true if all the Wrapper.Group#GROUP are completely in the Core.Zone#ZONE

    +
    SET_GROUP:AnyCompletelyInZone(ZoneObject, Zone) +

    Iterate the SET_GROUP and return true if at least one of the Wrapper.Group#GROUP is completely inside the Core.Zone#ZONE

    +
    SET_GROUP:AnyInZone(ZoneObject, Zone) +

    Iterate the SET_GROUP and return true if at least one #UNIT of one GROUP of the SET_GROUP is in ZONE

    +
    SET_GROUP:AnyPartlyInZone(ZoneObject, Zone) +

    Iterate the SET_GROUP and return true if at least one GROUP of the SET_GROUP is partly in ZONE.

    +
    SET_GROUP:CountInZone(ZoneObject, Zone) +

    Iterate the SETGROUP and count how many GROUPs are completely in the Zone +That could easily be done with SETGROUP:ForEachGroupCompletelyInZone(), but this function +provides an easy to use shortcut...

    +
    SET_GROUP:CountUnitInZone(ZoneObject, Zone) +

    Iterate the SET_GROUP and count how many UNITs are completely in the Zone

    SET_GROUP:FilterCategories(Categories)

    Builds a set of groups out of categories.

    +
    SET_GROUP:FilterCategoryAirplane() +

    Builds a set of groups out of airplane category.

    +
    SET_GROUP:FilterCategoryGround() +

    Builds a set of groups out of ground category.

    +
    SET_GROUP:FilterCategoryHelicopter() +

    Builds a set of groups out of helicopter category.

    +
    SET_GROUP:FilterCategoryShip() +

    Builds a set of groups out of ship category.

    +
    SET_GROUP:FilterCategoryStructure() +

    Builds a set of groups out of structure category.

    SET_GROUP:FindInDatabase(Event)

    Handles the Database to check on any event that Object exists in the Database.

    +
    SET_GROUP:FindNearestGroupFromPointVec2(PointVec2) +

    Iterate the SET_GROUP while identifying the nearest object from a Point#POINT_VEC2.

    SET_GROUP:New()

    Creates a new SET_GROUP object, building a set of groups belonging to a coalitions, categories, countries, types or with defined prefix names.

    +
    SET_GROUP:NoneInZone(ZoneObject, Zone) +

    Iterate the SET_GROUP and return true if no GROUP of the SET_GROUP is in ZONE +This could also be achieved with not SET_GROUP:AnyPartlyInZone(Zone), but it's easier for the +mission designer to add a dedicated method

    SET_GROUP:RemoveGroupsByName(RemoveGroupNames)

    Remove GROUP(s) from SET_GROUP.

    +
    SET_GROUP:_EventOnDeadOrCrash(Event) +

    Handles the OnDead or OnCrash event for alive groups set.

    @@ -761,6 +1019,19 @@
    SET_UNIT:ForEachUnitNotInZone(ZoneObject, IteratorFunction, ...)

    Iterate the SET_UNIT and call an iterator function for each alive UNIT presence not in a Zone, providing the UNIT and optional parameters to the called function.

    +
    SET_UNIT:ForEachUnitPerThreatLevel(FromThreatLevel, ToThreatLevel, IteratorFunction, ...) +

    Iterate the SET_UNIT sorted *per Threat Level and call an interator function for each alive UNIT, providing the UNIT and optional parameters.

    + +
    SET_UNIT:GetFirst() +

    Get the first unit from the set.

    + + + + +
    SETTINGS +

    SETTINGS class, extends Base#BASE

    + +
    +

    Type SETTINGS

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SETTINGS:A2AMenuSystem(MenuGroup, RootMenu, A2ASystem) + +
    SETTINGS.A2ASystem + +
    SETTINGS:A2GMenuSystem(MenuGroup, RootMenu, A2GSystem) + +
    SETTINGS.A2GSystem + +
    SETTINGS:GetLL_DDM_Accuracy() +

    Gets the SETTINGS LL accuracy.

    +
    SETTINGS:GetMGRS_Accuracy() +

    Gets the SETTINGS MGRS accuracy.

    +
    SETTINGS:IsA2A_BRAA() +

    Is BRA

    +
    SETTINGS:IsA2A_BULLS() +

    Is BULLS

    +
    SETTINGS:IsA2A_LL_DDM() +

    Is LL DDM

    +
    SETTINGS:IsA2A_LL_DMS() +

    Is LL DMS

    +
    SETTINGS:IsA2A_MGRS() +

    Is MGRS

    +
    SETTINGS:IsA2G_BR() +

    Is BRA

    +
    SETTINGS:IsA2G_LL_DDM() +

    Is LL DDM

    +
    SETTINGS:IsA2G_LL_DMS() +

    Is LL DMS

    +
    SETTINGS:IsA2G_MGRS() +

    Is MGRS

    +
    SETTINGS:IsImperial() +

    Gets if the SETTINGS is imperial.

    +
    SETTINGS:IsMetric() +

    Gets if the SETTINGS is metric.

    +
    SETTINGS.LL_Accuracy + +
    SETTINGS.LL_DMS + +
    SETTINGS.MGRS_Accuracy + +
    SETTINGS:MenuGroupA2ASystem(PlayerUnit, PlayerGroup, PlayerName, A2ASystem) + +
    SETTINGS:MenuGroupA2GSystem(PlayerUnit, PlayerGroup, PlayerName, A2GSystem) + +
    SETTINGS:MenuGroupLL_DDM_AccuracySystem(PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy) + +
    SETTINGS:MenuGroupMGRS_AccuracySystem(PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy) + +
    SETTINGS:MenuGroupMWSystem(PlayerUnit, PlayerGroup, PlayerName, MW) + +
    SETTINGS:MenuLL_DDM_Accuracy(MenuGroup, RootMenu, LL_Accuracy) + +
    SETTINGS:MenuMGRS_Accuracy(MenuGroup, RootMenu, MGRS_Accuracy) + +
    SETTINGS:MenuMWSystem(MenuGroup, RootMenu, MW) + +
    SETTINGS.Metric + +
    SETTINGS.PlayerMenu + +
    SETTINGS:RemovePlayerMenu(RootMenu, PlayerUnit) + +
    SETTINGS:Set(PlayerName) +

    SETTINGS constructor.

    +
    SETTINGS:SetA2A_BRAA() +

    Sets A2A BRA

    +
    SETTINGS:SetA2A_BULLS() +

    Sets A2A BULLS

    +
    SETTINGS:SetA2A_LL_DDM() +

    Sets A2A LL DDM

    +
    SETTINGS:SetA2A_LL_DMS() +

    Sets A2A LL DMS

    +
    SETTINGS:SetA2A_MGRS() +

    Sets A2A MGRS

    +
    SETTINGS:SetA2G_BR() +

    Sets A2G BRA

    +
    SETTINGS:SetA2G_LL_DDM() +

    Sets A2G LL DDM

    +
    SETTINGS:SetA2G_LL_DMS() +

    Sets A2G LL DMS

    +
    SETTINGS:SetA2G_MGRS() +

    Sets A2G MGRS

    +
    SETTINGS:SetImperial() +

    Sets the SETTINGS imperial.

    +
    SETTINGS:SetLL_Accuracy(LL_Accuracy) +

    Sets the SETTINGS LL accuracy.

    +
    SETTINGS:SetMGRS_Accuracy(MGRS_Accuracy) +

    Sets the SETTINGS MGRS accuracy.

    +
    SETTINGS:SetMetric() +

    Sets the SETTINGS metric.

    +
    SETTINGS:SetPlayerMenu(RootMenu, PlayerUnit, MenuText) + +
    SETTINGS:SetSystemMenu(MenuGroup, RootMenu) + +
    + +

    Global(s)

    +
    +
    + + #SETTINGS + +SETTINGS + +
    +
    + +

    SETTINGS class, extends Base#BASE

    + + +
    +
    +

    Type Settings

    + +

    Type SETTINGS

    +

    Field(s)

    +
    +
    + + +SETTINGS:A2AMenuSystem(MenuGroup, RootMenu, A2ASystem) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      MenuGroup :

      + +
    • +
    • + +

      RootMenu :

      + +
    • +
    • + +

      A2ASystem :

      + +
    • +
    +
    +
    +
    +
    + + #string + +SETTINGS.A2ASystem + +
    +
    + + + +
    +
    +
    +
    + + +SETTINGS:A2GMenuSystem(MenuGroup, RootMenu, A2GSystem) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      MenuGroup :

      + +
    • +
    • + +

      RootMenu :

      + +
    • +
    • + +

      A2GSystem :

      + +
    • +
    +
    +
    +
    +
    + + #string + +SETTINGS.A2GSystem + +
    +
    + + + +
    +
    +
    +
    + + +SETTINGS:GetLL_DDM_Accuracy() + +
    +
    + +

    Gets the SETTINGS LL accuracy.

    + +

    Return value

    + +

    #number:

    + + +
    +
    +
    +
    + + +SETTINGS:GetMGRS_Accuracy() + +
    +
    + +

    Gets the SETTINGS MGRS accuracy.

    + +

    Return value

    + +

    #number:

    + + +
    +
    +
    +
    + + +SETTINGS:IsA2A_BRAA() + +
    +
    + +

    Is BRA

    + +

    Return value

    + +

    #boolean: +true if BRA

    + +
    +
    +
    +
    + + +SETTINGS:IsA2A_BULLS() + +
    +
    + +

    Is BULLS

    + +

    Return value

    + +

    #boolean: +true if BULLS

    + +
    +
    +
    +
    + + +SETTINGS:IsA2A_LL_DDM() + +
    +
    + +

    Is LL DDM

    + +

    Return value

    + +

    #boolean: +true if LL DDM

    + +
    +
    +
    +
    + + +SETTINGS:IsA2A_LL_DMS() + +
    +
    + +

    Is LL DMS

    + +

    Return value

    + +

    #boolean: +true if LL DMS

    + +
    +
    +
    +
    + + +SETTINGS:IsA2A_MGRS() + +
    +
    + +

    Is MGRS

    + +

    Return value

    + +

    #boolean: +true if MGRS

    + +
    +
    +
    +
    + + +SETTINGS:IsA2G_BR() + +
    +
    + +

    Is BRA

    + +

    Return value

    + +

    #boolean: +true if BRA

    + +
    +
    +
    +
    + + +SETTINGS:IsA2G_LL_DDM() + +
    +
    + +

    Is LL DDM

    + +

    Return value

    + +

    #boolean: +true if LL DDM

    + +
    +
    +
    +
    + + +SETTINGS:IsA2G_LL_DMS() + +
    +
    + +

    Is LL DMS

    + +

    Return value

    + +

    #boolean: +true if LL DMS

    + +
    +
    +
    +
    + + +SETTINGS:IsA2G_MGRS() + +
    +
    + +

    Is MGRS

    + +

    Return value

    + +

    #boolean: +true if MGRS

    + +
    +
    +
    +
    + + +SETTINGS:IsImperial() + +
    +
    + +

    Gets if the SETTINGS is imperial.

    + +

    Return value

    + +

    #boolean: +true if imperial.

    + +
    +
    +
    +
    + + +SETTINGS:IsMetric() + +
    +
    + +

    Gets if the SETTINGS is metric.

    + +

    Return value

    + +

    #boolean: +true if metric.

    + +
    +
    +
    +
    + + #number + +SETTINGS.LL_Accuracy + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +SETTINGS.LL_DMS + +
    +
    + + + +
    +
    +
    +
    + + #number + +SETTINGS.MGRS_Accuracy + +
    +
    + + + +
    +
    +
    +
    + + +SETTINGS:MenuGroupA2ASystem(PlayerUnit, PlayerGroup, PlayerName, A2ASystem) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      PlayerUnit :

      + +
    • +
    • + +

      PlayerGroup :

      + +
    • +
    • + +

      PlayerName :

      + +
    • +
    • + +

      A2ASystem :

      + +
    • +
    +
    +
    +
    +
    + + +SETTINGS:MenuGroupA2GSystem(PlayerUnit, PlayerGroup, PlayerName, A2GSystem) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      PlayerUnit :

      + +
    • +
    • + +

      PlayerGroup :

      + +
    • +
    • + +

      PlayerName :

      + +
    • +
    • + +

      A2GSystem :

      + +
    • +
    +
    +
    +
    +
    + + +SETTINGS:MenuGroupLL_DDM_AccuracySystem(PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      PlayerUnit :

      + +
    • +
    • + +

      PlayerGroup :

      + +
    • +
    • + +

      PlayerName :

      + +
    • +
    • + +

      LL_Accuracy :

      + +
    • +
    +
    +
    +
    +
    + + +SETTINGS:MenuGroupMGRS_AccuracySystem(PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      PlayerUnit :

      + +
    • +
    • + +

      PlayerGroup :

      + +
    • +
    • + +

      PlayerName :

      + +
    • +
    • + +

      MGRS_Accuracy :

      + +
    • +
    +
    +
    +
    +
    + + +SETTINGS:MenuGroupMWSystem(PlayerUnit, PlayerGroup, PlayerName, MW) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      PlayerUnit :

      + +
    • +
    • + +

      PlayerGroup :

      + +
    • +
    • + +

      PlayerName :

      + +
    • +
    • + +

      MW :

      + +
    • +
    +
    +
    +
    +
    + + +SETTINGS:MenuLL_DDM_Accuracy(MenuGroup, RootMenu, LL_Accuracy) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      MenuGroup :

      + +
    • +
    • + +

      RootMenu :

      + +
    • +
    • + +

      LL_Accuracy :

      + +
    • +
    +
    +
    +
    +
    + + +SETTINGS:MenuMGRS_Accuracy(MenuGroup, RootMenu, MGRS_Accuracy) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      MenuGroup :

      + +
    • +
    • + +

      RootMenu :

      + +
    • +
    • + +

      MGRS_Accuracy :

      + +
    • +
    +
    +
    +
    +
    + + +SETTINGS:MenuMWSystem(MenuGroup, RootMenu, MW) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      MenuGroup :

      + +
    • +
    • + +

      RootMenu :

      + +
    • +
    • + +

      MW :

      + +
    • +
    +
    +
    +
    +
    + + #boolean + +SETTINGS.Metric + +
    +
    + + + +
    +
    +
    +
    + + + +SETTINGS.PlayerMenu + +
    +
    + + + +
    +
    +
    +
    + + +SETTINGS:RemovePlayerMenu(RootMenu, PlayerUnit) + +
    +
    + + + +

    Parameters

    + +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:Set(PlayerName) + +
    +
    + +

    SETTINGS constructor.

    + +

    Parameter

    +
      +
    • + +

      PlayerName :

      + +
    • +
    +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetA2A_BRAA() + +
    +
    + +

    Sets A2A BRA

    + +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetA2A_BULLS() + +
    +
    + +

    Sets A2A BULLS

    + +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetA2A_LL_DDM() + +
    +
    + +

    Sets A2A LL DDM

    + +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetA2A_LL_DMS() + +
    +
    + +

    Sets A2A LL DMS

    + +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetA2A_MGRS() + +
    +
    + +

    Sets A2A MGRS

    + +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetA2G_BR() + +
    +
    + +

    Sets A2G BRA

    + +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetA2G_LL_DDM() + +
    +
    + +

    Sets A2G LL DDM

    + +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetA2G_LL_DMS() + +
    +
    + +

    Sets A2G LL DMS

    + +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetA2G_MGRS() + +
    +
    + +

    Sets A2G MGRS

    + +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetImperial() + +
    +
    + +

    Sets the SETTINGS imperial.

    + +
    +
    +
    +
    + + +SETTINGS:SetLL_Accuracy(LL_Accuracy) + +
    +
    + +

    Sets the SETTINGS LL accuracy.

    + +

    Parameter

    +
      +
    • + +

      #number LL_Accuracy :

      + +
    • +
    +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetMGRS_Accuracy(MGRS_Accuracy) + +
    +
    + +

    Sets the SETTINGS MGRS accuracy.

    + +

    Parameter

    +
      +
    • + +

      #number MGRS_Accuracy :

      + +
    • +
    +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetMetric() + +
    +
    + +

    Sets the SETTINGS metric.

    + +
    +
    +
    +
    + + +SETTINGS:SetPlayerMenu(RootMenu, PlayerUnit, MenuText) + +
    +
    + + + +

    Parameters

    + +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    +
    +
    + + +SETTINGS:SetSystemMenu(MenuGroup, RootMenu) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      MenuGroup :

      + +
    • +
    • + +

      RootMenu :

      + +
    • +
    +

    Return value

    + +

    #SETTINGS:

    + + +
    +
    + +
    + + + + diff --git a/docs/Documentation/Smoke.html b/docs/Documentation/Smoke.html index f91a5703f..230977c2e 100644 --- a/docs/Documentation/Smoke.html +++ b/docs/Documentation/Smoke.html @@ -17,9 +17,16 @@ index diff --git a/docs/Documentation/Spawn.html b/docs/Documentation/Spawn.html index 3c2379643..3c14d9de6 100644 --- a/docs/Documentation/Spawn.html +++ b/docs/Documentation/Spawn.html @@ -17,9 +17,16 @@ index

    Module Spawn

    -

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:All --
    -Spawn groups of units dynamically in your missions.

    - -

    Banner Image

    - -
    - -

    1) #SPAWN class, extends Base#BASE

    - -

    The #SPAWN class allows to spawn dynamically new groups, based on pre-defined initialization settings, modifying the behaviour when groups are spawned.

    +

    Functional -- Spawn dynamically new GROUPs in your missions.

    -

    For each group to be spawned, within the mission editor, a group has to be created with the "late activation flag" set. We call this group the "Spawn Template" of the SPAWN object. -A reference to this Spawn Template needs to be provided when constructing the SPAWN object, by indicating the name of the group within the mission editor in the constructor methods.

    - -

    Within the SPAWN object, there is an internal index that keeps track of which group from the internal group list was spawned. -When new groups get spawned by using the SPAWN methods (see below), it will be validated whether the Limits (SPAWN.Limit) of the SPAWN object are not reached. -When all is valid, a new group will be created by the spawning methods, and the internal index will be increased with 1.

    - -

    Regarding the name of new spawned groups, a SpawnPrefix will be assigned for each new group created. -If you want to have the Spawn Template name to be used as the SpawnPrefix name, use the SPAWN.New constructor. -However, when the SPAWN.NewWithAlias constructor was used, the Alias name will define the SpawnPrefix name. -Groups will follow the following naming structure when spawned at run-time:

    - -
      -
    1. Spawned groups will have the name SpawnPrefix#ggg, where ggg is a counter from 0 to 999.
    2. -
    3. Spawned units will have the name SpawnPrefix#ggg-uu, where uu is a counter from 0 to 99 for each new spawned unit belonging to the group.
    4. -
    - -

    Some additional notes that need to be remembered:

    - -
      -
    • Templates are actually groups defined within the mission editor, with the flag "Late Activation" set. As such, these groups are never used within the mission, but are used by the #SPAWN module.
    • -
    • It is important to defined BEFORE you spawn new groups, a proper initialization of the SPAWN instance is done with the options you want to use.
    • -
    • When designing a mission, NEVER name groups using a "#" within the name of the group Spawn Template(s), or the SPAWN module logic won't work anymore.
    • -
    - -

    1.1) SPAWN construction methods

    - -

    Create a new SPAWN object with the SPAWN.New() or the SPAWN.NewWithAlias() methods:

    - -
      -
    • SPAWN.New(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition).
    • -
    • SPAWN.NewWithAlias(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition), and gives each spawned Group an different name.
    • -
    - -

    It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned. -The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons. -So in principle, the group list will contain all parameters and configurations after initialization, and when groups get actually spawned, this spawning can be done quickly and efficient.

    - -

    1.2) SPAWN initialization methods

    - -

    A spawn object will behave differently based on the usage of initialization methods, which all start with the Init prefix:

    - - - -

    1.3) SPAWN spawning methods

    - -

    Groups can be spawned at different times and methods:

    - - - -

    Note that SPAWN.Spawn and SPAWN.ReSpawn return a GROUP#GROUP.New object, that contains a reference to the DCSGroup object. -You can use the GROUP object to do further actions with the DCSGroup.

    - -

    1.4) Retrieve alive GROUPs spawned by the SPAWN object

    - -

    The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution. -Every time a SPAWN object spawns a new GROUP object, a reference to the GROUP object is added to an internal table of GROUPS. -SPAWN provides methods to iterate through that internal GROUP object reference table:

    - -
      -
    • SPAWN.GetFirstAliveGroup(): Will find the first alive GROUP it has spawned, and return the alive GROUP object and the first Index where the first alive GROUP object has been found.
    • -
    • SPAWN.GetNextAliveGroup(): Will find the next alive GROUP object from a given Index, and return a reference to the alive GROUP object and the next Index where the alive GROUP has been found.
    • -
    • SPAWN.GetLastAliveGroup(): Will find the last alive GROUP object, and will return a reference to the last live GROUP object and the last Index where the last alive GROUP object has been found.
    • -
    - -

    You can use the methods SPAWN.GetFirstAliveGroup() and sequently SPAWN.GetNextAliveGroup() to iterate through the alive GROUPS within the SPAWN object, and to actions... See the respective methods for an example. -The method SPAWN.GetGroupFromIndex() will return the GROUP object reference from the given Index, dead or alive...

    - -

    1.5) SPAWN object cleaning

    - -

    Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive. -In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't, -and it may occur that no new groups are or can be spawned as limits are reached. -To prevent this, a SPAWN.InitCleanUp() initialization method has been defined that will silently monitor the status of each spawned group. -Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time. -There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"... -In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically. -This models AI that has succesfully returned to their airbase, to restart their combat activities. -Check the SPAWN.InitCleanUp() for further info.

    - -

    1.6) Catch the Group spawn event in a callback function!

    - -

    When using the SpawnScheduled method, new Groups are created following the schedule timing parameters. -When a new Group is spawned, you maybe want to execute actions with that group spawned at the spawn event. -To SPAWN class supports this functionality through the SPAWN.OnSpawnGroup( *function( SpawnedGroup ) end * ) method, which takes a function as a parameter that you can define locally. -Whenever a new Group is spawned, the given function is called, and the Group that was just spawned, is given as a parameter. -As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned Group object. -A coding example is provided at the description of the SPAWN.OnSpawnGroup( *function( SpawnedGroup ) end * ) method.

    +

    +Banner Image


    -

    API CHANGE HISTORY

    +

    The documentation of the SPAWN class can be found further in this document.

    -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    +
    -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    +

    Demo Missions

    -

    Hereby the change log:

    +

    SPAWN Demo Missions source code

    -

    2017-03-14: SPAWN:InitKeepUnitNames() added.
    -2017-03-14: SPAWN:InitRandomizePosition( RandomizePosition, OuterRadious, InnerRadius ) added.

    +

    SPAWN Demo Missions, only for beta testers

    -

    2017-02-04: SPAWN:InitUnControlled( UnControlled ) replaces SPAWN:InitUnControlled().

    +

    ALL Demo Missions pack of the last release

    -

    2017-01-24: SPAWN:InitAIOnOff( AIOnOff ) added.

    +
    -

    2017-01-24: SPAWN:InitAIOn() added.

    +

    YouTube Channel

    -

    2017-01-24: SPAWN:InitAIOff() added.

    - -

    2016-08-15: SPAWN:InitCleanUp( SpawnCleanUpInterval ) replaces SPAWN:CleanUp( SpawnCleanUpInterval ).

    - -

    2016-08-15: SPAWN:InitRandomizeZones( SpawnZones ) added.

    - -

    2016-08-14: SPAWN:OnSpawnGroup( SpawnCallBackFunction, ... ) replaces SPAWN:SpawnFunction( SpawnCallBackFunction, ... ).

    - -

    2016-08-14: SPAWN.SpawnInZone( Zone, RandomizeGroup, SpawnIndex ) replaces SpawnInZone( Zone, RandomizeUnits, OuterRadius, InnerRadius, SpawnIndex ).

    - -

    2016-08-14: SPAWN.SpawnFromVec3( Vec3, SpawnIndex ) replaces SpawnFromVec3( Vec3, RandomizeUnits, OuterRadius, InnerRadius, SpawnIndex ):

    - -

    2016-08-14: SPAWN.SpawnFromVec2( Vec2, SpawnIndex ) replaces SpawnFromVec2( Vec2, RandomizeUnits, OuterRadius, InnerRadius, SpawnIndex ):

    - -

    2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromUnit( SpawnUnit, RandomizeUnits, OuterRadius, InnerRadius, SpawnIndex ):

    - -

    2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromStatic( SpawnStatic, RandomizeUnits, OuterRadius, InnerRadius, SpawnIndex ):

    - -

    2016-08-14: SPAWN.InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius ) added:

    - -

    2016-08-14: SPAWN.InitLimit( SpawnMaxUnitsAlive, SpawnMaxGroups ) replaces SPAWN.Limit( SpawnMaxUnitsAlive, SpawnMaxGroups ):

    - -

    2016-08-14: SPAWN.InitArray( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) replaces SPAWN.Array( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ).

    - -

    2016-08-14: SPAWN.InitRandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) replaces SPAWN.RandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ).

    - -

    2016-08-14: SPAWN.InitRandomizeTemplate( SpawnTemplatePrefixTable ) replaces SPAWN.RandomizeTemplate( SpawnTemplatePrefixTable ).

    - -

    2016-08-14: SPAWN.InitUnControlled() replaces SPAWN.UnControlled().

    +

    SPAWN YouTube Channel


    @@ -272,7 +154,9 @@ A coding example is provided at the description of the SPAWN
    +

    SPAWN class, extends Base#BASE

    +

    The SPAWN class allows to spawn dynamically new groups.

    @@ -300,6 +184,18 @@ A coding example is provided at the description of the SPAWN.CleanUpScheduler
    +
    SPAWN.DelayOnOff + +
    SPAWN:GetCoordinate() +

    Get the Coordinate of the Group that is Late Activated as the template for the SPAWN object.

    Get the index from a given group.

    +
    SPAWN.Grouping +

    CleanUp groups when they are still alive, but inactive.

    +
    SPAWN:InitDelayOff() +

    Turns the Delay Off for the Group when spawning.

    +
    SPAWN:InitDelayOn() +

    Turns the Delay On for the Group when spawning.

    +
    SPAWN:InitDelayOnOff(DelayOnOff) + +
    SPAWN:InitGrouping(Grouping) +

    When spawning a new group, make the grouping of the units according the InitGrouping setting.

    SPAWN.SpawnAliasPrefix +
    SPAWN:SpawnAtAirbase(Airbase, Takeoff, TakeoffAltitude) +

    Will spawn a group at an airbase.

    SPAWN:SpawnGroupName(SpawnIndex)

    Will return the SpawnGroupName either with with a specific count number or without any count.

    +
    SPAWN.SpawnGrouping +
    SPAWN.SpawnGroups +
    SPAWN.SpawnHookScheduler +
    SPAWN.SpawnZoneTable +
    SPAWN.Takeoff +
    SPAWN:_TranslateRotate(SpawnIndex, SpawnRootX, SpawnRootY, SpawnX, SpawnY, SpawnAngle) +
    + +

    Type SPAWN.Takeoff

    + + + +
    SPAWN.Takeoff.type +
    @@ -887,6 +847,246 @@ and any spaces before and after the resulting name are removed.

    +

    SPAWN class, extends Base#BASE

    + +

    The SPAWN class allows to spawn dynamically new groups.

    + + +

    Each SPAWN object needs to be have a related template group setup in the Mission Editor (ME), +which is a normal group with the Late Activation flag set. +This template group will never be activated in your mission.
    +SPAWN uses that template group to reference to all the characteristics +(air, ground, livery, unit composition, formation, skill level etc) of each new group to be spawned.

    + +

    Therefore, when creating a SPAWN object, the SPAWN.New and SPAWN.NewWithAlias require +the name of the template group to be given as a string to those constructor methods.

    + +

    Initialization settings can be applied on the SPAWN object, +which modify the behaviour or the way groups are spawned. +These initialization methods have the prefix Init. +There are also spawn methods with the prefix Spawn and will spawn new groups in various ways.

    + +

    IMPORTANT! The methods with prefix Init must be used before any methods with prefix Spawn method are used, or unexpected results may appear!!!

    + +

    Because SPAWN can spawn multiple groups of a template group, +SPAWN has an internal index that keeps track +which was the latest group that was spawned.

    + +

    Limits can be set on how many groups can be spawn in each SPAWN object, +using the method SPAWN.InitLimit. SPAWN has 2 kind of limits:

    + +
      +
    • The maximum amount of Units that can be alive at the same time...
    • +
    • The maximum amount of Groups that can be spawned... This is more of a resource-type of limit.
    • +
    + +

    When new groups get spawned using the Spawn methods, +it will be evaluated whether any limits have been reached. +When no spawn limit is reached, a new group will be created by the spawning methods, +and the internal index will be increased with 1.

    + +

    These limits ensure that your mission does not accidentally get flooded with spawned groups.
    +Additionally, it also guarantees that independent of the group composition, +at any time, the most optimal amount of groups are alive in your mission. +For example, if your template group has a group composition of 10 units, and you specify a limit of 100 units alive at the same time, +with unlimited resources = :InitLimit( 100, 0 ) and 10 groups are alive, but two groups have only one unit alive in the group, +then a sequent Spawn(Scheduled) will allow a new group to be spawned!!!

    + +

    IMPORTANT!! If a limit has been reached, it is possible that a Spawn method returns nil, meaning, no Group had been spawned!!!

    + +

    Spawned groups get the same name as the name of the template group.
    +Spawned units in those groups keep by default the same name as the name of the template group.
    +However, because multiple groups and units are created from the template group, +a suffix is added to each spawned group and unit.

    + +

    Newly spawned groups will get the following naming structure at run-time:

    + +
      +
    1. Spawned groups will have the name _GroupName#nnn_, where GroupName is the name of the template group, + and nnn is a counter from 0 to 999.
    2. +
    3. Spawned units will have the name _GroupName#nnn-uu_, + where uu is a counter from 0 to 99 for each new spawned unit belonging to the group.
    4. +
    + +

    That being said, there is a way to keep the same unit names!
    +The method SPAWN.InitKeepUnitNames() will keep the same unit names as defined within the template group, thus:

    + +
      +
    1. Spawned units will have the name _UnitName#nnn-uu_, + where UnitName is the unit name as defined in the template group*, + and uu is a **counter from 0 to 99 for each new spawned unit belonging to the group.
    2. +
    + +

    Some additional notes that need to be considered!!:

    + +
      +
    • templates are actually groups defined within the mission editor, with the flag "Late Activation" set. + As such, these groups are never used within the mission, but are used by the #SPAWN module.
    • +
    • It is important to defined BEFORE you spawn new groups, + a proper initialization of the SPAWN instance is done with the options you want to use.
    • +
    • When designing a mission, NEVER name groups using a "#" within the name of the group Spawn template(s), + or the SPAWN module logic won't work anymore.
    • +
    + +

    SPAWN construction methods

    + +

    Create a new SPAWN object with the SPAWN.New() or the SPAWN.NewWithAlias() methods:

    + +
      +
    • SPAWN.New(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition).
    • +
    • SPAWN.NewWithAlias(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition), and gives each spawned Group an different name.
    • +
    + +

    It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned. +The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons. +So in principle, the group list will contain all parameters and configurations after initialization, and when groups get actually spawned, this spawning can be done quickly and efficient.

    + +

    SPAWN Initialization methods

    + +

    A spawn object will behave differently based on the usage of initialization methods, which all start with the Init prefix:

    + +

    Unit Names

    + +
      +
    • SPAWN.InitKeepUnitNames(): Keeps the unit names as defined within the mission editor, but note that anything after a # mark is ignored, and any spaces before and after the resulting name are removed. IMPORTANT! This method MUST be the first used after :New !!!
    • +
    + +

    Route randomization

    + + + +

    Group composition randomization

    + +
      +
    • SPAWN.InitRandomizeTemplate(): Randomize the group templates so that when a new group is spawned, a random group template is selected from one of the templates defined.
    • +
    + +

    Uncontrolled

    + + + +

    Array formation

    + +
      +
    • SPAWN.InitArray(): Make groups visible before they are actually activated, and order these groups like a batallion in an array.
    • +
    + +

    Position randomization

    + +
      +
    • SPAWN.InitRandomizePosition(): Randomizes the position of Groups that are spawned within a radius band, given an Outer and Inner radius, from the point that the spawn happens.
    • +
    • SPAWN.InitRandomizeUnits(): Randomizes the Units in the Group that is spawned within a radius band, given an Outer and Inner radius.
    • +
    • SPAWN.InitRandomizeZones(): Randomizes the spawning between a predefined list of Zones that are declared using this function. Each zone can be given a probability factor.
    • +
    + +

    Enable / Disable AI when spawning a new Group

    + + + +

    Limit scheduled spawning

    + +
      +
    • SPAWN.InitLimit(): Limits the amount of groups that can be alive at the same time and that can be dynamically spawned.
    • +
    + +

    Delay initial scheduled spawn

    + + + +

    Repeat spawned Groups upon landing

    + + + + +

    SPAWN Spawn methods

    + +

    Groups can be spawned at different times and methods:

    + +

    Single spawning methods

    + + + +

    Note that SPAWN.Spawn and SPAWN.ReSpawn return a GROUP#GROUP.New object, that contains a reference to the DCSGroup object. +You can use the GROUP object to do further actions with the DCSGroup.

    + +

    Scheduled spawning methods

    + + + + + +

    Retrieve alive GROUPs spawned by the SPAWN object

    + +

    The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution. +Every time a SPAWN object spawns a new GROUP object, a reference to the GROUP object is added to an internal table of GROUPS. +SPAWN provides methods to iterate through that internal GROUP object reference table:

    + +
      +
    • SPAWN.GetFirstAliveGroup(): Will find the first alive GROUP it has spawned, and return the alive GROUP object and the first Index where the first alive GROUP object has been found.
    • +
    • SPAWN.GetNextAliveGroup(): Will find the next alive GROUP object from a given Index, and return a reference to the alive GROUP object and the next Index where the alive GROUP has been found.
    • +
    • SPAWN.GetLastAliveGroup(): Will find the last alive GROUP object, and will return a reference to the last live GROUP object and the last Index where the last alive GROUP object has been found.
    • +
    + +

    You can use the methods SPAWN.GetFirstAliveGroup() and sequently SPAWN.GetNextAliveGroup() to iterate through the alive GROUPS within the SPAWN object, and to actions... See the respective methods for an example. +The method SPAWN.GetGroupFromIndex() will return the GROUP object reference from the given Index, dead or alive...

    + +

    Spawned cleaning of inactive groups

    + +

    Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive. +In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't, +and it may occur that no new groups are or can be spawned as limits are reached. +To prevent this, a SPAWN.InitCleanUp() initialization method has been defined that will silently monitor the status of each spawned group. +Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time. +There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"... +In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically. +This models AI that has succesfully returned to their airbase, to restart their combat activities. +Check the SPAWN.InitCleanUp() for further info.

    + +

    Catch the Group Spawn Event in a callback function!

    + +

    When using the SPAWN.SpawnScheduleds are created following the spawn time interval parameters. +When a new Group is spawned, you maybe want to execute actions with that group spawned at the spawn event. +The SPAWN class supports this functionality through the method SPAWN.OnSpawnGroup( *function( SpawnedGroup ) end * ), +which takes a function as a parameter that you can define locally. +Whenever a new Group is spawned, the given function is called, and the Group that was just spawned, is given as a parameter. +As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned Group object. +A coding example is provided at the description of the SPAWN.OnSpawnGroup( *function( SpawnedGroup ) end * ) method.

    + +

    Delay the initial spawning

    + +

    When using the SPAWN.SpawnScheduled +immediately when :SpawnScheduled() is initiated. The methods SPAWN.InitDelayOnOff() and SPAWN.InitDelayOn() can be used to +activate a delay before the first Group is spawned. For completeness, a method SPAWN.InitDelayOff() is also available, that +can be used to switch off the initial delay. Because there is no delay by default, this method would only be used when a +SPAWN.SpawnScheduledStop() ; SPAWN.SpawnScheduledStart() sequence would have been used.

    +
    @@ -957,6 +1157,41 @@ and any spaces before and after the resulting name are removed.

    self.CleanUpFunction = routines.scheduleFunction( self._SpawnCleanUpScheduler, { self }, timer.getTime() + 1, SpawnCleanUpInterval )

    + + +
    +
    + + #boolean + +SPAWN.DelayOnOff + +
    +
    + + + + +

    No intial delay when spawning the first group.

    + +
    +
    +
    +
    + + +SPAWN:GetCoordinate() + +
    +
    + +

    Get the Coordinate of the Group that is Late Activated as the template for the SPAWN object.

    + +

    Return value

    + +

    Core.Point#COORDINATE: +The Coordinate

    +
    @@ -1133,6 +1368,22 @@ end
    + +SPAWN.Grouping + +
    +
    + + + + +

    No grouping

    + +
    +
    +
    +
    + SPAWN:InitAIOff() @@ -1274,6 +1525,90 @@ self

    Usage:

    Spawn_Helicopter:CleanUp( 20 )  -- CleanUp the spawning of the helicopters every 20 seconds when they become inactive.
    + +
    +
    +
    + + +SPAWN:InitDelayOff() + +
    +
    + +

    Turns the Delay Off for the Group when spawning.

    + +

    Return value

    + +

    #SPAWN: +The SPAWN object

    + +
    +
    +
    +
    + + +SPAWN:InitDelayOn() + +
    +
    + +

    Turns the Delay On for the Group when spawning.

    + +

    Return value

    + +

    #SPAWN: +The SPAWN object

    + +
    +
    +
    +
    + + +SPAWN:InitDelayOnOff(DelayOnOff) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      DelayOnOff :

      + +
    • +
    +
    +
    +
    +
    + + +SPAWN:InitGrouping(Grouping) + +
    +
    + +

    When spawning a new group, make the grouping of the units according the InitGrouping setting.

    + +

    Parameter

    +
      +
    • + +

      #number Grouping : +Indicates the maximum amount of units in the group.

      + +
    • +
    +

    Return value

    + +

    #SPAWN:

    + +
    @@ -1859,6 +2194,9 @@ The group that was spawned. You can use this group for further actions.

    + +

    Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.

    +
    @@ -1922,6 +2260,60 @@ The group that was spawned. You can use this group for further actions.

    + +
    +
    +
    + + +SPAWN:SpawnAtAirbase(Airbase, Takeoff, TakeoffAltitude) + +
    +
    + +

    Will spawn a group at an airbase.

    + + +

    This method is mostly advisable to be used if you want to simulate spawning units at an airbase. +Note that each point in the route assigned to the spawning group is reset to the point of the spawn. +You can use the returned group to further define the route to be followed.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Airbase#AIRBASE Airbase : +The Airbase where to spawn the group.

      + +
    • +
    • + +

      #SPAWN.Takeoff Takeoff : +(optional) The location and takeoff method. Default is Hot.

      + +
    • +
    • + +

      #number TakeoffAltitude : +(optional) The altitude above the ground.

      + +
    • +
    +

    Return values

    +
      +
    1. + +

      Wrapper.Group#GROUP: +that was spawned.

      + +
    2. +
    3. + +

      #nil: +Nothing was spawned.

      + +
    4. +
    @@ -2213,6 +2605,20 @@ Is the number of the Group that is to be spawned.

    #string: SpawnGroupName

    + +
    +
    +
    + + + +SPAWN.SpawnGrouping + +
    +
    + + +
    @@ -2230,6 +2636,23 @@ SpawnGroupName

    Array containing the descriptions of each Group to be Spawned.

    + +
    +
    +
    + + + +SPAWN.SpawnHookScheduler + +
    +
    + + + + +

    delay calling this for .1 seconds so that it hopefully comes after the BIRTH event of the group.

    +
    @@ -2572,6 +2995,11 @@ when nothing was spawned.

    Note: This method is only required to be called when the schedule was stopped.

    +

    Return value

    + +

    #SPAWN:

    + +
    @@ -2585,6 +3013,11 @@ when nothing was spawned.

    Will stop the scheduled spawning scheduler.

    +

    Return value

    + +

    #SPAWN:

    + +
    @@ -2696,7 +3129,7 @@ Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):Schedule( 600, 0.5 )
    - + #boolean SPAWN.SpawnUnControlled @@ -2720,7 +3153,7 @@ Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):Schedule( 600, 0.5 ) -

    When the first Spawn executes, all the Groups need to be made visible before start.

    +

    Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.

    @@ -2766,6 +3199,20 @@ The group that was spawned. You can use this group for further actions.

    + +
    +
    +
    + + #SPAWN.Takeoff + +SPAWN.Takeoff + +
    +
    + + +
    @@ -3291,6 +3738,25 @@ True = Continue Scheduler

    Type SPAWN.SpawnZoneTable

    +

    Type SPAWN.Takeoff

    + +

    Enumerator for spawns at airbases

    + +

    Field(s)

    +
    +
    + + +SPAWN.Takeoff.type + +
    +
    + + + +
    +
    + diff --git a/docs/Documentation/SpawnStatic.html b/docs/Documentation/SpawnStatic.html new file mode 100644 index 000000000..bc91b9624 --- /dev/null +++ b/docs/Documentation/SpawnStatic.html @@ -0,0 +1,484 @@ + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +

    Module SpawnStatic

    + +

    Core -- Spawn dynamically new STATICs in your missions.

    + + +

    +Banner Image

    + +
    + +

    SPAWNSTATIC spawns static structures in your missions dynamically. See below the SPAWNSTATIC class documentation.

    + +
    + +

    Demo Missions

    + +

    SPAWNSTATIC Demo Missions source code

    + +

    SPAWNSTATIC Demo Missions, only for beta testers

    + +

    ALL Demo Missions pack of the last release

    + +
    + +

    YouTube Channel

    + +

    SPAWNSTATIC YouTube Channel

    + +
    + +

    Author: Sven Van de Velde (FlightControl)

    +

    Contributions:

    + +
    + + +

    Global(s)

    + + + + + +
    SPAWNSTATIC +

    SPAWNSTATIC class, extends Base#BASE

    + +

    The SPAWNSTATIC class allows to spawn dynamically new Statics.

    +
    +

    Type SPAWNSTATIC

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SPAWNSTATIC.CountryID + +
    SPAWNSTATIC:NewFromStatic(SpawnTemplatePrefix, CountryID) +

    Creates the main object to spawn a Static defined in the ME.

    +
    SPAWNSTATIC:NewFromType(SpawnTypeName, SpawnShapeName, SpawnCategory, CountryID) +

    Creates the main object to spawn a Static based on a type name.

    +
    SPAWNSTATIC:SpawnFromPointVec2(PointVec2, Heading, (, NewName) +

    Creates a new Static from a POINT_VEC2.

    +
    SPAWNSTATIC:SpawnFromZone(Zone, Heading, (, NewName) +

    Creates a new Static from a Zone.

    +
    SPAWNSTATIC.SpawnIndex + +
    SPAWNSTATIC.SpawnTemplatePrefix + +
    SPAWNSTATIC.SpawnTypeName + +
    + +

    Global(s)

    +
    +
    + + #SPAWNSTATIC + +SPAWNSTATIC + +
    +
    + +

    SPAWNSTATIC class, extends Base#BASE

    + +

    The SPAWNSTATIC class allows to spawn dynamically new Statics.

    + + +

    Through creating a copy of an existing static object template as defined in the Mission Editor (ME), +SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy" +these properties to create a new static object and place it at the desired coordinate.

    + +

    New spawned Statics get the same name as the name of the template Static, +or gets the given name when a new name is provided at the Spawn method.
    +By default, spawned Statics will follow a naming convention at run-time:

    + +
      +
    • Spawned Statics will have the name _StaticName#nnn_, where StaticName is the name of the Template Static, + and nnn is a counter from 0 to 99999.
    • +
    + + +

    SPAWNSTATIC construction methods

    + +

    Create a new SPAWNSTATIC object with the SPAWNSTATIC.NewFromStatic():

    + +
      +
    • SPAWNSTATIC.NewFromStatic(): Creates a new SPAWNSTATIC object given a name that is used as the base of the naming of each spawned Static.
    • +
    + +

    Spawn methods

    + +

    Groups can be spawned at different times and methods:

    + + + +
    +
    +

    Type SpawnStatic

    + +

    Type SPAWNSTATIC

    +

    Field(s)

    +
    +
    + + + +SPAWNSTATIC.CountryID + +
    +
    + + + +
    +
    +
    +
    + + +SPAWNSTATIC:NewFromStatic(SpawnTemplatePrefix, CountryID) + +
    +
    + +

    Creates the main object to spawn a Static defined in the ME.

    + +

    Parameters

    +
      +
    • + +

      #string SpawnTemplatePrefix : +is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix.

      + +
    • +
    • + +

      CountryID :

      + +
    • +
    +

    Return value

    + +

    #SPAWNSTATIC:

    + + +
    +
    +
    +
    + + +SPAWNSTATIC:NewFromType(SpawnTypeName, SpawnShapeName, SpawnCategory, CountryID) + +
    +
    + +

    Creates the main object to spawn a Static based on a type name.

    + +

    Parameters

    +
      +
    • + +

      #string SpawnTypeName : +is the name of the type.

      + +
    • +
    • + +

      SpawnShapeName :

      + +
    • +
    • + +

      SpawnCategory :

      + +
    • +
    • + +

      CountryID :

      + +
    • +
    +

    Return value

    + +

    #SPAWNSTATIC:

    + + +
    +
    +
    +
    + + +SPAWNSTATIC:SpawnFromPointVec2(PointVec2, Heading, (, NewName) + +
    +
    + +

    Creates a new Static from a POINT_VEC2.

    + +

    Parameters

    +
      +
    • + +

      Core.Point#POINT_VEC2 PointVec2 : +The 2D coordinate where to spawn the static.

      + +
    • +
    • + +

      #number Heading : +The heading of the static, which is a number in degrees from 0 to 360.

      + +
    • +
    • + +

      #string ( : +ptional) The name of the new static.

      + +
    • +
    • + +

      NewName :

      + +
    • +
    +

    Return value

    + +

    #SPAWNSTATIC:

    + + +
    +
    +
    +
    + + +SPAWNSTATIC:SpawnFromZone(Zone, Heading, (, NewName) + +
    +
    + +

    Creates a new Static from a Zone.

    + +

    Parameters

    +
      +
    • + +

      Core.Zone#ZONE_BASE Zone : +The Zone where to spawn the static.

      + +
    • +
    • + +

      #number Heading : +The heading of the static, which is a number in degrees from 0 to 360.

      + +
    • +
    • + +

      #string ( : +ptional) The name of the new static.

      + +
    • +
    • + +

      NewName :

      + +
    • +
    +

    Return value

    + +

    #SPAWNSTATIC:

    + + +
    +
    +
    +
    + + +SPAWNSTATIC.SpawnIndex + +
    +
    + + + +
    +
    +
    +
    + + + +SPAWNSTATIC.SpawnTemplatePrefix + +
    +
    + + + +
    +
    +
    +
    + + + +SPAWNSTATIC.SpawnTypeName + +
    +
    + + + +
    +
    + +

    Type SPAWNSTATIC.SpawnZoneTable

    + +
    + +
    + + diff --git a/docs/Documentation/Spot.html b/docs/Documentation/Spot.html new file mode 100644 index 000000000..5fdc3b305 --- /dev/null +++ b/docs/Documentation/Spot.html @@ -0,0 +1,1002 @@ + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +

    Module Spot

    + +

    Core -- Management of SPOT logistics, that can be transported from and to transportation carriers.

    + + + +

    Banner Image

    + +
    + +

    SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to:

    + +
      +
    • Spot for a defined duration.
    • +
    • wiggle the spot at the target.
    • +
    • Provide a Unit as a target, instead of a point.
    • +
    • Implement a status machine, LaseOn, LaseOff.
    • +
    + +
    + +

    Demo Missions

    + +

    SPOT Demo Missions source code

    + +

    SPOT Demo Missions, only for beta testers

    + +

    ALL Demo Missions pack of the last release

    + +
    + +

    YouTube Channel

    + +

    SPOT YouTube Channel

    + +
    + +

    Author: Sven Van de Velde (FlightControl)

    +

    Contributions:

    + +
      +
    • Ciribob: Showing the way how to lase targets + how laser codes work!!! Explained the autolase script.
    • +
    • EasyEB: Ideas and Beta Testing
    • +
    • Wingthor: Beta Testing
    • +
    + +
    + + +

    Global(s)

    + + + + + +
    SPOT +

    SPOT class, extends Fsm#FSM

    + +

    SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to:

    + +
      +
    • Mark targets for a defined duration.
    • +
    +
    +

    Type SPOT

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SPOT:Destroyed() +

    Destroyed Trigger for SPOT

    +
    SPOT:IsLasing() +

    Check if the SPOT is lasing

    +
    SPOT:LaseOff() +

    LaseOff Trigger for SPOT

    +
    SPOT:LaseOn() +

    LaseOn Trigger for SPOT

    +
    SPOT.LaseScheduler + +
    SPOT.LaserCode + +
    SPOT.Lasing + +
    SPOT:New(Recce, LaserCode, Duration) +

    SPOT Constructor.

    +
    SPOT:OnAfterDestroyed(From, Event, To) +

    Destroyed Handler OnAfter for SPOT

    +
    SPOT:OnAfterLaseOff(From, Event, To) +

    LaseOff Handler OnAfter for SPOT

    +
    SPOT:OnAfterLaseOn(From, Event, To) +

    LaseOn Handler OnAfter for SPOT

    +
    SPOT:OnBeforeDestroyed(From, Event, To) +

    Destroyed Handler OnBefore for SPOT

    +
    SPOT:OnBeforeLaseOff(From, Event, To) +

    LaseOff Handler OnBefore for SPOT

    +
    SPOT:OnBeforeLaseOn(From, Event, To) +

    LaseOn Handler OnBefore for SPOT

    +
    SPOT:OnEventDead(EventData) + +
    SPOT.Recce + +
    SPOT.ScheduleID + +
    SPOT.SpotIR + +
    SPOT.SpotLaser + +
    SPOT.Target + +
    SPOT:__Destroyed(Delay) +

    Destroyed Asynchronous Trigger for SPOT

    +
    SPOT:__LaseOff(Delay) +

    LaseOff Asynchronous Trigger for SPOT

    +
    SPOT:__LaseOn(Delay) +

    LaseOn Asynchronous Trigger for SPOT

    +
    SPOT:onafterLaseOff(From, Event, To) + +
    SPOT:onafterLaseOn(From, Event, To, Target, LaserCode, Duration) + +
    SPOT:onafterLasing(From, Event, To) + +
    + +

    Global(s)

    +
    +
    + + #SPOT + +SPOT + +
    +
    + +

    SPOT class, extends Fsm#FSM

    + +

    SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to:

    + +
      +
    • Mark targets for a defined duration.
    • +
    + + +
      +
    • wiggle the spot at the target.
    • +
    • Provide a Unit as a target, instead of a point.
    • +
    • Implement a status machine, LaseOn, LaseOff.
    • +
    + +

    1. SPOT constructor

    + +
      +
    • SPOT.New(..\Presentations\SPOT\Dia2.JPG): Creates a new SPOT object.
    • +
    + +

    2. SPOT is a FSM

    + +

    Process

    + +

    2.1 SPOT States

    + +
      +
    • Off: Lasing is switched off.
    • +
    • On: Lasing is switched on.
    • +
    • Destroyed: Target is destroyed.
    • +
    + +

    2.2 SPOT Events

    + + + +

    3. Check if a Target is being lased

    + +

    The method SPOT.IsLasing() indicates whether lasing is on or off.

    + + +
    +
    +

    Type Spot

    + +

    Type SPOT

    +

    Field(s)

    +
    +
    + + +SPOT:Destroyed() + +
    +
    + +

    Destroyed Trigger for SPOT

    + +
    +
    +
    +
    + + +SPOT:IsLasing() + +
    +
    + +

    Check if the SPOT is lasing

    + +

    Return value

    + +

    #boolean: +true if it is lasing

    + +
    +
    +
    +
    + + +SPOT:LaseOff() + +
    +
    + +

    LaseOff Trigger for SPOT

    + +
    +
    +
    +
    + + +SPOT:LaseOn() + +
    +
    + +

    LaseOn Trigger for SPOT

    + +
    +
    +
    +
    + + + +SPOT.LaseScheduler + +
    +
    + + + +
    +
    +
    +
    + + + +SPOT.LaserCode + +
    +
    + + + +
    +
    +
    +
    + + #boolean + +SPOT.Lasing + +
    +
    + + + +
    +
    +
    +
    + + +SPOT:New(Recce, LaserCode, Duration) + +
    +
    + +

    SPOT Constructor.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Unit#UNIT Recce :

      + +
    • +
    • + +

      #number LaserCode :

      + +
    • +
    • + +

      #number Duration :

      + +
    • +
    +

    Return value

    + +

    #SPOT:

    + + +
    +
    +
    +
    + + +SPOT:OnAfterDestroyed(From, Event, To) + +
    +
    + +

    Destroyed Handler OnAfter for SPOT

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +SPOT:OnAfterLaseOff(From, Event, To) + +
    +
    + +

    LaseOff Handler OnAfter for SPOT

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +SPOT:OnAfterLaseOn(From, Event, To) + +
    +
    + +

    LaseOn Handler OnAfter for SPOT

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +
    +
    +
    +
    + + +SPOT:OnBeforeDestroyed(From, Event, To) + +
    +
    + +

    Destroyed Handler OnBefore for SPOT

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +SPOT:OnBeforeLaseOff(From, Event, To) + +
    +
    + +

    LaseOff Handler OnBefore for SPOT

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +SPOT:OnBeforeLaseOn(From, Event, To) + +
    +
    + +

    LaseOn Handler OnBefore for SPOT

    + +

    Parameters

    +
      +
    • + +

      #string From :

      + +
    • +
    • + +

      #string Event :

      + +
    • +
    • + +

      #string To :

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +SPOT:OnEventDead(EventData) + +
    +
    + + + +

    Parameter

    + +
    +
    +
    +
    + + + +SPOT.Recce + +
    +
    + + + +
    +
    +
    +
    + + + +SPOT.ScheduleID + +
    +
    + + + +
    +
    +
    +
    + + + +SPOT.SpotIR + +
    +
    + + + +
    +
    +
    +
    + + + +SPOT.SpotLaser + +
    +
    + + + +
    +
    +
    +
    + + + +SPOT.Target + +
    +
    + + + +
    +
    +
    +
    + + +SPOT:__Destroyed(Delay) + +
    +
    + +

    Destroyed Asynchronous Trigger for SPOT

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +SPOT:__LaseOff(Delay) + +
    +
    + +

    LaseOff Asynchronous Trigger for SPOT

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +SPOT:__LaseOn(Delay) + +
    +
    + +

    LaseOn Asynchronous Trigger for SPOT

    + +

    Parameter

    +
      +
    • + +

      #number Delay :

      + +
    • +
    +
    +
    +
    +
    + + +SPOT:onafterLaseOff(From, Event, To) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    +

    Return value

    + +

    #SPOT:

    + + +
    +
    +
    +
    + + +SPOT:onafterLaseOn(From, Event, To, Target, LaserCode, Duration) + +
    +
    + + + +

    Parameters

    + +
    +
    +
    +
    + + +SPOT:onafterLasing(From, Event, To) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    +
    +
    + +
    + +
    + + diff --git a/docs/Documentation/Static.html b/docs/Documentation/Static.html index ff176e054..efa72665c 100644 --- a/docs/Documentation/Static.html +++ b/docs/Documentation/Static.html @@ -17,9 +17,16 @@ index

    Module Static

    -

    This module contains the STATIC class.

    +

    Wrapper -- STATIC wraps the DCS StaticObject class.

    -

    1) Static#STATIC class, extends Positionable#POSITIONABLE

    -

    Statics are Static Units defined within the Mission Editor. -Note that Statics are almost the same as Units, but they don't have a controller. -The Static#STATIC class is a wrapper class to handle the DCS Static objects:

    +
    -
      -
    • Wraps the DCS Static objects.
    • -
    • Support all DCS Static APIs.
    • -
    • Enhance with Static specific APIs not in the DCS API set.
    • -
    +

    Author: Sven Van de Velde (FlightControl)

    -

    1.1) STATIC reference methods

    -

    For each DCS Static will have a STATIC wrapper object (instance) within the _DATABASE object. -This is done at the beginning of the mission (when the mission starts).

    +

    Contributions:

    -

    The STATIC class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference -using the Static Name.

    - -

    Another thing to know is that STATIC objects do not "contain" the DCS Static object. -The STATIc methods will reference the DCS Static object by name when it is needed during API execution. -If the DCS Static object does not exist or is nil, the STATIC methods will return nil and log an exception in the DCS.log file.

    - -

    The STATIc class provides the following functions to retrieve quickly the relevant STATIC instance:

    - -
      -
    • STATIC.FindByName(): Find a STATIC instance from the _DATABASE object using a DCS Static name.
    • -
    - -

    IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).

    +

    Global(s)

    @@ -113,19 +123,15 @@ If the DCS Static object does not exist or is nil, the STATIC methods will retur
    STATIC +

    STATIC class, extends Positionable#POSITIONABLE

    +

    Statics are Static Units defined within the Mission Editor.

    Type STATIC

    - - - -
    STATIC.ClassName - -
    STATIC:FindByName(StaticName, RaiseError)

    Finds a STATIC from the _DATABASE using the relevant Static Name.

    @@ -168,6 +174,39 @@ If the DCS Static object does not exist or is nil, the STATIC methods will retur
    +

    STATIC class, extends Positionable#POSITIONABLE

    + +

    Statics are Static Units defined within the Mission Editor.

    + + +

    Note that Statics are almost the same as Units, but they don't have a controller. +The Static#STATIC class is a wrapper class to handle the DCS Static objects:

    + +
      +
    • Wraps the DCS Static objects.
    • +
    • Support all DCS Static APIs.
    • +
    • Enhance with Static specific APIs not in the DCS API set.
    • +
    + +

    STATIC reference methods

    + +

    For each DCS Static will have a STATIC wrapper object (instance) within the _DATABASE object. +This is done at the beginning of the mission (when the mission starts).

    + +

    The STATIC class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference +using the Static Name.

    + +

    Another thing to know is that STATIC objects do not "contain" the DCS Static object. +The STATIc methods will reference the DCS Static object by name when it is needed during API execution. +If the DCS Static object does not exist or is nil, the STATIC methods will return nil and log an exception in the DCS.log file.

    + +

    The STATIc class provides the following functions to retrieve quickly the relevant STATIC instance:

    + +
      +
    • STATIC.FindByName(): Find a STATIC instance from the _DATABASE object using a DCS Static name.
    • +
    + +

    IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).

    @@ -175,24 +214,7 @@ If the DCS Static object does not exist or is nil, the STATIC methods will retur

    Type Static

    Type STATIC

    - -

    The STATIC class

    - -

    Field(s)

    -
    -
    - - #string - -STATIC.ClassName - -
    -
    - - - -
    -
    +

    Field(s)

    diff --git a/docs/Documentation/StaticObject.html b/docs/Documentation/StaticObject.html index dcecd0cf0..6ff504920 100644 --- a/docs/Documentation/StaticObject.html +++ b/docs/Documentation/StaticObject.html @@ -17,7 +17,17 @@ index

    Module Task

    -

    This module contains the TASK class.

    +

    Tasking -- This module contains the TASK class, the main engine to run human taskings.

    -

    1) #TASK class, extends Base#BASE

    -

    1.1) The #TASK class implements the methods for task orchestration within MOOSE.

    -

    The class provides a couple of methods to:

    - - - -

    1.2) Set and enquire task status (beyond the task state machine processing).

    -

    A task needs to implement as a minimum the following task states:

    - -
      -
    • Success: Expresses the successful execution and finalization of the task.
    • -
    • Failed: Expresses the failure of a task.
    • -
    • Planned: Expresses that the task is created, but not yet in execution and is not assigned yet.
    • -
    • Assigned: Expresses that the task is assigned to a Group of players, and that the task is in execution mode.
    • -
    - -

    A task may also implement the following task states:

    - -
      -
    • Rejected: Expresses that the task is rejected by a player, who was requested to accept the task.
    • -
    • Cancelled: Expresses that the task is cancelled by HQ or through a logical situation where a cancellation of the task is required.
    • -
    - -

    A task can implement more statusses than the ones outlined above. Please consult the documentation of the specific tasks to understand the different status modelled.

    - -

    The status of tasks can be set by the methods State followed by the task status. An example is StateAssigned(). -The status of tasks can be enquired by the methods IsState followed by the task status name. An example is if IsStateAssigned() then.

    - -

    1.3) Add scoring when reaching a certain task status:

    -

    Upon reaching a certain task status in a task, additional scoring can be given. If the Mission has a scoring system attached, the scores will be added to the mission scoring. -Use the method TASK.AddScore() to add scores when a status is reached.

    - -

    1.4) Task briefing:

    -

    A task briefing can be given that is shown to the player when he is assigned to the task.

    -
    -

    Authors: FlightControl - Design and Programming

    +

    Author: Sven Van de Velde (FlightControl)

    + +

    Contributions:

    + +

    Global(s)

    @@ -132,7 +123,9 @@ Use the method TASK.AddScore() to add scores whe
    TASK +

    TASK class, extends Base#BASE

    +

    The TASK class implements the methods for task orchestration within MOOSE.

    @@ -145,9 +138,15 @@ Use the method TASK.AddScore() to add scores whe
    TASK:AbortUnit(PlayerUnit)TASK:AbortGroup(PlayerUnit, PlayerGroup)

    Abort a PlayerUnit from a Task.

    +
    TASK:AddProgress(PlayerName, ProgressText, ProgressTime, ProgressPoints) +

    Add Task Progress for a Player Name

    TASK.ClassNameTASK:ClearGroupAssignment(TaskGroup) - +

    Clear the Group assignment from the Task.

    TASK:CrashUnit(PlayerUnit)TASK:CrashGroup(PlayerUnit, PlayerGroup)

    A PlayerUnit crashed in a Task.

    +
    TASK.DetectedItemIndex + +
    TASK.Detection +
    TASK.FsmTemplate +
    TASK:GetBriefing() +

    Gets the Task briefing.

    TASK:GetName()

    Gets the Name of the Task

    +
    TASK:GetPlayerCount() +

    Create a count of the players in the Task.

    +
    TASK:GetPlayerNames() +

    Create a list of the players in the Task.

    +
    TASK:GetPlayerProgress(PlayerName) +
    TASK:GetStateString()

    Gets the Task status.

    +
    TASK:GetTaskBriefing() +

    Returns the Task briefing.

    TASK:GetUnitProcess(TaskUnit)

    Get the Task FSM Process Template

    +
    TASK:Goal() +

    Goal Trigger for TASK

    TASK:IsAssignedToGroup(TaskGroup)TASK:IsGroupAssigned(TaskGroup)

    Returns if the Task is assigned to the Group.

    TASK.MenuTASK:MenuAssignToGroup(TaskGroup)
    TASK.MenuAssignToGroup(MenuParam)TASK.MenuAssigned + +
    TASK.MenuPlanned
    TASK:New(Mission, SetGroupAssign, TaskName, TaskType)TASK:New(Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing)

    Instantiates a new TASK.

    +
    TASK:OnAfterGoal(Controllable, From, Event, To) +

    Goal Handler OnAfter for TASK

    TASK.PlayersTASK:OnBeforeGoal(Controllable, From, Event, To) - +

    Goal Handler OnBefore for TASK

    TASK.ProcessClassesTASK:RefreshMenus(TaskGroup, MenuTime) - -
    TASK.Processes - +

    Remove the menu option of the Task for a Group.

    TASK:RemoveMenu(MenuTime)

    Remove the menu options of the Task to all the groups in the SetGroup.

    -
    TASK:RemovePlannedMenuForGroup(TaskGroup, MenuTime) -

    Remove the menu option of the Task for a Group.

    TASK:ReportDetails()TASK:ReportDetails(TaskGroup, ReportGroup)

    Create a detailed report of the Task.

    TASK:ReportSummary()TASK:ReportOverview(ReportGroup) -

    Create a summary report of the Task.

    +

    Create an overiew report of the Task.

    TASK.ScoresTASK:ReportSummary(ReportGroup) - +

    Create a summary report of the Task.

    TASK:SetBriefing(TaskBriefing)

    Sets a Task briefing.

    +
    TASK:SetDetection(Detection, DetectedItemIndex) +

    Set detection of a task

    TASK.SetGroup

    The Set of Groups assigned to the Task

    +
    TASK:SetGroupAssigned(TaskGroup) +

    Set Group assigned to the Task.

    TASK:SetID(TaskID)

    Sets the ID of the Task

    +
    TASK:SetInfo(TaskInfo, TaskInfoText, TaskInfoOrder) +

    Sets the Information on the Task

    TASK:SetPlannedMenuForGroup(TaskGroup, MenuText, MenuTime)

    Set the planned menu option of the Task.

    +
    TASK:SetScoreOnFail(PlayerName, Penalty, TaskUnit) +

    Set a penalty when the A2A attack has failed.

    +
    TASK:SetScoreOnProgress(PlayerName, Score, TaskUnit) +

    Set a score when progress has been made by the player.

    +
    TASK:SetScoreOnSuccess(PlayerName, Score, TaskUnit) +

    Set a score when all the targets in scope of the A2A attack, have been destroyed.

    TASK.TaskID +
    TASK.TaskInfo +
    TASK.TaskName +
    TASK.TaskProgress +
    TASK:__Fail()

    FSM Fail asynchronous event function for TASK.

    +
    TASK:__Goal(Delay) +

    Goal Asynchronous Trigger for TASK

    TASK:onenterAssigned(Event, From, To, PlayerUnit, PlayerName)

    FSM function for a TASK

    +
    TASK:onenterCancelled(From, Event, To) +

    FSM function for a TASK

    + + + + + + + + + + + + + + + + +
    TASK_A2A +

    TASK_A2A class, extends Task#TASK

    + +

    The TASK_A2A class defines Air To Air tasks for a Set of Target Units, +based on the tasking capabilities defined in Task#TASK.

    +
    TASK_A2A_ENGAGE +

    TASKA2AENGAGE class, extends TaskA2A#TASKA2A

    + +

    The TASKA2AENGAGE class defines an engage task for a human player to be executed.

    +
    TASK_A2A_INTERCEPT +

    TASKA2AINTERCEPT class, extends TaskA2A#TASKA2A

    + +

    The TASKA2AINTERCEPT class defines an intercept task for a human player to be executed.

    +
    TASK_A2A_SWEEP +

    TASKA2ASWEEP class, extends TaskA2A#TASKA2A

    + +

    The TASKA2ASWEEP class defines a sweep task for a human player to be executed.

    +
    +

    Type TASK_A2A

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TASK_A2A:GetGoalTotal() + +
    TASK_A2A:GetPlannedMenuText() + +
    TASK_A2A:GetRendezVousCoordinate(TaskUnit) + +
    TASK_A2A:GetRendezVousZone(TaskUnit) + +
    TASK_A2A:GetTargetCoordinate(TaskUnit) + +
    TASK_A2A:GetTargetZone(TaskUnit) + +
    TASK_A2A:New(Mission, SetAttack, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit, TaskType, TaskBriefing) +

    Instantiates a new TASK_A2A.

    +
    TASK_A2A:SetGoalTotal() + +
    TASK_A2A:SetRendezVousCoordinate(RendezVousCoordinate, RendezVousRange, TaskUnit) + +
    TASK_A2A:SetRendezVousZone(RendezVousZone, TaskUnit) + +
    TASK_A2A:SetTargetCoordinate(TargetCoordinate, TaskUnit) + +
    TASK_A2A:SetTargetZone(TargetZone, TaskUnit, Altitude, Heading) + +
    TASK_A2A.TargetSetUnit + +
    + +

    Type TASK_A2A_ENGAGE

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TASK_A2A_ENGAGE:New(Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing) +

    Instantiates a new TASKA2AENGAGE.

    +
    TASK_A2A_ENGAGE:ReportOrder(ReportGroup) + +
    TASK_A2A_ENGAGE:SetScoreOnFail(PlayerName, Penalty, TaskUnit) +

    Set a penalty when the A2A attack has failed.

    +
    TASK_A2A_ENGAGE:SetScoreOnProgress(PlayerName, Score, TaskUnit) +

    Set a score when a target in scope of the A2A attack, has been destroyed .

    +
    TASK_A2A_ENGAGE:SetScoreOnSuccess(PlayerName, Score, TaskUnit) +

    Set a score when all the targets in scope of the A2A attack, have been destroyed.

    +
    TASK_A2A_ENGAGE.TargetSetUnit + +
    TASK_A2A_ENGAGE:UpdateTaskInfo() + +
    TASK_A2A_ENGAGE:onafterGoal(TaskUnit, From, Event, To) + +
    + +

    Type TASK_A2A_INTERCEPT

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TASK_A2A_INTERCEPT:New(Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing) +

    Instantiates a new TASKA2AINTERCEPT.

    +
    TASK_A2A_INTERCEPT:ReportOrder(ReportGroup) + +
    TASK_A2A_INTERCEPT:SetScoreOnFail(PlayerName, Penalty, TaskUnit) +

    Set a penalty when the A2A attack has failed.

    +
    TASK_A2A_INTERCEPT:SetScoreOnProgress(PlayerName, Score, TaskUnit) +

    Set a score when a target in scope of the A2A attack, has been destroyed .

    +
    TASK_A2A_INTERCEPT:SetScoreOnSuccess(PlayerName, Score, TaskUnit) +

    Set a score when all the targets in scope of the A2A attack, have been destroyed.

    +
    TASK_A2A_INTERCEPT.TargetSetUnit + +
    TASK_A2A_INTERCEPT:UpdateTaskInfo() + +
    TASK_A2A_INTERCEPT:onafterGoal(TaskUnit, From, Event, To) + +
    + +

    Type TASK_A2A_SWEEP

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TASK_A2A_SWEEP:New(Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing) +

    Instantiates a new TASKA2ASWEEP.

    +
    TASK_A2A_SWEEP:ReportOrder(ReportGroup) + +
    TASK_A2A_SWEEP:SetScoreOnFail(PlayerName, Penalty, TaskUnit) +

    Set a penalty when the A2A attack has failed.

    +
    TASK_A2A_SWEEP:SetScoreOnProgress(PlayerName, Score, TaskUnit) +

    Set a score when a target in scope of the A2A attack, has been destroyed .

    +
    TASK_A2A_SWEEP:SetScoreOnSuccess(PlayerName, Score, TaskUnit) +

    Set a score when all the targets in scope of the A2A attack, have been destroyed.

    +
    TASK_A2A_SWEEP.TargetSetUnit + +
    TASK_A2A_SWEEP:UpdateTaskInfo() + +
    TASK_A2A_SWEEP:onafterGoal(TaskUnit, From, Event, To) + +
    + +

    Global(s)

    +
    +
    + + #TASK_A2A + +TASK_A2A + +
    +
    + +

    TASK_A2A class, extends Task#TASK

    + +

    The TASK_A2A class defines Air To Air tasks for a Set of Target Units, +based on the tasking capabilities defined in Task#TASK.

    + + +

    The TASK_A2A is implemented using a Fsm#FSM_TASK, and has the following statuses:

    + +
      +
    • None: Start of the process
    • +
    • Planned: The A2A task is planned.
    • +
    • Assigned: The A2A task is assigned to a Group#GROUP.
    • +
    • Success: The A2A task is successfully completed.
    • +
    • Failed: The A2A task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
    • +
    + +

    1.1) Set the scoring of achievements in an A2A attack.

    + +

    Scoring or penalties can be given in the following circumstances:

    + + + + +
    +
    +
    +
    + + #TASK_A2A_ENGAGE + +TASK_A2A_ENGAGE + +
    +
    + +

    TASKA2AENGAGE class, extends TaskA2A#TASKA2A

    + +

    The TASKA2AENGAGE class defines an engage task for a human player to be executed.

    + + +

    When enemy planes are close to human players, use this task type is used urge the players to get out there!

    + +

    The TASKA2AENGAGE is used by the TaskA2ADispatcher#TASKA2ADISPATCHER to automatically create engage tasks +based on detected airborne enemy targets intruding friendly airspace.

    + +

    The task is defined for a Mission#MISSION, where a friendly Set#SET_GROUP consisting of GROUPs with one human players each, is engaging the targets. +The task is given a name and a briefing, that is used in the menu structure and in the reporting.

    + + +
    +
    +
    +
    + + #TASK_A2A_INTERCEPT + +TASK_A2A_INTERCEPT + +
    +
    + +

    TASKA2AINTERCEPT class, extends TaskA2A#TASKA2A

    + +

    The TASKA2AINTERCEPT class defines an intercept task for a human player to be executed.

    + + +

    When enemy planes need to be intercepted by human players, use this task type to urgen the players to get out there!

    + +

    The TASKA2AINTERCEPT is used by the TaskA2ADispatcher#TASKA2ADISPATCHER to automatically create intercept tasks +based on detected airborne enemy targets intruding friendly airspace.

    + +

    The task is defined for a Mission#MISSION, where a friendly Set#SET_GROUP consisting of GROUPs with one human players each, is intercepting the targets. +The task is given a name and a briefing, that is used in the menu structure and in the reporting.

    + + +
    +
    +
    +
    + + #TASK_A2A_SWEEP + +TASK_A2A_SWEEP + +
    +
    + +

    TASKA2ASWEEP class, extends TaskA2A#TASKA2A

    + +

    The TASKA2ASWEEP class defines a sweep task for a human player to be executed.

    + + +

    A sweep task needs to be given when targets were detected but somehow the detection was lost. +Most likely, these enemy planes are hidden in the mountains or are flying under radar. +These enemy planes need to be sweeped by human players, and use this task type to urge the players to get out there and find those enemy fighters.

    + +

    The TASKA2ASWEEP is used by the TaskA2ADispatcher#TASKA2ADISPATCHER to automatically create sweep tasks +based on detected airborne enemy targets intruding friendly airspace, for which the detection has been lost for more than 60 seconds.

    + +

    The task is defined for a Mission#MISSION, where a friendly Set#SET_GROUP consisting of GROUPs with one human players each, is sweeping the targets. +The task is given a name and a briefing, that is used in the menu structure and in the reporting.

    + + +
    +
    +

    Type Task_A2A

    + +

    Type FSM_PROCESS

    + +

    Type TASK_A2A

    + +

    The TASK_A2A class

    + +

    Field(s)

    +
    +
    + + +TASK_A2A:GetGoalTotal() + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2A:GetPlannedMenuText() + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2A:GetRendezVousCoordinate(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return values

    +
      +
    1. + +

      Core.Point#COORDINATE: +The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.

      + +
    2. +
    3. + +

      #number: +The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.

      + +
    4. +
    +
    +
    +
    +
    + + +TASK_A2A:GetRendezVousZone(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return value

    + +

    Core.Zone#ZONE_BASE: +The Zone object where the RendezVous is located on the map.

    + +
    +
    +
    +
    + + +TASK_A2A:GetTargetCoordinate(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return value

    + +

    Core.Point#COORDINATE: +The Coordinate object where the Target is located on the map.

    + +
    +
    +
    +
    + + +TASK_A2A:GetTargetZone(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return value

    + +

    Core.Zone#ZONE_BASE: +The Zone object where the Target is located on the map.

    + +
    +
    +
    +
    + + +TASK_A2A:New(Mission, SetAttack, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit, TaskType, TaskBriefing) + +
    +
    + +

    Instantiates a new TASK_A2A.

    + +

    Parameters

    +
      +
    • + +

      Tasking.Mission#MISSION Mission :

      + +
    • +
    • + +

      Set#SET_GROUP SetAttack : +The set of groups for which the Task can be assigned.

      + +
    • +
    • + +

      #string TaskName : +The name of the Task.

      + +
    • +
    • + +

      Set#SET_UNIT UnitSetTargets :

      + +
    • +
    • + +

      #number TargetDistance : +The distance to Target when the Player is considered to have "arrived" at the engagement range.

      + +
    • +
    • + +

      Core.Zone#ZONE_BASE TargetZone : +The target zone, if known. +If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be.

      + +
    • +
    • + +

      TargetSetUnit :

      + +
    • +
    • + +

      TaskType :

      + +
    • +
    • + +

      TaskBriefing :

      + +
    • +
    +

    Return value

    + +

    #TASK_A2A: +self

    + +
    +
    +
    +
    + + +TASK_A2A:SetGoalTotal() + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2A:SetRendezVousCoordinate(RendezVousCoordinate, RendezVousRange, TaskUnit) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      Core.Point#COORDINATE RendezVousCoordinate : +The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.

      + +
    • +
    • + +

      #number RendezVousRange : +The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +
    +
    +
    +
    + + +TASK_A2A:SetRendezVousZone(RendezVousZone, TaskUnit) + +
    +
    + + + +

    Parameters

    + +
    +
    +
    +
    + + +TASK_A2A:SetTargetCoordinate(TargetCoordinate, TaskUnit) + +
    +
    + + + +

    Parameters

    + +
    +
    +
    +
    + + +TASK_A2A:SetTargetZone(TargetZone, TaskUnit, Altitude, Heading) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      Core.Zone#ZONE_BASE TargetZone : +The Zone object where the Target is located on the map.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    • + +

      Altitude :

      + +
    • +
    • + +

      Heading :

      + +
    • +
    +
    +
    +
    +
    + + Set#SET_UNIT + +TASK_A2A.TargetSetUnit + +
    +
    + + + +
    +
    + +

    Type TASK_A2A_ENGAGE

    + +

    The TASKA2AENGAGE class

    + +

    Field(s)

    +
    +
    + + +TASK_A2A_ENGAGE:New(Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing) + +
    +
    + +

    Instantiates a new TASKA2AENGAGE.

    + +

    Parameters

    +
      +
    • + +

      Tasking.Mission#MISSION Mission :

      + +
    • +
    • + +

      Core.Set#SET_GROUP SetGroup : +The set of groups for which the Task can be assigned.

      + +
    • +
    • + +

      #string TaskName : +The name of the Task.

      + +
    • +
    • + +

      Core.Set#SET_UNIT TargetSetUnit :

      + +
    • +
    • + +

      #string TaskBriefing : +The briefing of the task.

      + +
    • +
    +

    Return value

    + +

    #TASKA2AENGAGE: +self

    + +
    +
    +
    +
    + + +TASK_A2A_ENGAGE:ReportOrder(ReportGroup) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      ReportGroup :

      + +
    • +
    +
    +
    +
    +
    + + +TASK_A2A_ENGAGE:SetScoreOnFail(PlayerName, Penalty, TaskUnit) + +
    +
    + +

    Set a penalty when the A2A attack has failed.

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #number Penalty : +The penalty in points, must be a negative value!

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASKA2AENGAGE:

    + + +
    +
    +
    +
    + + +TASK_A2A_ENGAGE:SetScoreOnProgress(PlayerName, Score, TaskUnit) + +
    +
    + +

    Set a score when a target in scope of the A2A attack, has been destroyed .

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #number Score : +The score in points to be granted when task process has been achieved.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASKA2AENGAGE:

    + + +
    +
    +
    +
    + + +TASK_A2A_ENGAGE:SetScoreOnSuccess(PlayerName, Score, TaskUnit) + +
    +
    + +

    Set a score when all the targets in scope of the A2A attack, have been destroyed.

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #number Score : +The score in points.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASKA2AENGAGE:

    + + +
    +
    +
    +
    + + Set#SET_UNIT + +TASK_A2A_ENGAGE.TargetSetUnit + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2A_ENGAGE:UpdateTaskInfo() + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2A_ENGAGE:onafterGoal(TaskUnit, From, Event, To) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      TaskUnit :

      + +
    • +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    +
    +
    + +

    Type TASK_A2A_INTERCEPT

    + +

    The TASKA2AINTERCEPT class

    + +

    Field(s)

    +
    +
    + + +TASK_A2A_INTERCEPT:New(Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing) + +
    +
    + +

    Instantiates a new TASKA2AINTERCEPT.

    + +

    Parameters

    +
      +
    • + +

      Tasking.Mission#MISSION Mission :

      + +
    • +
    • + +

      Core.Set#SET_GROUP SetGroup : +The set of groups for which the Task can be assigned.

      + +
    • +
    • + +

      #string TaskName : +The name of the Task.

      + +
    • +
    • + +

      Core.Set#SET_UNIT TargetSetUnit :

      + +
    • +
    • + +

      #string TaskBriefing : +The briefing of the task.

      + +
    • +
    +

    Return value

    + +

    #TASKA2AINTERCEPT:

    + + +
    +
    +
    +
    + + +TASK_A2A_INTERCEPT:ReportOrder(ReportGroup) + +
    +
    + + + +

    Parameter

    + +
    +
    +
    +
    + + +TASK_A2A_INTERCEPT:SetScoreOnFail(PlayerName, Penalty, TaskUnit) + +
    +
    + +

    Set a penalty when the A2A attack has failed.

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #number Penalty : +The penalty in points, must be a negative value!

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASKA2AINTERCEPT:

    + + +
    +
    +
    +
    + + +TASK_A2A_INTERCEPT:SetScoreOnProgress(PlayerName, Score, TaskUnit) + +
    +
    + +

    Set a score when a target in scope of the A2A attack, has been destroyed .

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #number Score : +The score in points to be granted when task process has been achieved.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASKA2AINTERCEPT:

    + + +
    +
    +
    +
    + + +TASK_A2A_INTERCEPT:SetScoreOnSuccess(PlayerName, Score, TaskUnit) + +
    +
    + +

    Set a score when all the targets in scope of the A2A attack, have been destroyed.

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #number Score : +The score in points.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASKA2AINTERCEPT:

    + + +
    +
    +
    +
    + + Set#SET_UNIT + +TASK_A2A_INTERCEPT.TargetSetUnit + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2A_INTERCEPT:UpdateTaskInfo() + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2A_INTERCEPT:onafterGoal(TaskUnit, From, Event, To) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      TaskUnit :

      + +
    • +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    +
    +
    + +

    Type TASK_A2A_SWEEP

    + +

    The TASKA2ASWEEP class

    + +

    Field(s)

    +
    +
    + + +TASK_A2A_SWEEP:New(Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing) + +
    +
    + +

    Instantiates a new TASKA2ASWEEP.

    + +

    Parameters

    +
      +
    • + +

      Tasking.Mission#MISSION Mission :

      + +
    • +
    • + +

      Core.Set#SET_GROUP SetGroup : +The set of groups for which the Task can be assigned.

      + +
    • +
    • + +

      #string TaskName : +The name of the Task.

      + +
    • +
    • + +

      Core.Set#SET_UNIT TargetSetUnit :

      + +
    • +
    • + +

      #string TaskBriefing : +The briefing of the task.

      + +
    • +
    +

    Return value

    + +

    #TASKA2ASWEEP: +self

    + +
    +
    +
    +
    + + +TASK_A2A_SWEEP:ReportOrder(ReportGroup) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      ReportGroup :

      + +
    • +
    +
    +
    +
    +
    + + +TASK_A2A_SWEEP:SetScoreOnFail(PlayerName, Penalty, TaskUnit) + +
    +
    + +

    Set a penalty when the A2A attack has failed.

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #number Penalty : +The penalty in points, must be a negative value!

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASKA2ASWEEP:

    + + +
    +
    +
    +
    + + +TASK_A2A_SWEEP:SetScoreOnProgress(PlayerName, Score, TaskUnit) + +
    +
    + +

    Set a score when a target in scope of the A2A attack, has been destroyed .

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #number Score : +The score in points to be granted when task process has been achieved.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASKA2ASWEEP:

    + + +
    +
    +
    +
    + + +TASK_A2A_SWEEP:SetScoreOnSuccess(PlayerName, Score, TaskUnit) + +
    +
    + +

    Set a score when all the targets in scope of the A2A attack, have been destroyed.

    + +

    Parameters

    +
      +
    • + +

      #string PlayerName : +The name of the player.

      + +
    • +
    • + +

      #number Score : +The score in points.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASKA2ASWEEP:

    + + +
    +
    +
    +
    + + Set#SET_UNIT + +TASK_A2A_SWEEP.TargetSetUnit + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2A_SWEEP:UpdateTaskInfo() + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2A_SWEEP:onafterGoal(TaskUnit, From, Event, To) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      TaskUnit :

      + +
    • +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    +
    +
    + + + + + + diff --git a/docs/Documentation/Task_A2A_Dispatcher.html b/docs/Documentation/Task_A2A_Dispatcher.html new file mode 100644 index 000000000..6c6029eb9 --- /dev/null +++ b/docs/Documentation/Task_A2A_Dispatcher.html @@ -0,0 +1,839 @@ + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +

    Module Task_A2A_Dispatcher

    + +

    Tasking - The TASKA2ADISPATCHER creates and manages player TASK_A2A tasks based on detected targets.

    + + + +

    The #TASKA2ADISPATCHER classes implement the dynamic dispatching of tasks upon groups of detected units determined a Set of EWR installation groups.

    + +
    + +

    Author: Sven Van de Velde (FlightControl)

    + +

    Contributions:

    + +
    + + +

    Global(s)

    + + + + + +
    TASK_A2A_DISPATCHER +

    TASKA2ADISPATCHER class, extends Tasking#DETECTION_MANAGER

    + +

    Banner Image

    + +

    The #TASKA2ADISPATCHER class implements the dynamic dispatching of tasks upon groups of detected units determined a Set of EWR installation groups.

    +
    +

    Type TASK_A2A_DISPATCHER

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TASK_A2A_DISPATCHER.Detection + +
    TASK_A2A_DISPATCHER:EvaluateENGAGE(DetectedItem) +

    Creates an ENGAGE task when there are human friendlies airborne near the targets.

    +
    TASK_A2A_DISPATCHER:EvaluateINTERCEPT(DetectedItem) +

    Creates an INTERCEPT task when there are targets for it.

    +
    TASK_A2A_DISPATCHER:EvaluateRemoveTask(Mission, Task, Detection, DetectedItemID, DetectedItemChange, DetectedItem, DetectedItemIndex, DetectedItemChanged) +

    Evaluates the removal of the Task from the Mission.

    +
    TASK_A2A_DISPATCHER:EvaluateSWEEP(DetectedItem) +

    Creates an SWEEP task when there are targets for it.

    +
    TASK_A2A_DISPATCHER:GetFriendliesNearBy(DetectedItem) +

    Calculates which friendlies are nearby the area

    +
    TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy(DetectedItem) +

    Calculates which HUMAN friendlies are nearby the area

    +
    TASK_A2A_DISPATCHER.Mission + +
    TASK_A2A_DISPATCHER:New(Mission, SetGroup, Detection) +

    TASKA2ADISPATCHER constructor.

    +
    TASK_A2A_DISPATCHER:OnAfterAssign(From, Event, To, Task, TaskUnit, PlayerName) +

    OnAfter Transition Handler for Event Assign.

    +
    TASK_A2A_DISPATCHER:ProcessDetected(Detection) +

    Assigns tasks in relation to the detected items to the Set#SET_GROUP.

    +
    TASK_A2A_DISPATCHER:RemoveTask(TaskIndex) + +
    TASK_A2A_DISPATCHER:SetEngageRadius(EngageRadius) +

    Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission.

    +
    + +

    Global(s)

    +
    +
    + + #TASK_A2A_DISPATCHER + +TASK_A2A_DISPATCHER + +
    +
    + +

    TASKA2ADISPATCHER class, extends Tasking#DETECTION_MANAGER

    + +

    Banner Image

    + +

    The #TASKA2ADISPATCHER class implements the dynamic dispatching of tasks upon groups of detected units determined a Set of EWR installation groups.

    + + + +

    Banner Image

    + +

    The EWR will detect units, will group them, and will dispatch Tasks to groups. Depending on the type of target detected, different tasks will be dispatched. +Find a summary below describing for which situation a task type is created:

    + +

    Banner Image

    + +
      +
    • INTERCEPT Task: Is created when the target is known, is detected and within a danger zone, and there is no friendly airborne in range.
    • +
    • SWEEP Task: Is created when the target is unknown, was detected and the last position is only known, and within a danger zone, and there is no friendly airborne in range.
    • +
    • ENGAGE Task: Is created when the target is known, is detected and within a danger zone, and there is a friendly airborne in range, that will receive this task.
    • +
    + +

    1. TASK_A2A_DISPATCHER constructor:

    + +

    The TASKA2ADISPATCHER.New() method creates a new TASK_A2A_DISPATCHER instance.

    + +

    1.1. Define or set the Mission:

    + +

    Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter.

    + +
    local HQ = GROUP:FindByName( "HQ", "Bravo" )
    +local CommandCenter = COMMANDCENTER:New( HQ, "Lima" )
    +local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED )
    +
    + +

    Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission. +Create the MISSION object, and hook it under the command center.

    + +

    1.2. Build a set of the groups seated by human players:

    + +

    Banner Image

    + +

    A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into.

    + +
    local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart()
    +
    + +

    The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission. +Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available.

    + +

    1.3. Define the EWR network:

    + +

    As part of the TASK_A2A_DISPATCHER constructor, an EWR network must be given as the third parameter. +An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy.

    + +

    Banner Image

    + +

    Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. +These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). +Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. +The position of these units is very important as they need to provide enough coverage +to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them.

    + +

    Banner Image

    + +

    Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. +For example if they are a long way forward and can detect enemy planes on the ground and taking off +they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. +Having the radars further back will mean a slower escalation because fewer targets will be detected and +therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. +It all depends on what the desired effect is.

    + +

    EWR networks are dynamically constructed, that is, they form part of the Functional#DETECTION_BASE object that is given as the input parameter of the TASK_A2A_DISPATCHER class. +By defining in a smart way the names or name prefixes of the groups with EWR capable units, these groups will be automatically added or deleted from the EWR network, +increasing or decreasing the radar coverage of the Early Warning System.

    + +

    See the following example to setup an EWR network containing EWR stations and AWACS.

    + +
    local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart()
    +
    +local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 )
    +EWRDetection:SetFriendliesRange( 10000 )
    +EWRDetection:SetRefreshTimeInterval(30)
    +
    +-- Setup the A2A dispatcher, and initialize it.
    +A2ADispatcher = TASK_A2A_DISPATCHER:New( Mission, AttackGroups, EWRDetection )
    +
    + +

    The above example creates a SET_GROUP instance, and stores this in the variable (object) EWRSet. +EWRSet is then being configured to filter all active groups with a group name starting with EWR to be included in the Set. +EWRSet is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set. +Then a new EWRDetection object is created from the class DETECTION_AREAS. A grouping radius of 6000 is choosen, which is 6km. +The EWRDetection object is then passed to the TASKA2ADISPATCHER.New() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism.

    + +

    2. Define the detected target grouping radius:

    + +

    Banner Image

    + +

    The target grouping radius is a property of the Detection object, that was passed to the AI_A2A_DISPATCHER object, but can be changed. +The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. +Fast planes like in the 80s, need a larger radius than WWII planes.
    +Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft.

    + +

    Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate +group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small!

    + +

    3. Set the Engage radius:

    + +

    Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission.

    + +

    Banner Image

    + +

    So, if there is a target area detected and reported, +then any friendlies that are airborne near this target area, +will be commanded to (re-)engage that target when available (if no other tasks were commanded). +For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, +will be considered to receive the command to engage that target area. +You need to evaluate the value of this parameter carefully. +If too small, more intercept missions may be triggered upon detected target areas. +If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far.

    + +

    4. Set Scoring and Messages:

    + +

    The TASK_A2A_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a Task dispatched by the TASK_A2A_DISPATCHER. +An event handler can be defined to catch the Assign event, and add additional processing to set scoring and to define messages, +when the player reaches certain achievements in the task.

    + +

    The prototype to handle the Assign event needs to be developed as follows:

    + +
     TaskDispatcher = TASK_A2A_DISPATCHER:New( ... )
    +
    + --- @param #TaskDispatcher self
    + -- @param #string From Contains the name of the state from where the Event was triggered.
    + -- @param #string Event Contains the name of the event that was triggered. In this case Assign.
    + -- @param #string To Contains the name of the state that will be transitioned to.
    + -- @param Tasking.Task_A2A#TASK_A2A Task The Task object, which is any derived object from TASK_A2A.
    + -- @param Wrapper.Unit#UNIT TaskUnit The Unit or Client that contains the Player.
    + -- @param #string PlayerName The name of the Player that joined the TaskUnit.
    + function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName )
    +   Task:SetScoreOnProgress( PlayerName, 20, TaskUnit )
    +   Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit )
    +   Task:SetScoreOnFail( PlayerName, -100, TaskUnit )
    + end
    +
    + +

    The OnAfterAssign method (function) is added to the TaskDispatcher object. +This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher. +So, this method will be called only ONCE when a player joins a unit in scope of the task.

    + +

    The TASK class implements various methods to additional set scoring for player achievements:

    + +
      +
    • Tasking.Task#TASK.SetScoreOnProgress() will add additional scores when a player achieves Progress while executing the task. + Examples of task progress can be destroying units, arriving at zones etc.

    • +
    • Tasking.Task#TASK.SetScoreOnSuccess() will add additional scores when the task goes into Success state. + This means the task has been successfully completed.

    • +
    • Tasking.Task#TASK.SetScoreOnSuccess() will add additional (negative) scores when the task goes into Failed state. + This means the task has not been successfully completed, and the scores must be given with a negative value!

    • +
    + + +
    +
    +

    Type Task_A2A_Dispatcher

    + +

    Type TASK_A2A_DISPATCHER

    + +

    TASKA2ADISPATCHER class.

    + +

    Field(s)

    +
    +
    + + + +TASK_A2A_DISPATCHER.Detection + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2A_DISPATCHER:EvaluateENGAGE(DetectedItem) + +
    +
    + +

    Creates an ENGAGE task when there are human friendlies airborne near the targets.

    + +

    Parameter

    + +

    Return values

    +
      +
    1. + +

      Set#SET_UNIT: +TargetSetUnit: The target set of units.

      + +
    2. +
    3. + +

      #nil: +If there are no targets to be set.

      + +
    4. +
    +
    +
    +
    +
    + + +TASK_A2A_DISPATCHER:EvaluateINTERCEPT(DetectedItem) + +
    +
    + +

    Creates an INTERCEPT task when there are targets for it.

    + +

    Parameter

    + +

    Return values

    +
      +
    1. + +

      Set#SET_UNIT: +TargetSetUnit: The target set of units.

      + +
    2. +
    3. + +

      #nil: +If there are no targets to be set.

      + +
    4. +
    +
    +
    +
    +
    + + +TASK_A2A_DISPATCHER:EvaluateRemoveTask(Mission, Task, Detection, DetectedItemID, DetectedItemChange, DetectedItem, DetectedItemIndex, DetectedItemChanged) + +
    +
    + +

    Evaluates the removal of the Task from the Mission.

    + + +

    Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".

    + +

    Parameters

    + +

    Return value

    + +

    Tasking.Task#TASK:

    + + +
    +
    +
    +
    + + +TASK_A2A_DISPATCHER:EvaluateSWEEP(DetectedItem) + +
    +
    + +

    Creates an SWEEP task when there are targets for it.

    + +

    Parameter

    + +

    Return values

    +
      +
    1. + +

      Set#SET_UNIT: +TargetSetUnit: The target set of units.

      + +
    2. +
    3. + +

      #nil: +If there are no targets to be set.

      + +
    4. +
    +
    +
    +
    +
    + + +TASK_A2A_DISPATCHER:GetFriendliesNearBy(DetectedItem) + +
    +
    + +

    Calculates which friendlies are nearby the area

    + +

    Parameter

    +
      +
    • + +

      DetectedItem :

      + +
    • +
    +

    Return value

    + +

    #number, Core.CommandCenter#REPORT:

    + + +
    +
    +
    +
    + + +TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy(DetectedItem) + +
    +
    + +

    Calculates which HUMAN friendlies are nearby the area

    + +

    Parameter

    +
      +
    • + +

      DetectedItem :

      + +
    • +
    +

    Return value

    + +

    #number, Core.CommandCenter#REPORT:

    + + +
    +
    +
    +
    + + + +TASK_A2A_DISPATCHER.Mission + +
    +
    + + + +
    +
    +
    +
    + + +TASK_A2A_DISPATCHER:New(Mission, SetGroup, Detection) + +
    +
    + +

    TASKA2ADISPATCHER constructor.

    + +

    Parameters

    + +

    Return value

    + +

    #TASKA2ADISPATCHER: +self

    + +
    +
    +
    +
    + + +TASK_A2A_DISPATCHER:OnAfterAssign(From, Event, To, Task, TaskUnit, PlayerName) + +
    +
    + +

    OnAfter Transition Handler for Event Assign.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    • + +

      Tasking.TaskA2A#TASKA2A Task :

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    • + +

      #string PlayerName :

      + +
    • +
    +
    +
    +
    +
    + + +TASK_A2A_DISPATCHER:ProcessDetected(Detection) + +
    +
    + +

    Assigns tasks in relation to the detected items to the Set#SET_GROUP.

    + +

    Parameter

    + +

    Return value

    + +

    #boolean: +Return true if you want the task assigning to continue... false will cancel the loop.

    + +
    +
    +
    +
    + + +TASK_A2A_DISPATCHER:RemoveTask(TaskIndex) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      TaskIndex :

      + +
    • +
    +
    +
    +
    +
    + + +TASK_A2A_DISPATCHER:SetEngageRadius(EngageRadius) + +
    +
    + +

    Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission.

    + + +

    So, if there is a target area detected and reported, +then any friendlies that are airborne near this target area, +will be commanded to (re-)engage that target when available (if no other tasks were commanded). +An ENGAGE task will be created for those pilots. +For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, +will be considered to receive the command to engage that target area. +You need to evaluate the value of this parameter carefully. +If too small, more intercept missions may be triggered upon detected target areas. +If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far.

    + +

    Parameter

    +
      +
    • + +

      #number EngageRadius : +(Optional, Default = 100000) The radius to report friendlies near the target.

      + +
    • +
    +

    Return value

    + +

    #TASKA2ADISPATCHER:

    + + +

    Usage:

    +
    
    +  -- Set 50km as the radius to engage any target by airborne friendlies.
    +  TaskA2ADispatcher:SetEngageRadius( 50000 )
    +  
    +  -- Set 100km as the radius to engage any target by airborne friendlies.
    +  TaskA2ADispatcher:SetEngageRadius() -- 100000 is the default value.
    +  
    + +
    +
    + +
    + +
    + + diff --git a/docs/Documentation/Task_A2G.html b/docs/Documentation/Task_A2G.html index 46c91e395..841460bca 100644 --- a/docs/Documentation/Task_A2G.html +++ b/docs/Documentation/Task_A2G.html @@ -17,9 +17,16 @@ index
    @@ -79,110 +111,55 @@

    Banner Image

    - -

    1) TaskA2G#TASKA2G class, extends Task#TASK

    - -

    The #TASK_A2G class defines Air To Ground tasks for a Set of Target Units, -based on the tasking capabilities defined in Task#TASK. -The TASK_A2G is implemented using a Statemachine#FSM_TASK, and has the following statuses:

    - -
      -
    • None: Start of the process
    • -
    • Planned: The A2G task is planned.
    • -
    • Assigned: The A2G task is assigned to a Group#GROUP.
    • -
    • Success: The A2G task is successfully completed.
    • -
    • Failed: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
    • -
    - -

    1.1) Set the scoring of achievements in an A2G attack.

    - -

    Scoring or penalties can be given in the following circumstances:

    - - - -

    2) TaskA2G#TASKSEAD class, extends TaskA2G#TASKA2G

    - -

    The #TASK_SEAD class defines a SEAD task for a Set of Target Units.

    -
    -

    3) TaskA2G#TASKCAS class, extends TaskA2G#TASKA2G

    - -

    The #TASK_CAS class defines a CAS task for a Set of Target Units.

    - -
    - -

    4) TaskA2G#TASKBAI class, extends TaskA2G#TASKA2G

    - -

    The #TASK_BAI class defines a BAI task for a Set of Target Units.

    - -
    - -

    API CHANGE HISTORY

    - -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    - -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    - -

    Hereby the change log:

    - -

    2017-03-09: Revised version.

    - -
    - -

    AUTHORS and CONTRIBUTIONS

    +

    Author: Sven Van de Velde (FlightControl)

    Contributions:

    -
      -
    • [WingThor]: Concept, Advice & Testing.
    • -
    - -

    Authors:

    - -
      -
    • FlightControl: Concept, Design & Programming. -
    • -
    +
    +

    Global(s)

    - + - + - +
    TASK_A2G +

    TASK_A2G class, extends Task#TASK

    +

    The TASK_A2G class defines Air To Ground tasks for a Set of Target Units, +based on the tasking capabilities defined in Task#TASK.

    TASK_BAITASK_A2G_BAI +

    TASKA2GBAI class, extends TaskA2G#TASKA2G

    +

    The TASKA2GBAI class defines an Battlefield Air Interdiction task for a human player to be executed.

    TASK_CASTASK_A2G_CAS +

    TASKA2GCAS class, extends TaskA2G#TASKA2G

    +

    The TASKA2GCAS class defines an Close Air Support task for a human player to be executed.

    TASK_SEADTASK_A2G_SEAD +

    TASKA2GSEAD class, extends TaskA2G#TASKA2G

    +

    The TASKA2GSEAD class defines an Suppression or Extermination of Air Defenses task for a human player to be executed.

    Type TASK_A2G

    - + @@ -194,7 +171,7 @@ The TASK_A2G is implemented using a Stat - + @@ -206,7 +183,7 @@ The TASK_A2G is implemented using a Stat - + @@ -218,19 +195,19 @@ The TASK_A2G is implemented using a Stat - + - + - + @@ -242,19 +219,13 @@ The TASK_A2G is implemented using a Stat - + - - - - - + @@ -273,66 +244,156 @@ The TASK_A2G is implemented using a Stat
    TASK_A2G.ClassNameTASK_A2G:GetGoalTotal()
    TASK_A2G:GetRendezVousPointVec2(TaskUnit)TASK_A2G:GetRendezVousCoordinate(TaskUnit)
    TASK_A2G:GetTargetPointVec2(TaskUnit)TASK_A2G:GetTargetCoordinate(TaskUnit)
    TASK_A2G:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit, TaskType)TASK_A2G:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit, TaskType, TaskBriefing)

    Instantiates a new TASK_A2G.

    TASK_A2G:SetPenaltyOnFailed(Text, Penalty, TaskUnit)TASK_A2G:SetGoalTotal() -

    Set a penalty when the A2G attack has failed.

    +
    TASK_A2G:SetRendezVousPointVec2(RendezVousPointVec2, RendezVousRange, TaskUnit)TASK_A2G:SetRendezVousCoordinate(RendezVousCoordinate, RendezVousRange, TaskUnit)
    TASK_A2G:SetScoreOnDestroy(Text, Score, TaskUnit)TASK_A2G:SetTargetCoordinate(TargetCoordinate, TaskUnit) -

    Set a score when a target in scope of the A2G attack, has been destroyed .

    +
    TASK_A2G:SetScoreOnSuccess(Text, Score, TaskUnit) -

    Set a score when all the targets in scope of the A2G attack, have been destroyed.

    -
    TASK_A2G:SetTargetPointVec2(TargetPointVec2, TaskUnit)TASK_A2G:SetTargetSetUnit(TargetSetUnit)
    -

    Type TASK_BAI

    +

    Type TASK_A2G_BAI

    - + + + + + - + - + + + + + + + + + + + + + + + + +
    TASK_BAI.ClassNameTASK_A2G_BAI:New(Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing) +

    Instantiates a new TASKA2GBAI.

    +
    TASK_A2G_BAI:ReportOrder(ReportGroup)
    TASK_BAI:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit)TASK_A2G_BAI:SetScoreOnFail(PlayerName, Penalty, TaskUnit) -

    Instantiates a new TASK_BAI.

    +

    Set a penalty when the A2G attack has failed.

    TASK_BAI.TargetSetUnitTASK_A2G_BAI:SetScoreOnProgress(PlayerName, Score, TaskUnit) +

    Set a score when a target in scope of the A2G attack, has been destroyed .

    +
    TASK_A2G_BAI:SetScoreOnSuccess(PlayerName, Score, TaskUnit) +

    Set a score when all the targets in scope of the A2G attack, have been destroyed.

    +
    TASK_A2G_BAI.TargetSetUnit + +
    TASK_A2G_BAI:UpdateTaskInfo() + +
    TASK_A2G_BAI:onafterGoal(TaskUnit, From, Event, To)
    -

    Type TASK_CAS

    +

    Type TASK_A2G_CAS

    - + + + + + - + - + + + + + + + + + + + + + + + + +
    TASK_CAS.ClassNameTASK_A2G_CAS:New(Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing) +

    Instantiates a new TASKA2GCAS.

    +
    TASK_A2G_CAS:ReportOrder(ReportGroup)
    TASK_CAS:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit)TASK_A2G_CAS:SetScoreOnFail(PlayerName, Penalty, TaskUnit) -

    Instantiates a new TASK_CAS.

    +

    Set a penalty when the A2G attack has failed.

    TASK_CAS.TargetSetUnitTASK_A2G_CAS:SetScoreOnProgress(PlayerName, Score, TaskUnit) +

    Set a score when a target in scope of the A2G attack, has been destroyed .

    +
    TASK_A2G_CAS:SetScoreOnSuccess(PlayerName, Score, TaskUnit) +

    Set a score when all the targets in scope of the A2G attack, have been destroyed.

    +
    TASK_A2G_CAS.TargetSetUnit + +
    TASK_A2G_CAS:UpdateTaskInfo() + +
    TASK_A2G_CAS:onafterGoal(TaskUnit, From, Event, To)
    -

    Type TASK_SEAD

    +

    Type TASK_A2G_SEAD

    - + + + + + - + - + + + + + + + + + + + + + + + + + @@ -350,6 +411,31 @@ The TASK_A2G is implemented using a Stat
    +

    TASK_A2G class, extends Task#TASK

    + +

    The TASK_A2G class defines Air To Ground tasks for a Set of Target Units, +based on the tasking capabilities defined in Task#TASK.

    + + +

    The TASK_A2G is implemented using a Fsm#FSM_TASK, and has the following statuses:

    + +
      +
    • None: Start of the process
    • +
    • Planned: The A2G task is planned.
    • +
    • Assigned: The A2G task is assigned to a Group#GROUP.
    • +
    • Success: The A2G task is successfully completed.
    • +
    • Failed: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
    • +
    + +

    Set the scoring of achievements in an A2G attack.

    + +

    Scoring or penalties can be given in the following circumstances:

    + +
    @@ -357,13 +443,23 @@ The TASK_A2G is implemented using a Stat
    - #TASK_BAI - -TASK_BAI + #TASK_A2G_BAI + +TASK_A2G_BAI
    +

    TASKA2GBAI class, extends TaskA2G#TASKA2G

    + +

    The TASKA2GBAI class defines an Battlefield Air Interdiction task for a human player to be executed.

    + + +

    These tasks are more strategic in nature and are most of the time further away from friendly forces. +BAI tasks can also be used to express the abscence of friendly forces near the vicinity.

    + +

    The TASKA2GBAI is used by the TaskA2GDispatcher#TASKA2GDISPATCHER to automatically create BAI tasks +based on detected enemy ground targets.

    @@ -371,13 +467,22 @@ The TASK_A2G is implemented using a Stat
    - #TASK_CAS - -TASK_CAS + #TASK_A2G_CAS + +TASK_A2G_CAS
    +

    TASKA2GCAS class, extends TaskA2G#TASKA2G

    + +

    The TASKA2GCAS class defines an Close Air Support task for a human player to be executed.

    + + +

    Friendly forces will be in the vicinity within 6km from the enemy.

    + +

    The TASKA2GCAS is used by the TaskA2GDispatcher#TASKA2GDISPATCHER to automatically create CAS tasks +based on detected enemy ground targets.

    @@ -385,13 +490,22 @@ The TASK_A2G is implemented using a Stat
    - #TASK_SEAD - -TASK_SEAD + #TASK_A2G_SEAD + +TASK_A2G_SEAD
    +

    TASKA2GSEAD class, extends TaskA2G#TASKA2G

    + +

    The TASKA2GSEAD class defines an Suppression or Extermination of Air Defenses task for a human player to be executed.

    + + +

    These tasks are important to be executed as they will help to achieve air superiority at the vicinity.

    + +

    The TASKA2GSEAD is used by the TaskA2GDispatcher#TASKA2GDISPATCHER to automatically create SEAD tasks +based on detected enemy ground targets.

    @@ -408,9 +522,8 @@ The TASK_A2G is implemented using a Stat
    - #string - -TASK_A2G.ClassName + +TASK_A2G:GetGoalTotal()
    @@ -435,8 +548,8 @@ The TASK_A2G is implemented using a Stat
    - -TASK_A2G:GetRendezVousPointVec2(TaskUnit) + +TASK_A2G:GetRendezVousCoordinate(TaskUnit)
    @@ -455,8 +568,8 @@ The TASK_A2G is implemented using a Stat
    1. -

      Core.Point#POINT_VEC2: -The PointVec2 object referencing to the 2D point where the RendezVous point is located on the map.

      +

      Core.Point#COORDINATE: +The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.

    2. @@ -497,8 +610,8 @@ The Zone object where the RendezVous is located on the map.

      - -TASK_A2G:GetTargetPointVec2(TaskUnit) + +TASK_A2G:GetTargetCoordinate(TaskUnit)
      @@ -515,8 +628,8 @@ The Zone object where the RendezVous is located on the map.

      Return value

      -

      Core.Point#POINT_VEC2: -The PointVec2 object where the Target is located on the map.

      +

      Core.Point#COORDINATE: +The Coordinate object where the Target is located on the map.

      @@ -550,7 +663,7 @@ The Zone object where the Target is located on the map.

      -TASK_A2G:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit, TaskType) +TASK_A2G:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit, TaskType, TaskBriefing)
      @@ -603,6 +716,11 @@ If the TargetZone parameter is specified, the player will be routed to the cente

      TaskType :

      +
    3. +
    4. + +

      TaskBriefing :

      +
    5. Return value

      @@ -615,37 +733,12 @@ self

      - -TASK_A2G:SetPenaltyOnFailed(Text, Penalty, TaskUnit) + +TASK_A2G:SetGoalTotal()
      -

      Set a penalty when the A2G attack has failed.

      - -

      Parameters

      -
        -
      • - -

        #string Text : -The text to display to the player, when the A2G attack has failed.

        - -
      • -
      • - -

        #number Penalty : -The penalty in points.

        - -
      • -
      • - -

        Wrapper.Unit#UNIT TaskUnit :

        - -
      • -
      -

      Return value

      - -

      #TASK_A2G:

      @@ -653,8 +746,8 @@ The penalty in points.

      - -TASK_A2G:SetRendezVousPointVec2(RendezVousPointVec2, RendezVousRange, TaskUnit) + +TASK_A2G:SetRendezVousCoordinate(RendezVousCoordinate, RendezVousRange, TaskUnit)
      @@ -665,8 +758,8 @@ The penalty in points.

      @@ -79,49 +111,11 @@
      -

      1) #TASKA2GDISPATCHER class, extends #DETECTION_MANAGER

      - -

      The #TASKA2GDISPATCHER class implements the dynamic dispatching of tasks upon groups of detected units determined a Set of FAC (groups). -The FAC will detect units, will group them, and will dispatch Tasks to groups. Depending on the type of target detected, different tasks will be dispatched. -Find a summary below describing for which situation a task type is created:

      - -
        -
      • CAS Task: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter.
      • -
      • BAI Task: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter.
      • -
      • SEAD Task: Is created when there are enemy ground units wihtin range of the FAC, with air search radars.
      • -
      - -

      Other task types will follow...

      - -

      3.1) TASKA2GDISPATCHER constructor:

      -

      The TASKA2GDISPATCHER.New() method creates a new TASKA2GDISPATCHER instance.

      - -
      - -

      API CHANGE HISTORY

      - -

      The underlying change log documents the API changes. Please read this carefully. The following notation is used:

      - -
        -
      • Added parts are expressed in bold type face.
      • -
      • Removed parts are expressed in italic type face.
      • -
      - -

      Hereby the change log:

      - -

      2017-03-09: Initial class and API.

      - -
      - -

      AUTHORS and CONTRIBUTIONS

      +

      Author: Sven Van de Velde (FlightControl)

      Contributions:

      -

      Authors:

      - -
        -
      • FlightControl: Concept, Design & Programming.
      • -
      +

      Global(s)

      @@ -129,19 +123,15 @@ Find a summary below describing for which situation a task type is created:

    TASK_SEAD.ClassNameTASK_A2G_SEAD:New(Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing) +

    Instantiates a new TASKA2GSEAD.

    +
    TASK_A2G_SEAD:ReportOrder(ReportGroup)
    TASK_SEAD:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetDistance, TargetZone, TargetSetUnit)TASK_A2G_SEAD:SetScoreOnFail(PlayerName, Penalty, TaskUnit) -

    Instantiates a new TASK_SEAD.

    +

    Set a penalty when the A2G attack has failed.

    TASK_SEAD.TargetSetUnitTASK_A2G_SEAD:SetScoreOnProgress(PlayerName, Score, TaskUnit) +

    Set a score when a target in scope of the A2G attack, has been destroyed .

    +
    TASK_A2G_SEAD:SetScoreOnSuccess(PlayerName, Score, TaskUnit) +

    Set a score when all the targets in scope of the A2G attack, have been destroyed.

    +
    TASK_A2G_SEAD.TargetSetUnit + +
    TASK_A2G_SEAD:UpdateTaskInfo() + +
    TASK_A2G_SEAD:onafterGoal(TaskUnit, From, Event, To)
    TASK_A2G_DISPATCHER +

    TASKA2GDISPATCHE} class, extends #DETECTION_MANAGER

    +

    The TASKA2GDISPATCHER class implements the dynamic dispatching of tasks upon groups of detected units determined a Set of FAC (groups).

    Type TASK_A2G_DISPATCHER

    - - - - - + @@ -193,6 +183,12 @@ Find a summary below describing for which situation a task type is created:

    + + + + @@ -214,6 +210,25 @@ Find a summary below describing for which situation a task type is created:

    +

    TASKA2GDISPATCHE} class, extends #DETECTION_MANAGER

    + +

    The TASKA2GDISPATCHER class implements the dynamic dispatching of tasks upon groups of detected units determined a Set of FAC (groups).

    + + +

    The FAC will detect units, will group them, and will dispatch Tasks to groups. Depending on the type of target detected, different tasks will be dispatched. +Find a summary below describing for which situation a task type is created:

    + +
      +
    • CAS Task: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter.
    • +
    • BAI Task: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter.
    • +
    • SEAD Task: Is created when there are enemy ground units wihtin range of the FAC, with air search radars.
    • +
    + +

    Other task types will follow...

    + +

    TASKA2GDISPATCHER constructor

    + +

    The TASKA2GDISPATCHER.New() method creates a new TASKA2GDISPATCHER instance.

    @@ -228,20 +243,6 @@ Find a summary below describing for which situation a task type is created:

    - #string - -TASK_A2G_DISPATCHER.ClassName - -
    -
    - - - -
    -
    -
    -
    - Functional.Detection#DETECTION_BASE TASK_A2G_DISPATCHER.Detection @@ -277,11 +278,21 @@ Find a summary below describing for which situation a task type is created:

    -

    Return value

    +

    Return values

    +
      +
    1. -

      Tasking.Task#TASK:

      +

      Core.Set#SET_UNIT: +TargetSetUnit: The target set of units.

      +
    2. +
    3. + +

      #nil: +If there are no targets to be set.

      +
    4. +
    @@ -303,18 +314,28 @@ Find a summary below describing for which situation a task type is created:

    -

    Return value

    +

    Return values

    +
      +
    1. -

      Tasking.Task#TASK:

      +

      Core.Set#SET_UNIT: +TargetSetUnit: The target set of units.

      +
    2. +
    3. + +

      #nil: +If there are no targets to be set.

      +
    4. +
    -TASK_A2G_DISPATCHER:EvaluateRemoveTask(Mission, Task, DetectedItem) +TASK_A2G_DISPATCHER:EvaluateRemoveTask(Mission, Task, DetectedItemID, DetectedItemChange, TaskIndex, DetectedItemChanged)
    @@ -338,7 +359,22 @@ Find a summary below describing for which situation a task type is created:

  • -

    Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem :

    +

    #boolean DetectedItemID :

    + +
  • +
  • + +

    #boolean DetectedItemChange :

    + +
  • +
  • + +

    TaskIndex :

    + +
  • +
  • + +

    DetectedItemChanged :

  • @@ -372,7 +408,7 @@ Find a summary below describing for which situation a task type is created:

    1. -

      Set#SET_UNIT: +

      Core.Set#SET_UNIT: TargetSetUnit: The target set of units.

    2. @@ -512,6 +548,27 @@ The detection created by the Detectio

      #boolean: Return true if you want the task assigning to continue... false will cancel the loop.

      +
    +
    +
    +
    + + +TASK_A2G_DISPATCHER:RemoveTask(TaskIndex) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      TaskIndex :

      + +
    • +
    diff --git a/docs/Documentation/Task_Cargo.html b/docs/Documentation/Task_Cargo.html new file mode 100644 index 000000000..743615651 --- /dev/null +++ b/docs/Documentation/Task_Cargo.html @@ -0,0 +1,1674 @@ + + + + + + +
    +
    + +
    +
    +
    +
    + +
    +

    Module Task_Cargo

    + +

    Tasking -- The TASK_CARGO models tasks for players to transport Cargo.

    + + + +

    Banner Image

    + +
    + +

    The Moose framework provides various CARGO classes that allow DCS phisical or logical objects to be transported or sling loaded by Carriers. +The CARGO_ classes, as part of the moose core, are able to Board, Load, UnBoard and UnLoad cargo between Carrier units.

    + +

    This collection of classes in this module define tasks for human players to handle these cargo objects. +Cargo can be transported, picked-up, deployed and sling-loaded from and to other places.

    + +

    The following classes are important to consider:

    + +
      +
    • #TASKCARGOTRANSPORT: Defines a task for a human player to transport a set of cargo between various zones.
    • +
    + +
    + +

    Author: Sven Van de Velde (FlightControl)

    + +

    Contributions:

    + +
    +

    + +

    Global(s)

    +
    TASK_A2G_DISPATCHER.ClassName - -
    TASK_A2G_DISPATCHER.Detection

    The DETECTION_BASE object that is used to report the detected objects.

    @@ -160,7 +150,7 @@ Find a summary below describing for which situation a task type is created:

    TASK_A2G_DISPATCHER:EvaluateRemoveTask(Mission, Task, DetectedItem)TASK_A2G_DISPATCHER:EvaluateRemoveTask(Mission, Task, DetectedItemID, DetectedItemChange, TaskIndex, DetectedItemChanged)

    Evaluates the removal of the Task from the Mission.

    TASK_A2G_DISPATCHER:ProcessDetected(Detection)

    Assigns tasks in relation to the detected items to the Set#SET_GROUP.

    +
    TASK_A2G_DISPATCHER:RemoveTask(TaskIndex) +
    + + + + + + + + +
    TASK_CARGO +

    TASK_CARGO class, extends Task#TASK

    + +

    A flexible tasking system

    + +

    The TASK_CARGO classes provide you with a flexible tasking sytem, +that allows you to transport cargo of various types between various locations +and various dedicated deployment zones.

    +
    TASK_CARGO_TRANSPORT + +
    +

    Type FSM_PROCESS

    + + + + + + + + + +
    FSM_PROCESS.Cargo + +
    FSM_PROCESS.DeployZone + +
    + +

    Type TASK_CARGO

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TASK_CARGO:AddDeployZone(DeployZone, TaskUnit) + +
    TASK_CARGO.CargoItemCount + +
    TASK_CARGO.CargoLimit + +
    TASK_CARGO.DeployZones + +
    TASK_CARGO:GetCargoSet() + +
    TASK_CARGO:GetDeployZones() + +
    TASK_CARGO:GetGoalTotal() + +
    TASK_CARGO:GetPlannedMenuText() + +
    TASK_CARGO:GetSmokeColor() + +
    TASK_CARGO:GetTargetZone(TaskUnit) + +
    TASK_CARGO:New(Mission, SetGroup, TaskName, SetCargo, TaskType, TaskBriefing) +

    Instantiates a new TASK_CARGO.

    +
    TASK_CARGO:RemoveDeployZone(DeployZone, TaskUnit) + +
    TASK_CARGO.SetCargo + +
    TASK_CARGO:SetCargoLimit(CargoLimit) +

    Set a limit on the amount of cargo items that can be loaded into the Carriers.

    +
    TASK_CARGO:SetCargoPickup(Cargo, TaskUnit) + +
    TASK_CARGO:SetDeployZone(DeployZone, TaskUnit) + +
    TASK_CARGO:SetDeployZones(@, TaskUnit, DeployZones) + +
    TASK_CARGO:SetGoalTotal() + +
    TASK_CARGO:SetScoreOnFail(Text, Penalty, TaskUnit) +

    Set a penalty when the A2G attack has failed.

    +
    TASK_CARGO:SetScoreOnProgress(Text, Score, TaskUnit) +

    Set a score when a target in scope of the A2G attack, has been destroyed .

    +
    TASK_CARGO:SetScoreOnSuccess(Text, Score, TaskUnit) +

    Set a score when all the targets in scope of the A2G attack, have been destroyed.

    +
    TASK_CARGO.SetSmokeColor(Color, self, SmokeColor) + +
    TASK_CARGO.SmokeColor + +
    TASK_CARGO.TaskType + +
    + +

    Type TASK_CARGO_TRANSPORT

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TASK_CARGO_TRANSPORT:CargoDeployed(TaskUnit, Cargo, DeployZone) +

    Synchronous Event Trigger for Event CargoDeployed.

    +
    TASK_CARGO_TRANSPORT:CargoPickedUp(TaskUnit, Cargo) +

    Synchronous Event Trigger for Event CargoPickedUp.

    +
    TASK_CARGO_TRANSPORT.ClassName + +
    TASK_CARGO_TRANSPORT:IsAllCargoTransported() + +
    TASK_CARGO_TRANSPORT:New(Mission, SetGroup, TaskName, SetCargo, TaskBriefing) +

    Instantiates a new TASKCARGOTRANSPORT.

    +
    TASK_CARGO_TRANSPORT:OnAfterCargoDeployed(From, Event, To, TaskUnit, Cargo, DeployZone) +

    OnAfter Transition Handler for Event CargoDeployed.

    +
    TASK_CARGO_TRANSPORT:OnAfterCargoPickedUp(From, Event, To, TaskUnit, Cargo) +

    OnAfter Transition Handler for Event CargoPickedUp.

    +
    TASK_CARGO_TRANSPORT:OnBeforeCargoDeployed(From, Event, To, TaskUnit, Cargo, DeployZone) +

    OnBefore Transition Handler for Event CargoDeployed.

    +
    TASK_CARGO_TRANSPORT:OnBeforeCargoPickedUp(From, Event, To, TaskUnit, Cargo) +

    OnBefore Transition Handler for Event CargoPickedUp.

    +
    TASK_CARGO_TRANSPORT:ReportOrder(ReportGroup) + +
    TASK_CARGO_TRANSPORT:__CargoDeployed(Delay, TaskUnit, Cargo, DeployZone) +

    Asynchronous Event Trigger for Event CargoDeployed.

    +
    TASK_CARGO_TRANSPORT:__CargoPickedUp(Delay, TaskUnit, Cargo) +

    Asynchronous Event Trigger for Event CargoPickedUp.

    +
    TASK_CARGO_TRANSPORT:onafterGoal(TaskUnit, From, Event, To) + +
    + +

    Global(s)

    +
    +
    + + #TASK_CARGO + +TASK_CARGO + +
    +
    + +

    TASK_CARGO class, extends Task#TASK

    + +

    A flexible tasking system

    + +

    The TASK_CARGO classes provide you with a flexible tasking sytem, +that allows you to transport cargo of various types between various locations +and various dedicated deployment zones.

    + + + +

    The cargo in scope of the TASKCARGO classes must be explicitly given, and is of type SETCARGO. +The SET_CARGO contains a collection of CARGO objects that must be handled by the players in the mission.

    + + +

    Task execution experience from the player perspective

    + +

    A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J). +The player needs to accept the task from the task overview list within the mission, using the radio menus.

    + +

    Once the TASK_CARGO is assigned to the player and accepted by the player, the player will obtain +an extra Cargo Handling Radio Menu that contains the CARGO objects that need to be transported.

    + +

    Each CARGO object has a certain state:

    + +
      +
    • UnLoaded: The CARGO is located within the battlefield. It may still need to be transported.
    • +
    • Loaded: The CARGO is loaded within a Carrier. This can be your air unit, or another air unit, or even a vehicle.
    • +
    • Boarding: The CARGO is running or moving towards your Carrier for loading.
    • +
    • UnBoarding: The CARGO is driving or jumping out of your Carrier and moves to a location in the Deployment Zone.
    • +
    + +

    Cargo must be transported towards different **Deployment Zones**.

    + +

    The Cargo Handling Radio Menu system allows to execute various actions to handle the cargo. +In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed. +Depending on the location of your Carrier unit, the menu options will vary.

    + + +

    Cargo Pickup and Boarding

    + +

    For cargo boarding, a cargo can only execute the boarding actions if it is within the foreseen Reporting Range. +Therefore, it is important that you steer your Carrier within the Reporting Range, +so that boarding actions can be executed on the cargo. +To Pickup and Board cargo, the following menu items will be shown in your carrier radio menu:

    + +

    Board Cargo

    + +

    If your Carrier is within the Reporting Range of the cargo, it will allow to pickup the cargo by selecting this menu option. +Depending on the Cargo type, the cargo will either move to your Carrier or you will receive instructions how to handle the cargo +pickup. If the cargo moves to your carrier, it will indicate the boarding status. +Note that multiple units need to board your Carrier, so it is required to await the full boarding process. +Once the cargo is fully boarded within your Carrier, you will be notified of this.

    + +

    Note that for airborne Carriers, it is required to land first before the Boarding process can be initiated. +If during boarding the Carrier gets airborne, the boarding process will be cancelled.

    + +

    Pickup Cargo

    + +

    If your Carrier is not within the Reporting Range of the cargo, the HQ will guide you to its location. +Routing information is shown in flight that directs you to the cargo within Reporting Range. +Upon arrival, the Cargo will contact you and further instructions will be given. +When your Carrier is airborne, you will receive instructions to land your Carrier. +The action will not be completed until you've landed your Carrier.

    + + +

    Cargo Deploy and UnBoarding

    + +

    Various Deployment Zones can be foreseen in the scope of the Cargo transportation. Each deployment zone can be of varying Zone type. +The Cargo Handling Radio Menu provides with menu options to execute an action to steer your Carrier to a specific Zone.

    + +

    UnBoard Cargo

    + +

    If your Carrier is already within a Deployment Zone, +then the Cargo Handling Radio Menu allows to UnBoard a specific cargo that is +loaded within your Carrier group into the Deployment Zone. +Note that the Unboarding process takes a while, as the cargo units (infantry or vehicles) must unload from your Carrier. +Ensure that you stay at the position or stay on the ground while Unboarding. +If any unforeseen manoeuvre is done by the Carrier, then the Unboarding will be cancelled.

    + +

    Deploy Cargo

    + +

    If your Carrier is not within a Deployment Zone, you'll need to fly towards one. +Fortunately, the Cargo Handling Radio Menu provides you with menu options to select a specific Deployment Zone to fly towards. +Once a Deployment Zone has been selected, your Carrier will receive routing information from HQ towards the Deployment Zone center. +Upon arrival, the HQ will provide you with further instructions. +When your Carrier is airborne, you will receive instructions to land your Carrier. +The action will not be completed until you've landed your Carrier!

    + +

    Handle TASK_CARGO Events ...

    + +

    The TASK_CARGO classes define Cargo transport tasks, +based on the tasking capabilities defined in Task#TASK.

    + +

    Specific TASK_CARGO Events

    + +

    Specific Cargo Handling event can be captured, that allow to trigger specific actions!

    + +
      +
    • Boarded: Triggered when the Cargo has been Boarded into your Carrier.
    • +
    • UnBoarded: Triggered when the cargo has been Unboarded from your Carrier and has arrived at the Deployment Zone.
    • +
    + +

    Standard TASK_CARGO Events

    + +

    The TASK_CARGO is implemented using a Statemachine#FSM_TASK, and has the following standard statuses:

    + +
      +
    • None: Start of the process.
    • +
    • Planned: The cargo task is planned.
    • +
    • Assigned: The cargo task is assigned to a Group#GROUP.
    • +
    • Success: The cargo task is successfully completed.
    • +
    • Failed: The cargo task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
    • +
    + +
    + + +
    +
    +
    +
    + + #TASK_CARGO_TRANSPORT + +TASK_CARGO_TRANSPORT + +
    +
    + + + +
    +
    +

    Type Task_Cargo

    + +

    Type FSM_PROCESS

    +

    Field(s)

    +
    +
    + + + +FSM_PROCESS.Cargo + +
    +
    + + + +
    +
    +
    +
    + + +FSM_PROCESS.DeployZone + +
    +
    + + + +
    +
    + +

    Type TASK_CARGO

    +

    Field(s)

    +
    +
    + + +TASK_CARGO:AddDeployZone(DeployZone, TaskUnit) + +
    +
    + + + +

    Parameters

    + +

    Return value

    + +

    #TASK_CARGO:

    + + +
    +
    +
    +
    + + + +TASK_CARGO.CargoItemCount + +
    +
    + + + + +

    Map of Carriers having a cargo item count to check the cargo loading limits.

    + +
    +
    +
    +
    + + #number + +TASK_CARGO.CargoLimit + +
    +
    + + + +
    +
    +
    +
    + + + +TASK_CARGO.DeployZones + +
    +
    + + + + +

    setmetatable( {}, { __mode = "v" } ) -- weak table on value

    + +
    +
    +
    +
    + + +TASK_CARGO:GetCargoSet() + +
    +
    + + + +

    Return value

    + +

    Core.Set#SET_CARGO: +The Cargo Set.

    + +
    +
    +
    +
    + + +TASK_CARGO:GetDeployZones() + +
    +
    + + + +

    Return value

    + +

    #list: +Core.Zone#ZONE_BASE> The Deployment Zones.

    + +
    +
    +
    +
    + + +TASK_CARGO:GetGoalTotal() + +
    +
    + + + +
    +
    +
    +
    + + +TASK_CARGO:GetPlannedMenuText() + +
    +
    + + + +
    +
    +
    +
    + + +TASK_CARGO:GetSmokeColor() + +
    +
    + + + + +

    @return SmokeColor

    + +
    +
    +
    +
    + + +TASK_CARGO:GetTargetZone(TaskUnit) + +
    +
    + + + +

    Parameter

    + +

    Return value

    + +

    Core.Zone#ZONE_BASE: +The Zone object where the Target is located on the map.

    + +
    +
    +
    +
    + + +TASK_CARGO:New(Mission, SetGroup, TaskName, SetCargo, TaskType, TaskBriefing) + +
    +
    + +

    Instantiates a new TASK_CARGO.

    + +

    Parameters

    +
      +
    • + +

      Tasking.Mission#MISSION Mission :

      + +
    • +
    • + +

      Set#SET_GROUP SetGroup : +The set of groups for which the Task can be assigned.

      + +
    • +
    • + +

      #string TaskName : +The name of the Task.

      + +
    • +
    • + +

      Core.Set#SET_CARGO SetCargo : +The scope of the cargo to be transported.

      + +
    • +
    • + +

      #string TaskType : +The type of Cargo task.

      + +
    • +
    • + +

      #string TaskBriefing : +The Cargo Task briefing.

      + +
    • +
    +

    Return value

    + +

    #TASK_CARGO: +self

    + +
    +
    +
    +
    + + +TASK_CARGO:RemoveDeployZone(DeployZone, TaskUnit) + +
    +
    + + + +

    Parameters

    + +

    Return value

    + +

    #TASK_CARGO:

    + + +
    +
    +
    +
    + + + +TASK_CARGO.SetCargo + +
    +
    + + + +
    +
    +
    +
    + + +TASK_CARGO:SetCargoLimit(CargoLimit) + +
    +
    + +

    Set a limit on the amount of cargo items that can be loaded into the Carriers.

    + +

    Parameter

    +
      +
    • + +

      CargoLimit : +Specifies a number of cargo items that can be loaded in the helicopter.

      + +
    • +
    +

    Return value

    + +

    #TASK_CARGO:

    + + +
    +
    +
    +
    + + +TASK_CARGO:SetCargoPickup(Cargo, TaskUnit) + +
    +
    + + + +

    Parameters

    + +

    Return value

    + +

    #TASK_CARGO:

    + + +
    +
    +
    +
    + + +TASK_CARGO:SetDeployZone(DeployZone, TaskUnit) + +
    +
    + + + +

    Parameters

    + +

    Return value

    + +

    #TASK_CARGO:

    + + +
    +
    +
    +
    + + +TASK_CARGO:SetDeployZones(@, TaskUnit, DeployZones) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      @ : +ist DeployZones

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    • + +

      DeployZones :

      + +
    • +
    +

    Return value

    + +

    #TASK_CARGO:

    + + +
    +
    +
    +
    + + +TASK_CARGO:SetGoalTotal() + +
    +
    + + + +
    +
    +
    +
    + + +TASK_CARGO:SetScoreOnFail(Text, Penalty, TaskUnit) + +
    +
    + +

    Set a penalty when the A2G attack has failed.

    + +

    Parameters

    +
      +
    • + +

      #string Text : +The text to display to the player, when the A2G attack has failed.

      + +
    • +
    • + +

      #number Penalty : +The penalty in points.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK_CARGO:

    + + +
    +
    +
    +
    + + +TASK_CARGO:SetScoreOnProgress(Text, Score, TaskUnit) + +
    +
    + +

    Set a score when a target in scope of the A2G attack, has been destroyed .

    + +

    Parameters

    +
      +
    • + +

      #string Text : +The text to display to the player, when the target has been destroyed.

      + +
    • +
    • + +

      #number Score : +The score in points.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK_CARGO:

    + + +
    +
    +
    +
    + + +TASK_CARGO:SetScoreOnSuccess(Text, Score, TaskUnit) + +
    +
    + +

    Set a score when all the targets in scope of the A2G attack, have been destroyed.

    + +

    Parameters

    +
      +
    • + +

      #string Text : +The text to display to the player, when all targets hav been destroyed.

      + +
    • +
    • + +

      #number Score : +The score in points.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit :

      + +
    • +
    +

    Return value

    + +

    #TASK_CARGO:

    + + +
    +
    +
    +
    + + +TASK_CARGO.SetSmokeColor(Color, self, SmokeColor) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      Color : +Might be SMOKECOLOR.Blue, SMOKECOLOR.Red SMOKECOLOR.Orange, SMOKECOLOR.White or SMOKECOLOR.Green

      + +
    • +
    • + +

      self :

      + +
    • +
    • + +

      SmokeColor :

      + +
    • +
    +
    +
    +
    +
    + + + +TASK_CARGO.SmokeColor + +
    +
    + + + +
    +
    +
    +
    + + + +TASK_CARGO.TaskType + +
    +
    + + + +
    +
    + +

    Type TASK_CARGO_TRANSPORT

    + +

    The TASKCARGOTRANSPORT class

    + +

    Field(s)

    +
    +
    + + +TASK_CARGO_TRANSPORT:CargoDeployed(TaskUnit, Cargo, DeployZone) + +
    +
    + +

    Synchronous Event Trigger for Event CargoDeployed.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Unit#UNIT TaskUnit : +The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.

      + +
    • +
    • + +

      Core.Cargo#CARGO Cargo : +The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.

      + +
    • +
    • + +

      Core.Zone#ZONE DeployZone : +The zone where the Cargo got Deployed or UnBoarded.

      + +
    • +
    +
    +
    +
    +
    + + +TASK_CARGO_TRANSPORT:CargoPickedUp(TaskUnit, Cargo) + +
    +
    + +

    Synchronous Event Trigger for Event CargoPickedUp.

    + +

    Parameters

    +
      +
    • + +

      Wrapper.Unit#UNIT TaskUnit : +The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.

      + +
    • +
    • + +

      Core.Cargo#CARGO Cargo : +The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.

      + +
    • +
    +
    +
    +
    +
    + + #string + +TASK_CARGO_TRANSPORT.ClassName + +
    +
    + + + +
    +
    +
    +
    + + +TASK_CARGO_TRANSPORT:IsAllCargoTransported() + +
    +
    + + + +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + + +TASK_CARGO_TRANSPORT:New(Mission, SetGroup, TaskName, SetCargo, TaskBriefing) + +
    +
    + +

    Instantiates a new TASKCARGOTRANSPORT.

    + +

    Parameters

    +
      +
    • + +

      Tasking.Mission#MISSION Mission :

      + +
    • +
    • + +

      Set#SET_GROUP SetGroup : +The set of groups for which the Task can be assigned.

      + +
    • +
    • + +

      #string TaskName : +The name of the Task.

      + +
    • +
    • + +

      Core.Set#SET_CARGO SetCargo : +The scope of the cargo to be transported.

      + +
    • +
    • + +

      #string TaskBriefing : +The Cargo Task briefing.

      + +
    • +
    +

    Return value

    + +

    #TASKCARGOTRANSPORT: +self

    + +
    +
    +
    +
    + + +TASK_CARGO_TRANSPORT:OnAfterCargoDeployed(From, Event, To, TaskUnit, Cargo, DeployZone) + +
    +
    + +

    OnAfter Transition Handler for Event CargoDeployed.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit : +The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.

      + +
    • +
    • + +

      Core.Cargo#CARGO Cargo : +The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.

      + +
    • +
    • + +

      Core.Zone#ZONE DeployZone : +The zone where the Cargo got Deployed or UnBoarded.

      + +
    • +
    +
    +
    +
    +
    + + +TASK_CARGO_TRANSPORT:OnAfterCargoPickedUp(From, Event, To, TaskUnit, Cargo) + +
    +
    + +

    OnAfter Transition Handler for Event CargoPickedUp.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit : +The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.

      + +
    • +
    • + +

      Core.Cargo#CARGO Cargo : +The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.

      + +
    • +
    +
    +
    +
    +
    + + +TASK_CARGO_TRANSPORT:OnBeforeCargoDeployed(From, Event, To, TaskUnit, Cargo, DeployZone) + +
    +
    + +

    OnBefore Transition Handler for Event CargoDeployed.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit : +The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.

      + +
    • +
    • + +

      Core.Cargo#CARGO Cargo : +The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.

      + +
    • +
    • + +

      Core.Zone#ZONE DeployZone : +The zone where the Cargo got Deployed or UnBoarded.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +TASK_CARGO_TRANSPORT:OnBeforeCargoPickedUp(From, Event, To, TaskUnit, Cargo) + +
    +
    + +

    OnBefore Transition Handler for Event CargoPickedUp.

    + +

    Parameters

    +
      +
    • + +

      #string From : +The From State string.

      + +
    • +
    • + +

      #string Event : +The Event string.

      + +
    • +
    • + +

      #string To : +The To State string.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit : +The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.

      + +
    • +
    • + +

      Core.Cargo#CARGO Cargo : +The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.

      + +
    • +
    +

    Return value

    + +

    #boolean: +Return false to cancel Transition.

    + +
    +
    +
    +
    + + +TASK_CARGO_TRANSPORT:ReportOrder(ReportGroup) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      ReportGroup :

      + +
    • +
    +
    +
    +
    +
    + + +TASK_CARGO_TRANSPORT:__CargoDeployed(Delay, TaskUnit, Cargo, DeployZone) + +
    +
    + +

    Asynchronous Event Trigger for Event CargoDeployed.

    + +

    Parameters

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit : +The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.

      + +
    • +
    • + +

      Core.Cargo#CARGO Cargo : +The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.

      + +
    • +
    • + +

      Core.Zone#ZONE DeployZone : +The zone where the Cargo got Deployed or UnBoarded.

      + +
    • +
    +
    +
    +
    +
    + + +TASK_CARGO_TRANSPORT:__CargoPickedUp(Delay, TaskUnit, Cargo) + +
    +
    + +

    Asynchronous Event Trigger for Event CargoPickedUp.

    + +

    Parameters

    +
      +
    • + +

      #number Delay : +The delay in seconds.

      + +
    • +
    • + +

      Wrapper.Unit#UNIT TaskUnit : +The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.

      + +
    • +
    • + +

      Core.Cargo#CARGO Cargo : +The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.

      + +
    • +
    +
    +
    +
    +
    + + +TASK_CARGO_TRANSPORT:onafterGoal(TaskUnit, From, Event, To) + +
    +
    + + + +

    Parameters

    +
      +
    • + +

      TaskUnit :

      + +
    • +
    • + +

      From :

      + +
    • +
    • + +

      Event :

      + +
    • +
    • + +

      To :

      + +
    • +
    +
    +
    + +

    Type list

    + + + + + + diff --git a/docs/Documentation/Task_PICKUP.html b/docs/Documentation/Task_PICKUP.html index fe83f60db..327fe472e 100644 --- a/docs/Documentation/Task_PICKUP.html +++ b/docs/Documentation/Task_PICKUP.html @@ -17,9 +17,16 @@ index diff --git a/docs/Documentation/Task_SEAD.html b/docs/Documentation/Task_SEAD.html deleted file mode 100644 index a998527ee..000000000 --- a/docs/Documentation/Task_SEAD.html +++ /dev/null @@ -1,256 +0,0 @@ - - - - - - -
    -
    - -
    -
    -
    -
    - -
    -

    Module Task_SEAD

    - -

    This module contains the TASK_SEAD classes.

    - - - -

    1) #TASK_SEAD class, extends Task#TASK

    -

    The #TASK_SEAD class defines a SEAD task for a Set of Target Units, located at a Target Zone, -based on the tasking capabilities defined in Task#TASK. -The TASK_SEAD is implemented using a Statemachine#FSM_TASK, and has the following statuses:

    - -
      -
    • None: Start of the process
    • -
    • Planned: The SEAD task is planned. Upon Planned, the sub-process ProcessFsm.Assign#ACTASSIGN_ACCEPT is started to accept the task.
    • -
    • Assigned: The SEAD task is assigned to a Group#GROUP. Upon Assigned, the sub-process ProcessFsm.Route#ACTROUTE is started to route the active Units in the Group to the attack zone.
    • -
    • Success: The SEAD task is successfully completed. Upon Success, the sub-process ProcessSEAD#PROCESSSEAD is started to follow-up successful SEADing of the targets assigned in the task.
    • -
    • Failed: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
    • -
    - -
    - -

    Authors: FlightControl - Design and Programming

    - - -

    Global(s)

    - - - - - -
    TASK_SEAD - -
    -

    Type TASK_SEAD

    - - - - - - - - - - - - - - - - - -
    TASK_SEAD.ClassName - -
    TASK_SEAD:GetPlannedMenuText() - -
    TASK_SEAD:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetZone, TargetSetUnit) -

    Instantiates a new TASK_SEAD.

    -
    TASK_SEAD.TargetSetUnit - -
    - -

    Global(s)

    -
    -
    - - #TASK_SEAD - -TASK_SEAD - -
    -
    - - - -
    -
    -

    Type Task_SEAD

    - -

    Type TASK_SEAD

    - -

    The TASK_SEAD class

    - -

    Field(s)

    -
    -
    - - #string - -TASK_SEAD.ClassName - -
    -
    - - - -
    -
    -
    -
    - - -TASK_SEAD:GetPlannedMenuText() - -
    -
    - - - -
    -
    -
    -
    - - -TASK_SEAD:New(Mission, SetGroup, TaskName, UnitSetTargets, TargetZone, TargetSetUnit) - -
    -
    - -

    Instantiates a new TASK_SEAD.

    - -

    Parameters

    - -

    Return value

    - -

    #TASK_SEAD: -self

    - -
    -
    -
    -
    - - Set#SET_UNIT - -TASK_SEAD.TargetSetUnit - -
    -
    - - - -
    -
    - -
    - -
    - - diff --git a/docs/Documentation/Unit.html b/docs/Documentation/Unit.html index 097aa6cf3..2185df762 100644 --- a/docs/Documentation/Unit.html +++ b/docs/Documentation/Unit.html @@ -17,9 +17,16 @@ index
    @@ -85,10 +117,18 @@
  • Support all DCS Unit APIs.
  • Enhance with Unit specific APIs not in the DCS Unit API set.
  • Handle local Unit Controller.
  • -
  • Manage the "state" of the DCS Unit. -
  • +
  • Manage the "state" of the DCS Unit.
  • +
    + +

    Author: Sven Van de Velde (FlightControl)

    + +

    Contributions:

    + +
    + +

    Global(s)

    @@ -112,36 +152,6 @@ - - - - - - - - - - - - - - - - - - - - @@ -171,7 +181,7 @@ @@ -278,6 +288,12 @@ + + + + @@ -296,6 +312,12 @@ + + + + @@ -335,39 +357,9 @@ - + - - - - - - - - - - - - - - - - - - - - @@ -470,13 +462,22 @@ If you want to obtain the complete 3D position including ori�

    The UNIT class contains methods to test the location or proximity against zones or other objects.

    -

    Zones

    +

    Zones range

    To test whether the Unit is within a zone, use the UNIT.IsInZone() or the UNIT.IsNotInZone() methods. Any zone can be tested on, but the zone must be derived from Zone#ZONE_BASE.

    -

    Units

    +

    Unit range

    + +
      +
    • Test if another DCS Unit is within a given radius of the current DCS Unit, use the UNIT.OtherUnitInRadius() method.
    • +
    + +

    Test Line of Sight

    + +
      +
    • Use the UNIT.IsLOS() method to check if the given unit is within line of sight.
    • +
    -

    Test if another DCS Unit is within a given radius of the current DCS Unit, use the UNIT.OtherUnitInRadius() method.

    @@ -542,79 +543,6 @@ self

    - -UNIT:Flare(FlareColor) - -
    -
    - -

    Signal a flare at the position of the UNIT.

    - -

    Parameter

    - -
    -
    -
    -
    - - -UNIT:FlareGreen() - -
    -
    - -

    Signal a green flare at the position of the UNIT.

    - -
    -
    -
    -
    - - -UNIT:FlareRed() - -
    -
    - -

    Signal a red flare at the position of the UNIT.

    - -
    -
    -
    -
    - - -UNIT:FlareWhite() - -
    -
    - -

    Signal a white flare at the position of the UNIT.

    - -
    -
    -
    -
    - - -UNIT:FlareYellow() - -
    -
    - -

    Signal a yellow flare at the position of the UNIT.

    - -
    -
    -
    -
    - UNIT:GetAmmo() @@ -713,7 +641,7 @@ Category name = Helicopter, Airplane, Ground Unit, Ship

    -

    Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks.

    +

    Returns relative amount of fuel (from 0.0 to 1.0) the UNIT has in its internal tanks.

    If there are additional fuel tanks the value may be greater than 1.0.

    @@ -1250,6 +1178,32 @@ if the Unit is not existing or is not alive.

    + +UNIT:IsDetected(TargetUnit) + +
    +
    + +

    Returns if a unit is detecting the TargetUnit.

    + +

    Parameter

    +
      +
    • + +

      #UNIT TargetUnit :

      + +
    • +
    +

    Return value

    + +

    #boolean: +true If the TargetUnit is detected by the unit, otherwise false.

    + +
    +
    +
    +
    + UNIT:IsFriendly(FriendlyCoalition) @@ -1324,6 +1278,32 @@ Returns true if the unit is within the Zone#ZON
    + +UNIT:IsLOS(TargetUnit) + +
    +
    + +

    Returns if a unit has Line of Sight (LOS) with the TargetUnit.

    + +

    Parameter

    +
      +
    • + +

      #UNIT TargetUnit :

      + +
    • +
    +

    Return value

    + +

    #boolean: +true If the TargetUnit has LOS with the unit, otherwise false.

    + +
    +
    +
    +
    + UNIT:IsNotInZone(Zone) @@ -1499,91 +1479,18 @@ The name of the DCS unit.

    - -UNIT:Smoke(SmokeColor, Range) + +UNIT:ResetEvents()
    -

    Smoke the UNIT.

    +

    Reset the subscriptions.

    -

    Parameters

    -
      -
    • +

      Return value

      -

      SmokeColor :

      +

      #UNIT:

      -
    • -
    • - -

      Range :

      - -
    • -
    -
    -
    -
    -
    - - -UNIT:SmokeBlue() - -
    -
    - -

    Smoke the UNIT Blue.

    - -
    -
    -
    -
    - - -UNIT:SmokeGreen() - -
    -
    - -

    Smoke the UNIT Green.

    - -
    -
    -
    -
    - - -UNIT:SmokeOrange() - -
    -
    - -

    Smoke the UNIT Orange.

    - -
    -
    -
    -
    - - -UNIT:SmokeRed() - -
    -
    - -

    Smoke the UNIT Red.

    - -
    -
    -
    -
    - - -UNIT:SmokeWhite() - -
    -
    - -

    Smoke the UNIT White.

    diff --git a/docs/Documentation/Utils.html b/docs/Documentation/Utils.html index 59585ad25..13adb0b4a 100644 --- a/docs/Documentation/Utils.html +++ b/docs/Documentation/Utils.html @@ -17,9 +17,16 @@ index @@ -204,12 +236,41 @@ which are excellent tools to be reused in an OO environment!.

    + + + + + + + + @@ -271,12 +332,24 @@ use negative idp for rounding ahead of decimal place, positive for rounding afte + + + + + + + +
    UNIT:FindByName(UnitName)

    Find a UNIT in the _DATABASE using the name of an existing DCS Unit.

    -
    UNIT:Flare(FlareColor) -

    Signal a flare at the position of the UNIT.

    -
    UNIT:FlareGreen() -

    Signal a green flare at the position of the UNIT.

    -
    UNIT:FlareRed() -

    Signal a red flare at the position of the UNIT.

    -
    UNIT:FlareWhite() -

    Signal a white flare at the position of the UNIT.

    -
    UNIT:FlareYellow() -

    Signal a yellow flare at the position of the UNIT.

    UNIT:GetFuel() -

    Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks.

    +

    Returns relative amount of fuel (from 0.0 to 1.0) the UNIT has in its internal tanks.

    UNIT:IsAlive()

    Returns if the Unit is alive.

    +
    UNIT:IsDetected(TargetUnit) +

    Returns if a unit is detecting the TargetUnit.

    UNIT:IsInZone(Zone)

    Returns true if the unit is within a Zone.

    +
    UNIT:IsLOS(TargetUnit) +

    Returns if a unit has Line of Sight (LOS) with the TargetUnit.

    UNIT:Smoke(SmokeColor, Range)UNIT:ResetEvents() -

    Smoke the UNIT.

    -
    UNIT:SmokeBlue() -

    Smoke the UNIT Blue.

    -
    UNIT:SmokeGreen() -

    Smoke the UNIT Green.

    -
    UNIT:SmokeOrange() -

    Smoke the UNIT Orange.

    -
    UNIT:SmokeRed() -

    Smoke the UNIT Red.

    -
    UNIT:SmokeWhite() -

    Smoke the UNIT White.

    +

    Reset the subscriptions.

    UTILS.FeetToMeters(feet) +
    UTILS.IsInstanceOf(object, className) +

    Function to infer instance of an object

    + +

    Examples:

    + +
      +
    • UTILS.IsInstanceOf( 'some text', 'string' ) will return true

    • +
    • UTILS.IsInstanceOf( some_function, 'function' ) will return true

    • +
    • UTILS.IsInstanceOf( 10, 'number' ) will return true

    • +
    • UTILS.IsInstanceOf( false, 'boolean' ) will return true

    • +
    • UTILS.IsInstanceOf( nil, 'nil' ) will return true

    • +
    • UTILS.IsInstanceOf( ZONE:New( 'some zone', ZONE ) will return true

    • +
    • UTILS.IsInstanceOf( ZONE:New( 'some zone', 'ZONE' ) will return true

    • +
    • UTILS.IsInstanceOf( ZONE:New( 'some zone', 'zone' ) will return true

    • +
    • UTILS.IsInstanceOf( ZONE:New( 'some zone', 'BASE' ) will return true

    • +
    • UTILS.IsInstanceOf( ZONE:New( 'some zone', 'GROUP' ) will return false

    • +
    + +
    UTILS.KmphToMps(kmph) +
    UTILS.KnotsToKmph(knots) +
    UTILS.ToRadian(angle) +
    UTILS.spairs(t, order) +
    UTILS.tostringLL(lat, lon, acc, DMS) +
    UTILS.tostringMGRS(MGRS, acc) +
    @@ -564,6 +637,56 @@ use negative idp for rounding ahead of decimal place, positive for rounding afte
    + +UTILS.IsInstanceOf(object, className) + +
    +
    + +

    Function to infer instance of an object

    + +

    Examples:

    + +
      +
    • UTILS.IsInstanceOf( 'some text', 'string' ) will return true

    • +
    • UTILS.IsInstanceOf( some_function, 'function' ) will return true

    • +
    • UTILS.IsInstanceOf( 10, 'number' ) will return true

    • +
    • UTILS.IsInstanceOf( false, 'boolean' ) will return true

    • +
    • UTILS.IsInstanceOf( nil, 'nil' ) will return true

    • +
    • UTILS.IsInstanceOf( ZONE:New( 'some zone', ZONE ) will return true

    • +
    • UTILS.IsInstanceOf( ZONE:New( 'some zone', 'ZONE' ) will return true

    • +
    • UTILS.IsInstanceOf( ZONE:New( 'some zone', 'zone' ) will return true

    • +
    • UTILS.IsInstanceOf( ZONE:New( 'some zone', 'BASE' ) will return true

    • +
    • UTILS.IsInstanceOf( ZONE:New( 'some zone', 'GROUP' ) will return false

    • +
    + + + +

    Parameters

    +
      +
    • + +

      object : +is the object to be evaluated

      + +
    • +
    • + +

      className : +is the name of the class to evaluate (can be either a string or a Moose class)

      + +
    • +
    +

    Return value

    + +

    #boolean:

    + + +
    +
    +
    +
    + UTILS.KmphToMps(kmph) @@ -585,6 +708,27 @@ use negative idp for rounding ahead of decimal place, positive for rounding afte
    + +UTILS.KnotsToKmph(knots) + +
    +
    + + + +

    Parameter

    +
      +
    • + +

      knots :

      + +
    • +
    +
    +
    +
    +
    + UTILS.KnotsToMps(knots) @@ -804,6 +948,35 @@ use negative idp for rounding ahead of decimal place, positive for rounding afte
    + +UTILS.spairs(t, order) + +
    +
    + + + + +

    Here is a customized version of pairs, which I called spairs because it iterates over the table in a sorted order.

    + +

    Parameters

    +
      +
    • + +

      t :

      + +
    • +
    • + +

      order :

      + +
    • +
    +
    +
    +
    +
    + UTILS.tostringLL(lat, lon, acc, DMS) @@ -845,6 +1018,35 @@ So: +
    +
    +
    + + +UTILS.tostringMGRS(MGRS, acc) + +
    +
    + + + + +

    acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5.

    + +

    Parameters

    +
      +
    • + +

      MGRS :

      + +
    • +
    • + +

      acc :

      + +
    • +
    +
    diff --git a/docs/Documentation/Zone.html b/docs/Documentation/Zone.html index 86c4fee62..67e25dafa 100644 --- a/docs/Documentation/Zone.html +++ b/docs/Documentation/Zone.html @@ -17,9 +17,16 @@ index

    Module Zone

    -

    Core - ZONE classes define zones within your mission of various forms, with various capabilities.

    +

    Core -- ZONE classes define zones within your mission of various forms, with various capabilities.

    @@ -112,39 +144,8 @@
    -

    API CHANGE HISTORY

    - -

    The underlying change log documents the API changes. Please read this carefully. The following notation is used:

    - -
      -
    • Added parts are expressed in bold type face.
    • -
    • Removed parts are expressed in italic type face.
    • -
    - -

    Hereby the change log:

    - -

    2017-02-28: ZONE_BASE:IsVec2InZone() replaces ZONE_BASE:IsPointVec2InZone().
    -2017-02-28: ZONE_BASE:IsVec3InZone() replaces ZONE_BASE:IsPointVec3InZone().
    -2017-02-28: ZONE_RADIUS:IsVec2InZone() replaces ZONE_RADIUS:IsPointVec2InZone().
    -2017-02-28: ZONE_RADIUS:IsVec3InZone() replaces ZONE_RADIUS:IsPointVec3InZone().
    -2017-02-28: ZONE_POLYGON:IsVec2InZone() replaces ZONE_POLYGON:IsPointVec2InZone().
    -2017-02-28: ZONE_POLYGON:IsVec3InZone() replaces ZONE_POLYGON:IsPointVec3InZone().

    - -

    2017-02-18: ZONE_POLYGON_BASE:GetRandomPointVec2() added.

    - -

    2017-02-18: ZONE_POLYGON_BASE:GetRandomPointVec3() added.

    - -

    2017-02-18: ZONE_RADIUS:GetRandomPointVec3( inner, outer ) added.

    - -

    2017-02-18: ZONE_RADIUS:GetRandomPointVec2( inner, outer ) added.

    - -

    2016-08-15: ZONE_BASE:GetName() added.

    - -

    2016-08-15: ZONE_BASE:SetZoneProbability( ZoneProbability ) added.

    - -

    2016-08-15: ZONE_BASE:GetZoneProbability() added.

    - -

    2016-08-15: ZONE_BASE:GetZoneMaybe() added.

    +

    Author: Sven Van de Velde (FlightControl)

    +

    Contributions:


    @@ -154,7 +155,7 @@
    ZONE -

    3) ZONE class, extends Zone#ZONE_RADIUS

    +

    ZONE class, extends Zone#ZONE_RADIUS

    The ZONE class, defined by the zone name as defined within the Mission Editor.

    ZONE_BASE -

    1) ZONE_BASE class, extends Base#BASE

    +

    ZONE_BASE class, extends Base#BASE

    This class is an abstract BASE class for derived classes, and is not meant to be instantiated.

    ZONE_GROUP -

    5) #ZONE_GROUP class, extends Zone#ZONE_RADIUS

    +

    ZONE_GROUP class, extends Zone#ZONE_RADIUS

    The ZONE_GROUP class defines by a zone around a Group#GROUP with a radius.

    ZONE_POLYGON -

    7) ZONE_POLYGON class, extends Zone#ZONEPOLYGONBASE

    +

    ZONE_POLYGON class, extends Zone#ZONEPOLYGONBASE

    The ZONE_POLYGON class defined by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon.

    ZONE_POLYGON_BASE -

    6) ZONEPOLYGONBASE class, extends Zone#ZONE_BASE

    +

    ZONEPOLYGONBASE class, extends Zone#ZONE_BASE

    The ZONEPOLYGONBASE class defined by a sequence of Group#GROUP waypoints within the Mission Editor, forming a polygon.

    ZONE_RADIUS -

    2) Zone#ZONE_RADIUS class, extends Zone#ZONE_BASE

    +

    ZONE_RADIUS class, extends Zone#ZONE_BASE

    The ZONE_RADIUS class defined by a zone name, a location and a radius.

    ZONE_UNIT -

    4) #ZONE_UNIT class, extends Zone#ZONE_RADIUS

    +

    ZONE_UNIT class, extends Zone#ZONE_RADIUS

    The ZONE_UNIT class defined by a zone around a Unit#UNIT with a radius.

    ZONE_BASE:GetBoundingSquare()

    Get the bounding square the zone.

    +
    ZONE_BASE:GetCoordinate() +

    Returns a Point#COORDINATE of the zone.

    ZONE_BASE:GetZoneProbability()

    Get the randomization probability of a zone to be selected.

    +
    ZONE_BASE:IsPointVec2InZone(PointVec2) +

    Returns if a PointVec2 is within the zone.

    +
    ZONE_BASE:IsPointVec3InZone(PointVec3) +

    Returns if a PointVec3 is within the zone.

    ZONE_BASE:IsVec2InZone(Vec2) -

    Returns if a location is within the zone.

    +

    Returns if a Vec2 is within the zone.

    ZONE_BASE:IsVec3InZone(Vec3) -

    Returns if a point is within the zone.

    +

    Returns if a Vec3 is within the zone.

    + + + + - - - -
    ZONE_GROUP:GetRandomPointVec2(inner, outer) +

    Returns a Point#POINT_VEC2 object reflecting a random 2D location within the zone.

    +
    ZONE_GROUP:GetRandomVec2()

    Returns a random location within the zone of the Group.

    @@ -382,12 +407,6 @@
    ZONE_GROUP:New(ZoneName, ZoneGROUP, Radius)

    Constructor to create a ZONE_GROUP instance, taking the zone name, a zone Group#GROUP and a radius.

    -
    ZONE_GROUP.ZoneGROUP -
    @@ -438,6 +457,12 @@
    ZONE_POLYGON_BASE:GetRandomVec2()

    Define a random DCSTypes#Vec2 within the zone.

    +
    ZONE_POLYGON_BASE:GetVec2() +

    Returns the center location of the polygon.

    ZONE_POLYGON_BASE:New(ZoneName, PointsArray)

    Constructor to create a ZONEPOLYGONBASE instance, taking the zone name and an array of DCSTypes#Vec2, forming a polygon.

    -
    ZONE_POLYGON_BASE.Polygon -

    The polygon defined by an array of DCSTypes#Vec2.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -135,13 +260,13 @@ CLIENTS in a SET_CLIENT collection, which are not occupied by human players.

    @@ -153,60 +278,145 @@ CLIENTS in a SET_CLIENT collection, which are not occupied by human players.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -218,32 +428,32 @@ CLIENTS in a SET_CLIENT collection, which are not occupied by human players.

    @@ -255,43 +465,43 @@ are design patterns allowing efficient (long-lasting) processes and workflows. @@ -309,7 +519,7 @@ are design patterns allowing efficient (long-lasting) processes and workflows. @@ -321,25 +531,25 @@ are design patterns allowing efficient (long-lasting) processes and workflows. + + + + @@ -371,28 +587,49 @@ and creates a CSV file logging the scoring events and results for use at team or + + + + + + + + + + + + + + + + + + + + @@ -405,6 +642,12 @@ and creates a CSV file logging the scoring events and results for use at team or + + + + @@ -429,7 +672,19 @@ which are excellent tools to be reused in an OO environment!.

    + + + + + + + + diff --git a/docs/Documentation/land.html b/docs/Documentation/land.html index 4861ee69b..e4a3153a4 100644 --- a/docs/Documentation/land.html +++ b/docs/Documentation/land.html @@ -17,7 +17,17 @@ index diff --git a/docs/Installation/LDT_Project_Existing_Location.JPG b/docs/Installation/LDT_Project_Existing_Location.JPG new file mode 100644 index 000000000..a36765470 Binary files /dev/null and b/docs/Installation/LDT_Project_Existing_Location.JPG differ diff --git a/docs/Presentations/ACT_ACCOUNT/Dia1.JPG b/docs/Presentations/ACT_ACCOUNT/Dia1.JPG index dbb082a48..299783d46 100644 Binary files a/docs/Presentations/ACT_ACCOUNT/Dia1.JPG and b/docs/Presentations/ACT_ACCOUNT/Dia1.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_1.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_1.JPG new file mode 100644 index 000000000..bb638ea9b Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_1.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_10.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_10.JPG new file mode 100644 index 000000000..31aaf520d Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_10.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_11.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_11.JPG new file mode 100644 index 000000000..b27640e76 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_11.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_2.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_2.JPG new file mode 100644 index 000000000..db129826b Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_2.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_3.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_3.JPG new file mode 100644 index 000000000..ea4f6beaa Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_3.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_4.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_4.JPG new file mode 100644 index 000000000..e0b1db230 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_4.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_5.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_5.JPG new file mode 100644 index 000000000..749b96e85 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_5.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_6.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_6.JPG new file mode 100644 index 000000000..df6b1f564 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_6.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_7.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_7.JPG new file mode 100644 index 000000000..b6765497e Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_7.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_8.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_8.JPG new file mode 100644 index 000000000..513dbc503 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_8.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_9.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_9.JPG new file mode 100644 index 000000000..f012cb431 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_DISPATCHER-ME_9.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_1.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_1.JPG new file mode 100644 index 000000000..cb043205f Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_1.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_2.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_2.JPG new file mode 100644 index 000000000..dea32f3dd Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_2.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_3.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_3.JPG new file mode 100644 index 000000000..4be3b4dbc Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_3.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_4.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_4.JPG new file mode 100644 index 000000000..cb043205f Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_4.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_5.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_5.JPG new file mode 100644 index 000000000..12dcb59da Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_5.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_6.JPG b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_6.JPG new file mode 100644 index 000000000..87d52dbce Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/AI_A2A_GCICAP-ME_6.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia1.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia1.JPG new file mode 100644 index 000000000..e14f55463 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia1.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia10.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia10.JPG new file mode 100644 index 000000000..d5e37c339 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia10.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia11.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia11.JPG new file mode 100644 index 000000000..c5ded7dc8 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia11.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia12.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia12.JPG new file mode 100644 index 000000000..ae1f6aece Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia12.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia13.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia13.JPG new file mode 100644 index 000000000..0a8a2d548 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia13.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia14.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia14.JPG new file mode 100644 index 000000000..ec30b1767 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia14.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia15.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia15.JPG new file mode 100644 index 000000000..9b2a3736f Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia15.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia16.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia16.JPG new file mode 100644 index 000000000..ff20d7b7e Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia16.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia17.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia17.JPG new file mode 100644 index 000000000..39eaa493c Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia17.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia18.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia18.JPG new file mode 100644 index 000000000..e04dd5ddc Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia18.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia19.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia19.JPG new file mode 100644 index 000000000..64ec68d98 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia19.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia2.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia2.JPG new file mode 100644 index 000000000..422201ac6 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia2.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia20.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia20.JPG new file mode 100644 index 000000000..1c03869cc Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia20.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia21.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia21.JPG new file mode 100644 index 000000000..86b50ace8 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia21.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia22.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia22.JPG new file mode 100644 index 000000000..4480e7644 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia22.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia23.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia23.JPG new file mode 100644 index 000000000..58b60d2fd Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia23.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia24.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia24.JPG new file mode 100644 index 000000000..2c505d5a6 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia24.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia25.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia25.JPG new file mode 100644 index 000000000..d23d0e432 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia25.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia26.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia26.JPG new file mode 100644 index 000000000..8a70a531b Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia26.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia27.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia27.JPG new file mode 100644 index 000000000..f6b9bb461 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia27.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia28.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia28.JPG new file mode 100644 index 000000000..59a4bd0a9 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia28.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia29.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia29.JPG new file mode 100644 index 000000000..59569bcf1 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia29.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia3.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia3.JPG new file mode 100644 index 000000000..34be6072e Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia3.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia4.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia4.JPG new file mode 100644 index 000000000..f50f62c6d Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia4.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia5.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia5.JPG new file mode 100644 index 000000000..766fb4a7b Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia5.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia6.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia6.JPG new file mode 100644 index 000000000..ffe9728d6 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia6.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia7.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia7.JPG new file mode 100644 index 000000000..4f916d8dc Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia7.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia8.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia8.JPG new file mode 100644 index 000000000..eb7f43662 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia8.JPG differ diff --git a/docs/Presentations/AI_A2A_DISPATCHER/Dia9.JPG b/docs/Presentations/AI_A2A_DISPATCHER/Dia9.JPG new file mode 100644 index 000000000..8fcef4200 Binary files /dev/null and b/docs/Presentations/AI_A2A_DISPATCHER/Dia9.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia1.JPG b/docs/Presentations/AI_BAI/Dia1.JPG new file mode 100644 index 000000000..ea2e8afbd Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia1.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia10.JPG b/docs/Presentations/AI_BAI/Dia10.JPG new file mode 100644 index 000000000..2d5fb6055 Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia10.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia11.JPG b/docs/Presentations/AI_BAI/Dia11.JPG new file mode 100644 index 000000000..e1509d8f4 Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia11.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia12.JPG b/docs/Presentations/AI_BAI/Dia12.JPG new file mode 100644 index 000000000..d5f054d2d Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia12.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia13.JPG b/docs/Presentations/AI_BAI/Dia13.JPG new file mode 100644 index 000000000..f37811369 Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia13.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia2.JPG b/docs/Presentations/AI_BAI/Dia2.JPG new file mode 100644 index 000000000..ba382b9b7 Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia2.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia3.JPG b/docs/Presentations/AI_BAI/Dia3.JPG new file mode 100644 index 000000000..dbb63cee9 Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia3.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia4.JPG b/docs/Presentations/AI_BAI/Dia4.JPG new file mode 100644 index 000000000..1952fa188 Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia4.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia5.JPG b/docs/Presentations/AI_BAI/Dia5.JPG new file mode 100644 index 000000000..e9cd79126 Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia5.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia6.JPG b/docs/Presentations/AI_BAI/Dia6.JPG new file mode 100644 index 000000000..75ca905ad Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia6.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia7.JPG b/docs/Presentations/AI_BAI/Dia7.JPG new file mode 100644 index 000000000..9d5ac3a58 Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia7.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia8.JPG b/docs/Presentations/AI_BAI/Dia8.JPG new file mode 100644 index 000000000..daa96a088 Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia8.JPG differ diff --git a/docs/Presentations/AI_BAI/Dia9.JPG b/docs/Presentations/AI_BAI/Dia9.JPG new file mode 100644 index 000000000..be4379581 Binary files /dev/null and b/docs/Presentations/AI_BAI/Dia9.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia10.JPG b/docs/Presentations/AI_BALANCER/Dia10.JPG index e713661c4..f962157e0 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia10.JPG and b/docs/Presentations/AI_BALANCER/Dia10.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia11.JPG b/docs/Presentations/AI_BALANCER/Dia11.JPG index 1167fb811..af20ec0db 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia11.JPG and b/docs/Presentations/AI_BALANCER/Dia11.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia12.JPG b/docs/Presentations/AI_BALANCER/Dia12.JPG index f91a2fcbb..5e3b78d09 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia12.JPG and b/docs/Presentations/AI_BALANCER/Dia12.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia13.JPG b/docs/Presentations/AI_BALANCER/Dia13.JPG index 6b9350c1e..4e8e09743 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia13.JPG and b/docs/Presentations/AI_BALANCER/Dia13.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia14.JPG b/docs/Presentations/AI_BALANCER/Dia14.JPG index e90ad1b0e..3da8b07e4 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia14.JPG and b/docs/Presentations/AI_BALANCER/Dia14.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia15.JPG b/docs/Presentations/AI_BALANCER/Dia15.JPG index fa5e59f92..0c292625a 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia15.JPG and b/docs/Presentations/AI_BALANCER/Dia15.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia16.JPG b/docs/Presentations/AI_BALANCER/Dia16.JPG index ed220b049..889ea2e57 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia16.JPG and b/docs/Presentations/AI_BALANCER/Dia16.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia17.JPG b/docs/Presentations/AI_BALANCER/Dia17.JPG index 13a4b8d0c..9631a9b13 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia17.JPG and b/docs/Presentations/AI_BALANCER/Dia17.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia18.JPG b/docs/Presentations/AI_BALANCER/Dia18.JPG index e3db039e2..6c0cbd795 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia18.JPG and b/docs/Presentations/AI_BALANCER/Dia18.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia3.JPG b/docs/Presentations/AI_BALANCER/Dia3.JPG index f17322dd9..3d5d7ba51 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia3.JPG and b/docs/Presentations/AI_BALANCER/Dia3.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia4.JPG b/docs/Presentations/AI_BALANCER/Dia4.JPG index fe3378f0f..67f77f56d 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia4.JPG and b/docs/Presentations/AI_BALANCER/Dia4.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia5.JPG b/docs/Presentations/AI_BALANCER/Dia5.JPG index cf520aead..d79c546ec 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia5.JPG and b/docs/Presentations/AI_BALANCER/Dia5.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia6.JPG b/docs/Presentations/AI_BALANCER/Dia6.JPG index 768fe1f2b..d80d336ad 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia6.JPG and b/docs/Presentations/AI_BALANCER/Dia6.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia7.JPG b/docs/Presentations/AI_BALANCER/Dia7.JPG index 513786720..e5ed0ec89 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia7.JPG and b/docs/Presentations/AI_BALANCER/Dia7.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia8.JPG b/docs/Presentations/AI_BALANCER/Dia8.JPG index c49de1bf7..1f56ed6d9 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia8.JPG and b/docs/Presentations/AI_BALANCER/Dia8.JPG differ diff --git a/docs/Presentations/AI_BALANCER/Dia9.JPG b/docs/Presentations/AI_BALANCER/Dia9.JPG index 6eaaeb98b..e390c69a2 100644 Binary files a/docs/Presentations/AI_BALANCER/Dia9.JPG and b/docs/Presentations/AI_BALANCER/Dia9.JPG differ diff --git a/docs/Presentations/AI_Balancer/Dia1.JPG b/docs/Presentations/AI_Balancer/Dia1.JPG index eca62afff..81228b230 100644 Binary files a/docs/Presentations/AI_Balancer/Dia1.JPG and b/docs/Presentations/AI_Balancer/Dia1.JPG differ diff --git a/docs/Presentations/AI_Balancer/Dia2.JPG b/docs/Presentations/AI_Balancer/Dia2.JPG index 5f439e6b4..9a3a2bfa6 100644 Binary files a/docs/Presentations/AI_Balancer/Dia2.JPG and b/docs/Presentations/AI_Balancer/Dia2.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia1.JPG b/docs/Presentations/AI_CAP/Dia1.JPG index 7b1c0d8dc..f9250debb 100644 Binary files a/docs/Presentations/AI_CAP/Dia1.JPG and b/docs/Presentations/AI_CAP/Dia1.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia10.JPG b/docs/Presentations/AI_CAP/Dia10.JPG index c49f26545..978279355 100644 Binary files a/docs/Presentations/AI_CAP/Dia10.JPG and b/docs/Presentations/AI_CAP/Dia10.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia11.JPG b/docs/Presentations/AI_CAP/Dia11.JPG index 0e7e53142..4f3f5a120 100644 Binary files a/docs/Presentations/AI_CAP/Dia11.JPG and b/docs/Presentations/AI_CAP/Dia11.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia12.JPG b/docs/Presentations/AI_CAP/Dia12.JPG index b9c42741b..f150e8d91 100644 Binary files a/docs/Presentations/AI_CAP/Dia12.JPG and b/docs/Presentations/AI_CAP/Dia12.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia13.JPG b/docs/Presentations/AI_CAP/Dia13.JPG index efedca7b0..bea6c1b8d 100644 Binary files a/docs/Presentations/AI_CAP/Dia13.JPG and b/docs/Presentations/AI_CAP/Dia13.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia14.JPG b/docs/Presentations/AI_CAP/Dia14.JPG index 2913091b7..afcb5afda 100644 Binary files a/docs/Presentations/AI_CAP/Dia14.JPG and b/docs/Presentations/AI_CAP/Dia14.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia2.JPG b/docs/Presentations/AI_CAP/Dia2.JPG index 1b1492d6c..5da4e0286 100644 Binary files a/docs/Presentations/AI_CAP/Dia2.JPG and b/docs/Presentations/AI_CAP/Dia2.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia3.JPG b/docs/Presentations/AI_CAP/Dia3.JPG index f32e036c0..a0ad8c606 100644 Binary files a/docs/Presentations/AI_CAP/Dia3.JPG and b/docs/Presentations/AI_CAP/Dia3.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia4.JPG b/docs/Presentations/AI_CAP/Dia4.JPG index f3015fbeb..7cfc20fc0 100644 Binary files a/docs/Presentations/AI_CAP/Dia4.JPG and b/docs/Presentations/AI_CAP/Dia4.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia5.JPG b/docs/Presentations/AI_CAP/Dia5.JPG index 1fd33de78..5690a60fc 100644 Binary files a/docs/Presentations/AI_CAP/Dia5.JPG and b/docs/Presentations/AI_CAP/Dia5.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia6.JPG b/docs/Presentations/AI_CAP/Dia6.JPG index c5024ddbf..08969ac4c 100644 Binary files a/docs/Presentations/AI_CAP/Dia6.JPG and b/docs/Presentations/AI_CAP/Dia6.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia7.JPG b/docs/Presentations/AI_CAP/Dia7.JPG index fbf9987ad..503c9bde7 100644 Binary files a/docs/Presentations/AI_CAP/Dia7.JPG and b/docs/Presentations/AI_CAP/Dia7.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia8.JPG b/docs/Presentations/AI_CAP/Dia8.JPG index ee0b36aba..2c9fda84f 100644 Binary files a/docs/Presentations/AI_CAP/Dia8.JPG and b/docs/Presentations/AI_CAP/Dia8.JPG differ diff --git a/docs/Presentations/AI_CAP/Dia9.JPG b/docs/Presentations/AI_CAP/Dia9.JPG index 0c4760538..510ce2231 100644 Binary files a/docs/Presentations/AI_CAP/Dia9.JPG and b/docs/Presentations/AI_CAP/Dia9.JPG differ diff --git a/docs/Presentations/AI_CARGO/CARGO.JPG b/docs/Presentations/AI_CARGO/CARGO.JPG index 3096fad05..8f80a25cc 100644 Binary files a/docs/Presentations/AI_CARGO/CARGO.JPG and b/docs/Presentations/AI_CARGO/CARGO.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia1.JPG b/docs/Presentations/AI_CAS/Dia1.JPG index 0bf4c1e92..7f223938d 100644 Binary files a/docs/Presentations/AI_CAS/Dia1.JPG and b/docs/Presentations/AI_CAS/Dia1.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia10.JPG b/docs/Presentations/AI_CAS/Dia10.JPG index 6e5096863..e732e9c6d 100644 Binary files a/docs/Presentations/AI_CAS/Dia10.JPG and b/docs/Presentations/AI_CAS/Dia10.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia11.JPG b/docs/Presentations/AI_CAS/Dia11.JPG index a236d65e2..1968a6cef 100644 Binary files a/docs/Presentations/AI_CAS/Dia11.JPG and b/docs/Presentations/AI_CAS/Dia11.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia12.JPG b/docs/Presentations/AI_CAS/Dia12.JPG index bcf6d85ff..41d01e84c 100644 Binary files a/docs/Presentations/AI_CAS/Dia12.JPG and b/docs/Presentations/AI_CAS/Dia12.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia13.JPG b/docs/Presentations/AI_CAS/Dia13.JPG index 797dcbaf8..956051e91 100644 Binary files a/docs/Presentations/AI_CAS/Dia13.JPG and b/docs/Presentations/AI_CAS/Dia13.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia2.JPG b/docs/Presentations/AI_CAS/Dia2.JPG index 574ed5b9b..135c3822b 100644 Binary files a/docs/Presentations/AI_CAS/Dia2.JPG and b/docs/Presentations/AI_CAS/Dia2.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia3.JPG b/docs/Presentations/AI_CAS/Dia3.JPG index 9bcc78a57..d713f4c70 100644 Binary files a/docs/Presentations/AI_CAS/Dia3.JPG and b/docs/Presentations/AI_CAS/Dia3.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia4.JPG b/docs/Presentations/AI_CAS/Dia4.JPG index 2cd22e14a..cee857405 100644 Binary files a/docs/Presentations/AI_CAS/Dia4.JPG and b/docs/Presentations/AI_CAS/Dia4.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia5.JPG b/docs/Presentations/AI_CAS/Dia5.JPG index 22f923f5c..e9cd79126 100644 Binary files a/docs/Presentations/AI_CAS/Dia5.JPG and b/docs/Presentations/AI_CAS/Dia5.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia6.JPG b/docs/Presentations/AI_CAS/Dia6.JPG index 668413e95..75ca905ad 100644 Binary files a/docs/Presentations/AI_CAS/Dia6.JPG and b/docs/Presentations/AI_CAS/Dia6.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia7.JPG b/docs/Presentations/AI_CAS/Dia7.JPG index 04d4abd9a..ebdabf602 100644 Binary files a/docs/Presentations/AI_CAS/Dia7.JPG and b/docs/Presentations/AI_CAS/Dia7.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia8.JPG b/docs/Presentations/AI_CAS/Dia8.JPG index c1aebf592..cce26918b 100644 Binary files a/docs/Presentations/AI_CAS/Dia8.JPG and b/docs/Presentations/AI_CAS/Dia8.JPG differ diff --git a/docs/Presentations/AI_CAS/Dia9.JPG b/docs/Presentations/AI_CAS/Dia9.JPG index 47845f382..d91460dda 100644 Binary files a/docs/Presentations/AI_CAS/Dia9.JPG and b/docs/Presentations/AI_CAS/Dia9.JPG differ diff --git a/docs/Presentations/AI_FORMATION/Dia1.JPG b/docs/Presentations/AI_FORMATION/Dia1.JPG new file mode 100644 index 000000000..101d201e6 Binary files /dev/null and b/docs/Presentations/AI_FORMATION/Dia1.JPG differ diff --git a/docs/Presentations/AI_PATROL/Dia1.JPG b/docs/Presentations/AI_PATROL/Dia1.JPG index 26bc14fe0..cc03be842 100644 Binary files a/docs/Presentations/AI_PATROL/Dia1.JPG and b/docs/Presentations/AI_PATROL/Dia1.JPG differ diff --git a/docs/Presentations/AI_PATROL/Dia10.JPG b/docs/Presentations/AI_PATROL/Dia10.JPG index 63cee3a04..be365448c 100644 Binary files a/docs/Presentations/AI_PATROL/Dia10.JPG and b/docs/Presentations/AI_PATROL/Dia10.JPG differ diff --git a/docs/Presentations/AI_PATROL/Dia11.JPG b/docs/Presentations/AI_PATROL/Dia11.JPG index cae7f2722..32e956447 100644 Binary files a/docs/Presentations/AI_PATROL/Dia11.JPG and b/docs/Presentations/AI_PATROL/Dia11.JPG differ diff --git a/docs/Presentations/AI_PATROL/Dia12.JPG b/docs/Presentations/AI_PATROL/Dia12.JPG index 851e5f161..ac6219c85 100644 Binary files a/docs/Presentations/AI_PATROL/Dia12.JPG and b/docs/Presentations/AI_PATROL/Dia12.JPG differ diff --git a/docs/Presentations/AI_PATROL/Dia2.JPG b/docs/Presentations/AI_PATROL/Dia2.JPG index 0227de5ed..ea1ed8cc9 100644 Binary files a/docs/Presentations/AI_PATROL/Dia2.JPG and b/docs/Presentations/AI_PATROL/Dia2.JPG differ diff --git a/docs/Presentations/AI_PATROL/Dia3.JPG b/docs/Presentations/AI_PATROL/Dia3.JPG index f32e036c0..a0ad8c606 100644 Binary files a/docs/Presentations/AI_PATROL/Dia3.JPG and b/docs/Presentations/AI_PATROL/Dia3.JPG differ diff --git a/docs/Presentations/AI_PATROL/Dia4.JPG b/docs/Presentations/AI_PATROL/Dia4.JPG index f3015fbeb..7cfc20fc0 100644 Binary files a/docs/Presentations/AI_PATROL/Dia4.JPG and b/docs/Presentations/AI_PATROL/Dia4.JPG differ diff --git a/docs/Presentations/AI_PATROL/Dia5.JPG b/docs/Presentations/AI_PATROL/Dia5.JPG index 1fd33de78..5690a60fc 100644 Binary files a/docs/Presentations/AI_PATROL/Dia5.JPG and b/docs/Presentations/AI_PATROL/Dia5.JPG differ diff --git a/docs/Presentations/AI_PATROL/Dia6.JPG b/docs/Presentations/AI_PATROL/Dia6.JPG index a41920d1c..bb3a03a4a 100644 Binary files a/docs/Presentations/AI_PATROL/Dia6.JPG and b/docs/Presentations/AI_PATROL/Dia6.JPG differ diff --git a/docs/Presentations/AI_PATROL/Dia7.JPG b/docs/Presentations/AI_PATROL/Dia7.JPG index 55ee1a64b..d12628c2f 100644 Binary files a/docs/Presentations/AI_PATROL/Dia7.JPG and b/docs/Presentations/AI_PATROL/Dia7.JPG differ diff --git a/docs/Presentations/AI_PATROL/Dia8.JPG b/docs/Presentations/AI_PATROL/Dia8.JPG index 8c9155db4..cfcff5755 100644 Binary files a/docs/Presentations/AI_PATROL/Dia8.JPG and b/docs/Presentations/AI_PATROL/Dia8.JPG differ diff --git a/docs/Presentations/AI_PATROL/Dia9.JPG b/docs/Presentations/AI_PATROL/Dia9.JPG index 0c4760538..510ce2231 100644 Binary files a/docs/Presentations/AI_PATROL/Dia9.JPG and b/docs/Presentations/AI_PATROL/Dia9.JPG differ diff --git a/docs/Presentations/BASE/Dia1.JPG b/docs/Presentations/BASE/Dia1.JPG index a0fb1fb6b..683a74371 100644 Binary files a/docs/Presentations/BASE/Dia1.JPG and b/docs/Presentations/BASE/Dia1.JPG differ diff --git a/docs/Presentations/CARGO/Dia1.JPG b/docs/Presentations/CARGO/Dia1.JPG index c38ff1e02..8f80a25cc 100644 Binary files a/docs/Presentations/CARGO/Dia1.JPG and b/docs/Presentations/CARGO/Dia1.JPG differ diff --git a/docs/Presentations/CARGO/Dia2.JPG b/docs/Presentations/CARGO/Dia2.JPG index 626f6a55b..8c03cbad5 100644 Binary files a/docs/Presentations/CARGO/Dia2.JPG and b/docs/Presentations/CARGO/Dia2.JPG differ diff --git a/docs/Presentations/CARGO/Dia3.JPG b/docs/Presentations/CARGO/Dia3.JPG index 75e5d78b3..4a5ce49ab 100644 Binary files a/docs/Presentations/CARGO/Dia3.JPG and b/docs/Presentations/CARGO/Dia3.JPG differ diff --git a/docs/Presentations/CARGO/Dia4.JPG b/docs/Presentations/CARGO/Dia4.JPG index 93dad1a32..9e786e61a 100644 Binary files a/docs/Presentations/CARGO/Dia4.JPG and b/docs/Presentations/CARGO/Dia4.JPG differ diff --git a/docs/Presentations/CARGO/Dia5.JPG b/docs/Presentations/CARGO/Dia5.JPG index dfd0a3877..b33571559 100644 Binary files a/docs/Presentations/CARGO/Dia5.JPG and b/docs/Presentations/CARGO/Dia5.JPG differ diff --git a/docs/Presentations/CARGO/Dia6.JPG b/docs/Presentations/CARGO/Dia6.JPG index a7e01cdce..a52bdcb49 100644 Binary files a/docs/Presentations/CARGO/Dia6.JPG and b/docs/Presentations/CARGO/Dia6.JPG differ diff --git a/docs/Presentations/CARGO/Dia7.JPG b/docs/Presentations/CARGO/Dia7.JPG index 4ed46bfd8..2d103b1e5 100644 Binary files a/docs/Presentations/CARGO/Dia7.JPG and b/docs/Presentations/CARGO/Dia7.JPG differ diff --git a/docs/Presentations/CARGO/Dia8.JPG b/docs/Presentations/CARGO/Dia8.JPG index 87b45f85c..0bb6fed51 100644 Binary files a/docs/Presentations/CARGO/Dia8.JPG and b/docs/Presentations/CARGO/Dia8.JPG differ diff --git a/docs/Presentations/CARGO/Dia9.JPG b/docs/Presentations/CARGO/Dia9.JPG index 0f2a2e707..5009b2063 100644 Binary files a/docs/Presentations/CARGO/Dia9.JPG and b/docs/Presentations/CARGO/Dia9.JPG differ diff --git a/docs/Presentations/CLEANUP_AIRBASE/Dia1.JPG b/docs/Presentations/CLEANUP_AIRBASE/Dia1.JPG new file mode 100644 index 000000000..b5d0e85b8 Binary files /dev/null and b/docs/Presentations/CLEANUP_AIRBASE/Dia1.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia1.JPG b/docs/Presentations/DESIGNATE/Dia1.JPG new file mode 100644 index 000000000..4ea3fde1b Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia1.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia10.JPG b/docs/Presentations/DESIGNATE/Dia10.JPG new file mode 100644 index 000000000..cc950be4e Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia10.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia11.JPG b/docs/Presentations/DESIGNATE/Dia11.JPG new file mode 100644 index 000000000..bd2b4e0ee Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia11.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia12.JPG b/docs/Presentations/DESIGNATE/Dia12.JPG new file mode 100644 index 000000000..16a566a77 Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia12.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia13.JPG b/docs/Presentations/DESIGNATE/Dia13.JPG new file mode 100644 index 000000000..2fb888778 Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia13.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia14.JPG b/docs/Presentations/DESIGNATE/Dia14.JPG new file mode 100644 index 000000000..72a123624 Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia14.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia2.JPG b/docs/Presentations/DESIGNATE/Dia2.JPG new file mode 100644 index 000000000..35c65e42a Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia2.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia3.JPG b/docs/Presentations/DESIGNATE/Dia3.JPG new file mode 100644 index 000000000..e96e565af Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia3.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia4.JPG b/docs/Presentations/DESIGNATE/Dia4.JPG new file mode 100644 index 000000000..382e65e62 Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia4.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia5.JPG b/docs/Presentations/DESIGNATE/Dia5.JPG new file mode 100644 index 000000000..67eec2ee2 Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia5.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia6.JPG b/docs/Presentations/DESIGNATE/Dia6.JPG new file mode 100644 index 000000000..48b0eaa87 Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia6.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia7.JPG b/docs/Presentations/DESIGNATE/Dia7.JPG new file mode 100644 index 000000000..3a730dc21 Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia7.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia8.JPG b/docs/Presentations/DESIGNATE/Dia8.JPG new file mode 100644 index 000000000..7807e604d Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia8.JPG differ diff --git a/docs/Presentations/DESIGNATE/Dia9.JPG b/docs/Presentations/DESIGNATE/Dia9.JPG new file mode 100644 index 000000000..0e24ab9aa Binary files /dev/null and b/docs/Presentations/DESIGNATE/Dia9.JPG differ diff --git a/docs/Presentations/DETECTION/Dia1.JPG b/docs/Presentations/DETECTION/Dia1.JPG index 568646fa4..c086b1079 100644 Binary files a/docs/Presentations/DETECTION/Dia1.JPG and b/docs/Presentations/DETECTION/Dia1.JPG differ diff --git a/docs/Presentations/DETECTION/Dia10.JPG b/docs/Presentations/DETECTION/Dia10.JPG index 1d6cab399..954a0d3d0 100644 Binary files a/docs/Presentations/DETECTION/Dia10.JPG and b/docs/Presentations/DETECTION/Dia10.JPG differ diff --git a/docs/Presentations/DETECTION/Dia11.JPG b/docs/Presentations/DETECTION/Dia11.JPG index 0ce4a6d8a..810f16483 100644 Binary files a/docs/Presentations/DETECTION/Dia11.JPG and b/docs/Presentations/DETECTION/Dia11.JPG differ diff --git a/docs/Presentations/DETECTION/Dia12.JPG b/docs/Presentations/DETECTION/Dia12.JPG index befe2f3a3..a0ca1b061 100644 Binary files a/docs/Presentations/DETECTION/Dia12.JPG and b/docs/Presentations/DETECTION/Dia12.JPG differ diff --git a/docs/Presentations/DETECTION/Dia13.JPG b/docs/Presentations/DETECTION/Dia13.JPG index 2b2e3182c..497c005dd 100644 Binary files a/docs/Presentations/DETECTION/Dia13.JPG and b/docs/Presentations/DETECTION/Dia13.JPG differ diff --git a/docs/Presentations/DETECTION/Dia14.JPG b/docs/Presentations/DETECTION/Dia14.JPG index ca35da8f9..ef976534d 100644 Binary files a/docs/Presentations/DETECTION/Dia14.JPG and b/docs/Presentations/DETECTION/Dia14.JPG differ diff --git a/docs/Presentations/DETECTION/Dia15.JPG b/docs/Presentations/DETECTION/Dia15.JPG index 746860f9d..58981f28c 100644 Binary files a/docs/Presentations/DETECTION/Dia15.JPG and b/docs/Presentations/DETECTION/Dia15.JPG differ diff --git a/docs/Presentations/DETECTION/Dia16.JPG b/docs/Presentations/DETECTION/Dia16.JPG index 957c00307..4a65ce240 100644 Binary files a/docs/Presentations/DETECTION/Dia16.JPG and b/docs/Presentations/DETECTION/Dia16.JPG differ diff --git a/docs/Presentations/DETECTION/Dia17.JPG b/docs/Presentations/DETECTION/Dia17.JPG index a95e866c2..7409d05e5 100644 Binary files a/docs/Presentations/DETECTION/Dia17.JPG and b/docs/Presentations/DETECTION/Dia17.JPG differ diff --git a/docs/Presentations/DETECTION/Dia18.JPG b/docs/Presentations/DETECTION/Dia18.JPG index 98b861148..44c74ef17 100644 Binary files a/docs/Presentations/DETECTION/Dia18.JPG and b/docs/Presentations/DETECTION/Dia18.JPG differ diff --git a/docs/Presentations/DETECTION/Dia19.JPG b/docs/Presentations/DETECTION/Dia19.JPG index 02a90e680..c758c27cb 100644 Binary files a/docs/Presentations/DETECTION/Dia19.JPG and b/docs/Presentations/DETECTION/Dia19.JPG differ diff --git a/docs/Presentations/DETECTION/Dia2.JPG b/docs/Presentations/DETECTION/Dia2.JPG index a3dcfd13b..aee215bf0 100644 Binary files a/docs/Presentations/DETECTION/Dia2.JPG and b/docs/Presentations/DETECTION/Dia2.JPG differ diff --git a/docs/Presentations/DETECTION/Dia20.JPG b/docs/Presentations/DETECTION/Dia20.JPG index 8268dd239..c43490687 100644 Binary files a/docs/Presentations/DETECTION/Dia20.JPG and b/docs/Presentations/DETECTION/Dia20.JPG differ diff --git a/docs/Presentations/DETECTION/Dia21.JPG b/docs/Presentations/DETECTION/Dia21.JPG index 0b299e044..db6c1bbc2 100644 Binary files a/docs/Presentations/DETECTION/Dia21.JPG and b/docs/Presentations/DETECTION/Dia21.JPG differ diff --git a/docs/Presentations/DETECTION/Dia22.JPG b/docs/Presentations/DETECTION/Dia22.JPG index 460fd1a19..e51732a44 100644 Binary files a/docs/Presentations/DETECTION/Dia22.JPG and b/docs/Presentations/DETECTION/Dia22.JPG differ diff --git a/docs/Presentations/DETECTION/Dia23.JPG b/docs/Presentations/DETECTION/Dia23.JPG index da9dc5a0d..0bd7c6c48 100644 Binary files a/docs/Presentations/DETECTION/Dia23.JPG and b/docs/Presentations/DETECTION/Dia23.JPG differ diff --git a/docs/Presentations/DETECTION/Dia24.JPG b/docs/Presentations/DETECTION/Dia24.JPG index a521d0c09..4b2f126c9 100644 Binary files a/docs/Presentations/DETECTION/Dia24.JPG and b/docs/Presentations/DETECTION/Dia24.JPG differ diff --git a/docs/Presentations/DETECTION/Dia25.JPG b/docs/Presentations/DETECTION/Dia25.JPG index 0206d8f96..e1faa4db4 100644 Binary files a/docs/Presentations/DETECTION/Dia25.JPG and b/docs/Presentations/DETECTION/Dia25.JPG differ diff --git a/docs/Presentations/DETECTION/Dia26.JPG b/docs/Presentations/DETECTION/Dia26.JPG index 2d7109c90..90a86e1ae 100644 Binary files a/docs/Presentations/DETECTION/Dia26.JPG and b/docs/Presentations/DETECTION/Dia26.JPG differ diff --git a/docs/Presentations/DETECTION/Dia3.JPG b/docs/Presentations/DETECTION/Dia3.JPG index 1f2c58924..785e31624 100644 Binary files a/docs/Presentations/DETECTION/Dia3.JPG and b/docs/Presentations/DETECTION/Dia3.JPG differ diff --git a/docs/Presentations/DETECTION/Dia4.JPG b/docs/Presentations/DETECTION/Dia4.JPG index b9450da5c..07dac89f6 100644 Binary files a/docs/Presentations/DETECTION/Dia4.JPG and b/docs/Presentations/DETECTION/Dia4.JPG differ diff --git a/docs/Presentations/DETECTION/Dia5.JPG b/docs/Presentations/DETECTION/Dia5.JPG index 0df32d026..a21624e45 100644 Binary files a/docs/Presentations/DETECTION/Dia5.JPG and b/docs/Presentations/DETECTION/Dia5.JPG differ diff --git a/docs/Presentations/DETECTION/Dia6.JPG b/docs/Presentations/DETECTION/Dia6.JPG index 6707c5928..29cbff2b7 100644 Binary files a/docs/Presentations/DETECTION/Dia6.JPG and b/docs/Presentations/DETECTION/Dia6.JPG differ diff --git a/docs/Presentations/DETECTION/Dia7.JPG b/docs/Presentations/DETECTION/Dia7.JPG index f3e509fe3..effd511aa 100644 Binary files a/docs/Presentations/DETECTION/Dia7.JPG and b/docs/Presentations/DETECTION/Dia7.JPG differ diff --git a/docs/Presentations/DETECTION/Dia8.JPG b/docs/Presentations/DETECTION/Dia8.JPG index 388e6419d..b409d8b08 100644 Binary files a/docs/Presentations/DETECTION/Dia8.JPG and b/docs/Presentations/DETECTION/Dia8.JPG differ diff --git a/docs/Presentations/DETECTION/Dia9.JPG b/docs/Presentations/DETECTION/Dia9.JPG index 9b65f8733..5e2211712 100644 Binary files a/docs/Presentations/DETECTION/Dia9.JPG and b/docs/Presentations/DETECTION/Dia9.JPG differ diff --git a/docs/Presentations/EVENT.pptx b/docs/Presentations/EVENT.pptx deleted file mode 100644 index 5ae8628cb..000000000 Binary files a/docs/Presentations/EVENT.pptx and /dev/null differ diff --git a/docs/Presentations/EVENT/Dia1.JPG b/docs/Presentations/EVENT/Dia1.JPG index 2621f5880..921c06a70 100644 Binary files a/docs/Presentations/EVENT/Dia1.JPG and b/docs/Presentations/EVENT/Dia1.JPG differ diff --git a/docs/Presentations/EVENT/Dia10.JPG b/docs/Presentations/EVENT/Dia10.JPG index 1bba3324c..b7a47e267 100644 Binary files a/docs/Presentations/EVENT/Dia10.JPG and b/docs/Presentations/EVENT/Dia10.JPG differ diff --git a/docs/Presentations/EVENT/Dia11.JPG b/docs/Presentations/EVENT/Dia11.JPG index 9a54dbe58..381c4d94e 100644 Binary files a/docs/Presentations/EVENT/Dia11.JPG and b/docs/Presentations/EVENT/Dia11.JPG differ diff --git a/docs/Presentations/EVENT/Dia12.JPG b/docs/Presentations/EVENT/Dia12.JPG index c5d44cb5a..5bada0bee 100644 Binary files a/docs/Presentations/EVENT/Dia12.JPG and b/docs/Presentations/EVENT/Dia12.JPG differ diff --git a/docs/Presentations/EVENT/Dia13.JPG b/docs/Presentations/EVENT/Dia13.JPG index a9527b996..690c685ff 100644 Binary files a/docs/Presentations/EVENT/Dia13.JPG and b/docs/Presentations/EVENT/Dia13.JPG differ diff --git a/docs/Presentations/EVENT/Dia14.JPG b/docs/Presentations/EVENT/Dia14.JPG index 46773eef0..4be5e8994 100644 Binary files a/docs/Presentations/EVENT/Dia14.JPG and b/docs/Presentations/EVENT/Dia14.JPG differ diff --git a/docs/Presentations/EVENT/Dia15.JPG b/docs/Presentations/EVENT/Dia15.JPG index 430bf4cd8..6762bc5f5 100644 Binary files a/docs/Presentations/EVENT/Dia15.JPG and b/docs/Presentations/EVENT/Dia15.JPG differ diff --git a/docs/Presentations/EVENT/Dia16.JPG b/docs/Presentations/EVENT/Dia16.JPG index 8290075f6..4624879a2 100644 Binary files a/docs/Presentations/EVENT/Dia16.JPG and b/docs/Presentations/EVENT/Dia16.JPG differ diff --git a/docs/Presentations/EVENT/Dia17.JPG b/docs/Presentations/EVENT/Dia17.JPG index 340e62c88..760004b09 100644 Binary files a/docs/Presentations/EVENT/Dia17.JPG and b/docs/Presentations/EVENT/Dia17.JPG differ diff --git a/docs/Presentations/EVENT/Dia18.JPG b/docs/Presentations/EVENT/Dia18.JPG index 3e2df6692..7743882c2 100644 Binary files a/docs/Presentations/EVENT/Dia18.JPG and b/docs/Presentations/EVENT/Dia18.JPG differ diff --git a/docs/Presentations/EVENT/Dia2.JPG b/docs/Presentations/EVENT/Dia2.JPG index 55116b7a4..c01cf6f48 100644 Binary files a/docs/Presentations/EVENT/Dia2.JPG and b/docs/Presentations/EVENT/Dia2.JPG differ diff --git a/docs/Presentations/EVENT/Dia3.JPG b/docs/Presentations/EVENT/Dia3.JPG index 4cb2ee9a3..f668b6fc6 100644 Binary files a/docs/Presentations/EVENT/Dia3.JPG and b/docs/Presentations/EVENT/Dia3.JPG differ diff --git a/docs/Presentations/EVENT/Dia4.JPG b/docs/Presentations/EVENT/Dia4.JPG index 23ff25bba..cd3a3ae64 100644 Binary files a/docs/Presentations/EVENT/Dia4.JPG and b/docs/Presentations/EVENT/Dia4.JPG differ diff --git a/docs/Presentations/EVENT/Dia5.JPG b/docs/Presentations/EVENT/Dia5.JPG index f81a00aa2..de7ebba61 100644 Binary files a/docs/Presentations/EVENT/Dia5.JPG and b/docs/Presentations/EVENT/Dia5.JPG differ diff --git a/docs/Presentations/EVENT/Dia6.JPG b/docs/Presentations/EVENT/Dia6.JPG index 77cb3930d..39b17bddc 100644 Binary files a/docs/Presentations/EVENT/Dia6.JPG and b/docs/Presentations/EVENT/Dia6.JPG differ diff --git a/docs/Presentations/EVENT/Dia7.JPG b/docs/Presentations/EVENT/Dia7.JPG index d55b937c9..36ec17212 100644 Binary files a/docs/Presentations/EVENT/Dia7.JPG and b/docs/Presentations/EVENT/Dia7.JPG differ diff --git a/docs/Presentations/EVENT/Dia8.JPG b/docs/Presentations/EVENT/Dia8.JPG index 16b16bec1..e83589c00 100644 Binary files a/docs/Presentations/EVENT/Dia8.JPG and b/docs/Presentations/EVENT/Dia8.JPG differ diff --git a/docs/Presentations/EVENT/Dia9.JPG b/docs/Presentations/EVENT/Dia9.JPG index 2ac82a4b6..41d44256b 100644 Binary files a/docs/Presentations/EVENT/Dia9.JPG and b/docs/Presentations/EVENT/Dia9.JPG differ diff --git a/docs/Presentations/FSM/Dia1.JPG b/docs/Presentations/FSM/Dia1.JPG index a2979700e..0bcb86189 100644 Binary files a/docs/Presentations/FSM/Dia1.JPG and b/docs/Presentations/FSM/Dia1.JPG differ diff --git a/docs/Presentations/FSM/Dia2.JPG b/docs/Presentations/FSM/Dia2.JPG index 4ccd1b2c7..6893d1a38 100644 Binary files a/docs/Presentations/FSM/Dia2.JPG and b/docs/Presentations/FSM/Dia2.JPG differ diff --git a/docs/Presentations/FSM/Dia3.JPG b/docs/Presentations/FSM/Dia3.JPG index ef0dbc6af..597fb5911 100644 Binary files a/docs/Presentations/FSM/Dia3.JPG and b/docs/Presentations/FSM/Dia3.JPG differ diff --git a/docs/Presentations/FSM/Dia4.JPG b/docs/Presentations/FSM/Dia4.JPG index 258000d73..dd21b0ccb 100644 Binary files a/docs/Presentations/FSM/Dia4.JPG and b/docs/Presentations/FSM/Dia4.JPG differ diff --git a/docs/Presentations/FSM/Dia5.JPG b/docs/Presentations/FSM/Dia5.JPG index 64345fabc..78270f132 100644 Binary files a/docs/Presentations/FSM/Dia5.JPG and b/docs/Presentations/FSM/Dia5.JPG differ diff --git a/docs/Presentations/FSM/Dia6.JPG b/docs/Presentations/FSM/Dia6.JPG index fd8e91c67..e9b5f804f 100644 Binary files a/docs/Presentations/FSM/Dia6.JPG and b/docs/Presentations/FSM/Dia6.JPG differ diff --git a/docs/Presentations/FSM/Dia7.JPG b/docs/Presentations/FSM/Dia7.JPG index b0297c65c..535109064 100644 Binary files a/docs/Presentations/FSM/Dia7.JPG and b/docs/Presentations/FSM/Dia7.JPG differ diff --git a/docs/Presentations/MESSAGE/Dia1.JPG b/docs/Presentations/MESSAGE/Dia1.JPG index c105fe33d..fa6a8532e 100644 Binary files a/docs/Presentations/MESSAGE/Dia1.JPG and b/docs/Presentations/MESSAGE/Dia1.JPG differ diff --git a/docs/Presentations/MOOSE/Dia1.JPG b/docs/Presentations/MOOSE/Dia1.JPG index e9541c1aa..0543af860 100644 Binary files a/docs/Presentations/MOOSE/Dia1.JPG and b/docs/Presentations/MOOSE/Dia1.JPG differ diff --git a/docs/Presentations/MOOSE/Dia2.JPG b/docs/Presentations/MOOSE/Dia2.JPG index 90f7d21e9..fd56450a7 100644 Binary files a/docs/Presentations/MOOSE/Dia2.JPG and b/docs/Presentations/MOOSE/Dia2.JPG differ diff --git a/docs/Presentations/POINT/Dia1.JPG b/docs/Presentations/POINT/Dia1.JPG new file mode 100644 index 000000000..7470c42f4 Binary files /dev/null and b/docs/Presentations/POINT/Dia1.JPG differ diff --git a/docs/Presentations/RADIO/Dia1.JPG b/docs/Presentations/RADIO/Dia1.JPG new file mode 100644 index 000000000..e37b3b33b Binary files /dev/null and b/docs/Presentations/RADIO/Dia1.JPG differ diff --git a/docs/Presentations/SCHEDULER/Dia1.JPG b/docs/Presentations/SCHEDULER/Dia1.JPG index 81cfd2575..a5a7d750b 100644 Binary files a/docs/Presentations/SCHEDULER/Dia1.JPG and b/docs/Presentations/SCHEDULER/Dia1.JPG differ diff --git a/docs/Presentations/SCORING.pptx b/docs/Presentations/SCORING.pptx deleted file mode 100644 index 511f83f82..000000000 Binary files a/docs/Presentations/SCORING.pptx and /dev/null differ diff --git a/docs/Presentations/SCORING/Dia1.JPG b/docs/Presentations/SCORING/Dia1.JPG index d1b418574..631db9574 100644 Binary files a/docs/Presentations/SCORING/Dia1.JPG and b/docs/Presentations/SCORING/Dia1.JPG differ diff --git a/docs/Presentations/SCORING/Dia10.JPG b/docs/Presentations/SCORING/Dia10.JPG index ff450950b..8635218e8 100644 Binary files a/docs/Presentations/SCORING/Dia10.JPG and b/docs/Presentations/SCORING/Dia10.JPG differ diff --git a/docs/Presentations/SCORING/Dia11.JPG b/docs/Presentations/SCORING/Dia11.JPG index 3814c3d85..e9650a741 100644 Binary files a/docs/Presentations/SCORING/Dia11.JPG and b/docs/Presentations/SCORING/Dia11.JPG differ diff --git a/docs/Presentations/SCORING/Dia12.JPG b/docs/Presentations/SCORING/Dia12.JPG index 9228bac57..d6227e889 100644 Binary files a/docs/Presentations/SCORING/Dia12.JPG and b/docs/Presentations/SCORING/Dia12.JPG differ diff --git a/docs/Presentations/SCORING/Dia13.JPG b/docs/Presentations/SCORING/Dia13.JPG index 2e4a6114b..a376c7b50 100644 Binary files a/docs/Presentations/SCORING/Dia13.JPG and b/docs/Presentations/SCORING/Dia13.JPG differ diff --git a/docs/Presentations/SCORING/Dia14.JPG b/docs/Presentations/SCORING/Dia14.JPG index f98caa5e1..6cf31ebaf 100644 Binary files a/docs/Presentations/SCORING/Dia14.JPG and b/docs/Presentations/SCORING/Dia14.JPG differ diff --git a/docs/Presentations/SCORING/Dia2.JPG b/docs/Presentations/SCORING/Dia2.JPG index 51ad11dd5..0436a79ad 100644 Binary files a/docs/Presentations/SCORING/Dia2.JPG and b/docs/Presentations/SCORING/Dia2.JPG differ diff --git a/docs/Presentations/SCORING/Dia3.JPG b/docs/Presentations/SCORING/Dia3.JPG index 1518e6a24..ce310c789 100644 Binary files a/docs/Presentations/SCORING/Dia3.JPG and b/docs/Presentations/SCORING/Dia3.JPG differ diff --git a/docs/Presentations/SCORING/Dia4.JPG b/docs/Presentations/SCORING/Dia4.JPG index ab7571f24..6326cfa13 100644 Binary files a/docs/Presentations/SCORING/Dia4.JPG and b/docs/Presentations/SCORING/Dia4.JPG differ diff --git a/docs/Presentations/SCORING/Dia5.JPG b/docs/Presentations/SCORING/Dia5.JPG index 44ba79cdd..c4e7c8696 100644 Binary files a/docs/Presentations/SCORING/Dia5.JPG and b/docs/Presentations/SCORING/Dia5.JPG differ diff --git a/docs/Presentations/SCORING/Dia6.JPG b/docs/Presentations/SCORING/Dia6.JPG index 983ebc9e8..644652096 100644 Binary files a/docs/Presentations/SCORING/Dia6.JPG and b/docs/Presentations/SCORING/Dia6.JPG differ diff --git a/docs/Presentations/SCORING/Dia7.JPG b/docs/Presentations/SCORING/Dia7.JPG index c7daf51c2..db73f303c 100644 Binary files a/docs/Presentations/SCORING/Dia7.JPG and b/docs/Presentations/SCORING/Dia7.JPG differ diff --git a/docs/Presentations/SCORING/Dia8.JPG b/docs/Presentations/SCORING/Dia8.JPG index b993827f2..b73aecc0e 100644 Binary files a/docs/Presentations/SCORING/Dia8.JPG and b/docs/Presentations/SCORING/Dia8.JPG differ diff --git a/docs/Presentations/SCORING/Dia9.JPG b/docs/Presentations/SCORING/Dia9.JPG index c1ccceba7..01c9703c6 100644 Binary files a/docs/Presentations/SCORING/Dia9.JPG and b/docs/Presentations/SCORING/Dia9.JPG differ diff --git a/docs/Presentations/SET/Dia1.JPG b/docs/Presentations/SET/Dia1.JPG index dd975a856..a78c95048 100644 Binary files a/docs/Presentations/SET/Dia1.JPG and b/docs/Presentations/SET/Dia1.JPG differ diff --git a/docs/Presentations/SPAWN/Dia1.JPG b/docs/Presentations/SPAWN/Dia1.JPG index c38ff1e02..05ab34ec5 100644 Binary files a/docs/Presentations/SPAWN/Dia1.JPG and b/docs/Presentations/SPAWN/Dia1.JPG differ diff --git a/docs/Presentations/SPAWN/Dia2.JPG b/docs/Presentations/SPAWN/Dia2.JPG index 626f6a55b..aadb98ed2 100644 Binary files a/docs/Presentations/SPAWN/Dia2.JPG and b/docs/Presentations/SPAWN/Dia2.JPG differ diff --git a/docs/Presentations/SPAWN/Dia3.JPG b/docs/Presentations/SPAWN/Dia3.JPG index 75e5d78b3..338700a80 100644 Binary files a/docs/Presentations/SPAWN/Dia3.JPG and b/docs/Presentations/SPAWN/Dia3.JPG differ diff --git a/docs/Presentations/SPAWN/Dia4.JPG b/docs/Presentations/SPAWN/Dia4.JPG index 93dad1a32..a5170e8b5 100644 Binary files a/docs/Presentations/SPAWN/Dia4.JPG and b/docs/Presentations/SPAWN/Dia4.JPG differ diff --git a/docs/Presentations/SPAWN/Dia5.JPG b/docs/Presentations/SPAWN/Dia5.JPG index dfd0a3877..5a37bb090 100644 Binary files a/docs/Presentations/SPAWN/Dia5.JPG and b/docs/Presentations/SPAWN/Dia5.JPG differ diff --git a/docs/Presentations/SPAWN/Dia6.JPG b/docs/Presentations/SPAWN/Dia6.JPG index a7e01cdce..65adc4636 100644 Binary files a/docs/Presentations/SPAWN/Dia6.JPG and b/docs/Presentations/SPAWN/Dia6.JPG differ diff --git a/docs/Presentations/SPAWN/Dia7.JPG b/docs/Presentations/SPAWN/Dia7.JPG index 4ed46bfd8..32f890147 100644 Binary files a/docs/Presentations/SPAWN/Dia7.JPG and b/docs/Presentations/SPAWN/Dia7.JPG differ diff --git a/docs/Presentations/SPAWN/Dia8.JPG b/docs/Presentations/SPAWN/Dia8.JPG index 87b45f85c..ee57c73bb 100644 Binary files a/docs/Presentations/SPAWN/Dia8.JPG and b/docs/Presentations/SPAWN/Dia8.JPG differ diff --git a/docs/Presentations/SPAWN/Dia9.JPG b/docs/Presentations/SPAWN/Dia9.JPG index 0f2a2e707..5009b2063 100644 Binary files a/docs/Presentations/SPAWN/Dia9.JPG and b/docs/Presentations/SPAWN/Dia9.JPG differ diff --git a/docs/Presentations/SPAWN/SPAWN.JPG b/docs/Presentations/SPAWN/SPAWN.JPG index 41b4fe35c..efb582913 100644 Binary files a/docs/Presentations/SPAWN/SPAWN.JPG and b/docs/Presentations/SPAWN/SPAWN.JPG differ diff --git a/docs/Presentations/SPAWNSTATIC/Dia1.JPG b/docs/Presentations/SPAWNSTATIC/Dia1.JPG new file mode 100644 index 000000000..6447e7241 Binary files /dev/null and b/docs/Presentations/SPAWNSTATIC/Dia1.JPG differ diff --git a/docs/Presentations/SPOT/Dia1.JPG b/docs/Presentations/SPOT/Dia1.JPG new file mode 100644 index 000000000..b23abb17e Binary files /dev/null and b/docs/Presentations/SPOT/Dia1.JPG differ diff --git a/docs/Presentations/SPOT/Dia2.JPG b/docs/Presentations/SPOT/Dia2.JPG new file mode 100644 index 000000000..b6c8a1dff Binary files /dev/null and b/docs/Presentations/SPOT/Dia2.JPG differ diff --git a/docs/Presentations/SPOT/Dia3.JPG b/docs/Presentations/SPOT/Dia3.JPG new file mode 100644 index 000000000..599c22b24 Binary files /dev/null and b/docs/Presentations/SPOT/Dia3.JPG differ diff --git a/docs/Presentations/TASK_A2A/Dia1.JPG b/docs/Presentations/TASK_A2A/Dia1.JPG new file mode 100644 index 000000000..6abcff448 Binary files /dev/null and b/docs/Presentations/TASK_A2A/Dia1.JPG differ diff --git a/docs/Presentations/TASK_A2A/Dia10.JPG b/docs/Presentations/TASK_A2A/Dia10.JPG new file mode 100644 index 000000000..004e8a810 Binary files /dev/null and b/docs/Presentations/TASK_A2A/Dia10.JPG differ diff --git a/docs/Presentations/TASK_A2A/Dia11.JPG b/docs/Presentations/TASK_A2A/Dia11.JPG new file mode 100644 index 000000000..8ae19920e Binary files /dev/null and b/docs/Presentations/TASK_A2A/Dia11.JPG differ diff --git a/docs/Presentations/TASK_A2A/Dia2.JPG b/docs/Presentations/TASK_A2A/Dia2.JPG new file mode 100644 index 000000000..df1a4f593 Binary files /dev/null and b/docs/Presentations/TASK_A2A/Dia2.JPG differ diff --git a/docs/Presentations/TASK_A2A/Dia3.JPG b/docs/Presentations/TASK_A2A/Dia3.JPG new file mode 100644 index 000000000..e7d452c2b Binary files /dev/null and b/docs/Presentations/TASK_A2A/Dia3.JPG differ diff --git a/docs/Presentations/TASK_A2A/Dia4.JPG b/docs/Presentations/TASK_A2A/Dia4.JPG new file mode 100644 index 000000000..49eafe1ae Binary files /dev/null and b/docs/Presentations/TASK_A2A/Dia4.JPG differ diff --git a/docs/Presentations/TASK_A2A/Dia5.JPG b/docs/Presentations/TASK_A2A/Dia5.JPG new file mode 100644 index 000000000..77e2d78a4 Binary files /dev/null and b/docs/Presentations/TASK_A2A/Dia5.JPG differ diff --git a/docs/Presentations/TASK_A2A/Dia6.JPG b/docs/Presentations/TASK_A2A/Dia6.JPG new file mode 100644 index 000000000..7ae05ba76 Binary files /dev/null and b/docs/Presentations/TASK_A2A/Dia6.JPG differ diff --git a/docs/Presentations/TASK_A2A/Dia7.JPG b/docs/Presentations/TASK_A2A/Dia7.JPG new file mode 100644 index 000000000..e826fda26 Binary files /dev/null and b/docs/Presentations/TASK_A2A/Dia7.JPG differ diff --git a/docs/Presentations/TASK_A2A/Dia8.JPG b/docs/Presentations/TASK_A2A/Dia8.JPG new file mode 100644 index 000000000..3ca858a8d Binary files /dev/null and b/docs/Presentations/TASK_A2A/Dia8.JPG differ diff --git a/docs/Presentations/TASK_A2A/Dia9.JPG b/docs/Presentations/TASK_A2A/Dia9.JPG new file mode 100644 index 000000000..f608efda1 Binary files /dev/null and b/docs/Presentations/TASK_A2A/Dia9.JPG differ diff --git a/docs/Presentations/TASK_A2A_DISPATCHER/Dia1.JPG b/docs/Presentations/TASK_A2A_DISPATCHER/Dia1.JPG new file mode 100644 index 000000000..d2cce8ba7 Binary files /dev/null and b/docs/Presentations/TASK_A2A_DISPATCHER/Dia1.JPG differ diff --git a/docs/Presentations/TASK_A2A_DISPATCHER/Dia10.JPG b/docs/Presentations/TASK_A2A_DISPATCHER/Dia10.JPG new file mode 100644 index 000000000..0ec74665a Binary files /dev/null and b/docs/Presentations/TASK_A2A_DISPATCHER/Dia10.JPG differ diff --git a/docs/Presentations/TASK_A2A_DISPATCHER/Dia11.JPG b/docs/Presentations/TASK_A2A_DISPATCHER/Dia11.JPG new file mode 100644 index 000000000..ab024e79e Binary files /dev/null and b/docs/Presentations/TASK_A2A_DISPATCHER/Dia11.JPG differ diff --git a/docs/Presentations/TASK_A2A_DISPATCHER/Dia2.JPG b/docs/Presentations/TASK_A2A_DISPATCHER/Dia2.JPG new file mode 100644 index 000000000..1cedf0488 Binary files /dev/null and b/docs/Presentations/TASK_A2A_DISPATCHER/Dia2.JPG differ diff --git a/docs/Presentations/TASK_A2A_DISPATCHER/Dia3.JPG b/docs/Presentations/TASK_A2A_DISPATCHER/Dia3.JPG new file mode 100644 index 000000000..9575ddf60 Binary files /dev/null and b/docs/Presentations/TASK_A2A_DISPATCHER/Dia3.JPG differ diff --git a/docs/Presentations/TASK_A2A_DISPATCHER/Dia4.JPG b/docs/Presentations/TASK_A2A_DISPATCHER/Dia4.JPG new file mode 100644 index 000000000..5527c3c91 Binary files /dev/null and b/docs/Presentations/TASK_A2A_DISPATCHER/Dia4.JPG differ diff --git a/docs/Presentations/TASK_A2A_DISPATCHER/Dia5.JPG b/docs/Presentations/TASK_A2A_DISPATCHER/Dia5.JPG new file mode 100644 index 000000000..e034e3b6e Binary files /dev/null and b/docs/Presentations/TASK_A2A_DISPATCHER/Dia5.JPG differ diff --git a/docs/Presentations/TASK_A2A_DISPATCHER/Dia6.JPG b/docs/Presentations/TASK_A2A_DISPATCHER/Dia6.JPG new file mode 100644 index 000000000..6ec63947d Binary files /dev/null and b/docs/Presentations/TASK_A2A_DISPATCHER/Dia6.JPG differ diff --git a/docs/Presentations/TASK_A2A_DISPATCHER/Dia7.JPG b/docs/Presentations/TASK_A2A_DISPATCHER/Dia7.JPG new file mode 100644 index 000000000..4a81a7d77 Binary files /dev/null and b/docs/Presentations/TASK_A2A_DISPATCHER/Dia7.JPG differ diff --git a/docs/Presentations/TASK_A2A_DISPATCHER/Dia8.JPG b/docs/Presentations/TASK_A2A_DISPATCHER/Dia8.JPG new file mode 100644 index 000000000..ec2b4d47a Binary files /dev/null and b/docs/Presentations/TASK_A2A_DISPATCHER/Dia8.JPG differ diff --git a/docs/Presentations/TASK_A2A_DISPATCHER/Dia9.JPG b/docs/Presentations/TASK_A2A_DISPATCHER/Dia9.JPG new file mode 100644 index 000000000..40d612b50 Binary files /dev/null and b/docs/Presentations/TASK_A2A_DISPATCHER/Dia9.JPG differ diff --git a/docs/Presentations/TASK_A2G/Dia1.JPG b/docs/Presentations/TASK_A2G/Dia1.JPG index 08c642ea5..160431a1b 100644 Binary files a/docs/Presentations/TASK_A2G/Dia1.JPG and b/docs/Presentations/TASK_A2G/Dia1.JPG differ diff --git a/docs/Presentations/TASK_CARGO/Dia1.JPG b/docs/Presentations/TASK_CARGO/Dia1.JPG new file mode 100644 index 000000000..d56bce0bf Binary files /dev/null and b/docs/Presentations/TASK_CARGO/Dia1.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia1.JPG b/docs/Presentations/TASK_DISPATCHER/Dia1.JPG index 2501578e9..f71ee5d5d 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia1.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia1.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia10.JPG b/docs/Presentations/TASK_DISPATCHER/Dia10.JPG index 55f545f95..3fdf22073 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia10.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia10.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia11.JPG b/docs/Presentations/TASK_DISPATCHER/Dia11.JPG index 37f538608..659c6c2be 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia11.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia11.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia12.JPG b/docs/Presentations/TASK_DISPATCHER/Dia12.JPG index d69f65f24..d1ed49b67 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia12.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia12.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia13.JPG b/docs/Presentations/TASK_DISPATCHER/Dia13.JPG index 4c4e25a5a..967b83fce 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia13.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia13.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia14.JPG b/docs/Presentations/TASK_DISPATCHER/Dia14.JPG index 99f45e145..43995d797 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia14.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia14.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia15.JPG b/docs/Presentations/TASK_DISPATCHER/Dia15.JPG index 09a00e4eb..a702252f7 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia15.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia15.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia16.JPG b/docs/Presentations/TASK_DISPATCHER/Dia16.JPG index c74c69956..4c00ac4e6 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia16.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia16.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia17.JPG b/docs/Presentations/TASK_DISPATCHER/Dia17.JPG index ab23e55da..28dc9d79e 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia17.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia17.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia18.JPG b/docs/Presentations/TASK_DISPATCHER/Dia18.JPG index 9ad40904a..b6bc05504 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia18.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia18.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia2.JPG b/docs/Presentations/TASK_DISPATCHER/Dia2.JPG index edfc790e5..1dfd6d9c6 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia2.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia2.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia3.JPG b/docs/Presentations/TASK_DISPATCHER/Dia3.JPG index 2e129761a..20212e5f8 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia3.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia3.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia4.JPG b/docs/Presentations/TASK_DISPATCHER/Dia4.JPG index 6adebc4f2..02195aecb 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia4.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia4.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia5.JPG b/docs/Presentations/TASK_DISPATCHER/Dia5.JPG index 057a68877..cc64e30c7 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia5.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia5.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia6.JPG b/docs/Presentations/TASK_DISPATCHER/Dia6.JPG index 9387e30e8..c81ef0960 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia6.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia6.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia7.JPG b/docs/Presentations/TASK_DISPATCHER/Dia7.JPG index 66adb011f..a989eef34 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia7.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia7.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia8.JPG b/docs/Presentations/TASK_DISPATCHER/Dia8.JPG index cf0b93e30..194c65558 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia8.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia8.JPG differ diff --git a/docs/Presentations/TASK_DISPATCHER/Dia9.JPG b/docs/Presentations/TASK_DISPATCHER/Dia9.JPG index 49335ef21..0c2c48816 100644 Binary files a/docs/Presentations/TASK_DISPATCHER/Dia9.JPG and b/docs/Presentations/TASK_DISPATCHER/Dia9.JPG differ diff --git a/docs/Presentations/ZONE/Dia1.JPG b/docs/Presentations/ZONE/Dia1.JPG index ca4dd2b3f..75c601caa 100644 Binary files a/docs/Presentations/ZONE/Dia1.JPG and b/docs/Presentations/ZONE/Dia1.JPG differ diff --git a/docs/README.md b/docs/README.md index 56c645be6..b47295d70 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,4 @@ -# 1) MOOSE framework +# 1. MOOSE framework MOOSE is a **M**ission **O**bject **O**riented **S**cripting **E**nvironment, and is meant for mission designers and mission hosters. It allows to quickly setup complex missions using pre-scripted scenarios using the available classes within the MOOSE Framework. @@ -17,55 +17,7 @@ Within the community, key users will start supporting, documenting, explaining a It is the ambition to grow this framework as a de-facto standard for mission designers to use. - - -# 2) MOOSE usage - -The delivery of MOOSE follows a structured release process. Over time, new features are added that can be used in your mission. - -### The latest release of MOOSE can be downloaded **[here](https://github.com/FlightControl-Master/MOOSE/releases)**. - -There are 3 different ways how you can use MOOSE, each with a different engagement and complexity level: - - - -## 2.1) Use MOOSE as a Mission Designer - -Refer to the detailed **[Usage Guide](Usage_Guide.html)** for more information. - - - -## 2.2) Beta test MOOSE - -Beta testers of MOOSE are requested to install additional software. - -As a return or as a reward, testers get: - - * Newly developed features planned for the next MOOSE release can be tested and incorporated in your missions early. - * You can evaluate and contribute to the stability of the next release. - * Your mission creation workflow becomes very flexible. New features are dynamically added to you missions. - -Please read the detailed **[Beta Test Guide](Beta_Test_Guide.html)** for more information. - - - -## 2.3) Contribute on the MOOSE development - -Those people who have experience in lua development or are excited to contribute to the MOOSE project are welcome. - -Please consult the **[Contribution Guide](Contribution_Guide.html)** for more information. - - - -# 3) MOOSE Support Channels - -MOOSE is broadcasted, documented and supported through various social media channels. - -Click here for the **[communities guide](Communities.html)** of the MOOSE framework. - - - -# 4) MOOSE Framework +# 2. MOOSE Framework The following classes are currently embedded within MOOSE framework and can be included within your mission scripts: @@ -92,9 +44,9 @@ You'll need to browse to the right MOOSE Class within the inheritance tree struc -## 4.1) MOOSE Demonstration Missions +## MOOSE Demonstration Missions -The framework comes with demonstration missions which can be downloaded [here](https://github.com/FlightControl-Master/MOOSE_PRESENTATIONS/releases), that you can try out and helps you to code. +The framework comes with demonstration missions which can be downloaded [here](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases), that you can try out and helps you to code. These missions provide examples of defined use cases how the MOOSE framework can be utilized. Each test mission is located in a separate directory, which contains at least one .lua file and .miz file. The .lua file contains the mission script file that shows how the use case was implemented. You can copy/paste code the code snippets from this .lua file into your missions, as it will accellerate your mission developments. @@ -104,70 +56,47 @@ more complex mission scenarios by combining these MOOSE classes into a complex b Some of these exact test missions are also demonstrated in a video format on the [YouTube channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg). +## 2.1. MOOSE Human Tasking Classes -## 4.2) MOOSE Core Classes +MOOSE Tasking Classes provide a comprehensive Mission Orchestration System. +Through COMMANDCENTERs, multiple logical MISSIONs can be orchestrated for coalitions. +Within each MISSION, various TASKs can be defined. +Each TASK has a TASK ACTION flow, which is the flow that a player (hosted by a UNIT) within the simulator needs to follow and accomplish. -These classes define the base building blocks of the MOOSE framework. These classes are heavily used within the MOOSE framework. +* [COMMANDCENTER](Documentation/CommandCenter.html): Orchestrates various logical MISSIONs for a coalition. -* [BASE](Documentation/Base.html): The main class from which all MOOSE classes are derived from. The BASE class contains essential functions to support inheritance and MOOSE object execution tracing (logging within the DCS.log file in the saved games folder of the user). +* [MISSION](Documentation/Mission.html): Each MISSION has various TASKs to be executed and accomplished by players. -* [DATABASE](Documentation/Database.html): Creates a collection of GROUPS[], UNITS[], CLIENTS[] and managed these sets automatically. Provides an API set to retrieve a GROUP, UNIT or CLIENT instance from the _DATABASE object using defined APIs. The collections are maintained dynamically during the execution of the mission, so when players join, leave, when units are created or destroyed, the collections are dynamically updated. +* [TASK_A2A_DISPATCHER](Documentation/Task_A2A_Dispatcher.html): Automatically and dynamically dispatch A2A tasks to be executed by human players, as a result of the detection of airborne targets within a Mission scope. -* [EVENT](Documentation/Event.html): Provides the Event Dispatcher base class to handle DCS Events, being fired upon registered events within the DCS simulator. Note that EVENT is used by BASE, exposing OnEvent() methods to catch these DCS events. +* [TASK_A2G_DISPATCHER](Documentation/Task_A2G_Dispatcher.html): Automatically and dynamically dispatch A2G tasks to be executed by human players, as a result of the detection of ground targets within a Mission scope. -* [SCHEDULER](Documentation/Scheduler.html): This class implements a timer scheduler that will call at optional specified intervals repeatedly or just one time a scheduled function. +* [TASK_A2A](Documentation/Task_A2A.html): Models a A2A CAP, INTERCEPT and SWEEP tasks where a Player is routed towards an attack zone without enemies nearby, and various ground targets need to be eliminated. -* [FSM](Documentation/Fsm.html): The main FSM class can be used to build a Finite State Machine. The derived FSM_ classes provide Finite State Machine building capability for CONTROLLABLEs, ACT_ (Task Actions) classes, TASKs and SETs. - -* [MENU](Documentation/Menu.html): Set Menu options (F10) for All Players, Coalitions, Groups, Clients. MENU also manages the recursive removal of menus, which is a big asset! - -* [SET](Documentation/Set.html): Create SETs of MOOSE objects. SETs can be created for GROUPs, UNITs, AIRBASEs, ... -The SET can be filtered with defined filter criteria. -Iterators are available that iterate through the GROUPSET, calling a function for each object within the SET. - -* [MESSAGE](Documentation/Message.html): A message publishing system, displaying messages to Clients, Coalitions or All players. - -* [POINTS](Documentation/Point.html): A set of point classes to manage the 2D or 3D simulation space, through an extensive method library. -The POINT_VEC3 class manages the 3D simulation space, while the POINT_VEC2 class manages the 2D simulation space. - -* [ZONES](Documentation/Zone.html): A set of zone classes that provide the functionality to validate the presence of GROUPS, UNITS, CLIENTS, STATICS within a certain ZONE. The zones can take various forms and can be movable. +* [TASK_A2G](Documentation/Task_A2G.html): Models a A2G SEAD, CAS and BAI tasks where a Player is routed towards an attack zone with enemies nearby, and various ground targets need to be eliminated. +## 2.2. MOOSE AI Controlling Classes -## 4.3) MOOSE Wrapper Classes +MOOSE AI Controlling Classes provide mechanisms to control AI over long lasting processes. +These AI Controlling Classes are based on FSM (Finite State Machine) Classes, and provided an encapsulated way to make AI behave or execute an activity. -MOOSE Wrapper Classes provide an object oriented hierarchical mechanism to manage the DCS objects within the simulator. -Wrapper classes provide another easier mechanism to control Groups, Units, Statics, Airbases and other objects. +* [AI A2A Defenses](Documentation/AI_A2A_Dispatcher.html): Create automatic A2A defense systems executed by AI and perform CAP or GCI. + * [AI\_A2A\_GCICAP](Documentation/AI_A2A_Dispatcher.html#AI_A2A_GCICAP): Using an easy process you can define an A2A defense system using the Mission Editor. + * [AI\_A2A\_DISPATCHER](Documentation/AI_A2A_Dispatcher.html#AI_A2A_DISPATCHER): Same as AI\_A2A\_GCICAP, but is for more advanced or developer type mission designers. This class provides more options. -* **[OBJECT](Documentation/Object.html)**: This class provides the base for MOOSE objects. +* [AI\_BALANCER](Documentation/AI_Balancer.html): Compensate in a multi player mission the abscence of players with dynamically spawned AI air units. When players join CLIENTS, the AI will either be destroyed, or will fly back to the home or nearest friendly airbase. -* **[IDENTIFIABLE](Documentation/Identifiable.html)**: This class provides the base for MOOSE identifiable objects, which is every object within the simulator :-). +* [AI\_PATROL\_ZONE](Documentation/AI_Patrol_Zone.html): Make an alive AI Group patrol a zone derived from the ZONE_BASE class. Manage out-of-fuel events and set altitude and speed ranges for the patrol. -* **[POSITIONABLE](Documentation/Positionable.html)**: This class provides the base for MOOSE positionable objects. These are AIRBASEs, STATICs, GROUPs, UNITs ... +* [AI\_CAP](Documentation/AI_Cap.html): Make an alive AI Group perform Combat Air Patrol as a dynamic process. -* **[CONTROLLABLE](Documentation/Controllable.html)**: This class provides the base for MOOSE controllable objects. These are GROUPs, UNITs, CLIENTs. +* [AI\_CAS](Documentation/AI_Cas.html): Make an alive AI Group perform Close Air Support as a dynamic process. -* **[AIRBASE](Documentation/Airbase.html)**: This class wraps a DCS Airbase object within the simulator. - -* **[GROUP](Documentation/Group.html)**: This class wraps a DCS Group objects within the simulator, which are currently alive. -It provides a more extensive API set. -It takes an abstraction of the complexity to give tasks, commands and set various options to DCS Groups. -Additionally, the GROUP class provides a much richer API to identify various properties of the DCS Group. -For each DCS Group created object within a running mission, a GROUP object will be created automatically, beging managed within the DATABASE. - -* **[UNIT](Documentation/Unit.html)**: This class wraps a DCS Unit object within the simulator, which are currently alive. It provides a more extensive API set, as well takes an abstraction of the complexity to give commands and set various options to DCS Units. Additionally, the UNIT class provides a much richer API to identify various properties of the DCS Unit. For each DCS Unit object created within a running mission, a UNIT object will be created automatically, that is stored within the DATABASE, under the _DATABASE object. -the UNIT class provides a more extensive API set, taking an abstraction of the complexity to give tasks, commands and set various options to DCS Units. -For each DCS Unit created object within a running mission, a UNIT object will be created automatically, beging managed within the DATABASE. - -* **[CLIENT](Documentation/Client.html)**: This class wraps a DCS Unit object within the simulator, which has a skill Client or Player. -The CLIENT class derives from the UNIT class, thus contains the complete UNIT API set, and additionally, the CLIENT class provides an API set to manage players joining or leaving clients, sending messages to players, and manage the state of units joined by players. For each DCS Unit object created within a running mission that can be joined by a player, a CLIENT object will be created automatically, that is stored within the DATABASE, under the _DATABASE object. - -* **[STATIC](Documentation/Static.html)**: This class wraps a DCS StaticObject object within the simulator. -The STATIC class derives from the POSITIONABLE class, thus contains also the position API set. +* [AI\_BAI](Documentation/AI_Bai.html): Make an alive AI Group perform Battlefield Air Interdiction as a dynamic process. - -## 4.4) MOOSE Functional Classes +## 2.3. MOOSE Functional Classes MOOSE Functional Classes provide various functions that are useful in mission design. @@ -181,64 +110,136 @@ MOOSE Functional Classes provide various functions that are useful in mission de * [SCORING](Documentation/Scoring.html): Administer the scoring of player achievements, and create a CSV file logging the scoring events for use at team or squadron websites. +* [SEAD](Documentation/Sead.html): Make SAM sites avoid SEAD missiles being fired at. + +* [DESIGNATE](Documentation/Designate.html): Make AI automatically designate detected targets, and provide menu options for players to give instructions to the AI how to designate (by laser, smoke or illumination). + +* [AIRBASEPOLICE](Documentation/AirbasePolice.html): Control the speed of players at the airbases. Speeding players are eliminated (does not work due to a bug in the DCS). + +* [CLEANUP\_AIRBASE](Documentation/CleanUp.html): Keeps the airbases clean from clutter. (Only partly functional due to a bug in DCS, destroyed objects cannot be removed). -## 4.5) MOOSE AI Controlling Classes +## 2.4. MOOSE Wrapper Classes -MOOSE AI Controlling Classes provide mechanisms to control AI over long lasting processes. -These AI Controlling Classes are based on FSM (Finite State Machine) Classes, and provided an encapsulated way to make AI behave or execute an activity. +MOOSE Wrapper Classes provide an object oriented hierarchical mechanism to manage the DCS objects within the simulator. +Wrapper classes provide another easier mechanism to control Groups, Units, Statics, Airbases and other objects. -* [AI_BALANCER](Documentation/AI_Balancer.html): Compensate in a multi player mission the abscence of players with dynamically spawned AI air units. When players join CLIENTS, the AI will either be destroyed, or will fly back to the home or nearest friendly airbase. +* [OBJECT](Documentation/Object.html): This class provides the base for MOOSE objects. -* [AI_PATROL_ZONE](Documentation/AI_Patrol_Zone.html): Make an alive AI Group patrol a zone derived from the ZONE_BASE class. Manage out-of-fuel events and set altitude and speed ranges for the patrol. +* [IDENTIFIABLE](Documentation/Identifiable.html): This class provides the base for MOOSE identifiable objects, which is every object within the simulator :-). -* [AI_CAP](Documentation/AI_Cap.html): Make an alive AI Group perform Combat Air Patrol in a dynamic process. +* [POSITIONABLE](Documentation/Positionable.html): This class provides the base for MOOSE positionable objects. These are AIRBASEs, STATICs, GROUPs, UNITs ... -* [AI_CAS](Documentation/AI_Cas.html): Make an alive AI Group perform Close Air Support in a dynamic process. +* [CONTROLLABLE](Documentation/Controllable.html): This class provides the base for MOOSE controllable objects. These are GROUPs, UNITs, CLIENTs. -* [AI_CARGO](Documentation/AI_Cargo.html): Make AI behave as cargo. Various CARGO types exist. +* [AIRBASE](Documentation/Airbase.html): This class wraps a DCS Airbase object within the simulator. + +* [GROUP](Documentation/Group.html): This class wraps a DCS Group objects within the simulator. + +* [UNIT](Documentation/Unit.html): This class wraps a DCS Unit object within the simulator. + +* [CLIENT](Documentation/Client.html): This class wraps a DCS Unit object within the simulator, which has a skill Client or Player. + +* [STATIC](Documentation/Static.html): This class wraps a DCS StaticObject object within the simulator. + + +## 2.5. MOOSE Core Classes + +These classes define the base building blocks of the MOOSE framework. These classes are heavily used within the MOOSE framework. + +* [BASE](Documentation/Base.html): The main class from which all MOOSE classes are derived from. The BASE class contains essential functions to support inheritance and MOOSE object execution tracing (logging within the DCS.log file in the saved games folder of the user). + +* [DATABASE](Documentation/Database.html): Creates a collection of GROUPS[], UNITS[], CLIENTS[] and managed these sets automatically. Provides an API set to retrieve a GROUP, UNIT or CLIENT instance from the _DATABASE object using defined APIs. The collections are maintained dynamically during the execution of the mission, so when players join, leave, when units are created or destroyed, the collections are dynamically updated. + +* [EVENT](Documentation/Event.html): Provides the Event Dispatcher base class to handle DCS Events, being fired upon registered events within the DCS simulator. Note that EVENT is used by BASE, exposing OnEvent() methods to catch these DCS events. + +* [SCHEDULER](Documentation/Scheduler.html): This class implements a timer scheduler that will call at optional specified intervals repeatedly or just one time a scheduled function. + +* [Finite State Machines](Documentation/Fsm.html): Finite State Machine provides a process management or state machine capability for various scenarios. +* [FSM](Documentation/Fsm.html#FSM): The main FSM class can be used to build a generic Finite State Machine. +* [FSM\_CONTROLLABLE](Documentation/Fsm.html#FSM_CONTROLLABLE): An FSM class to control a Controllable. A controllable is a group or unit that can be controlled. + +* [MENU](Documentation/Menu.html): Set Menu options under the radio menu (F10). MENU classes also manage the recursive removal of menus, and the intelligent refresh of menu options (only the changes are applied). +* [MENU\_MISSION](Documentation/Menu.html#MENU_MISSION): Set a main menu structure for the complete mission, for all players. +* [MENU\_MISSION\_COMMAND](Documentation/Menu.html#MENU_MISSION_COMMAND): Set a main menu command for the complete mission, for all players. +* [MENU\_COALITION](Documentation/Menu.html#MENU_COALITION): Set a menu structure for a coalition, for all players of that coalition. +* [MENU\_COALITION\_COMMAND](Documentation/Menu.html#MENU_COALITION_COMMAND): Set a menu command for a coalition, for all players of that coalition. +* [MENU\_GROUP](Documentation/Menu.html#MENU_GROUP): Set a menu structure for a group, for all players of that group. +* [MENU\_GROUP\_COMMAND](Documentation/Menu.html#MENU_GROUP_COMMAND): Set a menu command for a group, for all players of that group. + +* [SET](Documentation/Set.html): Create SETs of MOOSE objects. The SET can be filtered with defined filter criteria. Iterators are available that iterate through the SET, calling a function for each object within the SET. +* [SET\_GROUP](Documentation/Set.html#SET_GROUP): Create a SET of GROUP objects. +* [SET\_UNIT](Documentation/Set.html#SET_UNIT): Create a SET of UNIT objects. +* [SET\_CLIENT](Documentation/Set.html#SET_CLIENT): Create a SET of CLIENT objects. +* [SET\_AIRBASE](Documentation/Set.html#SET_AIRBASE): Create a SET of AIRBASE objects. +* [SET\_CARGO](Documentation/Set.html#SET_CARGO): Create a SET of CARGO objects. + +* [MESSAGE](Documentation/Message.html): A message publishing system, displaying messages to Clients, Coalitions or All players. + +* [COORDINATE](Documentation/Point.html#COORDINATE): Manage 2D and 3D points in the simulation space, and use its methods for various actions on the coordinate. +* [POINT\_VEC2](Documentation/Point.html#POINT_VEC2): Manage 2D points in the simulation space. +* [POINT\_VEC3](Documentation/Point.html#POINT_VEC3): Manage 3D points in the simulation space. + +* [ZONES](Documentation/Zone.html#ZONE): Create a zone object from a trigger zone as defined in the Mission Editor. +* [ZONE\_POLYGON](Documentation/Zone.html#ZONE_POLYGON): Create a zone object from a group object, which is late activated, and its route points define the zone. +* [ZONE\_RADIUS](Documentation/Zone.html#ZONE_RADIUS): Create a zone object from a 2D vector on the battlefield, with a defined radius. +* [ZONE\_UNIT](Documentation/Zone.html#ZONE_UNIT): Create a zone object around a unit on the battlefield, with a defined radius. This, this zone is a moving zone! +* [ZONE\_GROUP](Documentation/Zone.html#ZONE_GROUP): Create a zone object around a group on the battlefield, with a defined radius. The first object in the group has the zone, and is thus a moving zone! + +* [CARGO](Documentation/Cargo.html): Manage Cargo in the simulation. +* [CARGO\_GROUP](Documentation/Cargo.html#CARGO_GROUP): Manage Cargo that is defined as a GROUP object within the simulation. + +* [SPAWNSTATIC](Documentation/SpawnStatic.html#SPAWNSTATIC): Spawn Static objects using a predefined "template". + +* [BEACON](Documentation/Radio.html): Create beacons. +* [RADIO](Documentation/Radio.html): Create radio communication. -## 4.6) MOOSE Human Tasking Classes +# 3. MOOSE usage -MOOSE Tasking Classes provide a comprehensive Mission Orchestration System. -Through COMMANDCENTERs, multiple logical MISSIONs can be orchestrated for coalitions. -Within each MISSION, various TASKs can be defined. -Each TASK has a TASK ACTION flow, which is the flow that a player (hosted by a UNIT) within the simulator needs to follow and accomplish. +The delivery of MOOSE follows a structured release process. Over time, new features are added that can be used in your mission. -* [COMMANDCENTER](Documentation/CommandCenter.html): Orchestrates various logical MISSIONs for a coalition. +### The latest release of MOOSE can be downloaded **[here](https://github.com/FlightControl-Master/MOOSE/releases)**. -* [MISSION](Documentation/Mission.html): Each MISSION has various TASKs to be executed and accomplished by players. +There are 3 different ways how you can use MOOSE, each with a different engagement and complexity level: -* [TASK](Documentation/Task.html): Each TASK has a status, and has a TASK ACTION flow for each Player acting and executing the TASK. -* [TASK_SEAD](Documentation/Task_SEAD.html): Models a SEAD Task, where a Player is routed towards an attack zone, and various SEADing targets need to be eliminated. +## 3.1. Use MOOSE as a Mission Designer -* [TASK_BAI](Documentation/Task_A2G.html): Models a CAP Task, where a Player is routed towards an attack zone without enemies nearby, and various ground targets need to be eliminated. +Refer to the detailed **[Usage Guide](Usage_Guide.html)** for more information. -* [TASK_CAS](Documentation/Task_A2G.html): Models a CAS Task, where a Player is routed towards an attack zone with enemies nearby, and various ground targets need to be eliminated. + +## 3.2. Beta test MOOSE + +Beta testers of MOOSE are requested to install additional software. + +As a return or as a reward, testers get: + + * Newly developed features planned for the next MOOSE release can be tested and incorporated in your missions early. + * You can evaluate and contribute to the stability of the next release. + * Your mission creation workflow becomes very flexible. New features are dynamically added to you missions. + +Please read the detailed **[Beta Test Guide](Beta_Test_Guide.html)** for more information. + + +## 3.3. Contribute on the MOOSE development + +Those people who have experience in lua development or are excited to contribute to the MOOSE project are welcome. + +Please consult the **[Contribution Guide](Contribution_Guide.html)** for more information. + + + +# 4. MOOSE Support Channels + +MOOSE is broadcasted, documented and supported through various social media channels. + +Click here for the **[communities guide](Communities.html)** of the MOOSE framework. -## 4.7) MOOSE Action Classes - -MOOSE Action Classes are task action sub-flows, that can be used and combined, to quickly define a comprehensive end-to-end task action flow. -For example, for the SEAD Task, the task action flow combines the actions ASSIGN, ROUTE, ACCOUNT and ASSIST task action sub-flows. - -* [ACT_ASSIGN](Documentation/Assign.html): Mechanism to accept a TASK by a player. For example, assign the task only after the player accepts the task using the menu. - -* [ACT_ROUTE](Documentation/Route.html): Mechanisms to route players to zones or any other positionable coordinate. For example, route a player towards a zone. - -* [ACT_ACCOUNT](Documentation/Account.html): Mechanisms to account various events within the simulator. For example, account the dead events, accounting dead units within a Target SET within a ZONE. - -* [ACT_ASSIST](Documentation/Assist.html): Mechanisms to assist players executing a task. For example, acquire targets through smoking them. - - - - - -# 5) Credits +# 5. Credits Note that most of the framework is based on code i've written myself, but some code of it is also based on code that i've seen as great scripting code and ideas, diff --git a/docs/Usage_Guide.md b/docs/Usage_Guide.md index 90afd885b..dba4e3368 100644 --- a/docs/Usage_Guide.md +++ b/docs/Usage_Guide.md @@ -3,6 +3,7 @@ Using the MOOSE framework is very easy, and this document provides you with a detailed explanation how to install and use MOOSE within your missions. + ## 1.1) MOOSE framework at GitHub You can find the source of [MOOSE framework on GITHUB](https://github.com/FlightControl-Master/MOOSE/). @@ -10,14 +11,22 @@ It is free for download and usage, since it is released under the GNU 3.0 open s Although the MOOSE contributors and tester are using the GitHub service to enforces a structured approval process, release and change management, and a communicative distribution and deployment, you, as a mission designer, don't need to mess with it. Still, if you are interrested intesting the latest features of MOOSE of in adding your own, you can read the [relevant](http://flightcontrol-master.github.io/MOOSE/Beta_Test_Guide.html) [guides](http://flightcontrol-master.github.io/MOOSE/Contribution_Guide.html) and/or contact FlightControl The MOOSE framework development is an open source project, and as such, contributors are welcome and encouraged to contribute on the development.Some key users have already started with this process. +## 1.2) MOOSE using a normal editor + +You can use the MOOSE framework with a normal editor. This is perfectly okay. +But you won't have IntelliSense enabled, which allows you to quickly search for the correct methods per class. + ## 1.2) Eclipse LDT -MOOSE utilizes the Eclipse Lua Development Tools. As a result, the MOOSE framework is documented using the luadocumentor standard. +The LDT environment or "Eclipse Lua Development Tools" is a fully integrated development environment for LUA developers. +It is recommended to use the LDT as your mission design editor using MOOSE. The MOOSE framework is documented using the luadocumentor standard. Every class, method and variable is documented within the source, and mission designers can write mission script lua code that is **intellisense**(-ed) ... What that means is that while you are coding your mission, your object and variables (derived from MOOSE classes) will list the methods and properties of that class interactively while coding ... ![Intellisense](Usage/Intellisense.JPG) +This highly increases the quality and the speed of your scripting. + ## 1.3) LUA training In order to efficiently use the MOOSE framework, it is highly recommended that you learn a couple of basic principles of lua. @@ -28,18 +37,23 @@ knowledge to "understand" the code, and also, to understand the syntax. **Therefore, I suggest you walk through this [lua quick guide](https://www.tutorialspoint.com/lua/lua_quick_guide.htm)**. Ignore the lua environment setup. DCS comes with a pre-defined lua environment. -# 2) MOOSE Installation Guide -## 2.1) Download the latest release of MOOSE + +# 2) Download the latest release of MOOSE The delivery of MOOSE follows a structured release process. Over time, new features are added that can be used in your mission. + ## The latest release of MOOSE can be downloaded **[here](https://github.com/FlightControl-Master/MOOSE/releases)**. **Unzip the files into a directory of your choice, but keep the folder structure intact**. -## 2.2) Download and install the Eclipse Lua Development Tools (LDT), which is an advanced lua editor. + +# 3) Download the software + + +## 3.1) Download and install the Eclipse Lua Development Tools **(LDT)**, which is an advanced lua editor. 1. If you don't have JAVA yet, you may have to install [java](https://www.java.com/en/download) first. 2. Download and Install [Eclipse LDT](https://eclipse.org/ldt) on your Windows 64 bit system. @@ -47,14 +61,17 @@ The delivery of MOOSE follows a structured release process. Over time, new featu TNow you should have a working LDT on your system. Don't skip this step, LDT is a game-changer. Don't believe us ? Well we challenge you to test and tell us what you think ! Once you tried coding with intellisense, you won't go back ! -## 2.3) Configure your LDT for the usage of MOOSE. -You need to configure your Eclipse LDT environment and link it with the MOOSE respository. + +# 4) Setup the **Moose_Framework project** in LDT. + +You need to configure your Eclipse LDT environment and link it with the Moose code. This will enable you to **start developing mission scripts** in lua, which will be **fully intellisense enabled**!!! Please follow the steps outlined! -### 2.3.1) Create a new **Workspace** in LDT. + +## 4.1) Create a new **Workspace** in LDT. The LDT editor has a concept of **Workspaces**, which contains all your settings of your editing environment, like views, menu options etc, and your code... Nothing to pay attention to really, but you need to set it up! @@ -63,44 +80,79 @@ When you open EclipseLDT for the first time, it will ask you where to put your * 1. Open Eclipse LDT. 2. Select the default workspace that LDT suggests. -### 2.3.2) Create a new **Project** in LDT. -Here, we will create a **New Project** called **Moose_Framework** in your LDT environment. -The project details are already defined within the MOOSE framework repository, -which is unzipped on your local MOOSE directory on your PC. -We will link into that directory and automatically load the Project properties. +## 4.2) Setup a **new project** in LDT and name it **Moose_Framework**. -1. Select from the Menu: **File** -> **New** -> **Lua Project**. +### 4.2.1) Select from the Menu: **File** -> **New** -> **Lua Project**. ![LDT_New_Project](Installation/LDT_New_Project.JPG) -2. A **New Project** dialog box is shown. +Here, we will create a **New Project** called **Moose_Framework** in your LDT environment. + +### Important! Name your project **Moose_Framework** + +The project details are already defined within the MOOSE framework repository, +which is unzipped in your **local Moose folder** on your PC. +We will link into that directory and **automatically load the Project properties**. + + +### 4.2.2) Type the Project Name: **Moose_Framework**. ![LDT_Project](Installation/LDT_Project.JPG) -3. Type the Project Name: **Moose_Framework**. -4. In the sub-box "Project Contents", select the option **Create Project at existing location** (from existing source). -5. **Browse** to the local MOOSE directory (press on the Browse button) and select the root directory of your local MO.OSE directory on your PC. Press OK. -6. You're back at the "New Project" dialog box. Press the **Next** button below the dialog box. +### 4.2.3) In the sub-box "Project Contents", select the option **Create Project at existing location** (from existing source). + +![LDT_Project](Installation/LDT_Project_Existing_Location.JPG) + +### 4.2.4) **Browse** to the local MOOSE directory (press on the Browse button) and select the **local Moose folder on your PC, which you unzipped**. Press OK. + +### 4.2.5) You're back at the "New Project" dialog box. Press the **Next** button below the dialog box. + __(All the other settings are by default ok)__. -7. You should see now a dialog box with the following properties. -Note that the Moose Development/Moose directory is flagged as the **Source Directory*. (It is listed totally on top.) + +### 4.2.6) You should see now a dialog box with the following properties. + +The Moose Development/Moose directory should be flagged as the **Source Directory*. (It is listed totally on top.) This is important because it will search in the files in this directory and sub directories for lua documentator enabled lua files. This will enable the intellisense of the MOOSE repository! ![LDT Finish](Installation/LDT_Moose_Framework_Finish.JPG) -8. Press the **Finish** button. +### 4.2.7) Press the **Finish** button. As a result, when you browse to the Script Explorer, you'll see the following: ![LDT_Script_Explorer](Installation/LDT_Script_Explorer.JPG) -**Congratulations! You have now setup your Moose_Framework project LDT environment!** +### 4.2.8) **Congratulations! You have now setup your Moose_Framework project LDT environment!** -# 2.4) Your first mission -## 2.4.1) Setup your **Mission Project** in LDT + +# 5) Setup a new project **Moose_Missions** in LDT, containing the the Moose demonstration missions. + +The framework comes with demonstration missions which can be downloaded [here](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases), that you can try out and helps you to code. +These missions provide examples of defined use cases how the MOOSE framework can be utilized. Each test mission is located in a separate directory, which contains at least one .lua file and .miz file. The .lua file contains the mission script file that shows how the use case was implemented. You can copy/paste code the code snippets from this .lua file into your missions, as it will accellerate your mission developments. You will learn, see, and understand how the different MOOSE classes need to be applied, and how you can create more complex mission scenarios by combining these MOOSE classes into a complex but powerful mission engine. +Some of these exact test missions are also demonstrated in a video format on the [YouTube channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg). + +## 5.1) Download the [Moose demonstration missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) latest release package. + + +## 5.2) Unzip the package into a local folder on your PC. + + +## 5.3) Setup a **new project** in LDT and name it **Moose_Missions**. + +### 5.3.1) In LDT, select from the menu **New lua project**, and name it Moose_Missions. + +### 5.3.2) Select the local folder on your PC, where you saved the demonstration missions contents. + +### 5.3.3) Press Finish + + + +# 6) Setup Your first mission + +## 6.1) Setup a new project in LDT and name it **My_Missions** In order to design your own missions, it is recommended you create a separate directory on your PC which contains your mission files. Your mission will be designed consisting possibly @@ -119,16 +171,15 @@ Therefore, **the recommendation is that your create for each mission a separate The MOOSE test mission folder structure is a good example how this could be organized. The LDT has been customized and provides a tool to **automatically** maintain your existing .miz files. -### 2.4.1.1. Select from the Menu: **File** -> **New** -> **Lua Project**. +### 6.1.1. Select from the Menu: **File** -> **New** -> **Lua Project**. ![LDT_New_Project](Installation/LDT_New_Project.JPG) -### 2.4.1.2. A **New Project** dialog box is shown. +### 6.1.2. A **New Project** dialog box is shown. ![LDT_Project](Installation/LDT_Project.JPG) -### 2.4.1.3. Type your Project Name: (In my example it is **DCS_Caucasus_Missions**. - +### 6.1.3. Type your Project Name: (In my example it is **DCS_Caucasus_Missions**. Note the indicated options in yellow: @@ -137,51 +188,51 @@ Note the indicated options in yellow: ![LDT_Project](Installation/LDT_Project_My_Missions.JPG) -### 2.4.1.4. Press **Next >** +### 6.1.4. Press **Next >** -### 2.4.1.5. Click the **Projects** tab at the top of the window. +### 6.1.5. Click the **Projects** tab at the top of the window. ![LDT_Project](Installation/LDT_New_Project_Projects.JPG) -### 2.4.1.6. Press the **Add...** button. +### 6.1.6. Press the **Add...** button. -### 2.4.1.7. A new windows will be displayed: **Required Project Selection**. +### 6.1.7. A new windows will be displayed: **Required Project Selection**. This is an important step. This will _link_ your project to the Moose_Framework project and will activate **intellisense**. ![LDT_Project](Installation/LDT_Select_Moose_Framework.JPG) -### 2.4.1.8. After the selection, press the **OK** button. +### 6.1.8. After the selection, press the **OK** button. -### 2.4.1.9. Watch your newly created project in the Script Explorer of LDT. +### 6.1.9. Watch your newly created project in the Script Explorer of LDT. You can delete the possibly created SRC directory. You won't need it at all. ![LDT_Project](Installation/LDT_Delete_Src.JPG) -### 2.4.1.10. Within your newly created Missions Project, right click and select **New -> Folder**. +### 6.1.10. Within your newly created Missions Project, right click and select **New -> Folder**. As explained above, each of your missions will be stored in a separate folder. Please follow the explanation how to do that. ![LDT_Project](Installation/LDT_Add_Folder.JPG) -### 2.4.1.11. Type the **Folder Name**. +### 6.1.11. Type the **Folder Name**. This can be any descriptive text explaining the title of your mission. ![LDT_Project](Installation/LDT_Mission_Folder_Name.JPG) -### 2.4.1.12. In your newly created **Mission Folder**, right click and select **New -> Lua File**. +### 6.1.12. In your newly created **Mission Folder**, right click and select **New -> Lua File**. This will create your **mission script file**, the file that contains all the lua code using the Moose framework using your mission. -### 2.4.1.13. Type the **Lua Mission Script Name**. +### 6.1.13. Type the **Lua Mission Script Name**. ![LDT_Project](Installation/LDT_Mission_Lua_File_Name.JPG) -## 2.4.2) Create your first Mission file +## 6.2) Create your first Mission file In the root of the MOOSE package, a file named **Moose.lua** can be found. In order to create or design a mission using the MOOSE framework, @@ -198,11 +249,11 @@ Voila, MOOSE is now included in your mission. During the execution of this missi Find below a detailed explanation of the actions to follow: -### 2.4.2.1. Open the Mission Editor in DCS, select an empty mission, and click the triggers button. +### 6.2.1. Open the Mission Editor in DCS, select an empty mission, and click the triggers button. ![LDT_Project](Installation/DCS_Triggers_Empty.JPG) -### 2.4.2.2. Add a new trigger, that will load the Moose.lua file. +### 6.2.2. Add a new trigger, that will load the Moose.lua file. Check the cyan colored circles: @@ -213,7 +264,7 @@ Check the cyan colored circles: ![LDT_Project](Installation/DCS_Triggers_Load_Moose_Add.JPG) -### 2.4.2.3. Select the Moose.lua loader from the **Moose Mission Setup** folder in the Moose_Framework pack. +### 6.2.3. Select the Moose.lua loader from the **Moose Mission Setup** folder in the Moose_Framework pack. Additional notes: @@ -224,11 +275,11 @@ Press the **OK** button. ![LDT_Project](Installation/DCS_Triggers_Load_Moose_Select_File.JPG) -### 2.4.2.4. Check that the Moose.lua file has been correctly added to your Mission. +### 6.2.4. Check that the Moose.lua file has been correctly added to your Mission. ![LDT_Project](Installation/DCS_Triggers_Load_Moose_File_Added.JPG) -### 2.4.2.5. Add a new trigger, that will load your mission .lua file. +### 6.2.5. Add a new trigger, that will load your mission .lua file. Check the cyan colored circles: @@ -239,7 +290,7 @@ Check the cyan colored circles: ![LDT_Project](Installation/DCS_Triggers_Load_Mission_Add.JPG) -### 2.4.2.6. Select the mission .lua file from your **missions** folder you just created or already have. +### 6.2.6. Select the mission .lua file from your **missions** folder you just created or already have. Additional notes: @@ -250,23 +301,24 @@ Press the **OK** button. ![LDT_Project](Installation/DCS_Triggers_Load_Mission_File_Select.JPG) -### 2.4.2.7. Check that your mission .lua script file has been correctly added to your mission. +### 6.2.7. Check that your mission .lua script file has been correctly added to your mission. ![LDT_Project](Installation/DCS_Triggers_Load_Mission_File_Added.JPG) -## 2.4.3) Maintain your .miz files +## 6.3) Maintain your .miz files IMPORTANT NOTE: When a new version of MOOSE is released, you'll have to UPDATE the Moose.lua file in EACH OF YOUR MISSION. This can be a tedious task, and for this purpose, a tool has been developed that will update the Moose.lua files automatically within your missions. -### 2.4.3.1. Select the **Update SELECTED Mission** from the External Tools in LDT. +### 6.3.1. Select the **Update SELECTED Mission** from the External Tools in LDT. This will activate a script that will automatically re-insert your mission .lua file into your mission. ![LDT_Project](Installation/DCS_Triggers_Load_Mission_File_Added.JPG) -## 2.4.4) Create folder links into your "My Missions" folder in Saved Games/DCS/Missions. + +## 6.4) Create folder links into your "My Missions" folder in Saved Games/DCS/Missions. ***TODO : Detail how hard links work, explain how they help the wworkflow*** @@ -274,13 +326,7 @@ This trick will save you a lot of time. You need to install the tool ... to crea Select from the following possible links that can be created to save you time while browing through the different folders to include script files: -### 2.4.4.1. Create a link to your **Moose Mission Setup** folder ... +### 6.4.1. Create a link to your **Moose Mission Setup** folder ... -### 2.4.4.2. Create a link to your **missions** folder ... - -# 4) Demonstration Missions - -The framework comes with demonstration missions which can be downloaded [here](https://github.com/FlightControl-Master/MOOSE_PRESENTATIONS/releases), that you can try out and helps you to code. -These missions provide examples of defined use cases how the MOOSE framework can be utilized. Each test mission is located in a separate directory, which contains at least one .lua file and .miz file. The .lua file contains the mission script file that shows how the use case was implemented. You can copy/paste code the code snippets from this .lua file into your missions, as it will accellerate your mission developments. You will learn, see, and understand how the different MOOSE classes need to be applied, and how you can create more complex mission scenarios by combining these MOOSE classes into a complex but powerful mission engine. -Some of these exact test missions are also demonstrated in a video format on the [YouTube channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg). +### 6.4.2. Create a link to your **missions** folder ... diff --git a/docs/_config.yml b/docs/_config.yml index 3397c9a49..de516ee15 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1 +1,2 @@ -theme: jekyll-theme-architect \ No newline at end of file +theme: jekyll-theme-architect +google_analytics: UA-97385487-1 \ No newline at end of file
    AI_A2A +

    AI -- AI A2A Air Patrolling or Staging.

    + +
    + +

    Author: Sven Van de Velde (FlightControl)

    +

    Contributions:

    + +
      +
    • Dutch_Baron: Working together with James has resulted in the creation of the AI_BALANCER class.
    • +
    +
    AI_A2A_Cap +

    AI -- Execute Combat Air Patrol (CAP).

    + +

    Banner Image

    + +
    + +

    AI CAP classes makes AI Controllables execute a Combat Air Patrol.

    +
    AI_A2A_Dispatcher +

    AI - The AIA2ADISPATCHER creates an automatic A2A defense system based on an EWR network targets and coordinating CAP and GCI.

    +
    AI_A2A_GCI +

    AI -- Execute Ground Controlled Interception (GCI).

    + +

    Banner Image

    + +
    + +

    AI A2A_INTEREPT class makes AI Groups execute an Intercept.

    +
    AI_A2A_Patrol +

    AI -- Air Patrolling or Staging.

    + +

    Banner Image

    + +
    + +

    AI PATROL classes makes AI Controllables execute an Patrol.

    +
    AI_Bai +

    AI -- Provide Battlefield Air Interdiction (bombing).

    + +

    Banner Image

    + +
    + +

    AI_BAI classes makes AI Controllables execute bombing tasks.

    +
    AI_Balancer -

    Single-Player:No / Multi-Player:Yes / AI:Yes / Human:No / Types:All -- AI Balancing will replace in multi player missions +

    AI -- AI Balancing will replace in multi player missions non-occupied human slots with AI groups, in order to provide an engaging simulation environment, even when there are hardly any players in the mission.

    @@ -84,16 +185,34 @@ even when there are hardly any players in the mission.


    -

    1) AIBalancer#AIBALANCER class, extends Fsm#FSM_SET

    +

    Demo Missions

    -

    The AIBalancer#AIBALANCER class monitors and manages as many replacement AI groups as there are -CLIENTS in a SET_CLIENT collection, which are not occupied by human players.

    +

    AI_BALANCER Demo Missions source code

    + +

    AI_BALANCER Demo Missions, only for beta testers

    + +

    ALL Demo Missions pack of the last release

    + +
    + +

    YouTube Channel

    + +

    AI_BALANCER YouTube Channel

    + +
    + +

    Author: Sven Van de Velde (FlightControl)

    +

    Contributions:

    + +
      +
    • Dutch_Baron: Working together with James has resulted in the creation of the AI_BALANCER class.
    • +
    AI_Cap -

    AI - Execute Combat Air Patrol (CAP).

    +

    AI -- Execute Combat Air Patrol (CAP).

    Banner Image

    @@ -112,6 +231,12 @@ CLIENTS in a SET_CLIENT collection, which are not occupied by human players.


    AI CAS classes makes AI Controllables execute a Close Air Support.

    +
    AI_Formation +

    AI -- Build large formations of AI Groups flying together.

    Airbase -

    This module contains the AIRBASE classes.

    +

    Wrapper -- AIRBASE is a wrapper class to handle the DCS Airbase objects.

    AirbasePolice -

    This module contains the AIRBASEPOLICE classes.

    +

    Functional -- This module monitors airbases traffic.

    Base -

    Core - BASE forms the basis of the MOOSE framework.

    +

    Core -- BASE forms the basis of the MOOSE framework.

    Cargo -

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:Ground --
    -Management of logical cargo objects, that can be transported from and to transportation carriers.

    - -

    Banner Image

    - -
    - -

    Cargo can be of various forms, always are composed out of ONE object ( one unit or one static or one slingload crate ):

    - -
      -
    • AICARGOUNIT, represented by a Unit in a Group: Cargo can be represented by a Unit in a Group.
    • -
    +

    Core -- Management of CARGO logistics, that can be transported from and to transportation carriers.

    CleanUp -

    The CLEANUP class keeps an area clean of crashing or colliding airplanes.

    +

    Functional -- The CLEANUP_AIRBASE class keeps an area clean of crashing or colliding airplanes.

    Client -

    This module contains the CLIENT class.

    +

    Wrapper -- CLIENT wraps DCS Unit objects acting as a Client or Player within a mission.

    CommandCenter -

    A COMMANDCENTER is the owner of multiple missions within MOOSE.

    +

    Tasking -- A COMMANDCENTER is the owner of multiple missions within MOOSE.

    Controllable -

    This module contains the CONTROLLABLE class.

    +

    Wrapper -- CONTROLLABLE is an intermediate class wrapping Group and Unit classes "controllers".

    +
    DCSAirbase + +
    DCSCoalitionObject + +
    DCSCommand + +
    DCSController + +
    DCSGroup + +
    DCSObject + +
    DCSTask + +
    DCSTypes + +
    DCSUnit + +
    DCSVec3 + +
    DCSWorld + +
    DCSZone + +
    DCScountry + +
    DCStimer + +
    DCStrigger +
    Database -

    This module contains the DATABASE class, managing the database of mission objects.

    +

    Core -- DATABASE manages the database of mission objects.

    +
    Designate +

    Functional -- Management of target Designation.

    Detection -

    Functional - DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods.

    +

    Functional -- DETECTION_ classes model the detection of enemy units by FACs or RECCEs and group them according various methods.

    Escort -

    Taking the lead of AI escorting your flight.

    +

    Functional -- Taking the lead of AI escorting your flight.

    Event -

    Core - EVENT models DCS event dispatching using a publish-subscribe model.

    +

    Core -- EVENT models DCS event dispatching using a publish-subscribe model.

    Fsm -

    Core - The FSM (Finite State Machine) class and derived FSM_ classes +

    Core -- The FSM (Finite State Machine) class and derived FSM_ classes are design patterns allowing efficient (long-lasting) processes and workflows.

    Group -

    Wrapper -- GROUP is a wrapper class for the DCS Class Group.

    +

    Wrapper -- GROUP wraps the DCS Class Group objects.

    Identifiable -

    This module contains the IDENTIFIABLE class.

    +

    Wrapper -- IDENTIFIABLE is an intermediate class wrapping DCS Object class derived Objects.

    Message -

    Core - MESSAGE class takes are of the real-time notifications and messages to players during a simulation.

    +

    Core -- MESSAGE class takes are of the real-time notifications and messages to players during a simulation.

    MissileTrainer -

    This module contains the MISSILETRAINER class.

    +

    Functional -- MISSILETRAINER helps you to train missile avoidance.

    Mission -

    A MISSION is the main owner of a Mission orchestration within MOOSE .

    +

    Tasking -- A MISSION is the main owner of a Mission orchestration within MOOSE.

    Movement -

    Limit the simultaneous movement of Groups within a running Mission.

    +

    Functional -- Limit the MOVEMENT of simulaneous moving ground vehicles.

    Object -

    This module contains the OBJECT class.

    +

    Wrapper -- OBJECT wraps the DCS Object derived objects.

    Point -

    Core - POINT_VEC classes define an extensive API to manage 3D points in the simulation space.

    +

    Core -- POINT_VEC classes define an extensive API to manage 3D points in the simulation space.

    Positionable -

    This module contains the POSITIONABLE class.

    +

    Wrapper -- POSITIONABLE wraps DCS classes that are "positionable".

    Radio -

    Core - The RADIO class is responsible for transmitting radio communications.

    +

    Core -- The RADIO Module is responsible for everything that is related to radio transmission and you can hear in DCS, be it TACAN beacons, Radio transmissions...

    Scenery -

    This module contains the SCENERY class.

    +

    Wrapper -- SCENERY models scenery within the DCS simulator.

    ScheduleDispatcher -

    This module defines the SCHEDULEDISPATCHER class, which is used by a central object called _SCHEDULEDISPATCHER.

    +

    Core -- SCHEDULEDISPATCHER dispatches the different schedules.

    Scheduler -

    Core - SCHEDULER prepares and handles the execution of functions over scheduled time (intervals).

    +

    Core -- SCHEDULER prepares and handles the execution of functions over scheduled time (intervals).

    Scoring -

    Single-Player:Yes / Multi-Player:Yes / Core:Yes -- Administer the scoring of player achievements, +

    Functional -- Administer the SCORING of player achievements, and create a CSV file logging the scoring events for use at team or squadron websites.

    Banner Image

    @@ -353,13 +563,19 @@ and creates a CSV file logging the scoring events and results for use at team or
    Sead -

    Provides defensive behaviour to a set of SAM sites within a running Mission.

    +

    Functional -- Provides defensive behaviour to a set of SAM sites within a running Mission.

    Set -

    Core - SET_ classes define collections of objects to perform bulk actions and logically group objects.

    +

    Core -- SET_ classes define collections of objects to perform bulk actions and logically group objects.

    +
    Settings +

    Core -- SETTINGS classe defines the format settings management for measurement.

    Spawn -

    Single-Player:Yes / Multi-Player:Yes / AI:Yes / Human:No / Types:All --
    -Spawn groups of units dynamically in your missions.

    - -

    Banner Image

    - -
    - -

    1) #SPAWN class, extends Base#BASE

    - -

    The #SPAWN class allows to spawn dynamically new groups, based on pre-defined initialization settings, modifying the behaviour when groups are spawned.

    +

    Functional -- Spawn dynamically new GROUPs in your missions.

    +
    SpawnStatic +

    Core -- Spawn dynamically new STATICs in your missions.

    +
    Spot +

    Core -- Management of SPOT logistics, that can be transported from and to transportation carriers.

    Static -

    This module contains the STATIC class.

    +

    Wrapper -- STATIC wraps the DCS StaticObject class.

    +
    StaticObject +
    Task -

    This module contains the TASK class.

    +

    Tasking -- This module contains the TASK class, the main engine to run human taskings.

    +
    Task_A2A +

    Tasking - The TASK_A2A models tasks for players in Air to Air engagements.

    +
    Task_A2A_Dispatcher +

    Tasking - The TASKA2ADISPATCHER creates and manages player TASK_A2A tasks based on detected targets.

    Task_A2G_Dispatcher

    Tasking - The TASKA2GDISPATCHER creates and manages player TASK_A2G tasks based on detected targets.

    +
    Task_Cargo +

    Tasking -- The TASK_CARGO models tasks for players to transport Cargo.

    Zone -

    Core - ZONE classes define zones within your mission of various forms, with various capabilities.

    +

    Core -- ZONE classes define zones within your mission of various forms, with various capabilities.

    +
    env + +
    land +