Merge remote-tracking branch 'origin/master' into develop

# Conflicts:
#	Moose Development/Moose/Ops/Airboss.lua
This commit is contained in:
Applevangelist 2023-07-26 17:43:16 +02:00
commit d58e93e2a4

View File

@ -152,16 +152,12 @@
-- @field #boolean ICLSon Automatic ICLS is activated. -- @field #boolean ICLSon Automatic ICLS is activated.
-- @field #number ICLSchannel ICLS channel. -- @field #number ICLSchannel ICLS channel.
-- @field #string ICLSmorse ICLS morse code, e.g. "STN". -- @field #string ICLSmorse ICLS morse code, e.g. "STN".
-- @field #AIRBOSS.Radio PilotRadio Radio for Pilot calls.
-- @field #AIRBOSS.Radio LSORadio Radio for LSO calls. -- @field #AIRBOSS.Radio LSORadio Radio for LSO calls.
-- @field #number LSOFreq LSO radio frequency in MHz. -- @field #number LSOFreq LSO radio frequency in MHz.
-- @field #string LSOModu LSO radio modulation "AM" or "FM". -- @field #string LSOModu LSO radio modulation "AM" or "FM".
-- @field #AIRBOSS.Radio MarshalRadio Radio for carrier calls. -- @field #AIRBOSS.Radio MarshalRadio Radio for carrier calls.
-- @field #number MarshalFreq Marshal radio frequency in MHz. -- @field #number MarshalFreq Marshal radio frequency in MHz.
-- @field #string MarshalModu Marshal radio modulation "AM" or "FM". -- @field #string MarshalModu Marshal radio modulation "AM" or "FM".
-- @field #AIRBOSS.Radio AirbossRadio Radio for carrier calls.
-- @field #number AirbossFreq Airboss radio frequency in MHz.
-- @field #string AirbossModu Airboss radio modulation "AM" or "FM".
-- @field #number TowerFreq Tower radio frequency in MHz. -- @field #number TowerFreq Tower radio frequency in MHz.
-- @field Core.Scheduler#SCHEDULER radiotimer Radio queue scheduler. -- @field Core.Scheduler#SCHEDULER radiotimer Radio queue scheduler.
-- @field Core.Zone#ZONE_UNIT zoneCCA Carrier controlled area (CCA), i.e. a zone of 50 NM radius around the carrier. -- @field Core.Zone#ZONE_UNIT zoneCCA Carrier controlled area (CCA), i.e. a zone of 50 NM radius around the carrier.
@ -1735,10 +1731,6 @@ AIRBOSS.Difficulty = {
-- @field #table trapsheet Groove data table recorded every 0.5 seconds. -- @field #table trapsheet Groove data table recorded every 0.5 seconds.
-- @field #boolean trapon If true, save trap sheets. -- @field #boolean trapon If true, save trap sheets.
-- @field #string debriefschedulerID Debrief scheduler ID. -- @field #string debriefschedulerID Debrief scheduler ID.
--
-- @field Sound.SRS#MSRS SRS
-- @field Sound.SRS#MSRSQUEUE SRSQ
--
-- @extends #AIRBOSS.FlightGroup -- @extends #AIRBOSS.FlightGroup
--- Main group level radio menu: F10 Other/Airboss. --- Main group level radio menu: F10 Other/Airboss.
@ -1884,7 +1876,6 @@ function AIRBOSS:New( carriername, alias )
-- Set up Airboss radio. -- Set up Airboss radio.
self:SetMarshalRadio() self:SetMarshalRadio()
self:SetAirbossRadio()
-- Set up LSO radio. -- Set up LSO radio.
self:SetLSORadio() self:SetLSORadio()
@ -3052,54 +3043,10 @@ function AIRBOSS:SetBeaconRefresh( TimeInterval )
return self return self
end end
--- Set up SRS for usage without sound files
-- @param #AIRBOSS self
-- @param #string PathToSRS Path to SRS folder, e.g. "C:\\Program Files\\DCS-SimpleRadio-Standalone".
-- @param #number Port Port of the SRS server, defaults to 5002.
-- @param #string Culture (Optional, Airboss Culture) Culture, defaults to "en-US".
-- @param #string Gender (Optional, Airboss Gender) Gender, e.g. "male" or "female". Defaults to "male".
-- @param #string Voice (Optional, Airboss Voice) Set to use a specific voice. Will **override gender and culture** settings.
-- @param #string GoogleCreds (Optional) Path to Google credentials, e.g. "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourgooglekey.json".
-- @param #number Volume (Optional) E.g. 0.75. Defaults to 1.0 (loudest).
-- @param #table AltBackend (Optional) See MSRS for details.
-- @return #AIRBOSS self
function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volume,AltBackend)
-- SRS
local Frequency = self.AirbossRadio.frequency
local Modulation = self.AirbossRadio.modulation
self.SRS = MSRS:New(PathToSRS,Frequency,Modulation,Volume,AltBackend)
self.SRS:SetCoalition(self:GetCoalition())
self.SRS:SetCoordinate(self:GetCoordinate())
self.SRS:SetCulture(Culture or "en-US")
--self.SRS:SetFrequencies(Frequencies)
self.SRS:SetGender(Gender or "male")
self.SRS:SetPath(PathToSRS)
self.SRS:SetPort(Port or 5002)
self.SRS:SetLabel(self.AirbossRadio.alias or "AIRBOSS")
--self.SRS:SetModulations(Modulations)
if GoogleCreds then
self.SRS:SetGoogle(GoogleCreds)
end
if Voice then
self.SRS:SetVoice(Voice)
end
self.SRS:SetVolume(Volume or 1.0)
-- SRSQUEUE
self.SRSQ = MSRSQUEUE:New("AIRBOSS")
self.SRSQ:SetTransmitOnlyWithPlayers(true)
if not self.PilotRadio then
self:SetSRSPilotVoice()
end
return self
end
--- Set LSO radio frequency and modulation. Default frequency is 264 MHz AM. --- Set LSO radio frequency and modulation. Default frequency is 264 MHz AM.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @param #number Frequency (Optional) Frequency in MHz. Default 264 MHz. -- @param #number Frequency (Optional) Frequency in MHz. Default 264 MHz.
-- @param #string Modulation (Optional) Modulation, "AM" or "FM". Default "AM". -- @param #string Modulation (Optional) Modulation, "AM" or "FM". Default "AM".
-- @param #string Voice (Optional) SRS specific voice
-- @param #string Gender (Optional) SRS specific gender
-- @param #string Culture (Optional) SRS specific culture
-- @return #AIRBOSS self -- @return #AIRBOSS self
function AIRBOSS:SetLSORadio( Frequency, Modulation, Voice, Gender, Culture ) function AIRBOSS:SetLSORadio( Frequency, Modulation, Voice, Gender, Culture )
@ -3116,9 +3063,6 @@ function AIRBOSS:SetLSORadio( Frequency, Modulation, Voice, Gender, Culture )
self.LSORadio.frequency = self.LSOFreq self.LSORadio.frequency = self.LSOFreq
self.LSORadio.modulation = self.LSOModu self.LSORadio.modulation = self.LSOModu
self.LSORadio.alias = "LSO" self.LSORadio.alias = "LSO"
self.LSORadio.voice = Voice
self.LSORadio.gender = Gender or "male"
self.LSORadio.culture = Culture or "en-US"
return self return self
end end
@ -3167,9 +3111,6 @@ end
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @param #number Frequency (Optional) Frequency in MHz. Default 305 MHz. -- @param #number Frequency (Optional) Frequency in MHz. Default 305 MHz.
-- @param #string Modulation (Optional) Modulation, "AM" or "FM". Default "AM". -- @param #string Modulation (Optional) Modulation, "AM" or "FM". Default "AM".
-- @param #string Voice (Optional) SRS specific voice
-- @param #string Gender (Optional) SRS specific gender
-- @param #string Culture (Optional) SRS specific culture
-- @return #AIRBOSS self -- @return #AIRBOSS self
function AIRBOSS:SetMarshalRadio( Frequency, Modulation, Voice, Gender, Culture ) function AIRBOSS:SetMarshalRadio( Frequency, Modulation, Voice, Gender, Culture )
@ -3186,9 +3127,6 @@ function AIRBOSS:SetMarshalRadio( Frequency, Modulation, Voice, Gender, Culture
self.MarshalRadio.frequency = self.MarshalFreq self.MarshalRadio.frequency = self.MarshalFreq
self.MarshalRadio.modulation = self.MarshalModu self.MarshalRadio.modulation = self.MarshalModu
self.MarshalRadio.alias = "MARSHAL" self.MarshalRadio.alias = "MARSHAL"
self.MarshalRadio.voice = Voice
self.MarshalRadio.gender = Gender or "male"
self.MarshalRadio.culture = Culture or "en-US"
return self return self
end end
@ -5459,7 +5397,6 @@ function AIRBOSS:_GetAircraftParameters( playerData, step )
local skyhawk = playerData.actype == AIRBOSS.AircraftCarrier.A4EC local skyhawk = playerData.actype == AIRBOSS.AircraftCarrier.A4EC
local tomcat = playerData.actype == AIRBOSS.AircraftCarrier.F14A or playerData.actype == AIRBOSS.AircraftCarrier.F14B local tomcat = playerData.actype == AIRBOSS.AircraftCarrier.F14A or playerData.actype == AIRBOSS.AircraftCarrier.F14B
local harrier = playerData.actype == AIRBOSS.AircraftCarrier.AV8B local harrier = playerData.actype == AIRBOSS.AircraftCarrier.AV8B
local goshawk = playerData.actype == AIRBOSS.AircraftCarrier.T45C
-- Return values. -- Return values.
local alt local alt
@ -5925,27 +5862,64 @@ function AIRBOSS:_ScanCarrierZone()
-- Get aircraft type name. -- Get aircraft type name.
local actype = group:GetTypeName() local actype = group:GetTypeName()
-- Create a new flight group
if knownflight then if knownflight then
-- Debug output.
self:T2(self.lid..string.format("Known flight group %s of type %s in CCA.", groupname, actype))
-- Check if flight is AI and if we want to handle it at all. -- Check if flight is AI and if we want to handle it at all.
if knownflight.ai and knownflight.flag == -100 and self.handleai then if knownflight.ai and self.handleai then
local putintomarshal = false -- Defines if AI group should be handled by the airboss.
local iscarriersquad=true
-- Get flight group. -- Check if AI group is part of the group set if a set was defined.
local flight = _DATABASE:GetOpsGroup( groupname ) if self.squadsetAI then
local group=self.squadsetAI:FindGroup(groupname)
if flight and flight:IsInbound() and flight.destbase:GetName() == self.carrier:GetName() then if group then
if flight.ishelo then iscarriersquad=true
else else
putintomarshal = true iscarriersquad=false
end end
flight.airboss = self
end end
-- Send AI flight to marshal stack. -- Check if group was explicitly excluded.
if putintomarshal then if self.excludesetAI then
local group=self.excludesetAI:FindGroup(groupname)
if group then
iscarriersquad=false
end
end
-- Get distance to carrier.
local dist=knownflight.group:GetCoordinate():Get2DDistance(self:GetCoordinate())
-- Close in distance. Is >0 if AC comes closer wrt to first detected distance d0.
local closein=knownflight.dist0-dist
-- Debug info.
self:T3(self.lid..string.format("Known AI flight group %s closed in by %.1f NM", knownflight.groupname, UTILS.MetersToNM(closein)))
-- Is this group the tanker?
local istanker=self.tanker and self.tanker.tanker:GetName()==groupname
-- Is this group the AWACS?
local isawacs=self.awacs and self.awacs.tanker:GetName()==groupname
-- Send tanker to marshal stack?
local tanker2marshal = istanker and self.tanker:IsReturning() and self.tanker.airbase:GetName()==self.airbase:GetName() and knownflight.flag==-100 and self.tanker.recovery==true
-- Send AWACS to marhsal stack?
local awacs2marshal = isawacs and self.awacs:IsReturning() and self.awacs.airbase:GetName()==self.airbase:GetName() and knownflight.flag==-100 and self.awacs.recovery==true
-- Put flight into Marshal.
local putintomarshal=closein>UTILS.NMToMeters(5) and knownflight.flag==-100 and iscarriersquad and istanker==false and isawacs==false
-- Send AI flight to marshal stack if group closes in more than 5 and has initial flag value.
if putintomarshal or tanker2marshal or awacs2marshal then
-- Get the next free stack for current recovery case. -- Get the next free stack for current recovery case.
local stack = self:_GetFreeStack( knownflight.ai ) local stack = self:_GetFreeStack( knownflight.ai )
@ -11551,49 +11525,9 @@ end
-- @return #number Carrier heading in degrees. -- @return #number Carrier heading in degrees.
function AIRBOSS:GetHeadingIntoWind( magnetic, coord ) function AIRBOSS:GetHeadingIntoWind( magnetic, coord )
local function adjustDegreesForWindSpeed(windSpeed)
local degreesAdjustment = 0
-- the windspeeds are in m/s
-- +0 degrees at 15m/s = 37kts
-- +0 degrees at 14m/s = 35kts
-- +0 degrees at 13m/s = 33kts
-- +4 degrees at 12m/s = 31kts
-- +4 degrees at 11m/s = 29kts
-- +4 degrees at 10m/s = 27kts
-- +4 degrees at 9m/s = 27kts
-- +4 degrees at 8m/s = 27kts
-- +8 degrees at 7m/s = 27kts
-- +8 degrees at 6m/s = 27kts
-- +8 degrees at 5m/s = 26kts
-- +20 degrees at 4m/s = 26kts
-- +20 degrees at 3m/s = 26kts
-- +30 degrees at 2m/s = 26kts 1s
if windSpeed > 0 and windSpeed < 3 then
degreesAdjustment = 30
elseif windSpeed >= 3 and windSpeed < 5 then
degreesAdjustment = 20
elseif windSpeed >= 5 and windSpeed < 8 then
degreesAdjustment = 8
elseif windSpeed >= 8 and windSpeed < 13 then
degreesAdjustment = 4
elseif windSpeed >= 13 then
degreesAdjustment = 0
end
return degreesAdjustment
end
-- Get direction the wind is blowing from. This is where we want to go. -- Get direction the wind is blowing from. This is where we want to go.
local windfrom, vwind = self:GetWind( nil, nil, coord ) local windfrom, vwind = self:GetWind( nil, nil, coord )
--self:I("windfrom="..windfrom.." vwind="..vwind)
vwind = vwind + adjustDegreesForWindSpeed(vwind)
--self:I("windfrom="..windfrom.." (c)vwind="..vwind)
-- Actually, we want the runway in the wind. -- Actually, we want the runway in the wind.
local intowind = windfrom - self.carrierparam.rwyangle local intowind = windfrom - self.carrierparam.rwyangle
@ -13527,8 +13461,8 @@ function AIRBOSS:CarrierTurnIntoWind( time, vdeck, uturn )
-- Wind speed. -- Wind speed.
local _, vwind = self:GetWind() local _, vwind = self:GetWind()
-- Speed of carrier in m/s but at least 4 knots. -- Speed of carrier in m/s but at least 2 knots.
local vtot = math.max( vdeck - vwind, UTILS.KnotsToMps( 4 ) ) local vtot = math.max( vdeck - vwind, UTILS.KnotsToMps( 2 ) )
-- Distance to travel -- Distance to travel
local dist = vtot * time local dist = vtot * time
@ -14732,8 +14666,6 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
return return
end end
if not self.SRS then
-- Create a new radio transmission item. -- Create a new radio transmission item.
local transmission = {} -- #AIRBOSS.Radioitem local transmission = {} -- #AIRBOSS.Radioitem
@ -14786,85 +14718,6 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
if click then if click then
self:RadioTransmission( radio, self[caller].CLICK, false, delay ) self:RadioTransmission( radio, self[caller].CLICK, false, delay )
end end
else
-- SRS transmission
local frequency = self.MarshalRadio.frequency
local modulation = self.MarshalRadio.modulation
local voice = nil
local gender = nil
local culture = nil
if radio.alias == "AIRBOSS" then
frequency = self.AirbossRadio.frequency
modulation = self.AirbossRadio.modulation
voice = self.AirbossRadio.voice
gender = self.AirbossRadio.gender
culture = self.AirbossRadio.culture
end
if radio.alias == "MARSHAL" then
voice = self.MarshalRadio.voice
gender = self.MarshalRadio.gender
culture = self.MarshalRadio.culture
end
if radio.alias == "LSO" then
frequency = self.LSORadio.frequency
modulation = self.LSORadio.modulation
voice = self.LSORadio.voice
gender = self.LSORadio.gender
culture = self.LSORadio.culture
end
if pilotcall then
voice = self.PilotRadio.voice
gender = self.PilotRadio.gender
culture = self.PilotRadio.culture
radio.alias = "PILOT"
end
if not radio.alias then
-- TODO - what freq to use here?
frequency = self.AirbossRadio.frequency
modulation = self.AirbossRadio.modulation
radio.alias = "AIRBOSS"
end
local volume = nil
if loud then
volume = 1.0
end
--local text = tostring(call.modexreceiver).."; "..radio.alias.."; "..call.subtitle
local text = call.subtitle
self:I(self.lid..text)
local srstext = self:_GetNiceSRSText(text)
self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, nil, 0.1, nil, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias)
end
end
--- Set SRS voice for the pilot calls.
-- @param #AIRBOSS self
-- @param #string Voice (Optional) SRS specific voice
-- @param #string Gender (Optional) SRS specific gender
-- @param #string Culture (Optional) SRS specific culture
-- @return #AIRBOSS self
function AIRBOSS:SetSRSPilotVoice( Voice, Gender, Culture )
self.PilotRadio = {} -- #AIRBOSS.Radio
self.PilotRadio.alias = "PILOT"
self.PilotRadio.voice = Voice or MSRS.Voices.Microsoft.David
self.PilotRadio.gender = Gender or "male"
self.PilotRadio.culture = Culture or "en-US"
if (not Voice) and self.SRS and self.SRS.google then
self.PilotRadio.voice = MSRS.Voices.Google.Standard.en_US_Standard_J
end
return self
end end
--- Check if a call needs a subtitle because the complete voice overs are not available. --- Check if a call needs a subtitle because the complete voice overs are not available.
@ -15110,39 +14963,6 @@ function AIRBOSS:_RadioFilename( call, loud, channel )
return filename return filename
end end
--- Format text into SRS friendly string
-- @param #AIRBOSS self
-- @param #string text
-- @return #string text
function AIRBOSS:_GetNiceSRSText(text)
text = string.gsub(text,"================================\n","")
text = string.gsub(text,"||","parallel")
text = string.gsub(text,"==","perpendicular")
text = string.gsub(text,"BRC","Base recovery")
--text = string.gsub(text,"#","Number")
text = string.gsub(text,"%((%a+)%)","Morse %1")
text = string.gsub(text,"°C","° Celsius")
text = string.gsub(text,"°"," degrees")
text = string.gsub(text," FB "," Final bearing ")
text = string.gsub(text," ops"," operations ")
text = string.gsub(text," kts"," knots")
text = string.gsub(text,"TACAN","Tackan")
text = string.gsub(text,"ICLS","I.C.L.S.")
text = string.gsub(text,"LSO","L.S.O.")
text = string.gsub(text,"inHg","inches of Mercury")
text = string.gsub(text,"QFE","Q.F.E.")
text = string.gsub(text,"hPa","hecto pascal")
text = string.gsub(text," NM"," nautical miles")
text = string.gsub(text," ft"," feet")
text = string.gsub(text,"A/C","aircraft")
text = string.gsub(text,"(#[%a%d%p%s]+)\n","")
text = string.gsub(text,"%.000"," dot zero")
text = string.gsub(text,"00"," double zero")
text = string.gsub(text," 0 "," zero " )
text = string.gsub(text,"\n","; ")
return text
end
--- Send text message to player client. --- Send text message to player client.
-- Message format will be "SENDER: RECCEIVER, MESSAGE". -- Message format will be "SENDER: RECCEIVER, MESSAGE".
-- @param #AIRBOSS self -- @param #AIRBOSS self
@ -15154,7 +14974,7 @@ end
-- @param #boolean clear If true, clear screen from previous messages. -- @param #boolean clear If true, clear screen from previous messages.
-- @param #number delay Delay in seconds, before the message is displayed. -- @param #number delay Delay in seconds, before the message is displayed.
function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duration, clear, delay ) function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duration, clear, delay )
self:I({sender,receiver,message})
if playerData and message and message ~= "" then if playerData and message and message ~= "" then
-- Default duration. -- Default duration.
@ -15178,7 +14998,6 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio
self:ScheduleOnce( delay, self.MessageToPlayer, self, playerData, message, sender, receiver, duration, clear ) self:ScheduleOnce( delay, self.MessageToPlayer, self, playerData, message, sender, receiver, duration, clear )
else else
if not self.SRS then
-- Wait until previous sound finished. -- Wait until previous sound finished.
local wait = 0 local wait = 0
@ -15220,48 +15039,7 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio
local filename = self:_RadioFilename( self.MarshalCall.CLICK ) local filename = self:_RadioFilename( self.MarshalCall.CLICK )
USERSOUND:New( filename ):ToGroup( playerData.group, wait ) USERSOUND:New( filename ):ToGroup( playerData.group, wait )
end end
else
-- SRS transmission
local frequency = self.MarshalRadio.frequency
local modulation = self.MarshalRadio.modulation
local voice = self.MarshalRadio.voice
local gender = self.MarshalRadio.gender
local culture = self.MarshalRadio.culture
if not sender then sender = "AIRBOSS" end
if string.find(sender,"AIRBOSS" ) then
frequency = self.AirbossRadio.frequency
modulation = self.AirbossRadio.modulation
voice = self.AirbossRadio.voice
gender = self.AirbossRadio.gender
culture = self.AirbossRadio.culture
end
--if sender == "MARSHAL" then
--voice = self.MarshalRadio.voice
--gender = self.MarshalRadio.gender
--culture = self.MarshalRadio.culture
--end
if sender == "LSO" then
frequency = self.LSORadio.frequency
modulation = self.LSORadio.modulation
voice = self.LSORadio.voice
gender = self.LSORadio.gender
culture = self.LSORadio.culture
--elseif not sender then
-- TODO - what freq to use here?
--frequency = self.AirbossRadio.frequency
--modulation = self.AirbossRadio.modulation
--sender = "AIRBOSS"
end
self:I(self.lid..text)
self:I({sender,frequency,modulation,voice})
local srstext = self:_GetNiceSRSText(text)
self.SRSQ:NewTransmission(srstext,duration,self.SRS,nil,0.1,nil,nil,nil,frequency,modulation,gender,culture,voice,1.0,sender)
end
-- Text message to player client. -- Text message to player client.
if playerData.client then if playerData.client then
MESSAGE:New( text, duration, sender, clear ):ToClient( playerData.client ) MESSAGE:New( text, duration, sender, clear ):ToClient( playerData.client )
@ -16600,7 +16378,7 @@ function AIRBOSS:_RequestSpinning( _unitName )
-- Some advice. -- Some advice.
if playerData.difficulty == AIRBOSS.Difficulty.EASY then if playerData.difficulty == AIRBOSS.Difficulty.EASY then
local text = "Climb to 1200 feet and proceed to the initial again." local text = "Climb to 1200 feet and proceed to the initial again."
self:MessageToPlayer( playerData, text, "AIRBOSS", "" ) self:MessageToPlayer( playerData, text, "INSTRUCTOR", "" )
end end
return return
@ -17355,7 +17133,7 @@ function AIRBOSS:_DisplayCarrierInfo( _unitname )
state = "Deck closed" state = "Deck closed"
end end
if self.turning then if self.turning then
state = state .. " (currently turning)" state = state .. " (turning currently)"
end end
-- Message text. -- Message text.