From 52c2401d93518272f1780754aa808d85fdc4043c Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 22 Nov 2023 22:53:54 +0100 Subject: [PATCH 01/63] 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/63] 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/63] 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/63] 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 97161627397dacf24bd16bb7cc2334080719c8c8 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 17 Dec 2023 22:55:31 +0100 Subject: [PATCH 05/63] 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 06/63] 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 zcmai%X*e6$9>%F^rd4#A)^ZWOTDmCOTSYWUwNyJT)xkuOL@i_5RM3btB8+VeT0uvx zv0PfDnpj3P_Kqc%qLv`F)t0D)GzlVt+_ZDQ+)wwzIp;mw`<(NC&U4Q1Bse+RtM1d@ zr=XyqdgHq7j|vLA%;ehOyFKze(bx?rPf~r&Yq}u&>I_x+LZVWh> z9&=JhGur9;wF4oAf0h=c@KR3N;Z$Sx?J6pDIG51eT_toLoHPAi*j;2cx%26?%Lifv5D+*ZEk@VerFpY=NGiPD;P1Id=UTgsmq~ zT-(5?A;XV#cQ2)F>U4SU6p!+A)iwT=sM=2&O&Q7ZA-WVj z@J7?u)`=o`;6IOUA3n6pe)6D~6R}bcpz#C$&IfS$hRvY;&J%4-q&V8j`1F;=F>X@6 z6#W!M%-Zx^YenvJH5`sF_;{S2His$@VN8KMZ8CWm^h>-b`>K5}|6a;6Q?El0_IWs> z)}~Td(!57fZt1{Wpk77>c)0)#fC-~6``yHs@_fJ81dBn7>r|U_wl9U{k55JPWrun5 zPc{vIJ`9z%S0wu;ts;KU9>Uaga1O$av$*SeN5;S<&X5O_q8A^(h!W%aDyLD`{i7ASklVUp5Ju0+Vdg>j3UO(k}5S|(j4r85kL=WB8BZQ+kow67D z{FC!KM`x2s!oUl)dq^`k7({S)n258RwbQ@^`H~{r*rmeLMH`rvn^~I4bPeVRh4@sPk@ql zq(13)XkZ9NH;;tY{a_FYMvzVQa4a?l44FXby?kJYY01R*4R2Wu!Z~pMutd#w$@nc4 zoe?BiGY(nT)flTTMpp#87!+wLN$cH#3rs<_Rd+V`(L6U-_qD^hBY@1bGg4yzp+;U= z@T%4P(<#L^64BfOHFHUl+5KWj9tD5Z{fhbrcDFHpaMOh~NnQu$XTbat9QiuV?|hPX zmmRq996?3XdIk|kuB5xF#8>W9&O(&_d(+y_y0nx?UGjs65s`N1_=u6trCAdeGIDW- z2zs}iLT+gY2}Uj}ubMHi#p7DcB(4BI&`$d+?CD?LQjv<3n{oMF474By&{?P zh#jK#ODKkWiq%<(r=&-6tYnI;+}HR+EBPHCJ%~yqMHr2k;TphBH1=KcRAvn-ks|5x^_`YQH+KG`t334F z2*UE0UPZC{&=}^zLeKY|E-PnlE_HCn1D-!gHi32eu1s)%V;q!S6K*=zd|0#Z>rwGn zeSgyFhtOJxT?S7c8s1t)?9dTK=>=FoP$bA>tS8Ilo(T*?j$o`|xq~o{CvCjUZ9`=m zxbJcv`vk!^KHU9di++5h=x0ZPfC%boe8|qj*4B315>P7_g`d1Kdg2BUjJd+cF}&<< z#f#D7Kh5>jaeVit37W)_UWB8*`LnZPSI!|z_7c_K2D0y@f?1 zq@m9^N<=SxiViyTk>hU@;g!UotZ=$B@g0~ZT>#V2@|We|x4)ml=UVq=&oxN7k7{tM z;4Gy^VLLG`SsPfJMW{vHS4%;nt_!kIUa{lgT3oF3y!~j-@8pYSdR|}jeZt{57tK6R z)ANg~CW_<7hXPt&Qd+l5xhuE*lhWGm`s&|A!NZ@o*>2XYxqm;%Jr^4j@`1eK3C?etYkX$q z1q`EeC`i5gJQu+Gt4!;-@!|%v{>Jb>o-PU`HTr;j%jpHi+|+BIoDd8aIjKGk8@&*p z><)D-nJ(Lm3ho6K^oU)rtr`Ex zm3W^YEuF8!F8nIZ#;li7DP(d$mGU(R)}_&VzImJ{MqOqvZrA-8buf3xqR=DXd!Fz$ zEiDC8S5`XST`Y720^$B_OIR#@&L)^wi)aZmqj*gx7csZ=7x4wgc>h%i^I^OWN(- z{SRar&6}SjuQ&=h*Q~UT(Z}XQCO0dKqkQ=;w>Xy#^`Cjtn<1kMBupY#0mucQBl#@Z@=+@VPOY^9ZrV#CK6b1g|O_S3DzoRfJk+GIE3Sb^2zZ%^xfJ(ksWK zE_Dg3ov)+8F`f%fJ`bLPz0b0RZ6M(h;5_f&?6;h9 zUS?7WcX@EY@Tqi~r=T#xYi#^T zWX;|4#jr=Zwe@*O;jh?pR0)QT30|$%(@4CR6J?jzCt;WU|w5%^_bPs8>m>Q+QLbs8RDWn$22T32KeMDrG zqXlER=CfZopClF41z9giC|84Z+C4cBmLKQFdLPSb8j)O#<54A6Cs%s@0B@N4pFtJ* zYc(9DAW9*4<5%c@p!6~auI@#G_ES9wsjyKmhX~44V$aIbZ`kSPSa*i2$#C7Xr)%Z3 z*M%LpWI68H3sv6VpXt*;R^k~w)QKrf;~2x!UZ;loL>;mR@zW$jP*G0Ide6!h2+vi@ zwq>HQm8XHA=AAg}m5EEz8bB=!qDIQA51f`5H48ql%Lx4e{EPxpD&(!v(=w8!xfSzM zEY_2_Rs*0iPt3h24``A@q^FDOd!K%2HGWmu|BcwwBE$31Kr)mdNqRGSQ1*6pu)XU1 zRPV;f8AOwAOvDMxP9)qw?VqCO83_FGCcp5X$6dChuPyiM2egoB8a=<^}sAUnb18_mv 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 07/63] 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 08/63] #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 09/63] #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 10/63] #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 11/63] 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 12/63] 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 13/63] 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 14/63] 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 15/63] 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 16/63] 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 17/63] #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 18/63] #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 19/63] 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 20/63] 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 21/63] 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 22/63] 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 23/63] 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 24/63] 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 25/63] 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 26/63] 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 27/63] 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 28/63] 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 29/63] 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 30/63] 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 31/63] 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 32/63] 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 33/63] 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 34/63] 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 35/63] 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 36/63] 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 37/63] 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 38/63] 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 39/63] 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 40/63] 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 41/63] 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 42/63] 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 43/63] 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 44/63] 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 45/63] 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 46/63] #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 47/63] #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 48/63] 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 49/63] 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 50/63] 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 51/63] 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 52/63] 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 53/63] 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 54/63] #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 55/63] #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 56/63] 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 57/63] #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 58/63] 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 59/63] 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 60/63] 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 61/63] #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 62/63] 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 63/63] 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) -- -- === --