Merge branch 'develop' into FF/Ops

This commit is contained in:
Frank
2023-06-21 22:00:14 +02:00
11 changed files with 473 additions and 142 deletions

View File

@@ -152,12 +152,16 @@
-- @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.
@@ -1731,6 +1735,10 @@ 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.
@@ -1743,7 +1751,7 @@ AIRBOSS.MenuF10Root = nil
--- Airboss class version. --- Airboss class version.
-- @field #string version -- @field #string version
AIRBOSS.version = "1.3.0" AIRBOSS.version = "1.3.2"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -1876,6 +1884,7 @@ 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()
@@ -3043,12 +3052,56 @@ 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 ) function AIRBOSS:SetLSORadio( Frequency, Modulation, Voice, Gender, Culture )
self.LSOFreq = (Frequency or 264) self.LSOFreq = (Frequency or 264)
Modulation = Modulation or "AM" Modulation = Modulation or "AM"
@@ -3063,16 +3116,62 @@ function AIRBOSS:SetLSORadio( Frequency, Modulation )
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
--- Set carrier radio frequency and modulation. Default frequency is 305 MHz AM. --- Set Airboss radio frequency and modulation. Default frequency is Tower frequency.
-- @param #AIRBOSS self
-- @param #number Frequency (Optional) Frequency in MHz. Default frequency is Tower frequency.
-- @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
-- @usage
-- -- Set single frequency
-- myairboss:SetAirbossRadio(127.5,"AM",MSRS.Voices.Google.Standard.en_GB_Standard_F)
--
-- -- Set multiple frequencies, note you **need** to pass one modulation per frequency given!
-- myairboss:SetAirbossRadio({127.5,243},{radio.modulation.AM,radio.modulation.AM},MSRS.Voices.Google.Standard.en_GB_Standard_F)
function AIRBOSS:SetAirbossRadio( Frequency, Modulation, Voice, Gender, Culture )
self.AirbossFreq = Frequency or self:_GetTowerFrequency() or 127.5
Modulation = Modulation or "AM"
if type(Modulation) == "table" then
self.AirbossModu = Modulation
else
if Modulation == "FM" then
self.AirbossModu = radio.modulation.FM
else
self.AirbossModu = radio.modulation.AM
end
end
self.AirbossRadio = {} -- #AIRBOSS.Radio
self.AirbossRadio.frequency = self.AirbossFreq
self.AirbossRadio.modulation = self.AirbossModu
self.AirbossRadio.alias = "AIRBOSS"
self.AirbossRadio.voice = Voice
self.AirbossRadio.gender = Gender or "male"
self.AirbossRadio.culture = Culture or "en-US"
return self
end
--- Set Marshal radio frequency and modulation. Default frequency is 305 MHz AM.
-- @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 ) function AIRBOSS:SetMarshalRadio( Frequency, Modulation, Voice, Gender, Culture )
self.MarshalFreq = Frequency or 305 self.MarshalFreq = Frequency or 305
Modulation = Modulation or "AM" Modulation = Modulation or "AM"
@@ -3087,6 +3186,9 @@ function AIRBOSS:SetMarshalRadio( Frequency, Modulation )
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
@@ -11448,8 +11550,42 @@ 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 ) + adjustDegreesForWindSpeed(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
@@ -13384,8 +13520,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 2 knots. -- Speed of carrier in m/s but at least 4 knots.
local vtot = math.max( vdeck - vwind, UTILS.KnotsToMps( 2 ) ) local vtot = math.max( vdeck - vwind, UTILS.KnotsToMps( 4 ) )
-- Distance to travel -- Distance to travel
local dist = vtot * time local dist = vtot * time
@@ -14588,59 +14724,140 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
if radio == nil or call == nil then if radio == nil or call == nil then
return return
end end
-- Create a new radio transmission item. if not self.SRS then
local transmission = {} -- #AIRBOSS.Radioitem
-- Create a new radio transmission item.
transmission.radio = radio local transmission = {} -- #AIRBOSS.Radioitem
transmission.call = call
transmission.Tplay = timer.getAbsTime() + (delay or 0) transmission.radio = radio
transmission.interval = interval transmission.call = call
transmission.isplaying = false transmission.Tplay = timer.getAbsTime() + (delay or 0)
transmission.Tstarted = nil transmission.interval = interval
transmission.loud = loud and call.loud transmission.isplaying = false
transmission.Tstarted = nil
-- Player onboard number if sender has one. transmission.loud = loud and call.loud
if self:_IsOnboard( call.modexsender ) then
self:_Number2Radio( radio, call.modexsender, delay, 0.3, pilotcall ) -- Player onboard number if sender has one.
end if self:_IsOnboard( call.modexsender ) then
self:_Number2Radio( radio, call.modexsender, delay, 0.3, pilotcall )
-- Play onboard number if receiver has one. end
if self:_IsOnboard( call.modexreceiver ) then
self:_Number2Radio( radio, call.modexreceiver, delay, 0.3, pilotcall ) -- Play onboard number if receiver has one.
end if self:_IsOnboard( call.modexreceiver ) then
self:_Number2Radio( radio, call.modexreceiver, delay, 0.3, pilotcall )
-- Add transmission to the right queue. end
local caller = ""
if radio.alias == "LSO" then -- Add transmission to the right queue.
local caller = ""
table.insert( self.RQLSO, transmission ) if radio.alias == "LSO" then
caller = "LSOCall" table.insert( self.RQLSO, transmission )
-- Schedule radio queue checks. caller = "LSOCall"
if not self.RQLid then
self:T( self.lid .. string.format( "Starting LSO radio queue." ) ) -- Schedule radio queue checks.
self.RQLid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQLSO, "LSO" }, 0.02, 0.05 ) if not self.RQLid then
self:T( self.lid .. string.format( "Starting LSO radio queue." ) )
self.RQLid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQLSO, "LSO" }, 0.02, 0.05 )
end
elseif radio.alias == "MARSHAL" then
table.insert( self.RQMarshal, transmission )
caller = "MarshalCall"
if not self.RQMid then
self:T( self.lid .. string.format( "Starting Marhal radio queue." ) )
self.RQMid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQMarshal, "MARSHAL" }, 0.02, 0.05 )
end
end
-- Append radio click sound at the end of the transmission.
if click then
self:RadioTransmission( radio, self[caller].CLICK, false, delay )
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 end
elseif radio.alias == "MARSHAL" then if not radio.alias then
-- TODO - what freq to use here?
table.insert( self.RQMarshal, transmission ) frequency = self.AirbossRadio.frequency
modulation = self.AirbossRadio.modulation
caller = "MarshalCall" radio.alias = "AIRBOSS"
if not self.RQMid then
self:T( self.lid .. string.format( "Starting Marhal radio queue." ) )
self.RQMid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQMarshal, "MARSHAL" }, 0.02, 0.05 )
end 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, tstart, 0.1, subgroups, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias)
end end
end
-- Append radio click sound at the end of the transmission. --- Set SRS voice for the pilot calls.
if click then -- @param #AIRBOSS self
self:RadioTransmission( radio, self[caller].CLICK, false, delay ) -- @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 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.
@@ -14886,6 +15103,39 @@ 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
@@ -14897,7 +15147,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.
@@ -14920,49 +15170,91 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio
-- SCHEDULER:New(nil, self.MessageToPlayer, {self, playerData, message, sender, receiver, duration, clear}, delay) -- SCHEDULER:New(nil, self.MessageToPlayer, {self, playerData, message, sender, receiver, duration, clear}, delay)
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
-- Wait until previous sound finished. if not self.SRS then
local wait = 0 -- Wait until previous sound finished.
local wait = 0
-- Onboard number to get the attention.
if receiver == playerData.onboard then -- Onboard number to get the attention.
if receiver == playerData.onboard then
-- Which voice over number to use.
if sender and (sender == "LSO" or sender == "MARSHAL" or sender == "AIRBOSS") then -- Which voice over number to use.
if sender and (sender == "LSO" or sender == "MARSHAL" or sender == "AIRBOSS") then
-- User sound of board number.
wait = wait + self:_Number2Sound( playerData, sender, receiver ) -- User sound of board number.
wait = wait + self:_Number2Sound( playerData, sender, receiver )
end
end end
end
-- Negative.
if string.find( text:lower(), "negative" ) then
local filename = self:_RadioFilename( self.MarshalCall.NEGATIVE, false, "MARSHAL" )
USERSOUND:New( filename ):ToGroup( playerData.group, wait )
wait = wait + self.MarshalCall.NEGATIVE.duration
end
-- Affirm.
if string.find( text:lower(), "affirm" ) then
local filename = self:_RadioFilename( self.MarshalCall.AFFIRMATIVE, false, "MARSHAL" )
USERSOUND:New( filename ):ToGroup( playerData.group, wait )
wait = wait + self.MarshalCall.AFFIRMATIVE.duration
end
-- Roger.
if string.find( text:lower(), "roger" ) then
local filename = self:_RadioFilename( self.MarshalCall.ROGER, false, "MARSHAL" )
USERSOUND:New( filename ):ToGroup( playerData.group, wait )
wait = wait + self.MarshalCall.ROGER.duration
end
-- Play click sound to end message.
if wait > 0 then
local filename = self:_RadioFilename( self.MarshalCall.CLICK )
USERSOUND:New( filename ):ToGroup( playerData.group, wait )
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
-- Negative. if string.find(sender,"AIRBOSS" ) then
if string.find( text:lower(), "negative" ) then frequency = self.AirbossRadio.frequency
local filename = self:_RadioFilename( self.MarshalCall.NEGATIVE, false, "MARSHAL" ) modulation = self.AirbossRadio.modulation
USERSOUND:New( filename ):ToGroup( playerData.group, wait ) voice = self.AirbossRadio.voice
wait = wait + self.MarshalCall.NEGATIVE.duration gender = self.AirbossRadio.gender
end culture = self.AirbossRadio.culture
end
-- Affirm. --if sender == "MARSHAL" then
if string.find( text:lower(), "affirm" ) then --voice = self.MarshalRadio.voice
local filename = self:_RadioFilename( self.MarshalCall.AFFIRMATIVE, false, "MARSHAL" ) --gender = self.MarshalRadio.gender
USERSOUND:New( filename ):ToGroup( playerData.group, wait ) --culture = self.MarshalRadio.culture
wait = wait + self.MarshalCall.AFFIRMATIVE.duration --end
end
-- Roger. if sender == "LSO" then
if string.find( text:lower(), "roger" ) then frequency = self.LSORadio.frequency
local filename = self:_RadioFilename( self.MarshalCall.ROGER, false, "MARSHAL" ) modulation = self.LSORadio.modulation
USERSOUND:New( filename ):ToGroup( playerData.group, wait ) voice = self.LSORadio.voice
wait = wait + self.MarshalCall.ROGER.duration gender = self.LSORadio.gender
end 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
-- Play click sound to end message. self:I(self.lid..text)
if wait > 0 then self:I({sender,frequency,modulation,voice})
local filename = self:_RadioFilename( self.MarshalCall.CLICK ) local srstext = self:_GetNiceSRSText(text)
USERSOUND:New( filename ):ToGroup( playerData.group, wait ) self.SRSQ:NewTransmission(srstext,duration,self.SRS,tstart,0.1,subgroups,subtitle,subduration,frequency,modulation,gender,culture,voice,volume,sender)
end 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 )
@@ -16301,7 +16593,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, "INSTRUCTOR", "" ) self:MessageToPlayer( playerData, text, "AIRBOSS", "" )
end end
return return

