From 52c2401d93518272f1780754aa808d85fdc4043c Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 22 Nov 2023 22:53:54 +0100 Subject: [PATCH 01/95] Update SRS.lua --- Moose Development/Moose/Sound/SRS.lua | 70 +++++++++++++++------------ 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 537825b1b..196729143 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -5,7 +5,7 @@ -- **Main Features:** -- -- * Play sound files via SRS --- * Play text-to-speach via SRS +-- * Play text-to-speech via SRS -- -- === -- @@ -65,6 +65,16 @@ -- -- This script needs SRS version >= 1.9.6. -- +-- ## Knwon Issues +-- +-- ### Pop-up Window +-- +-- The text-to-speech conversion of SRS is done via an external exe file. When this file is called, a windows `cmd` window is briefly opended. That puts DCS out of focus, which is annoying, +-- expecially in VR but unavoidable (if you have a solution, please feel free to share!). +-- +-- NOTE that this is not an issue if the mission is running on a server. +-- Also NOTE that using DCS-gRPC as backend will avoid the pop-up window. +-- -- # Play Sound Files -- -- local soundfile=SOUNDFILE:New("My Soundfile.ogg", "D:\\Sounds For DCS") @@ -188,7 +198,7 @@ MSRS = { gender = "female", culture = nil, voice = nil, - volume = 1, + volume = 1, speed = 1, coordinate = nil, Label = "ROBOT", @@ -201,7 +211,7 @@ MSRS = { --- MSRS class version. -- @field #string version -MSRS.version="0.1.3" +MSRS.version="0.2.0" --- Voices -- @type MSRS.Voices @@ -304,7 +314,20 @@ MSRS.Voices = { }, } ---- +--- Text-to-speech providers. These are compatible with the DCS-gRPC conventions. +-- @type MSRS.Provider +-- @field #string WINDOWS Microsoft windows (`win`). +-- @field #string GOOGLE Google (`gcloud`). +-- @field #string AZURE Microsoft Azure (`azure`). +-- @field #string AMAZON Amazon Web Service (`asw`). +MSRS.Provider = { + WINDOWS = "win", + GOOGLE = "gcloud", + AZURE = "azure", + AMAZON = "asw", +} + +--- Provider options. -- @type MSRS.ProviderOptions -- @field #string key -- @field #string secret @@ -312,7 +335,7 @@ MSRS.Voices = { -- @field #string defaultVoice -- @field #string voice ---- GRPC options +--- GRPC options. -- @type MSRS.GRPCOptions -- @field #string plaintext -- @field #string srsClientName @@ -339,6 +362,8 @@ MSRS.GRPCOptions.DefaultProvider = "win" -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: Refactoring of input/config file. +-- TODO: Refactoring gRPC backend. -- TODO: Add functions to remove freqs and modulations. -- DONE: Add coordinate. -- DONE: Add google. @@ -354,7 +379,7 @@ MSRS.GRPCOptions.DefaultProvider = "win" -- @param #string PathToSRS Path to the directory, where SRS is located. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies. -- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations. --- @param #number Volume Volume - 1.0 is max, 0.0 is silence +-- @param #number Volume Volume - 1.0 is max, 0.0 is silence. -- @param #table AltBackend Optional table containing tables 'Functions' and 'Vars' which add/replace functions and variables for the MSRS instance to allow alternate backends for transmitting to SRS. -- @return #MSRS self function MSRS:New(PathToSRS, Frequency, Modulation, Volume, AltBackend) @@ -534,13 +559,8 @@ end -- @param #table Frequencies Frequencies in MHz. Can also be given as a #number if only one frequency should be used. -- @return #MSRS self function MSRS:SetFrequencies(Frequencies) - - -- Ensure table. - if type(Frequencies)~="table" then - Frequencies={Frequencies} - end - self.frequencies=Frequencies + self.frequencies=UTILS.EnsureTable(Frequencies, false) return self end @@ -551,12 +571,8 @@ end -- @return #MSRS self function MSRS:AddFrequencies(Frequencies) - -- Ensure table. - if type(Frequencies)~="table" then - Frequencies={Frequencies} - end - - for _,_freq in pairs(Frequencies) do + for _,_freq in pairs(UTILS.EnsureTable(Frequencies, false)) do + self:T(self.lid..string.format("Adding frequency %s", tostring(_freq))) table.insert(self.frequencies,_freq) end @@ -576,13 +592,12 @@ end -- @param #table Modulations Modulations. Can also be given as a #number if only one modulation should be used. -- @return #MSRS self function MSRS:SetModulations(Modulations) - - -- Ensure table. - if type(Modulations)~="table" then - Modulations={Modulations} - end - self.modulations=Modulations + self.modulations=UTILS.EnsureTable(Modulations, false) + + -- Debug info. + self:T(self.lid.."Modulations:") + self:T(self.modulations) return self end @@ -592,13 +607,8 @@ end -- @param #table Modulations Modulations. Can also be given as a #number if only one modulation should be used. -- @return #MSRS self function MSRS:AddModulations(Modulations) - - -- Ensure table. - if type(Modulations)~="table" then - Modulations={Modulations} - end - for _,_mod in pairs(Modulations) do + for _,_mod in pairs(UTILS.EnsureTable(Modulations, false)) do table.insert(self.modulations,_mod) end From af3c579a03a4f75e74d84ec344a17d28af5bb0c7 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 23 Nov 2023 22:22:59 +0100 Subject: [PATCH 02/95] Update SRS.lua --- Moose Development/Moose/Sound/SRS.lua | 346 +++++++++++++++++++------- 1 file changed, 258 insertions(+), 88 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 196729143..dba599830 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -3,7 +3,8 @@ -- === -- -- **Main Features:** --- +-- +-- * Incease immersion of your missions with more sound output -- * Play sound files via SRS -- * Play text-to-speech via SRS -- @@ -113,7 +114,7 @@ -- **Pro-Tipp** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing. -- and also the Google Console error, in case you have missed a step in setting up your Google TTS. -- E.g. `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"` --- Plays a message on 255AM for the blue coalition in-game. +-- Plays a message on 255 MHz AM for the blue coalition in-game. -- -- ## Set Voice -- @@ -314,6 +315,16 @@ MSRS.Voices = { }, } + +--- Backend options to communicate with SRS. +-- @type MSRS.Backend +-- @field #string SRSEXE Use SRS exe. +-- @field #string GRPC Use DCS-gRPC. +MSRS.Backend = { + SRSEXE = "srsexe", + GRPC = "grpc", +} + --- Text-to-speech providers. These are compatible with the DCS-gRPC conventions. -- @type MSRS.Provider -- @field #string WINDOWS Microsoft windows (`win`). @@ -374,7 +385,10 @@ MSRS.GRPCOptions.DefaultProvider = "win" -- Constructor ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Create a new MSRS object. +--- Create a new MSRS object. Required argument is the frequency and modulation. +-- Other parameters are read from the `Moose_MSRS.lua` config file. If you do not have that file set up you must set up and use the SRS-TTS.exe (not DCS-gRPC) as backend, you need to still +-- set the path to the exe file via @{#MSRS.SetPath}. +-- -- @param #MSRS self -- @param #string PathToSRS Path to the directory, where SRS is located. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies. @@ -382,14 +396,16 @@ MSRS.GRPCOptions.DefaultProvider = "win" -- @param #number Volume Volume - 1.0 is max, 0.0 is silence. -- @param #table AltBackend Optional table containing tables 'Functions' and 'Vars' which add/replace functions and variables for the MSRS instance to allow alternate backends for transmitting to SRS. -- @return #MSRS self -function MSRS:New(PathToSRS, Frequency, Modulation, Volume, AltBackend) +function MSRS:New(Frequency, Modulation, Volume, AltBackend) -- Defaults. Frequency = Frequency or 143 Modulation = Modulation or radio.modulation.AM - -- Inherit everything from FSM class. + -- Inherit everything from BASE class. local self=BASE:Inherit(self, BASE:New()) -- #MSRS + + self.lid = string.format("%s-%s | ", "unknown", self.version) -- If AltBackend is supplied, initialize it, which will add/replace functions and variables in this MSRS instance. if type( AltBackend ) == "table" or type( self.AltBackend ) == "table" then @@ -452,6 +468,22 @@ end -- User Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Set backend to communicate with SRS. +-- There are two options: +-- +-- - `MSRS.Backend.SRSEXE`: This is the default and uses the SRS.exe. +-- - `MSRS.Backend.GRPC`: Via DCS-gRPC. +-- +-- @param #MSRS self +-- @param #string Backend Backend used. Default is `MSRS.Backend.SRSEXE`. +-- @return #MSRS self +function MSRS:SetBackend(Backend) + + self.backend=Backend or MSRS.Backend.SRSEXE + + return self +end + --- Set path to SRS install directory. More precisely, path to where the DCS- -- @param #MSRS self -- @param #string Path Path to the directory, where the sound file is located. This does **not** contain a final backslash or slash. @@ -649,7 +681,7 @@ function MSRS:SetCulture(Culture) return self end ---- Set to use a specific voice. Will override gender and culture settings. +--- Set to use a specific voice. Will override any gender and culture settings. -- @param #MSRS self -- @param #string Voice Voice. -- @return #MSRS self @@ -993,12 +1025,105 @@ function MSRS:_NewAltBackend(Backend) return self end + +--- Get lat, long and alt from coordinate. +-- @param #MSRS self +-- @param Core.Point#Coordinate Coordinate Coordinate. Can also be a DCS#Vec3. +-- @return #number Latitude. +-- @return #number Longitude. +-- @return #number Altitude. +function MSRS:_GetLatLongAlt(Coordinate) + + local lat, lon, alt=coord.LOtoLL(Coordinate) + + return lat, lon, math.floor(alt) +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Backend ExternalAudio.exe +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get SRS command to play sound using the `DCS-SR-ExternalAudio.exe`. +-- @param #MSRS self +-- @param #table freqs Frequencies in MHz. +-- @param #table modus Modulations. +-- @param #number coal Coalition. +-- @param #string gender Gender. +-- @param #string voice Voice. +-- @param #string culture Culture. +-- @param #number volume Volume. +-- @param #number speed Speed. +-- @param #number port Port. +-- @param #string label Label, defaults to "ROBOT" (displayed sender name in the radio overlay of SRS) - No spaces allowed! +-- @param Core.Point#COORDINATE coordinate Coordinate. +-- @return #string Command. +function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port, label, coordinate) + + local path=self:GetPath() or STTS.DIRECTORY + + local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe" + + freqs=table.concat(freqs or self.frequencies, ",") + modus=table.concat(modus or self.modulations, ",") + + coal=coal or self.coalition + gender=gender or self.gender + voice=voice or self.voice + culture=culture or self.culture + volume=volume or self.volume + speed=speed or self.speed + port=port or self.port + label=label or self.Label + coordinate=coordinate or self.coordinate + + -- Replace modulation + modus=modus:gsub("0", "AM") + modus=modus:gsub("1", "FM") + + -- Command. + local command=string.format('"%s\\%s" -f "%s" -m "%s" -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume) + + -- Set voice or gender/culture. + if voice then + -- Use a specific voice (no need for gender and/or culture. + command=command..string.format(" --voice=\"%s\"", tostring(voice)) + else + -- Add gender. + if gender and gender~="female" then + command=command..string.format(" -g %s", tostring(gender)) + end + -- Add culture. + if culture and culture~="en-GB" then + command=command..string.format(" -l %s", tostring(culture)) + end + end + + -- Set coordinate. + if coordinate then + local lat,lon,alt=self:_GetLatLongAlt(coordinate) + command=command..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt) + end + + -- Set google. + if self.google and self.ttsprovider == "Google" then + command=command..string.format(' --ssml -G "%s"', self.google) + end + + -- Debug output. + self:T("MSRS command="..command) + + return command +end + --- Execute SRS command to play sound using the `DCS-SR-ExternalAudio.exe`. -- @param #MSRS self -- @param #string command Command to executer -- @return #number Return value of os.execute() command. function MSRS:_ExecCommand(command) + + -- Debug info. self:T("SRS TTS command="..command) + -- Create a tmp file. local filename=os.getenv('TMP').."\\MSRS-"..STTS.uuid()..".bat" @@ -1064,7 +1189,7 @@ function MSRS:_ExecCommand(command) res=os.execute(command) -- Remove file in 1 second. - timer.scheduleFunction(os.remove, filename, timer.getTime()+1) + timer.scheduleFunction(os.remove, filename, timer.getTime()+1) end @@ -1072,88 +1197,133 @@ function MSRS:_ExecCommand(command) return res end ---- Get lat, long and alt from coordinate. +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- DCS-gRPC Backend Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- DCS-gRPC v0.70 TTS API call: +-- GRPC.tts(ssml, frequency[, options]) - Synthesize text (ssml; SSML tags supported) to speech and transmit it over SRS on the frequency with the following optional options (and their defaults): + +-- { +-- -- The plain text without any transformations made to it for the purpose of getting it spoken out +-- -- as desired (no SSML tags, no FOUR NINER instead of 49, ...). Even though this field is +-- -- optional, please consider providing it as it can be used to display the spoken text to players +-- -- with hearing impairments. +-- plaintext = null, -- e.g. `= "Hello Pilot"` + +-- -- Name of the SRS client. +-- srsClientName = "DCS-gRPC", + +-- -- The origin of the transmission. Relevant if the SRS server has "Line of +-- -- Sight" and/or "Distance Limit" enabled. +-- position = { +-- lat = 0.0, +-- lon = 0.0, +-- alt = 0.0, -- in meters +-- }, + +-- -- The coalition of the transmission. Relevant if the SRS server has "Secure +-- -- Coalition Radios" enabled. Supported values are: `blue` and `red`. Defaults +-- -- to being spectator if not specified. +-- coalition = null, + +-- -- TTS provider to be use. Defaults to the one configured in your config or to Windows' +-- -- built-in TTS. Examples: +-- -- `= { aws = {} }` / `= { aws = { voice = "..." } }` enable AWS TTS +-- -- `= { azure = {} }` / `= { azure = { voice = "..." } }` enable Azure TTS +-- -- `= { gcloud = {} }` / `= { gcloud = { voice = "..." } }` enable Google Cloud TTS +-- -- `= { win = {} }` / `= { win = { voice = "..." } }` enable Windows TTS +-- provider = null, +-- } + +--- Make DCS-gRPC API call to transmit text-to-speech over SRS. -- @param #MSRS self --- @param Core.Point#Coordinate Coordinate Coordinate. Can also be a DCS#Vec3. --- @return #number Latitude. --- @return #number Longitude. --- @return #number Altitude. -function MSRS:_GetLatLongAlt(Coordinate) - - local lat, lon, alt=coord.LOtoLL(Coordinate) - - return lat, lon, math.floor(alt) +-- @param #string Text Text of message to transmit (can also be SSML). +-- @param #string Optional plaintext version of message (for accessiblity). +-- @param #table Frequencies Radio frequencies to transmit on. Can also accept a number in MHz. +-- @param #string Voice Voice for the TTS provider to user. +-- @param #string Label Label (SRS diplays as name of the transmitter). +-- @return #MSRS self +function MSRS:_DCSgRPCtts(Text, Plaintext, Frequencies, Voice, Label) + + BASE:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") + BASE:T({Text, Plaintext, Frequencies, Voice, Label}) + + local options = self.ProviderOptions or MSRS.ProviderOptions or {} -- #MSRS.GRPCOptions + local ssml = Text or '' + + local XmitFrequencies = Frequencies or self.Frequency + if type(XmitFrequencies)~="table" then + XmitFrequencies={XmitFrequencies} + end + + options.plaintext = Plaintext + options.srsClientName = Label or self.Label + options.position = {} + if self.coordinate then + options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) + end + + options.position.lat = options.position.lat or 0.0 + options.position.lon = options.position.lon or 0.0 + options.position.alt = options.position.alt or 0.0 + + if UTILS.GetCoalitionName(self.coalition) == 'Blue' then + options.coalition = 'blue' + elseif UTILS.GetCoalitionName(self.coalition) == 'Red' then + options.coalition = 'red' + end + + local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider + + options.provider = {} + + options.provider[provider] = {} + + if self.APIKey then + options.provider[provider].key = self.APIKey + end + + if self.defaultVoice then + options.provider[provider].defaultVoice = self.defaultVoice + end + + if self.voice then + options.provider[provider].voice = Voice or self.voice or self.defaultVoice + elseif ssml then + -- DCS-gRPC doesn't directly support language/gender, but can use SSML + -- Only use if a voice isn't explicitly set + local preTag, genderProp, langProp, postTag = '', '', '', '' + + if self.gender then + genderProp = ' gender=\"' .. self.gender .. '\"' + end + if self.culture then + langProp = ' language=\"' .. self.culture .. '\"' + end + + if self.culture or self.gender then + preTag = '' + postTag = '' + ssml = preTag .. Text .. postTag + end + + end + + for _,_freq in ipairs(XmitFrequencies) do + local freq = _freq*1000000 + BASE:T("GRPC.tts") + BASE:T(ssml) + BASE:T(freq) + BASE:T({options}) + GRPC.tts(ssml, freq, options) + end + end - ---- Get SRS command to play sound using the `DCS-SR-ExternalAudio.exe`. --- @param #MSRS self --- @param #table freqs Frequencies in MHz. --- @param #table modus Modulations. --- @param #number coal Coalition. --- @param #string gender Gender. --- @param #string voice Voice. --- @param #string culture Culture. --- @param #number volume Volume. --- @param #number speed Speed. --- @param #number port Port. --- @param #string label Label, defaults to "ROBOT" (displayed sender name in the radio overlay of SRS) - No spaces allowed! --- @param Core.Point#COORDINATE coordinate Coordinate. --- @return #string Command. -function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port,label,coordinate) - - local path=self:GetPath() or STTS.DIRECTORY - local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe" - freqs=table.concat(freqs or self.frequencies, ",") - modus=table.concat(modus or self.modulations, ",") - coal=coal or self.coalition - gender=gender or self.gender - voice=voice or self.voice - culture=culture or self.culture - volume=volume or self.volume - speed=speed or self.speed - port=port or self.port - label=label or self.Label - coordinate=coordinate or self.coordinate - - -- Replace modulation - modus=modus:gsub("0", "AM") - modus=modus:gsub("1", "FM") - - -- Command. - local command=string.format('"%s\\%s" -f "%s" -m "%s" -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume) - - -- Set voice or gender/culture. - if voice then - -- Use a specific voice (no need for gender and/or culture. - command=command..string.format(" --voice=\"%s\"", tostring(voice)) - else - -- Add gender. - if gender and gender~="female" then - command=command..string.format(" -g %s", tostring(gender)) - end - -- Add culture. - if culture and culture~="en-GB" then - command=command..string.format(" -l %s", tostring(culture)) - end - end - - -- Set coordinate. - if coordinate then - local lat,lon,alt=self:_GetLatLongAlt(coordinate) - command=command..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt) - end - - -- Set google. - if self.google and self.ttsprovider == "Google" then - command=command..string.format(' --ssml -G "%s"', self.google) - end - - -- Debug output. - self:T("MSRS command="..command) - - return command -end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Config File +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Get central SRS configuration to be able to play tts over SRS radio using the `DCS-SR-ExternalAudio.exe`. -- @param #MSRS self @@ -1208,7 +1378,7 @@ end -- This will populate variables for the MSRS raw class and all instances you create with e.g. `mysrs = MSRS:New()` -- Optionally you can also load this per **single instance** if so needed, i.e. -- --- mysrs:LoadConfigFile(Path,Filename) +-- mysrs:LoadConfigFile(Path,Filename) -- -- 4) Use the config in your code like so, variable names are basically the same as in the config file, but all lower case, examples: -- @@ -1225,7 +1395,7 @@ end -- atis:SetSRS(nil,nil,nil,MSRS.Voices.Google.Standard.en_US_Standard_H) -- --Start ATIS -- atis:Start() -function MSRS:LoadConfigFile(Path,Filename) +function MSRS:LoadConfigFile(Path, Filename) local path = Path or lfs.writedir()..MSRS.ConfigFilePath local file = Filename or MSRS.ConfigFileName or "Moose_MSRS.lua" From 1b1f8e0d2c2138fb3082e55ffb358927ed1e4244 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 24 Nov 2023 23:59:31 +0100 Subject: [PATCH 03/95] Update SRS.lua --- Moose Development/Moose/Sound/SRS.lua | 371 ++++++++++++++++---------- 1 file changed, 234 insertions(+), 137 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 4e762d8b2..f9ff9e0bf 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -65,6 +65,7 @@ -- ## Prerequisites -- -- This script needs SRS version >= 1.9.6. +-- Optional: DCS-gRPC as backend to communicate with SRS (vide infra) -- -- ## Knwon Issues -- @@ -130,6 +131,7 @@ -- ## Set Coordinate -- -- Use @{#MSRS.SetCoordinate} to define the origin from where the transmission is broadcasted. +-- Note that this is only a factor if SRS server has line-of-sight and/or distance limit enabled. -- -- ## Set SRS Port -- @@ -193,6 +195,7 @@ MSRS = { lid = nil, port = 5002, name = "MSRS", + backend = "srsexe", frequencies = {}, modulations = {}, coalition = 0, @@ -202,12 +205,12 @@ MSRS = { volume = 1, speed = 1, coordinate = nil, + provider = "win", Label = "ROBOT", - AltBackend = nil, ConfigFileName = "Moose_MSRS.lua", ConfigFilePath = "Config\\", ConfigLoaded = false, - ttsprovider = "Microsoft", + poptions = {}, } --- MSRS class version. @@ -329,8 +332,8 @@ MSRS.Backend = { -- @type MSRS.Provider -- @field #string WINDOWS Microsoft windows (`win`). -- @field #string GOOGLE Google (`gcloud`). --- @field #string AZURE Microsoft Azure (`azure`). --- @field #string AMAZON Amazon Web Service (`asw`). +-- @field #string AZURE Microsoft Azure (`azure`). Only possible with DCS-gRPC backend. +-- @field #string AMAZON Amazon Web Service (`asw`). Only possible with DCS-gRPC backend. MSRS.Provider = { WINDOWS = "win", GOOGLE = "gcloud", @@ -340,10 +343,10 @@ MSRS.Provider = { --- Provider options. -- @type MSRS.ProviderOptions --- @field #string key --- @field #string secret --- @field #string region --- @field #string defaultVoice +-- @field #string key Access key (DCS-gRPC with Google, ASW, AZURE as provider). +-- @field #string secret Secret key (DCS-gRPC with ASW as provider) +-- @field #string region Region. +-- @field #string defaultVoice Default voice. -- @field #string voice --- GRPC options. @@ -390,13 +393,11 @@ MSRS.GRPCOptions.DefaultProvider = "win" -- set the path to the exe file via @{#MSRS.SetPath}. -- -- @param #MSRS self --- @param #string PathToSRS Path to the directory, where SRS is located. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies. -- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations. --- @param #number Volume Volume - 1.0 is max, 0.0 is silence. --- @param #table AltBackend Optional table containing tables 'Functions' and 'Vars' which add/replace functions and variables for the MSRS instance to allow alternate backends for transmitting to SRS. +-- @param #string Backend Backend used: `MSRS.Backend.SRSEXE` (default) or `MSRS.Backend.GRPC`. -- @return #MSRS self -function MSRS:New(Frequency, Modulation, Volume, AltBackend) +function MSRS:New(Frequency, Modulation, Backend) -- Defaults. Frequency = Frequency or 143 @@ -407,52 +408,27 @@ function MSRS:New(Frequency, Modulation, Volume, AltBackend) self.lid = string.format("%s-%s | ", "unknown", self.version) - -- If AltBackend is supplied, initialize it, which will add/replace functions and variables in this MSRS instance. - if type( AltBackend ) == "table" or type( self.AltBackend ) == "table" then - - local Backend = UTILS.DeepCopy(AltBackend) or UTILS.DeepCopy(self.AltBackend) - - -- Add parameters to vars so alternate backends can use them if applicable - Backend.Vars = Backend.Vars or {} - Backend.Vars.PathToSRS = PathToSRS - Backend.Vars.Frequency = UTILS.DeepCopy(Frequency) - Backend.Vars.Modulation = UTILS.DeepCopy(Modulation) - Backend.Vars.Volume = Volume - - Backend.Functions = Backend.Functions or {} - - return self:_NewAltBackend(Backend) - end - if not self.ConfigLoaded then - -- If no AltBackend table, the proceed with default initialisation - self:SetPath(PathToSRS) + -- Defaults. + self:SetPath() self:SetPort() self:SetFrequencies(Frequency) self:SetModulations(Modulation) self:SetGender() self:SetCoalition() self:SetLabel() - self:SetVolume(Volume) + self:SetVolume() else -- there might be some overwrites from :New() - if PathToSRS then - self:SetPath(PathToSRS) - end - if Frequency then self:SetFrequencies(Frequency) self:SetModulations(Modulation) end - if Volume then - self:SetVolume(Volume) - end - end self.lid = string.format("%s-%s | ", self.name, self.version) @@ -484,9 +460,37 @@ function MSRS:SetBackend(Backend) return self end +--- Set DCS-gRPC as backend to communicate with SRS. +-- @param #MSRS self +-- @return #MSRS self +function MSRS:SetBackendGRPC() + + self:SetBackend(MSRS.Backend.GRPC) + + return self +end + +--- Set SRS-TTS.exe as backend to communicate with SRS. +-- @param #MSRS self +-- @return #MSRS self +function MSRS:SetBackendSRSEXE(Backend) + + self:SetBackend(MSRS.Backend.SRSEXE) + + return self +end + + +--- Get currently set backend. +-- @param #MSRS self +-- @return #string Backend. +function MSRS:GetBackend() + return self.backend +end + --- Set path to SRS install directory. More precisely, path to where the DCS- -- @param #MSRS self --- @param #string Path Path to the directory, where the sound file is located. This does **not** contain a final backslash or slash. +-- @param #string Path Path to the directory, where the sound file is located. -- @return #MSRS self function MSRS:SetPath(Path) @@ -613,7 +617,7 @@ end --- Get frequencies. -- @param #MSRS self --- @param #table Frequencies in MHz. +-- @return #table Frequencies in MHz. function MSRS:GetFrequencies() return self.frequencies end @@ -672,7 +676,7 @@ end --- Set culture. -- @param #MSRS self --- @param #string Culture Culture, e.g. "en-GB" (default). +-- @param #string Culture Culture, e.g. "en-GB". -- @return #MSRS self function MSRS:SetCulture(Culture) @@ -681,7 +685,7 @@ function MSRS:SetCulture(Culture) return self end ---- Set to use a specific voice. Will override any gender and culture settings. +--- Set to use a specific voice. Note that this will override any gender and culture settings as a voice already has a certain gender/culture. -- @param #MSRS self -- @param #string Voice Voice. -- @return #MSRS self @@ -689,10 +693,6 @@ function MSRS:SetVoice(Voice) self.voice=Voice - --local defaultprovider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider or "win" - - --self.GRPCOptions[defaultprovider].voice = Voice - return self end @@ -703,13 +703,14 @@ end function MSRS:SetDefaultVoice(Voice) self.defaultVoice=Voice + local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider or "win" self.GRPCOptions[provider].defaultVoice = Voice return self end ---- Set the coordinate from which the transmissions will be broadcasted. +--- Set the coordinate from which the transmissions will be broadcasted. Note that this is only a factor if SRS has line-of-sight or distance enabled. -- @param #MSRS self -- @param Core.Point#COORDINATE Coordinate Origin of the transmission. -- @return #MSRS self @@ -755,6 +756,77 @@ function MSRS:SetGoogleAPIKey(APIKey) return self end + +--- Set provider used to generate text-to-speech. +-- These options are available: +-- +-- - `MSRS.Provider.WINDOWS`: Microsoft Windows (default) +-- - `MSRS.Provider.GOOGLE`: Google Cloud +-- - `MSRS.Provider.AZURE`: Microsoft Azure (only with DCS-gRPC backend) +-- - `MSRS.Provier.AMAZON`: Amazone Web Service (only with DCS-gRPC backend) +-- +-- Note that all providers except Microsoft Windows need as additonal information the credentials of your account. +-- +-- @param #MSRS self +-- @param #string Provider +-- @return #MSRS self +function MSRS:SetProvider(Provider) + self.provider = Provider or MSRS.Provider.WINDOWS + return self +end + + +--- Get provider. +-- @param #MSRS self +-- @return #MSRS self +function MSRS:GetProvider() + return self.provider or MSRS.Provider.WINDOWS +end + +--- Set provider options and credentials. +-- @param #MSRS self +-- @param #string Provider Provider. +-- @param #string CredentialsFile Full path to your credentials file. For Google this is the path to a JSON file. +-- @param #string AccessKey Your API access key. +-- @param #string SecretKey Your secret key. +-- @param #string Region Region to use. +-- @return #MSRS.ProviderOptions Provider optionas table. +function MSRS:SetProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) + + local option={} --#MSRS.ProviderOptions + + option.provider=Provider + option.credentials=CredentialsFile + option.key=AccessKey + option.secret=SecretKey + option.region=Region + + self.poptions[Provider]=option + + return option +end + +--- Set provider options and credentials for Google Cloud. +-- @param #MSRS self +-- @param #string CredentialsFile Full path to your credentials file. For Google this is the path to a JSON file. This is used if SRS-TTS.exe is used as backend. +-- @param #string AccessKey Your API access key. This is necessary if DCS-gRPC is used as backend. +-- @return #MSRS self +function MSRS:SetProviderOptionsGoogle(CredentialsFile, AccessKey) + + self:SetProviderOptions(MSRS.Provider.GOOGLE, CredentialsFile, AccessKey) + + return self +end + +--- Set provider options and credentials for Google Cloud. +-- @param #MSRS self +-- @param #string Provider Provider. Default is as set via @{#MSRS.SetProvider}. +-- @return #MSRS.ProviderOptions Provider options. +function MSRS:GetProviderOptions(Provider) + return self.poptions[Provider or self.provider] +end + + --- Use Google text-to-speech as default. -- @param #MSRS self -- @return #MSRS self @@ -894,15 +966,24 @@ function MSRS:PlayText(Text, Delay, Coordinate) if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, nil, Coordinate) else + + if self.backend==MSRS.Backend.SRSEXE then - -- Get command line. - local command=self:_GetCommand(nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,Coordinate) - - -- Append text. - command=command..string.format(" --text=\"%s\"", tostring(Text)) - - -- Execute command. - self:_ExecCommand(command) + -- Get command line. + local command=self:_GetCommand(nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,Coordinate) + + -- Append text. + command=command..string.format(" --text=\"%s\"", tostring(Text)) + + -- Execute command. + self:_ExecCommand(command) + + else + + self:T(self.lid.."Transmitting") + self:_DCSgRPCtts(Text, nil, nil , nil, nil, nil, nil, Coordinate) + + end end @@ -927,26 +1008,24 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) else + + if self.backend==MSRS.Backend.SRSEXE then - -- Ensure table. - if Frequencies and type(Frequencies)~="table" then - Frequencies={Frequencies} + -- Get command line. + local command=self:_GetCommand(UTILS.EnsureTable(Frequencies, false), UTILS.EnsureTable(Modulations, false), nil, Gender, Voice, Culture, Volume, nil, nil, Label, Coordinate) + + -- Append text. + command=command..string.format(" --text=\"%s\"", tostring(Text)) + + -- Execute command. + self:_ExecCommand(command) + + elseif self.backend==MSRS.Backend.GRPC then + + self:_DCSgRPCtts(Text,Optional,Frequencies,Voice,Label,Plaintext) + end - -- Ensure table. - if Modulations and type(Modulations)~="table" then - Modulations={Modulations} - end - - -- Get command line. - local command=self:_GetCommand(Frequencies, Modulations, nil, Gender, Voice, Culture, Volume, nil, nil, Label, Coordinate) - - -- Append text. - command=command..string.format(" --text=\"%s\"", tostring(Text)) - - -- Execute command. - self:_ExecCommand(command) - end return self @@ -1029,12 +1108,18 @@ end --- Get lat, long and alt from coordinate. -- @param #MSRS self -- @param Core.Point#Coordinate Coordinate Coordinate. Can also be a DCS#Vec3. --- @return #number Latitude. --- @return #number Longitude. --- @return #number Altitude. +-- @return #number Latitude (or 0 if no input coordinate was given). +-- @return #number Longitude (or 0 if no input coordinate was given). +-- @return #number Altitude (or 0 if no input coordinate was given). function MSRS:_GetLatLongAlt(Coordinate) - local lat, lon, alt=coord.LOtoLL(Coordinate) + local lat=0.0 + local lon=0.0 + local alt=0.0 + + if Coordinate then + lat, lon, alt=coord.LOtoLL(Coordinate) + end return lat, lon, math.floor(alt) end @@ -1239,85 +1324,92 @@ end --- Make DCS-gRPC API call to transmit text-to-speech over SRS. -- @param #MSRS self -- @param #string Text Text of message to transmit (can also be SSML). --- @param #string Optional plaintext version of message (for accessiblity). -- @param #table Frequencies Radio frequencies to transmit on. Can also accept a number in MHz. --- @param #string Voice Voice for the TTS provider to user. --- @param #string Label Label (SRS diplays as name of the transmitter). +-- @param #string Gender Gender. +-- @param #string Culture Culture. +-- @param #string Voice Voice. +-- @param #number Volume Volume. +-- @param #string Label Label. +-- @param Core.Point#COORDINATE Coordinate Coordinate. -- @return #MSRS self -function MSRS:_DCSgRPCtts(Text, Plaintext, Frequencies, Voice, Label) +function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate) - BASE:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") - BASE:T({Text, Plaintext, Frequencies, Voice, Label}) - - local options = self.ProviderOptions or MSRS.ProviderOptions or {} -- #MSRS.GRPCOptions - local ssml = Text or '' - - local XmitFrequencies = Frequencies or self.Frequency - if type(XmitFrequencies)~="table" then - XmitFrequencies={XmitFrequencies} - end - - options.plaintext = Plaintext - options.srsClientName = Label or self.Label + --Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) + + self:I("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") + self:I({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) + + --local options = self.ProviderOptions or MSRS.ProviderOptions or {} -- #MSRS.GRPCOptions + + local options = {} -- #MSRS.GRPCOptions + + local ssml = Text or '' + + Frequencies = UTILS.EnsureTable(Frequencies, true) or self:GetFrequencies() + + options.plaintext = Plaintext + options.srsClientName = Label or self.Label + + if false then + options.position = {} if self.coordinate then - options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) - end - - options.position.lat = options.position.lat or 0.0 - options.position.lon = options.position.lon or 0.0 - options.position.alt = options.position.alt or 0.0 - + options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) + end + if UTILS.GetCoalitionName(self.coalition) == 'Blue' then options.coalition = 'blue' elseif UTILS.GetCoalitionName(self.coalition) == 'Red' then options.coalition = 'red' end - + local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider - + options.provider = {} - - options.provider[provider] = {} - + --options.provider[provider] = {} + options.provider[provider] = self:GetProviderOptions(provider) + if self.APIKey then options.provider[provider].key = self.APIKey end - + if self.defaultVoice then options.provider[provider].defaultVoice = self.defaultVoice end + + end - if self.voice then - options.provider[provider].voice = Voice or self.voice or self.defaultVoice - elseif ssml then - -- DCS-gRPC doesn't directly support language/gender, but can use SSML - -- Only use if a voice isn't explicitly set - local preTag, genderProp, langProp, postTag = '', '', '', '' - - if self.gender then - genderProp = ' gender=\"' .. self.gender .. '\"' - end - if self.culture then - langProp = ' language=\"' .. self.culture .. '\"' - end - - if self.culture or self.gender then - preTag = '' - postTag = '' - ssml = preTag .. Text .. postTag - end - + if self.voice then + --options.provider[provider].voice = Voice or self.voice or self.defaultVoice + elseif ssml then + -- DCS-gRPC doesn't directly support language/gender, but can use SSML + -- Only use if a voice isn't explicitly set + local preTag, genderProp, langProp, postTag = '', '', '', '' + + if self.gender then + genderProp = ' gender=\"' .. self.gender .. '\"' end - - for _,_freq in ipairs(XmitFrequencies) do - local freq = _freq*1000000 - BASE:T("GRPC.tts") - BASE:T(ssml) - BASE:T(freq) - BASE:T({options}) - GRPC.tts(ssml, freq, options) + if self.culture then + langProp = ' language=\"' .. self.culture .. '\"' end + + if self.culture or self.gender then + preTag = '' + postTag = '' + ssml = preTag .. Text .. postTag + end + end + + env.info("FF freq") + for _,freq in pairs(Frequencies) do + env.info("FF freq1") + self:T("GRPC.tts") + self:T(ssml) + self:T(freq) + self:T({options}) + UTILS.PrintTableToLog(options) + --GRPC.tts(ssml, freq*1e6, options) + end end @@ -1778,10 +1870,15 @@ MSRS_BACKEND_DCSGRPC.Functions._DCSgRPCtts = function (self, Text, Plaintext, Fr end if self.voice then + + -- Set voice. options.provider[provider].voice = Voice or self.voice or self.defaultVoice + elseif ssml then + -- DCS-gRPC doesn't directly support language/gender, but can use SSML -- Only use if a voice isn't explicitly set + local preTag, genderProp, langProp, postTag = '', '', '', '' if self.gender then @@ -2200,7 +2297,7 @@ function MSRSQUEUE:_CheckRadioQueue(delay) end -MSRS.LoadConfigFile() +--MSRS.LoadConfigFile() ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 67924c894d4944daae8adbb23ea69bbf012dab9f Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 26 Nov 2023 16:45:19 +0100 Subject: [PATCH 04/95] Update SRS.lua - Removed altbackend functions --- Moose Development/Moose/Sound/SRS.lua | 508 ++++---------------------- 1 file changed, 73 insertions(+), 435 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index f9ff9e0bf..c85ddcd9b 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -801,6 +801,8 @@ function MSRS:SetProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey option.secret=SecretKey option.region=Region + self.poptions=self.poptions or {} + self.poptions[Provider]=option return option @@ -940,15 +942,23 @@ function MSRS:PlaySoundText(SoundText, Delay) if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlaySoundText, self, SoundText, 0) else + + if self.backend==MSRS.Backend.GRPC then + + self:_DCSgRPCtts(SoundText.text, nil, SoundText.gender, SoundText.culture, SoundText.voice, SoundText.volume, SoundText.label, SoundText.coordinate) + + else - -- Get command. - local command=self:_GetCommand(nil, nil, nil, SoundText.gender, SoundText.voice, SoundText.culture, SoundText.volume, SoundText.speed) - - -- Append text. - command=command..string.format(" --text=\"%s\"", tostring(SoundText.text)) - - -- Execute command. - self:_ExecCommand(command) + -- Get command. + local command=self:_GetCommand(nil, nil, nil, SoundText.gender, SoundText.voice, SoundText.culture, SoundText.volume, SoundText.speed) + + -- Append text. + command=command..string.format(" --text=\"%s\"", tostring(SoundText.text)) + + -- Execute command. + self:_ExecCommand(command) + + end end @@ -967,23 +977,16 @@ function MSRS:PlayText(Text, Delay, Coordinate) self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, nil, Coordinate) else - if self.backend==MSRS.Backend.SRSEXE then - - -- Get command line. - local command=self:_GetCommand(nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,Coordinate) - - -- Append text. - command=command..string.format(" --text=\"%s\"", tostring(Text)) - - -- Execute command. - self:_ExecCommand(command) - - else - + if self.backend==MSRS.Backend.GRPC then + self:T(self.lid.."Transmitting") self:_DCSgRPCtts(Text, nil, nil , nil, nil, nil, nil, Coordinate) - end + else + + self:PlayTextExt(Text,Delay, nil, nil, nil, nil, nil, nil, nil,Coordinate) + + end end @@ -1022,7 +1025,7 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture elseif self.backend==MSRS.Backend.GRPC then - self:_DCSgRPCtts(Text,Optional,Frequencies,Voice,Label,Plaintext) + self:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate) end @@ -1075,36 +1078,6 @@ end -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Adds or replaces functions and variables in the current MSRS class instance to enable an alternate backends for transmitting to SRS. --- @param #MSRS self --- @param #table A table containing a table `Functions` with new/replacement class functions and `Vars` with new/replacement variables. --- @return #MSRS self -function MSRS:_NewAltBackend(Backend) - BASE:T('Entering MSRS:_NewAltBackend()') - - -- Add/replace class instance functions with those defined in the alternate backend in Functions table - for funcName,funcDef in pairs(Backend.Functions) do - if type(funcDef) == 'function' then - BASE:T('MSRS (re-)defining function MSRS:' .. funcName ) - self[funcName] = funcDef - end - end - - -- Add/replace class instance variables with those defined in the alternate backend in Vars table - for varName,varVal in pairs(Backend.Vars) do - BASE:T('MSRS setting self.' .. varName) - self[varName] = UTILS.DeepCopy(varVal) - end - - -- If _MSRSbackendInit() is defined in the backend, then run it (it should return self) - if self._MSRSbackendInit and type(self._MSRSbackendInit) == 'function' then - return self:_MSRSbackendInit() - end - - return self -end - - --- Get lat, long and alt from coordinate. -- @param #MSRS self -- @param Core.Point#Coordinate Coordinate Coordinate. Can also be a DCS#Vec3. @@ -1345,59 +1318,62 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab local ssml = Text or '' + -- Get frequenceies. Frequencies = UTILS.EnsureTable(Frequencies, true) or self:GetFrequencies() - options.plaintext = Plaintext + -- Plain text (not really used. + options.plaintext=Text + + -- Name shows as sender. options.srsClientName = Label or self.Label - - if false then - - options.position = {} - if self.coordinate then - options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) - end - - if UTILS.GetCoalitionName(self.coalition) == 'Blue' then - options.coalition = 'blue' - elseif UTILS.GetCoalitionName(self.coalition) == 'Red' then - options.coalition = 'red' - end - - local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider - - options.provider = {} - --options.provider[provider] = {} - options.provider[provider] = self:GetProviderOptions(provider) - - if self.APIKey then - options.provider[provider].key = self.APIKey - end - - if self.defaultVoice then - options.provider[provider].defaultVoice = self.defaultVoice - end - - end - if self.voice then - --options.provider[provider].voice = Voice or self.voice or self.defaultVoice - elseif ssml then + -- Set position. + if self.coordinate then + options.position = {} + options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) + end + + -- Coalition (gRPC expects lower case) + options.coalition = UTILS.GetCoalitionName(self.coalition):lower() + + -- Provider (win, gcloud, ...) + local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider + + -- Provider options: voice, credentials + options.provider = {} + options.provider[provider] = self:GetProviderOptions(provider) + + -- Voice + Voice=Voice or self.voice + + if Voice then + -- We use a specific voice + options.provider[provider].voice = Voice + else -- DCS-gRPC doesn't directly support language/gender, but can use SSML - -- Only use if a voice isn't explicitly set + local preTag, genderProp, langProp, postTag = '', '', '', '' + local gender="" if self.gender then - genderProp = ' gender=\"' .. self.gender .. '\"' + --gender = ' gender=\"' .. self.gender .. '\"' + gender=string.format(' gender=\"\%s\"', self.gender) end + local language="" if self.culture then - langProp = ' language=\"' .. self.culture .. '\"' + --lang = ' language=\"' .. self.culture .. '\"' + language=string.format(' language=\"\%s\"', self.culture) end - if self.culture or self.gender then - preTag = '' - postTag = '' - ssml = preTag .. Text .. postTag - end +-- if self.culture or self.gender then +-- preTag = '' +-- postTag = '' +-- ssml = preTag .. Text .. postTag +-- end + + if self.gender or self.culture then + ssml=string.format("%s", gender, language, Text) + end end env.info("FF freq") @@ -1406,9 +1382,10 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab self:T("GRPC.tts") self:T(ssml) self:T(freq) - self:T({options}) + self:T(options) UTILS.PrintTableToLog(options) - --GRPC.tts(ssml, freq*1e6, options) + env.info(UTILS.OneLineSerialize(options)) + GRPC.tts(ssml, freq*1e6, options) end end @@ -1568,345 +1545,6 @@ function MSRS:LoadConfigFile(Path,Filename) return true end -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- MSRS DCS-gRPC alternate backend -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Alternate backend for MSRS to enable text-to-speech via DCS-gRPC. --- ### Author: **dogjutsu** --- A table containing functions and variables for MSRS to use DCS-gRPC [DCS-gRPC](https://github.com/DCS-gRPC/rust-server) 0.7.0 or newer as a backend to transmit over SRS. --- This is not a standalone class. Instead, variables and functions under the `Vars` and `Functions` tables get added to or replace MSRS variables/functions when activated. --- --- @type MSRS_BACKEND_DCSGRPC --- @field #number version Version number of this alternate backend. --- @field #table Functions A table of functions that will add or replace the default MSRS class functions. --- @field #table Vars A table of variables that will add or replace the default MSRS class variables. -MSRS_BACKEND_DCSGRPC = {} -MSRS_BACKEND_DCSGRPC.version = 0.1 - -MSRS_BACKEND_DCSGRPC.Functions = {} -MSRS_BACKEND_DCSGRPC.Vars = { provider = 'win' } - ---- Called by @{#MSRS._NewAltBackend} (if present) immediately after an alternate backend functions and variables for MSRS are added/replaced. --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions._MSRSbackendInit = function (self) - BASE:I('Loaded MSRS DCS-gRPC alternate backend version ' .. self.AltBackend.version or 'unspecified') - - return self -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- MSRS DCS-gRPC alternate backend User Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- No-op replacement function for @{#MSRS.SetPath} (Not Applicable) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetPath = function (self) - return self -end - ---- No-op replacement function for @{#MSRS.GetPath} (Not Applicable) --- @param #MSRS self --- @return #string Empty string -MSRS_BACKEND_DCSGRPC.Functions.GetPath = function (self) - return '' -end - ---- No-op replacement function for @{#MSRS.SetVolume} (Not Applicable) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetVolume = function (self) - BASE:I('NOTE: MSRS:SetVolume() not used with DCS-gRPC backend.') - return self -end - ---- No-op replacement function for @{#MSRS.GetVolume} (Not Applicable) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.GetVolume = function (self) - BASE:I('NOTE: MSRS:GetVolume() not used with DCS-gRPC backend.') - return 1 -end - ---- No-op replacement function for @{#MSRS.SetGender} (Not Applicable) --- @param #MSRS self --- #string Gender Gender: "male" or "female" --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetGender = function (self, Gender) - -- Use DCS-gRPC default if not specified - - if Gender then - self.gender=Gender:lower() - end - - -- Debug output. - self:T("Setting gender to "..tostring(self.gender)) - return self -end - ---- Replacement function for @{#MSRS.SetGoogle} to use google text-to-speech. (API key set as part of DCS-gRPC configuration) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetGoogle = function (self) - self.provider = 'gcloud' - return self -end - ---- Replacement function for @{#MSRS.SetGoogle} to use google text-to-speech - here: Set the API key --- @param #MSRS self --- @param #string key --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetAPIKey = function (self, key) - self.APIKey = key - return self -end - ---- Replacement function for @{#MSRS.SetGoogle} to use google text-to-speech - here: Set the API key --- @param #MSRS self --- @param #string voice --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetDefaultVoice = function (self, voice) - self.defaultVoice = voice - return self -end - ---- MSRS:SetAWS() Use AWS text-to-speech. (API key set as part of DCS-gRPC configuration) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetAWS = function (self) - self.provider = 'aws' - return self -end - ---- MSRS:SetAzure() Use Azure text-to-speech. (API key set as part of DCS-gRPC configuration) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetAzure = function (self) - self.provider = 'azure' - return self -end - ---- MSRS:SetWin() Use local Windows OS text-to-speech (Windows Server 2019 / Windows 11 / Windows 10? or newer). (Default) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.SetWin = function (self) - self.provider = 'win' - return self -end - ---- Replacement function for @{#MSRS.Help} to display help. --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.Help = function (self) - env.info('For DCS-gRPC help, please see: https://github.com/DCS-gRPC/rust-server') - return self -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- MSRS DCS-gRPC alternate backend Transmission Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- No-op replacement function for @{#MSRS.PlaySoundFile} (Not Applicable) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.PlaySoundFile = function (self) - BASE:E("ERROR: MSRS:PlaySoundFile() is not supported by the DCS-gRPC backend.") - return self -end - ---- Replacement function for @{#MSRS.PlaySoundText} --- @param #MSRS self --- @param Sound.SoundOutput#SOUNDTEXT SoundText Sound text. --- @param #number Delay Delay in seconds, before the sound file is played. --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.PlaySoundText = function (self, SoundText, Delay) - - if Delay and Delay>0 then - self:ScheduleOnce(Delay, self.PlaySoundText, self, SoundText, 0) - else - self:_DCSgRPCtts(tostring(SoundText.text)) - end - - return self -end - ---- Replacement function for @{#MSRS.PlayText} --- @param #MSRS self --- @param #string Text Text message. --- @param #number Delay Delay in seconds, before the message is played. --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.PlayText = function (self, Text, Delay) - - if Delay and Delay>0 then - self:ScheduleOnce(Delay, self.PlayText, self, Text, 0) - else - self:_DCSgRPCtts(tostring(Text)) - end - - return self -end - ---- Replacement function for @{#MSRS.PlayText} --- @param #MSRS self --- @param #string Text Text message. --- @param #number Delay Delay in seconds, before the message is played. --- @param #table Frequencies Radio frequencies. --- @param #table Modulations Radio modulations. (Non-functional, DCS-gRPC sets automatically) --- @param #string Gender Gender. (Non-functional, only 'Voice' supported) --- @param #string Culture Culture. (Non-functional, only 'Voice' supported) --- @param #string Voice Voice. --- @param #number Volume Volume. (Non-functional, all transmissions full volume with DCS-gRPC) --- @param #string Label Label. --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.PlayTextExt = function (self, Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label) - if Delay and Delay>0 then - self:ScheduleOnce(Delay, self.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label) - else - self:_DCSgRPCtts(tostring(Text), nil, Frequencies, Voice, Label) - end - - return self -end - ---- No-op replacement function for @{#MSRS.PlayTextFile} (Not Applicable) --- @param #MSRS self --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions.PlayTextFile = function (self, TextFile, Delay) - BASE:E("ERROR: MSRS:PlayTextFile() is not supported by the DCS-gRPC backend.") - return self -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- MSRS DCS-gRPC alternate backend Misc Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - --- DCS-gRPC v0.70 TTS API call: --- GRPC.tts(ssml, frequency[, options]) - Synthesize text (ssml; SSML tags supported) to speech and transmit it over SRS on the frequency with the following optional options (and their defaults): - --- { --- -- The plain text without any transformations made to it for the purpose of getting it spoken out --- -- as desired (no SSML tags, no FOUR NINER instead of 49, ...). Even though this field is --- -- optional, please consider providing it as it can be used to display the spoken text to players --- -- with hearing impairments. --- plaintext = null, -- e.g. `= "Hello Pilot"` - --- -- Name of the SRS client. --- srsClientName = "DCS-gRPC", - --- -- The origin of the transmission. Relevant if the SRS server has "Line of --- -- Sight" and/or "Distance Limit" enabled. --- position = { --- lat = 0.0, --- lon = 0.0, --- alt = 0.0, -- in meters --- }, - --- -- The coalition of the transmission. Relevant if the SRS server has "Secure --- -- Coalition Radios" enabled. Supported values are: `blue` and `red`. Defaults --- -- to being spectator if not specified. --- coalition = null, - --- -- TTS provider to be use. Defaults to the one configured in your config or to Windows' --- -- built-in TTS. Examples: --- -- `= { aws = {} }` / `= { aws = { voice = "..." } }` enable AWS TTS --- -- `= { azure = {} }` / `= { azure = { voice = "..." } }` enable Azure TTS --- -- `= { gcloud = {} }` / `= { gcloud = { voice = "..." } }` enable Google Cloud TTS --- -- `= { win = {} }` / `= { win = { voice = "..." } }` enable Windows TTS --- provider = null, --- } - ---- Make DCS-gRPC API call to transmit text-to-speech over SRS. --- @param #MSRS self --- @param #string Text Text of message to transmit (can also be SSML). --- @param #string Optional plaintext version of message (for accessiblity) --- @param #table Frequencies Radio frequencies to transmit on. Can also accept a number in MHz. --- @param #string Voice Voice for the TTS provider to user. --- @param #string Label Label (SRS diplays as name of the transmitter). --- @return #MSRS self -MSRS_BACKEND_DCSGRPC.Functions._DCSgRPCtts = function (self, Text, Plaintext, Frequencies, Voice, Label) - - BASE:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") - BASE:T({Text, Plaintext, Frequencies, Voice, Label}) - - local options = self.ProviderOptions or MSRS.ProviderOptions or {} -- #MSRS.GRPCOptions - local ssml = Text or '' - - local XmitFrequencies = Frequencies or self.Frequency - if type(XmitFrequencies)~="table" then - XmitFrequencies={XmitFrequencies} - end - - options.plaintext = Plaintext - options.srsClientName = Label or self.Label - options.position = {} - if self.coordinate then - options.position.lat, options.position.lon, options.position.alt = self:_GetLatLongAlt(self.coordinate) - end - - options.position.lat = options.position.lat or 0.0 - options.position.lon = options.position.lon or 0.0 - options.position.alt = options.position.alt or 0.0 - - if UTILS.GetCoalitionName(self.coalition) == 'Blue' then - options.coalition = 'blue' - elseif UTILS.GetCoalitionName(self.coalition) == 'Red' then - options.coalition = 'red' - end - - local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider - - options.provider = {} - - options.provider[provider] = {} - - if self.APIKey then - options.provider[provider].key = self.APIKey - end - - if self.defaultVoice then - options.provider[provider].defaultVoice = self.defaultVoice - end - - if self.voice then - - -- Set voice. - options.provider[provider].voice = Voice or self.voice or self.defaultVoice - - elseif ssml then - - -- DCS-gRPC doesn't directly support language/gender, but can use SSML - -- Only use if a voice isn't explicitly set - - local preTag, genderProp, langProp, postTag = '', '', '', '' - - if self.gender then - genderProp = ' gender=\"' .. self.gender .. '\"' - end - if self.culture then - langProp = ' language=\"' .. self.culture .. '\"' - end - - if self.culture or self.gender then - preTag = '' - postTag = '' - ssml = preTag .. Text .. postTag - end - - end - - for _,_freq in ipairs(XmitFrequencies) do - local freq = _freq*1000000 - BASE:T("GRPC.tts") - BASE:T(ssml) - BASE:T(freq) - BASE:T({options}) - GRPC.tts(ssml, freq, options) - end - -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Manages radio transmissions. From fac7a5fdc6b816e07d43d4df33325d56ff0ad0eb Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 4 Dec 2023 10:40:38 +0100 Subject: [PATCH 05/95] Update CTLD.lua (#2058) Add Remove Crates option --- Moose Development/Moose/Ops/CTLD.lua | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 9f0d780bd..07f15ad9d 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -2561,6 +2561,40 @@ function CTLD:_ListCratesNearby( _group, _unit) return self end +-- (Internal) Function to find and Remove nearby crates. +-- @param #CTLD self +-- @param Wrapper.Group#GROUP Group +-- @param Wrapper.Unit#UNIT Unit +-- @return #CTLD self +function CTLD:_RemoveCratesNearby( _group, _unit) + self:T(self.lid .. " _RemoveCratesNearby") + local finddist = self.CrateDistance or 35 + local crates,number = self:_FindCratesNearby(_group,_unit, finddist,true) -- #table + if number > 0 then + local text = REPORT:New("Removing Crates Found Nearby:") + text:Add("------------------------------------------------------------") + for _,_entry in pairs (crates) do + local entry = _entry -- #CTLD_CARGO + local name = entry:GetName() --#string + local dropped = entry:WasDropped() + if dropped then + text:Add(string.format("Crate for %s, %dkg removed",name, entry.PerCrateMass)) + else + text:Add(string.format("Crate for %s, %dkg removed",name, entry.PerCrateMass)) + end + entry:GetPositionable():Destroy(false) + end + if text:GetCount() == 1 then + text:Add(" N O N E") + end + text:Add("------------------------------------------------------------") + self:_SendMessage(text:Text(), 30, true, _group) + else + self:_SendMessage(string.format("No (loadable) crates within %d meters!",finddist), 10, false, _group) + end + return self +end + --- (Internal) Return distance in meters between two coordinates. -- @param #CTLD self -- @param Core.Point#COORDINATE _point1 Coordinate one @@ -3672,6 +3706,7 @@ function CTLD:_RefreshF10Menus() end end listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit) + listmenu = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",topcrates, self._RemoveCratesNearby, self, _group, _unit) local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit) if not self.nobuildmenu then local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit) From bc3a5271dc8ac91304862a21f55b5a4fb574b0ff Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 4 Dec 2023 22:19:31 +0100 Subject: [PATCH 06/95] Update OpsGroup.lua - Fixed group returning to legion when told not to --- Moose Development/Moose/Ops/OpsGroup.lua | 28 ++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index b5eb4079c..d7cbe4ab6 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -3472,10 +3472,9 @@ function OPSGROUP:RemoveWaypoint(wpindex) -- Could be that the waypoint we are currently moving to was the LAST waypoint. Then we now passed the final waypoint. if (self.adinfinitum or istemp) then - self:_PassedFinalWaypoint(false, "Removed PASSED temporary waypoint ") - end - - + self:_PassedFinalWaypoint(false, "Removed PASSED temporary waypoint") + end + end end @@ -5801,6 +5800,27 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission) end end + if self.legion and self.legionReturn==false and self.waypoints and #self.waypoints==1 then + --- + -- This is the case where a group was send on a mission (which is over now), has no addional + -- waypoints or tasks and should NOT return to its legion. + -- We create a new waypoint at the current position and let it hold here. + --- + + local Coordinate=self:GetCoordinate() + + if self.isArmygroup then + ARMYGROUP.AddWaypoint(self, Coordinate, 0, nil, nil, false) + elseif self.isNavygroup then + NAVYGROUP.AddWaypoint(self,Coordinate, 0, nil, nil, false) + end + + -- Remove original waypoint. + self:RemoveWaypoint(1) + + self:_PassedFinalWaypoint(true, "Passed final waypoint as group is done with mission but should NOT return to its legion") + end + -- Check if group is done. self:_CheckGroupDone(delay) From f789fbac709d5127e068ac09b8f52433f062c57a Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 5 Dec 2023 17:48:34 +0100 Subject: [PATCH 07/95] Update Zone.lua - Fixed ZONE_POLYGON:New called without initial points --- Moose Development/Moose/Core/Zone.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 229356455..26a164c83 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2380,13 +2380,13 @@ function ZONE_POLYGON_BASE:New( ZoneName, PointsArray ) self._.Polygon[i].y = PointsArray[i].y end + -- triangulate the polygon so we can work with it + self._Triangles = self:_Triangulate() + -- set the polygon's surface area + self.SurfaceArea = self:_CalculateSurfaceArea() + end - -- triangulate the polygon so we can work with it - self._Triangles = self:_Triangulate() - -- set the polygon's surface area - self.SurfaceArea = self:_CalculateSurfaceArea() - return self end From e078e488531e5d6e5d62276da17c2d293e4843f2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 6 Dec 2023 08:42:07 +0100 Subject: [PATCH 08/95] #SPAWN * Fix for a Link16 flight having a non-NATO callsign as number --- Moose Development/Moose/Core/Spawn.lua | 31 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index e562bfbf5..388b9aafe 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3321,10 +3321,10 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 SpawnTemplate.units[UnitID].callsign[2] = UnitID SpawnTemplate.units[UnitID].callsign[3] = "1" SpawnTemplate.units[UnitID].callsign["name"] = tostring(callsignname)..tostring(UnitID).."1" - -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) + -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) end else - -- Ruskis + -- Russkis for UnitID = 1, #SpawnTemplate.units do SpawnTemplate.units[UnitID].callsign = math.random(1,999) end @@ -3335,7 +3335,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 local Callsign = SpawnTemplate.units[UnitID].callsign if Callsign then if type( Callsign ) ~= "number" then -- blue callsign - --UTILS.PrintTableToLog(Callsign,1) + -- UTILS.PrintTableToLog(Callsign,1) Callsign[2] = ((SpawnIndex - 1) % 10) + 1 local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers @@ -3377,11 +3377,11 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end end -- VoiceCallsignNumber - if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber then + if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber and type( Callsign ) ~= "number" then SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber = SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3] end -- VoiceCallsignLabel - if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel then + if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel and type( Callsign ) ~= "number" then local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers local label = "NY" -- Navy One exception @@ -3390,7 +3390,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel = label end - -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) + -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1) -- FlightLead if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.settings then SpawnTemplate.units[UnitID].datalinks.Link16.settings.flightLead = UnitID == 1 and true or false @@ -3399,11 +3399,28 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.SADL and SpawnTemplate.units[UnitID].datalinks.SADL.settings then SpawnTemplate.units[UnitID].datalinks.SADL.settings.flightLead = UnitID == 1 and true or false end - --UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) + -- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1) + end + end + -- Link16 team members + for UnitID = 1, #SpawnTemplate.units do + if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.network then + local team = {} + local isF16 = string.find(SpawnTemplate.units[UnitID].type,"F-16",1,true) and true or false + for ID = 1, #SpawnTemplate.units do + local member = {} + member.missionUnitId = ID + if isF16 then + member.TDOA = true + end + table.insert(team,member) + end + SpawnTemplate.units[UnitID].datalinks.Link16.network.teamMembers = team end end self:T3( { "Template:", SpawnTemplate } ) + --UTILS.PrintTableToLog(SpawnTemplate,1) return SpawnTemplate end From 88e1bbd60d9c03e32a48c2ad8c49fda63930a1af Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 11:20:43 +0100 Subject: [PATCH 09/95] #SET * Repaired SET_UNIT:GetCoordinate() --- Moose Development/Moose/Core/Set.lua | 41 ++++++++++++++++------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index fec517906..c9cbc9f3a 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -419,7 +419,11 @@ do -- SET_BASE -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetRandom() - local tablemax = table.maxn(self.Index) + local tablemax = 0 + for _,_ind in pairs(self.Index) do + tablemax = tablemax + 1 + end + --local tablemax = table.maxn(self.Index) local RandomItem = self.Set[self.Index[math.random(1,tablemax)]] self:T3( { RandomItem } ) return RandomItem @@ -561,10 +565,12 @@ do -- SET_BASE return self end - --- Iterate the SET_BASE while identifying the nearest object from a @{Core.Point#POINT_VEC2}. + --- Iterate the SET_BASE while identifying the nearest object in the set from a @{Core.Point#POINT_VEC2}. -- @param #SET_BASE self - -- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set. + -- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#COORDINATE} or @{Core.Point#POINT_VEC2} object (but **not** a simple DCS#Vec2!) from where to evaluate the closest object in the set. -- @return Core.Base#BASE The closest object. + -- @usage + -- myset:FindNearestObjectFromPointVec2( ZONE:New("Test Zone"):GetCoordinate() ) function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 ) self:F2( PointVec2 ) @@ -2849,15 +2855,14 @@ do -- SET_UNIT function SET_UNIT:GetCoordinate() local Coordinate = nil - local unit = self:GetRandom() + local unit = self:GetFirst() if self:Count() == 1 and unit then return unit:GetCoordinate() end if unit then - local Coordinate = unit:GetCoordinate() - --self:F({Coordinate:GetVec3()}) - - + Coordinate = unit:GetCoordinate() + self:T2(UTILS.PrintTableToLog(Coordinate:GetVec3())) + local x1 = Coordinate.x local x2 = Coordinate.x local y1 = Coordinate.y @@ -2868,19 +2873,19 @@ do -- SET_UNIT local AvgHeading = nil local MovingCount = 0 - for UnitName, UnitData in pairs( self:GetAliveSet() ) do + for UnitName, UnitData in pairs( self.Set) do local Unit = UnitData -- Wrapper.Unit#UNIT - local Coordinate = Unit:GetCoordinate() + local Coord = Unit:GetCoordinate() - x1 = (Coordinate.x < x1) and Coordinate.x or x1 - x2 = (Coordinate.x > x2) and Coordinate.x or x2 - y1 = (Coordinate.y < y1) and Coordinate.y or y1 - y2 = (Coordinate.y > y2) and Coordinate.y or y2 - z1 = (Coordinate.y < z1) and Coordinate.z or z1 - z2 = (Coordinate.y > z2) and Coordinate.z or z2 + x1 = (Coord.x < x1) and Coord.x or x1 + x2 = (Coord.x > x2) and Coord.x or x2 + y1 = (Coord.y < y1) and Coord.y or y1 + y2 = (Coord.y > y2) and Coord.y or y2 + z1 = (Coord.y < z1) and Coord.z or z1 + z2 = (Coord.y > z2) and Coord.z or z2 - local Velocity = Coordinate:GetVelocity() + local Velocity = Coord:GetVelocity() if Velocity ~= 0 then MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity local Heading = Coordinate:GetHeading() @@ -2897,7 +2902,7 @@ do -- SET_UNIT Coordinate:SetHeading( AvgHeading ) Coordinate:SetVelocity( MaxVelocity ) - self:F( { Coordinate = Coordinate } ) + self:T2(UTILS.PrintTableToLog(Coordinate:GetVec3())) end return Coordinate From fe9d841af5ebda6ac44d856ad645dc5acac87ec4 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 11:21:52 +0100 Subject: [PATCH 10/95] #PLAYERRECCE * Fixed visual targets not being smoked * Added option to smoke an average coordinate of targets instead of all * Defaulted self-smoking of player to false --- Moose Development/Moose/Ops/PlayerRecce.lua | 79 +++++++++++++-------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/Moose Development/Moose/Ops/PlayerRecce.lua b/Moose Development/Moose/Ops/PlayerRecce.lua index ec3a57954..9c1cb9eb6 100644 --- a/Moose Development/Moose/Ops/PlayerRecce.lua +++ b/Moose Development/Moose/Ops/PlayerRecce.lua @@ -79,6 +79,7 @@ -- @field Utilities.FiFo#FIFO TargetCache -- @field #boolean smokeownposition -- @field #table SmokeOwn +-- @field #boolean smokeaveragetargetpos -- @extends Core.Fsm#FSM --- @@ -104,7 +105,7 @@ PLAYERRECCE = { ClassName = "PLAYERRECCE", verbose = true, lid = nil, - version = "0.0.22", + version = "0.1.23", ViewZone = {}, ViewZoneVisual = {}, ViewZoneLaser = {}, @@ -130,8 +131,9 @@ PLAYERRECCE = { ReferencePoint = nil, TForget = 600, TargetCache = nil, - smokeownposition = true, + smokeownposition = false, SmokeOwn = {}, + smokeaveragetargetpos = false, } --- @@ -1109,9 +1111,8 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername) self:T(self.lid.."_SmokeTargets") local cameraset = self:_GetTargetSet(client,true) -- Core.Set#SET_UNIT local visualset = self:_GetTargetSet(client,false) -- Core.Set#SET_UNIT - cameraset:AddSet(visualset) - if cameraset:CountAlive() > 0 then + if cameraset:CountAlive() > 0 or visualset:CountAlive() > 0 then self:__TargetsSmoked(-1,client,playername,cameraset) else return self @@ -1126,29 +1127,31 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername) -- laser targer gets extra smoke if laser and laser.Target and laser.Target:IsAlive() then laser.Target:GetCoordinate():Smoke(lasersmoke) - if cameraset:IsInSet(laser.Target) then - cameraset:Remove(laser.Target:GetName(),true) + end + + local coord = visualset:GetCoordinate() + if coord and self.smokeaveragetargetpos then + coord:SetAtLandheight() + coord:Smoke(medsmoke) + else + -- smoke everything + for _,_unit in pairs(visualset.Set) do + local unit = _unit --Wrapper.Unit#UNIT + if unit and unit:IsAlive() then + local coord = unit:GetCoordinate() + local threat = unit:GetThreatLevel() + if coord then + local color = lowsmoke + if threat > 7 then + color = highsmoke + elseif threat > 2 then + color = medsmoke + end + coord:Smoke(color) + end + end end end - - local coordinate = nil - local setthreat = 0 - -- smoke everything else - if cameraset:CountAlive() > 1 then - local coordinate = cameraset:GetCoordinate() - local setthreat = cameraset:CalculateThreatLevelA2G() - end - - if coordinate then - local color = lowsmoke - if setthreat > 7 then - color = medsmoke - elseif setthreat > 2 then - color = lowsmoke - end - coordinate:Smoke(color) - end - if self.SmokeOwn[playername] then local cc = client:GetVec2() -- don't smoke mid-air @@ -1189,15 +1192,15 @@ function PLAYERRECCE:_FlareTargets(client,group,playername) -- smoke everything else for _,_unit in pairs(cameraset.Set) do local unit = _unit --Wrapper.Unit#UNIT - if unit then + if unit and unit:IsAlive() then local coord = unit:GetCoordinate() local threat = unit:GetThreatLevel() if coord then local color = lowsmoke if threat > 7 then - color = medsmoke + color = highsmoke elseif threat > 2 then - color = lowsmoke + color = medsmoke end coord:Flare(color) end @@ -1546,7 +1549,7 @@ end -- @param #PLAYERRECCE self -- @return #PLAYERRECCE self function PLAYERRECCE:EnableSmokeOwnPosition() - self:T(self.lid.."ENableSmokeOwnPosition") + self:T(self.lid.."EnableSmokeOwnPosition") self.smokeownposition = true return self end @@ -1560,6 +1563,24 @@ function PLAYERRECCE:DisableSmokeOwnPosition() return self end +--- [User] Enable smoking of average target positions, instead of all targets visible. Loses smoke per threatlevel -- each is med threat. Default is - smoke all positions. +-- @param #PLAYERRECCE self +-- @return #PLAYERRECCE self +function PLAYERRECCE:EnableSmokeAverageTargetPosition() + self:T(self.lid.."ENableSmokeOwnPosition") + self.smokeaveragetargetpos = true + return self +end + +--- [User] Disable smoking of average target positions, instead of all targets visible. Default is - smoke all positions. +-- @param #PLAYERRECCE self +-- @return #PLAYERRECCE +function PLAYERRECCE:DisableSmokeAverageTargetPosition() + self:T(self.lid.."DisableSmokeAverageTargetPosition") + self.smokeaveragetargetpos = false + return self +end + --- [Internal] Get text for text-to-speech. -- Numbers are spaced out, e.g. "Heading 180" becomes "Heading 1 8 0 ". -- @param #PLAYERRECCE self From dd37a42470bff96230cd7257f141fc71f11460ff Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:12:19 +0100 Subject: [PATCH 11/95] Update CTLD.lua (#2060) Changes from @Rey --- Moose Development/Moose/Ops/CTLD.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 07f15ad9d..fac6173bf 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -3671,6 +3671,7 @@ function CTLD:_RefreshF10Menus() local loadmenu = MENU_GROUP_COMMAND:New(_group,"Load crates",topcrates, self._LoadCratesNearby, self, _group, _unit) local cratesmenu = MENU_GROUP:New(_group,"Get Crates",topcrates) local packmenu = MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit) + local removecratesmenu = MENU_GROUP:New(_group, "Remove crates", topcrates) if self.usesubcats then local subcatmenus = {} @@ -3706,7 +3707,7 @@ function CTLD:_RefreshF10Menus() end end listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit) - listmenu = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",topcrates, self._RemoveCratesNearby, self, _group, _unit) + removecrates = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",removecratesmenu, self._RemoveCratesNearby, self, _group, _unit) local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit) if not self.nobuildmenu then local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit) From 6f473faa924942d2509b27ce5014952c7b87b9f7 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:14:59 +0100 Subject: [PATCH 12/95] Update Message.lua #2059 fixed --- Moose Development/Moose/Core/Message.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 0feeecd2b..8b6da3cdd 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -98,7 +98,7 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen self.MessageType = nil - -- When no MessageCategory is given, we don't show it as a title... + -- 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 .. ": " @@ -368,7 +368,7 @@ function MESSAGE:ToCoalition( CoalitionSide, Settings ) if CoalitionSide then if self.MessageDuration ~= 0 then self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration ) - trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen ) + trigger.action.outTextForCoalition( CoalitionSide, self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen ) end end From 9ce1d360d6c921ad072bd3b077d3f89a772a9c37 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 13:31:40 +0100 Subject: [PATCH 13/95] #CTLD * Spawn dropped troops in a nice circle 5m (hover: 1.5m) left of the he --- Moose Development/Moose/Ops/CTLD.lua | 54 +++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index fac6173bf..095eb28de 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -24,7 +24,7 @@ -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update November 2023 +-- Last Update December 2023 do @@ -1228,7 +1228,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.43" +CTLD.version="1.0.44" --- Instantiate a new CTLD. -- @param #CTLD self @@ -3010,6 +3010,35 @@ function CTLD:IsHercules(Unit) end end + +--- (Internal) Function to set troops positions of a template to a nice circle +-- @param #CTLD self +-- @param Core.Point#COORDINATE Coordinate Start coordinate to use +-- @param #number Radius Radius to be used +-- @param #number Heading Heading starting with +-- @param #string Template The group template name +-- @return #table Positions The positions table +function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template) + local Positions = {} + local template = _DATABASE:GetGroupTemplate(Template) + UTILS.PrintTableToLog(template) + local numbertroops = #template.units + local newcenter = Coordinate:Translate(Radius,((Heading+270)%360)) + for i=1,360,math.floor(360/numbertroops) do + local phead = ((Heading+270+i)%360) + local post = newcenter:Translate(Radius,phead) + local pos1 = post:GetVec2() + local p1t = { + x = pos1.x, + y = pos1.y, + heading = phead, + } + table.insert(Positions,p1t) + end + UTILS.PrintTableToLog(Positions) + return Positions +end + --- (Internal) Function to unload troops from heli. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group @@ -3061,14 +3090,29 @@ function CTLD:_UnloadTroops(Group, Unit) zoneradius = Unit:GetVelocityMPS() or 100 end local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,zoneradius*factor) - local randomcoord = zone:GetRandomCoordinate(10,30*factor):GetVec2() + local randomcoord = zone:GetRandomCoordinate(10,30*factor) --:GetVec2() + local heading = Group:GetHeading() or 0 + -- Spawn troops left from us, closer when hovering, further off when landed + if hoverunload or grounded then + randomcoord = Group:GetCoordinate() + -- slightly left from us + local Angle = (heading+270)%360 + local offset = hoverunload and 1.5 or 5 + randomcoord:Translate(offset,Angle,nil,true) + end + local tempcount = 0 for _,_template in pairs(temptable) do self.TroopCounter = self.TroopCounter + 1 + tempcount = tempcount+1 local alias = string.format("%s-%d", _template, math.random(1,100000)) + local rad = 2.5+tempcount + local Positions = self:_GetUnitPositions(randomcoord,rad,heading,_template) self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias) - :InitRandomizeUnits(true,20,2) + --:InitRandomizeUnits(true,20,2) + --:InitHeading(heading) :InitDelayOff() - :SpawnFromVec2(randomcoord) + :InitSetUnitAbsolutePositions(Positions) + :SpawnFromVec2(randomcoord:GetVec2()) self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type) end -- template loop cargo:SetWasDropped(true) From c770f4cb680c546fd984e7ca72e16f756c4654a8 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 13:46:32 +0100 Subject: [PATCH 14/95] #ZONE Docu fixes --- Moose Development/Moose/Core/Zone.lua | 32 ++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index cb2648d0d..ab2f23949 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -10,7 +10,7 @@ -- * Create moving zones around a unit. -- * Create moving zones around a group. -- * Provide the zone behavior. Some zones are static, while others are moveable. --- * Enquiry if a coordinate is within a zone. +-- * Enquire if a coordinate is within a zone. -- * Smoke zones. -- * Set a zone probability to control zone selection. -- * Get zone coordinates. @@ -42,7 +42,7 @@ -- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Wrapper.Unit#UNIT} with a radius. -- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius. -- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- * @{#ZONE_OVAL}: The ZONE_OVAL class isdefined by a center point, major axis, minor axis, and angle. +-- * @{#ZONE_OVAL}: The ZONE_OVAL class is defined by a center point, major axis, minor axis, and angle. -- -- === -- @@ -2232,7 +2232,14 @@ end --- ZONE_POLYGON to accurately find a point inside a polygon; as well as getting the correct surface area of --- a polygon. -- @type _ZONE_TRIANGLE --- @extends #BASE +-- @extends Core.Zone#ZONE_BASE + +--- ## _ZONE_TRIANGLE class, extends @{#ZONE_BASE} +-- +-- _ZONE_TRIANGLE class is a helper class for ZONE_POLYGON +-- This class implements the inherited functions from @{#ZONE_BASE} taking into account the own zone format and properties. +-- +-- @field #_ZONE_TRIANGLE _ZONE_TRIANGLE = { ClassName="ZONE_TRIANGLE", Points={}, @@ -2241,7 +2248,12 @@ _ZONE_TRIANGLE = { SurfaceArea=0, DrawIDs={} } - +--- +-- @param #_ZONE_TRIANGLE self +-- @param DCS#Vec p1 +-- @param DCS#Vec p2 +-- @param DCS#Vec p3 +-- @return #_ZONE_TRIANGLE self function _ZONE_TRIANGLE:New(p1, p2, p3) local self = BASE:Inherit(self, ZONE_BASE:New()) self.Points = {p1, p2, p3} @@ -2260,6 +2272,7 @@ function _ZONE_TRIANGLE:New(p1, p2, p3) end --- Checks if a point is contained within the triangle. +-- @param #_ZONE_TRIANGLE self -- @param #table pt The point to check -- @param #table points (optional) The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it -- @return #bool True if the point is contained, false otherwise @@ -2281,6 +2294,7 @@ function _ZONE_TRIANGLE:ContainsPoint(pt, points) end --- Returns a random Vec2 within the triangle. +-- @param #_ZONE_TRIANGLE self -- @param #table points The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it -- @return #table The random Vec2 function _ZONE_TRIANGLE:GetRandomVec2(points) @@ -2296,6 +2310,8 @@ function _ZONE_TRIANGLE:GetRandomVec2(points) end --- Draw the triangle +-- @param #_ZONE_TRIANGLE self +-- @return #table of draw IDs function _ZONE_TRIANGLE:Draw(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) Coalition=Coalition or -1 @@ -2390,7 +2406,8 @@ end --- Triangulates the polygon. --- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua --- @return #table The #_TRIANGLE list that make up +-- @param #ZONE_POLYGON_BASE self +-- @return #table The #_ZONE_TRIANGLE list that makes up the polygon function ZONE_POLYGON_BASE:_Triangulate() local points = self._.Polygon local triangles = {} @@ -2519,6 +2536,7 @@ end --- Calculates the surface area of the polygon. The surface area is the sum of the areas of the triangles that make up the polygon. --- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua +-- @param #ZONE_POLYGON_BASE self -- @return #number The surface area of the polygon function ZONE_POLYGON_BASE:_CalculateSurfaceArea() local area = 0 @@ -3590,7 +3608,8 @@ end do -- ZONE_ELASTIC - + + --- -- @type ZONE_ELASTIC -- @field #table points Points in 2D. -- @field #table setGroups Set of GROUPs. @@ -3791,6 +3810,7 @@ end do -- ZONE_AIRBASE + --- -- @type ZONE_AIRBASE -- @field #boolean isShip If `true`, airbase is a ship. -- @field #boolean isHelipad If `true`, airbase is a helipad. From ff6704f123ca0517c6ea524607c883cc2a2f2012 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 15:11:53 +0100 Subject: [PATCH 15/95] #ZONE docu fixes --- Moose Development/Moose/Core/Zone.lua | 485 +++++++++++++------------- 1 file changed, 246 insertions(+), 239 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index ab2f23949..09ef67c2f 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -480,12 +480,14 @@ function ZONE_BASE:UndrawZone(Delay) if Delay and Delay>0 then self:ScheduleOnce(Delay, ZONE_BASE.UndrawZone, self) else - if self.DrawID and type(self.DrawID) ~= "table" then - UTILS.RemoveMark(self.DrawID) - else -- DrawID is a table with a collections of mark ids, as used in ZONE_POLYGON + if self.DrawID then + if type(self.DrawID) ~= "table" then + UTILS.RemoveMark(self.DrawID) + else -- DrawID is a table with a collections of mark ids, as used in ZONE_POLYGON for _, mark_id in pairs(self.DrawID) do - UTILS.RemoveMark(mark_id) + UTILS.RemoveMark(mark_id) end + end end end return self @@ -1999,234 +2001,6 @@ function ZONE_GROUP:GetRandomPointVec2( inner, outer ) end ---- ZONE_OVAL created from a center point, major axis, minor axis, and angle. --- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua --- @type ZONE_OVAL --- @extends Core.Zone#ZONE_BASE - ---- ## ZONE_OVAL class, extends @{#ZONE_BASE} --- --- The ZONE_OVAL class is defined by a center point, major axis, minor axis, and angle. --- This class implements the inherited functions from @{#ZONE_BASE} taking into account the own zone format and properties. --- --- @field #ZONE_OVAL -ZONE_OVAL = { - ClassName = "OVAL", - ZoneName="", - MajorAxis = nil, - MinorAxis = nil, - Angle = 0, - DrawPoly = nil -- let's just use a ZONE_POLYGON to draw the ZONE_OVAL on the map -} - ---- Creates a new ZONE_OVAL from a center point, major axis, minor axis, and angle. ---- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua --- @param #table vec2 The center point of the oval --- @param #number major_axis The major axis of the oval --- @param #number minor_axis The minor axis of the oval --- @param #number angle The angle of the oval --- @return #ZONE_OVAL The new oval -function ZONE_OVAL:New(name, vec2, major_axis, minor_axis, angle) - self = BASE:Inherit(self, ZONE_BASE:New()) - self.ZoneName = name - self.CenterVec2 = vec2 - self.MajorAxis = major_axis - self.MinorAxis = minor_axis - self.Angle = angle or 0 - - _DATABASE:AddZone(name, self) - - return self -end - ---- Constructor to create a ZONE_OVAL instance, taking the name of a drawing made with the draw tool in the Mission Editor. ---- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua --- @param #ZONE_OVAL self --- @param #string DrawingName The name of the drawing in the Mission Editor --- @return #ZONE_OVAL self -function ZONE_OVAL:NewFromDrawing(DrawingName) - self = BASE:Inherit(self, ZONE_BASE:New(DrawingName)) - for _, layer in pairs(env.mission.drawings.layers) do - for _, object in pairs(layer["objects"]) do - if string.find(object["name"], DrawingName, 1, true) then - if object["polygonMode"] == "oval" then - self.CenterVec2 = { x = object["mapX"], y = object["mapY"] } - self.MajorAxis = object["r1"] - self.MinorAxis = object["r2"] - self.Angle = object["angle"] - - end - end - end - end - - _DATABASE:AddZone(DrawingName, self) - - return self -end - ---- Gets the major axis of the oval. --- @param #ZONE_OVAL self --- @return #number The major axis of the oval -function ZONE_OVAL:GetMajorAxis() - return self.MajorAxis -end - ---- Gets the minor axis of the oval. --- @param #ZONE_OVAL self --- @return #number The minor axis of the oval -function ZONE_OVAL:GetMinorAxis() - return self.MinorAxis -end - ---- Gets the angle of the oval. --- @param #ZONE_OVAL self --- @return #number The angle of the oval -function ZONE_OVAL:GetAngle() - return self.Angle -end - ---- Returns a the center point of the oval --- @param #ZONE_OVAL self --- @return #table The center Vec2 -function ZONE_OVAL:GetVec2() - return self.CenterVec2 -end - ---- Checks if a point is contained within the oval. --- @param #ZONE_OVAL self --- @param #table point The point to check --- @return #bool True if the point is contained, false otherwise -function ZONE_OVAL:IsVec2InZone(vec2) - local cos, sin = math.cos, math.sin - local dx = vec2.x - self.CenterVec2.x - local dy = vec2.y - self.CenterVec2.y - local rx = dx * cos(self.Angle) + dy * sin(self.Angle) - local ry = -dx * sin(self.Angle) + dy * cos(self.Angle) - return rx * rx / (self.MajorAxis * self.MajorAxis) + ry * ry / (self.MinorAxis * self.MinorAxis) <= 1 -end - ---- Calculates the bounding box of the oval. The bounding box is the smallest rectangle that contains the oval. --- @param #ZONE_OVAL self --- @return #table The bounding box of the oval -function ZONE_OVAL:GetBoundingSquare() - local min_x = self.CenterVec2.x - self.MajorAxis - local min_y = self.CenterVec2.y - self.MinorAxis - local max_x = self.CenterVec2.x + self.MajorAxis - local max_y = self.CenterVec2.y + self.MinorAxis - - return { - {x=min_x, y=min_x}, {x=max_x, y=min_y}, {x=max_x, y=max_y}, {x=min_x, y=max_y} - } -end - ---- Find points on the edge of the oval --- @param #ZONE_OVAL self --- @param #number num_points How many points should be found. More = smoother shape --- @return #table Points on he edge -function ZONE_OVAL:PointsOnEdge(num_points) - num_points = num_points or 40 - local points = {} - local dtheta = 2 * math.pi / num_points - - for i = 0, num_points - 1 do - local theta = i * dtheta - local x = self.CenterVec2.x + self.MajorAxis * math.cos(theta) * math.cos(self.Angle) - self.MinorAxis * math.sin(theta) * math.sin(self.Angle) - local y = self.CenterVec2.y + self.MajorAxis * math.cos(theta) * math.sin(self.Angle) + self.MinorAxis * math.sin(theta) * math.cos(self.Angle) - table.insert(points, {x = x, y = y}) - end - - return points -end - ---- Returns a random Vec2 within the oval. --- @param #ZONE_OVAL self --- @return #table The random Vec2 -function ZONE_OVAL:GetRandomVec2() - local theta = math.rad(self.Angle) - - local random_point = math.sqrt(math.random()) --> uniformly - --local random_point = math.random() --> more clumped around center - local phi = math.random() * 2 * math.pi - local x_c = random_point * math.cos(phi) - local y_c = random_point * math.sin(phi) - local x_e = x_c * self.MajorAxis - local y_e = y_c * self.MinorAxis - local rx = (x_e * math.cos(theta) - y_e * math.sin(theta)) + self.CenterVec2.x - local ry = (x_e * math.sin(theta) + y_e * math.cos(theta)) + self.CenterVec2.y - - return {x=rx, y=ry} -end - ---- Define a random @{Core.Point#POINT_VEC2} within the zone. --- @param #ZONE_OVAL self --- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. -function ZONE_OVAL:GetRandomPointVec2() - return POINT_VEC2:NewFromVec2(self:GetRandomVec2()) -end - ---- Define a random @{Core.Point#POINT_VEC2} within the zone. --- @param #ZONE_OVAL self --- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. -function ZONE_OVAL:GetRandomPointVec3() - return POINT_VEC2:NewFromVec3(self:GetRandomVec2()) -end - ---- Draw the zone on the F10 map. ---- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua --- @param #ZONE_OVAL self --- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. --- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. --- @param #number Alpha Transparency [0,1]. Default 1. --- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work --- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work --- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. --- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. --- @return #ZONE_OVAL self -function ZONE_OVAL:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) - Coalition = Coalition or self:GetDrawCoalition() - - -- Set draw coalition. - self:SetDrawCoalition(Coalition) - - Color = Color or self:GetColorRGB() - Alpha = Alpha or 1 - - -- Set color. - self:SetColor(Color, Alpha) - - FillColor = FillColor or self:GetFillColorRGB() - if not FillColor then - UTILS.DeepCopy(Color) - end - FillAlpha = FillAlpha or self:GetFillColorAlpha() - if not FillAlpha then - FillAlpha = 0.15 - end - - LineType = LineType or 1 - - -- Set fill color -----------> has fill color worked in recent versions of DCS? - -- doing something like - -- - -- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "") - -- - -- doesn't seem to fill in the shape for an n-sided polygon - self:SetFillColor(FillColor, FillAlpha) - - self.DrawPoly = ZONE_POLYGON:NewFromPointsArray(self.ZoneName, self:PointsOnEdge(80)) - self.DrawPoly:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) -end - ---- Remove drawing from F10 map --- @param #ZONE_OVAL self -function ZONE_OVAL:UndrawZone() - if self.DrawPoly then - self.DrawPoly:UndrawZone() - end -end - - --- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Triangle.lua --- This triangle "zone" is not really to be used on its own, it only serves as building blocks for --- ZONE_POLYGON to accurately find a point inside a polygon; as well as getting the correct surface area of @@ -2394,13 +2168,13 @@ function ZONE_POLYGON_BASE:New( ZoneName, PointsArray ) self._.Polygon[i].y = PointsArray[i].y end + -- triangulate the polygon so we can work with it + self._Triangles = self:_Triangulate() + -- set the polygon's surface area + self.SurfaceArea = self:_CalculateSurfaceArea() + end - -- triangulate the polygon so we can work with it - self._Triangles = self:_Triangulate() - -- set the polygon's surface area - self.SurfaceArea = self:_CalculateSurfaceArea() - return self end @@ -3106,9 +2880,13 @@ function ZONE_POLYGON_BASE:Boundary(Coalition, Color, Radius, Alpha, Segments, C return self end +do -- Zone_Polygon + + --- -- @type ZONE_POLYGON -- @extends #ZONE_POLYGON_BASE +-- @extends #ZONE_BASE --- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon, OR by drawings made with the Draw tool @@ -3140,8 +2918,7 @@ end -- -- This class has been updated to use a accurate way of generating random points inside the polygon without having to use trial and error guesses. -- You can also get the surface area of the polygon now, handy if you want measure which coalition has the largest captured area, for example. - - +-- -- @field #ZONE_POLYGON ZONE_POLYGON = { ClassName="ZONE_POLYGON", @@ -3606,6 +3383,7 @@ function ZONE_POLYGON:IsNoneInZone() return self:CountScannedCoalitions() == 0 end +end do -- ZONE_ELASTIC @@ -3808,6 +3586,235 @@ do -- ZONE_ELASTIC end + + +--- ZONE_OVAL created from a center point, major axis, minor axis, and angle. +-- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @type ZONE_OVAL +-- @extends Core.Zone#ZONE_BASE + +--- ## ZONE_OVAL class, extends @{#ZONE_BASE} +-- +-- The ZONE_OVAL class is defined by a center point, major axis, minor axis, and angle. +-- This class implements the inherited functions from @{#ZONE_BASE} taking into account the own zone format and properties. +-- +-- @field #ZONE_OVAL +ZONE_OVAL = { + ClassName = "OVAL", + ZoneName="", + MajorAxis = nil, + MinorAxis = nil, + Angle = 0, + DrawPoly = nil -- let's just use a ZONE_POLYGON to draw the ZONE_OVAL on the map +} + +--- Creates a new ZONE_OVAL from a center point, major axis, minor axis, and angle. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #table vec2 The center point of the oval +-- @param #number major_axis The major axis of the oval +-- @param #number minor_axis The minor axis of the oval +-- @param #number angle The angle of the oval +-- @return #ZONE_OVAL The new oval +function ZONE_OVAL:New(name, vec2, major_axis, minor_axis, angle) + self = BASE:Inherit(self, ZONE_BASE:New()) + self.ZoneName = name + self.CenterVec2 = vec2 + self.MajorAxis = major_axis + self.MinorAxis = minor_axis + self.Angle = angle or 0 + + _DATABASE:AddZone(name, self) + + return self +end + +--- Constructor to create a ZONE_OVAL instance, taking the name of a drawing made with the draw tool in the Mission Editor. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #ZONE_OVAL self +-- @param #string DrawingName The name of the drawing in the Mission Editor +-- @return #ZONE_OVAL self +function ZONE_OVAL:NewFromDrawing(DrawingName) + self = BASE:Inherit(self, ZONE_BASE:New(DrawingName)) + for _, layer in pairs(env.mission.drawings.layers) do + for _, object in pairs(layer["objects"]) do + if string.find(object["name"], DrawingName, 1, true) then + if object["polygonMode"] == "oval" then + self.CenterVec2 = { x = object["mapX"], y = object["mapY"] } + self.MajorAxis = object["r1"] + self.MinorAxis = object["r2"] + self.Angle = object["angle"] + + end + end + end + end + + _DATABASE:AddZone(DrawingName, self) + + return self +end + +--- Gets the major axis of the oval. +-- @param #ZONE_OVAL self +-- @return #number The major axis of the oval +function ZONE_OVAL:GetMajorAxis() + return self.MajorAxis +end + +--- Gets the minor axis of the oval. +-- @param #ZONE_OVAL self +-- @return #number The minor axis of the oval +function ZONE_OVAL:GetMinorAxis() + return self.MinorAxis +end + +--- Gets the angle of the oval. +-- @param #ZONE_OVAL self +-- @return #number The angle of the oval +function ZONE_OVAL:GetAngle() + return self.Angle +end + +--- Returns a the center point of the oval +-- @param #ZONE_OVAL self +-- @return #table The center Vec2 +function ZONE_OVAL:GetVec2() + return self.CenterVec2 +end + +--- Checks if a point is contained within the oval. +-- @param #ZONE_OVAL self +-- @param #table point The point to check +-- @return #bool True if the point is contained, false otherwise +function ZONE_OVAL:IsVec2InZone(vec2) + local cos, sin = math.cos, math.sin + local dx = vec2.x - self.CenterVec2.x + local dy = vec2.y - self.CenterVec2.y + local rx = dx * cos(self.Angle) + dy * sin(self.Angle) + local ry = -dx * sin(self.Angle) + dy * cos(self.Angle) + return rx * rx / (self.MajorAxis * self.MajorAxis) + ry * ry / (self.MinorAxis * self.MinorAxis) <= 1 +end + +--- Calculates the bounding box of the oval. The bounding box is the smallest rectangle that contains the oval. +-- @param #ZONE_OVAL self +-- @return #table The bounding box of the oval +function ZONE_OVAL:GetBoundingSquare() + local min_x = self.CenterVec2.x - self.MajorAxis + local min_y = self.CenterVec2.y - self.MinorAxis + local max_x = self.CenterVec2.x + self.MajorAxis + local max_y = self.CenterVec2.y + self.MinorAxis + + return { + {x=min_x, y=min_x}, {x=max_x, y=min_y}, {x=max_x, y=max_y}, {x=min_x, y=max_y} + } +end + +--- Find points on the edge of the oval +-- @param #ZONE_OVAL self +-- @param #number num_points How many points should be found. More = smoother shape +-- @return #table Points on he edge +function ZONE_OVAL:PointsOnEdge(num_points) + num_points = num_points or 40 + local points = {} + local dtheta = 2 * math.pi / num_points + + for i = 0, num_points - 1 do + local theta = i * dtheta + local x = self.CenterVec2.x + self.MajorAxis * math.cos(theta) * math.cos(self.Angle) - self.MinorAxis * math.sin(theta) * math.sin(self.Angle) + local y = self.CenterVec2.y + self.MajorAxis * math.cos(theta) * math.sin(self.Angle) + self.MinorAxis * math.sin(theta) * math.cos(self.Angle) + table.insert(points, {x = x, y = y}) + end + + return points +end + +--- Returns a random Vec2 within the oval. +-- @param #ZONE_OVAL self +-- @return #table The random Vec2 +function ZONE_OVAL:GetRandomVec2() + local theta = math.rad(self.Angle) + + local random_point = math.sqrt(math.random()) --> uniformly + --local random_point = math.random() --> more clumped around center + local phi = math.random() * 2 * math.pi + local x_c = random_point * math.cos(phi) + local y_c = random_point * math.sin(phi) + local x_e = x_c * self.MajorAxis + local y_e = y_c * self.MinorAxis + local rx = (x_e * math.cos(theta) - y_e * math.sin(theta)) + self.CenterVec2.x + local ry = (x_e * math.sin(theta) + y_e * math.cos(theta)) + self.CenterVec2.y + + return {x=rx, y=ry} +end + +--- Define a random @{Core.Point#POINT_VEC2} within the zone. +-- @param #ZONE_OVAL self +-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. +function ZONE_OVAL:GetRandomPointVec2() + return POINT_VEC2:NewFromVec2(self:GetRandomVec2()) +end + +--- Define a random @{Core.Point#POINT_VEC2} within the zone. +-- @param #ZONE_OVAL self +-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. +function ZONE_OVAL:GetRandomPointVec3() + return POINT_VEC2:NewFromVec3(self:GetRandomVec2()) +end + +--- Draw the zone on the F10 map. +--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #ZONE_OVAL self +-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. +-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. +-- @param #number Alpha Transparency [0,1]. Default 1. +-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work +-- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work +-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. +-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. +-- @return #ZONE_OVAL self +function ZONE_OVAL:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) + Coalition = Coalition or self:GetDrawCoalition() + + -- Set draw coalition. + self:SetDrawCoalition(Coalition) + + Color = Color or self:GetColorRGB() + Alpha = Alpha or 1 + + -- Set color. + self:SetColor(Color, Alpha) + + FillColor = FillColor or self:GetFillColorRGB() + if not FillColor then + UTILS.DeepCopy(Color) + end + FillAlpha = FillAlpha or self:GetFillColorAlpha() + if not FillAlpha then + FillAlpha = 0.15 + end + + LineType = LineType or 1 + + -- Set fill color -----------> has fill color worked in recent versions of DCS? + -- doing something like + -- + -- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "") + -- + -- doesn't seem to fill in the shape for an n-sided polygon + self:SetFillColor(FillColor, FillAlpha) + + self.DrawPoly = ZONE_POLYGON:NewFromPointsArray(self.ZoneName, self:PointsOnEdge(80)) + self.DrawPoly:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) +end + +--- Remove drawing from F10 map +-- @param #ZONE_OVAL self +function ZONE_OVAL:UndrawZone() + if self.DrawPoly then + self.DrawPoly:UndrawZone() + end +end + do -- ZONE_AIRBASE --- From 6903e252d2461b7827a5b07df2bfff5937044f52 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 7 Dec 2023 16:08:47 +0100 Subject: [PATCH 16/95] #UTILS * Nicer PrintTableToLog() --- Moose Development/Moose/Utilities/Utils.lua | 23 ++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index f8fbfe2eb..9fe3d9823 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -443,22 +443,35 @@ end --- Print a table to log in a nice format -- @param #table table The table to print --- @param #number indent Number of idents +-- @param #number indent Number of indents +-- @return #string text Text created on the fly of the log output function UTILS.PrintTableToLog(table, indent) + local text = "\n" if not table then env.warning("No table passed!") - return + return nil end if not indent then indent = 0 end for k, v in pairs(table) do + if string.find(k," ") then k='"'..k..'"'end if type(v) == "table" then env.info(string.rep(" ", indent) .. tostring(k) .. " = {") - UTILS.PrintTableToLog(v, indent + 1) - env.info(string.rep(" ", indent) .. "}") + text = text ..string.rep(" ", indent) .. tostring(k) .. " = {\n" + text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1)).."\n" + env.info(string.rep(" ", indent) .. "},") + text = text .. string.rep(" ", indent) .. "},\n" else - env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(v)) + local value + if tostring(v) == "true" or tostring(v) == "false" or tonumber(v) ~= nil then + value=v + else + value = '"'..tostring(v)..'"' + end + env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n") + text = text .. string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n" end end + return text end --- Returns table in a easy readable string representation. From b3a006096ce2a83a467697026251489437a2b3a6 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 13:03:34 +0100 Subject: [PATCH 17/95] fixes --- Moose Development/Moose/Core/Set.lua | 88 +++++++++++------------ Moose Development/Moose/Wrapper/Group.lua | 1 + 2 files changed, 41 insertions(+), 48 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index c9cbc9f3a..43fbd8fce 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2854,58 +2854,50 @@ do -- SET_UNIT -- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units. function SET_UNIT:GetCoordinate() - local Coordinate = nil - local unit = self:GetFirst() - if self:Count() == 1 and unit then - return unit:GetCoordinate() - end - if unit then - Coordinate = unit:GetCoordinate() - self:T2(UTILS.PrintTableToLog(Coordinate:GetVec3())) - - local x1 = Coordinate.x - local x2 = Coordinate.x - local y1 = Coordinate.y - local y2 = Coordinate.y - local z1 = Coordinate.z - local z2 = Coordinate.z - local MaxVelocity = 0 - local AvgHeading = nil - local MovingCount = 0 - - for UnitName, UnitData in pairs( self.Set) do - - local Unit = UnitData -- Wrapper.Unit#UNIT - local Coord = Unit:GetCoordinate() - - x1 = (Coord.x < x1) and Coord.x or x1 - x2 = (Coord.x > x2) and Coord.x or x2 - y1 = (Coord.y < y1) and Coord.y or y1 - y2 = (Coord.y > y2) and Coord.y or y2 - z1 = (Coord.y < z1) and Coord.z or z1 - z2 = (Coord.y > z2) and Coord.z or z2 - - local Velocity = Coord:GetVelocity() - if Velocity ~= 0 then - MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity - local Heading = Coordinate:GetHeading() - AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading - MovingCount = MovingCount + 1 + local function GetSetVec3(units) + -- Init. + local x=0 + local y=0 + local z=0 + local n=0 + -- Loop over all units. + for _,unit in pairs(units) do + local vec3=nil --DCS#Vec3 + if unit and unit:IsAlive() then + vec3 = unit:GetVec3() + end + if vec3 then + -- Sum up posits. + x=x+vec3.x + y=y+vec3.y + z=z+vec3.z + -- Increase counter. + n=n+1 end end - - AvgHeading = AvgHeading and (AvgHeading / MovingCount) - - Coordinate.x = (x2 - x1) / 2 + x1 - Coordinate.y = (y2 - y1) / 2 + y1 - Coordinate.z = (z2 - z1) / 2 + z1 - Coordinate:SetHeading( AvgHeading ) - Coordinate:SetVelocity( MaxVelocity ) - - self:T2(UTILS.PrintTableToLog(Coordinate:GetVec3())) + if n>0 then + -- Average. + local Vec3={x=x/n, y=y/n, z=z/n} --DCS#Vec3 + return Vec3 + end + return nil + end + + local Coordinate = nil + local Vec3 = GetSetVec3(self.Set) + if Vec3 then + Coordinate = COORDINATE:NewFromVec3(Vec3) end - return Coordinate + if Coordinate then + local heading = self:GetHeading() or 0 + local velocity = self:GetVelocity() or 0 + Coordinate:SetHeading( heading ) + Coordinate:SetVelocity( velocity ) + self:I(UTILS.PrintTableToLog(Coordinate)) + end + + return Coordinate end --- Get the maximum velocity of the SET_UNIT. diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 9ee2d2c21..fe0833189 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1157,6 +1157,7 @@ function GROUP:GetAverageCoordinate() local coord = COORDINATE:NewFromVec3(vec3) local Heading = self:GetHeading() coord.Heading = Heading + return coord else BASE:E( { "Cannot GetAverageCoordinate", Group = self, Alive = self:IsAlive() } ) return nil From 6b270916c4b192b9d671801ad3e0abd324e83cd5 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 13:53:27 +0100 Subject: [PATCH 18/95] # DETECTION_BASE * Added `SetRadarBlur(minheight,thresheight,thresblur)` --- .../Moose/Functional/Detection.lua | 68 ++++++++++++++----- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 9fc086cd9..2ec01a349 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -39,7 +39,7 @@ do -- DETECTION_BASE - --- @type DETECTION_BASE + -- @type DETECTION_BASE -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role. -- @field DCS#Distance DetectionRange The range till which targets are accepted to be detected. -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. @@ -269,10 +269,10 @@ do -- DETECTION_BASE DetectedItemsByIndex = {}, } - --- @type DETECTION_BASE.DetectedObjects + -- @type DETECTION_BASE.DetectedObjects -- @list <#DETECTION_BASE.DetectedObject> - --- @type DETECTION_BASE.DetectedObject + -- @type DETECTION_BASE.DetectedObject -- @field #string Name -- @field #boolean IsVisible -- @field #boolean KnowType @@ -284,7 +284,7 @@ do -- DETECTION_BASE -- @field #boolean LastPos -- @field #number LastVelocity - --- @type DETECTION_BASE.DetectedItems + -- @type DETECTION_BASE.DetectedItems -- @list <#DETECTION_BASE.DetectedItem> --- Detected item data structure. @@ -522,7 +522,7 @@ do -- DETECTION_BASE do -- State Transition Handling - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. @@ -530,7 +530,7 @@ do -- DETECTION_BASE self:__Detect( 1 ) end - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. @@ -570,7 +570,7 @@ do -- DETECTION_BASE end - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self -- @param #number The amount of alive recce. function DETECTION_BASE:CountAliveRecce() @@ -578,7 +578,7 @@ do -- DETECTION_BASE end - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self function DETECTION_BASE:ForEachAliveRecce( IteratorFunction, ... ) self:F2( arg ) @@ -587,7 +587,7 @@ do -- DETECTION_BASE return self end - --- @param #DETECTION_BASE self + -- @param #DETECTION_BASE self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. @@ -712,6 +712,22 @@ do -- DETECTION_BASE end end + -- Calculate radar blue probability + + if self.RadarBlur then + local minheight = self.RadarBlurMinHeight or 250 -- meters + local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group + local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall + local fheight = math.floor(math.random(1,10000)/100) + local fblur = math.floor(math.random(1,10000)/100) + local unit = UNIT:FindByName(DetectedObjectName) + if unit and unit:IsAlive() then + local AGL = unit:GetAltitude(true) + if AGL <= minheight and fheight > thresheight then DetectionAccepted = false end + if fblur > thresblur then DetectionAccepted = false end + end + end + -- Calculate additional probabilities if not self.DetectedObjects[DetectedObjectName] and TargetIsVisible and self.DistanceProbability then @@ -1011,7 +1027,21 @@ do -- DETECTION_BASE return self end - + + --- Method to make the radar detection less accurate, e.g. for WWII scenarios. + -- @param #DETECTION_BASE self + -- @param #number minheight Minimum flight height to be detected, in meters AGL (above ground) + -- @param #number thresheight Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance) + -- @param #number thresblur Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found) + -- @return #DETECTION_BASE self + function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur) + self.RadarBlur = true + self.RadarBlurMinHeight = minheight or 250 -- meters + self.RadarBlurThresHeight = thresheight or 90 -- 10% chance to find a low flying group + self.RadarBlurThresBlur = thresblur or 85 -- 25% chance to escape the radar overall + return self + end + end do @@ -1354,7 +1384,7 @@ do -- DETECTION_BASE } } - --- @param DCS#Unit FoundDCSUnit + -- @param DCS#Unit FoundDCSUnit -- @param Wrapper.Group#GROUP ReportGroup -- @param Core.Set#SET_GROUP ReportSetGroup local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) @@ -1419,7 +1449,7 @@ do -- DETECTION_BASE DetectedItem.PlayersNearBy = nil _DATABASE:ForEachPlayer( - --- @param Wrapper.Unit#UNIT PlayerUnit + -- @param Wrapper.Unit#UNIT PlayerUnit function( PlayerUnitName ) local PlayerUnit = UNIT:FindByName( PlayerUnitName ) @@ -1975,8 +2005,9 @@ do -- DETECTION_BASE end do -- DETECTION_UNITS - - --- @type DETECTION_UNITS + + --- + -- @type DETECTION_UNITS -- @field DCS#Distance DetectionRange The range till which targets are detected. -- @extends Functional.Detection#DETECTION_BASE @@ -2232,7 +2263,7 @@ end do -- DETECTION_TYPES - --- @type DETECTION_TYPES + -- @type DETECTION_TYPES -- @extends Functional.Detection#DETECTION_BASE --- Will detect units within the battle zone. @@ -2434,8 +2465,9 @@ do -- DETECTION_TYPES end do -- DETECTION_AREAS - - --- @type DETECTION_AREAS + + --- + -- @type DETECTION_AREAS -- @field DCS#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 @{Wrapper.Unit}s, @{Core.Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. -- @extends Functional.Detection#DETECTION_BASE @@ -2961,7 +2993,7 @@ do -- DETECTION_AREAS -- DetectedSet:Flush( self ) - DetectedSet:ForEachUnit( --- @param Wrapper.Unit#UNIT DetectedUnit + DetectedSet:ForEachUnit( -- @param Wrapper.Unit#UNIT DetectedUnit function( DetectedUnit ) if DetectedUnit:IsAlive() then -- self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() ) From 0f4162a9a96750c5c1f3c6298ecaabaaa76104dc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 14:34:41 +0100 Subject: [PATCH 19/95] * fixes --- .../Moose/Functional/Detection.lua | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 2ec01a349..4ddf6ff9c 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -38,7 +38,8 @@ -- @image Detection.JPG do -- DETECTION_BASE - + + --- -- @type DETECTION_BASE -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role. -- @field DCS#Distance DetectionRange The range till which targets are accepted to be detected. @@ -91,6 +92,11 @@ do -- DETECTION_BASE -- -- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) -- + -- + -- ## Radar Blur - use to make the radar less exact, e.g. for WWII scenarios + -- + -- * @{DETECTION_BASE.SetRadarBlur}(): Set the radar blur to be used. + -- -- ## **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 @@ -268,10 +274,12 @@ do -- DETECTION_BASE DetectedItems = {}, DetectedItemsByIndex = {}, } - + + --- -- @type DETECTION_BASE.DetectedObjects -- @list <#DETECTION_BASE.DetectedObject> + --- -- @type DETECTION_BASE.DetectedObject -- @field #string Name -- @field #boolean IsVisible @@ -283,7 +291,8 @@ do -- DETECTION_BASE -- @field #number LastTime -- @field #boolean LastPos -- @field #number LastVelocity - + + --- -- @type DETECTION_BASE.DetectedItems -- @list <#DETECTION_BASE.DetectedItem> @@ -723,7 +732,7 @@ do -- DETECTION_BASE local unit = UNIT:FindByName(DetectedObjectName) if unit and unit:IsAlive() then local AGL = unit:GetAltitude(true) - if AGL <= minheight and fheight > thresheight then DetectionAccepted = false end + if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end if fblur > thresblur then DetectionAccepted = false end end end @@ -2262,7 +2271,8 @@ do -- DETECTION_UNITS end do -- DETECTION_TYPES - + + --- -- @type DETECTION_TYPES -- @extends Functional.Detection#DETECTION_BASE From bc3f9ed7c0ac72560b83a054d2c6a7f7d5b4fe8a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 15:51:35 +0100 Subject: [PATCH 20/95] #SPAWN * Added SPAWN:InitCallSign(ID,Name,Minor,Major) --- Moose Development/Moose/Core/Spawn.lua | 33 ++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 388b9aafe..df588f14a 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1108,6 +1108,22 @@ function SPAWN:InitRandomizeCallsign() return self end +--- [BLUE AIR only!] This method sets a specific callsign for a spawned group. Use for a group with one unit only! +-- @param #SPAWN self +-- @param #number ID ID of the callsign enumerator, e.g. CALLSIGN.Tanker.Texaco - - resulting in e.g. Texaco-2-1 +-- @param #string Name Name of this callsign as it cannot be determined from the ID because of the dependency on the task type of the plane, and the plane type. E.g. "Texaco" +-- @param #number Minor Minor number, i.e. the unit number within the group, e.g 2 - resulting in e.g. Texaco-2-1 +-- @param #number Major Major number, i.e. the group number of this name, e.g. 1 - resulting in e.g. Texaco-2-1 +-- @return #SPAWN self +function SPAWN:InitCallSign(ID,Name,Minor,Major) + self.SpawnInitCallSign = true + self.SpawnInitCallSignID = ID or 1 + self.SpawnInitCallSignMinor = Minor or 1 + self.SpawnInitCallSignMajor = Major or 1 + self.SpawnInitCallSignName = string.lower(Name) or "enfield" + return self +end + --- This method sets a spawn position for the group that is different from the location of the template. -- @param #SPAWN self -- @param Core.Point#COORDINATE Coordinate The position to spawn from @@ -3331,10 +3347,23 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 end end + if self.SpawnInitCallSign then + for UnitID = 1, #SpawnTemplate.units do + local Callsign = SpawnTemplate.units[UnitID].callsign + if Callsign and type( Callsign ) ~= "number" then + SpawnTemplate.units[UnitID].callsign[1] = self.SpawnInitCallSignID + SpawnTemplate.units[UnitID].callsign[2] = self.SpawnInitCallSignMinor + SpawnTemplate.units[UnitID].callsign[3] = self.SpawnInitCallSignMajor + SpawnTemplate.units[UnitID].callsign["name"] = string.format("%s%d%d",self.SpawnInitCallSignName,self.SpawnInitCallSignMinor,self.SpawnInitCallSignMajor) + --UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1) + end + end + end + for UnitID = 1, #SpawnTemplate.units do local Callsign = SpawnTemplate.units[UnitID].callsign if Callsign then - if type( Callsign ) ~= "number" then -- blue callsign + if type( Callsign ) ~= "number" and not self.SpawnInitCallSign then -- blue callsign -- UTILS.PrintTableToLog(Callsign,1) Callsign[2] = ((SpawnIndex - 1) % 10) + 1 local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string @@ -3342,7 +3371,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2 local CallsignLen = CallsignName:len() SpawnTemplate.units[UnitID].callsign[2] = UnitID SpawnTemplate.units[UnitID].callsign["name"] = CallsignName:sub( 1, CallsignLen ) .. SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3] - else + elseif type( Callsign ) == "number" then SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex end end From d2d6fac7df3d3bf5b205fc3dfa90ddffca1fa5bc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 9 Dec 2023 18:16:29 +0100 Subject: [PATCH 21/95] # DETECTION, logic fix --- Moose Development/Moose/Functional/Detection.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 4ddf6ff9c..a33e7f7b0 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -724,6 +724,7 @@ do -- DETECTION_BASE -- Calculate radar blue probability if self.RadarBlur then + BASE:I("RadarBlur") local minheight = self.RadarBlurMinHeight or 250 -- meters local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall @@ -732,8 +733,11 @@ do -- DETECTION_BASE local unit = UNIT:FindByName(DetectedObjectName) if unit and unit:IsAlive() then local AGL = unit:GetAltitude(true) - if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end + BASE:I("Unit "..DetectedObjectName.." is at "..AGL.."m.") + BASE:I(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur)) if fblur > thresblur then DetectionAccepted = false end + if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end + BASE:I("Detection Accepted = "..tostring(DetectionAccepted)) end end From 87f1a5ed0d303a4a128abec56fc694e8f35c2d8f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 10 Dec 2023 11:58:19 +0100 Subject: [PATCH 22/95] # DETECTION * Option to make Radar Blue decision visible in logs (self.debug) and/or screen (self.verbose) --- Moose Development/Moose/Functional/Detection.lua | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index a33e7f7b0..09ad78104 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -46,6 +46,8 @@ do -- DETECTION_BASE -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. -- @field #number DetectionRun + -- @field #boolean debug + -- @field #boolean verbose -- @extends Core.Fsm#FSM --- Defines the core functions to administer detected objects. @@ -273,6 +275,8 @@ do -- DETECTION_BASE DetectedObjectsIdentified = {}, DetectedItems = {}, DetectedItemsByIndex = {}, + debug = false, + verbose = false, } --- @@ -721,10 +725,10 @@ do -- DETECTION_BASE end end - -- Calculate radar blue probability + -- Calculate radar blur probability if self.RadarBlur then - BASE:I("RadarBlur") + MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose) local minheight = self.RadarBlurMinHeight or 250 -- meters local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall @@ -733,11 +737,11 @@ do -- DETECTION_BASE local unit = UNIT:FindByName(DetectedObjectName) if unit and unit:IsAlive() then local AGL = unit:GetAltitude(true) - BASE:I("Unit "..DetectedObjectName.." is at "..AGL.."m.") - BASE:I(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur)) + MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m.",10):ToLogIf(self.debug):ToAllIf(self.verbose) + MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose) if fblur > thresblur then DetectionAccepted = false end - if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end - BASE:I("Detection Accepted = "..tostring(DetectionAccepted)) + if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end + MESSAGE:New("Detection Accepted = "..tostring(DetectionAccepted),10):ToLogIf(self.debug):ToAllIf(self.verbose) end end From c089e56060974539e58a346665f7536a2898c4cf Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 10 Dec 2023 14:37:41 +0100 Subject: [PATCH 23/95] # DETECTION * Make the radar blur less effective when under 20km distance --- Moose Development/Moose/Functional/Detection.lua | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 09ad78104..1acd82ad2 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -46,8 +46,6 @@ do -- DETECTION_BASE -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. -- @field #number DetectionRun - -- @field #boolean debug - -- @field #boolean verbose -- @extends Core.Fsm#FSM --- Defines the core functions to administer detected objects. @@ -275,8 +273,6 @@ do -- DETECTION_BASE DetectedObjectsIdentified = {}, DetectedItems = {}, DetectedItemsByIndex = {}, - debug = false, - verbose = false, } --- @@ -725,19 +721,24 @@ do -- DETECTION_BASE end end - -- Calculate radar blur probability + -- Calculate radar blue probability if self.RadarBlur then MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose) local minheight = self.RadarBlurMinHeight or 250 -- meters local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall + local dist = math.floor(Distance) + if dist <= 20 then + thresheight = (((dist*dist)/400)*thresheight) + thresblur = (((dist*dist)/400)*thresblur) + end local fheight = math.floor(math.random(1,10000)/100) local fblur = math.floor(math.random(1,10000)/100) local unit = UNIT:FindByName(DetectedObjectName) if unit and unit:IsAlive() then local AGL = unit:GetAltitude(true) - MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m.",10):ToLogIf(self.debug):ToAllIf(self.verbose) + MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m. Distance "..math.floor(Distance).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose) MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose) if fblur > thresblur then DetectionAccepted = false end if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end From 230d9d82bfe1ba3cc6f57c2ab1a689d98eb53b0f Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:04:35 +0100 Subject: [PATCH 24/95] Update Detection.lua (#2063) # RadarBlur - make burn-through limit configureable --- Moose Development/Moose/Functional/Detection.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 1acd82ad2..a71e194ed 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -721,7 +721,7 @@ do -- DETECTION_BASE end end - -- Calculate radar blue probability + -- Calculate radar blur probability if self.RadarBlur then MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose) @@ -729,9 +729,9 @@ do -- DETECTION_BASE local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall local dist = math.floor(Distance) - if dist <= 20 then - thresheight = (((dist*dist)/400)*thresheight) - thresblur = (((dist*dist)/400)*thresblur) + if dist <= self.RadarBlurClosing then + thresheight = (((dist*dist)/self.RadarBlurClosingSquare)*thresheight) + thresblur = (((dist*dist)/self.RadarBlurClosingSquare)*thresblur) end local fheight = math.floor(math.random(1,10000)/100) local fblur = math.floor(math.random(1,10000)/100) @@ -1051,12 +1051,15 @@ do -- DETECTION_BASE -- @param #number minheight Minimum flight height to be detected, in meters AGL (above ground) -- @param #number thresheight Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance) -- @param #number thresblur Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found) + -- @param #number closing Closing-in in km - the limit of km from which on it becomes increasingly difficult to escape radar detection if flying towards the radar position. Should be about 1/3 of the radar detection radius in kilometers, defaults to 20. -- @return #DETECTION_BASE self - function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur) + function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur,closing) self.RadarBlur = true self.RadarBlurMinHeight = minheight or 250 -- meters self.RadarBlurThresHeight = thresheight or 90 -- 10% chance to find a low flying group self.RadarBlurThresBlur = thresblur or 85 -- 25% chance to escape the radar overall + self.RadarBlurClosing = closing or 20 -- 20km + self.RadarBlurClosingSquare = self.RadarBlurClosing * self.RadarBlurClosing return self end From b8d44643c1e2b87f0af3cf29fb4178973d63d5da Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:49:00 +0100 Subject: [PATCH 25/95] Update Detection.lua (#2063) (#2064) # RadarBlur - make burn-through limit configureable --- Moose Development/Moose/Functional/Detection.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 1acd82ad2..a71e194ed 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -721,7 +721,7 @@ do -- DETECTION_BASE end end - -- Calculate radar blue probability + -- Calculate radar blur probability if self.RadarBlur then MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose) @@ -729,9 +729,9 @@ do -- DETECTION_BASE local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall local dist = math.floor(Distance) - if dist <= 20 then - thresheight = (((dist*dist)/400)*thresheight) - thresblur = (((dist*dist)/400)*thresblur) + if dist <= self.RadarBlurClosing then + thresheight = (((dist*dist)/self.RadarBlurClosingSquare)*thresheight) + thresblur = (((dist*dist)/self.RadarBlurClosingSquare)*thresblur) end local fheight = math.floor(math.random(1,10000)/100) local fblur = math.floor(math.random(1,10000)/100) @@ -1051,12 +1051,15 @@ do -- DETECTION_BASE -- @param #number minheight Minimum flight height to be detected, in meters AGL (above ground) -- @param #number thresheight Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance) -- @param #number thresblur Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found) + -- @param #number closing Closing-in in km - the limit of km from which on it becomes increasingly difficult to escape radar detection if flying towards the radar position. Should be about 1/3 of the radar detection radius in kilometers, defaults to 20. -- @return #DETECTION_BASE self - function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur) + function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur,closing) self.RadarBlur = true self.RadarBlurMinHeight = minheight or 250 -- meters self.RadarBlurThresHeight = thresheight or 90 -- 10% chance to find a low flying group self.RadarBlurThresBlur = thresblur or 85 -- 25% chance to escape the radar overall + self.RadarBlurClosing = closing or 20 -- 20km + self.RadarBlurClosingSquare = self.RadarBlurClosing * self.RadarBlurClosing return self end From cd4fb0c6c5723deceba69dfb26826508bfca35de Mon Sep 17 00:00:00 2001 From: ttrebuchon Date: Mon, 11 Dec 2023 05:50:09 -0500 Subject: [PATCH 26/95] Add missing files to Moose.files: (#2062) Utilities/Socket.lua Core/Astar.lua Core/MarkerOps_Base.lua Functional/ZoneGoalCargo.lua Ops/OpsZone.lua Ops/ArmyGroup.lua Ops/OpsTransport.lua Ops/Target.lua --- Moose Setup/Moose.files | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index eb513c320..90a6e761d 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -5,6 +5,7 @@ Utilities/Profiler.lua Utilities/Templates.lua Utilities/STTS.lua Utilities/FiFo.lua +Utilities/Socket.lua Core/Base.lua Core/Beacon.lua @@ -32,6 +33,8 @@ Core/TextAndSound.lua Core/Condition.lua Core/Pathline.lua Core/ClientMenu.lua +Core/Astar.lua +Core/MarkerOps_Base.lua Wrapper/Object.lua Wrapper/Identifiable.lua @@ -79,6 +82,7 @@ Functional/Shorad.lua Functional/Autolase.lua Functional/AICSAR.lua Functional/AmmoTruck.lua +Functional/ZoneGoalCargo.lua Ops/Airboss.lua Ops/RecoveryTanker.lua @@ -107,6 +111,10 @@ Ops/FlightControl.lua Ops/PlayerTask.lua Ops/PlayerRecce.lua Ops/EasyGCICAP.lua +Ops/OpsZone.lua +Ops/ArmyGroup.lua +Ops/OpsTransport.lua +Ops/Target.lua AI/AI_Balancer.lua AI/AI_Air.lua From f837e9dec7522605ce60b31bbccfcb092c839b8b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 12 Dec 2023 10:53:37 +0100 Subject: [PATCH 27/95] #COORDINATE * Added functions to create a COORDINATE from MGRS --- Moose Development/Moose/Core/Point.lua | 52 ++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 3911a52f3..9cf13ba3e 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -25,7 +25,7 @@ do -- COORDINATE - --- @type COORDINATE + -- @type COORDINATE -- @field #string ClassName Name of the class -- @field #number x Component of the 3D vector. -- @field #number y Component of the 3D vector. @@ -2551,7 +2551,7 @@ do -- COORDINATE Offset=Offset or 2 - -- Measurement of visibility should not be from the ground, so Adding a hypotethical 2 meters to each Coordinate. + -- Measurement of visibility should not be from the ground, so Adding a hypothetical 2 meters to each Coordinate. local FromVec3 = self:GetVec3() FromVec3.y = FromVec3.y + Offset @@ -2952,10 +2952,10 @@ do -- COORDINATE end -- corrected Track to be direction of travel of bogey (self in this case) - local track = "Maneuver" - - if self.Heading then - track = UTILS.BearingToCardinal(self.Heading) or "North" + local track = "Maneuver" + + if self.Heading then + track = UTILS.BearingToCardinal(self.Heading) or "North" end if rangeNM > 3 then @@ -3100,6 +3100,44 @@ do -- COORDINATE local MGRS = coord.LLtoMGRS( lat, lon ) return "MGRS " .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy ) end + + --- Provides a COORDINATE from an MGRS String + -- @param #COORDINATE self + -- @param #string MGRSString MGRS String, e.g. "MGRS 37T DK 12345 12345" + -- @return #COORDINATE self + function COORDINATE:NewFromMGRSString( MGRSString ) + local myparts = UTILS.Split(MGRSString," ") + UTILS.PrintTableToLog(myparts,1) + local MGRS = { + UTMZone = myparts[2], + MGRSDigraph = myparts[3], + Easting = tonumber(myparts[4]), + Northing = tonumber(myparts[5]), + } + local lat, lon = coord.MGRStoLL(MGRS) + local point = coord.LLtoLO(lat, lon, 0) + local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z}) + return coord + end + + --- Provides a COORDINATE from an MGRS Coordinate + -- @param #COORDINATE self + -- @param #string UTMZone UTM Zone, e.g. "37T" + -- @param #string MGRSDigraph Digraph, e.g. "DK" + -- @param #number Easting Meters easting + -- @param #number Northing Meters northing + -- @return #COORDINATE self + function COORDINATE:NewFromMGRS( UTMZone, MGRSDigraph, Easting, Northing ) + local MGRS = { + UTMZone = UTMZone, + MGRSDigraph = MGRSDigraph, + Easting = Easting, + Northing = Northing, + } + local lat, lon = coord.MGRStoLL(MGRS) + local point = coord.LLtoLO(lat, lon, 0) + local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z}) + end --- Provides a coordinate string of the point, based on a coordinate format system: -- * Uses default settings in COORDINATE. @@ -3613,7 +3651,7 @@ end do -- POINT_VEC2 - --- @type POINT_VEC2 + -- @type POINT_VEC2 -- @field DCS#Distance x The x coordinate in meters. -- @field DCS#Distance y the y coordinate in meters. -- @extends Core.Point#COORDINATE From 2a7213c1b74da5d8cbca9db153a05e1ccf061c76 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 12 Dec 2023 10:54:16 +0100 Subject: [PATCH 28/95] xxx --- Moose Development/Moose/Ops/Intelligence.lua | 132 +++++++++++++++++-- 1 file changed, 119 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index 0f71a234e..6e900790f 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -26,6 +26,7 @@ -- @field #table filterCategoryGroup Filter for group categories. -- @field Core.Set#SET_ZONE acceptzoneset Set of accept zones. If defined, only contacts in these zones are considered. -- @field Core.Set#SET_ZONE rejectzoneset Set of reject zones. Contacts in these zones are not considered, even if they are in accept zones. +-- @field Core.Set#SET_ZONE conflictzoneset Set of conflict zones. Contacts in these zones are considered, even if they are not in accept zones or if they are in reject zones. -- @field #table Contacts Table of detected items. -- @field #table ContactsLost Table of lost detected items. -- @field #table ContactsUnknown Table of new detected items. @@ -159,13 +160,12 @@ INTEL.Ctype={ --- INTEL class version. -- @field #string version -INTEL.version="0.3.5" +INTEL.version="0.3.6" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Make forget times user input. Currently these are hard coded. -- TODO: Add min cluster size. Only create new clusters if they have a certain group size. -- TODO: process detected set asynchroniously for better performance. -- DONE: Add statics. @@ -266,6 +266,7 @@ function INTEL:New(DetectionSet, Coalition, Alias) self:SetForgetTime() self:SetAcceptZones() self:SetRejectZones() + self:SetConflictZones() ------------------------ --- Pseudo Functions --- @@ -416,7 +417,7 @@ function INTEL:RemoveAcceptZone(AcceptZone) end --- Set reject zones. Contacts detected in this/these zone(s) are rejected and not reported by the detection. --- Note that reject zones overrule accept zones, i.e. if a unit is inside and accept zone and inside a reject zone, it is rejected. +-- Note that reject zones overrule accept zones, i.e. if a unit is inside an accept zone and inside a reject zone, it is rejected. -- @param #INTEL self -- @param Core.Set#SET_ZONE RejectZoneSet Set of reject zone(s). -- @return #INTEL self @@ -426,7 +427,7 @@ function INTEL:SetRejectZones(RejectZoneSet) end --- Add a reject zone. Contacts detected in this zone are rejected and not reported by the detection. --- Note that reject zones overrule accept zones, i.e. if a unit is inside and accept zone and inside a reject zone, it is rejected. +-- Note that reject zones overrule accept zones, i.e. if a unit is inside an accept zone and inside a reject zone, it is rejected. -- @param #INTEL self -- @param Core.Zone#ZONE RejectZone Add a zone to the reject zone set. -- @return #INTEL self @@ -444,6 +445,36 @@ function INTEL:RemoveRejectZone(RejectZone) return self end +--- Set conflict zones. Contacts detected in this/these zone(s) are reported by the detection. +-- Note that conflict zones overrule all other zones, i.e. if a unit is outside of an accept zone and inside a reject zone, it is still reported if inside a conflict zone. +-- @param #INTEL self +-- @param Core.Set#SET_ZONE ConflictZoneSet Set of conflict zone(s). +-- @return #INTEL self +function INTEL:SetConflictZones(ConflictZoneSet) + self.conflictzoneset=ConflictZoneSet or SET_ZONE:New() + return self +end + +--- Add a conflict zone. Contacts detected in this zone are conflicted and not reported by the detection. +-- Note that conflict zones overrule all other zones, i.e. if a unit is outside of an accept zone and inside a reject zone, it is still reported if inside a conflict zone. +-- @param #INTEL self +-- @param Core.Zone#ZONE ConflictZone Add a zone to the conflict zone set. +-- @return #INTEL self +function INTEL:AddConflictZone(ConflictZone) + self.conflictzoneset:AddZone(ConflictZone) + return self +end + +--- Remove a conflict zone from the conflict zone set. +-- Note that conflict zones overrule all other zones, i.e. if a unit is outside of an accept zone and inside a reject zone, it is still reported if inside a conflict zone. +-- @param #INTEL self +-- @param Core.Zone#ZONE ConflictZone Remove a zone from the conflict zone set. +-- @return #INTEL self +function INTEL:RemoveConflictZone(ConflictZone) + self.conflictzoneset:Remove(ConflictZone:GetName(), true) + return self +end + --- **OBSOLETE, will be removed in next version!** Set forget contacts time interval. -- Previously known contacts that are not detected any more, are "lost" after this time. -- This avoids fast oscillations between a contact being detected and undetected. @@ -481,6 +512,33 @@ function INTEL:SetFilterCategory(Categories) return self end +--- Method to make the radar detection less accurate, e.g. for WWII scenarios. +-- @param #INTEL self +-- @param #number minheight Minimum flight height to be detected, in meters AGL (above ground) +-- @param #number thresheight Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance) +-- @param #number thresblur Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found) +-- @param #number closing Closing-in in km - the limit of km from which on it becomes increasingly difficult to escape radar detection if flying towards the radar position. Should be about 1/3 of the radar detection radius in kilometers, defaults to 20. +-- @return #INTEL self +function INTEL:SetRadarBlur(minheight,thresheight,thresblur,closing) + self.RadarBlur = true + self.RadarBlurMinHeight = minheight or 250 -- meters + self.RadarBlurThresHeight = thresheight or 90 -- 10% chance to find a low flying group + self.RadarBlurThresBlur = thresblur or 85 -- 25% chance to escape the radar overall + self.RadarBlurClosing = closing or 20 -- 20km + self.RadarBlurClosingSquare = self.RadarBlurClosing * self.RadarBlurClosing + return self +end + +--- Set the accept range in kilometers from each of the recce. Only object closer than this range will be detected. +-- @param #INTEL self +-- @param #number Range Range in kilometers +-- @return #INTEL self +function INTEL:SetAcceptRange(Range) + self.RadarAcceptRange = true + self.RadarAcceptRangeKilometers = Range or 75 + return self +end + --- Filter group categories. Valid categories are: -- -- * Group.Category.AIRPLANE @@ -780,7 +838,19 @@ function INTEL:UpdateIntel() local remove={} for unitname,_unit in pairs(DetectedUnits) do local unit=_unit --Wrapper.Unit#UNIT - + + local inconflictzone=false + -- Check if unit is in any of the conflict zones. + if self.conflictzoneset:Count()>0 then + for _,_zone in pairs(self.conflictzoneset.Set) do + local zone=_zone --Core.Zone#ZONE + if unit:IsInZone(zone) then + inconflictzone=true + break + end + end + end + -- Check if unit is in any of the accept zones. if self.acceptzoneset:Count()>0 then local inzone=false @@ -793,7 +863,7 @@ function INTEL:UpdateIntel() end -- Unit is not in accept zone ==> remove! - if not inzone then + if (not inzone) and (not inconflictzone) then table.insert(remove, unitname) end end @@ -810,7 +880,7 @@ function INTEL:UpdateIntel() end -- Unit is inside a reject zone ==> remove! - if inzone then + if inzone and (not inconflictzone) then table.insert(remove, unitname) end end @@ -1037,7 +1107,7 @@ function INTEL:CreateDetectedItems(DetectedGroups, DetectedStatics, RecceDetecti end --- (Internal) Return the detected target groups of the controllable as a @{SET_GROUP}. --- The optional parametes specify the detection methods that can be applied. +-- The optional parameters 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 #INTEL self -- @param Wrapper.Unit#UNIT Unit The unit detecting. @@ -1053,6 +1123,7 @@ function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisua -- Get detected DCS units. local reccename = Unit:GetName() + local detectedtargets=Unit:GetDetectedTargets(DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) for DetectionObjectID, Detection in pairs(detectedtargets or {}) do @@ -1071,11 +1142,47 @@ function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisua if status then local unit=UNIT:FindByName(name) - + if unit and unit:IsAlive() then - DetectedUnits[name]=unit - RecceDetecting[name]=reccename - self:T(string.format("Unit %s detect by %s", name, reccename)) + local DetectionAccepted = true + + if self.RadarAcceptRange then + local reccecoord = Unit:GetCoordinate() + local coord = unit:GetCoordinate() + local dist = math.floor(coord:Get2DDistance(reccecoord)/1000) -- km + if dist > self.RadarAcceptRangeKilometers then DetectionAccepted = false end + end + + if self.RadarBlur then + local reccecoord = Unit:GetCoordinate() + local coord = unit:GetCoordinate() + local dist = math.floor(coord:Get2DDistance(reccecoord)/1000) -- km + local AGL = unit:GetAltitude(true) + local minheight = self.RadarBlurMinHeight or 250 -- meters + local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group + local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall + --local dist = math.floor(Distance) + if dist <= self.RadarBlurClosing then + thresheight = (((dist*dist)/self.RadarBlurClosingSquare)*thresheight) + thresblur = (((dist*dist)/self.RadarBlurClosingSquare)*thresblur) + end + local fheight = math.floor(math.random(1,10000)/100) + local fblur = math.floor(math.random(1,10000)/100) + if fblur > thresblur then DetectionAccepted = false end + if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end + if self.debug or self.verbose > 1 then + MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose>1) + MESSAGE:New("Unit "..name.." is at "..math.floor(AGL).."m. Distance "..math.floor(dist).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose>1) + MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose>1) + MESSAGE:New("Detection Accepted = "..tostring(DetectionAccepted),10):ToLogIf(self.debug):ToAllIf(self.verbose>1) + end + end + + if DetectionAccepted then + DetectedUnits[name]=unit + RecceDetecting[name]=reccename + self:T(string.format("Unit %s detect by %s", name, reccename)) + end else if self.detectStatics then local static=STATIC:FindByName(name, false) @@ -1093,7 +1200,6 @@ function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisua end end end - end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 8382eb9cd80cc0144b9cb4f407b9383086035cfe Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 13 Dec 2023 19:21:28 +0100 Subject: [PATCH 29/95] Update Range.lua (#2066) MSRS config compatibility --- Moose Development/Moose/Functional/Range.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index e238914a9..13fb54c04 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -1207,18 +1207,18 @@ end -- @return #RANGE self function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey) - if PathToSRS then + if PathToSRS or MSRS.path then self.useSRS=true - self.controlmsrs=MSRS:New(PathToSRS, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0) - self.controlmsrs:SetPort(Port) + self.controlmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0) + self.controlmsrs:SetPort(Port or MSRS.port) self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE) self.controlmsrs:SetLabel("RANGEC") self.controlsrsQ = MSRSQUEUE:New("CONTROL") - self.instructmsrs=MSRS:New(PathToSRS, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0) - self.instructmsrs:SetPort(Port) + self.instructmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0) + self.instructmsrs:SetPort(Port or MSRS.port) self.instructmsrs:SetCoalition(Coalition or coalition.side.BLUE) self.instructmsrs:SetLabel("RANGEI") self.instructsrsQ = MSRSQUEUE:New("INSTRUCT") From 42fd2322d233a061faf0dce204b2c47ae737ba49 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 13 Dec 2023 19:33:07 +0100 Subject: [PATCH 30/95] Update Range.lua (#2066) (#2067) MSRS config compatibility --- Moose Development/Moose/Functional/Range.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index e238914a9..13fb54c04 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -1207,18 +1207,18 @@ end -- @return #RANGE self function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey) - if PathToSRS then + if PathToSRS or MSRS.path then self.useSRS=true - self.controlmsrs=MSRS:New(PathToSRS, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0) - self.controlmsrs:SetPort(Port) + self.controlmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0) + self.controlmsrs:SetPort(Port or MSRS.port) self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE) self.controlmsrs:SetLabel("RANGEC") self.controlsrsQ = MSRSQUEUE:New("CONTROL") - self.instructmsrs=MSRS:New(PathToSRS, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0) - self.instructmsrs:SetPort(Port) + self.instructmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0) + self.instructmsrs:SetPort(Port or MSRS.port) self.instructmsrs:SetCoalition(Coalition or coalition.side.BLUE) self.instructmsrs:SetLabel("RANGEI") self.instructsrsQ = MSRSQUEUE:New("INSTRUCT") From 68548f45815f682a81cecf4538b242e0f883238e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 14 Dec 2023 11:12:14 +0100 Subject: [PATCH 31/95] #COORDINATE * Fix for NewFromMGRS for less precise coordinates (below level 5) --- Moose Development/Moose/Core/Point.lua | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 9cf13ba3e..a3c83da1f 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -24,7 +24,8 @@ do -- COORDINATE - + + --- -- @type COORDINATE -- @field #string ClassName Name of the class -- @field #number x Component of the 3D vector. @@ -3107,13 +3108,16 @@ do -- COORDINATE -- @return #COORDINATE self function COORDINATE:NewFromMGRSString( MGRSString ) local myparts = UTILS.Split(MGRSString," ") - UTILS.PrintTableToLog(myparts,1) + local northing = tostring(myparts[5]) or "" + local easting = tostring(myparts[4]) or "" + if string.len(easting) < 5 then easting = easting..string.rep("0",5-string.len(easting)) end + if string.len(northing) < 5 then northing = northing..string.rep("0",5-string.len(northing)) end local MGRS = { UTMZone = myparts[2], MGRSDigraph = myparts[3], - Easting = tonumber(myparts[4]), - Northing = tonumber(myparts[5]), - } + Easting = easting, + Northing = northing, + } local lat, lon = coord.MGRStoLL(MGRS) local point = coord.LLtoLO(lat, lon, 0) local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z}) @@ -3124,10 +3128,12 @@ do -- COORDINATE -- @param #COORDINATE self -- @param #string UTMZone UTM Zone, e.g. "37T" -- @param #string MGRSDigraph Digraph, e.g. "DK" - -- @param #number Easting Meters easting - -- @param #number Northing Meters northing + -- @param #string Easting Meters easting - string in order to allow for leading zeros, e.g. "01234". Should be 5 digits. + -- @param #string Northing Meters northing - string in order to allow for leading zeros, e.g. "12340". Should be 5 digits. -- @return #COORDINATE self function COORDINATE:NewFromMGRS( UTMZone, MGRSDigraph, Easting, Northing ) + if string.len(Easting) < 5 then Easting = Easting..string.rep("0",5-string.len(Easting) )end + if string.len(Northing) < 5 then Northing = Northing..string.rep("0",5-string.len(Northing) )end local MGRS = { UTMZone = UTMZone, MGRSDigraph = MGRSDigraph, From b31fc3ed446c686e42d73fc66c377e6ab3c2286c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 14 Dec 2023 11:12:50 +0100 Subject: [PATCH 32/95] Limit Attacked() to have at least the capture threatlevel --- Moose Development/Moose/Ops/OpsZone.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/OpsZone.lua b/Moose Development/Moose/Ops/OpsZone.lua index 780b389c0..ee83650aa 100644 --- a/Moose Development/Moose/Ops/OpsZone.lua +++ b/Moose Development/Moose/Ops/OpsZone.lua @@ -97,7 +97,7 @@ OPSZONE.ZoneType={ --- OPSZONE class version. -- @field #string version -OPSZONE.version="0.6.0" +OPSZONE.version="0.6.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -1277,7 +1277,7 @@ function OPSZONE:EvaluateZone() if Nblu>0 then - if not self:IsAttacked() then + if not self:IsAttacked() and self.Tnut>=self.threatlevelCapture then self:Attacked(coalition.side.BLUE) end @@ -1329,7 +1329,7 @@ function OPSZONE:EvaluateZone() if Nred>0 then - if not self:IsAttacked() then + if not self:IsAttacked() and self.Tnut>=self.threatlevelCapture then -- Red is attacking blue zone. self:Attacked(coalition.side.RED) end From 55ffe37a794e7dba9eeaf3bb5fee618ec2989a60 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 14 Dec 2023 12:42:13 +0100 Subject: [PATCH 33/95] #USERSOUND * Added USERSOUND:ToClient( Client, Delay ) --- Moose Development/Moose/Sound/UserSound.lua | 22 ++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Sound/UserSound.lua b/Moose Development/Moose/Sound/UserSound.lua index cbccffe12..1c753a538 100644 --- a/Moose Development/Moose/Sound/UserSound.lua +++ b/Moose Development/Moose/Sound/UserSound.lua @@ -137,7 +137,7 @@ do -- UserSound return self end - --- Play the usersound to the given @{Wrapper.Unit}. + --- Play the usersound to the given @{Wrapper.Unit}. -- @param #USERSOUND self -- @param Wrapper.Unit#UNIT Unit The @{Wrapper.Unit} to play the usersound to. -- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0. @@ -159,4 +159,24 @@ do -- UserSound return self end + + --- Play the usersound to the given @{Wrapper.Unit}. + -- @param #USERSOUND self + -- @param Wrapper.Client#CLIENT The @{Wrapper.Client} to play the usersound to. + -- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0. + -- @return #USERSOUND The usersound instance. + -- @usage + -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) + -- local PlayerUnit = CLIENT:FindByPlayerName("Karl Heinz")-- Search for the active client with playername "Karl Heinz", a human player. + -- BlueVictory:ToClient( PlayerUnit ) -- Play the victory sound to the player unit. + -- + function USERSOUND:ToClient( Client, Delay ) + Delay=Delay or 0 + if Delay>0 then + SCHEDULER:New(nil, USERSOUND.ToClient,{self, Client}, Delay) + else + trigger.action.outSoundForUnit( Client:GetID(), self.UserSoundFileName ) + end + return self + end end \ No newline at end of file From e84e16f58bf975dc7bdd9787ad227f286010efe0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 14 Dec 2023 12:43:36 +0100 Subject: [PATCH 34/95] xx --- Moose Development/Moose/Sound/UserSound.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Sound/UserSound.lua b/Moose Development/Moose/Sound/UserSound.lua index 1c753a538..ceaeeb6e2 100644 --- a/Moose Development/Moose/Sound/UserSound.lua +++ b/Moose Development/Moose/Sound/UserSound.lua @@ -160,7 +160,7 @@ do -- UserSound end - --- Play the usersound to the given @{Wrapper.Unit}. + --- Play the usersound to the given @{Wrapper.Client}. -- @param #USERSOUND self -- @param Wrapper.Client#CLIENT The @{Wrapper.Client} to play the usersound to. -- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0. From bda4efc634c804ea01ddebc97163477932a99985 Mon Sep 17 00:00:00 2001 From: kaltokri Date: Fri, 15 Dec 2023 14:08:45 +0100 Subject: [PATCH 35/95] Added page "Create your own Hello world" --- docs/beginner/demo-missions.md | 13 ++ docs/beginner/hello-world-build.md | 169 +++++++++++++++++- .../dcs-triggers-once-conditions-conf.png | Bin 0 -> 11728 bytes 3 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 docs/beginner/demo-missions.md create mode 100644 docs/images/beginner/dcs-triggers-once-conditions-conf.png diff --git a/docs/beginner/demo-missions.md b/docs/beginner/demo-missions.md new file mode 100644 index 000000000..073ae42fd --- /dev/null +++ b/docs/beginner/demo-missions.md @@ -0,0 +1,13 @@ +--- +parent: Beginner +nav_order: 04 +--- + +# Demo missions +{: .no_toc } + +1. Table of contents +{:toc} + +{: .warning } +> THIS DOCUMENT IS STILL WORK IN PROGRESS! diff --git a/docs/beginner/hello-world-build.md b/docs/beginner/hello-world-build.md index fde7e63da..1f7509abd 100644 --- a/docs/beginner/hello-world-build.md +++ b/docs/beginner/hello-world-build.md @@ -4,6 +4,171 @@ nav_order: 03 --- # Create your own Hello world +{: .no_toc } -{: .warning } -> THIS DOCUMENT IS STILL WORK IN PROGRESS! +1. Table of contents +{:toc} + +This page will lead you step by step through the process of creating a mission +with MOOSE. This time we include a simple mission script, which sends only +a "Hello world" message to all players. But the steps are the same to add +another mission script, which will do whatever class(es) you want to use. + +## Create Mission script + +At first we will create the mission script. It is a simple text file and can be +changed with a lot of different tools. Theoretically even the Microsoft Notepad +editor can be used. But it lacks a lot of features, which helps you to avoid +errors. + +For this guide we suggest you to download, install and use [Notepad++]{:target="_blank"}. + +{: .important } +> Windows hides filename extensions by default. So when you create a text file +> and name it `hello-world.lua` it's name is `hello-world.lua.txt` in reality. +> You must activate the display of the file name extension. +> Open a `File Explorer`, switch to menu `View` and find the option +> `File name extensions` in the section `Show/hide`. Activate it. + +- Open a File Explorer. +- Go to the subfolder `Missions` of your [Saved Games folder]{:target="_blank"}. +- Create a new text file and name it `hello-world.lua`. +- Add the following content and save the file: + + `MESSAGE:New( "Hello World! This messages is printed by MOOSE", 35, "INFO" ):ToAll()` + +## Get Moose + +To download Moose click on the following link: + +- [Moose_.lua from develop branch]{:target="_blank"} + +Press `Ctrl + S` to save the file on your hard disk next to your mission script. + +## Create the mission + +- Start DCS. +- In the main menu choose `MISSION EDITOR`. +- Click on `create new mission`. +- In the dialog `NEW MISSION SETTINGS`: + - Choose map `Caucasus`. + - In the drop box upper left choose `Modern` as coalition preset. + - Click on `OK`. +- The mission editor will load with a fresh new and empty mission. +- Click on `File` in the menu bar and `SAVE` or Press `Ctrl + S`. +- Open `My Missions` and save the file with the name `hello-world.miz`. + +## Add Moose to the mission + +- On the left side activate `TRIGGERS`: + + ![dcs-triggers-toolbar.png](../images/beginner/dcs-triggers-toolbar.png) + +- On the right side the `TRIGGERS` dialog opens with a lot of options. +- Click on `NEW`, choose `4 MISSION START` as **TYPE**. +- Give it the `Load MOOSE` as **NAME**. +- Leave the **EVENT** option set to `NO EVENT`. +- Optional: Choose a color for easy recognition (e.g. yellow). + + ![dcs-triggers-mission-start-conf.png](../images/beginner/dcs-triggers-mission-start-conf.png) + +- In the middle part the `CONDITIONS` will be shown. + For this trigger we do not configure any conditions. + + ![dcs-triggers-mission-start-conditions.png](../images/beginner/dcs-triggers-mission-start-conditions.png) + + {: .important } + > The trigger type `4 MISSION START` does not support `CONDITIONS`.
+ > So `CONDITIONS` must left blank when using it.
+ > **If you add a condition the trigger will never be executed!** + + ![dcs-triggers-mission-start-actions-conf.png](../images/beginner/dcs-triggers-mission-start-actions-conf.png) + +- On the right side `ACTIONS` is shown. +- We need to click on `NEW`. +- Choose **ACTION** `Do SCRIPT FILE` and ignore all other actions. +- Click **OPEN** and navigate to the downloaded `Moose_.lua` file. +- The result should look like this: + + ![dcs-triggers-mission-start-actions.png](../images/beginner/dcs-triggers-mission-start-actions.png) + +## Add the mission script + +- Click on `NEW`, choose `1 ONCE` as **TYPE**. +- Give it the `Load Mission Script` as **NAME**. +- Leave the **EVENT** option set to `NO EVENT`. +- Optional: Choose a color for easy recognition (e.g. green). +- The result should look like this: + + ![dcs-triggers-once-conf.png](../images/beginner/dcs-triggers-once-conf.png) + +- Switch to the middle part, the `CONDITIONS` section.
+ For this trigger we add one condition: + + ![dcs-triggers-once-conditions.png](../images/beginner/dcs-triggers-once-conditions-conf.png) + +- The combination of `1 ONCE` with `TIME MORE(1)` will ensure, that the mission + script is executed 1 second after the mission is started. + +- On the right side under `ACTIONS` you need to add the script: +- Click on `NEW`. +- Choose **ACTION** `Do SCRIPT FILE`. +- Click **OPEN** and navigate to the created `hello-world.lua` file. + +{: .important } +> Most important is the fact, that the mission script (`hello-world.lua`) +> is executed **after** `Moose_.lua`, because the mission script needs the +> classes defined in `Moose_.lua`. And they are only available when `Moose_.lua` +> is executed before the mission script. + +## Test the mission + +- Save the mission again. +- Click on the green **Fly mission** cirlce on the left tool side bar. +- It is an empty mission, so skip `BRIEFING` with `START` and then `FLY`. +- You spawn as a spectator. After some seconds you will see this message in + the upper right corner: + + ![dcs-message.jpg](../images/beginner/dcs-message.jpg) + +This is the same result as already seen in the last chapter, but this time you +have create everything on your own. + +{: .note } +> You can use this mission as a template for your own missions. So you don't +> need to do alle these steps again and again. + +## Update mission script + +- Open the `hello-world.lua` with Notepad++ again. +- Change the text a little bit, like `Hello Dude! ...` and save the file. +- Run the mission again. +- The text will not be changed in the mission. Why? + The mission editor copies the script into the mission file when you add it. + Ever change on the script file on your hard disk is not recognized by mission editor. + You have to add the file after each change again. + +- On the left side of the `TRIGGERS` dialog click on `Load Mission Script`. +- On the right side under `ACTIONS` you need to add the script again: + - Click **OPEN** and navigate to the created `hello-world.lua` file. +- Save the mission and test it again. +- Now the new text should be shown. + +## Update Moose + +Moose is constantly being developed so that new functionallity is added or +existing errors are corrected. Also from time to time changes of the DCS +scripting engine comes with a new DCS version. It may therefore be useful or +necessary to update Moose. + +- To update Moose download it again and add it again in the same way you did + with the mission script in the last step. + +## Next step + +Let's move on to the [demo missions]. + +[Notepad++]: https://notepad-plus-plus.org/downloads/ +[Saved Games folder]: tipps-and-tricks.md#find-the-saved-games-folder +[Moose_.lua from develop branch]: https://raw.githubusercontent.com/FlightControl-Master/MOOSE_INCLUDE/develop/Moose_Include_Static/Moose_.lua +[demo missions]: demo-missions.md diff --git a/docs/images/beginner/dcs-triggers-once-conditions-conf.png b/docs/images/beginner/dcs-triggers-once-conditions-conf.png new file mode 100644 index 0000000000000000000000000000000000000000..e751ea40ff51d2066e06b7a5b10522d0d7051005 GIT binary patch literal 11728 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 literal 0 HcmV?d00001 From 0ae9be49daacf38be2efbeef7224d3e3ceace495 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 16 Dec 2023 09:31:44 +0100 Subject: [PATCH 36/95] Update Range.lua - Fixed random good by phrase --- Moose Development/Moose/Functional/Range.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 13fb54c04..8da132f14 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -2187,7 +2187,7 @@ function RANGE:onafterExitRange( From, Event, To, player ) local text = "You left the bombing range zone. " - local r=math.random(2) + local r=math.random(5) if r==1 then text=text.."Have a nice day!" From 97161627397dacf24bd16bb7cc2334080719c8c8 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 17 Dec 2023 22:55:31 +0100 Subject: [PATCH 37/95] Update SRS.lua --- Moose Development/Moose/Sound/SRS.lua | 64 +++++++++++---------------- 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index c85ddcd9b..a7f810493 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -48,10 +48,11 @@ -- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". -- @field #string Label Label showing up on the SRS radio overlay. Default is "ROBOT". No spaces allowed. -- @field #table AltBackend Table containing functions and variables to enable an alternate backend to transmit to SRS. --- @field #string ConfigFileName Name of the standard config file --- @field #string ConfigFilePath Path to the standard config file --- @field #boolean ConfigLoaded --- @field #string ttsprovider Default provider TTS backend, e.g. "Google" or "Microsoft", default is Microsoft +-- @field #string ConfigFileName Name of the standard config file. +-- @field #string ConfigFilePath Path to the standard config file. +-- @field #boolean ConfigLoaded If `true` if config file was loaded. +-- @field #string ttsprovider Default provider TTS backend, e.g. "Google" or "Microsoft", default is Microsoft. +-- @field #table poptions Provider options. Each element is a data structure of type `MSRS.ProvierOptions`. -- @extends Core.Base#BASE --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde @@ -64,8 +65,9 @@ -- -- ## Prerequisites -- --- This script needs SRS version >= 1.9.6. --- Optional: DCS-gRPC as backend to communicate with SRS (vide infra) +-- * This script needs SRS version >= 1.9.6 +-- * You need to de-sanitize os, io and lfs in hte missionscripting.lua +-- * Optional: DCS-gRPC as backend to communicate with SRS (vide infra) -- -- ## Knwon Issues -- @@ -112,7 +114,7 @@ -- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml -- -- --- **Pro-Tipp** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing. +-- **Pro-Tip** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing. -- and also the Google Console error, in case you have missed a step in setting up your Google TTS. -- E.g. `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"` -- Plays a message on 255 MHz AM for the blue coalition in-game. @@ -210,7 +212,7 @@ MSRS = { ConfigFileName = "Moose_MSRS.lua", ConfigFilePath = "Config\\", ConfigLoaded = false, - poptions = {}, + poptions = {}, } --- MSRS class version. @@ -343,6 +345,8 @@ MSRS.Provider = { --- Provider options. -- @type MSRS.ProviderOptions +-- @field #string provider Provider. +-- @field #string credentials Google credentials JSON file (full path). -- @field #string key Access key (DCS-gRPC with Google, ASW, AZURE as provider). -- @field #string secret Secret key (DCS-gRPC with ASW as provider) -- @field #string region Region. @@ -653,7 +657,7 @@ end --- Get modulations. -- @param #MSRS self --- @param #table Modulations. +-- @return #table Modulations. function MSRS:GetModulations() return self.modulations end @@ -874,32 +878,6 @@ function MSRS:Help() return self end ---- Sets an alternate SRS backend to be used by MSRS to transmit over SRS for all new MSRS class instances. --- @param #table Backend A table containing a table `Functions` with new/replacement class functions and `Vars` with new/replacement variables. --- @return #boolean Returns 'true' on success. -function MSRS.SetDefaultBackend(Backend) - if type(Backend) == "table" then - MSRS.AltBackend = UTILS.DeepCopy(Backend) - else - return false - end - - return true -end - ---- Restores default SRS backend (DCS-SR-ExternalAudio.exe) to be used by all new MSRS class instances to transmit over SRS. --- @return #boolean Returns 'true' on success. -function MSRS.ResetDefaultBackend() - MSRS.AltBackend = nil - return true -end - ---- Sets DCS-gRPC as the default SRS backend for all new MSRS class instances. --- @return #boolean Returns 'true' on success. -function MSRS.SetDefaultBackendGRPC() - return MSRS.SetDefaultBackend(MSRS_BACKEND_DCSGRPC) -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Transmission Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -984,7 +962,7 @@ function MSRS:PlayText(Text, Delay, Coordinate) else - self:PlayTextExt(Text,Delay, nil, nil, nil, nil, nil, nil, nil,Coordinate) + self:PlayTextExt(Text, Delay, nil, nil, nil, nil, nil, nil, nil, Coordinate) end @@ -1012,6 +990,9 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) else + Frequencies = Frequencies or self:GetFrequencies() + Modulations = Modulations or self:GetModulations() + if self.backend==MSRS.Backend.SRSEXE then -- Get command line. @@ -1162,9 +1143,14 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp command=command..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt) end - -- Set google. - if self.google and self.ttsprovider == "Google" then - command=command..string.format(' --ssml -G "%s"', self.google) + -- Set provider options + if self.provider==MSRS.Provider.GOOGLE then + local pops=self:GetProviderOptions() + command=command..string.format(' --ssml -G "%s"', pops.credentials) + elseif self.provider==MSRS.Provider.WINDOWS then + -- Nothing to do. + else + self:E("ERROR: SRS only supports WINWOWS and GOOGLE as TTS providers! Use DCS-gRPC backend for other providers such as ") end -- Debug output. From 92e03522db5ffa0933a6376d2aced762c5fb26b6 Mon Sep 17 00:00:00 2001 From: kaltokri Date: Mon, 18 Dec 2023 16:21:32 +0100 Subject: [PATCH 38/95] Finished beginner section --- docs/beginner/ask-for-help.md | 77 ++++++++++++++++++ docs/beginner/demo-missions.md | 49 ++++++++++- docs/beginner/hello-world-build.md | 13 ++- docs/beginner/problems.md | 46 +++++++++++ docs/beginner/tipps-and-tricks.md | 4 +- docs/images/beginner/discord-fomat-logs.png | Bin 0 -> 3707 bytes .../beginner/discord-multi-line-code.png | Bin 0 -> 7504 bytes .../beginner/discord-single-line-code.png | Bin 0 -> 3348 bytes 8 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 docs/beginner/ask-for-help.md create mode 100644 docs/beginner/problems.md create mode 100644 docs/images/beginner/discord-fomat-logs.png create mode 100644 docs/images/beginner/discord-multi-line-code.png create mode 100644 docs/images/beginner/discord-single-line-code.png diff --git a/docs/beginner/ask-for-help.md b/docs/beginner/ask-for-help.md new file mode 100644 index 000000000..bf8a9c8f6 --- /dev/null +++ b/docs/beginner/ask-for-help.md @@ -0,0 +1,77 @@ +--- +parent: Beginner +nav_order: 06 +--- +# How to ask for help +{: .no_toc } + +1. Table of contents +{:toc} + +After you have tried to solve the problem on your own, you can also get help +from the community. + +{: .highlight } +> But it is important to follow certain rules! Read them below. + +## Communities + +There are two ways to communicate with the community. +The fastest way is to use Discord: + +- {:target="_blank"} + +But if you don't like Discord, you are able to post in the DCS forum. +Check out the MOOSE thread here: + +- + +## How to post requests + +MOOSE is a community project and support is community based. + +Please remember when posting a question: + +- Before posting anything follow the [troubleshooting steps]. +- **Read your logs**. + +A post should contain the following: + +1. A describtion what you expected to happen and what actually happened. + - Do not use vague words this stuff is hard to help with! Be specific. + +2. Describe what happens instead. + - The less detail you offer, the less chance you can be helped. + - Don’t say it doesn’t work. Or is it broken. Say what it actually does. + +3. Post your code in Discord as formatted code: + + - Wrap a single line of code in backticks \` like this: + + ![discord-single-line-code.png](../images/beginner/discord-single-line-code.png) + + - Multiple lines of code should be posted like this: + + ![discord-multi-line-code.png](../images/beginner/discord-multi-line-code.png) + +- Post your log lines with the error or warning messages. Format them like this: + + ![discord-fomat-logs.png](../images/beginner/discord-fomat-logs.png) + +- Some complex problems need the mission (.miz file) also. + + - But post your mission only when requested. + - Try to simplify your mission if it is complex! + +There are people in the Discord and in the forum, who spend their free time to +help you.
+It is your responsibility to make their "work" as easy as possible. + +Welcome to MOOSE and good luck! + +## Next step + +Last but not least some [tipps and tricks]. + +[troubleshooting steps]: problems.md +[tipps and tricks]: tipps-and-tricks.md diff --git a/docs/beginner/demo-missions.md b/docs/beginner/demo-missions.md index 073ae42fd..b5e3eea1b 100644 --- a/docs/beginner/demo-missions.md +++ b/docs/beginner/demo-missions.md @@ -9,5 +9,50 @@ nav_order: 04 1. Table of contents {:toc} -{: .warning } -> THIS DOCUMENT IS STILL WORK IN PROGRESS! +The best way to get compftable with a Moose class is to try the demo missions of +the class you want to learn. The Moose team created a lot of demo missions for +most of the classes. + +## Download demo missions + +Go to the repository [MOOSE_MISSIONS]{:target="_blank"}, search the folder of +the class, download the mission (`.miz`) and rum them. + +## Read the mission script + +In the same folder a `.lua` file with the same name is placed which is the +included mission script. You can watch these mission scripts easily online at +GitHub to understand what is happening in the mission. + +## Read documentation + +Next step is to read the [documentation]{:target="_blank"} of the class to +understand the code of the demo mission. + +{: .note } +> The documentation is quite long and might be confusing for beginners. +> Start by looking at the description at the top of the documentation of a +> class. It often contains examples and explanations.

+> Then search for the function names and look at the description of the +> functions and its parameters. + +## Make small changes to the script + +Download the `.lua` file, change the parameters to suit your needs in +[Notepad++]{:target="_blank"}, add it to the mission and rerun the mission. +Observe what happens and adapt the code. + +If you want to use more functions combine them all up. + +{: .note } +> But it is wise to do this in small steps. So it is easier to find errors. + +## Next step + +If the mission shows not the expected behaviour take a look at section +[problems]. + +[MOOSE_MISSIONS]: https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop +[documentation]: https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/index.html +[Notepad++]: https://notepad-plus-plus.org/downloads/ +[problems]: problems.md diff --git a/docs/beginner/hello-world-build.md b/docs/beginner/hello-world-build.md index 1f7509abd..5227a295d 100644 --- a/docs/beginner/hello-world-build.md +++ b/docs/beginner/hello-world-build.md @@ -144,9 +144,16 @@ have create everything on your own. - Change the text a little bit, like `Hello Dude! ...` and save the file. - Run the mission again. - The text will not be changed in the mission. Why? - The mission editor copies the script into the mission file when you add it. - Ever change on the script file on your hard disk is not recognized by mission editor. - You have to add the file after each change again. + +{: .important } +The mission editor copies the script into the mission file when you add it. +Every change on the script file on your hard disk is not recognized by mission +editor. **You have to add the file after each change again!** + +There is also another method available to dynamically load mission scripts. +But this method has some brawbacks and will be explained in the advanced section. + +Now we add the mission script again: - On the left side of the `TRIGGERS` dialog click on `Load Mission Script`. - On the right side under `ACTIONS` you need to add the script again: diff --git a/docs/beginner/problems.md b/docs/beginner/problems.md new file mode 100644 index 000000000..ebc71077a --- /dev/null +++ b/docs/beginner/problems.md @@ -0,0 +1,46 @@ +--- +parent: Beginner +nav_order: 05 +--- + +# Problems +{: .no_toc } + +1. Table of contents +{:toc} + +## Something went wrong + +If the mission shows not the expected behaviour do the following steps: + +1. Double check if you added the changed mission script to the mission again! +1. Check if the triggers are configured as requested in the last sections. + +## Read the logs + +The DCS log is a super important and useful log for the entire of DCS World. +All scripting and other errors are recorded here. It is the one stop shop for +things that occurred in your mission. It will tell you if there was a mistake. + +1. Open the file `dcs.log` in the `Logs` subfolder in your DCS + [Saved Games folder]. + +1. Search for the following line: `*** MOOSE INCLUDE END ***` + - If it is included in the log, Moose was loaded. + - If the line is not in the log check the triggers again! + +1. Search for lines with `SCRIPTING` and `WARNING` or `ERROR` and read them. + - This might help to find your error. + + {: .note } + > You will find a lot of warning and error lines in the log which are not + > related to `SCRIPTING`. They are related to stuff from Eagle Dynamics or + > Third Parties and you have to ignore them. EA does the same. ;o) + +## Next step + +If you don't find the error and/or don't understand the messages in the log file +you can [ask for help]. + +[Saved Games folder]: tipps-and-tricks.md#find-the-saved-games-folder +[ask for help]: ask-for-help.md diff --git a/docs/beginner/tipps-and-tricks.md b/docs/beginner/tipps-and-tricks.md index 3ea1bd781..f97615318 100644 --- a/docs/beginner/tipps-and-tricks.md +++ b/docs/beginner/tipps-and-tricks.md @@ -33,7 +33,9 @@ This folder can be found in your userprofile as subfolder of `Saved Games`. The easiest way to find it, is to open search and paste the text below into it and press Enter: -```%userprofile%\Saved Games``` +``` +%userprofile%\Saved Games +``` {: .note } > The text will work even if your Windows is installed with another language, diff --git a/docs/images/beginner/discord-fomat-logs.png b/docs/images/beginner/discord-fomat-logs.png new file mode 100644 index 0000000000000000000000000000000000000000..a78cecce32599f9709776ccb16bb65994a502244 GIT binary patch literal 3707 zcmbtXdsx!h*8VllS2L&UoXN~f+MGF+Q&Su>MGd7+Q+6j!yr4m4<^?aKZ#hKI5YyaT$2LJ%p9y|I?EC76T)$JobS?S&bZQ4U_vjP(v{a2t<=r`*IAEzHY zbr1l$t5z>v_%Ap9^xLCJ7ywu|@Y`A;$ZNagb~YUQ=3rb2(jZs1pBwb%&$*K52ZI9n z0Uz&TW*yvc2>$2#tA9T_ec^;1jC7-Q6xHIPf&v&^;9Oc_QAP1YCchFT) z>8%#_hi@0^L!eXGbV)=tejdzqPCi=gCe}P%u>kCFuejYPS ztA^Eqm8HaD=fr>C9H#pQ>;dOCzl##|x?{rjEPcB9Kg;j;)bwv+wl2PwJS#A{Y{Qni zJ#{u)zQY2lD~CFJ^!+U5y`tF3fmeMFE2E!Yotn7XaKa#>;bxEZh=M;jb`cZ(mn?z; zS|`UjWgZxy6r;W6eJNIPJf7zeoV4H?Z4*?F^Cxe=In|0*o{bcv#zU3z^gkUxU`!|t zqD{Y?xjc7BOJFQeA$S-hsI&?6IG5|X2g7nljq#Os#{dTm4+^ytHADs z&clsxC0<*1BDwl0!H9^81wOSl}4-ZWdytAT(3581AM4mz^#40e| zc)}*h8RshK(g&zvUvA@dTP44s=`#Ucy|_VJitcY7vy-A|jgapJ7OoUc7^ zqkd@VP0E|w3k6UvXG1iS?2~MJayw|dbT&WM(-G)T>hI07?y`5-v$SX>YJcv&dQ*nN zUj)NSHnT~!wtwExgz3i3Q!iii#?l&8Rm`}^s+PU%t<*&A!&KX^xZDSzED zT>c4i&j|4&A?Vi9Jrn!P5s>ex?F-@-mM(pwHRFQR|72Wr`4+qo)Be`Q0sZ~uvMe9o zxwsHIo56uEQyaKrqnoL%BlOEHIZ04bD~gH^?VP!>s%v*n@?%tizqpG(77@0ds>N!< z?j?%Sj9rZQ@}Cnoo<$RPa;Bo5>5~jAMrfrjvwypeE<=Pg+vlN2CZ;fP0WFIiF|?K_ zGR?fMM&Ch- zd@!i=Vsgct<|PEhxGAOTi|-E|!JSj}DHXOGsw`TABU_COiZ|&Y$}!oGvabk(D(zNL ztE`JZqt#z-9uN%mw;oQaoPI4bn7)W4*llm%jA5@pC6p+YBH)1BTmfO5B_R9qFT76 zk^3^DE$?VB^hk0XnEw-nCDbS8*y%aAIO+O^;fX2~;B%;uXIVg62qav*)q;GF$ga?D zXrwINq9u)oNg>e8Na9dAPY;Q2r@`mKMfKI|(sTpiWNR|U7_-IZIUMp2+g%PmZjHTT zZpN=ghkPeU6W^f`#6m}~QZCI{i$E~{STm>%mRfyu7D>2O5-4i%O0C5pX*Il;$rn*f zibU4?*c>ba+x=9E?^kR0_JZkB_GZ|v7+VOLKh6$YMm`s_71A3jGX4_V+Fn50%TB+z zhWtvpJKwm+@&Vovw2Jy=cSj{mjlP)q>Ix8dAb)O4ic5WFTR4N3vA3JN{asyOoT0Gk zbMLQepHy5_#M2Yx@dcjpTH>?A%?Tilr0+{{S(ECiVmdRy5Oa6-ZA)w*cYgo!gyO0h@0_D znL1<$k#kC=ZR8im@27pId;93kVq3OhD(<%jo8iC~$a96}+us&DzC@uUFrb z4jTvt=RU;s+W1Z$HdC#@j!xtCe`{kL!TKj_YB9#mnC$#z=2V8EX}yS1Q+FIa67AS7 zSPYw}(zI>f*9>(KzP6-9M%RQ_vG26wm|dTdLJ%e-lA4l7`l1q3{G*`jA}K9PrGyfr z7T?UbN^Q~}9nf|)nH#vV<20}N27$l!1-jvZQF1AvV&k@*{Op6Sr&z$z@%_LvP zjquvBGMq{iHM7!^f|2)~*7Y-TjSTAN(ubZ^I$o#*EkA4U)t<%kysk=l-Fj9baLYPr ziUN^PXURK+;zF~Jz)=Y`LT#;PYTjJwgLB!*`2kpxm}r!u??$B}&w&?yihw68O)=2$ zXe^4yGVbShERe5US%T zIPo*IQje?@&92eyP@Ort1cxEst)tVI^u*Y2kGew7k1*E3uBAdQ1(>fEWjj9o z%et$;?Y9p-m)DduUkMApKk$N6_I{kwLpI1C9`XYUUyoB@QrEK$Vd&ql2>#9V{Qv5U zM|PmZYOvJ?_`9Ac`j?&NrC)}t6kPW-Jv=}mBt`WVntx@-B)cU+;}&-b08l**`=5Uq z;1dA&i}(MX{SM&m4^WsTnTu~1n$(ke#_8etq%>BmUaBlfXF3NHhF62wrHEB-DnX_g zd=#kF-H)T?u-5<62ZdDc4IkjLis6LkL^RLe-~>wLkmB1-CS$@+P@K3?;m jc{r4R)VGopp_)XNv{;I)CHqWvWtqw{HG_$v#aOduEJe)EmLg1OCZw^8jGd_w z5|J==lO@|=EHlP1!#h2{_pj%<-uHUm<&XQm&$-TZ-QRQ0cl(_4iMwfIE+il!z{A5M zblt+_HV@B!d9Lhz=pgqEfDU-k9tqpm2Dv|}+E__`5u&>2k ze;yv;mcP%wcAo+_9v-Pb*G&xVLY$Z1j(&2t4{X~I=}n5N`JLp!$ETblp_vWdd_}F547Gn}ncxI%~TsW-}2>gq}a(2{xW4WpwE5^P{uZ1X7Q3rLNc$@?6yj zBXrT~uR%@nHioMna=H3m?J(Cs^0aU{{?{=4k|g`r@ab&K^}j#3-%@r`-Y2xDA=Z)@ zO!Z|+4{Eg=|21TH#B_|7H->Jlw}6k}be_;0WM~VB#7%2lqgTjQMY#y1iOHJ~^zZN` zq#IzX3IAnA3vaT%y5q)fUc#oRkDkUDYBpzb7PbPdqC9R*Yl*XE3gK;VcN@X~I%EG0 zqVuoUC@7{%Ky*T8J3}v4D`GWs!+#>&Eo6KU2y|SZ=K8j~)9*V^S&>rNA)%{h(cAzg zr`&^LI{q-n)TDONuvo3IrIE=WK248nmp+-Jja4W7nmI*Qe*;mmxevkEz0_fYYlJc= z;k$Ftzj^Ro(;$*&N+H4-lt7N}7OF-F?aa~TSJB&;RDSa}qmJ2KWokJ*I`lk13zfFF zy840XM0lw~XD|PoVcySJF?sYO+(*((8k2n_P9W8i7#J49nbz`3A}(YGi=g5Erd;Yk zP?OqScG$NC$!=!T#`nop|C%EiyNn+Rl;$VXIH$`}5xbj{KP`iIE7Vw7h=eIO_1zn{ z9xrGm3uUk&&ENk{pYZ+$C}wrzTsdm}!(<9B8_E_?3r}#Ji5V4ZZOe&lLewthBwuPS zy5+JSD>>1yRVw8s%-L~jp~}?zX|u;+w*Rph_@9m||HtY$>Tu?N&c5;g>+<<8KTr~t zr?9r#VkEi@MSlYl*@yL$oxN z!2&+~;Uqd$r2P&sKExGc$c*FAo-rnENJ3b*b5m$8W3I~n4mIv@3@PHop&j>NJY4{g zVxz2YQm(eP=7U4I8)?9#|8xiw9= z`2bYg*q&SCBilLLdaAm+4nuMic_>%*R{nCzl)PYSh4oBKY|ZtbfhFDTJ^r8&zuxrn zA#Dl}j;Xz*YjE~5S*iQLHq31@-#byc(qUbPN1g9!i2qpBg7-0$F1HwGuBB>)) zX(jV`YuBNLocL~QscyaeG+7G==tfb$c(-)v0MigTQS`9mBx7;~d!qw3%6!L6LD3R6QfU;doc0|DOvqHQ(`Qstp|z_KoFlLg8W-5bbZ{*VI%Qmbd{#Vj zbe%U`^Onc%9C7RO)vVtU-+B=3;7b8&9}bKwT0MPIIaujr2=AmhpXJe0wptbZI5`NJ zPI=Voda2od&`$f|)2W#REa6}^b3?JVZP(X|id);uVNK1z2(N}y;|vM$7Hfg~K!d76`W8SI7Y3_X#+dFL$h zu+_ME)Aeg+DB_z3CprE1-pt9Qy; zhIGad=VLnTY1$g>shQ{A~9d1?~#-5J)vc(CKmuO%={RaEZ#>BFB>_fK~U0OSCx z?`>60l!mL8AY;z?X*2VBJ4^nfW9{UxYR1u!1T?@A@dq%dM zy9iWn5M`o<$4+B;q0`D&p_u-1xEI}p{-<;cw6z0P*Rj!Y5Kur9 zF&}wkq4#XQarfMoi*viy8`&#pR;KMEmI8D$?STHXkmG)=8w`=KrbMc(Dq>5HQgxE> z#yD}jrs*JV?MwNW*BFTGbZEB2m{dp4qn<}_I@#{guxKfxp=WpEPx@InmEhX+blg5( z$SrKo#~ZOJ>``W= z+JckNu0hLB>P4C8bDb|)1 zL1NG&jiFL^0UNK1BQ)B4s?>-l<$qD@#W%ETd{G2Spib$QebtHwrChN7;-%V&p+1RF zU?14U%r8nr(PIWZ(`)bE{@b^d{~&*yOsoe}_*p%6#@0&}s_RaD;jHJo}Z(N;gCivVQVa*RLBDFJ^A>O!Y&?}63~^J?ZGrqsg0 z>Nc~%`%Ai2uFHu|3ilj$%r-X>Jvsv`;>r!c*wd_=Od$>GQc{t*7hD8s?|H?Xn0DSE zD)4wRS)xr`4~?=?v7-045WV-Qp12^@Pf@z$n0}RWLg|F^VkBz|Uya3BRg4~ojA6&Of?WgRA!s1HQ6*5>AhN2= zy;);r%{NT7YAH1PUE41&8X1dZ5j=n7y4SpDSgH4!A$DHMBP7GtnBn-HcXGuQip0cF zXB(GN1#WFIZp_#FI_j}K`=~I=QfZTJsKokdUI~>u_40tjMGt0*d&irhqY30YAzrsZ z7#le*zP$WA*Uc6e#)sRr2q=zN7tk}uU29V7#k*}o2V-{&2Q9(;bI{k%quXtO_wv;a zyL?#P`oz>EE6x`u1j@9={E|>W(Aw4fPau-&xsx{Ubm6djB3?LtZa7Y?U{PnYEQVzaP}i`Q7TZ zYjx3zt}x(|1}g5>-}9*w^{h^L&t3(IbdTx!Bs$-!@^<;Pe%#$W;@rm{&gsUp`U zUaa2CQ+ov;DC>r>M!)lZ{XEM5=7c=zi0OenuUA3^QB#HZ9+l%)v(lR@BE#$w_$H8- zRa2#TPlaj!y(GO))DQpx=c|R!$oYw0#MaI;P1>{nV!PX}J`4V#rco({Gw&ayO}X{O z#6?fMUG!ZHRT|nLY&dUbbSMTTUJhO&c9xMeakbox(e^^uZe*RCrY8_bJ!~=A>=jk? z`9UY~^V=^uU0RXNFP|uj%KMu!w~FR^bg7PmWN%}2(y8sO)E-Y5fJXFRLBqnk@|Ne6 z8_8uX?}}d1^N8?2`{6pFE!BP_z;5=$owOKxL4eBYxdmcM$)u$Su)Y9lFsy+F<|M61 z;#i3TinR)Q2U~eV*AiG&GAMTd()bqy7p2`*xSJ;unb9FIL)CZdZAsz+K;>3rq&;Zu zAqR$B=(C25+TKa0nY*`l zSPQ$X&H`j{Kk4hTYU;Ss1(ORJ^nSMzwVZ!)#B=@`4sglc5r@w0rwzc{7Yz>1yCm); zpwgSkwmdTELZ-xK-Q1gG=WYvUr-{M+EB*xPbVS58TFK3>Mv<|y@;5ARum%4H1`Ma_ z)BSv9de0IySEx?0ZWXscGk&v|&1C#yC6b*Jg(W1FtS-n#_-~g@&c7okouc5i{>H;d} za3uow3;B!BtxW(BvNg7MrM(|*6Hu&-feEJB0~*8*E*1le$JcXm>kL5pW{R`@&hibA zlG~VnxTsWrxMD6)#}Bao8tAK>6=PbLXK8AktT*8lMCuU)rVQN-=?Rs^S1)ZWK8PUb#qKvrhA zgDnVA@mRP?F?9RiLrGiA#|4h-&iy|IT6d8Ei#DH2vyW0=ba*5Rt-Q0`^kENGm`bk3 zK*o<;gFW_+)+y_AHvtD7DHVbj(AR9&^1ukOZlWKuncSw|p%FZu<@NUcc&>fep@2b> zX-AMpiqC?$l2v2D!wwbRCW6`{hCQtF^8*8~-jRLdi)eCvXeH`dbLJZEY>wI*PCbXG zcTnH8MgZ{kSQQ6b;J<(d0r=m;f7=SZYXZ& z6psN8N-eP)zWCrq=joua2djZrj5^b|RO&j$&-r%1vg~OwJ#KZjIH;;T|L{@d-+ zv)d4zQoK@@iWSBfms0C9bWxJ1pcgyJ%zDZ!{c%B3qF4hFRJHeZPzzw)RhX*MPLx41n1^7I5H zzTFUk03mKCBy9Vi3mkJvB*pBHy;{#)i z`7SU6<9|anjKxVXw+QS$$9VB(khuaC(M<9E<^cL|Eo$T3Q(0J~68h`#&MCkx!To4p z+iTAZIZEd0TEz%Wl&`@abB1O*_IiIV+O5Jk3!-`@6dA3k$G3?UV3I?U3r(+6i%)Zx z^-zOs+C^s7^Ny~@iFQG< z{Pq}oMKX66(v!<9t6a@wR-LJZl|VdS#K0#k&{aj|PsYxYE=(5#V=7HE>bC;#m3dmd zD)bkTNEHMaFjmW9p9T2@_!&$A!Hj%sP^*Ax!ZsYZTgZhtwo98oV^n+vl9S8Rc^K_F!$;Hn)#Jd{_I@;Kwc3V7*_2IMM;T>~FLEQ%|3yJnC4V z5Co_K(>rVoQMG|^dI8AzoL$(w&+9`YDfvg&)F=7kH+PCq4oqL96}6vZtWJx&&OBxT zZfchj)l?p@_mSo{p~3|kA!F_Urxmeq<*dL&=KRYD(B+pK6r;l3dSSP~-2$9KWlGv| zk#Xfp>MHe4pruXaX}wv8hWeAhp6LSMH44Rm$>BR4d=1}z3c4wL4r!o%*u_4;f_3)e zV1MrCTW5Qf%t$LaO_Tf_sn0lv%EK--_j;D+PU(noo6H8$U|xnQF*aHV?aQIpQRV@D z@hKlOd{y0g-YUdgAF>Ti);B&h`Hlq9%1+?t{C@;+lewx^TegFxk>r4ikKORwa`FtE zUtuJ^f+WH&-23PuT(v6_!yc&Tfb%vW;;cEiW;6GcS9;O;xlL*sV06g;w9ay*Sazonz>WRb!%3C~F3UPoPs-mchjAOu8{D!( z+oA`!Gd86rw-&kZ#ayhMaA6g^81Y+a-!*C~8*^#CPbst!_hf~)ys9`%bzdFG`KMje zMDTd8WY$$F`a5v_xX(=E569zD$O8cYk&YVn%L&~Hm_jWW!DnKLo>I(OAtvu=mLgJ0 ztEr*y^YZ%;=EShHk{u^*m#hg~+gQ>x761ma^3qf1iub^Q5zQS$rK zUY`86b@s=Xo-bYdnygM@K5@%`)QLMD4JvJHKh!{{617m*uvu}TV7anc*fYe1W&XEKbs&?lLB?k?ay# zFRMMr}+lo;w&GReApi0wuEpH$}BMC#koH0v5U$}3Z}!eVAN z{_F5Ep{@9Z`$o=oFR!#C44yo?-b@Z%fGibRCp&e{)t9AnxI@+&Fs$R!FcP1R8fjZB zL0w4fK*3Z+!A5+!{NVr_oiW(vLMl+M&_nE$zzaMU*f22Ys!p@NJM{&BpZo*n8l2B{Vq zJC5yPjU(P2gtDBrMFLYh7mbvCLP@PcE``A_s!+EtNhc0+1XhAAn;*0=?Ya9;R3J54 zpyR_yr5vH_)h8e3SSuHCS)Oc#n2tkD;QkyDu;?P-)ko?0arU3Vqec%?N$;P5;=w6S zNnxfHQk!;D+ylewzssrpFKbeHF?k>2`r>4#2uM9_cBTmy-iS|DAJGb5ub!N2Zw5Lt zCPX}c{AGNMFyH^rD7E0>zpT}B_nG4Czrz1LA^4x2KTMO_m3guv_kKKWLx{_Q@?1By LF)1^;7xg~?o4y`h literal 0 HcmV?d00001 diff --git a/docs/images/beginner/discord-single-line-code.png b/docs/images/beginner/discord-single-line-code.png new file mode 100644 index 0000000000000000000000000000000000000000..fc1e7dafd246d268c3c6180354ab9fc7588b0248 GIT binary patch literal 3348 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 WI$N$HGUY!Rg&TH`w$v+L(f literal 0 HcmV?d00001 From c72f10955362b10b425ebbd281bc9a009b974c5c Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 19 Dec 2023 00:02:17 +0100 Subject: [PATCH 39/95] SRS --- Moose Development/Moose/Core/Point.lua | 13 +- Moose Development/Moose/Core/Zone.lua | 18 ++ Moose Development/Moose/Sound/SRS.lua | 240 ++++++++++++++++--------- 3 files changed, 179 insertions(+), 92 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index a3c83da1f..89807b436 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -2456,15 +2456,18 @@ do -- COORDINATE -- Write command as string and execute that. Idea by Grimes https://forum.dcs.world/topic/324201-mark-to-all-function/#comment-5273793 local s=string.format("trigger.action.markupToAll(7, %d, %d,", Coalition, MarkID) for _,vec in pairs(vecs) do - s=s..string.format("%s,", UTILS._OneLineSerialize(vec)) + --s=s..string.format("%s,", UTILS._OneLineSerialize(vec)) + s=s..string.format("{x=%.1f, y=%.1f, z=%.1f},", vec.x, vec.y, vec.z) end - s=s..string.format("%s, %s, %s, %s", UTILS._OneLineSerialize(Color), UTILS._OneLineSerialize(FillColor), tostring(LineType), tostring(ReadOnly)) - if Text and Text~="" then - s=s..string.format(", \"%s\"", Text) + s=s..string.format("{%.3f, %.3f, %.3f, %.3f},", Color[1], Color[2], Color[3], Color[4]) + s=s..string.format("{%.3f, %.3f, %.3f, %.3f},", FillColor[1], FillColor[2], FillColor[3], FillColor[4]) + s=s..string.format("%d,", LineType or 1) + s=s..string.format("%s", tostring(ReadOnly)) + if Text and type(Text)=="string" and string.len(Text)>0 then + s=s..string.format(", \"%s\"", tostring(Text)) end s=s..")" - -- Execute string command local success=UTILS.DoString(s) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 09ef67c2f..1037ddf02 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2473,6 +2473,20 @@ end -- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. -- @return #ZONE_POLYGON_BASE self function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, IncludeTriangles) + + local coords = self:GetVerticiesCoordinates() + + local coord=coords[1] --Core.Point#COORDINATE + + table.remove(coords, 1) + + coord:MarkupToAllFreeForm(coords, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, "Drew Polygon") + + if true then + return + end + + if self._.Polygon and #self._.Polygon >= 3 then Coalition = Coalition or self:GetDrawCoalition() @@ -3610,13 +3624,17 @@ ZONE_OVAL = { --- Creates a new ZONE_OVAL from a center point, major axis, minor axis, and angle. --- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua +-- @param #ZONE_OVAL self +-- @param #string name Name of the zone. -- @param #table vec2 The center point of the oval -- @param #number major_axis The major axis of the oval -- @param #number minor_axis The minor axis of the oval -- @param #number angle The angle of the oval -- @return #ZONE_OVAL The new oval function ZONE_OVAL:New(name, vec2, major_axis, minor_axis, angle) + self = BASE:Inherit(self, ZONE_BASE:New()) + self.ZoneName = name self.CenterVec2 = vec2 self.MajorAxis = major_axis diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index a7f810493..82689f9e5 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -42,7 +42,7 @@ -- @field #number volume Volume between 0 (min) and 1 (max). Default 1. -- @field #string culture Culture. Default "en-GB". -- @field #string gender Gender. Default "female". --- @field #string voice Specific voce. +-- @field #string voice Specific voice. -- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send. -- @field #string path Path to the SRS exe. This includes the final slash "/". -- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". @@ -365,17 +365,6 @@ MSRS.Provider = { -- @field #MSRS.ProviderOptions aws -- @field #string DefaultProvider -MSRS.GRPCOptions = {} -- #MSRS.GRPCOptions -MSRS.GRPCOptions.gcloud = {} -- #MSRS.ProviderOptions -MSRS.GRPCOptions.win = {} -- #MSRS.ProviderOptions -MSRS.GRPCOptions.azure = {} -- #MSRS.ProviderOptions -MSRS.GRPCOptions.aws = {} -- #MSRS.ProviderOptions - -MSRS.GRPCOptions.win.defaultVoice = "Hedda" -MSRS.GRPCOptions.win.voice = "Hedda" - -MSRS.GRPCOptions.DefaultProvider = "win" - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -700,20 +689,36 @@ function MSRS:SetVoice(Voice) return self end ---- Set to use a specific voice. Will override gender and culture settings. +--- Set to use a specific voice for a given provider. Note that this will override any gender and culture settings. -- @param #MSRS self +-- @param #string Provider Provider. Default `MSRS.Provider.WINDOWS`. -- @param #string Voice Voice. -- @return #MSRS self -function MSRS:SetDefaultVoice(Voice) - - self.defaultVoice=Voice +function MSRS:SetProviderVoice(Provider, Voice) - local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider or "win" - self.GRPCOptions[provider].defaultVoice = Voice + self.poptions=self.poptions or {} + + self.poptions[Provider or MSRS.Provider.WINDOWSo]=Voice return self end +--- Get voice. +-- @param #MSRS self +-- @param #string Provider Provider. Default is the currently set provider (`self.provider`). +-- @return #MSRS self +function MSRS:GetVoice(Provider) + + Provider=Provider or self.provider + + if Provider then + return self.poptions[Provider].voice + else + return self.voice + end + +end + --- Set the coordinate from which the transmissions will be broadcasted. Note that this is only a factor if SRS has line-of-sight or distance enabled. -- @param #MSRS self -- @param Core.Point#COORDINATE Coordinate Origin of the transmission. @@ -796,6 +801,34 @@ end -- @param #string Region Region to use. -- @return #MSRS.ProviderOptions Provider optionas table. function MSRS:SetProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) + + local option=MSRS._CreateProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) + + if self then + + self.poptions=self.poptions or {} + + self.poptions[Provider]=option + + else + + MSRS.poptions=MSRS.poptions or {} + + MSRS.poptions[Provider]=option + + end + + return option +end + +--- Create MSRS.ProviderOptions. +-- @param #string Provider Provider. +-- @param #string CredentialsFile Full path to your credentials file. For Google this is the path to a JSON file. +-- @param #string AccessKey Your API access key. +-- @param #string SecretKey Your secret key. +-- @param #string Region Region to use. +-- @return #MSRS.ProviderOptions Provider optionas table. +function MSRS._CreateProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) local option={} --#MSRS.ProviderOptions @@ -804,10 +837,6 @@ function MSRS:SetProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey option.key=AccessKey option.secret=SecretKey option.region=Region - - self.poptions=self.poptions or {} - - self.poptions[Provider]=option return option end @@ -824,7 +853,7 @@ function MSRS:SetProviderOptionsGoogle(CredentialsFile, AccessKey) return self end ---- Set provider options and credentials for Google Cloud. +--- Get provider options. -- @param #MSRS self -- @param #string Provider Provider. Default is as set via @{#MSRS.SetProvider}. -- @return #MSRS.ProviderOptions Provider options. @@ -1107,7 +1136,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp coal=coal or self.coalition gender=gender or self.gender - voice=voice or self.voice + voice=voice or self:GetVoice(self.provider) or self.voice culture=culture or self.culture volume=volume or self.volume speed=speed or self.speed @@ -1297,9 +1326,7 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab self:I("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") self:I({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) - - --local options = self.ProviderOptions or MSRS.ProviderOptions or {} -- #MSRS.GRPCOptions - + local options = {} -- #MSRS.GRPCOptions local ssml = Text or '' @@ -1323,14 +1350,14 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab options.coalition = UTILS.GetCoalitionName(self.coalition):lower() -- Provider (win, gcloud, ...) - local provider = self.provider or self.GRPCOptions.DefaultProvider or MSRS.GRPCOptions.DefaultProvider + local provider = self.provider or MSRS.Provider.WINDOWS -- Provider options: voice, credentials options.provider = {} options.provider[provider] = self:GetProviderOptions(provider) -- Voice - Voice=Voice or self.voice + Voice=Voice or self:GetVoice(self.provider) or self.voice if Voice then -- We use a specific voice @@ -1453,76 +1480,115 @@ end function MSRS:LoadConfigFile(Path,Filename) if lfs == nil then - env.info("*****Note - lfs and os need to be desanitized for MSRS to work!") - return false + env.info("*****Note - lfs and os need to be desanitized for MSRS to work!") + return false end + local path = Path or lfs.writedir()..MSRS.ConfigFilePath local file = Filename or MSRS.ConfigFileName or "Moose_MSRS.lua" local pathandfile = path..file local filexsists = UTILS.FileExists(pathandfile) if filexsists and not MSRS.ConfigLoaded then + + env.info("FF reading config file") + + -- Load global MSRS_Config assert(loadfile(path..file))() - -- now we should have a global var MSRS_Config + if MSRS_Config then - if self then - self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" - self.port = MSRS_Config.Port or 5002 - self.frequencies = MSRS_Config.Frequency or {127,243} - self.modulations = MSRS_Config.Modulation or {0,0} - self.coalition = MSRS_Config.Coalition or 0 - if MSRS_Config.Coordinate then - self.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1],MSRS_Config.Coordinate[2],MSRS_Config.Coordinate[3]) - end - self.culture = MSRS_Config.Culture or "en-GB" - self.gender = MSRS_Config.Gender or "male" - self.google = MSRS_Config.Google - if MSRS_Config.Provider then - self.ttsprovider = MSRS_Config.Provider - end - self.Label = MSRS_Config.Label or "MSRS" - self.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel - if MSRS_Config.GRPC then - self.provider = MSRS_Config.GRPC.DefaultProvider - if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then - self.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key - self.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice - self.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret - self.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region - end - end - self.ConfigLoaded = true - else - MSRS.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" - MSRS.port = MSRS_Config.Port or 5002 - MSRS.frequencies = MSRS_Config.Frequency or {127,243} - MSRS.modulations = MSRS_Config.Modulation or {0,0} - MSRS.coalition = MSRS_Config.Coalition or 0 - if MSRS_Config.Coordinate then - MSRS.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1],MSRS_Config.Coordinate[2],MSRS_Config.Coordinate[3]) - end - MSRS.culture = MSRS_Config.Culture or "en-GB" - MSRS.gender = MSRS_Config.Gender or "male" - MSRS.google = MSRS_Config.Google - if MSRS_Config.Provider then - MSRS.ttsprovider = MSRS_Config.Provider - end - MSRS.Label = MSRS_Config.Label or "MSRS" - MSRS.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel - if MSRS_Config.GRPC then - MSRS.provider = MSRS_Config.GRPC.DefaultProvider - if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then - MSRS.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key - MSRS.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice - MSRS.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret - MSRS.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region - end - end - MSRS.ConfigLoaded = true + + local Self = self or MSRS --#MSRS + + Self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + Self.port = MSRS_Config.Port or 5002 + Self.frequencies = MSRS_Config.Frequency or {127,243} + Self.modulations = MSRS_Config.Modulation or {0,0} + Self.coalition = MSRS_Config.Coalition or 0 + if MSRS_Config.Coordinate then + Self.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1], MSRS_Config.Coordinate[2], MSRS_Config.Coordinate[3] ) end + Self.culture = MSRS_Config.Culture or "en-GB" + Self.gender = MSRS_Config.Gender or "male" + Self.Label = MSRS_Config.Label or "MSRS" + Self.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel + + Self.provider = MSRS_Config.Provider or MSRS.Provider.WINDOWS + for _,provider in pairs(MSRS.Provider) do + if MSRS_Config[provider] then + Self.poptions[provider]=MSRS_Config[provider] + end + end + + Self.ConfigLoaded = true + + if false then + + if self then + self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + self.port = MSRS_Config.Port or 5002 + self.frequencies = MSRS_Config.Frequency or {127,243} + self.modulations = MSRS_Config.Modulation or {0,0} + self.coalition = MSRS_Config.Coalition or 0 + if MSRS_Config.Coordinate then + self.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1], MSRS_Config.Coordinate[2], MSRS_Config.Coordinate[3] ) + end + self.culture = MSRS_Config.Culture or "en-GB" + self.gender = MSRS_Config.Gender or "male" + self.google = MSRS_Config.Google + if MSRS_Config.Provider then + self.ttsprovider = MSRS_Config.Provider + end + self.Label = MSRS_Config.Label or "MSRS" + self.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel + + if MSRS_Config.GRPC then + self.provider = MSRS_Config.GRPC.DefaultProvider + if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then + self.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key + self.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice + self.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret + self.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region + end + end + + self.ConfigLoaded = true + else + + MSRS.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + MSRS.port = MSRS_Config.Port or 5002 + MSRS.frequencies = MSRS_Config.Frequency or {127,243} + MSRS.modulations = MSRS_Config.Modulation or {0,0} + MSRS.coalition = MSRS_Config.Coalition or 0 + if MSRS_Config.Coordinate then + MSRS.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1], MSRS_Config.Coordinate[2], MSRS_Config.Coordinate[3] ) + end + MSRS.culture = MSRS_Config.Culture or "en-GB" + MSRS.gender = MSRS_Config.Gender or "male" + MSRS.google = MSRS_Config.Google + if MSRS_Config.Provider then + MSRS.ttsprovider = MSRS_Config.Provider + end + MSRS.Label = MSRS_Config.Label or "MSRS" + MSRS.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel + if MSRS_Config.GRPC then + MSRS.provider = MSRS_Config.GRPC.DefaultProvider + if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then + MSRS.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key + MSRS.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice + MSRS.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret + MSRS.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region + end + end + MSRS.ConfigLoaded = true + end + + end + end env.info("MSRS - Successfully loaded default configuration from disk!",false) end + if not filexsists then env.info("MSRS - Cannot find default configuration file!",false) return false @@ -1921,7 +1987,7 @@ function MSRSQUEUE:_CheckRadioQueue(delay) end ---MSRS.LoadConfigFile() +MSRS.LoadConfigFile() ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 53f89fd42c7b97126c503e705b3c7d1fe565e097 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 19 Dec 2023 10:18:20 +0100 Subject: [PATCH 40/95] #DATABASE * Read color, fill color from drawing data --- Moose Development/Moose/Core/Database.lua | 53 ++++++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index e3e7c17c1..ac290aa2f 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -449,10 +449,10 @@ do -- Zones and Pathlines -- Loop over layers. for layerID, layerData in pairs(env.mission.drawings.layers or {}) do - + -- Loop over objects in layers. for objectID, objectData in pairs(layerData.objects or {}) do - + -- Check for polygon which has at least 4 points (we would need 3 but the origin seems to be there twice) if objectData.polygonMode and (objectData.polygonMode=="free") and objectData.points and #objectData.points>=4 then @@ -488,10 +488,32 @@ do -- Zones and Pathlines -- Create new polygon zone. local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, points) - + + --Zone.DrawID = objectID + -- Set color. Zone:SetColor({1, 0, 0}, 0.15) - + Zone:SetFillColor({1, 0, 0}, 0.15) + + if objectData.colorString then + -- eg colorString = 0xff0000ff + local color = string.gsub(objectData.colorString,"^0x","") + local r = tonumber(string.sub(color,1,2),16)/255 + local g = tonumber(string.sub(color,3,4),16)/255 + local b = tonumber(string.sub(color,5,6),16)/255 + local a = tonumber(string.sub(color,7,8),16)/255 + Zone:SetColor({r, g, b}, a) + end + if objectData.fillColorString then + -- eg fillColorString = 0xff00004b + local color = string.gsub(objectData.colorString,"^0x","") + local r = tonumber(string.sub(color,1,2),16)/255 + local g = tonumber(string.sub(color,3,4),16)/255 + local b = tonumber(string.sub(color,5,6),16)/255 + local a = tonumber(string.sub(color,7,8),16)/255 + Zone:SetFillColor({r, g, b}, a) + end + -- Store in DB. self.ZONENAMES[ZoneName] = ZoneName @@ -532,7 +554,26 @@ do -- Zones and Pathlines -- Set color. Zone:SetColor({1, 0, 0}, 0.15) - + + if objectData.colorString then + -- eg colorString = 0xff0000ff + local color = string.gsub(objectData.colorString,"^0x","") + local r = tonumber(string.sub(color,1,2),16)/255 + local g = tonumber(string.sub(color,3,4),16)/255 + local b = tonumber(string.sub(color,5,6),16)/255 + local a = tonumber(string.sub(color,7,8),16)/255 + Zone:SetColor({r, g, b}, a) + end + if objectData.fillColorString then + -- eg fillColorString = 0xff00004b + local color = string.gsub(objectData.colorString,"^0x","") + local r = tonumber(string.sub(color,1,2),16)/255 + local g = tonumber(string.sub(color,3,4),16)/255 + local b = tonumber(string.sub(color,5,6),16)/255 + local a = tonumber(string.sub(color,7,8),16)/255 + Zone:SetFillColor({r, g, b}, a) + end + -- Store in DB. self.ZONENAMES[ZoneName] = ZoneName @@ -756,7 +797,7 @@ end -- cargo --- Finds a CLIENT based on the ClientName. -- @param #DATABASE self --- @param #string ClientName +-- @param #string ClientName - Note this is the UNIT name of the client! -- @return Wrapper.Client#CLIENT The found CLIENT. function DATABASE:FindClient( ClientName ) From d803b51e847f1160ef27669d970204fa62696798 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 19 Dec 2023 10:19:44 +0100 Subject: [PATCH 41/95] #ZONE * Fixed filling/drawing of more complex polygon zones * Added function to (re-)fill polygon * Added function to (re-)draw polygon outline --- Moose Development/Moose/Core/Zone.lua | 169 ++++++++++++++++++-------- 1 file changed, 116 insertions(+), 53 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 09ef67c2f..33302b4a0 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2020,7 +2020,7 @@ _ZONE_TRIANGLE = { Coords={}, CenterVec2={x=0, y=0}, SurfaceArea=0, - DrawIDs={} + DrawID={} } --- -- @param #_ZONE_TRIANGLE self @@ -2100,15 +2100,35 @@ function _ZONE_TRIANGLE:Draw(Coalition, Color, Alpha, FillColor, FillAlpha, Line for i=1, #self.Coords do local c1 = self.Coords[i] local c2 = self.Coords[i % #self.Coords + 1] - table.add(self.DrawIDs, c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly)) + local id = c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly) + self.DrawID[#self.DrawID+1] = id end - return self.DrawIDs + local newID = self.Coords[1]:MarkupToAllFreeForm({self.Coords[2],self.Coords[3]},Coalition,Color,Alpha,FillColor,FillAlpha,LineType,ReadOnly) + self.DrawID[#self.DrawID+1] = newID + return self.DrawID +end + +--- Draw the triangle +-- @param #_ZONE_TRIANGLE self +-- @return #table of draw IDs +function _ZONE_TRIANGLE:Fill(Coalition, FillColor, FillAlpha, ReadOnly) + Coalition=Coalition or -1 + FillColor = FillColor + FillAlpha = FillAlpha + local newID = self.Coords[1]:MarkupToAllFreeForm({self.Coords[2],self.Coords[3]},Coalition,nil,nil,FillColor,FillAlpha,0,nil) + self.DrawID[#self.DrawID+1] = newID + return self.DrawID end --- -- @type ZONE_POLYGON_BASE -- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}. +-- @field #number SurfaceArea +-- @field #table DrawID +-- @field #table FillTriangles +-- @field #table _Triangles +-- @field #table Borderlines -- @extends #ZONE_BASE @@ -2133,9 +2153,11 @@ end -- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE = { ClassName="ZONE_POLYGON_BASE", - _Triangles={}, -- _ZONE_TRIANGLES + _Triangles={}, -- #table of #_ZONE_TRIANGLE SurfaceArea=0, - DrawID={} -- making a table out of the MarkID so its easier to draw an n-sided polygon, see ZONE_POLYGON_BASE:Draw() + DrawID={}, -- making a table out of the MarkID so its easier to draw an n-sided polygon, see ZONE_POLYGON_BASE:Draw() + FillTriangles = {}, + Borderlines = {}, } --- A 2D points array. @@ -2470,57 +2492,97 @@ end -- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work -- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work -- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. --- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. +-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.s -- @return #ZONE_POLYGON_BASE self function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, IncludeTriangles) - if self._.Polygon and #self._.Polygon >= 3 then - Coalition = Coalition or self:GetDrawCoalition() - - -- Set draw coalition. - self:SetDrawCoalition(Coalition) - - Color = Color or self:GetColorRGB() - Alpha = Alpha or 1 - - -- Set color. - self:SetColor(Color, Alpha) - - FillColor = FillColor or self:GetFillColorRGB() - if not FillColor then - UTILS.DeepCopy(Color) - end - FillAlpha = FillAlpha or self:GetFillColorAlpha() - if not FillAlpha then - FillAlpha = 0.15 - end - - -- Set fill color -----------> has fill color worked in recent versions of DCS? - -- doing something like - -- - -- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "") - -- - -- doesn't seem to fill in the shape for an n-sided polygon - self:SetFillColor(FillColor, FillAlpha) - - IncludeTriangles = IncludeTriangles or false - - -- just draw the triangles, we get the outline for free - if IncludeTriangles then - for _, triangle in pairs(self._Triangles) do - local draw_ids = triangle:Draw() - table.combine(self.DrawID, draw_ids) - end - -- draw outline only - else - local coords = self:GetVerticiesCoordinates() - for i = 1, #coords do - local c1 = coords[i] - local c2 = coords[i % #coords + 1] - table.add(self.DrawID, c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly)) - end - end + + if self._.Polygon and #self._.Polygon >= 3 then + Coalition = Coalition or self:GetDrawCoalition() + + -- Set draw coalition. + self:SetDrawCoalition(Coalition) + + Color = Color or self:GetColorRGB() + Alpha = Alpha or self:GetColorAlpha() + + FillColor = FillColor or self:GetFillColorRGB() + FillAlpha = FillAlpha or self:GetFillColorAlpha() + + if FillColor then + self:ReFill(Color,Alpha) end - return self + + if Color then + self:ReDrawBorderline(Color,Alpha,LineType) + end + end + + return self +end + +--- Change/Re-fill a Polygon Zone +-- @param #ZONE_POLYGON_BASE self +-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. +-- @param #number Alpha Transparency [0,1]. Default 1. +-- @return #ZONE_POLYGON_BASE self +function ZONE_POLYGON_BASE:ReFill(Color,Alpha) + local color = Color or self:GetFillColorRGB() or {1,0,0} + local alpha = Alpha or self:GetFillColorAlpha() or 1 + local coalition = self:GetDrawCoalition() or -1 + -- undraw if already filled + if #self.FillTriangles > 0 then + for _, triangle in pairs(self._Triangles) do + triangle:UndrawZone() + end + -- remove mark IDs + for _,_value in pairs(self.FillTriangles) do + table.remove_by_value(self.DrawID, _value) + end + self.FillTriangles = nil + self.FillTriangles = {} + end + -- refill + for _, triangle in pairs(self._Triangles) do + local draw_ids = triangle:Fill(coalition,color,alpha,nil) + self.FillTriangles = draw_ids + table.combine(self.DrawID, draw_ids) + end + return self +end + +--- Change/Re-draw the border of a Polygon Zone +-- @param #ZONE_POLYGON_BASE self +-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. +-- @param #number Alpha Transparency [0,1]. Default 1. +-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. +-- @return #ZONE_POLYGON_BASE +function ZONE_POLYGON_BASE:ReDrawBorderline(Color, Alpha, LineType) + local color = Color or self:GetFillColorRGB() or {1,0,0} + local alpha = Alpha or self:GetFillColorAlpha() or 1 + local coalition = self:GetDrawCoalition() or -1 + local linetype = LineType or 1 + -- undraw if already drawn + if #self.Borderlines > 0 then + for _, MarkID in pairs(self.Borderlines) do + trigger.action.removeMark(MarkID) + end + -- remove mark IDs + for _,_value in pairs(self.Borderlines) do + table.remove_by_value(self.DrawID, _value) + end + self.Borderlines = nil + self.Borderlines = {} + end + -- Redraw border + local coords = self:GetVerticiesCoordinates() + for i = 1, #coords do + local c1 = coords[i] + local c2 = coords[i % #coords + 1] + local newID = c1:LineToAll(c2, coalition, color, alpha, linetype, nil) + self.DrawID[#self.DrawID+1]=newID + self.Borderlines[#self.Borderlines+1] = newID + end + return self end --- Get the surface area of this polygon @@ -2856,6 +2918,7 @@ function ZONE_POLYGON_BASE:Boundary(Coalition, Color, Radius, Alpha, Segments, C Alpha = Alpha or 1 Segments = Segments or 10 Closed = Closed or false + local Limit local i = 1 local j = #self._.Polygon if (Closed) then From 465ec216ea6e1dce689a3ae43aa54331c08bf457 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 19 Dec 2023 12:11:09 +0100 Subject: [PATCH 42/95] #CONTROLLABLE - Option ECM --- .../Moose/Wrapper/Controllable.lua | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index e7d4ca3c9..0bed05a72 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -3772,54 +3772,66 @@ function CONTROLLABLE:OptionProhibitAfterburner( Prohibit ) return self end ---- Defines the usage of Electronic Counter Measures by airborne forces. Disables the ability for AI to use their ECM. +--- [Air] Defines the usage of Electronic Counter Measures by airborne forces. -- @param #CONTROLLABLE self +-- @param #number ECMvalue Can be - 0=Never on, 1=if locked by radar, 2=if detected by radar, 3=always on, defaults to 1 -- @return #CONTROLLABLE self -function CONTROLLABLE:OptionECM_Never() +function CONTROLLABLE:OptionECM( ECMvalue ) self:F2( { self.ControllableName } ) - if self:IsAir() then - self:SetOption( AI.Option.Air.id.ECM_USING, 0 ) + local DCSControllable = self:GetDCSObject() + if DCSControllable then + local Controller = self:_GetController() + + if self:IsAir() then + Controller:setOption( AI.Option.Air.id.ECM_USING, ECMvalue or 1 ) + end + end return self end ---- Defines the usage of Electronic Counter Measures by airborne forces. If the AI is actively being locked by an enemy radar they will enable their ECM jammer. +--- [Air] Defines the usage of Electronic Counter Measures by airborne forces. Disables the ability for AI to use their ECM. +-- @param #CONTROLLABLE self +-- @return #CONTROLLABLE self +function CONTROLLABLE:OptionECM_Never() + self:F2( { self.ControllableName } ) + + self:OptionECM(0) + + return self +end + +--- [Air] Defines the usage of Electronic Counter Measures by airborne forces. If the AI is actively being locked by an enemy radar they will enable their ECM jammer. -- @param #CONTROLLABLE self -- @return #CONTROLLABLE self function CONTROLLABLE:OptionECM_OnlyLockByRadar() self:F2( { self.ControllableName } ) - if self:IsAir() then - self:SetOption( AI.Option.Air.id.ECM_USING, 1 ) - end + self:OptionECM(1) return self end ---- Defines the usage of Electronic Counter Measures by airborne forces. If the AI is being detected by a radar they will enable their ECM. +--- [Air] Defines the usage of Electronic Counter Measures by airborne forces. If the AI is being detected by a radar they will enable their ECM. -- @param #CONTROLLABLE self -- @return #CONTROLLABLE self function CONTROLLABLE:OptionECM_DetectedLockByRadar() self:F2( { self.ControllableName } ) - if self:IsAir() then - self:SetOption( AI.Option.Air.id.ECM_USING, 2 ) - end + self:OptionECM(2) return self end ---- Defines the usage of Electronic Counter Measures by airborne forces. AI will leave their ECM on all the time. +--- [Air] Defines the usage of Electronic Counter Measures by airborne forces. AI will leave their ECM on all the time. -- @param #CONTROLLABLE self -- @return #CONTROLLABLE self function CONTROLLABLE:OptionECM_AlwaysOn() self:F2( { self.ControllableName } ) - if self:IsAir() then - self:SetOption( AI.Option.Air.id.ECM_USING, 3 ) - end + self:OptionECM(3) return self end From 702ec75935683fc0cc89c617408bc36a39daded7 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 19 Dec 2023 17:38:31 +0100 Subject: [PATCH 43/95] Small fix --- Moose Development/Moose/Core/Zone.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 33302b4a0..15570ee87 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2509,7 +2509,7 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph FillAlpha = FillAlpha or self:GetFillColorAlpha() if FillColor then - self:ReFill(Color,Alpha) + self:ReFill(FillColor,FillAlpha) end if Color then From ebe486c69a8111181e9ab90063dda7a7bc14d2de Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 20 Dec 2023 10:07:42 +0100 Subject: [PATCH 44/95] UTILS * Small fix for UH60L door checker --- Moose Development/Moose/Utilities/Utils.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 9fe3d9823..49739f2a6 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1064,9 +1064,9 @@ function UTILS.BeaufortScale(speed) return bn,bd end ---- Split string at seperators. C.f. [split-string-in-lua](http://stackoverflow.com/questions/1426954/split-string-in-lua). +--- Split string at separators. C.f. [split-string-in-lua](http://stackoverflow.com/questions/1426954/split-string-in-lua). -- @param #string str Sting to split. --- @param #string sep Speparator for split. +-- @param #string sep Separator for split. -- @return #table Split text. function UTILS.Split(str, sep) local result = {} @@ -2146,17 +2146,17 @@ function UTILS.IsLoadingDoorOpen( unit_name ) return true end - if string.find(type_name, "Bell-47") then -- bell aint got no doors so always ready to load injured soldiers + if type_name == "Bell-47" then -- bell aint got no doors so always ready to load injured soldiers BASE:T(unit_name .. " door is open") return true end - - if string.find(type_name, "UH-60L") and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then + + if type_name == "UH-60L" and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then BASE:T(unit_name .. " cargo door is open") return true end - if string.find(type_name, "UH-60L" ) and (unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(400) == 1 ) then + if type_name == "UH-60L" and (unit:getDrawArgumentValue(38) > 0 or unit:getDrawArgumentValue(400) == 1 ) then BASE:T(unit_name .. " front door(s) are open") return true end From 0d18ce086c7a2d31d5f919fb2ec10a0cd7668e89 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 21 Dec 2023 22:33:08 +0100 Subject: [PATCH 45/95] Update Zone.lua --- Moose Development/Moose/Core/Zone.lua | 68 --------------------------- 1 file changed, 68 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 2b5b1d4f3..d9abec36e 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2509,74 +2509,6 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph end - if self._.Polygon and #self._.Polygon >= 3 then - Coalition = Coalition or self:GetDrawCoalition() - - -- Set draw coalition. - self:SetDrawCoalition(Coalition) - - Color = Color or self:GetColorRGB() - Alpha = Alpha or 1 - - -- Set color. - self:SetColor(Color, Alpha) - - FillColor = FillColor or self:GetFillColorRGB() - if not FillColor then - UTILS.DeepCopy(Color) - end - FillAlpha = FillAlpha or self:GetFillColorAlpha() - if not FillAlpha then - FillAlpha = 0.15 - end - - -- Set fill color -----------> has fill color worked in recent versions of DCS? - -- doing something like - -- - -- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "") - -- - -- doesn't seem to fill in the shape for an n-sided polygon - self:SetFillColor(FillColor, FillAlpha) - - IncludeTriangles = IncludeTriangles or false - - -- just draw the triangles, we get the outline for free - if IncludeTriangles then - for _, triangle in pairs(self._Triangles) do - local draw_ids = triangle:Draw() - table.combine(self.DrawID, draw_ids) - end - -- draw outline only - else - local coords = self:GetVerticiesCoordinates() - for i = 1, #coords do - local c1 = coords[i] - local c2 = coords[i % #coords + 1] - table.add(self.DrawID, c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly)) - end - end - - if self._.Polygon and #self._.Polygon >= 3 then - Coalition = Coalition or self:GetDrawCoalition() - - -- Set draw coalition. - self:SetDrawCoalition(Coalition) - - Color = Color or self:GetColorRGB() - Alpha = Alpha or self:GetColorAlpha() - - FillColor = FillColor or self:GetFillColorRGB() - FillAlpha = FillAlpha or self:GetFillColorAlpha() - - if FillColor then - self:ReFill(FillColor,FillAlpha) - end - - if Color then - self:ReDrawBorderline(Color,Alpha,LineType) - end - end - return self end From e89b921f3e64aae1235fb8c83c47b0f8983275dc Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 22 Dec 2023 10:27:00 +0100 Subject: [PATCH 46/95] Update Zone.lua --- Moose Development/Moose/Core/Zone.lua | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index d9abec36e..3317df260 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2496,6 +2496,30 @@ end -- @return #ZONE_POLYGON_BASE self function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, IncludeTriangles) + + if self._.Polygon and #self._.Polygon >= 3 then + Coalition = Coalition or self:GetDrawCoalition() + + -- Set draw coalition. + self:SetDrawCoalition(Coalition) + + Color = Color or self:GetColorRGB() + Alpha = Alpha or self:GetColorAlpha() + + FillColor = FillColor or self:GetFillColorRGB() + FillAlpha = FillAlpha or self:GetFillColorAlpha() + + if FillColor then + self:ReFill(FillColor,FillAlpha) + end + + if Color then + self:ReDrawBorderline(Color,Alpha,LineType) + end + end + + + if false then local coords = self:GetVerticiesCoordinates() local coord=coords[1] --Core.Point#COORDINATE @@ -2507,7 +2531,7 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph if true then return end - + end return self end From f86b3505b26c322aaf649efb1ef6d40d3fdfc1ee Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 22 Dec 2023 10:36:25 +0100 Subject: [PATCH 47/95] Update Airboss.lua - Fixed Attitude Monitor --- Moose Development/Moose/Ops/Airboss.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index c8710d3bf..cb732bb4f 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -11196,7 +11196,7 @@ function AIRBOSS:_AttitudeMonitor( playerData ) end text = text .. string.format( "\nPitch=%.1f° | Roll=%.1f° | Yaw=%.1f°", pitch, roll, yaw ) text = text .. string.format( "\nClimb Angle=%.1f° | Rate=%d ft/min", unit:GetClimbAngle(), velo.y * 196.85 ) - local dist = self:_GetOptLandingCoordinate():Get3DDistance( playerData.unit ) + local dist = self:_GetOptLandingCoordinate():Get3DDistance( playerData.unit:GetVec3() ) -- Get player velocity in km/h. local vplayer = playerData.unit:GetVelocityKMH() -- Get carrier velocity in km/h. From e7fb073bab0364793c2c9cbf4e1dd282263a2bed Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 22 Dec 2023 21:11:30 +0100 Subject: [PATCH 48/95] Update RAT.lua - Removed restriction that zones need to be defined in the ME --- Moose Development/Moose/Functional/RAT.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 57a0783da..ae6408d76 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -170,7 +170,7 @@ -- -- * A specific departure and/or destination airport can be chosen. -- * Valid coalitions can be set, e.g. only red, blue or neutral, all three "colours". --- * It is possible to start in air within a zone defined in the mission editor or within a zone above an airport of the map. +-- * It is possible to start in air within a zone or within a zone above an airport of the map. -- -- ## Flight Plan -- @@ -1179,13 +1179,13 @@ function RAT:SetTakeoffAir() return self end ---- Set possible departure ports. This can be an airport or a zone defined in the mission editor. +--- Set possible departure ports. This can be an airport or a zone. -- @param #RAT self -- @param #string departurenames Name or table of names of departure airports or zones. -- @return #RAT RAT self object. -- @usage RAT:SetDeparture("Sochi-Adler") will spawn RAT objects at Sochi-Adler airport. -- @usage RAT:SetDeparture({"Sochi-Adler", "Gudauta"}) will spawn RAT aircraft radomly at Sochi-Adler or Gudauta airport. --- @usage RAT:SetDeparture({"Zone A", "Gudauta"}) will spawn RAT aircraft in air randomly within Zone A, which has to be defined in the mission editor, or within a zone around Gudauta airport. Note that this also requires RAT:takeoff("air") to be set. +-- @usage RAT:SetDeparture({"Zone A", "Gudauta"}) will spawn RAT aircraft in air randomly within Zone A, or within a zone around Gudauta airport. Note that this also requires RAT:takeoff("air") to be set. function RAT:SetDeparture(departurenames) self:F2(departurenames) @@ -2537,7 +2537,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) end elseif self:_ZoneExists(_departure) then -- If it's not an airport, check whether it's a zone. - departure=ZONE:New(_departure) + departure=ZONE:FindByName(_departure) else local text=string.format("ERROR! Specified departure airport %s does not exist for %s.", _departure, self.alias) self:E(RAT.id..text) @@ -2635,7 +2635,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) end elseif self:_ZoneExists(_destination) then - destination=ZONE:New(_destination) + destination=ZONE:FindByName(_destination) else local text=string.format("ERROR: Specified destination airport/zone %s does not exist for %s!", _destination, self.alias) self:E(RAT.id.."ERROR: "..text) @@ -3142,7 +3142,7 @@ function RAT:_PickDeparture(takeoff) end elseif self:_ZoneExists(name) then if takeoff==RAT.wp.air then - dep=ZONE:New(name) + dep=ZONE:FindByName(name) else self:E(RAT.id..string.format("ERROR! Takeoff is not in air. Cannot use %s as departure.", name)) end @@ -3254,7 +3254,7 @@ function RAT:_PickDestination(departure, q, minrange, maxrange, random, landing) end elseif self:_ZoneExists(name) then if landing==RAT.wp.air then - dest=ZONE:New(name) + dest=ZONE:FindByName(name) else self:E(RAT.id..string.format("ERROR! Landing is not in air. Cannot use zone %s as destination!", name)) end @@ -4930,12 +4930,12 @@ function RAT:_AirportExists(name) return false end ---- Test if a trigger zone defined in the mission editor exists. +--- Test if a zone exists. -- @param #RAT self -- @param #string name -- @return #boolean True if zone exsits, false otherwise. function RAT:_ZoneExists(name) - local z=trigger.misc.getZone(name) + local z=ZONE:FindByName(name) --trigger.misc.getZone(name) as suggested by @Viking on MOOSE discord #rat if z then return true end From 7cc040c23489047b68a376827c7b8ffd1b609d51 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 23 Dec 2023 14:51:55 +0100 Subject: [PATCH 49/95] #RANGE * Fixed Range trying to find a pilot on each and every birth event... --- Moose Development/Moose/Functional/Range.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 8da132f14..5b4d52623 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -1737,7 +1737,9 @@ end -- @param Core.Event#EVENTDATA EventData function RANGE:OnEventBirth( EventData ) self:F( { eventbirth = EventData } ) - + + if not EventData.IniPlayerName then return end + local _unitName = EventData.IniUnitName local _unit, _playername = self:_GetPlayerUnitAndName( _unitName ) From c7ef270d4d126cd52add2fcfd819c874e28e63bc Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 23 Dec 2023 14:53:11 +0100 Subject: [PATCH 50/95] #FLIGHTCONTROL * Added a delay in spawning the parking guard so in MP we have time to get the heading of the Player's plane --- Moose Development/Moose/Ops/FlightControl.lua | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 8403e0c24..112834e53 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -4424,14 +4424,11 @@ end -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Add parking guard in front of a parking aircraft. +--- [INTERNAL] Add parking guard in front of a parking aircraft - delayed for MP. -- @param #FLIGHTCONTROL self -- @param Wrapper.Unit#UNIT unit The aircraft. -function FLIGHTCONTROL:SpawnParkingGuard(unit) - - if unit and self.parkingGuard then - - -- Position of the unit. +function FLIGHTCONTROL:_SpawnParkingGuard(unit) + -- Position of the unit. local coordinate=unit:GetCoordinate() -- Parking spot. @@ -4478,6 +4475,17 @@ function FLIGHTCONTROL:SpawnParkingGuard(unit) else self:E(self.lid.."ERROR: Parking Guard already exists!") end +end + +--- Add parking guard in front of a parking aircraft. +-- @param #FLIGHTCONTROL self +-- @param Wrapper.Unit#UNIT unit The aircraft. +function FLIGHTCONTROL:SpawnParkingGuard(unit) + + if unit and self.parkingGuard then + + -- Schedule delay so in MP we get the heading of the client's plane + self:ScheduleOnce(1,FLIGHTCONTROL._SpawnParkingGuard,self,unit) end From 9ec92a8fcaaa617039fb0ccd18437be75d5e57f4 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 23 Dec 2023 15:57:27 +0100 Subject: [PATCH 51/95] SRS - Refactoring --- Moose Development/Moose/Ops/ATIS.lua | 6 +- Moose Development/Moose/Sound/SRS.lua | 471 +++++++++++++++----------- 2 files changed, 279 insertions(+), 198 deletions(-) diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 6819ee36c..064ab1880 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -890,7 +890,7 @@ _ATIS = {} --- ATIS class version. -- @field #string version -ATIS.version = "0.10.4" +ATIS.version = "1.0.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1528,12 +1528,12 @@ end --- Use SRS Simple-Text-To-Speech for transmissions. No sound files necessary. -- @param #ATIS self --- @param #string PathToSRS Path to SRS directory. +-- @param #string PathToSRS Path to SRS directory (only necessary if SRS exe backend is used). -- @param #string Gender Gender: "male" or "female" (default). -- @param #string Culture Culture, e.g. "en-GB" (default). -- @param #string Voice Specific voice. Overrides `Gender` and `Culture`. -- @param #number Port SRS port. Default 5002. --- @param #string GoogleKey Path to Google JSON-Key. +-- @param #string GoogleKey Path to Google JSON-Key (SRS exe backend) or Google API key (DCS-gRPC backend). -- @return #ATIS self function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey) if PathToSRS or MSRS.path then diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 82689f9e5..899d41fba 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -42,17 +42,16 @@ -- @field #number volume Volume between 0 (min) and 1 (max). Default 1. -- @field #string culture Culture. Default "en-GB". -- @field #string gender Gender. Default "female". --- @field #string voice Specific voice. +-- @field #string voice Specific voice. Only used if no explicit provider voice specified. -- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send. --- @field #string path Path to the SRS exe. This includes the final slash "/". --- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". +-- @field #string path Path to the SRS exe. -- @field #string Label Label showing up on the SRS radio overlay. Default is "ROBOT". No spaces allowed. --- @field #table AltBackend Table containing functions and variables to enable an alternate backend to transmit to SRS. -- @field #string ConfigFileName Name of the standard config file. -- @field #string ConfigFilePath Path to the standard config file. -- @field #boolean ConfigLoaded If `true` if config file was loaded. --- @field #string ttsprovider Default provider TTS backend, e.g. "Google" or "Microsoft", default is Microsoft. -- @field #table poptions Provider options. Each element is a data structure of type `MSRS.ProvierOptions`. +-- @field #string provider Provider of TTS (win, gcloud, azure, amazon). +-- @field #string backend Backend used as interface to SRS (MSRS.Backend.SRSEXE or MSRS.Backend.GRPC). -- @extends Core.Base#BASE --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde @@ -106,29 +105,18 @@ -- -- Use a specific "culture" with the @{#MSRS.SetCulture} function, e.g. `:SetCulture("en-US")` or `:SetCulture("de-DE")`. -- --- ## Set Google --- --- Use Google's text-to-speech engine with the @{#MSRS.SetGoogle} function, e.g. ':SetGoogle()'. --- By enabling this it also allows you to utilize SSML in your text for added flexibility. --- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech --- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml --- --- --- **Pro-Tip** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing. --- and also the Google Console error, in case you have missed a step in setting up your Google TTS. --- E.g. `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"` --- Plays a message on 255 MHz AM for the blue coalition in-game. --- -- ## Set Voice -- -- Use a specific voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Note that this must be installed on your windows system. --- If enabling SetGoogle(), you can use voices provided by Google --- Google's supported voices: https://cloud.google.com/text-to-speech/docs/voices +-- +-- Note that you can set voices for each provider via the @{#MSRS.SetVoiceProvider} function. Also shortcuts are available, *i.e.* +-- @{#MSRS.SetVoiceWindows}, @{#MSRS.SetVoiceGoogle}, @{#MSRS.SetVoiceAzure} and @{#MSRS.SetVoiceAmazon}. +-- -- For voices there are enumerators in this class to help you out on voice names: -- --- MSRS.Voices.Microsoft -- e.g. MSRS.Voices.Microsoft.Hedda - the Microsoft enumerator contains all voices known to work with SRS --- MSRS.Voices.Google -- e.g. MSRS.Voices.Google.Standard.en_AU_Standard_A or MSRS.Voices.Google.Wavenet.de_DE_Wavenet_C - The Google enumerator contains voices for EN, DE, IT, FR and ES. +-- MSRS.Voices.Microsoft -- e.g. MSRS.Voices.Microsoft.Hedda - the Microsoft enumerator contains all voices known to work with SRS +-- MSRS.Voices.Google -- e.g. MSRS.Voices.Google.Standard.en_AU_Standard_A or MSRS.Voices.Google.Wavenet.de_DE_Wavenet_C - The Google enumerator contains voices for EN, DE, IT, FR and ES. -- -- ## Set Coordinate -- @@ -146,8 +134,63 @@ -- ## Config file for many variables, auto-loaded by Moose -- -- See @{#MSRS.LoadConfigFile} for details on how to set this up. +-- +-- ## TTS Providers +-- +-- The default provider for generating speech from text is the native Windows TTS service. Note that you need to install the voices you want to use. +-- +-- **Pro-Tip** - use the command line with power shell to call `DCS-SR-ExternalAudio.exe` - it will tell you what is missing, +-- and also the Google Console error, in case you have missed a step in setting up your Google TTS. +-- For example, `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"` +-- plays a message on 255 MHz AM for the blue coalition in-game. +-- +-- ### Google +-- +-- In order to use Google Cloud for TTS you need to use @{#MSRS.SetProvider} and @{MSRS.SetProviderOptionsGoogle} functions: +-- +-- msrs:SetProvider(MSRS.Provider.GOOGLE) +-- msrs:SetProviderOptionsGoogle(CredentialsFile, AccessKey) +-- +-- The parameter `CredentialsFile` is used with the default 'DCS-SR-ExternalAudio.exe' backend and must be the full path to the credentials JSON file. +-- The `AccessKey` parameter is used with the DCS-gRPC backend (see below). +-- +-- You can set the voice to use with Google via @{#MSRS.SetVoiceGoogle}. +-- +-- When using Google it also allows you to utilize SSML in your text for more flexibility. +-- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech +-- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml +-- +-- ### Amazon Web Service [Only DCS-gRPC backend] +-- +-- In order to use Amazon Web Service (ASW) for TTS you need to use @{#MSRS.SetProvider} and @{MSRS.SetProviderOptionsAmazon} functions: +-- +-- msrs:SetProvider(MSRS.Provider.AMAZON) +-- msrs:SetProviderOptionsAmazon(AccessKey, SecretKey, Region) +-- +-- The parameters `AccessKey` and `SecretKey` are your ASW access and secret keys, respectively. The parameter `Region` is your [ASW region](https://docs.aws.amazon.com/general/latest/gr/pol.html). +-- +-- You can set the voice to use with ASW via @{#MSRS.SetVoiceAmazon}. +-- +-- ### Microsoft Azure [Only DCS-gRPC backend] +-- +-- In order to use Microsoft Azure for TTS you need to use @{#MSRS.SetProvider} and @{MSRS.SetProviderOptionsAzure} functions: +-- +-- msrs:SetProvider(MSRS.Provider.AZURE) +-- msrs:SetProviderOptionsAmazon(AccessKey, Region) +-- +-- The parameter `AccessKey` is your Azure access key. The parameter `Region` is your [Azure region](https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/regions). +-- +-- You can set the voice to use with Azure via @{#MSRS.SetVoiceAzure}. +-- +-- ## Backend +-- +-- The default interface to SRS is via calling the 'DCS-SR-ExternalAudio.exe'. As noted above, this has the unavoidable drawback that a pop-up briefly appears +-- and DCS might be put out of focus. +-- +-- ## DCS-gRPC as an alternative to 'DCS-SR-ExternalAudio.exe' for TTS -- --- ## Set DCS-gRPC as an alternative to 'DCS-SR-ExternalAudio.exe' for TTS +-- Another interface to SRS is [DCS-gRPC](https://github.com/DCS-gRPC/rust-server). This does not call an exe file and therefore avoids the annoying pop-up window. +-- In addition to Windows and Google cloud, it also offers Microsoft Azure and Amazon Web Service as providers for TTS. -- -- Use @{#MSRS.SetDefaultBackendGRPC} to enable [DCS-gRPC](https://github.com/DCS-gRPC/rust-server) as an alternate backend for transmitting text-to-speech over SRS. -- This can be useful if 'DCS-SR-ExternalAudio.exe' cannot be used in the environment, or to use Azure or AWS clouds for TTS. Note that DCS-gRPC does not (yet?) support @@ -217,7 +260,7 @@ MSRS = { --- MSRS class version. -- @field #string version -MSRS.version="0.2.0" +MSRS.version="0.3.0" --- Voices -- @type MSRS.Voices @@ -323,7 +366,7 @@ MSRS.Voices = { --- Backend options to communicate with SRS. -- @type MSRS.Backend --- @field #string SRSEXE Use SRS exe. +-- @field #string SRSEXE Use `DCS-SR-ExternalAudio.exe`. -- @field #string GRPC Use DCS-gRPC. MSRS.Backend = { SRSEXE = "srsexe", @@ -350,8 +393,8 @@ MSRS.Provider = { -- @field #string key Access key (DCS-gRPC with Google, ASW, AZURE as provider). -- @field #string secret Secret key (DCS-gRPC with ASW as provider) -- @field #string region Region. --- @field #string defaultVoice Default voice. --- @field #string voice +-- @field #string defaultVoice Default voice (not used). +-- @field #string voice Voice used. --- GRPC options. -- @type MSRS.GRPCOptions @@ -369,8 +412,8 @@ MSRS.Provider = { -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Refactoring of input/config file. --- TODO: Refactoring gRPC backend. +-- DONE: Refactoring of input/config file. +-- DONE: Refactoring gRPC backend. -- TODO: Add functions to remove freqs and modulations. -- DONE: Add coordinate. -- DONE: Add google. @@ -382,15 +425,16 @@ MSRS.Provider = { ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Create a new MSRS object. Required argument is the frequency and modulation. --- Other parameters are read from the `Moose_MSRS.lua` config file. If you do not have that file set up you must set up and use the SRS-TTS.exe (not DCS-gRPC) as backend, you need to still +-- Other parameters are read from the `Moose_MSRS.lua` config file. If you do not have that file set up you must set up and use the `DCS-SR-ExternalAudio.exe` (not DCS-gRPC) as backend, you need to still -- set the path to the exe file via @{#MSRS.SetPath}. -- -- @param #MSRS self +-- @param #string Path Path to SRS directory. Default `C:\\Program Files\\DCS-SimpleRadio-Standalone`. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies. -- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations. -- @param #string Backend Backend used: `MSRS.Backend.SRSEXE` (default) or `MSRS.Backend.GRPC`. -- @return #MSRS self -function MSRS:New(Frequency, Modulation, Backend) +function MSRS:New(Path, Frequency, Modulation, Backend) -- Defaults. Frequency = Frequency or 143 @@ -404,7 +448,7 @@ function MSRS:New(Frequency, Modulation, Backend) if not self.ConfigLoaded then -- Defaults. - self:SetPath() + self:SetPath(Path) self:SetPort() self:SetFrequencies(Frequency) self:SetModulations(Modulation) @@ -415,11 +459,18 @@ function MSRS:New(Frequency, Modulation, Backend) else - -- there might be some overwrites from :New() + -- Default wverwrites from :New() + + if Path then + self:SetPath(Path) + end if Frequency then - self:SetFrequencies(Frequency) - self:SetModulations(Modulation) + self:SetFrequencies(Frequency) + end + + if Modulation then + self:SetModulations(Modulation) end end @@ -440,7 +491,7 @@ end --- Set backend to communicate with SRS. -- There are two options: -- --- - `MSRS.Backend.SRSEXE`: This is the default and uses the SRS.exe. +-- - `MSRS.Backend.SRSEXE`: This is the default and uses the `DCS-SR-ExternalAudio.exe`. -- - `MSRS.Backend.GRPC`: Via DCS-gRPC. -- -- @param #MSRS self @@ -463,7 +514,7 @@ function MSRS:SetBackendGRPC() return self end ---- Set SRS-TTS.exe as backend to communicate with SRS. +--- Set `DCS-SR-ExternalAudio.exe` as backend to communicate with SRS. -- @param #MSRS self -- @return #MSRS self function MSRS:SetBackendSRSEXE(Backend) @@ -473,6 +524,17 @@ function MSRS:SetBackendSRSEXE(Backend) return self end +--- Set the default backend. +-- @param #MSRS self +function MSRS.SetDefaultBackend(Backend) + MSRS.backend=Backend or MSRS.Backend.SRSEXE +end + +--- Set DCS-gRPC to be the default backend. +-- @param #MSRS self +function MSRS.SetDefaultBackendGRPC() + MSRS.backend=MSRS.Backend.GRPC +end --- Get currently set backend. -- @param #MSRS self @@ -481,31 +543,25 @@ function MSRS:GetBackend() return self.backend end ---- Set path to SRS install directory. More precisely, path to where the DCS- +--- Set path to SRS install directory. More precisely, path to where the `DCS-SR-ExternalAudio.exe` is located. -- @param #MSRS self --- @param #string Path Path to the directory, where the sound file is located. +-- @param #string Path Path to the directory, where the sound file is located. Default is `C:\\Program Files\\DCS-SimpleRadio-Standalone`. -- @return #MSRS self function MSRS:SetPath(Path) - if Path==nil and not self.path then - self:E("ERROR: No path to SRS directory specified!") - return nil + -- Set path. + self.path=Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" + + -- Remove (back)slashes. + local n=1 ; local nmax=1000 + while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do + self.path=self.path:sub(1,#self.path-1) + n=n+1 end - if Path then - -- Set path. - self.path=Path - - -- Remove (back)slashes. - local n=1 ; local nmax=1000 - while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do - self.path=self.path:sub(1,#self.path-1) - n=n+1 - end - - -- Debug output. - self:T(string.format("SRS path=%s", self:GetPath())) - end + -- Debug output. + self:T(string.format("SRS path=%s", self:GetPath())) + return self end @@ -669,7 +725,7 @@ end --- Set culture. -- @param #MSRS self --- @param #string Culture Culture, e.g. "en-GB". +-- @param #string Culture Culture, *e.g.* "en-GB". -- @return #MSRS self function MSRS:SetCulture(Culture) @@ -691,14 +747,59 @@ end --- Set to use a specific voice for a given provider. Note that this will override any gender and culture settings. -- @param #MSRS self --- @param #string Provider Provider. Default `MSRS.Provider.WINDOWS`. -- @param #string Voice Voice. +-- @param #string Provider Provider. Default is as set by @{#MSRS.SetProvider}, which itself defaults to `MSRS.Provider.WINDOWS` if not set. -- @return #MSRS self -function MSRS:SetProviderVoice(Provider, Voice) +function MSRS:SetVoiceProvider(Voice, Provider) self.poptions=self.poptions or {} - self.poptions[Provider or MSRS.Provider.WINDOWSo]=Voice + self.poptions[Provider or self:GetProvider()]=Voice + + return self +end + +--- Set to use a specific voice if Microsoft Windows' native TTS is use as provider. Note that this will override any gender and culture settings. +-- @param #MSRS self +-- @param #string Voice Voice. Default `"Microsoft Hazel Desktop"`. +-- @return #MSRS self +function MSRS:SetVoiceWindows(Voice) + + self:SetVoiceProvider(Voice or "Microsoft Hazel Desktop", MSRS.Provider.WINDOWS) + + return self +end + +--- Set to use a specific voice if Google is use as provider. Note that this will override any gender and culture settings. +-- @param #MSRS self +-- @param #string Voice Voice. Default `MSRS.Voices.Google.Standard.en_GB_Standard_A`. +-- @return #MSRS self +function MSRS:SetVoiceGoogle(Voice) + + self:SetVoiceProvider(Voice or MSRS.Voices.Google.Standard.en_GB_Standard_A, MSRS.Provider.GOOGLE) + + return self +end + + +--- Set to use a specific voice if Microsoft Azure is use as provider (only DCS-gRPC backend). Note that this will override any gender and culture settings. +-- @param #MSRS self +-- @param #string Voice [Azure Voice](https://learn.microsoft.com/azure/cognitive-services/speech-service/language-support). Default `"en-US-AriaNeural"`. +-- @return #MSRS self +function MSRS:SetVoiceAzure(Voice) + + self:SetVoiceProvider(Voice or "en-US-AriaNeural", MSRS.Provider.AZURE) + + return self +end + +--- Set to use a specific voice if Amazon Web Service is use as provider (only DCS-gRPC backend). Note that this will override any gender and culture settings. +-- @param #MSRS self +-- @param #string Voice [ASW Voice](https://docs.aws.amazon.com/polly/latest/dg/voicelist.html). Default `"Brian"`. +-- @return #MSRS self +function MSRS:SetVoiceAmazon(Voice) + + self:SetVoiceProvider(Voice or "Brian", MSRS.Provider.AMAZON) return self end @@ -706,12 +807,12 @@ end --- Get voice. -- @param #MSRS self -- @param #string Provider Provider. Default is the currently set provider (`self.provider`). --- @return #MSRS self +-- @return #string Voice. function MSRS:GetVoice(Provider) Provider=Provider or self.provider - if Provider then + if Provider and self.poptions[Provider] and self.poptions[Provider].voice then return self.poptions[Provider].voice else return self.voice @@ -730,7 +831,7 @@ function MSRS:SetCoordinate(Coordinate) return self end ---- Use google text-to-speech credentials. Also sets Google as default TTS provider. +--- **[Deprecated]** Use google text-to-speech credentials. Also sets Google as default TTS provider. -- @param #MSRS self -- @param #string PathToCredentials Full path to the google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". Can also be the Google API key. -- @return #MSRS self @@ -738,29 +839,30 @@ function MSRS:SetGoogle(PathToCredentials) if PathToCredentials then - self.google=PathToCredentials - self.APIKey=PathToCredentials - self.provider = "gcloud" - - self.GRPCOptions.DefaultProvider = "gcloud" - self.GRPCOptions.gcloud.key = PathToCredentials - self.ttsprovider = "Google" + self.provider = MSRS.Provider.GOOGLE + + self:SetProviderOptionsGoogle(PathToCredentials, PathToCredentials) end return self end ---- gRPC Backend: Use google text-to-speech set the API key. +--- **[Deprecated]** Use google text-to-speech set the API key (only for DCS-gRPC). -- @param #MSRS self -- @param #string APIKey API Key, usually a string of length 40 with characters and numbers. -- @return #MSRS self function MSRS:SetGoogleAPIKey(APIKey) if APIKey then - self.APIKey=APIKey - self.provider = "gcloud" - self.GRPCOptions.DefaultProvider = "gcloud" - self.GRPCOptions.gcloud.key = APIKey + + self.provider = MSRS.Provider.GOOGLE + + if self.poptions[MSRS.Provider.GOOGLE] then + self.poptions[MSRS.Provider.GOOGLE].key=APIKey + else + self:SetProviderOptionsGoogle(nil ,APIKey) + end + end return self end @@ -843,7 +945,7 @@ end --- Set provider options and credentials for Google Cloud. -- @param #MSRS self --- @param #string CredentialsFile Full path to your credentials file. For Google this is the path to a JSON file. This is used if SRS-TTS.exe is used as backend. +-- @param #string CredentialsFile Full path to your credentials file. For Google this is the path to a JSON file. This is used if `DCS-SR-ExternalAudio.exe` is used as backend. -- @param #string AccessKey Your API access key. This is necessary if DCS-gRPC is used as backend. -- @return #MSRS self function MSRS:SetProviderOptionsGoogle(CredentialsFile, AccessKey) @@ -853,6 +955,32 @@ function MSRS:SetProviderOptionsGoogle(CredentialsFile, AccessKey) return self end +--- Set provider options and credentials for Amazon Web Service (ASW). Only supported in combination with DCS-gRPC as backend. +-- @param #MSRS self +-- @param #string AccessKey Your API access key. +-- @param #string SecretKey Your secret key. +-- @param #string Region Your ASW [region](https://docs.aws.amazon.com/general/latest/gr/pol.html). +-- @return #MSRS self +function MSRS:SetProviderOptionsAmazon(AccessKey, SecretKey, Region) + + self:SetProviderOptions(MSRS.Provider.AMAZON, nil, AccessKey, SecretKey, Region) + + return self +end + +--- Set provider options and credentials for Microsoft Azure. Only supported in combination with DCS-gRPC as backend. +-- @param #MSRS self +-- @param #string AccessKey Your API access key. +-- @param #string Region Your Azure [region](https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/regions). +-- @return #MSRS self +function MSRS:SetProviderOptionsAzure(AccessKey, Region) + + self:SetProviderOptions(MSRS.Provider.AZURE, nil, AccessKey, nil, Region) + + return self +end + + --- Get provider options. -- @param #MSRS self -- @param #string Provider Provider. Default is as set via @{#MSRS.SetProvider}. @@ -862,22 +990,40 @@ function MSRS:GetProviderOptions(Provider) end ---- Use Google text-to-speech as default. +--- Use Google to provide text-to-speech. -- @param #MSRS self -- @return #MSRS self function MSRS:SetTTSProviderGoogle() - self.ttsprovider = "Google" + self:SetProvider(MSRS.Provider.GOOGLE) return self end ---- Use Microsoft text-to-speech as default. +--- Use Microsoft to provide text-to-speech. -- @param #MSRS self -- @return #MSRS self function MSRS:SetTTSProviderMicrosoft() - self.ttsprovider = "Microsoft" + self:SetProvider(MSRS.Provider.WINDOWS) return self end + +--- Use Microsoft Azure to provide text-to-speech. Only supported if used in combination with DCS-gRPC as backend. +-- @param #MSRS self +-- @return #MSRS self +function MSRS:SetTTSProviderAzure() + self:SetProvider(MSRS.Provider.AZURE) + return self +end + +--- Use Amazon Web Service (ASW) to provide text-to-speech. Only supported if used in combination with DCS-gRPC as backend. +-- @param #MSRS self +-- @return #MSRS self +function MSRS:SetTTSProviderAmazon() + self:SetProvider(MSRS.Provider.AMAZON) + return self +end + + --- Print SRS STTS help to DCS log file. -- @param #MSRS self -- @return #MSRS self @@ -1322,10 +1468,9 @@ end -- @return #MSRS self function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate) - --Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) - - self:I("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") - self:I({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) + -- Debug info. + self:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") + self:T({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) local options = {} -- #MSRS.GRPCOptions @@ -1369,35 +1514,23 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab local gender="" if self.gender then - --gender = ' gender=\"' .. self.gender .. '\"' gender=string.format(' gender=\"\%s\"', self.gender) end local language="" if self.culture then - --lang = ' language=\"' .. self.culture .. '\"' language=string.format(' language=\"\%s\"', self.culture) end - --- if self.culture or self.gender then --- preTag = '' --- postTag = '' --- ssml = preTag .. Text .. postTag --- end - + if self.gender or self.culture then ssml=string.format("%s", gender, language, Text) end end - env.info("FF freq") for _,freq in pairs(Frequencies) do - env.info("FF freq1") self:T("GRPC.tts") self:T(ssml) self:T(freq) self:T(options) - UTILS.PrintTableToLog(options) - env.info(UTILS.OneLineSerialize(options)) GRPC.tts(ssml, freq*1e6, options) end @@ -1418,36 +1551,46 @@ end -- 1) Create a config file named "Moose_MSRS.lua" at this location "C:\Users\\Saved Games\DCS\Config" (or wherever your Saved Games folder resides). -- 2) The file needs the following structure: -- --- -- Moose MSRS default Config --- MSRS_Config = { --- Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone", -- adjust as needed, note double \\ --- Port = 5002, -- adjust as needed --- Frequency = {127,243}, -- must be a table, 1..n entries! --- Modulation = {0,0}, -- must be a table, 1..n entries, one for each frequency! --- Volume = 1.0, -- 0.0 to 1.0 --- Coalition = 0, -- 0 = Neutral, 1 = Red, 2 = Blue --- Coordinate = {0,0,0}, -- x,y,altitude - optional, all in meters --- Culture = "en-GB", --- Gender = "male", --- Google = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- path to google json key file - optional. --- Label = "MSRS", --- Voice = "Microsoft Hazel Desktop", --- Provider = "Microsoft", -- this is the default TTS provider, e.g. "Google" or "Microsoft" --- -- gRPC (optional) --- GRPC = { -- see https://github.com/DCS-gRPC/rust-server --- coalition = "blue", -- blue, red, neutral --- DefaultProvider = "gcloud", -- win, gcloud, aws, or azure, some of the values below depend on your cloud provider --- gcloud = { --- key = "", -- for gRPC Google API key --- --secret = "", -- needed for aws --- --region = "",-- needed for aws --- defaultVoice = MSRS.Voices.Google.Standard.en_GB_Standard_F, --- }, --- win = { --- defaultVoice = "Hazel", --- }, --- } --- } +-- -- Moose MSRS default Config +-- MSRS_Config = { +-- Path = C:\\Program Files\\DCS-SimpleRadio-Standalone, -- Path to SRS install directory. +-- Port = 5002, -- Port of SRS server. Default 5002. +-- Backend = "srsexe", -- Interface to SRS: "srsexe" or "grpc". +-- Frequency = {127, 243}, -- Default frequences. Must be a table 1..n entries! +-- Modulation = {0,0}, -- Default modulations. Must be a table, 1..n entries, one for each frequency! +-- Volume = 1.0, -- Default volume [0,1]. +-- Coalition = 0, -- 0 = Neutral, 1 = Red, 2 = Blue (only a factor if SRS server has encryption enabled). +-- Coordinate = {0,0,0}, -- x, y, alt (only a factor if SRS server has line-of-sight and/or distance limit enabled). +-- Culture = "en-GB", +-- Gender = "male", +-- Voice = "Microsoft Hazel Desktop", -- Voice that is used if no explicit provider voice is specified. +-- Label = "MSRS", +-- Provider = "win", --Provider for generating TTS (win, gcloud, azure, asw). +-- +-- -- Windows +-- win = { +-- voice = "Microsoft Hazel Desktop", +-- }, +-- -- Google Cloud +-- gcloud = { +-- voice = "en-GB-Standard-A", -- The Google Cloud voice to use (see https://cloud.google.com/text-to-speech/docs/voices). +-- credentials="C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- Full path to credentials JSON file (only for SRS-TTS.exe backend) +-- key="Your access Key", -- Google API access key (only for DCS-gRPC backend) +-- }, +-- -- Amazon Web Service +-- aws = { +-- voice = "Brian", -- The default AWS voice to use (see https://docs.aws.amazon.com/polly/latest/dg/voicelist.html). +-- key="Your access Key", -- Your AWS key. +-- secret="Your secret key", -- Your AWS secret key. +-- region="eu-central-1", -- Your AWS region (see https://docs.aws.amazon.com/general/latest/gr/pol.html). +-- }, +-- -- Microsoft Azure +-- azure = { +-- voice="en-US-AriaNeural", --The default Azure voice to use (see https://learn.microsoft.com/azure/cognitive-services/speech-service/language-support). +-- key="Your access key", -- Your Azure access key. +-- region="westeurope", -- The Azure region to use (see https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/regions). +-- }, +-- } -- -- 3) The config file is automatically loaded when Moose starts. YOu can also load the config into the MSRS raw class manually before you do anything else: -- @@ -1472,11 +1615,11 @@ end -- -- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS(243,radio.modulation.AM,nil,nil,MSRS.Voices.Google.Standard.fr_FR_Standard_C) -- --- -- Create new ATIS as usual --- atis=ATIS:New(AIRBASE.Caucasus.Batumi, 123, radio.modulation.AM) --- atis:SetSRS(nil,nil,nil,MSRS.Voices.Google.Standard.en_US_Standard_H) --- --Start ATIS --- atis:Start() +-- -- Create new ATIS as usual +-- atis=ATIS:New(AIRBASE.Caucasus.Batumi, 123, radio.modulation.AM) +-- atis:SetSRS(nil,nil,nil,MSRS.Voices.Google.Standard.en_US_Standard_H) +-- --Start ATIS +-- atis:Start() function MSRS:LoadConfigFile(Path,Filename) if lfs == nil then @@ -1502,6 +1645,7 @@ function MSRS:LoadConfigFile(Path,Filename) Self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" Self.port = MSRS_Config.Port or 5002 + Self.backend = MSRS_Config.Backend or MSRS.Backend.SRSEXE Self.frequencies = MSRS_Config.Frequency or {127,243} Self.modulations = MSRS_Config.Modulation or {0,0} Self.coalition = MSRS_Config.Coalition or 0 @@ -1522,69 +1666,6 @@ function MSRS:LoadConfigFile(Path,Filename) Self.ConfigLoaded = true - if false then - - if self then - self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" - self.port = MSRS_Config.Port or 5002 - self.frequencies = MSRS_Config.Frequency or {127,243} - self.modulations = MSRS_Config.Modulation or {0,0} - self.coalition = MSRS_Config.Coalition or 0 - if MSRS_Config.Coordinate then - self.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1], MSRS_Config.Coordinate[2], MSRS_Config.Coordinate[3] ) - end - self.culture = MSRS_Config.Culture or "en-GB" - self.gender = MSRS_Config.Gender or "male" - self.google = MSRS_Config.Google - if MSRS_Config.Provider then - self.ttsprovider = MSRS_Config.Provider - end - self.Label = MSRS_Config.Label or "MSRS" - self.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel - - if MSRS_Config.GRPC then - self.provider = MSRS_Config.GRPC.DefaultProvider - if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then - self.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key - self.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice - self.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret - self.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region - end - end - - self.ConfigLoaded = true - else - - MSRS.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" - MSRS.port = MSRS_Config.Port or 5002 - MSRS.frequencies = MSRS_Config.Frequency or {127,243} - MSRS.modulations = MSRS_Config.Modulation or {0,0} - MSRS.coalition = MSRS_Config.Coalition or 0 - if MSRS_Config.Coordinate then - MSRS.coordinate = COORDINATE:New( MSRS_Config.Coordinate[1], MSRS_Config.Coordinate[2], MSRS_Config.Coordinate[3] ) - end - MSRS.culture = MSRS_Config.Culture or "en-GB" - MSRS.gender = MSRS_Config.Gender or "male" - MSRS.google = MSRS_Config.Google - if MSRS_Config.Provider then - MSRS.ttsprovider = MSRS_Config.Provider - end - MSRS.Label = MSRS_Config.Label or "MSRS" - MSRS.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel - if MSRS_Config.GRPC then - MSRS.provider = MSRS_Config.GRPC.DefaultProvider - if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then - MSRS.APIKey = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].key - MSRS.defaultVoice = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].defaultVoice - MSRS.region = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].secret - MSRS.secret = MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider].region - end - end - MSRS.ConfigLoaded = true - end - - end - end env.info("MSRS - Successfully loaded default configuration from disk!",false) end From 5f7115f4fef9656fcd4e3278b724c34fd65095cc Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 23 Dec 2023 16:53:42 +0100 Subject: [PATCH 52/95] Update SRS.lua --- Moose Development/Moose/Sound/SRS.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 899d41fba..36a300b67 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -986,7 +986,7 @@ end -- @param #string Provider Provider. Default is as set via @{#MSRS.SetProvider}. -- @return #MSRS.ProviderOptions Provider options. function MSRS:GetProviderOptions(Provider) - return self.poptions[Provider or self.provider] + return self.poptions[Provider or self.provider] or {} end From b9830a8437fb4c4b66e85416698d0fcd3f1b42cf Mon Sep 17 00:00:00 2001 From: kaltokri Date: Mon, 25 Dec 2023 05:35:41 +0100 Subject: [PATCH 53/95] Fixed some typos in demo-missions.md --- docs/beginner/demo-missions.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/beginner/demo-missions.md b/docs/beginner/demo-missions.md index b5e3eea1b..30ba6593d 100644 --- a/docs/beginner/demo-missions.md +++ b/docs/beginner/demo-missions.md @@ -9,14 +9,14 @@ nav_order: 04 1. Table of contents {:toc} -The best way to get compftable with a Moose class is to try the demo missions of -the class you want to learn. The Moose team created a lot of demo missions for -most of the classes. +The best way to get comfortable with a Moose class is to try the demo missions +of the class you want to learn. The Moose team created a lot of demo missions +for most of the classes. ## Download demo missions Go to the repository [MOOSE_MISSIONS]{:target="_blank"}, search the folder of -the class, download the mission (`.miz`) and rum them. +the class, download the mission (`.miz`) and run them. ## Read the mission script @@ -49,7 +49,7 @@ If you want to use more functions combine them all up. ## Next step -If the mission shows not the expected behaviour take a look at section +If the mission does not show the expected behaviour take a look at section [problems]. [MOOSE_MISSIONS]: https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop From bbc7f7e14c4e0f522a39b663b64d671ffcbc0826 Mon Sep 17 00:00:00 2001 From: kaltokri Date: Mon, 25 Dec 2023 05:54:50 +0100 Subject: [PATCH 54/95] Added mission repositries to repositories.md --- docs/repositories.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/repositories.md b/docs/repositories.md index eb181e646..b52c4cd37 100644 --- a/docs/repositories.md +++ b/docs/repositories.md @@ -26,14 +26,30 @@ You only need to load **one** of those files at the beginning of your mission. This repository contains the generated documentation and pictures and other references. The generated documentation is reflected in html and is published at: -- Stable `master` branch: +- `master` branch: - `develop` branch: +## [MOOSE_GUIDES](https://github.com/FlightControl-Master/MOOSE_GUIDES) - For external documentation and help + +This repository will be removed in future. + +## [MOOSE_PRESENTATIONS](https://github.com/FlightControl-Master/MOOSE_PRESENTATIONS) + +A collection of presentations used in the videos on the youtube channel of FlightControl. + ## [MOOSE_MISSIONS](https://github.com/FlightControl-Master/MOOSE_MISSIONS) - For users (provides demo missions) This repository contains all the demonstration missions in packed format (*.miz), and can be used without any further setup in DCS WORLD. +## [Moose_Community_Scripts](https://github.com/FlightControl-Master/Moose_Community_Scripts) + +This repository is for Moose based helper scripts, snippets, functional demos. + +## [MOOSE_SOUND](https://github.com/FlightControl-Master/MOOSE_SOUND) + +Sound packs for different MOOSE framework classes. + ## [MOOSE_MISSIONS_DYNAMIC](https://github.com/FlightControl-Master/MOOSE_MISSIONS_DYNAMIC) - Outdated This repository will be removed in future. @@ -41,3 +57,11 @@ This repository will be removed in future. ## [MOOSE_MISSIONS_UNPACKED](https://github.com/FlightControl-Master/MOOSE_MISSIONS_UNPACKED) - Outdated This repository will be removed in future. + +## [MOOSE_COMMUNITY_MISSIONS](https://github.com/FlightControl-Master/MOOSE_COMMUNITY_MISSIONS) - Outdated + +A database of missions created by the community, using MOOSE. + +## [MOOSE_TOOLS](https://github.com/FlightControl-Master/MOOSE_TOOLS) - Outdated + +A collection of the required tools to develop and contribute in the MOOSE framework for DCS World. From 1527b53c76fca3f242da3938814203a922a59752 Mon Sep 17 00:00:00 2001 From: kaltokri Date: Mon, 25 Dec 2023 06:09:31 +0100 Subject: [PATCH 55/95] Fixed ASW typo in SRS.lua --- Moose Development/Moose/Sound/SRS.lua | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 36a300b67..6cdc61a4e 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -162,14 +162,14 @@ -- -- ### Amazon Web Service [Only DCS-gRPC backend] -- --- In order to use Amazon Web Service (ASW) for TTS you need to use @{#MSRS.SetProvider} and @{MSRS.SetProviderOptionsAmazon} functions: +-- In order to use Amazon Web Service (AWS) for TTS you need to use @{#MSRS.SetProvider} and @{MSRS.SetProviderOptionsAmazon} functions: -- -- msrs:SetProvider(MSRS.Provider.AMAZON) -- msrs:SetProviderOptionsAmazon(AccessKey, SecretKey, Region) -- --- The parameters `AccessKey` and `SecretKey` are your ASW access and secret keys, respectively. The parameter `Region` is your [ASW region](https://docs.aws.amazon.com/general/latest/gr/pol.html). +-- The parameters `AccessKey` and `SecretKey` are your AWS access and secret keys, respectively. The parameter `Region` is your [AWS region](https://docs.aws.amazon.com/general/latest/gr/pol.html). -- --- You can set the voice to use with ASW via @{#MSRS.SetVoiceAmazon}. +-- You can set the voice to use with AWS via @{#MSRS.SetVoiceAmazon}. -- -- ### Microsoft Azure [Only DCS-gRPC backend] -- @@ -378,20 +378,20 @@ MSRS.Backend = { -- @field #string WINDOWS Microsoft windows (`win`). -- @field #string GOOGLE Google (`gcloud`). -- @field #string AZURE Microsoft Azure (`azure`). Only possible with DCS-gRPC backend. --- @field #string AMAZON Amazon Web Service (`asw`). Only possible with DCS-gRPC backend. +-- @field #string AMAZON Amazon Web Service (`aws`). Only possible with DCS-gRPC backend. MSRS.Provider = { WINDOWS = "win", GOOGLE = "gcloud", AZURE = "azure", - AMAZON = "asw", + AMAZON = "aws", } --- Provider options. -- @type MSRS.ProviderOptions -- @field #string provider Provider. -- @field #string credentials Google credentials JSON file (full path). --- @field #string key Access key (DCS-gRPC with Google, ASW, AZURE as provider). --- @field #string secret Secret key (DCS-gRPC with ASW as provider) +-- @field #string key Access key (DCS-gRPC with Google, AWS, AZURE as provider). +-- @field #string secret Secret key (DCS-gRPC with AWS as provider) -- @field #string region Region. -- @field #string defaultVoice Default voice (not used). -- @field #string voice Voice used. @@ -795,7 +795,7 @@ end --- Set to use a specific voice if Amazon Web Service is use as provider (only DCS-gRPC backend). Note that this will override any gender and culture settings. -- @param #MSRS self --- @param #string Voice [ASW Voice](https://docs.aws.amazon.com/polly/latest/dg/voicelist.html). Default `"Brian"`. +-- @param #string Voice [AWS Voice](https://docs.aws.amazon.com/polly/latest/dg/voicelist.html). Default `"Brian"`. -- @return #MSRS self function MSRS:SetVoiceAmazon(Voice) @@ -955,11 +955,11 @@ function MSRS:SetProviderOptionsGoogle(CredentialsFile, AccessKey) return self end ---- Set provider options and credentials for Amazon Web Service (ASW). Only supported in combination with DCS-gRPC as backend. +--- Set provider options and credentials for Amazon Web Service (AWS). Only supported in combination with DCS-gRPC as backend. -- @param #MSRS self -- @param #string AccessKey Your API access key. -- @param #string SecretKey Your secret key. --- @param #string Region Your ASW [region](https://docs.aws.amazon.com/general/latest/gr/pol.html). +-- @param #string Region Your AWS [region](https://docs.aws.amazon.com/general/latest/gr/pol.html). -- @return #MSRS self function MSRS:SetProviderOptionsAmazon(AccessKey, SecretKey, Region) @@ -1015,7 +1015,7 @@ function MSRS:SetTTSProviderAzure() return self end ---- Use Amazon Web Service (ASW) to provide text-to-speech. Only supported if used in combination with DCS-gRPC as backend. +--- Use Amazon Web Service (AWS) to provide text-to-speech. Only supported if used in combination with DCS-gRPC as backend. -- @param #MSRS self -- @return #MSRS self function MSRS:SetTTSProviderAmazon() @@ -1565,7 +1565,7 @@ end -- Gender = "male", -- Voice = "Microsoft Hazel Desktop", -- Voice that is used if no explicit provider voice is specified. -- Label = "MSRS", --- Provider = "win", --Provider for generating TTS (win, gcloud, azure, asw). +-- Provider = "win", --Provider for generating TTS (win, gcloud, azure, aws). -- -- -- Windows -- win = { From 071554bfc51925be46096b8173d39e9b175266c1 Mon Sep 17 00:00:00 2001 From: kaltokri Date: Mon, 25 Dec 2023 09:39:21 +0100 Subject: [PATCH 56/95] Fixed some small typos and removed STTS in comments --- Moose Development/Moose/Sound/SRS.lua | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 6cdc61a4e..00617105c 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -38,7 +38,7 @@ -- @field #table modulations Modulations used in the transmissions. -- @field #number coalition Coalition of the transmission. -- @field #number port Port. Default 5002. --- @field #string name Name. Default "DCS-STTS". +-- @field #string name Name. Default "MSRS". -- @field #number volume Volume between 0 (min) and 1 (max). Default 1. -- @field #string culture Culture. Default "en-GB". -- @field #string gender Gender. Default "female". @@ -65,7 +65,7 @@ -- ## Prerequisites -- -- * This script needs SRS version >= 1.9.6 --- * You need to de-sanitize os, io and lfs in hte missionscripting.lua +-- * You need to de-sanitize os, io and lfs in the missionscripting.lua -- * Optional: DCS-gRPC as backend to communicate with SRS (vide infra) -- -- ## Knwon Issues @@ -139,7 +139,7 @@ -- -- The default provider for generating speech from text is the native Windows TTS service. Note that you need to install the voices you want to use. -- --- **Pro-Tip** - use the command line with power shell to call `DCS-SR-ExternalAudio.exe` - it will tell you what is missing, +-- **Pro-Tip** - use the command line with power shell to call `DCS-SR-ExternalAudio.exe` - it will tell you what is missing -- and also the Google Console error, in case you have missed a step in setting up your Google TTS. -- For example, `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"` -- plays a message on 255 MHz AM for the blue coalition in-game. @@ -193,7 +193,7 @@ -- In addition to Windows and Google cloud, it also offers Microsoft Azure and Amazon Web Service as providers for TTS. -- -- Use @{#MSRS.SetDefaultBackendGRPC} to enable [DCS-gRPC](https://github.com/DCS-gRPC/rust-server) as an alternate backend for transmitting text-to-speech over SRS. --- This can be useful if 'DCS-SR-ExternalAudio.exe' cannot be used in the environment, or to use Azure or AWS clouds for TTS. Note that DCS-gRPC does not (yet?) support +-- This can be useful if 'DCS-SR-ExternalAudio.exe' cannot be used in the environment or to use Azure or AWS clouds for TTS. Note that DCS-gRPC does not (yet?) support -- all of the features and options available with 'DCS-SR-ExternalAudio.exe'. Of note, only text-to-speech is supported and it it cannot be used to transmit audio files. -- -- DCS-gRPC must be installed and configured per the [DCS-gRPC documentation](https://github.com/DCS-gRPC/rust-server) and already running via either the 'autostart' mechanism @@ -459,7 +459,7 @@ function MSRS:New(Path, Frequency, Modulation, Backend) else - -- Default wverwrites from :New() + -- Default overwrites from :New() if Path then self:SetPath(Path) @@ -612,6 +612,7 @@ end -- @return #MSRS self function MSRS:SetPort(Port) self.port=Port or 5002 + self:T(string.format("SRS port=%s", self:GetPort())) return self end @@ -1024,7 +1025,7 @@ function MSRS:SetTTSProviderAmazon() end ---- Print SRS STTS help to DCS log file. +--- Print SRS help to DCS log file. -- @param #MSRS self -- @return #MSRS self function MSRS:Help() @@ -1045,7 +1046,7 @@ function MSRS:Help() f:close() -- Print to log file. - env.info("SRS STTS help output:") + env.info("SRS help output:") env.info("======================================================================") env.info(data) env.info("======================================================================") @@ -1118,7 +1119,7 @@ function MSRS:PlaySoundText(SoundText, Delay) return self end ---- Play text message via STTS. +--- Play text message via MSRS. -- @param #MSRS self -- @param #string Text Text message. -- @param #number Delay Delay in seconds, before the message is played. @@ -1146,7 +1147,7 @@ function MSRS:PlayText(Text, Delay, Coordinate) return self end ---- Play text message via STTS with explicitly specified options. +--- Play text message via MSRS with explicitly specified options. -- @param #MSRS self -- @param #string Text Text message. -- @param #number Delay Delay in seconds, before the message is played. @@ -1191,7 +1192,7 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture end ---- Play text file via STTS. +--- Play text file via MSRS. -- @param #MSRS self -- @param #string TextFile Full path to the file. -- @param #number Delay Delay in seconds, before the message is played. @@ -1220,6 +1221,7 @@ function MSRS:PlayTextFile(TextFile, Delay) -- Count length of command. local l=string.len(command) + self:T(string.format("Command length=%d", l)) -- Execute command. self:_ExecCommand(command) From f6f318950462023bb2f410e3ba5d92c93f5b9443 Mon Sep 17 00:00:00 2001 From: kaltokri Date: Mon, 25 Dec 2023 11:15:03 +0100 Subject: [PATCH 57/95] MSRS enhancements - Added more tracing - A check if executable exists - Removed STTS references --- Moose Development/Moose/Sound/SRS.lua | 94 ++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 00617105c..516d4907a 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -386,6 +386,16 @@ MSRS.Provider = { AMAZON = "aws", } +--- Function for UUID. +function MSRS.uuid() + local random = math.random + local template = 'yxxx-xxxxxxxxxxxx' + return string.gsub( template, '[xy]', function( c ) + local v = (c == 'x') and random( 0, 0xf ) or random( 8, 0xb ) + return string.format( '%x', v ) + end ) +end + --- Provider options. -- @type MSRS.ProviderOptions -- @field #string provider Provider. @@ -1031,11 +1041,11 @@ end function MSRS:Help() -- Path and exe. - local path=self:GetPath() or STTS.DIRECTORY - local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe" + local path=self:GetPath() + local exe="DCS-SR-ExternalAudio.exe" -- Text file for output. - local filename = os.getenv('TMP') .. "\\MSRS-help-"..STTS.uuid()..".txt" + local filename = os.getenv('TMP') .. "\\MSRS-help-"..MSRS.uuid()..".txt" -- Print help. local command=string.format("%s/%s --help > %s", path, exe, filename) @@ -1275,9 +1285,9 @@ end -- @return #string Command. function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port, label, coordinate) - local path=self:GetPath() or STTS.DIRECTORY - - local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe" + local path=self:GetPath() + local exe="DCS-SR-ExternalAudio.exe" + local fullPath = string.format("%s\\%s", path, exe) freqs=table.concat(freqs or self.frequencies, ",") modus=table.concat(modus or self.modulations, ",") @@ -1330,8 +1340,13 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp self:E("ERROR: SRS only supports WINWOWS and GOOGLE as TTS providers! Use DCS-gRPC backend for other providers such as ") end + if not UTILS.FileExists(fullPath) then + self:E("ERROR: MSRS SRS executable does not exist! FullPath="..fullPath) + command="CommandNotFound" + end + -- Debug output. - self:T("MSRS command="..command) + self:T("MSRS command from _GetCommand="..command) return command end @@ -1342,24 +1357,25 @@ end -- @return #number Return value of os.execute() command. function MSRS:_ExecCommand(command) - -- Debug info. - self:T("SRS TTS command="..command) + -- Skip this function if _GetCommand was not able to find the executable + if string.find(command, "CommandNotFound") then return 0 end + local batContent = command.." && exit" -- Create a tmp file. - local filename=os.getenv('TMP').."\\MSRS-"..STTS.uuid()..".bat" + local filename=os.getenv('TMP').."\\MSRS-"..MSRS.uuid()..".bat" local script=io.open(filename, "w+") - script:write(command.." && exit") + script:write(batContent) script:close() - -- Play command. - command=string.format('start /b "" "%s"', filename) + self:T("MSRS batch file created: "..filename) + self:T("MSRS batch content: "..batContent) local res=nil if true then -- Create a tmp file. - local filenvbs = os.getenv('TMP') .. "\\MSRS-"..STTS.uuid()..".vbs" + local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs" -- VBS script local script = io.open(filenvbs, "w+") @@ -1368,12 +1384,12 @@ function MSRS:_ExecCommand(command) script:write(string.format('WinScriptHost.Run Chr(34) & "%s" & Chr(34), 0\n', filename)) script:write(string.format('Set WinScriptHost = Nothing')) script:close() + self:T("MSRS vbs file created to start batch="..filenvbs) -- Run visual basic script. This still pops up a window but very briefly and does not put the DCS window out of focus. local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs) -- Debug output. - self:T("MSRS execute command="..command) self:T("MSRS execute VBS command="..runvbs) -- Play file in 0.01 seconds @@ -1382,11 +1398,12 @@ function MSRS:_ExecCommand(command) -- Remove file in 1 second. timer.scheduleFunction(os.remove, filename, timer.getTime()+1) timer.scheduleFunction(os.remove, filenvbs, timer.getTime()+1) + self:T("MSRS vbs and batch file removed") elseif false then -- Create a tmp file. - local filenvbs = os.getenv('TMP') .. "\\MSRS-"..STTS.uuid()..".vbs" + local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs" -- VBS script local script = io.open(filenvbs, "w+") @@ -1402,6 +1419,8 @@ function MSRS:_ExecCommand(command) res=os.execute(runvbs) else + -- Play command. + command=string.format('start /b "" "%s"', filename) -- Debug output. self:T("MSRS execute command="..command) @@ -1680,6 +1699,47 @@ function MSRS:LoadConfigFile(Path,Filename) return true end +--- Function returns estimated speech time in seconds. +-- Assumptions for time calc: 100 Words per min, average of 5 letters for english word so +-- +-- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second +-- +-- So length of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function: +-- +-- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min +-- +-- @param #number length can also be passed as #string +-- @param #number speed Defaults to 1.0 +-- @param #boolean isGoogle We're using Google TTS +function MSRS.getSpeechTime(length,speed,isGoogle) + + local maxRateRatio = 3 + + speed = speed or 1.0 + isGoogle = isGoogle or false + + local speedFactor = 1.0 + if isGoogle then + speedFactor = speed + else + if speed ~= 0 then + speedFactor = math.abs( speed ) * (maxRateRatio - 1) / 10 + 1 + end + if speed < 0 then + speedFactor = 1 / speedFactor + end + end + + local wpm = math.ceil( 100 * speedFactor ) + local cps = math.floor( (wpm * 5) / 60 ) + + if type( length ) == "string" then + length = string.len( length ) + end + + return length/cps --math.ceil(length/cps) +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Manages radio transmissions. @@ -1837,7 +1897,7 @@ function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgr -- Create a new transmission object. local transmission={} --#MSRSQUEUE.Transmission transmission.text=text - transmission.duration=duration or STTS.getSpeechTime(text) + transmission.duration=duration or MSRS.getSpeechTime(text) transmission.msrs=msrs transmission.Tplay=tstart or timer.getAbsTime() transmission.subtitle=subtitle From 16f3dcbbb467715800799acb8f445dd75a077822 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 25 Dec 2023 12:14:41 +0100 Subject: [PATCH 58/95] New SRS fixes --- Moose Development/Moose/Core/Message.lua | 5 +---- Moose Development/Moose/Ops/Airboss.lua | 8 +++++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 8b6da3cdd..46c896f0d 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -98,7 +98,7 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen self.MessageType = nil - -- When no MessageCategory is given, we don't show it as a title... + -- 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 .. ": " @@ -498,7 +498,6 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G _MESSAGESRS.Gender = Gender or "female" _MESSAGESRS.MSRS:SetGoogle(PathToCredentials) - _MESSAGESRS.google = PathToCredentials _MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE") _MESSAGESRS.label = Label or "MESSAGE" @@ -512,8 +511,6 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G if Voice then _MESSAGESRS.MSRS:SetVoice(Voice) end _MESSAGESRS.voice = Voice --or MSRS.Voices.Microsoft.Hedda - --if _MESSAGESRS.google and not Voice then _MESSAGESRS.Voice = MSRS.Voices.Google.Standard.en_GB_Standard_A end - --_MESSAGESRS.MSRS:SetVoice(Voice or _MESSAGESRS.voice) _MESSAGESRS.SRSQ = MSRSQUEUE:New(Label or "MESSAGE") end diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index cb732bb4f..75b621966 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -1746,7 +1746,7 @@ AIRBOSS.MenuF10Root = nil --- Airboss class version. -- @field #string version -AIRBOSS.version = "1.3.2" +AIRBOSS.version = "1.3.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -3071,6 +3071,7 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum self.SRS:SetPath(PathToSRS) self.SRS:SetPort(Port or 5002) self.SRS:SetLabel(self.AirbossRadio.alias or "AIRBOSS") + self.SRS:SetCoordinate(self.carrier:GetCoordinate()) --self.SRS:SetModulations(Modulations) if GoogleCreds then self.SRS:SetGoogle(GoogleCreds) @@ -10266,7 +10267,7 @@ function AIRBOSS:_GetSternCoord() elseif case==2 or case==1 then -- V/Stol: Translate 8 meters port. self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true) - end + end elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then -- Stennis: translate 7 meters starboard wrt Final bearing. self.sterncoord:Translate( self.carrierparam.sterndist, hdg, true, true ):Translate( 7, FB + 90, true, true ) @@ -14881,6 +14882,7 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p end else + -- SRS transmission if call.subtitle ~= nil and string.len(call.subtitle) > 1 then @@ -14955,7 +14957,7 @@ function AIRBOSS:SetSRSPilotVoice( Voice, Gender, Culture ) self.PilotRadio.gender = Gender or "male" self.PilotRadio.culture = Culture or "en-US" - if (not Voice) and self.SRS and self.SRS.google then + if (not Voice) and self.SRS and self.SRS:GetProvider() == MSRS.Provider.GOOGLE then self.PilotRadio.voice = MSRS.Voices.Google.Standard.en_US_Standard_J end From db7e94d879080e5502bbc9e0893e2ab06a6d820e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 25 Dec 2023 12:14:57 +0100 Subject: [PATCH 59/95] New SRS Fixes --- Moose Development/Moose/Functional/AICSAR.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index 5f158932f..5b9da5494 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -606,8 +606,10 @@ function AICSAR:SetPilotTTSVoice(Voice,Culture,Gender) self.SRSPilot:SetCulture(Culture or "en-US") self.SRSPilot:SetGender(Gender or "male") self.SRSPilot:SetLabel("PILOT") - if self.SRS.google then - self.SRSPilot:SetGoogle(self.SRS.google) + if self.SRSGoogle then + local poptions = self.SRS:GetProviderOptions(MSRS.Provider.GOOGLE) -- Sound.SRS#MSRS.ProviderOptions + self.SRSOperator:SetGoogle(poptions.credentials) + self.SRSOperator:SetGoogleAPIKey(poptions.key) end return self end @@ -628,8 +630,10 @@ function AICSAR:SetOperatorTTSVoice(Voice,Culture,Gender) self.SRSOperator:SetCulture(Culture or "en-GB") self.SRSOperator:SetGender(Gender or "female") self.SRSPilot:SetLabel("RESCUE") - if self.SRS.google then - self.SRSOperator:SetGoogle(self.SRS.google) + if self.SRSGoogle then + local poptions = self.SRS:GetProviderOptions(MSRS.Provider.GOOGLE) -- Sound.SRS#MSRS.ProviderOptions + self.SRSOperator:SetGoogle(poptions.credentials) + self.SRSOperator:SetGoogleAPIKey(poptions.key) end return self end From 8eec7fcc9ebdf858e24d5771206fe51711a56c0b Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 25 Dec 2023 12:29:40 +0100 Subject: [PATCH 60/95] Update build-includes.yml (#2071) --- .github/workflows/build-includes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-includes.yml b/.github/workflows/build-includes.yml index 4bd22bdbe..1f24421c3 100644 --- a/.github/workflows/build-includes.yml +++ b/.github/workflows/build-includes.yml @@ -97,7 +97,7 @@ jobs: - name: Run LuaSrcDiet run: | - luasrcdiet --basic --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua + luasrcdiet --maximum --details --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua ######################################################################### # Run LuaCheck From 1d5952b4a58780a209510a47576c6135c0c2c2a6 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 25 Dec 2023 12:34:57 +0100 Subject: [PATCH 61/95] Update build-includes.yml (#2072) --- .github/workflows/build-includes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-includes.yml b/.github/workflows/build-includes.yml index 1f24421c3..de58838a0 100644 --- a/.github/workflows/build-includes.yml +++ b/.github/workflows/build-includes.yml @@ -97,7 +97,7 @@ jobs: - name: Run LuaSrcDiet run: | - luasrcdiet --maximum --details --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua + luasrcdiet --basic --opt-srcequiv --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua ######################################################################### # Run LuaCheck From 3f384d72ab003eee04e3463230204c0d1590d80d Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 25 Dec 2023 12:38:18 +0100 Subject: [PATCH 62/95] Update build-includes.yml (#2073) --- .github/workflows/build-includes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-includes.yml b/.github/workflows/build-includes.yml index de58838a0..52ad01444 100644 --- a/.github/workflows/build-includes.yml +++ b/.github/workflows/build-includes.yml @@ -97,7 +97,7 @@ jobs: - name: Run LuaSrcDiet run: | - luasrcdiet --basic --opt-srcequiv --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua + luasrcdiet --opt-binequiv ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua ######################################################################### # Run LuaCheck From c5dec374c8759c740ff0e2610167dc3af830d51b Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 25 Dec 2023 12:40:33 +0100 Subject: [PATCH 63/95] Update build-includes.yml (#2074) --- .github/workflows/build-includes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-includes.yml b/.github/workflows/build-includes.yml index 52ad01444..61979d072 100644 --- a/.github/workflows/build-includes.yml +++ b/.github/workflows/build-includes.yml @@ -97,7 +97,7 @@ jobs: - name: Run LuaSrcDiet run: | - luasrcdiet --opt-binequiv ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua + luasrcdiet ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua ######################################################################### # Run LuaCheck From 44b3f41ae43d78efc30936cf3cd74a8fc9cfc3c3 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 25 Dec 2023 12:49:54 +0100 Subject: [PATCH 64/95] Update build-includes.yml (#2075) --- .github/workflows/build-includes.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-includes.yml b/.github/workflows/build-includes.yml index 61979d072..9632094c0 100644 --- a/.github/workflows/build-includes.yml +++ b/.github/workflows/build-includes.yml @@ -95,9 +95,9 @@ jobs: export COMMIT_TIME=$(git show -s --format=%cd ${{ github.sha }} --date=iso-strict) lua5.3 "./Moose Setup/Moose_Create.lua" D "$COMMIT_TIME-${{ github.sha }}" "./Moose Development/Moose" "./Moose Setup" "./build/result/Moose_Include_Dynamic" - - name: Run LuaSrcDiet - run: | - luasrcdiet ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua + #- name: Run LuaSrcDiet + # run: | + # luasrcdiet ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua ######################################################################### # Run LuaCheck From 3ccfcdbd0fac3655c4f530c9957c491247ea18ce Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 25 Dec 2023 13:00:36 +0100 Subject: [PATCH 65/95] CSAR --- Moose Development/Moose/Ops/CSAR.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index becdf6ec3..ee2d58a9b 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -31,7 +31,7 @@ -- @image OPS_CSAR.jpg -- Date: May 2023 --- Last: Update Oct 2024 +-- Last: Update Dec 2024 ------------------------------------------------------------------------- --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM From 7e2f8771b50fe01c9c6181bb7d65b16c23be5bb4 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 25 Dec 2023 13:01:59 +0100 Subject: [PATCH 66/95] Update build-includes.yml (#2076) --- .github/workflows/build-includes.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-includes.yml b/.github/workflows/build-includes.yml index 4bd22bdbe..ed2a18fbe 100644 --- a/.github/workflows/build-includes.yml +++ b/.github/workflows/build-includes.yml @@ -95,9 +95,9 @@ jobs: export COMMIT_TIME=$(git show -s --format=%cd ${{ github.sha }} --date=iso-strict) lua5.3 "./Moose Setup/Moose_Create.lua" D "$COMMIT_TIME-${{ github.sha }}" "./Moose Development/Moose" "./Moose Setup" "./build/result/Moose_Include_Dynamic" - - name: Run LuaSrcDiet - run: | - luasrcdiet --basic --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua + #- name: Run LuaSrcDiet + # run: | + # luasrcdiet --basic --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua ######################################################################### # Run LuaCheck From c72cdd8f0b894101b7383469c41de305cf11866a Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 25 Dec 2023 13:08:21 +0100 Subject: [PATCH 67/95] Update build-includes.yml (#2077) --- .github/workflows/build-includes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-includes.yml b/.github/workflows/build-includes.yml index ed2a18fbe..ba2aca482 100644 --- a/.github/workflows/build-includes.yml +++ b/.github/workflows/build-includes.yml @@ -106,7 +106,7 @@ jobs: if: ${{ vars.SKIP_LUACHECK != true }} continue-on-error: true run: | - luacheck --std=lua51c --config=.luacheckrc -gurasqq "Moose Development/Moose" + luacheck --std=lua51c --config=.luacheckrc -guras "Moose Development/Moose" ######################################################################### # Push to MOOSE_INCLUDE From d65042c6404b03915a659675092c76e3958f93c2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 25 Dec 2023 13:11:59 +0100 Subject: [PATCH 68/95] SRS error from luacheck --- Moose Development/Moose/Sound/SRS.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 516d4907a..e6f044ed8 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1535,11 +1535,11 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab local gender="" if self.gender then - gender=string.format(' gender=\"\%s\"', self.gender) + gender=string.format(' gender=\"%s\"', self.gender) end local language="" if self.culture then - language=string.format(' language=\"\%s\"', self.culture) + language=string.format(' language=\"%s\"', self.culture) end if self.gender or self.culture then From e83c8c3ee05cf453ccf815ba26b81ce765bff8b0 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 25 Dec 2023 13:14:46 +0100 Subject: [PATCH 69/95] Update build-includes.yml (#2078) --- .github/workflows/build-includes.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-includes.yml b/.github/workflows/build-includes.yml index ba2aca482..19749a58b 100644 --- a/.github/workflows/build-includes.yml +++ b/.github/workflows/build-includes.yml @@ -95,10 +95,6 @@ jobs: export COMMIT_TIME=$(git show -s --format=%cd ${{ github.sha }} --date=iso-strict) lua5.3 "./Moose Setup/Moose_Create.lua" D "$COMMIT_TIME-${{ github.sha }}" "./Moose Development/Moose" "./Moose Setup" "./build/result/Moose_Include_Dynamic" - #- name: Run LuaSrcDiet - # run: | - # luasrcdiet --basic --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua - ######################################################################### # Run LuaCheck ######################################################################### @@ -108,6 +104,10 @@ jobs: run: | luacheck --std=lua51c --config=.luacheckrc -guras "Moose Development/Moose" + - name: Run LuaSrcDiet + run: | + luasrcdiet --basic --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua + ######################################################################### # Push to MOOSE_INCLUDE ######################################################################### From aeac2eb3d7e46ad1a60de95e1496ad3aa331f4dc Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 25 Dec 2023 13:20:13 +0100 Subject: [PATCH 70/95] Update build-includes.yml --- .github/workflows/build-includes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-includes.yml b/.github/workflows/build-includes.yml index 19749a58b..0925e3892 100644 --- a/.github/workflows/build-includes.yml +++ b/.github/workflows/build-includes.yml @@ -102,7 +102,7 @@ jobs: if: ${{ vars.SKIP_LUACHECK != true }} continue-on-error: true run: | - luacheck --std=lua51c --config=.luacheckrc -guras "Moose Development/Moose" + luacheck --std=lua51c --config=.luacheckrc -gurasqq "Moose Development/Moose" - name: Run LuaSrcDiet run: | From 2a7dbad02a3c0075045a23b26cc1cc16384414a0 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 25 Dec 2023 13:21:51 +0100 Subject: [PATCH 71/95] Update build-includes.yml --- .github/workflows/build-includes.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-includes.yml b/.github/workflows/build-includes.yml index 9632094c0..1535ded87 100644 --- a/.github/workflows/build-includes.yml +++ b/.github/workflows/build-includes.yml @@ -95,10 +95,6 @@ jobs: export COMMIT_TIME=$(git show -s --format=%cd ${{ github.sha }} --date=iso-strict) lua5.3 "./Moose Setup/Moose_Create.lua" D "$COMMIT_TIME-${{ github.sha }}" "./Moose Development/Moose" "./Moose Setup" "./build/result/Moose_Include_Dynamic" - #- name: Run LuaSrcDiet - # run: | - # luasrcdiet ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua - ######################################################################### # Run LuaCheck ######################################################################### @@ -108,6 +104,10 @@ jobs: run: | luacheck --std=lua51c --config=.luacheckrc -gurasqq "Moose Development/Moose" + - name: Run LuaSrcDiet + run: | + luasrcdiet ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua + ######################################################################### # Push to MOOSE_INCLUDE ######################################################################### From e46b14872142596aa18745e1d3b04ea8c0ab0725 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 25 Dec 2023 13:28:00 +0100 Subject: [PATCH 72/95] SRS --- Moose Development/Moose/Sound/SRS.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 118edb9e1..9ec6b347e 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -1529,6 +1529,7 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab -- We use a specific voice options.provider[provider].voice = Voice else + -- DCS-gRPC doesn't directly support language/gender, but can use SSML local preTag, genderProp, langProp, postTag = '', '', '', '' From a51afeda144b3062cb1cbd51c31e3557fc325d02 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 25 Dec 2023 13:31:35 +0100 Subject: [PATCH 73/95] Update build-includes.yml --- .github/workflows/build-includes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-includes.yml b/.github/workflows/build-includes.yml index 1535ded87..15065ba9e 100644 --- a/.github/workflows/build-includes.yml +++ b/.github/workflows/build-includes.yml @@ -106,7 +106,7 @@ jobs: - name: Run LuaSrcDiet run: | - luasrcdiet ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua + luasrcdiet --basic --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua ######################################################################### # Push to MOOSE_INCLUDE From bf0ad93bf768308374fea84fabdc479154e34732 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 25 Dec 2023 13:59:23 +0100 Subject: [PATCH 74/95] AICSAR Fix --- Moose Development/Moose/Functional/AICSAR.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/AICSAR.lua b/Moose Development/Moose/Functional/AICSAR.lua index 5b9da5494..322959dea 100644 --- a/Moose Development/Moose/Functional/AICSAR.lua +++ b/Moose Development/Moose/Functional/AICSAR.lua @@ -608,8 +608,8 @@ function AICSAR:SetPilotTTSVoice(Voice,Culture,Gender) self.SRSPilot:SetLabel("PILOT") if self.SRSGoogle then local poptions = self.SRS:GetProviderOptions(MSRS.Provider.GOOGLE) -- Sound.SRS#MSRS.ProviderOptions - self.SRSOperator:SetGoogle(poptions.credentials) - self.SRSOperator:SetGoogleAPIKey(poptions.key) + self.SRSPilot:SetGoogle(poptions.credentials) + self.SRSPilot:SetGoogleAPIKey(poptions.key) end return self end @@ -629,7 +629,7 @@ function AICSAR:SetOperatorTTSVoice(Voice,Culture,Gender) self.SRSOperator:SetVoice(Voice) self.SRSOperator:SetCulture(Culture or "en-GB") self.SRSOperator:SetGender(Gender or "female") - self.SRSPilot:SetLabel("RESCUE") + self.SRSOperator:SetLabel("RESCUE") if self.SRSGoogle then local poptions = self.SRS:GetProviderOptions(MSRS.Provider.GOOGLE) -- Sound.SRS#MSRS.ProviderOptions self.SRSOperator:SetGoogle(poptions.credentials) From 1b01b893438d56e32c36709491a49c0793c814cc Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 25 Dec 2023 23:12:55 +0100 Subject: [PATCH 75/95] TARGET - Fixed IsAlive --- Moose Development/Moose/Ops/ArmyGroup.lua | 2 ++ Moose Development/Moose/Ops/Target.lua | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 1bbad51e8..d6b54a757 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -1860,6 +1860,7 @@ function ARMYGROUP:_UpdateEngageTarget() else -- Could not get position of target (not alive any more?) ==> Disengage. + self:T(self.lid.."Could not get position of target ==> Disengage!") self:Disengage() end @@ -1867,6 +1868,7 @@ function ARMYGROUP:_UpdateEngageTarget() else -- Target not alive any more ==> Disengage. + self:T(self.lid.."Target not ALIVE ==> Disengage!") self:Disengage() end diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 4cdbbdbfa..c5b9ca558 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -526,11 +526,11 @@ function TARGET:IsAlive() for _,_target in pairs(self.targets) do local target=_target --Ops.Target#TARGET.Object - if target.Status==TARGET.ObjectStatus.ALIVE then + if target.Status~=TARGET.ObjectStatus.DEAD then return true end end - + return false end From e2e9bd7de05ab1ca26295a6af6d0e19428baa96d Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 26 Dec 2023 08:53:11 +0100 Subject: [PATCH 76/95] Update EasyGCICAP.lua --- Moose Development/Moose/Ops/EasyGCICAP.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 0d2ce7d99..34ec1301b 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -141,14 +141,14 @@ -- -- **Note** If you need different tanker types, i.e. Boom and Drogue, set them up at different AirWings! -- -- Add a tanker point -- mywing:AddPatrolPointTanker(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone Tanker"):GetCoordinate(),20000,280,270,50) --- -- Add an AWACS squad - Radio 251 AM, TACAN 51Y +-- -- Add a tanker squad - Radio 251 AM, TACAN 51Y -- mywing:AddTankerSquadron("Blue Tanker","Tanker Ops Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.EXCELLENT,602,nil,251,radio.modulation.AM,51) -- -- ### Add an AWACS (optional) -- -- -- Add an AWACS point -- mywing:AddPatrolPointAwacs(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone AWACS"):GetCoordinate(),25000,300,270,50) --- -- Add a tanker squad - Radio 251 AM, TACAN 51Y +-- -- Add an AWACS squad - Radio 251 AM, TACAN 51Y -- mywing:AddAWACSSquadron("Blue AWACS","AWACS Ops Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.AVERAGE,702,nil,271,radio.modulation.AM) -- -- # Fine-Tuning From 46f70dd8a6124e17e236682755d77e989455d26b Mon Sep 17 00:00:00 2001 From: kaltokri Date: Tue, 26 Dec 2023 13:24:56 +0100 Subject: [PATCH 77/95] Fixed logic in SoundOutput.lua to play internal sound files with SRS --- Moose Development/Moose/Sound/SoundOutput.lua | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/Moose Development/Moose/Sound/SoundOutput.lua b/Moose Development/Moose/Sound/SoundOutput.lua index 38a8337fe..e1c34916b 100644 --- a/Moose Development/Moose/Sound/SoundOutput.lua +++ b/Moose Development/Moose/Sound/SoundOutput.lua @@ -160,52 +160,61 @@ do -- Sound File -- @param #string FileName The name of the sound file, e.g. "Hello World.ogg". -- @param #string Path The path of the directory, where the sound file is located. Default is "l10n/DEFAULT/" within the miz file. -- @param #number Duration Duration in seconds, how long it takes to play the sound file. Default is 3 seconds. + -- @param #bolean UseSrs Set if SRS should be used to play this file. Default is false. -- @return #SOUNDFILE self - function SOUNDFILE:New(FileName, Path, Duration) - + function SOUNDFILE:New(FileName, Path, Duration, UseSrs) + -- Inherit BASE. local self=BASE:Inherit(self, BASE:New()) -- #SOUNDFILE + -- Debug info: + self:F( {FileName, Path, Duration, UseSrs} ) + -- Set file name. self:SetFileName(FileName) - + + -- Set if SRS should be used to play this file + self:SetPlayWithSRS(UseSrs or false) + -- Set path. self:SetPath(Path) - + -- Set duration. self:SetDuration(Duration) - - -- Debug info: - self:T(string.format("New SOUNDFILE: file name=%s, path=%s", self.filename, self.path)) return self end - + --- Set path, where the sound file is located. -- @param #SOUNDFILE self -- @param #string Path Path to the directory, where the sound file is located. In case this is nil, it defaults to the DCS mission temp directory. -- @return #SOUNDFILE self function SOUNDFILE:SetPath(Path) - + self:F( {Path} ) + -- Init path. - self.path=Path or "l10n/DEFAULT/" - - if not Path and self.useSRS then -- use path to mission temp dir - self.path = os.getenv('TMP') .. "\\DCS\\Mission\\l10n\\DEFAULT" - end - + if not Path then + if self.useSRS then -- use path to mission temp dir + self.path = lfs.tempdir() .. "Mission\\l10n\\DEFAULT" + else -- use internal path in miz file + self.path="l10n/DEFAULT/" + end + end + -- Remove (back)slashes. local nmax=1000 ; local n=1 while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do self.path=self.path:sub(1,#self.path-1) n=n+1 end - + -- Append slash. self.path=self.path.."/" - + + self:T("self.path=".. self.path) + return self - end + end --- Get path of the directory, where the sound file is located. -- @param #SOUNDFILE self @@ -228,7 +237,7 @@ do -- Sound File --- Get the sound file name. -- @param #SOUNDFILE self -- @return #string Name of the soud file. This does *not* include its path. - function SOUNDFILE:GetFileName() + function SOUNDFILE:GetFileName() return self.filename end @@ -264,14 +273,16 @@ do -- Sound File -- @param #boolean Switch If true or nil, use SRS. If false, use DCS transmission. -- @return #SOUNDFILE self function SOUNDFILE:SetPlayWithSRS(Switch) + self:F( {Switch} ) if Switch==true or Switch==nil then self.useSRS=true else self.useSRS=false end + self:T("self.useSRS=".. tostring(self.useSRS)) return self - end - + end + end do -- Text-To-Speech From 3bdf4b4c76c7761fcb13a1582e634ec154dd4bfa Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 26 Dec 2023 19:18:08 +0100 Subject: [PATCH 78/95] #CTLD * Fix multi-crate requests deducting too much Stock --- Moose Development/Moose/Ops/CTLD.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 095eb28de..0ae3a2ef8 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1228,7 +1228,7 @@ CTLD.UnitTypeCapabilities = { --- CTLD class version. -- @field #string version -CTLD.version="1.0.44" +CTLD.version="1.0.45" --- Instantiate a new CTLD. -- @param #CTLD self @@ -2454,11 +2454,13 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack) realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,true,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass,nil,subcat) table.insert(droppedcargo,realcargo) else - realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],false,cargotype.PerCrateMass,nil,subcat) - Cargo:RemoveStock() + realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],false,cargotype.PerCrateMass,nil,subcat) end table.insert(self.Spawned_Cargo, realcargo) end + if not (drop or pack) then + Cargo:RemoveStock() + end local text = string.format("Crates for %s have been positioned near you!",cratename) if drop then text = string.format("Crates for %s have been dropped!",cratename) @@ -3824,7 +3826,7 @@ end -- @param #CTLD_CARGO.Enum Type Type of cargo. I.e. VEHICLE or FOB. VEHICLE will move to destination zones when dropped/build, FOB stays put. -- @param #number NoCrates Number of crates needed to build this cargo. -- @param #number PerCrateMass Mass in kg of each crate --- @param #number Stock Number of groups in stock. Nil for unlimited. +-- @param #number Stock Number of buildable groups in stock. Nil for unlimited. -- @param #string SubCategory Name of sub-category (optional). function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory) self:T(self.lid .. " AddCratesCargo") From 6ac46addf041abdd400514792824deeae109ae9a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 27 Dec 2023 19:28:19 +0100 Subject: [PATCH 79/95] #SET * Added `FilterFunction()` for these SETs: UNIT, GROUP, CLIENT, STATIC --- Moose Development/Moose/Core/Set.lua | 147 ++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 43fbd8fce..31a941c6b 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -146,7 +146,52 @@ do -- SET_BASE return self end - + + --- [Internal] Add a functional filter + -- @param #SET_BASE self + -- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a CONTROLLABLE object as first argument. + -- @param ... Condition function arguments, if any. + -- @return #boolean If true, at least one condition is true + function SET_BASE:FilterFunction(ConditionFunction, ...) + + local condition={} + condition.func=ConditionFunction + condition.arg={} + + if arg then + condition.arg=arg + end + + if not self.Filter.Functions then self.Filter.Functions = {} end + table.insert(self.Filter.Functions, condition) + + return self + end + + --- [Internal] Check if the condition functions returns true. + -- @param #SET_BASE self + -- @param Wrapper.Controllable#CONTROLLABLE Object The object to filter for + -- @return #boolean If true, at least one condition is true. + function SET_BASE:_EvalFilterFunctions(Object) + + -- Any one condition must be true. + for _,_condition in pairs(self.Filter.Functions or {}) do + local condition=_condition + + -- Call function. + local istrue=condition.func(Object,unpack(condition.arg)) + + -- Any true will return true. + if istrue then + return true + end + + end + + -- No condition was true. + return false + end + --- Clear the Objects in the Set. -- @param #SET_BASE self -- @param #boolean TriggerEvent If `true`, an event remove is triggered for each group that is removed from the set. @@ -967,6 +1012,7 @@ do -- * @{#SET_GROUP.FilterCategoryShip}: Builds the SET_GROUP from ships. -- * @{#SET_GROUP.FilterCategoryStructure}: Builds the SET_GROUP from structures. -- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Core.Zone#ZONE}. + -- * @{#SET_GROUP.FilterFunction}: Builds the SET_GROUP with a custom condition. -- -- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: -- @@ -1040,6 +1086,7 @@ do Countries = nil, GroupPrefixes = nil, Zones = nil, + Functions = nil, }, FilterMeta = { Coalitions = { @@ -1253,7 +1300,26 @@ do return self end + + --- [User] Add a custom condition function. + -- @function [parent=#SET_GROUP] FilterFunction + -- @param #SET_GROUP self + -- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a GROUP object as first argument. + -- @param ... Condition function arguments if any. + -- @return #SET_GROUP self + -- @usage + -- -- Image you want to exclude a specific GROUP from a SET: + -- local groundset = SET_GROUP:New():FilterCoalitions("blue"):FilterCategoryGround():FilterFunction( + -- -- The function needs to take a GROUP object as first - and in this case, only - argument. + -- function(grp) + -- local isinclude = true + -- if grp:GetName() == "Exclude Me" then isinclude = false end + -- return isinclude + -- end + -- ):FilterOnce() + -- BASE:I(groundset:Flush()) + --- Builds a set of groups of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_GROUP self @@ -1981,6 +2047,11 @@ do end MGroupInclude = MGroupInclude and MGroupZone end + + if self.Filter.Functions then + local MGroupFunc = self:_EvalFilterFunctions(MGroup) + MGroupInclude = MGroupInclude and MGroupFunc + end self:T2( MGroupInclude ) return MGroupInclude @@ -2080,6 +2151,7 @@ do -- SET_UNIT -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- * @{#SET_UNIT.FilterActive}: Builds the SET_UNIT with the units that are only active. Units that are inactive (late activation) won't be included in the set! -- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Core.Zone#ZONE}. + -- * @{#SET_UNIT.FilterFunction}: Builds the SET_UNIT with a custom condition. -- -- Once the filter criteria have been set for the SET_UNIT, you can start filtering using: -- @@ -2158,6 +2230,7 @@ do -- SET_UNIT Countries = nil, UnitPrefixes = nil, Zones = nil, + Functions = nil, }, FilterMeta = { Coalitions = { @@ -2529,6 +2602,25 @@ do -- SET_UNIT return self end + --- [User] Add a custom condition function. + -- @function [parent=#SET_UNIT] FilterFunction + -- @param #SET_UNIT self + -- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a UNIT object as first argument. + -- @param ... Condition function arguments if any. + -- @return #SET_UNIT self + -- @usage + -- -- Image you want to exclude a specific UNIT from a SET: + -- local groundset = SET_UNIT:New():FilterCoalitions("blue"):FilterCategories("ground"):FilterFunction( + -- -- The function needs to take a UNIT object as first - and in this case, only - argument. + -- function(unit) + -- local isinclude = true + -- if unit:GetName() == "Exclude Me" then isinclude = false end + -- return isinclude + -- end + -- ):FilterOnce() + -- BASE:I(groundset:Flush()) + + --- 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 @@ -3196,6 +3288,11 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MGroupZone end + if self.Filter.Functions then + local MUnitFunc = self:_EvalFilterFunctions(MUnit) + MUnitInclude = MUnitInclude and MUnitFunc + end + self:T2( MUnitInclude ) return MUnitInclude end @@ -3277,6 +3374,7 @@ do -- SET_STATIC -- * @{#SET_STATIC.FilterPrefixes}: Builds the SET_STATIC with the units containing the same string(s) in their name. **Attention!** LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results. -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- * @{#SET_STATIC.FilterZones}: Builds the SET_STATIC with the units within a @{Core.Zone#ZONE}. + -- * @{#SET_STATIC.FilterFunction}: Builds the SET_STATIC with a custom condition. -- -- Once the filter criteria have been set for the SET_STATIC, you can start filtering using: -- @@ -3479,7 +3577,25 @@ do -- SET_STATIC end return self end - + + --- [User] Add a custom condition function. + -- @function [parent=#SET_STATIC] FilterFunction + -- @param #SET_STATIC self + -- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a STATIC object as first argument. + -- @param ... Condition function arguments if any. + -- @return #SET_STATIC self + -- @usage + -- -- Image you want to exclude a specific CLIENT from a SET: + -- local groundset = SET_STATIC:New():FilterCoalitions("blue"):FilterActive(true):FilterFunction( + -- -- The function needs to take a STATIC object as first - and in this case, only - argument. + -- function(static) + -- local isinclude = true + -- if static:GetName() == "Exclude Me" then isinclude = false end + -- return isinclude + -- end + -- ):FilterOnce() + -- BASE:I(groundset:Flush()) + --- Builds a set of units of defined countries. -- Possible current countries are those known within DCS world. -- @param #SET_STATIC self @@ -4036,6 +4152,7 @@ do -- SET_CLIENT -- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching) -- * @{#SET_CLIENT.FilterActive}: Builds the SET_CLIENT with the units that are only active. Units that are inactive (late activation) won't be included in the set! -- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Core.Zone#ZONE}. + -- * @{#SET_CLIENT.FilterFunction}: Builds the SET_CLIENT with a custom condition. -- -- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: -- @@ -4538,6 +4655,25 @@ do -- SET_CLIENT return AliveSet.Set or {} end + --- [User] Add a custom condition function. + -- @function [parent=#SET_CLIENT] FilterFunction + -- @param #SET_CLIENT self + -- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a CLIENT object as first argument. + -- @param ... Condition function arguments if any. + -- @return #SET_CLIENT self + -- @usage + -- -- Image you want to exclude a specific CLIENT from a SET: + -- local groundset = SET_CLIENT:New():FilterCoalitions("blue"):FilterActive(true):FilterFunction( + -- -- The function needs to take a UNIT object as first - and in this case, only - argument. + -- function(client) + -- local isinclude = true + -- if client:GetPlayerName() == "Exclude Me" then isinclude = false end + -- return isinclude + -- end + -- ):FilterOnce() + -- BASE:I(groundset:Flush()) + + --- -- @param #SET_CLIENT self -- @param Wrapper.Client#CLIENT MClient @@ -4660,6 +4796,11 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientCallsigns end + if self.Filter.Functions then + local MClientFunc = self:_EvalFilterFunctions(MClient) + MClientInclude = MClientInclude and MClientFunc + end + end self:T2( MClientInclude ) return MClientInclude @@ -7809,7 +7950,7 @@ do -- SET_OPSGROUP local MGroupPrefix = false for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do - if string.find( MGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then --Not sure why "-" is replaced by "%-" ?! + if string.find( MGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then --Not sure why "-" is replaced by "%-" ?! - So we can still match group names with a dash in them MGroupPrefix = true end end From 501ab7099246020cf403ed010cc5f79a90d01dfb Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Wed, 27 Dec 2023 19:34:20 +0100 Subject: [PATCH 80/95] xxx --- Moose Development/Moose/Core/Set.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 31a941c6b..d0670abd6 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -5394,7 +5394,7 @@ do -- SET_AIRBASE function SET_AIRBASE:GetRandomAirbase() local RandomAirbase = self:GetRandom() - self:F( { RandomAirbase = RandomAirbase:GetName() } ) + --self:F( { RandomAirbase = RandomAirbase:GetName() } ) return RandomAirbase end From 6ffe69484c43fcab55d3dda2873bd45dbfa8ef42 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Thu, 28 Dec 2023 13:32:53 +0100 Subject: [PATCH 81/95] Reduce noise --- Moose Development/Moose/Cargo/Cargo.lua | 62 +++++++++---------- Moose Development/Moose/Cargo/CargoGroup.lua | 42 ++++++------- Moose Development/Moose/Functional/Mantis.lua | 4 +- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index c9297378a..8b7d6040e 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -370,7 +370,7 @@ CARGOS = {} do -- CARGO - --- @type 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. @@ -433,7 +433,7 @@ do -- CARGO Reported = {}, } - --- @type CARGO.CargoObjects + -- @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. @@ -447,7 +447,7 @@ do -- CARGO function CARGO:New( Type, Name, Weight, LoadRadius, NearRadius ) --R2.1 local self = BASE:Inherit( self, FSM:New() ) -- #CARGO - self:F( { Type, Name, Weight, LoadRadius, NearRadius } ) + self:T( { Type, Name, Weight, LoadRadius, NearRadius } ) self:SetStartState( "UnLoaded" ) self:AddTransition( { "UnLoaded", "Boarding" }, "Board", "Boarding" ) @@ -711,7 +711,7 @@ do -- CARGO -- @param #CARGO self -- @return #CARGO function CARGO:Spawn( PointVec2 ) - self:F() + self:T() end @@ -812,7 +812,7 @@ do -- CARGO -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the CargoGroup is within the loading radius. function CARGO:IsInLoadRadius( Coordinate ) - self:F( { Coordinate, LoadRadius = self.LoadRadius } ) + self:T( { Coordinate, LoadRadius = self.LoadRadius } ) local Distance = 0 if self:IsUnLoaded() then @@ -832,7 +832,7 @@ do -- CARGO -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo can report itself. function CARGO:IsInReportRadius( Coordinate ) - self:F( { Coordinate } ) + self:T( { Coordinate } ) local Distance = 0 if self:IsUnLoaded() then @@ -853,23 +853,23 @@ do -- CARGO -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). -- @return #boolean function CARGO:IsNear( Coordinate, NearRadius ) - --self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } ) + --self:T( { PointVec2 = PointVec2, NearRadius = NearRadius } ) if self.CargoObject:IsAlive() then --local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) - --self:F( { CargoObjectName = self.CargoObject:GetName() } ) - --self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) - --self:F( { PointVec2 = PointVec2:GetVec2() } ) + --self:T( { CargoObjectName = self.CargoObject:GetName() } ) + --self:T( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) + --self:T( { PointVec2 = PointVec2:GetVec2() } ) local Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() ) - --self:F( { Distance = Distance, NearRadius = NearRadius or "nil" } ) + --self:T( { Distance = Distance, NearRadius = NearRadius or "nil" } ) if Distance <= NearRadius then - --self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = true } ) + --self:T( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = true } ) return true end end - --self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = false } ) + --self:T( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = false } ) return false end @@ -878,12 +878,12 @@ do -- CARGO -- @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 } ) + --self:T( { Zone } ) if self:IsLoaded() then return Zone:IsPointVec2InZone( self.CargoCarrier:GetPointVec2() ) else - --self:F( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } ) + --self:T( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } ) if self.CargoObject:GetSize() ~= 0 then return Zone:IsPointVec2InZone( self.CargoObject:GetPointVec2() ) else @@ -1034,7 +1034,7 @@ end -- CARGO do -- CARGO_REPRESENTABLE - --- @type CARGO_REPRESENTABLE + -- @type CARGO_REPRESENTABLE -- @extends #CARGO -- @field test @@ -1056,7 +1056,7 @@ do -- CARGO_REPRESENTABLE -- Inherit CARGO. local self = BASE:Inherit( self, CARGO:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE - self:F( { Type, Name, LoadRadius, NearRadius } ) + self:T( { Type, Name, LoadRadius, NearRadius } ) -- Descriptors. local Desc=CargoObject:GetDesc() @@ -1086,7 +1086,7 @@ do -- CARGO_REPRESENTABLE function CARGO_REPRESENTABLE:Destroy() -- Cargo objects are deleted from the _DATABASE and SET_CARGO objects. - self:F( { CargoName = self:GetName() } ) + self:T( { CargoName = self:GetName() } ) --_EVENTDISPATCHER:CreateEventDeleteCargo( self ) return self @@ -1123,12 +1123,12 @@ do -- CARGO_REPRESENTABLE CoordinateZone:Scan( { Object.Category.UNIT } ) for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do local NearUnit = UNIT:Find( DCSUnit ) - self:F({NearUnit=NearUnit}) + self:T({NearUnit=NearUnit}) local NearUnitCoalition = NearUnit:GetCoalition() local CargoCoalition = self:GetCoalition() if NearUnitCoalition == CargoCoalition then local Attributes = NearUnit:GetDesc() - self:F({Desc=Attributes}) + self:T({Desc=Attributes}) if NearUnit:HasAttribute( "Trucks" ) then MESSAGE:New( Message, 20, NearUnit:GetCallsign() .. " reporting - Cargo " .. self:GetName() ):ToGroup( TaskGroup ) break @@ -1142,7 +1142,7 @@ end -- CARGO_REPRESENTABLE do -- CARGO_REPORTABLE - --- @type CARGO_REPORTABLE + -- @type CARGO_REPORTABLE -- @extends #CARGO CARGO_REPORTABLE = { ClassName = "CARGO_REPORTABLE" @@ -1158,7 +1158,7 @@ do -- CARGO_REPORTABLE -- @return #CARGO_REPORTABLE function CARGO_REPORTABLE:New( Type, Name, Weight, LoadRadius, NearRadius ) local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight, LoadRadius, NearRadius ) ) -- #CARGO_REPORTABLE - self:F( { Type, Name, Weight, LoadRadius, NearRadius } ) + self:T( { Type, Name, Weight, LoadRadius, NearRadius } ) return self end @@ -1178,7 +1178,7 @@ end do -- CARGO_PACKAGE - --- @type CARGO_PACKAGE + -- @type CARGO_PACKAGE -- @extends #CARGO_REPRESENTABLE CARGO_PACKAGE = { ClassName = "CARGO_PACKAGE" @@ -1195,7 +1195,7 @@ do -- CARGO_PACKAGE -- @return #CARGO_PACKAGE function CARGO_PACKAGE:New( CargoCarrier, Type, Name, Weight, LoadRadius, NearRadius ) local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoCarrier, Type, Name, Weight, LoadRadius, NearRadius ) ) -- #CARGO_PACKAGE - self:F( { Type, Name, Weight, LoadRadius, NearRadius } ) + self:T( { Type, Name, Weight, LoadRadius, NearRadius } ) self:T( CargoCarrier ) self.CargoCarrier = CargoCarrier @@ -1213,7 +1213,7 @@ end -- @param #number BoardDistance -- @param #number Angle function CARGO_PACKAGE:onafterOnBoard( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - self:F() + self:T() self.CargoInAir = self.CargoCarrier:InAir() @@ -1246,7 +1246,7 @@ end -- @param Wrapper.Unit#UNIT CargoCarrier -- @return #boolean function CARGO_PACKAGE:IsNear( CargoCarrier ) - self:F() + self:T() local CargoCarrierPoint = CargoCarrier:GetCoordinate() @@ -1271,7 +1271,7 @@ end -- @param #number LoadDistance -- @param #number Angle function CARGO_PACKAGE:onafterOnBoarded( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - self:F() + self:T() if self:IsNear( CargoCarrier ) then self:__Load( 1, CargoCarrier, Speed, LoadDistance, Angle ) @@ -1292,7 +1292,7 @@ end -- @param #number Radius -- @param #number Angle function CARGO_PACKAGE:onafterUnBoard( From, Event, To, CargoCarrier, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle ) - self:F() + self:T() self.CargoInAir = self.CargoCarrier:InAir() @@ -1331,7 +1331,7 @@ end -- @param Wrapper.Unit#UNIT CargoCarrier -- @param #number Speed function CARGO_PACKAGE:onafterUnBoarded( From, Event, To, CargoCarrier, Speed ) - self:F() + self:T() if self:IsNear( CargoCarrier ) then self:__UnLoad( 1, CargoCarrier, Speed ) @@ -1350,7 +1350,7 @@ end -- @param #number LoadDistance -- @param #number Angle function CARGO_PACKAGE:onafterLoad( From, Event, To, CargoCarrier, Speed, LoadDistance, Angle ) - self:F() + self:T() self.CargoCarrier = CargoCarrier @@ -1378,7 +1378,7 @@ end -- @param #number Distance -- @param #number Angle function CARGO_PACKAGE:onafterUnLoad( From, Event, To, CargoCarrier, Speed, Distance, Angle ) - self:F() + self:T() local StartPointVec2 = self.CargoCarrier:GetPointVec2() local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 4f76aac4c..0cb2f1d5b 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -64,7 +64,7 @@ do -- CARGO_GROUP -- Inherit CAROG_REPORTABLE local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_GROUP - self:F( { Type, Name, LoadRadius } ) + self:T( { Type, Name, LoadRadius } ) self.CargoSet = SET_CARGO:New() self.CargoGroup = CargoGroup @@ -146,7 +146,7 @@ do -- CARGO_GROUP -- @param #CARGO_GROUP self function CARGO_GROUP:Respawn() - self:F( { "Respawning" } ) + self:T( { "Respawning" } ) for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do local Cargo = CargoData -- Cargo.Cargo#CARGO @@ -227,7 +227,7 @@ do -- CARGO_GROUP -- @param #CARGO_GROUP self function CARGO_GROUP:Regroup() - self:F("Regroup") + self:T("Regroup") if self.Grouped == false then @@ -241,7 +241,7 @@ do -- CARGO_GROUP for CargoUnitName, CargoUnit in pairs( self.CargoSet:GetSet() ) do local CargoUnit = CargoUnit -- Cargo.CargoUnit#CARGO_UNIT - self:F( { CargoUnit:GetName(), UnLoaded = CargoUnit:IsUnLoaded() } ) + self:T( { CargoUnit:GetName(), UnLoaded = CargoUnit:IsUnLoaded() } ) if CargoUnit:IsUnLoaded() then @@ -258,7 +258,7 @@ do -- CARGO_GROUP -- Then we register the new group in the database self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID ) - self:F( { "Regroup", GroupTemplate } ) + self:T( { "Regroup", GroupTemplate } ) -- Now we spawn the new group based on the template created. self.CargoObject = _DATABASE:Spawn( GroupTemplate ) @@ -309,14 +309,14 @@ do -- CARGO_GROUP -- @param Wrapper.Unit#UNIT CargoCarrier -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { CargoCarrier.UnitName, From, Event, To, NearRadius = NearRadius } ) + self:T( { CargoCarrier.UnitName, From, Event, To, NearRadius = NearRadius } ) NearRadius = NearRadius or self.NearRadius -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 self.CargoSet:ForEach( function( Cargo, ... ) - self:F( { "Board Unit", Cargo:GetName( ), Cargo:IsDestroyed(), Cargo.CargoObject:IsAlive() } ) + self:T( { "Board Unit", Cargo:GetName( ), Cargo:IsDestroyed(), Cargo.CargoObject:IsAlive() } ) local CargoGroup = Cargo.CargoObject --Wrapper.Group#GROUP CargoGroup:OptionAlarmStateGreen() Cargo:__Board( 1, CargoCarrier, NearRadius, ... ) @@ -334,7 +334,7 @@ do -- CARGO_GROUP -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier function CARGO_GROUP:onafterLoad( From, Event, To, CargoCarrier, ... ) - --self:F( { From, Event, To, CargoCarrier, ...} ) + --self:T( { From, Event, To, CargoCarrier, ...} ) if From == "UnLoaded" then -- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier. @@ -359,7 +359,7 @@ do -- CARGO_GROUP -- @param Wrapper.Unit#UNIT CargoCarrier -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - --self:F( { CargoCarrier.UnitName, From, Event, To } ) + --self:T( { CargoCarrier.UnitName, From, Event, To } ) local Boarded = true local Cancelled = false @@ -393,7 +393,7 @@ do -- CARGO_GROUP if not Boarded then self:__Boarding( -5, CargoCarrier, NearRadius, ... ) else - self:F("Group Cargo is loaded") + self:T("Group Cargo is loaded") self:__Load( 1, CargoCarrier, ... ) end else @@ -413,7 +413,7 @@ do -- CARGO_GROUP -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onafterUnBoard( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( {From, Event, To, ToPointVec2, NearRadius } ) + self:T( {From, Event, To, ToPointVec2, NearRadius } ) NearRadius = NearRadius or 25 @@ -456,7 +456,7 @@ do -- CARGO_GROUP -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - --self:F( { From, Event, To, ToPointVec2, NearRadius } ) + --self:T( { From, Event, To, ToPointVec2, NearRadius } ) --local NearRadius = NearRadius or 25 @@ -493,7 +493,7 @@ do -- CARGO_GROUP -- @param #string To -- @param Core.Point#POINT_VEC2 ToPointVec2 function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... ) - --self:F( { From, Event, To, ToPointVec2 } ) + --self:T( { From, Event, To, ToPointVec2 } ) if From == "Loaded" then @@ -611,7 +611,7 @@ do -- CARGO_GROUP -- @param #CARGO_GROUP self -- @param Core.Point#COORDINATE Coordinate function CARGO_GROUP:RouteTo( Coordinate ) - --self:F( {Coordinate = Coordinate } ) + --self:T( {Coordinate = Coordinate } ) -- For each Cargo within the CargoSet, route each object to the Coordinate self.CargoSet:ForEach( @@ -629,13 +629,13 @@ do -- CARGO_GROUP -- @param #number NearRadius -- @return #boolean The Cargo is near to the Carrier or #nil if the Cargo is not near to the Carrier. function CARGO_GROUP:IsNear( CargoCarrier, NearRadius ) - self:F( {NearRadius = NearRadius } ) + self:T( {NearRadius = NearRadius } ) for _, Cargo in pairs( self.CargoSet:GetSet() ) do local Cargo = Cargo -- Cargo.Cargo#CARGO if Cargo:IsAlive() then if Cargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) then - self:F( "Near" ) + self:T( "Near" ) return true end end @@ -649,7 +649,7 @@ do -- CARGO_GROUP -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Group is within the load radius. function CARGO_GROUP:IsInLoadRadius( Coordinate ) - --self:F( { Coordinate } ) + --self:T( { Coordinate } ) local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO @@ -669,7 +669,7 @@ do -- CARGO_GROUP return false end - self:F( { Distance = Distance, LoadRadius = self.LoadRadius } ) + self:T( { Distance = Distance, LoadRadius = self.LoadRadius } ) if Distance <= self.LoadRadius then return true else @@ -687,12 +687,12 @@ do -- CARGO_GROUP -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the Cargo Group is within the report radius. function CARGO_GROUP:IsInReportRadius( Coordinate ) - --self:F( { Coordinate } ) + --self:T( { Coordinate } ) local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO if Cargo then - self:F( { Cargo } ) + self:T( { Cargo } ) local Distance = 0 if Cargo:IsUnLoaded() then Distance = Coordinate:Get2DDistance( Cargo.CargoObject:GetCoordinate() ) @@ -738,7 +738,7 @@ do -- CARGO_GROUP -- @return #boolean **true** if the first element of the CargoGroup is in the Zone -- @return #boolean **false** if there is no element of the CargoGroup in the Zone. function CARGO_GROUP:IsInZone( Zone ) - --self:F( { Zone } ) + --self:T( { Zone } ) local Cargo = self.CargoSet:GetFirst() -- Cargo.Cargo#CARGO diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index ce95806b1..1ebe4124e 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -94,7 +94,7 @@ -- Known SAM types at the time of writing are: -- -- * Avenger --- * Chaparrel +-- * Chaparral -- * Hawk -- * Linebacker -- * NASAMS @@ -365,7 +365,7 @@ MANTIS.SamData = { ["SA-15"] = { Range=11, Blindspot=0, Height=6, Type="Short", Radar="Tor 9A331" }, ["SA-13"] = { Range=5, Blindspot=0, Height=3, Type="Short", Radar="Strela" }, ["Avenger"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Avenger" }, - ["Chaparrel"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" }, + ["Chaparral"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" }, ["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Linebacker" }, ["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" }, -- units from HDS Mod, multi launcher options is tricky From 4fe1318e7c1c46842de54c9fd7b3d5ed256f849c Mon Sep 17 00:00:00 2001 From: kaltokri Date: Thu, 28 Dec 2023 16:38:26 +0100 Subject: [PATCH 82/95] Fixed a logic failure with external sound files in SoundOutput.lua --- Moose Development/Moose/Sound/SoundOutput.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Moose Development/Moose/Sound/SoundOutput.lua b/Moose Development/Moose/Sound/SoundOutput.lua index e1c34916b..408be7b96 100644 --- a/Moose Development/Moose/Sound/SoundOutput.lua +++ b/Moose Development/Moose/Sound/SoundOutput.lua @@ -199,6 +199,8 @@ do -- Sound File else -- use internal path in miz file self.path="l10n/DEFAULT/" end + else + self.path = Path end -- Remove (back)slashes. From b052d993491064276fa37f5841734ff3e31dd879 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 29 Dec 2023 14:49:57 +0100 Subject: [PATCH 83/95] Fixes --- Moose Development/Moose/Functional/Mantis.lua | 8 +++---- Moose Development/Moose/Functional/Sead.lua | 8 +++---- .../Moose/Wrapper/Controllable.lua | 21 ++++++++++++++----- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 1ebe4124e..a9c4ddd79 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -22,7 +22,7 @@ -- @module Functional.Mantis -- @image Functional.Mantis.jpg -- --- Last Update: Nov 2023 +-- Last Update: Dec 2023 ------------------------------------------------------------------------- --- **MANTIS** class, extends Core.Base#BASE @@ -631,7 +631,7 @@ do -- TODO Version -- @field #string version - self.version="0.8.15" + self.version="0.8.16" self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) --- FSM Functions --- @@ -1149,7 +1149,7 @@ do --self:T(self.lid.." Relocating HQ") local text = self.lid.." Relocating HQ" --local m= MESSAGE:New(text,10,"MANTIS"):ToAll() - _hqgrp:RelocateGroundRandomInRadius(20,500,true,true) + _hqgrp:RelocateGroundRandomInRadius(20,500,true,true,nil,true) end --relocate EWR -- TODO: maybe dependent on AlarmState? Observed: SA11 SR only relocates if no objects in reach @@ -1163,7 +1163,7 @@ do local text = self.lid.." Relocating EWR ".._grp:GetName() local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) if self.verbose then self:I(text) end - _grp:RelocateGroundRandomInRadius(20,500,true,true) + _grp:RelocateGroundRandomInRadius(20,500,true,true,nil,true) end end end diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index 07fb312c5..4c02b7b7f 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -17,9 +17,9 @@ -- -- === -- --- ### Authors: **FlightControl**, **applevangelist** +-- ### Authors: **applevangelist**, **FlightControl** -- --- Last Update: Oct 2023 +-- Last Update: Dec 2023 -- -- === -- @@ -144,7 +144,7 @@ function SEAD:New( SEADGroupPrefixes, Padding ) self:AddTransition("*", "ManageEvasion", "*") self:AddTransition("*", "CalculateHitZone", "*") - self:I("*** SEAD - Started Version 0.4.5") + self:I("*** SEAD - Started Version 0.4.6") return self end @@ -401,7 +401,7 @@ function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADP grp:EnableEmission(false) end grp:OptionAlarmStateGreen() -- needed else we cannot move around - grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond") + grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond",true) if self.UseCallBack then local object = self.CallBack object:SeadSuppressionStart(grp,name,attacker) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 0bed05a72..67a3cdcfe 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -2937,7 +2937,7 @@ function CONTROLLABLE:CopyRoute( Begin, End, Randomize, Radius ) end --- Return the detected targets of the controllable. --- The optional parametes specify the detection methods that can be applied. +-- The optional parameters 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 #CONTROLLABLE self -- @param #boolean DetectVisual (optional) @@ -4025,14 +4025,22 @@ end -- @param #boolean onroad If true, route on road (less problems with AI way finding), default true -- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false -- @param #string formation Formation string as in the mission editor, e.g. "Vee", "Diamond", "Line abreast", etc. Defaults to "Off Road" +-- @param #boolean onland (optional) If true, try up to 50 times to get a coordinate on land.SurfaceType.LAND. Note - this descriptor value is not reliably implemented on all maps. -- @return #CONTROLLABLE self -function CONTROLLABLE:RelocateGroundRandomInRadius( speed, radius, onroad, shortcut, formation ) +function CONTROLLABLE:RelocateGroundRandomInRadius( speed, radius, onroad, shortcut, formation, onland ) self:F2( { self.ControllableName } ) local _coord = self:GetCoordinate() local _radius = radius or 500 local _speed = speed or 20 local _tocoord = _coord:GetRandomCoordinateInRadius( _radius, 100 ) + if onland then + for i=1,50 do + local island = _tocoord:GetSurfaceType() == land.SurfaceType.LAND and true or false + if island then break end + _tocoord = _coord:GetRandomCoordinateInRadius( _radius, 100 ) + end + end local _onroad = onroad or true local _grptsk = {} local _candoroad = false @@ -5349,9 +5357,10 @@ end -- @param #number Altitude (Optional) Altitude in meters. Defaults to the altitude of the coordinate. -- @param #number Speed (Optional) Speed in kph. Defaults to 500 kph. -- @param #number Formation (Optional) Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation). +-- @param #boolean AGL (Optional) If true, set altitude to above ground level (AGL), not above sea level (ASL). -- @param #number Delay (Optional) Set the task after delay seconds only. -- @return #CONTROLLABLE self -function CONTROLLABLE:PatrolRaceTrack(Point1, Point2, Altitude, Speed, Formation, Delay) +function CONTROLLABLE:PatrolRaceTrack(Point1, Point2, Altitude, Speed, Formation, AGL, Delay) local PatrolGroup = self -- Wrapper.Group#GROUP @@ -5373,8 +5382,10 @@ function CONTROLLABLE:PatrolRaceTrack(Point1, Point2, Altitude, Speed, Formation -- Calculate the new Route if Altitude then - FromCoord:SetAltitude(Altitude) - ToCoord:SetAltitude(Altitude) + local asl = true + if AGL then asl = false end + FromCoord:SetAltitude(Altitude, asl) + ToCoord:SetAltitude(Altitude, asl) end -- Create a "air waypoint", which is a "point" structure that can be given as a parameter to a Task From 9cc32ff8dc99a113b8040aa860aea35f723e3dac Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Fri, 29 Dec 2023 15:02:41 +0100 Subject: [PATCH 84/95] AIRBOSS - Superfluous error message removal --- Moose Development/Moose/Ops/Airboss.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 75b621966..c3c6c9f2a 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -8212,7 +8212,7 @@ function AIRBOSS:OnEventBirth( EventData ) self:E( EventData ) return end - if EventData.IniUnit == nil then + if EventData.IniUnit == nil and (not EventData.IniObjectCategory == Object.Category.STATIC) then self:E( self.lid .. "ERROR: EventData.IniUnit=nil in event BIRTH!" ) self:E( EventData ) return From 0347e42fc7a509be814056dbc5f19172446937d0 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 30 Dec 2023 16:50:12 +0100 Subject: [PATCH 85/95] CARGO noise --- Moose Development/Moose/Cargo/CargoCrate.lua | 20 +++++++------- Moose Development/Moose/Cargo/CargoGroup.lua | 4 +-- .../Moose/Cargo/CargoSlingload.lua | 16 ++++++------ Moose Development/Moose/Cargo/CargoUnit.lua | 26 +++++++++---------- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 2abc9365b..c0fbdd631 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -59,7 +59,7 @@ do -- CARGO_CRATE -- @return #CARGO_CRATE function CARGO_CRATE:New( CargoStatic, Type, Name, LoadRadius, NearRadius ) local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_CRATE - self:F( { Type, Name, NearRadius } ) + self:T( { Type, Name, NearRadius } ) self.CargoObject = CargoStatic -- Wrapper.Static#STATIC @@ -116,7 +116,7 @@ do -- CARGO_CRATE -- @param #string To -- @param Core.Point#POINT_VEC2 function CARGO_CRATE:onenterUnLoaded( From, Event, To, ToPointVec2 ) - --self:F( { ToPointVec2, From, Event, To } ) + --self:T( { ToPointVec2, From, Event, To } ) local Angle = 180 local Speed = 10 @@ -153,7 +153,7 @@ do -- CARGO_CRATE -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier function CARGO_CRATE:onenterLoaded( From, Event, To, CargoCarrier ) - --self:F( { From, Event, To, CargoCarrier } ) + --self:T( { From, Event, To, CargoCarrier } ) self.CargoCarrier = CargoCarrier @@ -190,7 +190,7 @@ do -- CARGO_CRATE -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Crate is within the report radius. function CARGO_CRATE:IsInReportRadius( Coordinate ) - --self:F( { Coordinate, LoadRadius = self.LoadRadius } ) + --self:T( { Coordinate, LoadRadius = self.LoadRadius } ) local Distance = 0 if self:IsUnLoaded() then @@ -210,7 +210,7 @@ do -- CARGO_CRATE -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the Cargo Crate is within the loading radius. function CARGO_CRATE:IsInLoadRadius( Coordinate ) - --self:F( { Coordinate, LoadRadius = self.NearRadius } ) + --self:T( { Coordinate, LoadRadius = self.NearRadius } ) local Distance = 0 if self:IsUnLoaded() then @@ -231,7 +231,7 @@ do -- CARGO_CRATE -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. -- @return #nil There is no valid Cargo in the CargoGroup. function CARGO_CRATE:GetCoordinate() - --self:F() + --self:T() return self.CargoObject:GetCoordinate() end @@ -261,7 +261,7 @@ do -- CARGO_CRATE -- @param #CARGO_CRATE self -- @param Core.Point#COORDINATE Coordinate function CARGO_CRATE:RouteTo( Coordinate ) - self:F( {Coordinate = Coordinate } ) + self:T( {Coordinate = Coordinate } ) end @@ -274,7 +274,7 @@ do -- CARGO_CRATE -- @return #boolean The Cargo is near to the Carrier. -- @return #nil The Cargo is not near to the Carrier. function CARGO_CRATE:IsNear( CargoCarrier, NearRadius ) - self:F( {NearRadius = NearRadius } ) + self:T( {NearRadius = NearRadius } ) return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) end @@ -283,7 +283,7 @@ do -- CARGO_CRATE -- @param #CARGO_CRATE self function CARGO_CRATE:Respawn() - self:F( { "Respawning crate " .. self:GetName() } ) + self:T( { "Respawning crate " .. self:GetName() } ) -- Respawn the group... @@ -300,7 +300,7 @@ do -- CARGO_CRATE -- @param #CARGO_CRATE self function CARGO_CRATE:onafterReset() - self:F( { "Reset crate " .. self:GetName() } ) + self:T( { "Reset crate " .. self:GetName() } ) -- Respawn the group... diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 0cb2f1d5b..ca6a96a69 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -271,7 +271,7 @@ do -- CARGO_GROUP -- @param Core.Event#EVENTDATA EventData function CARGO_GROUP:OnEventCargoDead( EventData ) - self:E(EventData) + self:T(EventData) local Destroyed = false @@ -296,7 +296,7 @@ do -- CARGO_GROUP if Destroyed then self:Destroyed() - self:E( { "Cargo group destroyed" } ) + self:T( { "Cargo group destroyed" } ) end end diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index ff6d81f17..ad26e8868 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -52,7 +52,7 @@ do -- CARGO_SLINGLOAD -- @return #CARGO_SLINGLOAD function CARGO_SLINGLOAD:New( CargoStatic, Type, Name, LoadRadius, NearRadius ) local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_SLINGLOAD - self:F( { Type, Name, NearRadius } ) + self:T( { Type, Name, NearRadius } ) self.CargoObject = CargoStatic @@ -130,7 +130,7 @@ do -- CARGO_SLINGLOAD -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Crate is within the report radius. function CARGO_SLINGLOAD:IsInReportRadius( Coordinate ) - --self:F( { Coordinate, LoadRadius = self.LoadRadius } ) + --self:T( { Coordinate, LoadRadius = self.LoadRadius } ) local Distance = 0 if self:IsUnLoaded() then @@ -149,7 +149,7 @@ do -- CARGO_SLINGLOAD -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Slingload is within the loading radius. function CARGO_SLINGLOAD:IsInLoadRadius( Coordinate ) - --self:F( { Coordinate } ) + --self:T( { Coordinate } ) local Distance = 0 if self:IsUnLoaded() then @@ -169,7 +169,7 @@ do -- CARGO_SLINGLOAD -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. -- @return #nil There is no valid Cargo in the CargoGroup. function CARGO_SLINGLOAD:GetCoordinate() - --self:F() + --self:T() return self.CargoObject:GetCoordinate() end @@ -199,7 +199,7 @@ do -- CARGO_SLINGLOAD -- @param #CARGO_SLINGLOAD self -- @param Core.Point#COORDINATE Coordinate function CARGO_SLINGLOAD:RouteTo( Coordinate ) - --self:F( {Coordinate = Coordinate } ) + --self:T( {Coordinate = Coordinate } ) end @@ -212,7 +212,7 @@ do -- CARGO_SLINGLOAD -- @return #boolean The Cargo is near to the Carrier. -- @return #nil The Cargo is not near to the Carrier. function CARGO_SLINGLOAD:IsNear( CargoCarrier, NearRadius ) - --self:F( {NearRadius = NearRadius } ) + --self:T( {NearRadius = NearRadius } ) return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) end @@ -222,7 +222,7 @@ do -- CARGO_SLINGLOAD -- @param #CARGO_SLINGLOAD self function CARGO_SLINGLOAD:Respawn() - --self:F( { "Respawning slingload " .. self:GetName() } ) + --self:T( { "Respawning slingload " .. self:GetName() } ) -- Respawn the group... @@ -239,7 +239,7 @@ do -- CARGO_SLINGLOAD -- @param #CARGO_SLINGLOAD self function CARGO_SLINGLOAD:onafterReset() - --self:F( { "Reset slingload " .. self:GetName() } ) + --self:T( { "Reset slingload " .. self:GetName() } ) -- Respawn the group... diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 830f02662..a1d86dd49 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -75,7 +75,7 @@ do -- CARGO_UNIT -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #number NearRadius (optional) Defaut 25 m. function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) + self:T( { From, Event, To, ToPointVec2, NearRadius } ) local Angle = 180 local Speed = 60 @@ -114,7 +114,7 @@ do -- CARGO_UNIT else self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading ) end - self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) + self:T( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) self.CargoCarrier = nil local Points = {} @@ -148,7 +148,7 @@ do -- CARGO_UNIT -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #number NearRadius (optional) Defaut 100 m. function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) + self:T( { From, Event, To, ToPointVec2, NearRadius } ) local Angle = 180 local Speed = 10 @@ -174,7 +174,7 @@ do -- CARGO_UNIT -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #number NearRadius (optional) Defaut 100 m. function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) + self:T( { From, Event, To, ToPointVec2, NearRadius } ) self.CargoInAir = self.CargoObject:InAir() @@ -199,7 +199,7 @@ do -- CARGO_UNIT -- @param #string To -- @param Core.Point#POINT_VEC2 function CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) + self:T( { ToPointVec2, From, Event, To } ) local Angle = 180 local Speed = 10 @@ -236,7 +236,7 @@ do -- CARGO_UNIT -- @param Wrapper.Group#GROUP CargoCarrier -- @param #number NearRadius function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier, NearRadius = NearRadius } ) + self:T( { From, Event, To, CargoCarrier, NearRadius = NearRadius } ) self.CargoInAir = self.CargoObject:InAir() @@ -244,7 +244,7 @@ do -- CARGO_UNIT local MaxSpeed = Desc.speedMaxOffRoad local TypeName = Desc.typeName - --self:F({Unit=self.CargoObject:GetName()}) + --self:T({Unit=self.CargoObject:GetName()}) -- A cargo unit can only be boarded if it is not dead @@ -298,9 +298,9 @@ do -- CARGO_UNIT -- @param Wrapper.Client#CLIENT CargoCarrier -- @param #number NearRadius Default 25 m. function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier:GetName(), NearRadius = NearRadius } ) + self:T( { From, Event, To, CargoCarrier:GetName(), NearRadius = NearRadius } ) - self:F( { IsAlive=self.CargoObject:IsAlive() } ) + self:T( { IsAlive=self.CargoObject:IsAlive() } ) if CargoCarrier and CargoCarrier:IsAlive() then -- and self.CargoObject and self.CargoObject:IsAlive() then if (CargoCarrier:IsAir() and not CargoCarrier:InAir()) or true then @@ -321,7 +321,7 @@ do -- CARGO_UNIT local Angle = 180 local Distance = 0 - --self:F({Unit=self.CargoObject:GetName()}) + --self:T({Unit=self.CargoObject:GetName()}) local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. @@ -348,7 +348,7 @@ do -- CARGO_UNIT self.CargoObject:SetCommand( self.CargoObject:CommandStopRoute( true ) ) end else - self:E("Something is wrong") + self:T("Something is wrong") end end @@ -361,11 +361,11 @@ do -- CARGO_UNIT -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier function CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier ) - self:F( { From, Event, To, CargoCarrier } ) + self:T( { From, Event, To, CargoCarrier } ) self.CargoCarrier = CargoCarrier - --self:F({Unit=self.CargoObject:GetName()}) + --self:T({Unit=self.CargoObject:GetName()}) -- Only destroy the CargoObject if there is a CargoObject (packages don't have CargoObjects). if self.CargoObject then From f3063613170216e92f21cdb309d5222bfd6dccf2 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sat, 30 Dec 2023 16:51:45 +0100 Subject: [PATCH 86/95] #GROUP * Added IsSam and IsAAA #SET * Corrected EvalFilterFunctions - all must be true --- Moose Development/Moose/Core/Set.lua | 24 ++++++--------- Moose Development/Moose/Wrapper/Group.lua | 37 +++++++++++++++++++++-- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index d0670abd6..ba08c103b 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -171,25 +171,18 @@ do -- SET_BASE --- [Internal] Check if the condition functions returns true. -- @param #SET_BASE self -- @param Wrapper.Controllable#CONTROLLABLE Object The object to filter for - -- @return #boolean If true, at least one condition is true. - function SET_BASE:_EvalFilterFunctions(Object) - - -- Any one condition must be true. + -- @return #boolean If true, if **all** conditions are true + function SET_BASE:_EvalFilterFunctions(Object) + -- All conditions must be true. for _,_condition in pairs(self.Filter.Functions or {}) do local condition=_condition - -- Call function. - local istrue=condition.func(Object,unpack(condition.arg)) - - -- Any true will return true. - if istrue then - return true + if condition.func(Object,unpack(condition.arg)) == false then + return false end - - end - + end -- No condition was true. - return false + return true end --- Clear the Objects in the Set. @@ -2049,7 +2042,8 @@ do end if self.Filter.Functions then - local MGroupFunc = self:_EvalFilterFunctions(MGroup) + local MGroupFunc = false + MGroupFunc = self:_EvalFilterFunctions(MGroup) MGroupInclude = MGroupInclude and MGroupFunc end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index fe0833189..046efe091 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2763,7 +2763,7 @@ end --- Switch on/off invisible flag for the group. -- @param #GROUP self --- @param #boolean switch If true, emission is enabled. If false, emission is disabled. +-- @param #boolean switch If true, Invisible is enabled. If false, Invisible is disabled. -- @return #GROUP self function GROUP:SetCommandInvisible(switch) self:F2( self.GroupName ) @@ -2777,7 +2777,7 @@ end --- Switch on/off immortal flag for the group. -- @param #GROUP self --- @param #boolean switch If true, emission is enabled. If false, emission is disabled. +-- @param #boolean switch If true, Immortal is enabled. If false, Immortal is disabled. -- @return #GROUP self function GROUP:SetCommandImmortal(switch) self:F2( self.GroupName ) @@ -2983,3 +2983,36 @@ function GROUP:GetGroupSTN() return tSTN,text end +--- [GROUND] Determine if a GROUP is a SAM unit, i.e. has radar or optical tracker and is no mobile AAA. +-- @param #GROUP self +-- @return #boolean IsSAM True if SAM, else false +function GROUP:IsSAM() + local issam = false + local units = self:GetUnits() + for _,_unit in pairs(units or {}) do + local unit = _unit -- Wrapper.Unit#UNIT + if unit:HasSEAD() and unit:IsGround() and (not unit:HasAttribute("Mobile AAA")) then + issam = true + break + end + end + return issam +end + +--- [GROUND] Determine if a GROUP is a AAA unit, i.e. has no radar or optical tracker but the AAA = true or the "Mobile AAA" = true attribute. +-- @param #GROUP self +-- @return #boolean IsSAM True if AAA, else false +function GROUP:IsAAA() + local issam = true + local units = self:GetUnits() + for _,_unit in pairs(units or {}) do + local unit = _unit -- Wrapper.Unit#UNIT + if unit:HasSEAD() or (not unit:IsGround()) then + issam = false + if unit:HasAttribute("Mobile AAA") then + issam = true + end + end + end + return issam +end From 0835022c5cbddb1b329e892564af1cee6dbe1133 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 31 Dec 2023 14:57:13 +0100 Subject: [PATCH 87/95] #GROUP * Corrections for "IsAAA()" --- Moose Development/Moose/Wrapper/Group.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 046efe091..b9eb10db0 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2999,19 +2999,19 @@ function GROUP:IsSAM() return issam end ---- [GROUND] Determine if a GROUP is a AAA unit, i.e. has no radar or optical tracker but the AAA = true or the "Mobile AAA" = true attribute. +--- [GROUND] Determine if a GROUP has a AAA unit, i.e. has no radar or optical tracker but the AAA = true or the "Mobile AAA" = true attribute. -- @param #GROUP self -- @return #boolean IsSAM True if AAA, else false function GROUP:IsAAA() - local issam = true + local issam = false local units = self:GetUnits() for _,_unit in pairs(units or {}) do local unit = _unit -- Wrapper.Unit#UNIT - if unit:HasSEAD() or (not unit:IsGround()) then - issam = false - if unit:HasAttribute("Mobile AAA") then - issam = true - end + local desc = unit:GetDesc() or {} + local attr = desc.attributes or {} + if unit:HasSEAD() then return false end + if attr["AAA"] or attr["SAM related"] then + issam = true end end return issam From ea2175bba8a255efba2f75975d8acfa94d299b9c Mon Sep 17 00:00:00 2001 From: kaltokri Date: Sat, 30 Dec 2023 15:52:38 +0100 Subject: [PATCH 88/95] Added debug output to SRS.lua --- Moose Development/Moose/Sound/SRS.lua | 270 ++++++++++++++------------ 1 file changed, 145 insertions(+), 125 deletions(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index e6f044ed8..f3a16e14a 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -446,13 +446,15 @@ end -- @return #MSRS self function MSRS:New(Path, Frequency, Modulation, Backend) + -- Inherit everything from BASE class. + local self=BASE:Inherit(self, BASE:New()) -- #MSRS + + self:F( {Path, Frequency, Modulation, Backend} ) + -- Defaults. Frequency = Frequency or 143 Modulation = Modulation or radio.modulation.AM - -- Inherit everything from BASE class. - local self=BASE:Inherit(self, BASE:New()) -- #MSRS - self.lid = string.format("%s-%s | ", "unknown", self.version) if not self.ConfigLoaded then @@ -466,6 +468,7 @@ function MSRS:New(Path, Frequency, Modulation, Backend) self:SetCoalition() self:SetLabel() self:SetVolume() + self:SetBackend(Backend) else @@ -476,11 +479,15 @@ function MSRS:New(Path, Frequency, Modulation, Backend) end if Frequency then - self:SetFrequencies(Frequency) + self:SetFrequencies(Frequency) end - + if Modulation then - self:SetModulations(Modulation) + self:SetModulations(Modulation) + end + + if Backend then + self:SetBackend(Backend) end end @@ -508,7 +515,7 @@ end -- @param #string Backend Backend used. Default is `MSRS.Backend.SRSEXE`. -- @return #MSRS self function MSRS:SetBackend(Backend) - + self:F( {Backend=Backend} ) self.backend=Backend or MSRS.Backend.SRSEXE return self @@ -518,7 +525,7 @@ end -- @param #MSRS self -- @return #MSRS self function MSRS:SetBackendGRPC() - + self:F() self:SetBackend(MSRS.Backend.GRPC) return self @@ -527,8 +534,8 @@ end --- Set `DCS-SR-ExternalAudio.exe` as backend to communicate with SRS. -- @param #MSRS self -- @return #MSRS self -function MSRS:SetBackendSRSEXE(Backend) - +function MSRS:SetBackendSRSEXE() + self:F() self:SetBackend(MSRS.Backend.SRSEXE) return self @@ -537,12 +544,14 @@ end --- Set the default backend. -- @param #MSRS self function MSRS.SetDefaultBackend(Backend) + self:F( {Backend=Backend} ) MSRS.backend=Backend or MSRS.Backend.SRSEXE end --- Set DCS-gRPC to be the default backend. -- @param #MSRS self function MSRS.SetDefaultBackendGRPC() + self:F() MSRS.backend=MSRS.Backend.GRPC end @@ -558,6 +567,7 @@ end -- @param #string Path Path to the directory, where the sound file is located. Default is `C:\\Program Files\\DCS-SimpleRadio-Standalone`. -- @return #MSRS self function MSRS:SetPath(Path) + self:F( {Path=Path} ) -- Set path. self.path=Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" @@ -570,7 +580,7 @@ function MSRS:SetPath(Path) end -- Debug output. - self:T(string.format("SRS path=%s", self:GetPath())) + self:F(string.format("SRS path=%s", self:GetPath())) return self end @@ -587,6 +597,7 @@ end -- @param #number Volume Volume - 1.0 is max, 0.0 is silence -- @return #MSRS self function MSRS:SetVolume(Volume) + self:F( {Volume=Volume} ) local volume = Volume or 1 if volume > 1 then volume = 1 elseif volume < 0 then volume = 0 end self.volume = volume @@ -605,6 +616,7 @@ end -- @param #number Label. Default "ROBOT" -- @return #MSRS self function MSRS:SetLabel(Label) + self:F( {Label=Label} ) self.Label=Label or "ROBOT" return self end @@ -621,6 +633,7 @@ end -- @param #number Port Port. Default 5002. -- @return #MSRS self function MSRS:SetPort(Port) + self:F( {Port=Port} ) self.port=Port or 5002 self:T(string.format("SRS port=%s", self:GetPort())) return self @@ -638,6 +651,7 @@ end -- @param #number Coalition Coalition. Default 0. -- @return #MSRS self function MSRS:SetCoalition(Coalition) + self:F( {Coalition=Coalition} ) self.coalition=Coalition or 0 return self end @@ -655,7 +669,7 @@ end -- @param #table Frequencies Frequencies in MHz. Can also be given as a #number if only one frequency should be used. -- @return #MSRS self function MSRS:SetFrequencies(Frequencies) - + self:F( Frequencies ) self.frequencies=UTILS.EnsureTable(Frequencies, false) return self @@ -666,7 +680,7 @@ end -- @param #table Frequencies Frequencies in MHz. Can also be given as a #number if only one frequency should be used. -- @return #MSRS self function MSRS:AddFrequencies(Frequencies) - + self:F( Frequencies ) for _,_freq in pairs(UTILS.EnsureTable(Frequencies, false)) do self:T(self.lid..string.format("Adding frequency %s", tostring(_freq))) table.insert(self.frequencies,_freq) @@ -688,7 +702,7 @@ end -- @param #table Modulations Modulations. Can also be given as a #number if only one modulation should be used. -- @return #MSRS self function MSRS:SetModulations(Modulations) - + self:F( Modulations ) self.modulations=UTILS.EnsureTable(Modulations, false) -- Debug info. @@ -703,7 +717,7 @@ end -- @param #table Modulations Modulations. Can also be given as a #number if only one modulation should be used. -- @return #MSRS self function MSRS:AddModulations(Modulations) - + self:F( Modulations ) for _,_mod in pairs(UTILS.EnsureTable(Modulations, false)) do table.insert(self.modulations,_mod) end @@ -723,7 +737,7 @@ end -- @param #string Gender Gender: "male" or "female" (default). -- @return #MSRS self function MSRS:SetGender(Gender) - + self:F( {Gender=Gender} ) Gender=Gender or "female" self.gender=Gender:lower() @@ -739,7 +753,7 @@ end -- @param #string Culture Culture, *e.g.* "en-GB". -- @return #MSRS self function MSRS:SetCulture(Culture) - + self:F( {Culture=Culture} ) self.culture=Culture return self @@ -750,7 +764,7 @@ end -- @param #string Voice Voice. -- @return #MSRS self function MSRS:SetVoice(Voice) - + self:F( {Voice=Voice} ) self.voice=Voice return self @@ -762,7 +776,7 @@ end -- @param #string Provider Provider. Default is as set by @{#MSRS.SetProvider}, which itself defaults to `MSRS.Provider.WINDOWS` if not set. -- @return #MSRS self function MSRS:SetVoiceProvider(Voice, Provider) - + self:F( {Voice=Voice, Provider=Provider} ) self.poptions=self.poptions or {} self.poptions[Provider or self:GetProvider()]=Voice @@ -775,7 +789,7 @@ end -- @param #string Voice Voice. Default `"Microsoft Hazel Desktop"`. -- @return #MSRS self function MSRS:SetVoiceWindows(Voice) - + self:F( {Voice=Voice} ) self:SetVoiceProvider(Voice or "Microsoft Hazel Desktop", MSRS.Provider.WINDOWS) return self @@ -786,7 +800,7 @@ end -- @param #string Voice Voice. Default `MSRS.Voices.Google.Standard.en_GB_Standard_A`. -- @return #MSRS self function MSRS:SetVoiceGoogle(Voice) - + self:F( {Voice=Voice} ) self:SetVoiceProvider(Voice or MSRS.Voices.Google.Standard.en_GB_Standard_A, MSRS.Provider.GOOGLE) return self @@ -798,7 +812,7 @@ end -- @param #string Voice [Azure Voice](https://learn.microsoft.com/azure/cognitive-services/speech-service/language-support). Default `"en-US-AriaNeural"`. -- @return #MSRS self function MSRS:SetVoiceAzure(Voice) - + self:F( {Voice=Voice} ) self:SetVoiceProvider(Voice or "en-US-AriaNeural", MSRS.Provider.AZURE) return self @@ -809,7 +823,7 @@ end -- @param #string Voice [AWS Voice](https://docs.aws.amazon.com/polly/latest/dg/voicelist.html). Default `"Brian"`. -- @return #MSRS self function MSRS:SetVoiceAmazon(Voice) - + self:F( {Voice=Voice} ) self:SetVoiceProvider(Voice or "Brian", MSRS.Provider.AMAZON) return self @@ -836,7 +850,7 @@ end -- @param Core.Point#COORDINATE Coordinate Origin of the transmission. -- @return #MSRS self function MSRS:SetCoordinate(Coordinate) - + self:F( Coordinate ) self.coordinate=Coordinate return self @@ -847,7 +861,7 @@ end -- @param #string PathToCredentials Full path to the google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". Can also be the Google API key. -- @return #MSRS self function MSRS:SetGoogle(PathToCredentials) - + self:F( {PathToCredentials=PathToCredentials} ) if PathToCredentials then self.provider = MSRS.Provider.GOOGLE @@ -864,6 +878,7 @@ end -- @param #string APIKey API Key, usually a string of length 40 with characters and numbers. -- @return #MSRS self function MSRS:SetGoogleAPIKey(APIKey) + self:F( {APIKey=APIKey} ) if APIKey then self.provider = MSRS.Provider.GOOGLE @@ -893,6 +908,7 @@ end -- @param #string Provider -- @return #MSRS self function MSRS:SetProvider(Provider) + self:F( {Provider=Provider} ) self.provider = Provider or MSRS.Provider.WINDOWS return self end @@ -914,21 +930,15 @@ end -- @param #string Region Region to use. -- @return #MSRS.ProviderOptions Provider optionas table. function MSRS:SetProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) - + self:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} ) local option=MSRS._CreateProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) - + if self then - self.poptions=self.poptions or {} - self.poptions[Provider]=option - else - MSRS.poptions=MSRS.poptions or {} - MSRS.poptions[Provider]=option - end return option @@ -942,9 +952,9 @@ end -- @param #string Region Region to use. -- @return #MSRS.ProviderOptions Provider optionas table. function MSRS._CreateProviderOptions(Provider, CredentialsFile, AccessKey, SecretKey, Region) - + self:F( {Provider, CredentialsFile, AccessKey, SecretKey, Region} ) local option={} --#MSRS.ProviderOptions - + option.provider=Provider option.credentials=CredentialsFile option.key=AccessKey @@ -960,9 +970,8 @@ end -- @param #string AccessKey Your API access key. This is necessary if DCS-gRPC is used as backend. -- @return #MSRS self function MSRS:SetProviderOptionsGoogle(CredentialsFile, AccessKey) - + self:F( {CredentialsFile, AccessKey} ) self:SetProviderOptions(MSRS.Provider.GOOGLE, CredentialsFile, AccessKey) - return self end @@ -973,9 +982,8 @@ end -- @param #string Region Your AWS [region](https://docs.aws.amazon.com/general/latest/gr/pol.html). -- @return #MSRS self function MSRS:SetProviderOptionsAmazon(AccessKey, SecretKey, Region) - + self:F( {AccessKey, SecretKey, Region} ) self:SetProviderOptions(MSRS.Provider.AMAZON, nil, AccessKey, SecretKey, Region) - return self end @@ -985,9 +993,8 @@ end -- @param #string Region Your Azure [region](https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/regions). -- @return #MSRS self function MSRS:SetProviderOptionsAzure(AccessKey, Region) - + self:F( {AccessKey, Region} ) self:SetProviderOptions(MSRS.Provider.AZURE, nil, AccessKey, nil, Region) - return self end @@ -1005,6 +1012,7 @@ end -- @param #MSRS self -- @return #MSRS self function MSRS:SetTTSProviderGoogle() + self:F() self:SetProvider(MSRS.Provider.GOOGLE) return self end @@ -1013,6 +1021,7 @@ end -- @param #MSRS self -- @return #MSRS self function MSRS:SetTTSProviderMicrosoft() + self:F() self:SetProvider(MSRS.Provider.WINDOWS) return self end @@ -1022,6 +1031,7 @@ end -- @param #MSRS self -- @return #MSRS self function MSRS:SetTTSProviderAzure() + self:F() self:SetProvider(MSRS.Provider.AZURE) return self end @@ -1030,6 +1040,7 @@ end -- @param #MSRS self -- @return #MSRS self function MSRS:SetTTSProviderAmazon() + self:F() self:SetProvider(MSRS.Provider.AMAZON) return self end @@ -1039,7 +1050,7 @@ end -- @param #MSRS self -- @return #MSRS self function MSRS:Help() - + self:F() -- Path and exe. local path=self:GetPath() local exe="DCS-SR-ExternalAudio.exe" @@ -1074,14 +1085,22 @@ end -- @param #number Delay Delay in seconds, before the sound file is played. -- @return #MSRS self function MSRS:PlaySoundFile(Soundfile, Delay) + self:F( {Soundfile, Delay} ) + + -- Sound file name. + local soundfile=Soundfile:GetName() + + -- First check if text file exists! + local exists=UTILS.FileExists(soundfile) + if not exists then + self:E("ERROR: MSRS sound file does not exist! File="..soundfile) + return self + end if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlaySoundFile, self, Soundfile, 0) else - -- Sound file name. - local soundfile=Soundfile:GetName() - -- Get command. local command=self:_GetCommand() @@ -1102,15 +1121,14 @@ end -- @param #number Delay Delay in seconds, before the sound file is played. -- @return #MSRS self function MSRS:PlaySoundText(SoundText, Delay) + self:F( {SoundText, Delay} ) if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlaySoundText, self, SoundText, 0) else if self.backend==MSRS.Backend.GRPC then - self:_DCSgRPCtts(SoundText.text, nil, SoundText.gender, SoundText.culture, SoundText.voice, SoundText.volume, SoundText.label, SoundText.coordinate) - else -- Get command. @@ -1136,21 +1154,18 @@ end -- @param Core.Point#COORDINATE Coordinate Coordinate. -- @return #MSRS self function MSRS:PlayText(Text, Delay, Coordinate) + self:F( {Text, Delay, Coordinate} ) if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, nil, Coordinate) else - + if self.backend==MSRS.Backend.GRPC then - self:T(self.lid.."Transmitting") self:_DCSgRPCtts(Text, nil, nil , nil, nil, nil, nil, Coordinate) - else - self:PlayTextExt(Text, Delay, nil, nil, nil, nil, nil, nil, nil, Coordinate) - - end + end end @@ -1171,6 +1186,7 @@ end -- @param Core.Point#COORDINATE Coordinate Coordinate. -- @return #MSRS self function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) + self:F( {Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate} ) if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate) @@ -1208,6 +1224,7 @@ end -- @param #number Delay Delay in seconds, before the message is played. -- @return #MSRS self function MSRS:PlayTextFile(TextFile, Delay) + self:F( {TextFile, Delay} ) if Delay and Delay>0 then self:ScheduleOnce(Delay, MSRS.PlayTextFile, self, TextFile, 0) @@ -1253,6 +1270,7 @@ end -- @return #number Longitude (or 0 if no input coordinate was given). -- @return #number Altitude (or 0 if no input coordinate was given). function MSRS:_GetLatLongAlt(Coordinate) + self:F( {Coordinate=Coordinate} ) local lat=0.0 local lon=0.0 @@ -1284,6 +1302,7 @@ end -- @param Core.Point#COORDINATE coordinate Coordinate. -- @return #string Command. function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port, label, coordinate) + self:F( {freqs, modus, coal, gender, voice, culture, volume, speed, port, label, coordinate} ) local path=self:GetPath() local exe="DCS-SR-ExternalAudio.exe" @@ -1356,82 +1375,83 @@ end -- @param #string command Command to executer -- @return #number Return value of os.execute() command. function MSRS:_ExecCommand(command) + self:F( {command=command} ) - -- Skip this function if _GetCommand was not able to find the executable - if string.find(command, "CommandNotFound") then return 0 end + -- Skip this function if _GetCommand was not able to find the executable + if string.find(command, "CommandNotFound") then return 0 end + + local batContent = command.." && exit" + -- Create a tmp file. + local filename=os.getenv('TMP').."\\MSRS-"..MSRS.uuid()..".bat" + + local script=io.open(filename, "w+") + script:write(batContent) + script:close() + + self:T("MSRS batch file created: "..filename) + self:T("MSRS batch content: "..batContent) + + local res=nil + if true then - local batContent = command.." && exit" -- Create a tmp file. - local filename=os.getenv('TMP').."\\MSRS-"..MSRS.uuid()..".bat" + local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs" - local script=io.open(filename, "w+") - script:write(batContent) + -- VBS script + local script = io.open(filenvbs, "w+") + script:write(string.format('Dim WinScriptHost\n')) + script:write(string.format('Set WinScriptHost = CreateObject("WScript.Shell")\n')) + script:write(string.format('WinScriptHost.Run Chr(34) & "%s" & Chr(34), 0\n', filename)) + script:write(string.format('Set WinScriptHost = Nothing')) + script:close() + self:T("MSRS vbs file created to start batch="..filenvbs) + + -- Run visual basic script. This still pops up a window but very briefly and does not put the DCS window out of focus. + local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs) + + -- Debug output. + self:T("MSRS execute VBS command="..runvbs) + + -- Play file in 0.01 seconds + res=os.execute(runvbs) + + -- Remove file in 1 second. + timer.scheduleFunction(os.remove, filename, timer.getTime()+1) + timer.scheduleFunction(os.remove, filenvbs, timer.getTime()+1) + self:T("MSRS vbs and batch file removed") + + elseif false then + + -- Create a tmp file. + local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs" + + -- VBS script + local script = io.open(filenvbs, "w+") + script:write(string.format('Set oShell = CreateObject ("Wscript.Shell")\n')) + script:write(string.format('Dim strArgs\n')) + script:write(string.format('strArgs = "cmd /c %s"\n', filename)) + script:write(string.format('oShell.Run strArgs, 0, false')) script:close() - self:T("MSRS batch file created: "..filename) - self:T("MSRS batch content: "..batContent) + local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs) - local res=nil - if true then + -- Play file in 0.01 seconds + res=os.execute(runvbs) - -- Create a tmp file. - local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs" + else + -- Play command. + command=string.format('start /b "" "%s"', filename) - -- VBS script - local script = io.open(filenvbs, "w+") - script:write(string.format('Dim WinScriptHost\n')) - script:write(string.format('Set WinScriptHost = CreateObject("WScript.Shell")\n')) - script:write(string.format('WinScriptHost.Run Chr(34) & "%s" & Chr(34), 0\n', filename)) - script:write(string.format('Set WinScriptHost = Nothing')) - script:close() - self:T("MSRS vbs file created to start batch="..filenvbs) + -- Debug output. + self:T("MSRS execute command="..command) - -- Run visual basic script. This still pops up a window but very briefly and does not put the DCS window out of focus. - local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs) + -- Execute command + res=os.execute(command) - -- Debug output. - self:T("MSRS execute VBS command="..runvbs) + -- Remove file in 1 second. + timer.scheduleFunction(os.remove, filename, timer.getTime()+1) - -- Play file in 0.01 seconds - res=os.execute(runvbs) - - -- Remove file in 1 second. - timer.scheduleFunction(os.remove, filename, timer.getTime()+1) - timer.scheduleFunction(os.remove, filenvbs, timer.getTime()+1) - self:T("MSRS vbs and batch file removed") - - elseif false then - - -- Create a tmp file. - local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs" - - -- VBS script - local script = io.open(filenvbs, "w+") - script:write(string.format('Set oShell = CreateObject ("Wscript.Shell")\n')) - script:write(string.format('Dim strArgs\n')) - script:write(string.format('strArgs = "cmd /c %s"\n', filename)) - script:write(string.format('oShell.Run strArgs, 0, false')) - script:close() - - local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs) - - -- Play file in 0.01 seconds - res=os.execute(runvbs) - - else - -- Play command. - command=string.format('start /b "" "%s"', filename) - - -- Debug output. - self:T("MSRS execute command="..command) - - -- Execute command - res=os.execute(command) - - -- Remove file in 1 second. - timer.scheduleFunction(os.remove, filename, timer.getTime()+1) - - end + end return res @@ -1489,9 +1509,9 @@ end -- @return #MSRS self function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate) - -- Debug info. - self:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") - self:T({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) + -- Debug info. + self:F("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()") + self:F({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate}) local options = {} -- #MSRS.GRPCOptions @@ -1517,6 +1537,7 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab -- Provider (win, gcloud, ...) local provider = self.provider or MSRS.Provider.WINDOWS + self:F({provider=provider}) -- Provider options: voice, credentials options.provider = {} @@ -1548,10 +1569,9 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab end for _,freq in pairs(Frequencies) do - self:T("GRPC.tts") - self:T(ssml) - self:T(freq) - self:T(options) + self:F("Calling GRPC.tts with the following parameter:") + self:F({ssml=ssml, freq=freq, options=options}) + self:F(options.provider[provider]) GRPC.tts(ssml, freq*1e6, options) end @@ -1647,7 +1667,7 @@ function MSRS:LoadConfigFile(Path,Filename) env.info("*****Note - lfs and os need to be desanitized for MSRS to work!") return false end - + local path = Path or lfs.writedir()..MSRS.ConfigFilePath local file = Filename or MSRS.ConfigFileName or "Moose_MSRS.lua" local pathandfile = path..file From 5ba8f9e0e88ee4b1ba65d46e3153703c8c5629aa Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 31 Dec 2023 17:20:45 +0100 Subject: [PATCH 89/95] #UTILS * Added NATO name for KC-135MPRS #SET * Made filtering a tad faster --- Moose Development/Moose/Core/Set.lua | 58 ++++++++++----------- Moose Development/Moose/Utilities/Enums.lua | 1 + 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index ba08c103b..f4e5a0ed7 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1986,7 +1986,7 @@ do MGroupInclude = MGroupInclude and MGroupActive end - if self.Filter.Coalitions then + if self.Filter.Coalitions and MGroupInclude then local MGroupCoalition = false for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do self:T3( { "Coalition:", MGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) @@ -1997,7 +1997,7 @@ do MGroupInclude = MGroupInclude and MGroupCoalition end - if self.Filter.Categories then + if self.Filter.Categories and MGroupInclude then local MGroupCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do self:T3( { "Category:", MGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } ) @@ -2008,7 +2008,7 @@ do MGroupInclude = MGroupInclude and MGroupCategory end - if self.Filter.Countries then + if self.Filter.Countries and MGroupInclude then local MGroupCountry = false for CountryID, CountryName in pairs( self.Filter.Countries ) do self:T3( { "Country:", MGroup:GetCountry(), CountryName } ) @@ -2019,7 +2019,7 @@ do MGroupInclude = MGroupInclude and MGroupCountry end - if self.Filter.GroupPrefixes then + if self.Filter.GroupPrefixes and MGroupInclude then local MGroupPrefix = false for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do self:T3( { "Prefix:", string.find( MGroup:GetName(), GroupPrefix, 1 ), GroupPrefix } ) @@ -2030,7 +2030,7 @@ do MGroupInclude = MGroupInclude and MGroupPrefix end - if self.Filter.Zones then + if self.Filter.Zones and MGroupInclude then local MGroupZone = false for ZoneName, Zone in pairs( self.Filter.Zones ) do --self:T( "Zone:", ZoneName ) @@ -2041,7 +2041,7 @@ do MGroupInclude = MGroupInclude and MGroupZone end - if self.Filter.Functions then + if self.Filter.Functions and MGroupInclude then local MGroupFunc = false MGroupFunc = self:_EvalFilterFunctions(MGroup) MGroupInclude = MGroupInclude and MGroupFunc @@ -3192,7 +3192,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitActive end - if self.Filter.Coalitions then + if self.Filter.Coalitions and MUnitInclude then local MUnitCoalition = false for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do self:F( { "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) @@ -3203,7 +3203,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitCoalition end - if self.Filter.Categories then + if self.Filter.Categories and MUnitInclude then local MUnitCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do self:T3( { "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } ) @@ -3214,7 +3214,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitCategory end - if self.Filter.Types then + if self.Filter.Types and MUnitInclude then local MUnitType = false for TypeID, TypeName in pairs( self.Filter.Types ) do self:T3( { "Type:", MUnit:GetTypeName(), TypeName } ) @@ -3225,7 +3225,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitType end - if self.Filter.Countries then + if self.Filter.Countries and MUnitInclude then local MUnitCountry = false for CountryID, CountryName in pairs( self.Filter.Countries ) do self:T3( { "Country:", MUnit:GetCountry(), CountryName } ) @@ -3236,7 +3236,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitCountry end - if self.Filter.UnitPrefixes then + if self.Filter.UnitPrefixes and MUnitInclude then local MUnitPrefix = false for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do self:T3( { "Prefix:", string.find( MUnit:GetName(), UnitPrefix, 1 ), UnitPrefix } ) @@ -3247,7 +3247,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitPrefix end - if self.Filter.RadarTypes then + if self.Filter.RadarTypes and MUnitInclude then local MUnitRadar = false for RadarTypeID, RadarType in pairs( self.Filter.RadarTypes ) do self:T3( { "Radar:", RadarType } ) @@ -3261,7 +3261,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitRadar end - if self.Filter.SEAD then + if self.Filter.SEAD and MUnitInclude then local MUnitSEAD = false if MUnit:HasSEAD() == true then self:T3( "SEAD Found" ) @@ -3271,7 +3271,7 @@ do -- SET_UNIT end end - if self.Filter.Zones then + if self.Filter.Zones and MUnitInclude then local MGroupZone = false for ZoneName, Zone in pairs( self.Filter.Zones ) do self:T3( "Zone:", ZoneName ) @@ -3282,7 +3282,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MGroupZone end - if self.Filter.Functions then + if self.Filter.Functions and MUnitInclude then local MUnitFunc = self:_EvalFilterFunctions(MUnit) MUnitInclude = MUnitInclude and MUnitFunc end @@ -4689,7 +4689,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientActive end - if self.Filter.Coalitions then + if self.Filter.Coalitions and MClientInclude then local MClientCoalition = false for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName ) @@ -4702,7 +4702,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientCoalition end - if self.Filter.Categories then + if self.Filter.Categories and MClientInclude then local MClientCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) @@ -4715,7 +4715,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientCategory end - if self.Filter.Types then + if self.Filter.Types and MClientInclude then local MClientType = false for TypeID, TypeName in pairs( self.Filter.Types ) do self:T3( { "Type:", MClient:GetTypeName(), TypeName } ) @@ -4727,7 +4727,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientType end - if self.Filter.Countries then + if self.Filter.Countries and MClientInclude then local MClientCountry = false for CountryID, CountryName in pairs( self.Filter.Countries ) do local ClientCountryID = _DATABASE:GetCountryFromClientTemplate( MClientName ) @@ -4740,7 +4740,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientCountry end - if self.Filter.ClientPrefixes then + if self.Filter.ClientPrefixes and MClientInclude then local MClientPrefix = false for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do self:T3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } ) @@ -4752,7 +4752,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientPrefix end - if self.Filter.Zones then + if self.Filter.Zones and MClientInclude then local MClientZone = false for ZoneName, Zone in pairs( self.Filter.Zones ) do self:T3( "Zone:", ZoneName ) @@ -4764,7 +4764,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientZone end - if self.Filter.Playernames then + if self.Filter.Playernames and MClientInclude then local MClientPlayername = false local playername = MClient:GetPlayerName() or "Unknown" --self:T(playername) @@ -4777,7 +4777,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientPlayername end - if self.Filter.Callsigns then + if self.Filter.Callsigns and MClientInclude then local MClientCallsigns = false local callsign = MClient:GetCallsign() --self:I(callsign) @@ -4790,7 +4790,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientCallsigns end - if self.Filter.Functions then + if self.Filter.Functions and MClientInclude then local MClientFunc = self:_EvalFilterFunctions(MClient) MClientInclude = MClientInclude and MClientFunc end @@ -5554,7 +5554,7 @@ do -- SET_AIRBASE MAirbaseInclude = MAirbaseInclude and MAirbaseCoalition end - if self.Filter.Categories then + if self.Filter.Categories and MAirbaseInclude then local MAirbaseCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase( MAirbaseName ) @@ -7900,7 +7900,7 @@ do -- SET_OPSGROUP end -- Filter coalitions. - if self.Filter.Coalitions then + if self.Filter.Coalitions and MGroupInclude then local MGroupCoalition = false @@ -7914,7 +7914,7 @@ do -- SET_OPSGROUP end -- Filter categories. - if self.Filter.Categories then + if self.Filter.Categories and MGroupInclude then local MGroupCategory = false @@ -7928,7 +7928,7 @@ do -- SET_OPSGROUP end -- Filter countries. - if self.Filter.Countries then + if self.Filter.Countries and MGroupInclude then local MGroupCountry = false for CountryID, CountryName in pairs( self.Filter.Countries ) do if country.id[CountryName] == MGroup:GetCountry() then @@ -7939,7 +7939,7 @@ do -- SET_OPSGROUP end -- Filter "prefixes". - if self.Filter.GroupPrefixes then + if self.Filter.GroupPrefixes and MGroupInclude then local MGroupPrefix = false diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index beab1f0fb..d3b63882f 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -523,6 +523,7 @@ ENUMS.ReportingName = Hawkeye = "E-2D", Sentry = "E-3A", Stratotanker = "KC-135", + Gasstation = "KC-135MPRS", Extender = "KC-10", Orion = "P-3C", Viking = "S-3B", From 5d510807c9c495a1caa09832cca9f8c032bf181f Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 31 Dec 2023 17:25:56 +0100 Subject: [PATCH 90/95] xxx --- Moose Development/Moose/Core/Set.lua | 58 ++++++++++++++-------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index ba08c103b..f4e5a0ed7 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1986,7 +1986,7 @@ do MGroupInclude = MGroupInclude and MGroupActive end - if self.Filter.Coalitions then + if self.Filter.Coalitions and MGroupInclude then local MGroupCoalition = false for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do self:T3( { "Coalition:", MGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) @@ -1997,7 +1997,7 @@ do MGroupInclude = MGroupInclude and MGroupCoalition end - if self.Filter.Categories then + if self.Filter.Categories and MGroupInclude then local MGroupCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do self:T3( { "Category:", MGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } ) @@ -2008,7 +2008,7 @@ do MGroupInclude = MGroupInclude and MGroupCategory end - if self.Filter.Countries then + if self.Filter.Countries and MGroupInclude then local MGroupCountry = false for CountryID, CountryName in pairs( self.Filter.Countries ) do self:T3( { "Country:", MGroup:GetCountry(), CountryName } ) @@ -2019,7 +2019,7 @@ do MGroupInclude = MGroupInclude and MGroupCountry end - if self.Filter.GroupPrefixes then + if self.Filter.GroupPrefixes and MGroupInclude then local MGroupPrefix = false for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do self:T3( { "Prefix:", string.find( MGroup:GetName(), GroupPrefix, 1 ), GroupPrefix } ) @@ -2030,7 +2030,7 @@ do MGroupInclude = MGroupInclude and MGroupPrefix end - if self.Filter.Zones then + if self.Filter.Zones and MGroupInclude then local MGroupZone = false for ZoneName, Zone in pairs( self.Filter.Zones ) do --self:T( "Zone:", ZoneName ) @@ -2041,7 +2041,7 @@ do MGroupInclude = MGroupInclude and MGroupZone end - if self.Filter.Functions then + if self.Filter.Functions and MGroupInclude then local MGroupFunc = false MGroupFunc = self:_EvalFilterFunctions(MGroup) MGroupInclude = MGroupInclude and MGroupFunc @@ -3192,7 +3192,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitActive end - if self.Filter.Coalitions then + if self.Filter.Coalitions and MUnitInclude then local MUnitCoalition = false for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do self:F( { "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) @@ -3203,7 +3203,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitCoalition end - if self.Filter.Categories then + if self.Filter.Categories and MUnitInclude then local MUnitCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do self:T3( { "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } ) @@ -3214,7 +3214,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitCategory end - if self.Filter.Types then + if self.Filter.Types and MUnitInclude then local MUnitType = false for TypeID, TypeName in pairs( self.Filter.Types ) do self:T3( { "Type:", MUnit:GetTypeName(), TypeName } ) @@ -3225,7 +3225,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitType end - if self.Filter.Countries then + if self.Filter.Countries and MUnitInclude then local MUnitCountry = false for CountryID, CountryName in pairs( self.Filter.Countries ) do self:T3( { "Country:", MUnit:GetCountry(), CountryName } ) @@ -3236,7 +3236,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitCountry end - if self.Filter.UnitPrefixes then + if self.Filter.UnitPrefixes and MUnitInclude then local MUnitPrefix = false for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do self:T3( { "Prefix:", string.find( MUnit:GetName(), UnitPrefix, 1 ), UnitPrefix } ) @@ -3247,7 +3247,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitPrefix end - if self.Filter.RadarTypes then + if self.Filter.RadarTypes and MUnitInclude then local MUnitRadar = false for RadarTypeID, RadarType in pairs( self.Filter.RadarTypes ) do self:T3( { "Radar:", RadarType } ) @@ -3261,7 +3261,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitRadar end - if self.Filter.SEAD then + if self.Filter.SEAD and MUnitInclude then local MUnitSEAD = false if MUnit:HasSEAD() == true then self:T3( "SEAD Found" ) @@ -3271,7 +3271,7 @@ do -- SET_UNIT end end - if self.Filter.Zones then + if self.Filter.Zones and MUnitInclude then local MGroupZone = false for ZoneName, Zone in pairs( self.Filter.Zones ) do self:T3( "Zone:", ZoneName ) @@ -3282,7 +3282,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MGroupZone end - if self.Filter.Functions then + if self.Filter.Functions and MUnitInclude then local MUnitFunc = self:_EvalFilterFunctions(MUnit) MUnitInclude = MUnitInclude and MUnitFunc end @@ -4689,7 +4689,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientActive end - if self.Filter.Coalitions then + if self.Filter.Coalitions and MClientInclude then local MClientCoalition = false for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName ) @@ -4702,7 +4702,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientCoalition end - if self.Filter.Categories then + if self.Filter.Categories and MClientInclude then local MClientCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) @@ -4715,7 +4715,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientCategory end - if self.Filter.Types then + if self.Filter.Types and MClientInclude then local MClientType = false for TypeID, TypeName in pairs( self.Filter.Types ) do self:T3( { "Type:", MClient:GetTypeName(), TypeName } ) @@ -4727,7 +4727,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientType end - if self.Filter.Countries then + if self.Filter.Countries and MClientInclude then local MClientCountry = false for CountryID, CountryName in pairs( self.Filter.Countries ) do local ClientCountryID = _DATABASE:GetCountryFromClientTemplate( MClientName ) @@ -4740,7 +4740,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientCountry end - if self.Filter.ClientPrefixes then + if self.Filter.ClientPrefixes and MClientInclude then local MClientPrefix = false for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do self:T3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } ) @@ -4752,7 +4752,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientPrefix end - if self.Filter.Zones then + if self.Filter.Zones and MClientInclude then local MClientZone = false for ZoneName, Zone in pairs( self.Filter.Zones ) do self:T3( "Zone:", ZoneName ) @@ -4764,7 +4764,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientZone end - if self.Filter.Playernames then + if self.Filter.Playernames and MClientInclude then local MClientPlayername = false local playername = MClient:GetPlayerName() or "Unknown" --self:T(playername) @@ -4777,7 +4777,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientPlayername end - if self.Filter.Callsigns then + if self.Filter.Callsigns and MClientInclude then local MClientCallsigns = false local callsign = MClient:GetCallsign() --self:I(callsign) @@ -4790,7 +4790,7 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientCallsigns end - if self.Filter.Functions then + if self.Filter.Functions and MClientInclude then local MClientFunc = self:_EvalFilterFunctions(MClient) MClientInclude = MClientInclude and MClientFunc end @@ -5554,7 +5554,7 @@ do -- SET_AIRBASE MAirbaseInclude = MAirbaseInclude and MAirbaseCoalition end - if self.Filter.Categories then + if self.Filter.Categories and MAirbaseInclude then local MAirbaseCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase( MAirbaseName ) @@ -7900,7 +7900,7 @@ do -- SET_OPSGROUP end -- Filter coalitions. - if self.Filter.Coalitions then + if self.Filter.Coalitions and MGroupInclude then local MGroupCoalition = false @@ -7914,7 +7914,7 @@ do -- SET_OPSGROUP end -- Filter categories. - if self.Filter.Categories then + if self.Filter.Categories and MGroupInclude then local MGroupCategory = false @@ -7928,7 +7928,7 @@ do -- SET_OPSGROUP end -- Filter countries. - if self.Filter.Countries then + if self.Filter.Countries and MGroupInclude then local MGroupCountry = false for CountryID, CountryName in pairs( self.Filter.Countries ) do if country.id[CountryName] == MGroup:GetCountry() then @@ -7939,7 +7939,7 @@ do -- SET_OPSGROUP end -- Filter "prefixes". - if self.Filter.GroupPrefixes then + if self.Filter.GroupPrefixes and MGroupInclude then local MGroupPrefix = false From 2d086a62f089e4e50599d71710ffbdc43c57cf8b Mon Sep 17 00:00:00 2001 From: kaltokri Date: Mon, 1 Jan 2024 00:38:48 +0100 Subject: [PATCH 91/95] Added link to example missions to MSRS --- Moose Development/Moose/Sound/SRS.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index f3a16e14a..f8561928d 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -14,7 +14,7 @@ -- -- === -- --- ## Missions: None yet +-- ## Example Missions: [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Sound/MSRS). -- -- === -- From d640acc7cc00f5e1e18d28320115124ef64ca04c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 1 Jan 2024 13:07:05 +0100 Subject: [PATCH 92/95] docu --- Moose Development/Moose/Ops/CTLD.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 0ae3a2ef8..805be85cc 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1443,6 +1443,7 @@ function CTLD:New(Coalition, Prefixes, Alias) -- @param #number delay Delay in seconds. --- Triggers the FSM event "Stop". Stops the CTLD and all its event handlers. + -- @function [parent=#CTLD] Stop -- @param #CTLD self --- Triggers the FSM event "Stop" after a delay. Stops the CTLD and all its event handlers. From e0a108e00db41dcb1ab230a839747dabaa015a20 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 1 Jan 2024 13:11:44 +0100 Subject: [PATCH 93/95] #TIRESIAS * Initial release --- .../Moose/Functional/Tiresias.lua | 590 ++++++++++++++++++ Moose Development/Moose/Modules.lua | 1 + Moose Setup/Moose.files | 1 + 3 files changed, 592 insertions(+) create mode 100644 Moose Development/Moose/Functional/Tiresias.lua diff --git a/Moose Development/Moose/Functional/Tiresias.lua b/Moose Development/Moose/Functional/Tiresias.lua new file mode 100644 index 000000000..078bfda56 --- /dev/null +++ b/Moose Development/Moose/Functional/Tiresias.lua @@ -0,0 +1,590 @@ +--- **Functional** - TIRESIAS - manages AI behaviour. +-- +-- === +-- +-- The @{#TIRESIAS} class is working in the back to keep your large-scale ground units in check. +-- +-- ## Features: +-- +-- * Designed to keep CPU and Network usage lower on missions with a lot of ground units. +-- * Does not affect ships to keep the Navy guys happy. +-- * Does not affect OpsGroup type groups. +-- * Distinguishes between SAM groups, AAA groups and other ground groups. +-- * Exceptions can be defined to keep certain actions going. +-- * Works coalition-independent in the back +-- * Easy setup. +-- +-- === +-- +-- ## Missions: +-- +-- ### [TIRESIAS](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master) +-- +-- === +-- +-- ### Author : **applevangelist ** +-- +-- @module Functional.Tiresias +-- @image Functional.Tiresias.jpg +-- +-- Last Update: Dec 2023 + +------------------------------------------------------------------------- +--- **TIRESIAS** class, extends Core.Base#BASE +-- @type TIRESIAS +-- @field #string ClassName +-- @field #booelan debug +-- @field #string version +-- @field #number Interval +-- @field Core.Set#SET_GROUP GroundSet +-- @field #number Coalition +-- @field Core.Set#SET_GROUP VehicleSet +-- @field Core.Set#SET_GROUP AAASet +-- @field Core.Set#SET_GROUP SAMSet +-- @field Core.Set#SET_GROUP ExceptionSet +-- @field Core.Set#SET_OPSGROUP OpsGroupSet +-- @field #number AAARange +-- @field #number HeloSwitchRange +-- @field #number PlaneSwitchRange +-- @field Core.Set#SET_GROUP FlightSet +-- @field #boolean SwitchAAA +-- @extends Core.Fsm#FSM + +--- +-- @type TIRESIAS.Data +-- @field #string type +-- @field #number range +-- @field #boolean invisible +-- @field #boolean AIOff +-- @field #boolean exception + + +--- *Tiresias, Greek demi-god and shapeshifter, blinded by the Gods, works as oracle for you.* (Wiki) +-- +-- === +-- +-- ## TIRESIAS Concept +-- +-- * Designed to keep CPU and Network usage lower on missions with a lot of ground units. +-- * Does not affect ships to keep the Navy guys happy. +-- * Does not affect OpsGroup type groups. +-- * Distinguishes between SAM groups, AAA groups and other ground groups. +-- * Exceptions can be defined in SET_GROUP objects to keep certain actions going. +-- * Works coalition-independent in the back +-- * Easy setup. +-- +-- ## Setup +-- +-- Setup is a one-liner: +-- +-- local blinder = TIRESIAS:New() +-- +-- Optionally you can set up exceptions, e.g. for convoys driving around +-- +-- local exceptionset = SET_GROUP:New():FilterCoalitions("red"):FilterPrefixes("Convoy"):FilterStart() +-- local blinder = TIRESIAS:New() +-- blinder:AddExceptionSet(exceptionset) +-- +-- Options +-- +-- -- Setup different radius for activation around helo and airplane groups (applies to AI and humans) +-- blinder:SetActivationRanges(10,25) -- defaults are 10, and 25 +-- +-- -- Setup engagement ranges for AAA (non-advanced SAM units like Flaks etc) and if you want them to be AIOff +-- blinder:SetAAARanges(60,true) -- defaults are 60, and true +-- +-- @field #TIRESIAS +TIRESIAS = { + ClassName = "TIRESIAS", + debug = false, + version = "0.0.4", + Interval = 20, + GroundSet = nil, + VehicleSet = nil, + AAASet = nil, + SAMSet = nil, + ExceptionSet = nil, + AAARange = 60, -- 60% + HeloSwitchRange = 10, -- NM + PlaneSwitchRange = 25, -- NM + SwitchAAA = true, +} + +--- [USER] Create a new Tiresias object and start it up. +-- @param #TIRESIAS self +-- @return #TIRESIAS self +function TIRESIAS:New() + + -- Inherit everything from FSM class. + local self = BASE:Inherit(self, FSM:New()) -- #TIRESIAS + + --- FSM Functions --- + + -- Start State. + self:SetStartState("Stopped") + + -- Add FSM transitions. + -- From State --> Event --> To State + self:AddTransition("Stopped", "Start", "Running") -- Start FSM. + self:AddTransition("*", "Status", "*") -- TIRESIAS status update. + self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. + + self.ExceptionSet = SET_GROUP:New():Clear(false) + + self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler) + + self.lid = string.format("TIRESIAS %s | ",self.version) + + self:I(self.lid.."Managing ground groups!") + + --- Triggers the FSM event "Stop". Stops TIRESIAS and all its event handlers. + -- @function [parent=#TIRESIAS] Stop + -- @param #TIRESIAS self + + --- Triggers the FSM event "Stop" after a delay. Stops TIRESIAS and all its event handlers. + -- @function [parent=#TIRESIAS] __Stop + -- @param #TIRESIAS self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Start". Starts TIRESIAS and all its event handlers. Note - `:New()` already starts the instance. + -- @function [parent=#TIRESIAS] Start + -- @param #TIRESIAS self + + --- Triggers the FSM event "Start" after a delay. Starts TIRESIAS and all its event handlers. Note - `:New()` already starts the instance. + -- @function [parent=#TIRESIAS] __Start + -- @param #TIRESIAS self + -- @param #number delay Delay in seconds. + + self:__Start(1) + return self +end + +------------------------------------------------------------------------------------------------------------- +-- +-- Helper Functions +-- +------------------------------------------------------------------------------------------------------------- + +---[USER] Set activation radius for Helos and Planes in Nautical Miles. +-- @param #TIRESIAS self +-- @param #number HeloMiles Radius around a Helicopter in which AI ground units will be activated. Defaults to 10NM. +-- @param #number PlaneMiles Radius around an Airplane in which AI ground units will be activated. Defaults to 25NM. +-- @return #TIRESIAS self +function TIRESIAS:SetActivationRanges(HeloMiles,PlaneMiles) + self.HeloSwitchRange = HeloMiles or 10 + self.PlaneSwitchRange = PlaneMiles or 25 + return self +end + +---[USER] Set AAA Ranges - AAA equals non-SAM systems which qualify as AAA in DCS world. +-- @param #TIRESIAS self +-- @param #number FiringRange The engagement range that AAA units will be set to. Can be 0 to 100 (percent). Defaults to 60. +-- @param #boolean SwitchAAA Decide if these system will have their AI switched off, too. Defaults to true. +-- @return #TIRESIAS self +function TIRESIAS:SetAAARanges(FiringRange,SwitchAAA) + self.AAARange = FiringRange or 60 + self.SwitchAAA = (SwitchAAA == false) and false or true + return self +end + +--- [USER] Add a SET_GROUP of GROUP objects as exceptions. Can be done multiple times. +-- @param #TIRESIAS self +-- @param Core.Set#SET_GROUP Set to add to the exception list. +-- @return #TIRESIAS self +function TIRESIAS:AddExceptionSet(Set) + self:T(self.lid.."AddExceptionSet") + local exceptions = self.ExceptionSet + Set:ForEachGroupAlive( + function(grp) + if not grp.Tiresias then + grp.Tiresias = { -- #TIRESIAS.Data + type = "Exception", + exception = true, + } + exceptions:AddGroup(grp,true) + end + BASE:I("TIRESIAS: Added exception group: "..grp:GetName()) + end + ) + return self +end + +--- [INTERNAL] Filter Function +-- @param Wrapper.Group#GROUP Group +-- @return #boolean isin +function TIRESIAS._FilterNotAAA(Group) + local grp = Group -- Wrapper.Group#GROUP + local isaaa = grp:IsAAA() + if isaaa == true and grp:IsGround() and not grp:IsShip() then + return false -- remove from SET + else + return true -- keep in SET + end +end + +--- [INTERNAL] Filter Function +-- @param Wrapper.Group#GROUP Group +-- @return #boolean isin +function TIRESIAS._FilterNotSAM(Group) + local grp = Group -- Wrapper.Group#GROUP + local issam = grp:IsSAM() + if issam == true and grp:IsGround() and not grp:IsShip() then + return false -- remove from SET + else + return true -- keep in SET + end +end + +--- [INTERNAL] Filter Function +-- @param Wrapper.Group#GROUP Group +-- @return #boolean isin +function TIRESIAS._FilterAAA(Group) + local grp = Group -- Wrapper.Group#GROUP + local isaaa = grp:IsAAA() + if isaaa == true and grp:IsGround() and not grp:IsShip() then + return true -- remove from SET + else + return false -- keep in SET + end +end + +--- [INTERNAL] Filter Function +-- @param Wrapper.Group#GROUP Group +-- @return #boolean isin +function TIRESIAS._FilterSAM(Group) + local grp = Group -- Wrapper.Group#GROUP + local issam = grp:IsSAM() + if issam == true and grp:IsGround() and not grp:IsShip() then + return true -- remove from SET + else + return false -- keep in SET + end +end + +--- [INTERNAL] Init Groups +-- @param #TIRESIAS self +-- @return #TIRESIAS self +function TIRESIAS:_InitGroups() + self:T(self.lid.."_InitGroups") + -- Set all groups invisible/motionless + local EngageRange = self.AAARange + local SwitchAAA = self.SwitchAAA + --- AAA + self.AAASet:ForEachGroupAlive( + function(grp) + if not grp.Tiresias then + grp:OptionEngageRange(EngageRange) + grp:SetCommandInvisible(true) + if SwitchAAA then + grp:SetAIOff() + grp:EnableEmission(false) + end + grp.Tiresias = { -- #TIRESIAS.Data + type = "AAA", + invisible = true, + range = EngageRange, + exception = false, + AIOff = SwitchAAA, + } + end + if grp.Tiresias and (not grp.Tiresias.exception == true) then + if grp.Tiresias.invisible and grp.Tiresias.invisible == false then + grp:SetCommandInvisible(true) + grp.Tiresias.invisible = true + if SwitchAAA then + grp:SetAIOff() + grp:EnableEmission(false) + grp.Tiresias.AIOff = true + end + end + end + --BASE:I(string.format("Init/Switch off AAA %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception))) + end + ) + --- Vehicles + self.VehicleSet:ForEachGroupAlive( + function(grp) + if not grp.Tiresias then + grp:SetAIOff() + grp:SetCommandInvisible(true) + grp.Tiresias = { -- #TIRESIAS.Data + type = "Vehicle", + invisible = true, + AIOff = true, + exception = false, + } + end + if grp.Tiresias and (not grp.Tiresias.exception == true) then + if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then + grp:SetCommandInvisible(true) + grp:SetAIOff() + grp.Tiresias.invisible = true + end + end + --BASE:I(string.format("Init/Switch off Vehicle %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception))) + end + ) + --- SAM + self.SAMSet:ForEachGroupAlive( + function(grp) + if not grp.Tiresias then + grp:SetCommandInvisible(true) + grp.Tiresias = { -- #TIRESIAS.Data + type = "SAM", + invisible = true, + exception = false, + } + end + if grp.Tiresias and (not grp.Tiresias.exception == true) then + if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then + grp:SetCommandInvisible(true) + grp.Tiresias.invisible = true + end + end + --BASE:I(string.format("Init/Switch off SAM %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception))) + end + ) + return self +end + +--- [INTERNAL] Event handler function +-- @param #TIRESIAS self +-- @param Core.Event#EVENTDATA EventData +-- @return #TIRESIAS self +function TIRESIAS:_EventHandler(EventData) + self:T(string.format("%s Event = %d",self.lid, EventData.id)) + local event = EventData -- Core.Event#EVENTDATA + if event.id == EVENTS.PlayerEnterAircraft or event.id == EVENTS.PlayerEnterUnit then + --local _coalition = event.IniCoalition + --if _coalition ~= self.Coalition then + -- return --ignore! + --end + local unitname = event.IniUnitName or "none" + local _unit = event.IniUnit + local _group = event.IniGroup + if _group and _group:IsAlive() then + local radius = self.PlaneSwitchRange + if _group:IsHelicopter() then + radius = self.HeloSwitchRange + end + self:_SwitchOnGroups(_group,radius) + end + end + return self +end + +--- [INTERNAL] Switch Groups Behaviour +-- @param #TIRESIAS self +-- @param Wrapper.Group#GROUP group +-- @param #number radius Radius in NM +-- @return #TIRESIAS self +function TIRESIAS:_SwitchOnGroups(group,radius) + self:T(self.lid.."_SwitchOnGroups "..group:GetName().." Radius "..radius.." NM") + local zone = ZONE_GROUP:New("Zone-"..group:GetName(),group,UTILS.NMToMeters(radius)) + local ground = SET_GROUP:New():FilterCategoryGround():FilterZones({zone}):FilterOnce() + local count = ground:CountAlive() + if self.debug then + local text = string.format("There are %d groups around this plane or helo!",count) + self:I(text) + end + local SwitchAAA = self.SwitchAAA + if ground:CountAlive() > 0 then + ground:ForEachGroupAlive( + function(grp) + if grp.Tiresias and grp.Tiresias.type and (not grp.Tiresias.exception == true ) then + if grp.Tiresias.invisible == true then + grp:SetCommandInvisible(false) + grp.Tiresias.invisible = false + end + if grp.Tiresias.type == "Vehicle" and grp.Tiresias.AIOff and grp.Tiresias.AIOff == true then + grp:SetAIOn() + grp.Tiresias.AIOff = false + end + if SwitchAAA and grp.Tiresias.type == "AAA" and grp.Tiresias.AIOff and grp.Tiresias.AIOff == true then + grp:SetAIOn() + grp:EnableEmission(true) + grp.Tiresias.AIOff = false + end + --BASE:I(string.format("TIRESIAS - Switch on %s %s (Exception %s)",tostring(grp.Tiresias.type),grp:GetName(),tostring(grp.Tiresias.exception))) + else + BASE:E("TIRESIAS - This group has not been initialized or is an exception!") + end + end + ) + end + return self +end + +------------------------------------------------------------------------------------------------------------- +-- +-- FSM Functions +-- +------------------------------------------------------------------------------------------------------------- + +--- [INTERNAL] FSM Function +-- @param #TIRESIAS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TIRESIAS self +function TIRESIAS:onafterStart(From, Event, To) + self:T({From, Event, To}) + + local VehicleSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterNotAAA):FilterFunction(TIRESIAS._FilterNotSAM):FilterStart() + local AAASet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterAAA):FilterStart() + local SAMSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterSAM):FilterStart() + local OpsGroupSet = SET_OPSGROUP:New():FilterActive(true):FilterStart() + self.FlightSet = SET_GROUP:New():FilterCategories({"plane","helicopter"}):FilterStart() + + local EngageRange = self.AAARange + + local ExceptionSet = self.ExceptionSet + if self.ExceptionSet then + function ExceptionSet:OnAfterAdded(From,Event,To,ObjectName,Object) + BASE:I("TIRESIAS: EXCEPTION Object Added: "..Object:GetName()) + if Object and Object:IsAlive() then + Object.Tiresias = { -- #TIRESIAS.Data + type = "Exception", + exception = true, + } + Object:SetAIOn() + Object:SetCommandInvisible(false) + Object:EnableEmission(true) + end + end + + local OGS = OpsGroupSet:GetAliveSet() + for _,_OG in pairs(OGS or {}) do + local OG = _OG -- Ops.OpsGroup#OPSGROUP + local grp = OG:GetGroup() + ExceptionSet:AddGroup(grp,true) + end + + function OpsGroupSet:OnAfterAdded(From,Event,To,ObjectName,Object) + local grp = Object:GetGroup() + ExceptionSet:AddGroup(grp,true) + end + end + + function VehicleSet:OnAfterAdded(From,Event,To,ObjectName,Object) + BASE:I("TIRESIAS: VEHCILE Object Added: "..Object:GetName()) + if Object and Object:IsAlive() then + Object:SetAIOff() + Object:SetCommandInvisible(true) + Object.Tiresias = { -- #TIRESIAS.Data + type = "Vehicle", + invisible = true, + AIOff = true, + exception = false, + } + end + end + + local SwitchAAA = self.SwitchAAA + + function AAASet:OnAfterAdded(From,Event,To,ObjectName,Object) + if Object and Object:IsAlive() then + BASE:I("TIRESIAS: AAA Object Added: "..Object:GetName()) + Object:OptionEngageRange(EngageRange) + Object:SetCommandInvisible(true) + if SwitchAAA then + Object:SetAIOff() + Object:EnableEmission(false) + end + Object.Tiresias = { -- #TIRESIAS.Data + type = "AAA", + invisible = true, + range = EngageRange, + exception = false, + AIOff = SwitchAAA, + } + end + end + + function SAMSet:OnAfterAdded(From,Event,To,ObjectName,Object) + if Object and Object:IsAlive() then + BASE:I("TIRESIAS: SAM Object Added: "..Object:GetName()) + Object:SetCommandInvisible(true) + Object.Tiresias = { -- #TIRESIAS.Data + type = "SAM", + invisible = true, + exception = false, + } + end + end + + self.VehicleSet = VehicleSet + self.AAASet = AAASet + self.SAMSet = SAMSet + self.OpsGroupSet = OpsGroupSet + + self:_InitGroups() + + self:__Status(1) + return self +end + +--- [INTERNAL] FSM Function +-- @param #TIRESIAS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TIRESIAS self +function TIRESIAS:onbeforeStatus(From, Event, To) + self:T({From, Event, To}) + if self:GetState() == "Stopped" then + return false + end + return self +end + +--- [INTERNAL] FSM Function +-- @param #TIRESIAS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TIRESIAS self +function TIRESIAS:onafterStatus(From, Event, To) + self:T({From, Event, To}) + if self.debug then + local count = self.VehicleSet:CountAlive() + local AAAcount = self.AAASet:CountAlive() + local SAMcount = self.SAMSet:CountAlive() + local text = string.format("Overall: %d | Vehicles: %d | AAA: %d | SAM: %d",count+AAAcount+SAMcount,count,AAAcount,SAMcount) + self:I(text) + end + self:_InitGroups() + if self.FlightSet:CountAlive() > 0 then + local Set = self.FlightSet:GetAliveSet() + for _,_plane in pairs(Set) do + local plane = _plane -- Wrapper.Group#GROUP + local radius = self.PlaneSwitchRange + if plane:IsHelicopter() then + radius = self.HeloSwitchRange + end + self:_SwitchOnGroups(_plane,radius) + end + end + if self:GetState() ~= "Stopped" then + self:__Status(self.Interval) + end + return self +end + +--- [INTERNAL] FSM Function +-- @param #TIRESIAS self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #TIRESIAS self +function TIRESIAS:onafterStop(From, Event, To) + self:T({From, Event, To}) + self:UnHandleEvent(EVENTS.PlayerEnterAircraft) + return self +end + +------------------------------------------------------------------------------------------------------------- +-- +-- End +-- +------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 99ca565a1..7ab4ad2e8 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -83,6 +83,7 @@ __Moose.Include( 'Scripts/Moose/Functional/ZoneCaptureCoalition.lua' ) __Moose.Include( 'Scripts/Moose/Functional/ZoneGoal.lua' ) __Moose.Include( 'Scripts/Moose/Functional/ZoneGoalCargo.lua' ) __Moose.Include( 'Scripts/Moose/Functional/ZoneGoalCoalition.lua' ) +__Moose.Include( 'Scripts/Moose/Functional/Tiresias.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' ) __Moose.Include( 'Scripts/Moose/Ops/AirWing.lua' ) diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 90a6e761d..a863db719 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -83,6 +83,7 @@ Functional/Autolase.lua Functional/AICSAR.lua Functional/AmmoTruck.lua Functional/ZoneGoalCargo.lua +Functional/Tiresias.lua Ops/Airboss.lua Ops/RecoveryTanker.lua From 5fe77956cb46e33b504d8a5f6742c95fd8359b82 Mon Sep 17 00:00:00 2001 From: kaltokri Date: Mon, 1 Jan 2024 15:51:02 +0100 Subject: [PATCH 94/95] Fixed broken links of restructure --- .../Moose/AI/AI_A2A_Dispatcher.lua | 17 +++++++------ .../Moose/AI/AI_A2G_Dispatcher.lua | 2 +- .../Moose/AI/AI_Air_Dispatcher.lua | 2 +- Moose Development/Moose/AI/AI_Balancer.lua | 24 ++++++++++--------- .../Moose/AI/AI_Cargo_Dispatcher.lua | 10 ++++---- .../Moose/Functional/ATC_Ground.lua | 7 +++--- .../Moose/Functional/Detection.lua | 2 +- Moose Development/Moose/Sound/SRS.lua | 6 ++--- Moose Development/Moose/Wrapper/Group.lua | 4 ++-- Moose Development/Moose/Wrapper/Unit.lua | 4 ++-- 10 files changed, 42 insertions(+), 36 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 59941b899..5e87dad03 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -23,7 +23,7 @@ -- -- ## Missions: -- --- [AID-A2A - AI A2A Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching) +-- [AID-A2A - AI A2A Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher) -- -- === -- @@ -310,7 +310,7 @@ do -- AI_A2A_DISPATCHER -- 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/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching/AID-A2A-019%20-%20Engage%20Range%20Test) + -- Demonstration Mission: [AID-019 - AI_A2A - Engage Range Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher/AID-A2A-019%20-%20Engage%20Range%20Test) -- -- In this example an Engage Radius is set to various values. -- @@ -333,7 +333,7 @@ do -- AI_A2A_DISPATCHER -- 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/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching/AID-A2A-013%20-%20Intercept%20Test) + -- Demonstration Mission: [AID-013 - AI_A2A - Intercept Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher/AID-A2A-013%20-%20Intercept%20Test) -- -- In these examples, the Gci Radius is set to various values: -- @@ -366,7 +366,7 @@ do -- AI_A2A_DISPATCHER -- 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/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching/AID-A2A-009%20-%20Border%20Test) + -- Demonstration Mission: [AID-009 - AI_A2A - Border Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher/AID-A2A-009%20-%20Border%20Test) -- -- In this example a border is set for the CCCP A2A dispatcher: -- @@ -3257,7 +3257,8 @@ do -- AI_A2A_DISPATCHER end end - --- @param #AI_A2A_DISPATCHER self + --- AI_A2A_Fsm:onafterHome + -- @param #AI_A2A_DISPATCHER self function AI_A2A_Fsm:onafterHome( Defender, From, Event, To, Action ) if Defender and Defender:IsAlive() then self:F( { "CAP Home", Defender:GetName() } ) @@ -3505,7 +3506,8 @@ do -- AI_A2A_DISPATCHER Dispatcher:ClearDefenderTaskTarget( DefenderGroup ) end - --- @param #AI_A2A_DISPATCHER self + --- function Fsm:onafterLostControl + -- @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 ) @@ -3518,7 +3520,8 @@ do -- AI_A2A_DISPATCHER end end - --- @param #AI_A2A_DISPATCHER self + --- function Fsm:onafterHome + -- @param #AI_A2A_DISPATCHER self function Fsm:onafterHome( DefenderGroup, From, Event, To, Action ) self:F( { "GCI Home", DefenderGroup:GetName() } ) self:GetParent( self ).onafterHome( self, DefenderGroup, From, Event, To ) diff --git a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua index 1fe9b4c5a..1dad17759 100644 --- a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua @@ -24,7 +24,7 @@ -- -- ## Missions: -- --- [AID-A2G - AI A2G Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2G%20-%20AI%20A2G%20Dispatching) +-- [AID-A2G - AI A2G Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2G_Dispatcher) -- -- === -- diff --git a/Moose Development/Moose/AI/AI_Air_Dispatcher.lua b/Moose Development/Moose/AI/AI_Air_Dispatcher.lua index b422ac03e..8d0bbd9cf 100644 --- a/Moose Development/Moose/AI/AI_Air_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Air_Dispatcher.lua @@ -24,7 +24,7 @@ -- -- ## Missions: -- --- [AID-AIR - AI AIR Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching) +-- [AI_A2A_Dispatcher](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher) -- -- === -- diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index fad0746ff..b64e27057 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -9,7 +9,7 @@ -- -- === -- --- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AIB%20-%20AI%20Balancing) +-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_Balancer) -- -- === -- @@ -168,7 +168,8 @@ function AI_BALANCER:ReturnToHomeAirbase( ReturnThresholdRange ) self.ReturnThresholdRange = ReturnThresholdRange end ---- @param #AI_BALANCER self +--- AI_BALANCER:onenterSpawning +-- @param #AI_BALANCER self -- @param Core.Set#SET_GROUP SetGroup -- @param #string ClientName -- @param Wrapper.Group#GROUP AIGroup @@ -190,7 +191,8 @@ function AI_BALANCER:onenterSpawning( SetGroup, From, Event, To, ClientName ) end end ---- @param #AI_BALANCER self +--- AI_BALANCER:onenterDestroying +-- @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 ) @@ -233,15 +235,16 @@ function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup ) end - ---- @param #AI_BALANCER self +--- AI_BALANCER:onenterMonitoring +-- @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 + --- SetClient:ForEachClient + -- @param Wrapper.Client#CLIENT Client function( Client ) self:T3(Client.ClientName) @@ -264,7 +267,8 @@ function AI_BALANCER:onenterMonitoring( SetGroup ) self:T2( RangeZone ) _DATABASE:ForEachPlayerUnit( - --- @param Wrapper.Unit#UNIT RangeTestUnit + --- Nameless function + -- @param Wrapper.Unit#UNIT RangeTestUnit function( RangeTestUnit, RangeZone, AIGroup, PlayerInRange ) self:T2( { PlayerInRange, RangeTestUnit.UnitName, RangeZone.ZoneName } ) if RangeTestUnit:IsInZone( RangeZone ) == true then @@ -276,7 +280,8 @@ function AI_BALANCER:onenterMonitoring( SetGroup ) end end, - --- @param Core.Zone#ZONE_RADIUS RangeZone + --- Nameless function + -- @param Core.Zone#ZONE_RADIUS RangeZone -- @param Wrapper.Group#GROUP AIGroup function( RangeZone, AIGroup, PlayerInRange ) if PlayerInRange.Value == false then @@ -307,6 +312,3 @@ function AI_BALANCER:onenterMonitoring( SetGroup ) self:__Monitor( 10 ) end - - - diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index ea954b97d..0fedc9643 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -18,7 +18,7 @@ -- -- Test missions can be located on the main GITHUB site. -- --- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching) +-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_Cargo_Dispatcher) -- -- === -- @@ -572,7 +572,7 @@ -- A home zone can be specified to where the Carriers will move when there isn't any cargo left for pickup. -- Use @{#AI_CARGO_DISPATCHER.SetHomeZone}() to specify the home zone. -- --- If no home zone is specified, the carriers will wait near the deploy zone for a new pickup command. +-- If no home zone is specified, the carriers will wait near the deploy zone for a new pickup command. -- -- === -- @@ -583,10 +583,12 @@ AI_CARGO_DISPATCHER = { PickupCargo = {} } ---- @field #list +--- List of AI_Cargo +-- @field #list AI_CARGO_DISPATCHER.AI_Cargo = {} ---- @field #list +--- List of PickupCargo +-- @field #list AI_CARGO_DISPATCHER.PickupCargo = {} diff --git a/Moose Development/Moose/Functional/ATC_Ground.lua b/Moose Development/Moose/Functional/ATC_Ground.lua index fc65fcce6..cdb90e712 100644 --- a/Moose Development/Moose/Functional/ATC_Ground.lua +++ b/Moose Development/Moose/Functional/ATC_Ground.lua @@ -10,9 +10,7 @@ -- -- === -- --- ## Missions: --- --- [ABP - Airbase Police](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/ABP%20-%20Airbase%20Police) +-- ## Missions: None -- -- === -- @@ -699,7 +697,8 @@ end function ATC_GROUND_UNIVERSAL:_AirbaseMonitor() self:I("_AirbaseMonitor") self.SetClient:ForEachClient( - --- @param Wrapper.Client#CLIENT Client + --- Nameless function + -- @param Wrapper.Client#CLIENT Client function( Client ) if Client:IsAlive() then diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index a71e194ed..edafc2eb6 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -95,7 +95,7 @@ do -- DETECTION_BASE -- -- ## Radar Blur - use to make the radar less exact, e.g. for WWII scenarios -- - -- * @{DETECTION_BASE.SetRadarBlur}(): Set the radar blur to be used. + -- * @{#DETECTION_BASE.SetRadarBlur}(): Set the radar blur to be used. -- -- ## **DETECTION_ derived classes** group the detected units into a **DetectedItems[]** list -- diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index f8561928d..b94aed461 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -146,7 +146,7 @@ -- -- ### Google -- --- In order to use Google Cloud for TTS you need to use @{#MSRS.SetProvider} and @{MSRS.SetProviderOptionsGoogle} functions: +-- In order to use Google Cloud for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsGoogle} functions: -- -- msrs:SetProvider(MSRS.Provider.GOOGLE) -- msrs:SetProviderOptionsGoogle(CredentialsFile, AccessKey) @@ -162,7 +162,7 @@ -- -- ### Amazon Web Service [Only DCS-gRPC backend] -- --- In order to use Amazon Web Service (AWS) for TTS you need to use @{#MSRS.SetProvider} and @{MSRS.SetProviderOptionsAmazon} functions: +-- In order to use Amazon Web Service (AWS) for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsAmazon} functions: -- -- msrs:SetProvider(MSRS.Provider.AMAZON) -- msrs:SetProviderOptionsAmazon(AccessKey, SecretKey, Region) @@ -173,7 +173,7 @@ -- -- ### Microsoft Azure [Only DCS-gRPC backend] -- --- In order to use Microsoft Azure for TTS you need to use @{#MSRS.SetProvider} and @{MSRS.SetProviderOptionsAzure} functions: +-- In order to use Microsoft Azure for TTS you need to use @{#MSRS.SetProvider} and @{#MSRS.SetProviderOptionsAzure} functions: -- -- msrs:SetProvider(MSRS.Provider.AZURE) -- msrs:SetProviderOptionsAmazon(AccessKey, Region) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index b9eb10db0..d232bf604 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -302,7 +302,7 @@ end --- Find the first(!) GROUP matching using patterns. Note that this is **a lot** slower than `:FindByName()`! -- @param #GROUP self --- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA. +-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA. -- @return #GROUP The GROUP. -- @usage -- -- Find a group with a partial group name @@ -327,7 +327,7 @@ end --- Find all GROUP objects matching using patterns. Note that this is **a lot** slower than `:FindByName()`! -- @param #GROUP self --- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA. +-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA. -- @return #table Groups Table of matching #GROUP objects found -- @usage -- -- Find all group with a partial group name diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 0b3e3300f..322d23540 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -164,7 +164,7 @@ end --- Find the first(!) UNIT matching using patterns. Note that this is **a lot** slower than `:FindByName()`! -- @param #UNIT self --- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA. +-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA. -- @return #UNIT The UNIT. -- @usage -- -- Find a group with a partial group name @@ -189,7 +189,7 @@ end --- Find all UNIT objects matching using patterns. Note that this is **a lot** slower than `:FindByName()`! -- @param #UNIT self --- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA. +-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA. -- @return #table Units Table of matching #UNIT objects found -- @usage -- -- Find all group with a partial group name From 8385b1d21a65adbdc159838793afb2c31c428a55 Mon Sep 17 00:00:00 2001 From: kaltokri Date: Mon, 1 Jan 2024 19:06:21 +0100 Subject: [PATCH 95/95] Fixed broken links in A2A_Dispatcher --- Moose Development/Moose/AI/AI_A2A_Dispatcher.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 5e87dad03..a548e83d9 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -1233,7 +1233,7 @@ do -- AI_A2A_DISPATCHER -- -- **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/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching/AID-A2A-019%20-%20Engage%20Range%20Test) + -- Demonstration Mission: [AID-019 - AI_A2A - Engage Range Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher/AID-A2A-019%20-%20Engage%20Range%20Test) -- -- @param #AI_A2A_DISPATCHER self -- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target. @@ -1283,7 +1283,7 @@ do -- AI_A2A_DISPATCHER -- 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/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching/AID-A2A-013%20-%20Intercept%20Test) + -- Demonstration Mission: [AID-013 - AI_A2A - Intercept Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher/AID-A2A-013%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. @@ -3962,7 +3962,7 @@ do -- -- # Demo Missions -- - -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching) + -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher) -- -- === --