diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 5997fb856..4006b4a2a 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -979,13 +979,15 @@ function DATABASE:_RegisterClients() for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do self:I(string.format("Register Client: %s", tostring(ClientName))) - self:AddClient( ClientName ) + local client=self:AddClient( ClientName ) + client.SpawnCoord=COORDINATE:New(ClientTemplate.x, ClientTemplate.alt, ClientTemplate.y) end return self end ---- @param #DATABASE self +--- Private method that registeres all static objects. +-- @param #DATABASE self function DATABASE:_RegisterStatics() local CoalitionsData={GroupsRed=coalition.getStaticObjects(coalition.side.RED), GroupsBlue=coalition.getStaticObjects(coalition.side.BLUE), GroupsNeutral=coalition.getStaticObjects(coalition.side.NEUTRAL)} @@ -1036,11 +1038,6 @@ function DATABASE:_RegisterAirbases() text=text.."]" self:I(text) - -- Check for DCS bug IDs. - if airbaseID~=airbase:GetID() then - --self:E("WARNING: :getID does NOT match :GetID!") - end - end return self diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index ddc6b3e0c..5debc3a12 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -398,7 +398,7 @@ end -- @param #ZONE_BASE self -- @return #table Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value. function ZONE_BASE:GetColor() - return self.Color + return self.Color or {1, 0, 0, 0.15} end --- Get RGB color of zone. @@ -406,9 +406,10 @@ end -- @return #table Table with three entries, e.g. {1, 0, 0}, which is the RGB color code. function ZONE_BASE:GetColorRGB() local rgb={} - rgb[1]=self.Color[1] - rgb[2]=self.Color[2] - rgb[3]=self.Color[3] + local Color=self:GetColor() + rgb[1]=Color[1] + rgb[2]=Color[2] + rgb[3]=Color[3] return rgb end @@ -416,7 +417,8 @@ end -- @param #ZONE_BASE self -- @return #number Alpha value. function ZONE_BASE:GetColorAlpha() - local alpha=self.Color[4] + local Color=self:GetColor() + local alpha=Color[4] return alpha end @@ -443,7 +445,7 @@ end -- @param #ZONE_BASE self -- @return #table Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value. function ZONE_BASE:GetFillColor() - return self.FillColor + return self.FillColor or {1, 0, 0, 0.15} end --- Get RGB fill color of zone. @@ -451,9 +453,10 @@ end -- @return #table Table with three entries, e.g. {1, 0, 0}, which is the RGB color code. function ZONE_BASE:GetFillColorRGB() local rgb={} - rgb[1]=self.FillColor[1] - rgb[2]=self.FillColor[2] - rgb[3]=self.FillColor[3] + local FillColor=self:GetFillColor() + rgb[1]=FillColor[1] + rgb[2]=FillColor[2] + rgb[3]=FillColor[3] return rgb end @@ -461,7 +464,8 @@ end -- @param #ZONE_BASE self -- @return #number Alpha value. function ZONE_BASE:GetFillColorAlpha() - local alpha=self.FillColor[4] + local FillColor=self:GetFillColor() + local alpha=FillColor[4] return alpha end @@ -1683,7 +1687,7 @@ end --- @type ZONE_POLYGON_BASE --- --@field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}. +-- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}. -- @extends #ZONE_BASE @@ -2387,8 +2391,8 @@ do -- ZONE_ELASTIC --- Update the convex hull of the polygon. -- This uses the [Graham scan](https://en.wikipedia.org/wiki/Graham_scan). -- @param #ZONE_ELASTIC self - -- @field #number Delay Delay in seconds before the zone is updated. Default 0. - -- @field #boolean Draw Draw the zone. Default `nil`. + -- @param #number Delay Delay in seconds before the zone is updated. Default 0. + -- @param #boolean Draw Draw the zone. Default `nil`. -- @return #ZONE_ELASTIC self function ZONE_ELASTIC:Update(Delay, Draw) @@ -2420,6 +2424,7 @@ do -- ZONE_ELASTIC end end + return self end --- Start the updating scheduler. @@ -2427,7 +2432,7 @@ do -- ZONE_ELASTIC -- @param #number Tstart Time in seconds before the updating starts. -- @param #number dT Time interval in seconds between updates. Default 60 sec. -- @param #number Tstop Time in seconds after which the updating stops. Default `nil`. - -- @field #boolean Draw Draw the zone. Default `nil`. + -- @param #boolean Draw Draw the zone. Default `nil`. -- @return #ZONE_ELASTIC self function ZONE_ELASTIC:StartUpdate(Tstart, dT, Tstop, Draw) diff --git a/Moose Development/Moose/Functional/Fox.lua b/Moose Development/Moose/Functional/Fox.lua index 6624c352e..55b6e8548 100644 --- a/Moose Development/Moose/Functional/Fox.lua +++ b/Moose Development/Moose/Functional/Fox.lua @@ -792,7 +792,7 @@ function FOX:onafterMissileLaunch(From, Event, To, missile) local text=string.format("Missile launch detected! Distance %.1f NM, bearing %03d°.", UTILS.MetersToNM(distance), bearing) -- Say notching headings. - BASE:ScheduleOnce(5, FOX._SayNotchingHeadings, self, player, missile.weapon) + self:ScheduleOnce(5, FOX._SayNotchingHeadings, self, player, missile.weapon) --TODO: ALERT or INFO depending on whether this is a direct target. --TODO: lauchalertall option. @@ -1114,6 +1114,13 @@ end -- Event Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- FOX event handler for event birth. +-- @param #FOX self +-- @param Core.Event#EVENTDATA EventData +function FOX:OnEventPlayerEnterAircraft(EventData) + +end + --- FOX event handler for event birth. -- @param #FOX self -- @param Core.Event#EVENTDATA EventData @@ -1155,7 +1162,7 @@ function FOX:OnEventBirth(EventData) -- Add F10 radio menu for player. if not self.menudisabled then - SCHEDULER:New(nil, self._AddF10Commands, {self,_unitName}, 0.1) + self:ScheduleOnce(0.1, FOX._AddF10Commands, self, _unitname) end -- Player data. diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 2324577b8..c69e80f6a 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -1978,7 +1978,8 @@ function AIRBOSS:New( carriername, alias ) -- Init carrier parameters. if self.carriertype == AIRBOSS.CarrierType.STENNIS then - self:_InitStennis() + --self:_InitStennis() + self:_InitNimitz() elseif self.carriertype == AIRBOSS.CarrierType.ROOSEVELT then self:_InitNimitz() elseif self.carriertype == AIRBOSS.CarrierType.LINCOLN then diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 39e23ebc5..48755bd0a 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -51,6 +51,7 @@ -- @field Core.Spawn#SPAWN parkingGuard Parking guard spawner. -- @field #table holdingpatterns Holding points. -- @field #number hpcounter Counter for holding zones. +-- @field Sound.SRS#MSRSQUEUE msrsqueue Queue for TTS transmissions using MSRS class. -- @field Sound.SRS#MSRS msrsTower Moose SRS wrapper. -- @field Sound.SRS#MSRS msrsPilot Moose SRS wrapper. -- @field #number Tlastmessage Time stamp (abs.) of last radio transmission. @@ -326,7 +327,7 @@ FLIGHTCONTROL.FlightStatus={ --- FlightControl class version. -- @field #string version -FLIGHTCONTROL.version="0.7.0" +FLIGHTCONTROL.version="0.7.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -405,6 +406,9 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS) self:SetMarkHoldingPattern(true) self:SetRunwayRepairtime() + -- Init msrs queue. + self.msrsqueue=MSRSQUEUE:New(self.alias) + -- SRS for Tower. self.msrsTower=MSRS:New(PathToSRS, Frequency, Modulation) self:SetSRSTower() @@ -977,6 +981,7 @@ end -- @param #FLIGHTCONTROL self function FLIGHTCONTROL:onbeforeStatusUpdate() + --[[ if self.Tlastmessage then local Tnow=timer.getAbsTime() @@ -1001,6 +1006,21 @@ function FLIGHTCONTROL:onbeforeStatusUpdate() self:T2(self.lid..string.format("Last radio sent %d>%d sec ago. Status update allowed", dT, self.dTmessage)) end end + ]] + + local Tqueue=self.msrsqueue:CalcTransmisstionDuration() + + if Tqueue>0 then + -- Debug info. + local text=string.format("Still got %d messages in the radio queue. Will call status again in %.1f sec", #self.msrsqueue, Tqueue) + self:I(self.lid..text) + + -- Call status again in dt seconds. + self:__StatusUpdate(-Tqueue) + + -- Deny transition. + return false + end return true end @@ -3273,8 +3293,10 @@ function FLIGHTCONTROL:_PlayerAbortLanding(groupname) local flight=_DATABASE:GetOpsGroup(groupname) --Ops.FlightGroup#FLIGHTGROUP if flight then + + local flightstatus=self:GetFlightStatus(flight) - if flight:IsLanding() and self:IsControlling(flight) then + if (flight:IsLanding() or flightstatus==FLIGHTCONTROL.FlightStatus.LANDING) and self:IsControlling(flight) then -- Call sign. local callsign=self:_GetCallsignName(flight) @@ -3357,9 +3379,12 @@ function FLIGHTCONTROL:_PlayerRequestDirectLanding(groupname) self:TransmissionTower(text, flight, 10) else + + -- Runway. + local runway=self:GetActiveRunwayText() -- Message text. - local text=string.format("%s, affirmative! Confirm approach", callsign) + local text=string.format("%s, affirmative, runway %s. Confirm approach!", callsign, runway) -- Send message. self:TransmissionTower(text, flight, 10) @@ -4025,12 +4050,15 @@ function FLIGHTCONTROL:_CheckFlights() local onRunway=self:IsCoordinateRunway(coord) -- Debug output. - self:I(self.lid..string.format("Player %s speed %.1f knots (max=%.1f) onRunway=%s", playerElement.playerName, UTILS.MpsToKnots(speed), UTILS.MpsToKnots(self.speedLimitTaxi), tostring(onRunway))) + self:T(self.lid..string.format("Player %s speed %.1f knots (max=%.1f) onRunway=%s", playerElement.playerName, UTILS.MpsToKnots(speed), UTILS.MpsToKnots(self.speedLimitTaxi), tostring(onRunway))) if speed and speed>self.speedLimitTaxi and not onRunway then + -- Callsign. + local callsign=self:_GetCallsignName(flight) + -- Radio text. - local text="Slow down, you are taxiing too fast!" + local text=string.format("%s, slow down, you are taxiing too fast!", callsign) -- Radio message to player. self:TransmissionTower(text, flight) @@ -4227,18 +4255,20 @@ function FLIGHTCONTROL:TransmissionTower(Text, Flight, Delay) -- Spoken text. local text=self:_GetTextForSpeech(Text) - - -- Tower radio call. - self.msrsTower:PlayText(text, Delay) - + -- "Subtitle". + local subgroups=nil if Flight and not Flight.isAI then local playerData=Flight:_GetPlayerData() if playerData.subtitles then - self:TextMessageToFlight(Text, Flight, 5, false, Delay) + subgroups=subgroups or {} + table.insert(subgroups, Flight.group) end end + -- New transmission. + local transmission=self.msrsqueue:NewTransmission(text, nil, self.msrsTower, nil, 1, subgroups, Text) + -- Set time stamp. Can be in the future. self.Tlastmessage=timer.getAbsTime() + (Delay or 0) @@ -4256,29 +4286,35 @@ function FLIGHTCONTROL:TransmissionPilot(Text, Flight, Delay) -- Get player data. local playerData=Flight:_GetPlayerData() - + -- Check if player enabled his "voice". if playerData==nil or playerData.myvoice then -- Spoken text. local text=self:_GetTextForSpeech(Text) + + -- MSRS instance to use. + local msrs=self.msrsPilot if Flight.useSRS and Flight.msrs then -- Pilot radio call using settings of the FLIGHTGROUP. We just overwrite the frequency. - Flight.msrs:PlayTextExt(text, Delay, self.frequency, self.modulation, Gender, Culture, Voice, Volume, Label) - - else - - -- Pilot radio call using the default settings. - self.msrsPilot:PlayText(text, Delay) - - end - - -- "Subtitle". - if Flight and not Flight.isAI then - self:TextMessageToFlight(Text, Flight, 5, false, Delay) + msrs=Flight.msrs + end + + -- "Subtitle". + local subgroups=nil + if Flight and not Flight.isAI then + local playerData=Flight:_GetPlayerData() + if playerData.subtitles then + subgroups=subgroups or {} + table.insert(subgroups, Flight.group) + end + end + + -- Add transmission to msrsqueue. + self.msrsqueue:NewTransmission(text, nil, msrs, nil, 1, subgroups, Text, nil, self.frequency, self.modulation) end @@ -4439,7 +4475,7 @@ end --- Get text for text-to-speech. -- Numbers are spaced out, e.g. "Heading 180" becomes "Heading 1 8 0 ". -- @param #FLIGHTCONTROL self --- @param #string text +-- @param #string text Original text. -- @return #string Spoken text. function FLIGHTCONTROL:_GetTextForSpeech(text) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 6bc993d13..342b34cb0 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -576,10 +576,11 @@ end -- @param Ops.OpsGroup#OPSGROUP.Element Element (Optional) Only check status for given element. -- @return #boolean If true, flight is parking after spawned. function FLIGHTGROUP:IsParking(Element) + local is=self:Is("Parking") if Element then - return Element.status==OPSGROUP.ElementStatus.PARKING + is=Element.status==OPSGROUP.ElementStatus.PARKING end - return self:Is("Parking") + return is end --- Check if is taxiing to the runway. @@ -587,10 +588,11 @@ end -- @param Ops.OpsGroup#OPSGROUP.Element Element (Optional) Only check status for given element. -- @return #boolean If true, flight is taxiing after engine start up. function FLIGHTGROUP:IsTaxiing(Element) + local is=self:Is("Taxiing") if Element then - return Element.status==OPSGROUP.ElementStatus.TAXIING + is=Element.status==OPSGROUP.ElementStatus.TAXIING end - return self:Is("Taxiing") + return is end --- Check if flight is airborne or cruising. @@ -598,17 +600,19 @@ end -- @param Ops.OpsGroup#OPSGROUP.Element Element (Optional) Only check status for given element. -- @return #boolean If true, flight is airborne. function FLIGHTGROUP:IsAirborne(Element) + local is=self:Is("Airborne") or self:Is("Cruising") if Element then - return Element.status==OPSGROUP.ElementStatus.AIRBORNE + is=Element.status==OPSGROUP.ElementStatus.AIRBORNE end - return self:Is("Airborne") or self:Is("Cruising") + return is end --- Check if flight is airborne or cruising. -- @param #FLIGHTGROUP self -- @return #boolean If true, flight is airborne. function FLIGHTGROUP:IsCruising() - return self:Is("Cruising") + local is=self:Is("Cruising") + return is end --- Check if flight is landing. @@ -616,10 +620,11 @@ end -- @param Ops.OpsGroup#OPSGROUP.Element Element (Optional) Only check status for given element. -- @return #boolean If true, flight is landing, i.e. on final approach. function FLIGHTGROUP:IsLanding(Element) + local is=self:Is("Landing") if Element then - return Element.status==OPSGROUP.ElementStatus.LANDING + is=Element.status==OPSGROUP.ElementStatus.LANDING end - return self:Is("Landing") + return is end --- Check if flight has landed and is now taxiing to its parking spot. @@ -627,10 +632,11 @@ end -- @param Ops.OpsGroup#OPSGROUP.Element Element (Optional) Only check status for given element. -- @return #boolean If true, flight has landed function FLIGHTGROUP:IsLanded(Element) + local is=self:Is("Landed") if Element then - return Element.status==OPSGROUP.ElementStatus.LANDED + is=Element.status==OPSGROUP.ElementStatus.LANDED end - return self:Is("Landed") + return is end --- Check if flight has arrived at its destination parking spot. @@ -638,45 +644,51 @@ end -- @param Ops.OpsGroup#OPSGROUP.Element Element (Optional) Only check status for given element. -- @return #boolean If true, flight has arrived at its destination and is parking. function FLIGHTGROUP:IsArrived(Element) + local is=self:Is("Arrived") if Element then - return Element.status==OPSGROUP.ElementStatus.ARRIVED + is=Element.status==OPSGROUP.ElementStatus.ARRIVED end - return self:Is("Arrived") + return is end --- Check if flight is inbound and traveling to holding pattern. -- @param #FLIGHTGROUP self -- @return #boolean If true, flight is holding. function FLIGHTGROUP:IsInbound() - return self:Is("Inbound") + local is=self:Is("Inbound") + return is end --- Check if flight is holding and waiting for landing clearance. -- @param #FLIGHTGROUP self -- @return #boolean If true, flight is holding. function FLIGHTGROUP:IsHolding() - return self:Is("Holding") + local is=self:Is("Holding") + return is end --- Check if flight is going for fuel. -- @param #FLIGHTGROUP self -- @return #boolean If true, flight is refueling. function FLIGHTGROUP:IsGoing4Fuel() - return self:Is("Going4Fuel") + local is=self:Is("Going4Fuel") + return is end --- Check if helo(!) flight is ordered to land at a specific point. -- @param #FLIGHTGROUP self -- @return #boolean If true, group has task to land somewhere. function FLIGHTGROUP:IsLandingAt() - return self:Is("LandingAt") + local is=self:Is("LandingAt") + return is end --- Check if helo(!) flight has landed at a specific point. -- @param #FLIGHTGROUP self -- @return #boolean If true, has landed somewhere. function FLIGHTGROUP:IsLandedAt() - return self:Is("LandedAt") + is=self:Is("LandedAt") + return is end --- Check if flight is low on fuel. diff --git a/Moose Development/Moose/Sound/SRS.lua b/Moose Development/Moose/Sound/SRS.lua index 502a78ffc..0d91736c0 100644 --- a/Moose Development/Moose/Sound/SRS.lua +++ b/Moose Development/Moose/Sound/SRS.lua @@ -52,8 +52,6 @@ -- -- === -- --- ![Banner Image](..\Presentations\ATIS\ATIS_Main.png) --- -- # The MSRS Concept -- -- This class allows to broadcast sound files or text via Simple Radio Standalone (SRS). @@ -143,7 +141,7 @@ MSRS = { --- MSRS class version. -- @field #string version -MSRS.version="0.0.6" +MSRS.version="0.1.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -181,7 +179,7 @@ function MSRS:New(PathToSRS, Frequency, Modulation, Volume) self:SetCoalition() self:SetLabel() self:SetVolume() - self.lid = string.format("%s-%s | ",self.name,self.version) + self.lid = string.format("%s-%s | ", self.name, self.version) if not io or not os then self:E(self.lid.."***** ERROR - io or os NOT desanitized! MSRS will not work!") @@ -455,19 +453,8 @@ function MSRS:PlaySoundFile(Soundfile, Delay) -- Append file. command=command..' --file="'..tostring(soundfile)..'"' + -- Execute command. self:_ExecCommand(command) - - --[[ - - command=command.." > bla.txt" - - -- Debug output. - self:I(string.format("MSRS PlaySoundfile command=%s", command)) - - -- Execute SRS command. - local x=os.execute(command) - - ]] end @@ -493,16 +480,6 @@ function MSRS:PlaySoundText(SoundText, Delay) -- Execute command. self:_ExecCommand(command) - - --[[ - command=command.." > bla.txt" - - -- Debug putput. - self:I(string.format("MSRS PlaySoundfile command=%s", command)) - - -- Execute SRS command. - local x=os.execute(command) - ]] end @@ -538,6 +515,13 @@ end -- @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. +-- @param #string Gender Gender. +-- @param #string Culture Culture. +-- @param #string Voice Voice. +-- @param #number Volume Volume. +-- @param #string Label Label. -- @return #MSRS self function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label) @@ -765,5 +749,354 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Manages radio transmissions. +-- +-- The purpose of the MSRSQUEUE class is to manage SRS text-to-speech (TTS) messages using the MSRS class. +-- This can be used to submit multiple TTS messages and the class takes care that they are transmitted one after the other (and not overlapping). +-- +-- @type MSRSQUEUE +-- @field #string ClassName Name of the class "MSRSQUEUE". +-- @field #string lid ID for dcs.log. +-- @field #table queue The queue of transmissions. +-- @field #string alias Name of the radio queue. +-- @field #number dt Time interval in seconds for checking the radio queue. +-- @field #number Tlast Time (abs) when the last transmission finished. +-- @field #boolean checking If `true`, the queue update function is scheduled to be called again. +-- @extends Core.Base#BASE +MSRSQUEUE = { + ClassName = "MSRSQUEUE", + Debugmode = nil, + lid = nil, + queue = {}, + alias = nil, + dt = nil, + Tlast = nil, + checking = nil, +} + +--- Radio queue transmission data. +-- @type MSRSQUEUE.Transmission +-- @field #string text Text to be transmitted. +-- @field Sound.SRS#MSRS msrs MOOSE SRS object. +-- @field #number duration Duration in seconds. +-- @field #table subgroups Groups to send subtitle to. +-- @field #string subtitle Subtitle of the transmission. +-- @field #number subduration Duration of the subtitle being displayed. +-- @field #number frequency Frequency. +-- @field #number modulation Modulation. +-- @field #number Tstarted Mission time (abs) in seconds when the transmission started. +-- @field #boolean isplaying If true, transmission is currently playing. +-- @field #number Tplay Mission time (abs) in seconds when the transmission should be played. +-- @field #number interval Interval in seconds before next transmission. + +--- Create a new MSRSQUEUE object for a given radio frequency/modulation. +-- @param #MSRSQUEUE self +-- @param #string alias (Optional) Name of the radio queue. +-- @return #MSRSQUEUE self The MSRSQUEUE object. +function MSRSQUEUE:New(alias) + + -- Inherit base + local self=BASE:Inherit(self, BASE:New()) --#MSRSQUEUE + + self.alias=alias or "My Radio" + + self.dt=1.0 + + self.lid=string.format("MSRSQUEUE %s | ", self.alias) + + return self +end + +--- Clear the radio queue. +-- @param #MSRSQUEUE self +-- @return #MSRSQUEUE self The MSRSQUEUE object. +function MSRSQUEUE:Clear() + self:I(self.lid.."Clearning MSRSQUEUE") + self.queue={} + return self +end + + +--- Add a transmission to the radio queue. +-- @param #MSRSQUEUE self +-- @param #MSRSQUEUE.Transmission transmission The transmission data table. +-- @return #MSRSQUEUE self +function MSRSQUEUE:AddTransmission(transmission) + + -- Init. + transmission.isplaying=false + transmission.Tstarted=nil + + -- Add to queue. + table.insert(self.queue, transmission) + + -- Start checking. + if not self.checking then + self:_CheckRadioQueue() + end + + return self +end + +--- Create a new transmission and add it to the radio queue. +-- @param #MSRSQUEUE self +-- @param #string text Text to play. +-- @param #number duration Duration in seconds the file lasts. Default is determined by number of characters of the text message. +-- @param Sound.SRS#MSRS msrs MOOSE SRS object. +-- @param #number tstart Start time (abs) seconds. Default now. +-- @param #number interval Interval in seconds after the last transmission finished. +-- @param #table subgroups Groups that should receive the subtiltle. +-- @param #string subtitle Subtitle displayed when the message is played. +-- @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 modulation Radio modulation if other then MSRS default. +-- @return #MSRSQUEUE.Transmission Radio transmission table. +function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation) + + -- Sanity checks. + if not text then + self:E(self.lid.."ERROR: No text specified.") + return nil + end + if type(text)~="string" then + self:E(self.lid.."ERROR: Text specified is NOT a string.") + return nil + end + + + -- Create a new transmission object. + local transmission={} --#MSRSQUEUE.Transmission + transmission.text=text + transmission.duration=duration or STTS.getSpeechTime(text) + transmission.msrs=msrs + transmission.Tplay=tstart or timer.getAbsTime() + transmission.subtitle=subtitle + transmission.interval=interval or 0 + transmission.frequency=frequency + transmission.modulation=modulation + transmission.subgroups=subgroups + if transmission.subtitle then + transmission.subduration=subduration or transmission.duration + else + transmission.subduration=0 --nil + end + + -- Add transmission to queue. + self:AddTransmission(transmission) + + return transmission +end + +--- Broadcast radio message. +-- @param #MSRSQUEUE self +-- @param #MSRSQUEUE.Transmission transmission The transmission. +function MSRSQUEUE:Broadcast(transmission) + + if transmission.frequency then + transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, Gender, Culture, Voice, Volume, Label) + else + transmission.msrs:PlayText(transmission.text) + end + + local function texttogroup(gid) + -- Text to group. + trigger.action.outTextForGroup(gid, transmission.subtitle, transmission.subduration, true) + end + + if transmission.subgroups and #transmission.subgroups>0 then + + for _,_group in pairs(transmission.subgroups) do + local group=_group --Wrapper.Group#GROUP + + if group and group:IsAlive() then + local gid=group:GetID() + + self:ScheduleOnce(4, texttogroup, gid) + end + + end + + end + +end + +--- Calculate total transmission duration of all transmission in the queue. +-- @param #MSRSQUEUE self +-- @return #number Total transmission duration. +function MSRSQUEUE:CalcTransmisstionDuration() + + local Tnow=timer.getAbsTime() + + local T=0 + for _,_transmission in pairs(self.queue) do + local transmission=_transmission --#MSRSQUEUE.Transmission + + if transmission.isplaying then + + -- Playing for dt seconds. + local dt=Tnow-transmission.Tstarted + + T=T+transmission.duration-dt + + else + T=T+transmission.duration + end + + end + + return T +end + +--- Check radio queue for transmissions to be broadcasted. +-- @param #MSRSQUEUE self +-- @param #number delay Delay in seconds before checking. +function MSRSQUEUE:_CheckRadioQueue(delay) + + -- Transmissions in queue. + local N=#self.queue + + -- Debug info. + self:T2(self.lid..string.format("Check radio queue %s: delay=%.3f sec, N=%d, checking=%s", self.alias, delay or 0, N, tostring(self.checking))) + + if delay and delay>0 then + + -- Delayed call. + self:ScheduleOnce(delay, MSRSQUEUE._CheckRadioQueue, self) + + -- Checking on. + self.checking=true + + else + + -- Check if queue is empty. + if N==0 then + + -- Debug info. + self:T(self.lid..string.format("Check radio queue %s empty ==> disable checking", self.alias)) + + -- Queue is now empty. Nothing to else to do. We start checking again, if a transmission is added. + self.checking=false + + return + end + + -- Get current abs time. + local time=timer.getAbsTime() + + -- Checking on. + self.checking=true + + -- Set dt. + local dt=self.dt + + + local playing=false + local next=nil --#MSRSQUEUE.Transmission + local remove=nil + for i,_transmission in ipairs(self.queue) do + local transmission=_transmission --#MSRSQUEUE.Transmission + + -- Check if transmission time has passed. + if time>=transmission.Tplay then + + -- Check if transmission is currently playing. + if transmission.isplaying then + + -- Check if transmission is finished. + if time>=transmission.Tstarted+transmission.duration then + + -- Transmission over. + transmission.isplaying=false + + -- Remove ith element in queue. + remove=i + + -- Store time last transmission finished. + self.Tlast=time + + else -- still playing + + -- Transmission is still playing. + playing=true + + dt=transmission.duration-(time-transmission.Tstarted) + + end + + else -- not playing yet + + local Tlast=self.Tlast + + if transmission.interval==nil then + + -- Not playing ==> this will be next. + if next==nil then + next=transmission + end + + else + + if Tlast==nil or time-Tlast>=transmission.interval then + next=transmission + else + + end + end + + -- We got a transmission or one with an interval that is not due yet. No need for anything else. + if next or Tlast then + break + end + + end + + else + + -- Transmission not due yet. + + end + end + + -- Found a new transmission. + if next~=nil and not playing then + -- Debug info. + self:T(self.lid..string.format("Broadcasting text=\"%s\" at T=%.3f", next.text, time)) + + -- Call SRS. + self:Broadcast(next) + + next.isplaying=true + next.Tstarted=time + dt=next.duration + end + + -- Remove completed call from queue. + if remove then + -- Remove from queue. + table.remove(self.queue, remove) + N=N-1 + + -- Check if queue is empty. + if #self.queue==0 then + -- Debug info. + self:T(self.lid..string.format("Check radio queue %s empty ==> disable checking", self.alias)) + + self.checking=false + + return + end + end + + -- Check queue. + self:_CheckRadioQueue(dt) + + end + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + diff --git a/Moose Development/Moose/Utilities/FiFo.lua b/Moose Development/Moose/Utilities/FiFo.lua index b004de791..323cc7526 100644 --- a/Moose Development/Moose/Utilities/FiFo.lua +++ b/Moose Development/Moose/Utilities/FiFo.lua @@ -20,11 +20,11 @@ do -- @type FIFO -- @field #string ClassName Name of the class. -- @field #string lid Class id string for output to DCS log file. --- @field #string version Version of FiFo --- @field #number counter --- @field #number pointer --- @field #table stackbypointer --- @field #table stackbyid +-- @field #string version Version of FiFo. +-- @field #number counter Counter. +-- @field #number pointer Pointer. +-- @field #table stackbypointer Stack by pointer. +-- @field #table stackbyid Stack by ID. -- @extends Core.Base#BASE --- @@ -45,12 +45,12 @@ FIFO = { stackbyid = {} } ---- Instantiate a new FIFO Stack +--- Instantiate a new FIFO Stack. -- @param #FIFO self -- @return #FIFO self function FIFO:New() -- Inherit everything from BASE class. - local self=BASE:Inherit(self, BASE:New()) + local self=BASE:Inherit(self, BASE:New()) --#FIFO self.pointer = 0 self.counter = 0 self.stackbypointer = {} @@ -62,7 +62,7 @@ function FIFO:New() return self end ---- Empty FIFO Stack +--- Empty FIFO Stack. -- @param #FIFO self -- @return #FIFO self function FIFO:Clear() @@ -77,7 +77,7 @@ function FIFO:Clear() return self end ---- FIFO Push Object to Stack +--- FIFO Push Object to Stack. -- @param #FIFO self -- @param #table Object -- @param #string UniqueID (optional) - will default to current pointer + 1. Note - if you intend to use `FIFO:GetIDStackSorted()` keep the UniqueID numerical! @@ -97,7 +97,7 @@ function FIFO:Push(Object,UniqueID) return self end ---- FIFO Pull Object from Stack +--- FIFO Pull Object from Stack. -- @param #FIFO self -- @return #table Object or nil if stack is empty function FIFO:Pull() diff --git a/Moose Development/Moose/Utilities/STTS.lua b/Moose Development/Moose/Utilities/STTS.lua index dccbdd67b..f36a8e5e1 100644 --- a/Moose Development/Moose/Utilities/STTS.lua +++ b/Moose Development/Moose/Utilities/STTS.lua @@ -154,7 +154,7 @@ function STTS.getSpeechTime(length,speed,isGoogle) length = string.len(length) end - return math.ceil(length/cps) + return length/cps --math.ceil(length/cps) end --- Text to speech function. diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index ee493f3ec..157715061 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -1081,12 +1081,10 @@ function AIRBASE:_InitParkingSpots() -- Get client coordinates. local function isClient(coord) local clients=_DATABASE.CLIENTS - for clientname, client in pairs(clients) do - local template=_DATABASE:GetGroupTemplateFromUnitName(clientname) - local units=template.units - for i,unit in pairs(units) do - local Coord=COORDINATE:New(unit.x, unit.alt, unit.y) - local dist=Coord:Get2DDistance(coord) + for clientname, _client in pairs(clients) do + local client=_client --Wrapper.Client#CLIENT + if client and client.SpawnCoord then + local dist=client.SpawnCoord:Get2DDistance(coord) if dist<2 then return true, clientname end @@ -1743,16 +1741,6 @@ function AIRBASE:_InitRunways(IncludeInverse) return ((b.z - a.z)*(c.x - a.x) - (b.x - a.x)*(c.z - a.z)) > 0 end - --[[ - local a={x=1, y=0, z=0} - local A={x=0, y=0, z=0} - local b={x=0, y=0, z=1} - local c={x=0, y=0, z=-1} - local bl=isLeft(A, a, b) - local cl=isLeft(A, a, c) - env.info(string.format("b left=%s, c left=%s", tostring(bl), tostring(cl))) - ]] - for i,j in pairs(rpairs) do local ri=Runways[i] --#AIRBASE.Runway local rj=Runways[j] --#AIRBASE.Runway @@ -1769,13 +1757,6 @@ function AIRBASE:_InitRunways(IncludeInverse) local b=UTILS.VecSubstract(rj.center, ri.center) b=UTILS.VecAdd(ri.center, b) - --[[ - local ca=COORDINATE:NewFromVec3(a) - local cb=COORDINATE:NewFromVec3(b) - c0:ArrowToAll(ca, nil , {0,1,0}) - c0:ArrowToAll(cb, nil , {0,0,1}) - ]] - -- Check if rj is left of ri. local left=isLeft(c0, a, b) @@ -1992,7 +1973,7 @@ function AIRBASE:SetActiveRunwayLanding(Name, PreferLeft) end if runway then - self:I("Setting active runway for landing as "..self:GetRunwayName(runway)) + self:I(string.format("%s: Setting active runway for landing as %s", self.AirbaseName, self:GetRunwayName(runway))) else self:E("ERROR: Could not set the runway for landing!") end @@ -2040,7 +2021,7 @@ function AIRBASE:SetActiveRunwayTakeoff(Name, PreferLeft) end if runway then - self:I("Setting active runway for takeoff as "..self:GetRunwayName(runway)) + self:I(string.format("%s: Setting active runway for takeoff as %s", self.AirbaseName, self:GetRunwayName(runway))) else self:E("ERROR: Could not set the runway for takeoff!") end diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 445c655ee..529accdcf 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -13,6 +13,17 @@ --- The CLIENT class -- @type CLIENT +-- @field #string ClassName Name of the class. +-- @field #string ClientName Name of the client. +-- @field #string ClientBriefing Briefing. +-- @field #function ClientCallBack Callback function. +-- @field #table ClientParameters Parameters of the callback function. +-- @field #number ClientGroupID Group ID of the client. +-- @field #string ClientGroupName Group name. +-- @field #boolean ClientAlive Client alive. +-- @field #boolean ClientAlive2 Client alive 2. +-- @field #table Players Player table. +-- @field Core.Point#COORDINATE SpawnCoord Spawn coordinate from the template. -- @extends Wrapper.Unit#UNIT