View File

@@ -114,6 +114,9 @@
-- @field #number NcarriersMax Max number of required carrier assets. -- @field #number NcarriersMax Max number of required carrier assets.
-- @field Core.Zone#ZONE transportDeployZone Deploy zone of an OPSTRANSPORT. -- @field Core.Zone#ZONE transportDeployZone Deploy zone of an OPSTRANSPORT.
-- @field Core.Zone#ZONE transportDisembarkZone Disembark zone of an OPSTRANSPORT. -- @field Core.Zone#ZONE transportDisembarkZone Disembark zone of an OPSTRANSPORT.
-- @param #table carrierCategories Transport group categories.
-- @field #table carrierAttributes Generalized attribute(s) of transport assets.
-- @field #table carrierProperties DCS attribute(s) of transport assets.
-- --
-- @field #number artyRadius Radius in meters. -- @field #number artyRadius Radius in meters.
-- @field #number artyShots Number of shots fired. -- @field #number artyShots Number of shots fired.
@@ -3056,15 +3059,18 @@ end
-- @param #number NcarriersMin Number of carriers *at least* required. Default 1. -- @param #number NcarriersMin Number of carriers *at least* required. Default 1.
-- @param #number NcarriersMax Number of carriers *at most* used for transportation. Default is same as `NcarriersMin`. -- @param #number NcarriersMax Number of carriers *at most* used for transportation. Default is same as `NcarriersMin`.
-- @param Core.Zone#ZONE DisembarkZone Zone where assets are disembarked to. -- @param Core.Zone#ZONE DisembarkZone Zone where assets are disembarked to.
-- @param #table Categories Group categories.
-- @param #table Attributes Generalizes group attributes.
-- @param #table Properties DCS attributes.
-- @return #AUFTRAG self -- @return #AUFTRAG self
function AUFTRAG:SetRequiredTransport(DeployZone, NcarriersMin, NcarriersMax, DisembarkZone) function AUFTRAG:SetRequiredTransport(DeployZone, NcarriersMin, NcarriersMax, DisembarkZone, Categories, Attributes, Properties)
-- OPS transport from pickup to deploy zone. -- OPS transport from pickup to deploy zone.
self.transportDeployZone=DeployZone self.transportDeployZone=DeployZone
self.transportDisembarkZone=DisembarkZone self.transportDisembarkZone=DisembarkZone
-- Set required carriers. -- Set required carriers.
self:SetRequiredCarriers(NcarriersMin, NcarriersMax) self:SetRequiredCarriers(NcarriersMin, NcarriersMax, Categories, Attributes, Properties)
return self return self
end end
@@ -3115,8 +3121,11 @@ end
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param #number NcarriersMin Number of carriers *at least* required. Default 1. -- @param #number NcarriersMin Number of carriers *at least* required. Default 1.
-- @param #number NcarriersMax Number of carriers *at most* used for transportation. Default is same as `NcarriersMin`. -- @param #number NcarriersMax Number of carriers *at most* used for transportation. Default is same as `NcarriersMin`.
-- @param #table Categories Group categories.
-- @param #table Attributes Group attributes. See `GROUP.Attribute.`
-- @param #table Properties DCS attributes.
-- @return #AUFTRAG self -- @return #AUFTRAG self
function AUFTRAG:SetRequiredCarriers(NcarriersMin, NcarriersMax) function AUFTRAG:SetRequiredCarriers(NcarriersMin, NcarriersMax, Categories, Attributes, Properties)
self.NcarriersMin=NcarriersMin or 1 self.NcarriersMin=NcarriersMin or 1
@@ -3127,6 +3136,10 @@ function AUFTRAG:SetRequiredCarriers(NcarriersMin, NcarriersMax)
self.NcarriersMax=self.NcarriersMin self.NcarriersMax=self.NcarriersMin
end end
self.carrierCategories = UTILS.EnsureTable(Categories, true)
self.carrierAttributes = UTILS.EnsureTable(Attributes, true)
self.carrierProperties = UTILS.EnsureTable(Properties, true)
return self return self
end end

View File

@@ -1508,7 +1508,7 @@ function COMMANDER:CheckMissionQueue()
local Transport=nil local Transport=nil
local Legions=mission.transportLegions or self.legions local Legions=mission.transportLegions or self.legions
TransportAvail, Transport=LEGION.AssignAssetsForTransport(self, Legions, assets, mission.NcarriersMin, mission.NcarriersMax, mission.transportDeployZone, mission.transportDisembarkZone) TransportAvail, Transport=LEGION.AssignAssetsForTransport(self, Legions, assets, mission.NcarriersMin, mission.NcarriersMax, mission.transportDeployZone, mission.transportDisembarkZone, mission.carrierCategories, mission.carrierAttributes, mission.carrierProperties)
-- Add opstransport to mission. -- Add opstransport to mission.
if TransportAvail and Transport then if TransportAvail and Transport then
@@ -1702,8 +1702,7 @@ function COMMANDER:RecruitAssetsForMission(Mission)
local cohort=_cohort --Ops.Cohort#COHORT local cohort=_cohort --Ops.Cohort#COHORT
-- Check if cohort can perform transport to target. -- Check if cohort can perform transport to target.
--TODO: Option to filter transport carrier asset categories, attributes and/or properties. local can=LEGION._CohortCan(cohort, AUFTRAG.Type.OPSTRANSPORT, Mission.carrierCategories, Mission.carrierAttributes, Mission.carrierProperties, nil, TargetVec2)
local can=LEGION._CohortCan(cohort, AUFTRAG.Type.OPSTRANSPORT, Categories, Attributes, Properties, nil, TargetVec2)
-- MaxWeight of cargo assets is limited by the largets available cargo bay. We don't want to select, e.g., tanks that cannot be transported by APCs or helos. -- MaxWeight of cargo assets is limited by the largets available cargo bay. We don't want to select, e.g., tanks that cannot be transported by APCs or helos.
if can and (MaxWeight==nil or cohort.cargobayLimit>MaxWeight) then if can and (MaxWeight==nil or cohort.cargobayLimit>MaxWeight) then

View File

@@ -714,7 +714,7 @@ function LEGION:CheckMissionQueue()
local Legions=mission.transportLegions or {self} local Legions=mission.transportLegions or {self}
-- Assign carrier assets for transport. -- Assign carrier assets for transport.
TransportAvail, Transport=self:AssignAssetsForTransport(Legions, assets, mission.NcarriersMin, mission.NcarriersMax, mission.transportDeployZone, mission.transportDisembarkZone) TransportAvail, Transport=self:AssignAssetsForTransport(Legions, assets, mission.NcarriersMin, mission.NcarriersMax, mission.transportDeployZone, mission.transportDisembarkZone, mission.carrierCategories, mission.carrierAttributes, mission.carrierProperties)
end end
-- Add opstransport to mission. -- Add opstransport to mission.
@@ -2252,8 +2252,7 @@ function LEGION:RecruitAssetsForMission(Mission)
local cohort=_cohort --Ops.Cohort#COHORT local cohort=_cohort --Ops.Cohort#COHORT
-- Check if cohort can perform transport to target. -- Check if cohort can perform transport to target.
--TODO: Option to filter transport carrier asset categories, attributes and/or properties. local can=LEGION._CohortCan(cohort, AUFTRAG.Type.OPSTRANSPORT, Mission.carrierCategories, Mission.carrierAttributes, Mission.carrierProperties, nil, TargetVec2)
local can=LEGION._CohortCan(cohort, AUFTRAG.Type.OPSTRANSPORT, Categories, Attributes, Properties, nil, TargetVec2)
-- MaxWeight of cargo assets is limited by the largets available cargo bay. We don't want to select, e.g., tanks that cannot be transported by APCs or helos. -- MaxWeight of cargo assets is limited by the largets available cargo bay. We don't want to select, e.g., tanks that cannot be transported by APCs or helos.
if can and (MaxWeight==nil or cohort.cargobayLimit>MaxWeight) then if can and (MaxWeight==nil or cohort.cargobayLimit>MaxWeight) then

View File

@@ -1291,8 +1291,8 @@ function NAVYGROUP:onafterTurnIntoWind(From, Event, To, IntoWind)
-- Convert to knots. -- Convert to knots.
vwind=UTILS.MpsToKnots(vwind) vwind=UTILS.MpsToKnots(vwind)
-- Speed of carrier relative to wind but at least 2 knots. -- Speed of carrier relative to wind but at least 4 knots.
local speed=math.max(IntoWind.Speed-vwind, 2) local speed=math.max(IntoWind.Speed-vwind, 4)
-- Debug info. -- Debug info.
self:T(self.lid..string.format("Steaming into wind: Heading=%03d Speed=%.1f Vwind=%.1f Vtot=%.1f knots, Tstart=%d Tstop=%d", IntoWind.Heading, speed, vwind, speed+vwind, IntoWind.Tstart, IntoWind.Tstop)) self:T(self.lid..string.format("Steaming into wind: Heading=%03d Speed=%.1f Vwind=%.1f Vtot=%.1f knots, Tstart=%d Tstop=%d", IntoWind.Heading, speed, vwind, speed+vwind, IntoWind.Tstart, IntoWind.Tstop))

View File

@@ -1536,7 +1536,7 @@ PLAYERTASKCONTROLLER.Messages = {
--- PLAYERTASK class version. --- PLAYERTASK class version.
-- @field #string version -- @field #string version
PLAYERTASKCONTROLLER.version="0.1.60" PLAYERTASKCONTROLLER.version="0.1.60a"
--- Create and run a new TASKCONTROLLER instance. --- Create and run a new TASKCONTROLLER instance.
-- @param #PLAYERTASKCONTROLLER self -- @param #PLAYERTASKCONTROLLER self
@@ -2195,8 +2195,10 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData)
--local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext) --local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext)
local text = string.format(switchtext,playername,self.MenuName or self.Name,freqtext) local text = string.format(switchtext,playername,self.MenuName or self.Name,freqtext)
self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation) self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation)
if EventData.IniUnitName then if EventData.IniPlayerName then
self:_BuildMenus(CLIENT:FindByName(EventData.IniUnitName)) self.PlayerMenu[EventData.IniPlayerName] = nil
--self:_BuildMenus(CLIENT:FindByName(EventData.IniUnitName))
self:_BuildMenus(CLIENT:FindByPlayerName(EventData.IniPlayerName))
end end
end end
end end
@@ -3460,7 +3462,14 @@ end
-- @return #PLAYERTASKCONTROLLER self -- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess) function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess)
self:T(self.lid.."_BuildMenus") self:T(self.lid.."_BuildMenus")
if self.MenuBuildLocked and (timer.getAbsTime() - self.MenuBuildLocked < 2) then
self:ScheduleOnce(2,self._BuildMenus,self,Client,enforced,fromsuccess)
return self
else
self.MenuBuildLocked = timer.getAbsTime()
end
local clients = self.ClientSet:GetAliveSet() local clients = self.ClientSet:GetAliveSet()
local joinorabort = false local joinorabort = false
local timedbuild = false local timedbuild = false
@@ -3653,6 +3662,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess)
end end
end end
end end
self.MenuBuildLocked = false
return self return self
end end

View File

@@ -1364,6 +1364,11 @@ MSRSQUEUE = {
-- @field #number interval Interval in seconds before next transmission. -- @field #number interval Interval in seconds before next transmission.
-- @field #boolean TransmitOnlyWithPlayers If true, only transmit if there are alive Players. -- @field #boolean TransmitOnlyWithPlayers If true, only transmit if there are alive Players.
-- @field Core.Set#SET_CLIENT PlayerSet PlayerSet created when TransmitOnlyWithPlayers == true -- @field Core.Set#SET_CLIENT PlayerSet PlayerSet created when TransmitOnlyWithPlayers == true
-- @field #string gender Voice gender
-- @field #string culture Voice culture
-- @field #string voice Voice if any
-- @field #number volume Volume
-- @field #string label Label to be used
--- Create a new MSRSQUEUE object for a given radio frequency/modulation. --- Create a new MSRSQUEUE object for a given radio frequency/modulation.
-- @param #MSRSQUEUE self -- @param #MSRSQUEUE self
@@ -1443,8 +1448,13 @@ end
-- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec. -- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec.
-- @param #number frequency Radio frequency if other than MSRS default. -- @param #number frequency Radio frequency if other than MSRS default.
-- @param #number modulation Radio modulation if other then MSRS default. -- @param #number modulation Radio modulation if other then MSRS default.
-- @param #string gender Gender of the voice
-- @param #string culture Culture of the voice
-- @param #string voice Specific voice
-- @param #number volume Volume setting
-- @param #string label Label to be used
-- @return #MSRSQUEUE.Transmission Radio transmission table. -- @return #MSRSQUEUE.Transmission Radio transmission table.
function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation) function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation, gender, culture, voice, volume, label)
if self.TransmitOnlyWithPlayers then if self.TransmitOnlyWithPlayers then
if self.PlayerSet and self.PlayerSet:CountAlive() == 0 then if self.PlayerSet and self.PlayerSet:CountAlive() == 0 then
@@ -1479,6 +1489,11 @@ function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgr
else else
transmission.subduration=0 --nil transmission.subduration=0 --nil
end end
transmission.gender = gender
transmission.culture = culture
transmission.voice = voice
transmission.gender = volume
transmission.label = label
-- Add transmission to queue. -- Add transmission to queue.
self:AddTransmission(transmission) self:AddTransmission(transmission)
@@ -1492,7 +1507,7 @@ end
function MSRSQUEUE:Broadcast(transmission) function MSRSQUEUE:Broadcast(transmission)
if transmission.frequency then if transmission.frequency then
transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, Gender, Culture, Voice, Volume, Label) transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, transmission.gender, transmission.culture, transmission.voice, transmission.volume, transmission.label)
else else
transmission.msrs:PlayText(transmission.text) transmission.msrs:PlayText(transmission.text)
end end

View File

@@ -426,7 +426,7 @@ AIRBASE.TheChannel = {
-- * AIRBASE.Syria.Al_Dumayr -- * AIRBASE.Syria.Al_Dumayr
-- * AIRBASE.Syria.Gazipasa -- * AIRBASE.Syria.Gazipasa
-- * AIRBASE.Syria.Hatay -- * AIRBASE.Syria.Hatay
-- * AIRBASE.Syria.Nicosia -- * AIRBASE.Syria.Nicosia [Deactivated by ED as of June/2023]
-- * AIRBASE.Syria.Pinarbashi -- * AIRBASE.Syria.Pinarbashi
-- * AIRBASE.Syria.Paphos -- * AIRBASE.Syria.Paphos
-- * AIRBASE.Syria.Kingsfield -- * AIRBASE.Syria.Kingsfield
@@ -491,7 +491,7 @@ AIRBASE.Syria={
["Al_Dumayr"]="Al-Dumayr", ["Al_Dumayr"]="Al-Dumayr",
["Gazipasa"]="Gazipasa", ["Gazipasa"]="Gazipasa",
["Hatay"]="Hatay", ["Hatay"]="Hatay",
["Nicosia"]="Nicosia", --["Nicosia"]="Nicosia",
["Pinarbashi"]="Pinarbashi", ["Pinarbashi"]="Pinarbashi",
["Paphos"]="Paphos", ["Paphos"]="Paphos",
["Kingsfield"]="Kingsfield", ["Kingsfield"]="Kingsfield",

View File

@@ -35,6 +35,7 @@ do
-- @field #number id -- @field #number id
-- @field #number side -- @field #number side
-- @field #number slot -- @field #number slot
-- @field #numner timestamp
--- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net) --- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net)
-- with some added FSM functions and options to block/unblock players in MP environments. -- with some added FSM functions and options to block/unblock players in MP environments.
@@ -205,7 +206,7 @@ function NET:_EventHandler(EventData)
-- Joining -- Joining
if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then
self:T(self.lid.."Pilot Joining: "..name.." | UCID: "..ucid) self:T(self.lid.."Pilot Joining: "..name.." | UCID: "..ucid.." | Event ID: "..data.id)
-- Check for blockages -- Check for blockages
local blocked = self:IsAnyBlocked(ucid,name,PlayerID,PlayerSide,PlayerSlot) local blocked = self:IsAnyBlocked(ucid,name,PlayerID,PlayerSide,PlayerSlot)
@@ -213,15 +214,18 @@ function NET:_EventHandler(EventData)
-- block pilot -- block pilot
local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' ) local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' )
else else
self.KnownPilots[name] = {
name = name,
ucid = ucid,
id = PlayerID,
side = PlayerSide,
slot = PlayerSlot,
}
local client = CLIENT:FindByPlayerName(name) or data.IniUnit local client = CLIENT:FindByPlayerName(name) or data.IniUnit
self:__PlayerJoined(1,client,name) if not self.KnownPilots[name] or (self.KnownPilots[name] and TNow-self.KnownPilots[name].timestamp > 3) then
self:__PlayerJoined(1,client,name)
self.KnownPilots[name] = {
name = name,
ucid = ucid,
id = PlayerID,
side = PlayerSide,
slot = PlayerSlot,
timestamp = TNow,
}
end
return self return self
end end
end end

