diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index bb5d65987..6270f1fa6 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -1582,12 +1582,29 @@ end -- @param DCS#Airbase airbase Airbase. -- @return #DATABASE self function DATABASE:_RegisterAirbase(airbase) - + + local IsSyria = UTILS.GetDCSMap() == "Syria" and true or false + local countHSyria = 0 + if airbase then -- Get the airbase name. local DCSAirbaseName = airbase:getName() - + + -- DCS 2.9.8.1107 added 143 helipads all named H with the same object ID .. + if IsSyria and DCSAirbaseName == "H" and countHSyria > 0 then + --[[ + local p = airbase:getPosition().p + local mgrs = COORDINATE:New(p.x,p.z,p.y):ToStringMGRS() + self:I("Airbase on Syria map named H @ "..mgrs) + countHSyria = countHSyria + 1 + if countHSyria > 1 then return self end + --]] + return self + elseif IsSyria and DCSAirbaseName == "H" and countHSyria == 0 then + countHSyria = countHSyria + 1 + end + -- This gave the incorrect value to be inserted into the airdromeID for DCS 2.5.6. Is fixed now. local airbaseID=airbase:getID() diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index 314c9422f..ac89dddb4 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -155,7 +155,7 @@ function SEAD:New( SEADGroupPrefixes, Padding ) self:AddTransition("*", "ManageEvasion", "*") self:AddTransition("*", "CalculateHitZone", "*") - self:I("*** SEAD - Started Version 0.4.7") + self:I("*** SEAD - Started Version 0.4.8") return self end @@ -463,21 +463,24 @@ end -- @return #SEAD self function SEAD:HandleEventShot( EventData ) self:T( { EventData.id } ) - local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT - local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP - local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE - local SEADUnit = EventData.IniDCSUnit - local SEADUnitName = EventData.IniDCSUnitName + local SEADWeapon = EventData.Weapon -- Identify the weapon fired - local SEADWeaponName = EventData.WeaponName -- return weapon type - - local WeaponWrapper = WEAPON:New(EventData.Weapon) -- Wrapper.Weapon#WEAPON - --local SEADWeaponSpeed = WeaponWrapper:GetSpeed() -- mps + local SEADWeaponName = EventData.WeaponName or "None" -- return weapon type - self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName) - --self:T({ SEADWeapon }) - if self:_CheckHarms(SEADWeaponName) then + local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT + + if not SEADPlane then return self end -- case IniUnit is empty + + local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP + local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE + local SEADUnit = EventData.IniDCSUnit + local SEADUnitName = EventData.IniDCSUnitName + + local WeaponWrapper = WEAPON:New(EventData.Weapon) -- Wrapper.Weapon#WEAPON + + self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName) + self:T( '*** SEAD - Weapon Match' ) if self.WeaponTrack == true then WeaponWrapper:SetFuncTrack(function(weapon) env.info(string.format("*** Weapon Speed: %d m/s",weapon:GetSpeed() or -1)) end) diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 3f621d594..ff6c95c9e 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -52,6 +52,7 @@ -- @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). +-- @field #boolean UsePowerShell Use PowerShell to execute the command and not cmd.exe -- @extends Core.Base#BASE --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde @@ -256,11 +257,12 @@ MSRS = { ConfigFilePath = "Config\\", ConfigLoaded = false, poptions = {}, + UsePowerShell = false, } --- MSRS class version. -- @field #string version -MSRS.version="0.3.0" +MSRS.version="0.3.3" --- Voices -- @type MSRS.Voices @@ -588,7 +590,7 @@ function MSRS:SetBackendSRSEXE() end --- Set the default backend. --- @param #MSRS self +-- @param #string Backend function MSRS.SetDefaultBackend(Backend) MSRS.backend=Backend or MSRS.Backend.SRSEXE end @@ -1375,20 +1377,25 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp modus=modus:gsub("1", "FM") -- Command. + local pwsh = string.format('Start-Process -WindowStyle Hidden -WorkingDirectory \"%s\" -FilePath \"%s\" -ArgumentList \'-f "%s" -m "%s" -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume ) + 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 + if voice and self.UsePowerShell ~= true then -- Use a specific voice (no need for gender and/or culture. command=command..string.format(" --voice=\"%s\"", tostring(voice)) + pwsh=pwsh..string.format(" --voice=\"%s\"", tostring(voice)) else -- Add gender. if gender and gender~="female" then command=command..string.format(" -g %s", tostring(gender)) + pwsh=pwsh..string.format(" -g %s", tostring(gender)) end -- Add culture. if culture and culture~="en-GB" then command=command..string.format(" -l %s", tostring(culture)) + pwsh=pwsh..string.format(" -l %s", tostring(culture)) end end @@ -1396,12 +1403,14 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp if coordinate then local lat,lon,alt=self:_GetLatLongAlt(coordinate) command=command..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt) + pwsh=pwsh..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt) end -- Set provider options if self.provider==MSRS.Provider.GOOGLE then local pops=self:GetProviderOptions() command=command..string.format(' --ssml -G "%s"', pops.credentials) + pwsh=pwsh..string.format(' --ssml -G "%s"', pops.credentials) elseif self.provider==MSRS.Provider.WINDOWS then -- Nothing to do. else @@ -1415,8 +1424,12 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp -- Debug output. self:T("MSRS command from _GetCommand="..command) - - return command + + if self.UsePowerShell == true then + return pwsh + else + return command + end end --- Execute SRS command to play sound using the `DCS-SR-ExternalAudio.exe`. @@ -1424,7 +1437,7 @@ end -- @param #string command Command to executer -- @return #number Return value of os.execute() command. function MSRS:_ExecCommand(command) - self:F( {command=command} ) + self:T2( {command=command} ) -- Skip this function if _GetCommand was not able to find the executable if string.find(command, "CommandNotFound") then return 0 end @@ -1432,7 +1445,13 @@ function MSRS:_ExecCommand(command) local batContent = command.." && exit" -- Create a tmp file. local filename=os.getenv('TMP').."\\MSRS-"..MSRS.uuid()..".bat" - + + if self.UsePowerShell == true then + filename=os.getenv('TMP').."\\MSRS-"..MSRS.uuid()..".ps1" + batContent = command .. "\'" + self:I({batContent=batContent}) + end + local script=io.open(filename, "w+") script:write(batContent) script:close() @@ -1441,7 +1460,7 @@ function MSRS:_ExecCommand(command) self:T("MSRS batch content: "..batContent) local res=nil - if true then + if self.UsePowerShell ~= true then -- Create a tmp file. local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs" @@ -1469,23 +1488,20 @@ function MSRS:_ExecCommand(command) 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) + elseif self.UsePowerShell == true then + local pwsh = string.format('powershell.exe -ExecutionPolicy Unrestricted -WindowStyle Hidden -Command "%s"',filename) + --env.info("[MSRS] TextToSpeech Command :\n" .. pwsh.."\n") + + if string.len(pwsh) > 255 then + self:E("[MSRS] - pwsh string too long") + end + -- Play file in 0.01 seconds - res=os.execute(runvbs) + res=os.execute(pwsh) + + -- Remove file in 1 second. + timer.scheduleFunction(os.remove, filename, timer.getTime()+1) else -- Play command. diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 4759ebf2f..21cf26938 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -63,6 +63,11 @@ -- To be able to distinguish easily in your code the difference between a AIRBASE API call and a DCS Airbase API call, -- the first letter of the method is also capitalized. So, by example, the DCS Airbase method DCSWrapper.Airbase#Airbase.getName() -- is implemented in the AIRBASE class as @{#AIRBASE.GetName}(). +-- +-- ## Note on the "H" heli pads in the Syria map: +-- +-- As of the time of writing (Oct 2024, DCS DCS 2.9.8.1107), these 143 objects have the **same name and object ID**, which makes them unusable in Moose, e.g. you cannot find a specific one for spawning etc. +-- Waiting for Ugra and ED to fix this issue. -- -- @field #AIRBASE AIRBASE AIRBASE = { @@ -450,6 +455,7 @@ AIRBASE.TheChannel = { -- * AIRBASE.Syria.Gaziantep -- * AIRBASE.Syria.Gazipasa -- * AIRBASE.Syria.Gecitkale +-- * AIRBASE.Syria.H -- * AIRBASE.Syria.H3 -- * AIRBASE.Syria.H3_Northwest -- * AIRBASE.Syria.H3_Southwest @@ -518,6 +524,7 @@ AIRBASE.Syria={ ["Gaziantep"] = "Gaziantep", ["Gazipasa"] = "Gazipasa", ["Gecitkale"] = "Gecitkale", + ["H"] = "H", ["H3"] = "H3", ["H3_Northwest"] = "H3 Northwest", ["H3_Southwest"] = "H3 Southwest", @@ -752,12 +759,14 @@ AIRBASE.Sinai = { -- -- * AIRBASE.Kola.Banak -- * AIRBASE.Kola.Bodo +-- * AIRBASE.Kola.Ivalo -- * AIRBASE.Kola.Jokkmokk -- * AIRBASE.Kola.Kalixfors -- * AIRBASE.Kola.Kallax -- * AIRBASE.Kola.Kemi_Tornio -- * AIRBASE.Kola.Kirkenes -- * AIRBASE.Kola.Kiruna +-- * AIRBASE.Kola.Kuusamo -- * AIRBASE.Kola.Monchegorsk -- * AIRBASE.Kola.Murmansk_International -- * AIRBASE.Kola.Olenya @@ -771,20 +780,22 @@ AIRBASE.Sinai = { AIRBASE.Kola = { ["Banak"] = "Banak", ["Bodo"] = "Bodo", + ["Ivalo"] = "Ivalo", ["Jokkmokk"] = "Jokkmokk", ["Kalixfors"] = "Kalixfors", + ["Kallax"] = "Kallax", ["Kemi_Tornio"] = "Kemi Tornio", + ["Kirkenes"] = "Kirkenes", ["Kiruna"] = "Kiruna", + ["Kuusamo"] = "Kuusamo", ["Monchegorsk"] = "Monchegorsk", ["Murmansk_International"] = "Murmansk International", ["Olenya"] = "Olenya", ["Rovaniemi"] = "Rovaniemi", ["Severomorsk_1"] = "Severomorsk-1", ["Severomorsk_3"] = "Severomorsk-3", - ["Vuojarvi"] = "Vuojarvi", - ["Kirkenes"] = "Kirkenes", - ["Kallax"] = "Kallax", ["Vidsel"] = "Vidsel", + ["Vuojarvi"] = "Vuojarvi", } --- Airbases of the Afghanistan map @@ -926,7 +937,7 @@ function AIRBASE:Register(AirbaseName) -- Debug info. --self:I({airbase=AirbaseName, descriptors=self.descriptors}) - + -- Category. self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME