From 5a327b1d6b8c99c84bfe86bfd92d9841cd57dc3a Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 6 Dec 2018 16:10:17 +0100 Subject: [PATCH] AIRBOSS v0.4.8w --- .../Moose/Functional/Artillery.lua | 2 +- Moose Development/Moose/Ops/Airboss.lua | 350 +++++++++++------- .../Moose/Ops/RecoveryTanker.lua | 94 +++-- 3 files changed, 285 insertions(+), 161 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 3c370915f..637560d03 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -2878,7 +2878,7 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) self.Controllable:ClearTasks() else - self:E(ARTY.id.."ERROR: No target in cease fire for group %s.", self.groupname) + self:E(ARTY.id..string.format("ERROR: No target in cease fire for group %s.", self.groupname)) end -- Set number of shots to zero. diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index d0ec72d92..7222b4c92 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -73,7 +73,11 @@ -- @field #AIRBOSS.Checkpoint Platform Case II/III descent at 2000 ft/min at 5000 ft platform. -- @field #AIRBOSS.Checkpoint DirtyUp Case II/III dirty up and on speed position at 1200 ft and 10-12 NM from the carrier. -- @field #AIRBOSS.Checkpoint Bullseye Case III intercept glideslope and follow ICLS aka "bullseye". --- @field #number case Recovery case I, II or III in progress. +-- @field #number defaultcase Default recovery case. This is the case used if not specified otherwise. +-- @field #number case Recovery case I, II or III currently in progress. +-- @field #table recoverytimes List of time windows when aircraft are recovered including the recovery case and holding offset. +-- @field #number defaultoffset Default holding pattern update if not specified otherwise. +-- @field #number holdingoffset Offset [degrees] of Case II/III holding pattern. -- @field #table flights List of all flights in the CCA. -- @field #table Qmarshal Queue of marshalling aircraft groups. -- @field #table Qpattern Queue of aircraft groups in the landing pattern. @@ -82,8 +86,6 @@ -- @field #number Nmaxpattern Max number of aircraft in landing pattern. -- @field Ops.RecoveryTanker#RECOVERYTANKER tanker Recovery tanker flying overhead of carrier. -- @field Functional.Warehouse#WAREHOUSE warehouse Warehouse object of the carrier. --- @field #table recoverytimes List of time windows when aircraft are recovered including the recovery case. --- @field #number holdingoffset Offset [degrees] of Case II/III holding pattern. Default 0 degrees. -- @extends Core.Fsm#FSM --- The boss! @@ -163,7 +165,11 @@ AIRBOSS = { Platform = {}, DirtyUp = {}, Bullseye = {}, - case = 1, + defaultcase = nil, + case = nil, + defaultoffset = nil, + holdingoffset = nil, + recoverytimes = {}, flights = {}, Qpattern = {}, Qmarshal = {}, @@ -172,8 +178,6 @@ AIRBOSS = { Nmaxpattern = nil, tanker = nil, warehouse = nil, - recoverytimes = {}, - holdingoffset = nil, } --- Player aircraft types capable of landing on carriers. @@ -235,9 +239,9 @@ AIRBOSS.CarrierType={ -- @type AIRBOSS.AircraftAoA -- @field #number OnSpeedMin Minimum on speed AoA. Values below are fast -- @field #number OnSpeedMax Maximum on speed AoA. Values above are slow. --- @field #number OnSpeed Optimal AoA. +-- @field #number OnSpeed Optimal on-speed AoA. -- @field #number Fast Fast AoA threshold. Smaller means faster. --- @field #number Slow Slow AoA threshold. Larger means slower +-- @field #number Slow Slow AoA threshold. Larger means slower. --- Pattern steps. -- @type AIRBOSS.PatternStep @@ -270,7 +274,7 @@ AIRBOSS.PatternStep={ } --- Radio sound file and subtitle. --- @type AIRBOSS.RadioSound +-- @type AIRBOSS.RadioCall -- @field #string file Sound file name without suffix. -- @field #string suffix File suffix/extention, e.g. "ogg". -- @field #boolean loud Loud version of sound file available. @@ -279,31 +283,39 @@ AIRBOSS.PatternStep={ --- LSO radio calls. -- @type AIRBOSS.LSOCall --- @field #AIRBOSS.RadioSound RIGHTFORLINEUP "Right for line up" call. --- @field #AIRBOSS.RadioSound COMELEFT "Come left" call. --- @field #AIRBOSS.RadioSound HIGH "You're high" call. --- @field #AIRBOSS.RadioSound LOW "You're low" call. --- @field #AIRBOSS.RadioSound POWER "Power" call. --- @field #AIRBOSS.RadioSound FAST "You're fast" call. --- @field #AIRBOSS.RadioSound SLOW "You're slow" call. --- @field #AIRBOSS.RadioSound PADDLESCONTACT "Paddles, contact" call. --- @field #AIRBOSS.RadioSound CALLTHEBALL "Call the Ball" --- @field #AIRBOSS.RadioSound ROGERBALL "Roger ball" call. --- @field #AIRBOSS.RadioSound WAVEOFF "Wafe off" call --- @field #AIRBOSS.RadioSound BOLTER "Bolter, Bolter" call --- @field #AIRBOSS.RadioSound LONGINGROOVE "You're long in the groove. Depart and re-enter." call. --- @field #AIRBOSS.RadioSound DEPARTANDREENTER "Depart and re-enter" call. --- @field #AIRBOSS.RadioSound N1 "One" call. --- @field #AIRBOSS.RadioSound N2 "Two" call. --- @field #AIRBOSS.RadioSound N3 "Three" call. --- @field #AIRBOSS.RadioSound N4 "Four" call. --- @field #AIRBOSS.RadioSound N5 "Five" call. --- @field #AIRBOSS.RadioSound N6 "Six" call. --- @field #AIRBOSS.RadioSound N7 "Seven" call. --- @field #AIRBOSS.RadioSound N8 "Eight" call. --- @field #AIRBOSS.RadioSound N9 "Nine" call. --- @field #AIRBOSS.RadioSound N0 "Zero" call. +-- @field #AIRBOSS.RadioCall RADIOCHECK "Paddles, radio check" call. +-- @field #AIRBOSS.RadioCall RIGHTFORLINEUP "Right for line up" call. +-- @field #AIRBOSS.RadioCall COMELEFT "Come left" call. +-- @field #AIRBOSS.RadioCall HIGH "You're high" call. +-- @field #AIRBOSS.RadioCall LOW "You're low" call. +-- @field #AIRBOSS.RadioCall POWER "Power" call. +-- @field #AIRBOSS.RadioCall FAST "You're fast" call. +-- @field #AIRBOSS.RadioCall SLOW "You're slow" call. +-- @field #AIRBOSS.RadioCall PADDLESCONTACT "Paddles, contact" call. +-- @field #AIRBOSS.RadioCall CALLTHEBALL "Call the Ball" +-- @field #AIRBOSS.RadioCall ROGERBALL "Roger ball" call. +-- @field #AIRBOSS.RadioCall WAVEOFF "Wafe off" call +-- @field #AIRBOSS.RadioCall BOLTER "Bolter, Bolter" call +-- @field #AIRBOSS.RadioCall LONGINGROOVE "You're long in the groove. Depart and re-enter." call. +-- @field #AIRBOSS.RadioCall DEPARTANDREENTER "Depart and re-enter" call. +-- @field #AIRBOSS.RadioCall N0 "Zero" call. +-- @field #AIRBOSS.RadioCall N1 "One" call. +-- @field #AIRBOSS.RadioCall N2 "Two" call. +-- @field #AIRBOSS.RadioCall N3 "Three" call. +-- @field #AIRBOSS.RadioCall N4 "Four" call. +-- @field #AIRBOSS.RadioCall N5 "Five" call. +-- @field #AIRBOSS.RadioCall N6 "Six" call. +-- @field #AIRBOSS.RadioCall N7 "Seven" call. +-- @field #AIRBOSS.RadioCall N8 "Eight" call. +-- @field #AIRBOSS.RadioCall N9 "Nine" call. AIRBOSS.LSOCall={ + RADIOCHECK={ + file="LSO-RadioCheck", + suffix="ogg", + louder=false, + subtitle="Paddles, radio check", + duration=1.1, + }, RIGHTFORLINEUP={ file="LSO-RightForLineup", suffix="ogg", @@ -402,13 +414,6 @@ AIRBOSS.LSOCall={ subtitle="Paddles, contact", duration=1.0, }, - RADIOCHECK={ - file="LSO-RadioCheck", - suffix="ogg", - louder=false, - subtitle="Paddles, radio check", - duration=1.1, - }, N0={ file="LSO-N0", suffix="ogg", @@ -483,16 +488,17 @@ AIRBOSS.LSOCall={ --- Marshal radio calls. -- @type AIRBOSS.MarshalCall --- @field #AIRBOSS.RadioSound N1 "One" call. --- @field #AIRBOSS.RadioSound N2 "Two" call. --- @field #AIRBOSS.RadioSound N3 "Three" call. --- @field #AIRBOSS.RadioSound N4 "Four" call. --- @field #AIRBOSS.RadioSound N5 "Five" call. --- @field #AIRBOSS.RadioSound N6 "Six" call. --- @field #AIRBOSS.RadioSound N7 "Seven" call. --- @field #AIRBOSS.RadioSound N8 "Eight" call. --- @field #AIRBOSS.RadioSound N9 "Nine" call. --- @field #AIRBOSS.RadioSound N0 "Zero" call. +-- @field #AIRBOSS.RadioCall RADIOCHECK "Marshal, radio check" call. +-- @field #AIRBOSS.RadioCall N0 "Zero" call. +-- @field #AIRBOSS.RadioCall N1 "One" call. +-- @field #AIRBOSS.RadioCall N2 "Two" call. +-- @field #AIRBOSS.RadioCall N3 "Three" call. +-- @field #AIRBOSS.RadioCall N4 "Four" call. +-- @field #AIRBOSS.RadioCall N5 "Five" call. +-- @field #AIRBOSS.RadioCall N6 "Six" call. +-- @field #AIRBOSS.RadioCall N7 "Seven" call. +-- @field #AIRBOSS.RadioCall N8 "Eight" call. +-- @field #AIRBOSS.RadioCall N9 "Nine" call. AIRBOSS.MarshalCall={ RADIOCHECK={ file="MARSHAL-RadioCheck", @@ -585,11 +591,12 @@ AIRBOSS.Difficulty={ HARD="TOPGUN Graduate", } ---- Recovery time. +--- Recovery window parameters. -- @type AIRBOSS.Recovery -- @field #number START Start of recovery in seconds of abs time. -- @field #number STOP End of recovery in seconds of abs time. -- @field #number CASE Recovery case (1-3) of that time slot. +-- @field #number OFFSET Angle offset of the holding pattern in degrees. Usually 0, +-15, or +-30 degrees. --- Groove position. -- @type AIRBOSS.GroovePos @@ -641,11 +648,6 @@ AIRBOSS.GroovePos={ -- @field #number LimitXmax Latitudal threshold for triggering the next step if X>Xmax. -- @field #number LimitZmin Latitudal threshold for triggering the next step if ZZmax. --- @field #number Altitude Optimal altitude at this point. --- @field #number AoA Optimal AoA at this point. --- @field #number Distance Optimal distance at this point. --- @field #number Speed Optimal speed at this point. --- @field #table Checklist Table of checklist text items to display at this point. --- Parameters of a flight group. -- @type AIRBOSS.Flightitem @@ -694,7 +696,7 @@ AIRBOSS.MenuF10={} --- Airboss class version. -- @field #string version -AIRBOSS.version="0.4.8" +AIRBOSS.version="0.4.8w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -706,7 +708,7 @@ AIRBOSS.version="0.4.8" -- TODO: Option to turn AI handling off. -- TODO: Check distance to players during approach. PWO if too close. -- TODO: Spin pattern. Add radio menu entry. Not sure what to add though?! --- TODO: Add radio check (LSO, AIRBOSS) to F10 radio menu. +-- DONE: Add radio check (LSO, AIRBOSS) to F10 radio menu. -- TODO: Add user functions. -- TODO: Generalize parameters for other carriers. -- TODO: Generalize parameters for other aircraft. @@ -799,11 +801,11 @@ function AIRBOSS:New(carriername, alias) -- Set max aircraft in landing pattern. self:SetMaxLandingPattern(2) - -- Set holding offset to 0 degrees. - self:SetHoldingOffsetAngle(15) - - -- Default recovery case. + -- Default recovery case. This sets self.defaultcase and self.case. self:SetRecoveryCase(1) + + -- Set holding offset to 0 degrees. This set self.defaultoffset and self.holdingoffset. + self:SetHoldingOffsetAngle(15) -- CCA 50 NM radius zone around the carrier. self:SetCarrierControlledArea() @@ -845,7 +847,7 @@ function AIRBOSS:New(carriername, alias) --[[ -- Init default sound files. for _name,_sound in pairs(AIRBOSS.LSOCall) do - local sound=_sound --#AIRBOSS.RadioSound + local sound=_sound --#AIRBOSS.RadioCall local text=string.format() sound.subtitle=1 sound.louder=1 @@ -901,12 +903,14 @@ function AIRBOSS:New(carriername, alias) -- @function [parent=#AIRBOSS] RecoveryStart -- @param #AIRBOSS self -- @param #number Case Recovery case (1, 2 or 3) that is started. + -- @param #number Offset Holding pattern offset angle in degrees for CASE II/III recoveries. --- Triggers the FSM delayed event "RecoveryStart" that starts the recovery of aircraft. Marshalling aircraft are send to the landing pattern. -- @function [parent=#AIRBOSS] __RecoveryStart + -- @param #number delay Delay in seconds. -- @param #AIRBOSS self -- @param #number Case Recovery case (1, 2 or 3) that is started. - -- @param #number delay Delay in seconds. + -- @param #number Offset Holding pattern offset angle in degrees for CASE II/III recoveries. --- Triggers the FSM event "RecoveryStop" that stops the recovery of aircraft. @@ -923,12 +927,14 @@ function AIRBOSS:New(carriername, alias) -- @function [parent=#AIRBOSS] RecoveryCase -- @param #AIRBOSS self -- @param #number Case The new recovery case (1, 2 or 3). + -- @param #number Offset Holding pattern offset angle in degrees for CASE II/III recoveries. --- Triggers the delayed FSM event "RecoveryCase" that sets the used aircraft recovery case. -- @function [parent=#AIRBOSS] __Case -- @param #AIRBOSS self -- @param #number delay Delay in seconds. -- @param #number Case The new recovery case (1, 2 or 3). + -- @param #number Offset Holding pattern offset angle in degrees for CASE II/III recoveries. --- Triggers the FSM event "Stop" that stops the airboss. Event handlers are stopped. @@ -975,14 +981,16 @@ function AIRBOSS:SetCarrierControlledZone(radius) return self end ---- Set recovery case pattern. +--- Set the default recovery case. -- @param #AIRBOSS self --- @param #number case Case of recovery. Either 1 or 3. Default 1. +-- @param #number case Case of recovery. Either 1, 2 or 3. Default 1. -- @return #AIRBOSS self function AIRBOSS:SetRecoveryCase(case) - self.case=case or 1 - + self.defaultcase=case or 1 + + self.case=self.defaultcase + return self end @@ -993,8 +1001,11 @@ end -- @return #AIRBOSS self function AIRBOSS:SetHoldingOffsetAngle(offset) - self.holdingoffset=offset or 0 + self.defaultoffset=offset or 0 + + self.holdingoffset=self.defaultoffset + return self end @@ -1002,9 +1013,10 @@ end -- @param #AIRBOSS self -- @param #string starttime Start time, e.g. "8:00" for eight o'clock. Default now. -- @param #string stoptime Stop time, e.g. "9:00" for nine o'clock. Default 90 minutes after start time. --- @param #number case Recovery case for that time slot. Number between one and three. Default 1. +-- @param #number case Recovery case for that time slot. Number between one and three. +-- @param #number holdingoffset Only for CASE II/III: Angle in degrees the holding pattern is offset. -- @return #AIRBOSS self -function AIRBOSS:AddRecoveryTime(starttime, stoptime, case) +function AIRBOSS:AddRecoveryTime(starttime, stoptime, case, holdingoffset) -- Set start time. local Tstart=UTILS.ClockToSeconds(starttime or UTILS.SecondsToClock(timer.getAbsTime())) @@ -1018,17 +1030,22 @@ function AIRBOSS:AddRecoveryTime(starttime, stoptime, case) return self end - -- Default is Case 1 recovery. - case=case or 1 + -- Case or default value. + case=case or self.defaultcase + + -- Holding offset or default value. + holdingoffset=holdingoffset or self.defaultoffset + -- Recovery window. - local rtime={} --#AIRBOSS.Recovery - rtime.START=Tstart - rtime.STOP=Tstop - rtime.CASE=case + local recovery={} --#AIRBOSS.Recovery + recovery.START=Tstart + recovery.STOP=Tstop + recovery.CASE=case + recovery.OFFSET=holdingoffset -- Add to table - table.insert(self.recoverytimes, rtime) + table.insert(self.recoverytimes, recovery) return self end @@ -1066,7 +1083,7 @@ end --- Set ICLS channel of carrier. -- @param #AIRBOSS self -- @param #number channel ICLS channel. Default 1. --- @param #string morsecode Morse code identifier. Three letters, e.g. "STN". +-- @param #string morsecode Morse code identifier. Three letters, e.g. "STN". Default "STN". -- @return #AIRBOSS self function AIRBOSS:SetICLS(channel, morsecode) @@ -1262,6 +1279,13 @@ function AIRBOSS:_CheckRecoveryTimes() text=" none!" end + -- Sort windows wrt to start time. + local _sort=function(a, b) return a.START1 then + text=text..string.format(" Holding offset angle %d degrees.", Offset) + end + MESSAGE:New(text, 20, self.alias):ToAllIf(self.Debug) + self:I(self.lid..text) -- Set new recovery case. self.case=Case + + -- Set holding offset. + self.holdingoffset=Offset end --- On after "RecoveryStart" event. Recovery of aircraft is started and carrier switches to state "Recovering". @@ -1339,14 +1398,26 @@ end -- @param #string Event Event. -- @param #string To To state. -- @param #number Case The recovery case (1, 2 or 3) to start. -function AIRBOSS:onafterRecoveryStart(From, Event, To, Case) +-- @param #number Offset Holding pattern offset angle in degrees for CASE II/III recoveries. +function AIRBOSS:onafterRecoveryStart(From, Event, To, Case, Offset) + + -- Input or default value. + Case=Case or self.defaultcase + + -- Input or default value. + Offset=Offset or self.defaultoffset -- Debug output. - self:I(self.lid..string.format("Starting aircraft recovery in case %d.", Case)) + local text=string.format("Starting aircraft recovery case %d.", Case) + if Case>1 then + text=text..string.format(" Holding offset angle %d degrees.", Offset) + end + MESSAGE:New(text, 20, self.alias):ToAllIf(self.Debug) + self:I(self.lid..text) -- Switch to case. - self:RecoveryCase(Case) - + self:RecoveryCase(Case, Offset) + end --- On after "RecoveryStop" event. Recovery of aircraft is stopped and carrier switches to state "Idle". @@ -1357,10 +1428,7 @@ end function AIRBOSS:onafterRecoveryStop(From, Event, To) -- Debug output. - self:I(self.lid..string.format("Stopping aircraft recovery.")) - - -- Switch to idle state. - self:Idle() + self:I(self.lid..string.format("Stopping aircraft recovery. Carrier goes to state idle.")) end @@ -1741,11 +1809,15 @@ function AIRBOSS:_CheckQueue() -- Time (last) flight has entered landing pattern. local Tpattern=9999 local npunits=1 + local pcase=1 if npattern>0 then -- Last flight group send to pattern. local patternflight=self.Qpattern[#self.Qpattern] --#AIRBOSS.Flightitem + -- Recovery case of pattern flight. + pcase=patternflight.case + -- Number of aircraft in this group. local npunits=patternflight.nunits @@ -1756,7 +1828,7 @@ function AIRBOSS:_CheckQueue() -- Min time in pattern before next aircraft is allowed. local TpatternMin - if self.case==1 then + if pcase==1 then TpatternMin=45*npunits -- 45 seconds interval per plane! else TpatternMin=120*npunits -- 120 seconds interval per plane! @@ -1781,6 +1853,7 @@ function AIRBOSS:_ScanCarrierZone() -- Carrier position. local coord=self:GetCoordinate() + -- Scan radius. local Rout=UTILS.NMToMeters(50) -- Scan units in carrier zone. @@ -1890,12 +1963,9 @@ function AIRBOSS:_MarshalPlayer(playerData) -- Check if flight is known to the airboss already. if playerData then - - -- Number of flight groups in stack. - local ngroups, nunits=self:_GetQueueInfo(self.Qmarshal, self.case) - - -- Assign next free stack to this flight. - local mystack=ngroups+1 + + -- Get free stack. + local mystack=self:_GetFreeStack(self.case) -- Add group to marshal stack. self:_AddMarshalGroup(playerData, mystack) @@ -1963,13 +2033,15 @@ function AIRBOSS:_MarshalAI(flight, nstack) -- Set up waypoints including collapsing the stack. for stack=nstack, 1, -1 do + -- TODO: skip stack 6 if recoverytanker (or at whatever angels the tanker orbits). + -- Get altitude and positions. local Altitude, p1, p2=self:_GetMarshalAltitude(stack) local p1=p1 --Core.Point#COORDINATE local Dist=p1:Get2DDistance(self:GetCoordinate()) - -- Orbit task. + -- Task: orbit at specified position, altitude and speed until flag=stack-1 local TaskOrbit=_taskorbit(p1, Altitude, Speed, stack-1, p2) -- Waypoint description. @@ -1981,7 +2053,7 @@ function AIRBOSS:_MarshalAI(flight, nstack) end -- Landing waypoint. - wp[#wp+1]=Carrier:WaypointAirLanding(Speed, self.airbase, nil, "Landing") + wp[#wp+1]=Carrier:SetAltitude(250):WaypointAirLanding(Speed, self.airbase, nil, "Landing") -- Reinit waypoints. group:WayPointInitialize(wp) @@ -2112,7 +2184,7 @@ function AIRBOSS:_CheckCollapseMarshalStack(flight) -- Hint for easy skill. if playerData.difficulty==AIRBOSS.Difficulty.EASY then - self:MessageToPlayer(flight, string.format("Use F10 radio menu \"Commence!\" command when you are ready!"), nil, "", 5) + self:MessageToPlayer(flight, string.format("Use F10 radio menu \"Request Commence\" command when ready!"), nil, "", 5) end end @@ -2147,6 +2219,7 @@ function AIRBOSS:_CollapseMarshalStack(flight, nopattern) if stack>0 and mstack>stack then -- Decrease stack/flag by one ==> AI will go lower. + -- TODO: If we include the recovery tanker, this needs to be generalized. mflight.flag:Set(mstack-1) -- Inform players. @@ -2216,16 +2289,32 @@ function AIRBOSS:_GetFreeStack(case) case=case or self.case -- Get stack - local stack + local nfull if case==1 then -- Lowest Case I stack. - stack=self:_GetQueueInfo(self.Qmarshal, 1) + nfull=self:_GetQueueInfo(self.Qmarshal, 1) else -- Lowest Case II or III stack. - stack=self:_GetQueueInfo(self.Qmarshal, 23) + nfull=self:_GetQueueInfo(self.Qmarshal, 23) end - return stack+1 + -- Get recovery tanker stack. + local tankerstack=9999 + if self.tanker and case==1 then + tankerstack=self:_GetAngels(self.tanker.altitude) + end + + local nfree + if nfull1 then -- "You're high!" self:RadioTransmission(self.LSOradio, AIRBOSS.LSOCall.HIGH, true) + advice=advice+AIRBOSS.LSOCall.HIGH.duration elseif glideslopeError>0.5 then -- "You're a little high." self:RadioTransmission(self.LSOradio, AIRBOSS.LSOCall.HIGH, false) + advice=advice+AIRBOSS.LSOCall.HIGH.duration elseif glideslopeError<-1.0 then -- "Power!" self:RadioTransmission(self.LSOradio, AIRBOSS.LSOCall.POWER, true) + advice=advice+AIRBOSS.LSOCall.POWER.duration elseif glideslopeError<-0.5 then -- "You're a little low." self:RadioTransmission(self.LSOradio, AIRBOSS.LSOCall.POWER, false) + advice=advice+AIRBOSS.LSOCall.POWER.duration else text="Good altitude." end @@ -4774,15 +4866,19 @@ function AIRBOSS:_LSOadvice(playerData, glideslopeError, lineupError) if lineupError<-3 then -- "Come left!" self:RadioTransmission(self.LSOradio, AIRBOSS.LSOCall.COMELEFT, true) + advice=advice+AIRBOSS.LSOCall.COMELEFT.duration elseif lineupError<-1 then -- "Come left." - self:RadioTransmission(self.LSOradio, AIRBOSS.LSOCall.COMELEFT, false) + self:RadioTransmission(self.LSOradio, AIRBOSS.LSOCall.COMELEFT, false) + advice=advice+AIRBOSS.LSOCall.COMELEFT.duration elseif lineupError>3 then -- "Right for lineup!" - self:RadioTransmission(self.LSOradio, AIRBOSS.LSOCall.RIGHTFORLINEUP, true) + self:RadioTransmission(self.LSOradio, AIRBOSS.LSOCall.RIGHTFORLINEUP, true) + advice=advice+AIRBOSS.LSOCall.RIGHTFORLINEUP.duration elseif lineupError>1 then -- "Right for lineup." self:RadioTransmission(self.LSOradio, AIRBOSS.LSOCall.RIGHTFORLINEUP, false) + advice=advice+AIRBOSS.LSOCall.RIGHTFORLINEUP.duration else text=text.."Good lineup." end @@ -4798,18 +4894,22 @@ function AIRBOSS:_LSOadvice(playerData, glideslopeError, lineupError) -- Rate aoa. if aoa>=aircraftaoa.Slow then -- "Your're slow!" - self:RadioTransmission(self.LSOradio, AIRBOSS.LSOCall.SLOW, true) + self:RadioTransmission(self.LSOradio, AIRBOSS.LSOCall.SLOW, true) + advice=advice+AIRBOSS.LSOCall.SLOW.duration elseif aoa>=aircraftaoa.OnSpeedMax and aoa=aircraftaoa.OnSpeedMin and aoa=aircraftaoa.Fast and aoabadscore then @@ -5613,15 +5713,13 @@ function AIRBOSS:_GetFuelState(unit) return UTILS.kg2lbs(fuelstate) end ---- Get altitude in angels. +--- Convert altitude from meters to angels (thousands of feet). -- @param #AIRBOSS self --- @param Wrapper.Unit#UNIT unit The unit for which the mass is determined. --- @return #number Altitude of unit in Anglels = thouthands of feet. -function AIRBOSS:_GetAngels(unit) +-- @param alt Alitude in meters. +-- @return #number Altitude in Anglels = thousands of feet using math.floor(). +function AIRBOSS:_GetAngels(alt) - local alt=unit:GetAltitude() - - local angels=math.floor(UTILS.MetersToFeet(alt))/1000 + local angels=math.floor(UTILS.MetersToFeet(alt)/1000) return angels end @@ -5777,7 +5875,7 @@ end -- @field #number prio Priority 0-100. -- @field #boolean isplaying Currently playing. -- @field Core.Beacon#RADIO radio Radio object. --- @field #AIRBOSS.RadioSound call Radio sound. +-- @field #AIRBOSS.RadioCall call Radio sound. --- Check radio queue for transmissions to be broadcasted. -- @param #AIRBOSS self @@ -5862,7 +5960,7 @@ end --- Add Radio transmission to radio queue -- @param #AIRBOSS self -- @param Core.Radio#RADIO radio sending transmission. --- @param #AIRBOSS.RadioSound call Radio sound files and subtitles. +-- @param #AIRBOSS.RadioCall call Radio sound files and subtitles. -- @param #boolean loud If true, play loud sound file version. -- @param #number delay Delay in seconds, before the message is broadcasted. function AIRBOSS:RadioTransmission(radio, call, loud, delay) @@ -5893,7 +5991,7 @@ end --- Transmission radio message. -- @param #AIRBOSS self -- @param Core.Radio#RADIO radio sending transmission. --- @param #AIRBOSS.RadioSound call Radio sound files and subtitles. +-- @param #AIRBOSS.RadioCall call Radio sound files and subtitles. -- @param #boolean loud If true, play loud sound file version. -- @param #number delay Delay in seconds, before the message is broadcasted. function AIRBOSS:RadioTransmit(radio, call, loud, delay) diff --git a/Moose Development/Moose/Ops/RecoveryTanker.lua b/Moose Development/Moose/Ops/RecoveryTanker.lua index e6babf046..827f24e1f 100644 --- a/Moose Development/Moose/Ops/RecoveryTanker.lua +++ b/Moose Development/Moose/Ops/RecoveryTanker.lua @@ -36,7 +36,8 @@ -- @field #number distStern Race-track distance astern. -- @field #number distBow Race-track distance bow. -- @field #number Dupdate Pattern update when carrier changes its position by more than this distance (meters). --- @field #number Hupdate Pattern update when carrier changes its heading by more than this number (degrees). +-- @field #number Hupdate Pattern update when carrier changes its heading by more than this number (degrees). +-- @field #boolean turning If true, carrier is turning. -- @field #number dTupdate Minimum time interval in seconds before the next pattern update can happen. -- @field #number Tupdate Last time the pattern was updated. -- @field #number takeoff Takeoff type (cold, hot, air). @@ -45,6 +46,7 @@ -- @field #boolean respawninair If true, tanker will always be respawned in air. This has no impact on the initial spawn setting. -- @field #boolean uncontrolledac If true, use and uncontrolled tanker group already present in the mission. -- @field DCS#Vec3 orientation Orientation of the carrier. Used to monitor changes and update the pattern if heading changes significantly. +-- @field DCS#Vec3 orientlast Orientation of the carrier for checking if carrier is currently turning. -- @field Core.Point#COORDINATE position Positon of carrier. Used to monitor if carrier significantly changed its position and then update the tanker pattern. -- @field Core.Zone#ZONE_UNIT zoneUpdate Moving zone relative to carrier. Each time the tanker is in this zone, its pattern is updated. -- @extends Core.Fsm#FSM @@ -197,6 +199,7 @@ RECOVERYTANKER = { dTupdate = nil, Dupdate = nil, Hupdate = nil, + turning = nil, Tupdate = nil, takeoff = nil, lowfuel = nil, @@ -204,13 +207,14 @@ RECOVERYTANKER = { respawninair = nil, uncontrolledac = nil, orientation = nil, + orientlast = nil, position = nil, zoneUpdate = nil, } --- Class version. -- @field #string version -RECOVERYTANKER.version="0.9.6" +RECOVERYTANKER.version="0.9.6w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -274,7 +278,7 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname) -- Moving zone: Zone 1 NM astern the carrier with radius of 1 NM. self.zoneUpdate=ZONE_UNIT:New("Pattern Update Zone", self.carrier, UTILS.NMToMeters(1), {dx=-UTILS.NMToMeters(1), dy=0, relative_to_unit=true}) - self.zoneUpdate:SmokeZone(SMOKECOLOR.White, 45) + --self.zoneUpdate:SmokeZone(SMOKECOLOR.White, 45) ----------------------- --- FSM Transitions --- @@ -691,7 +695,9 @@ function RECOVERYTANKER:onafterStart(From, Event, To) -- Get initial orientation and position of carrier. self.orientation=self.carrier:GetOrientationX() + self.orientlast=self.carrier:GetOrientationX() self.position=self.carrier:GetCoordinate() + self.turning=false -- Init status updates in 10 seconds. self:__Status(10) @@ -1098,45 +1104,65 @@ function RECOVERYTANKER:_CheckPatternUpdate(dt) -- Assume no update necessary. local update=false + + local Hchange=false + local Dchange=false + local turning=false -- Get current position and orientation of carrier. local pos=self.carrier:GetCoordinate() - local vC=self.carrier:GetOrientationX() + local vNew=self.carrier:GetOrientationX() - -- Check if tanker is running and last updated is more than 10 minutes ago. - if self:IsRunning() and dt>self.dTupdate then - -- Last saved orientation of carrier. - local vP=self.orientation - - -- We only need the X-Z plane. - vC.y=0 ; vP.y=0 - - -- Get angle between the two orientation vectors in rad. - local rhdg=math.deg(math.acos(UTILS.VecDot(vC,vP)/UTILS.VecNorm(vC)/UTILS.VecNorm(vP))) + -- Reference orientation of carrier after the last update + local vOld=self.orientation - -- Check if orientation changed. - if math.abs(rhdg)>self.Hupdate then - self:T(string.format("Carrier heading changed by %d degrees. Updating recovery tanker pattern.", rhdg)) - update=true - end - - -- Get distance to saved position. - local dist=pos:Get2DDistance(self.position) - - -- Check if carrier moved more than ~10 km. - if dist>self.Dupdate then - self:T(string.format("Carrier position changed by %.1f km. Updating recovery tanker pattern.", dist/1000)) - update=true - end - + -- Last orientation from 30 seconds ago. + local vLast=self.orientlast + + -- We only need the X-Z plane. + vNew.y=0 ; vOld.y=0 + + -- Get angle between old and new orientation vectors in rad and convert to degrees. + local deltaHeading=math.deg(math.acos(UTILS.VecDot(vNew,vOld)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vOld))) + + -- Angle between current heading and last time we checked ~30 seconds ago. + local deltaLast=math.deg(math.acos(UTILS.VecDot(vNew,vLast)/UTILS.VecNorm(vNew)/UTILS.VecNorm(vLast))) + + -- Last orientation becomes new orientation + self.orientlast=vNew + + -- Carrier is turning + local turning=deltaLast>=1 + + -- Check if orientation changed. + if math.abs(deltaHeading)>self.Hupdate then + self:T(string.format("Carrier heading changed by %d degrees. Turning=%s.", deltaHeading, tostring(turning))) + Hchange=true end - -- If pattern is updated then update orientation AND positon. - -- But only if last update is less then 10 minutes ago. - if update then - self.orientation=vC - self.position=pos + -- Get distance to saved position. + local dist=pos:Get2DDistance(self.position) + + -- Check if carrier moved more than ~10 km. + if dist>self.Dupdate then + self:T(string.format("Carrier position changed by %.1f km. Turning=%s.", dist/1000, tostring(turning))) + Dchange=true + end + + -- Assume no update necessary. + local update=false + + -- No update if currently turning! Also must be running (nor RTB or refuelling) and T>~10 min. + if self:IsRunning() and dt>self.dTupdate and not turning then + + -- Update if heading or distance changed. + if Hchange or Dchange then + self.orientation=vNew + self.position=pos + update=true + end + end return update