View File

@@ -239,19 +239,18 @@ end
function POSITIONABLE:GetVec3() function POSITIONABLE:GetVec3()
local DCSPositionable = self:GetDCSObject() local DCSPositionable = self:GetDCSObject()
if DCSPositionable then if DCSPositionable then
-- local status, vec3 = pcall( --local status, vec3 = pcall(
-- function() -- function()
-- local vec3 = DCSPositionable:getPoint() -- local vec3 = DCSPositionable:getPoint()
-- return vec3 -- return vec3
-- end --end
-- ) --)
local vec3 = DCSPositionable:getPoint() local vec3 = DCSPositionable:getPoint()
return vec3 --if status then
-- if status then return vec3
-- return vec3 --else
-- else --self:E( { "Cannot get Vec3 from DCS Object", Positionable = self, Alive = self:IsAlive() } )
-- self:E( { "Cannot get Vec3 from DCS Object", Positionable = self, Alive = self:IsAlive() } ) --end
-- end
end end
-- ERROR! -- ERROR!
self:E( { "Cannot get the Positionable DCS Object for GetVec3", Positionable = self, Alive = self:IsAlive() } ) self:E( { "Cannot get the Positionable DCS Object for GetVec3", Positionable = self, Alive = self:IsAlive() } )

View File

@@ -703,15 +703,15 @@ function UNIT:GetAmmo()
self:F2( self.UnitName ) self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject() local DCSUnit = self:GetDCSObject()
if DCSUnit then if DCSUnit then
-- local status, unitammo = pcall( --local status, unitammo = pcall(
-- function() -- function()
-- local UnitAmmo = DCSUnit:getAmmo() -- local UnitAmmo = DCSUnit:getAmmo()
-- return UnitAmmo -- return UnitAmmo
-- end --end
-- ) --)
-- if status then --if status then
-- return unitammo --return unitammo
-- end --end
local UnitAmmo = DCSUnit:getAmmo() local UnitAmmo = DCSUnit:getAmmo()
return UnitAmmo return UnitAmmo
end end