AIRBOSS v0.3.5w

This commit is contained in:
funkyfranky 2018-11-26 16:35:35 +01:00
parent 58886dc42d
commit 6b24673b6f
3 changed files with 376 additions and 237 deletions

View File

@ -432,6 +432,7 @@ AIRBOSS.GroovePos={
-- @field #string onboard Onboard number. -- @field #string onboard Onboard number.
-- @field #string difficulty Difficulty level. -- @field #string difficulty Difficulty level.
-- @field #string step Coming pattern step. -- @field #string step Coming pattern step.
-- @field #boolean warning Set true once the player got a warning.
-- @field #number passes Number of passes. -- @field #number passes Number of passes.
-- @field #boolean attitudemonitor If true, display aircraft attitude and other parameters constantly. -- @field #boolean attitudemonitor If true, display aircraft attitude and other parameters constantly.
-- @field #table debrief Debrief analysis of the current step of this pass. -- @field #table debrief Debrief analysis of the current step of this pass.
@ -455,7 +456,7 @@ AIRBOSS.MenuF10={}
--- Airboss class version. --- Airboss class version.
-- @field #string version -- @field #string version
AIRBOSS.version="0.3.5" AIRBOSS.version="0.3.5w"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -1212,6 +1213,8 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
dist=-UTILS.NMToMeters(3) dist=-UTILS.NMToMeters(3)
aoa=8.1
elseif step==AIRBOSS.PatternStep.INITIAL then elseif step==AIRBOSS.PatternStep.INITIAL then
if hornet then if hornet then
@ -1226,8 +1229,10 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
if hornet then if hornet then
alt=UTILS.FeetToMeters(800) alt=UTILS.FeetToMeters(800)
speed=UTILS.KnotsToMps(350)
elseif skyhawk then elseif skyhawk then
alt=UTILS.FeetToMeters(600) alt=UTILS.FeetToMeters(600)
speed=UTILS.KnotsToMps(250)
end end
elseif step==AIRBOSS.PatternStep.EARLYBREAK then elseif step==AIRBOSS.PatternStep.EARLYBREAK then
@ -1971,6 +1976,7 @@ function AIRBOSS:_InitPlayer(playerData)
playerData.step=AIRBOSS.PatternStep.UNDEFINED playerData.step=AIRBOSS.PatternStep.UNDEFINED
playerData.groove={} playerData.groove={}
playerData.debrief={} playerData.debrief={}
playerData.warning=nil
playerData.holding=nil playerData.holding=nil
playerData.lig=false playerData.lig=false
playerData.patternwo=false playerData.patternwo=false
@ -2123,18 +2129,19 @@ end
--- Remove a flight from all queues. Also set player step to undefined if applicable. --- Remove a flight from all queues. Also set player step to undefined if applicable.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @param #AIRBOSS.Flightitem flight The flight to be removed. -- @param #AIRBOSS.PlayerData flight The flight to be removed.
function AIRBOSS:_RemoveFlight(flight) function AIRBOSS:_RemoveFlight(flight)
-- Remove flight from all queues. -- Remove flight from all queues.
self:_RemoveFlightFromQueue(self.Qmarshal, flight) self:_RemoveFlightFromQueue(self.Qmarshal, flight)
self:_RemoveFlightFromQueue(self.Qpattern, flight) self:_RemoveFlightFromQueue(self.Qpattern, flight)
-- Set Playerstep to undefined. -- Check if player or AI
if flight.player then if flight.player then
-- Set Playerstep to undefined.
flight.step=AIRBOSS.PatternStep.UNDEFINED flight.step=AIRBOSS.PatternStep.UNDEFINED
else else
-- Remove flight completely -- Remove AI flight completely.
self:_RemoveFlightFromQueue(self.flights, flight) self:_RemoveFlightFromQueue(self.flights, flight)
end end
@ -2668,10 +2675,10 @@ function AIRBOSS:_Commencing(playerData)
self:_InitPlayer(playerData) self:_InitPlayer(playerData)
-- Commence -- Commence
local text=string.format("Commencing. Case %d.", self.case) local text=string.format("Commencing. (Case %d)", self.case)
-- Message to player. -- Message to all players.
self:_SendMessageToPlayer(text, 10, playerData) self:MessageToAll(text, playerData.onboard, "", 5)
-- Next step: depends on case recovery. -- Next step: depends on case recovery.
if self.case==1 then if self.case==1 then
@ -2694,11 +2701,11 @@ function AIRBOSS:_Initial(playerData)
-- Inform player. -- Inform player.
local hint=string.format("Entering the pattern.") local hint=string.format("Entering the pattern.")
if playerData.difficulty==AIRBOSS.Difficulty.EASY then if playerData.difficulty==AIRBOSS.Difficulty.EASY then
hint=hint.."Aim for 800 feet and 350 kts at the break entry." hint=hint.."\nAim for 800 feet and 350 kts at the break entry."
end end
-- Send message. -- Send message.
self:_SendMessageToPlayer(hint, 10, playerData) self:MessageToPlayer(playerData, hint, "MARSHAL")
-- Next step: upwind. -- Next step: upwind.
playerData.step=AIRBOSS.PatternStep.UPWIND playerData.step=AIRBOSS.PatternStep.UPWIND
@ -2722,23 +2729,6 @@ function AIRBOSS:_Descent4k(playerData)
-- Check if we are in front of the boat (diffX > 0). -- Check if we are in front of the boat (diffX > 0).
if self:_CheckLimits(X, Z, self.Descent4k) then if self:_CheckLimits(X, Z, self.Descent4k) then
-- Get optimal altitude, distance and speed.
local altitude=self:_GetAircraftParameters(playerData)
-- TODO: only speed is checked here!
MESSAGE:New("Descent 4k step reached", 5):ToAllIf(self.Debug)
-- Get altitude.
local hint, debrief=self:_AltitudeCheck(playerData, altitude)
-- Message to player
self:_SendMessageToPlayer(hint, 10, playerData)
-- Debrief.
self:_AddToSummary(playerData, "Descent 4k", debrief)
-- Next step: Platform at 5k -- Next step: Platform at 5k
playerData.step=AIRBOSS.PatternStep.PLATFORM playerData.step=AIRBOSS.PatternStep.PLATFORM
end end
@ -2749,13 +2739,16 @@ end
-- @param #AIRBOSS.PlayerData playerData Player data table. -- @param #AIRBOSS.PlayerData playerData Player data table.
function AIRBOSS:_Platform(playerData) function AIRBOSS:_Platform(playerData)
-- Get distances between carrier and player unit (parallel and perpendicular to direction of movement of carrier) -- Check if player is in valid zone
local X, Z, rho, phi=self:_GetDistances(playerData.unit) local validzone=self:_GetCase23ValidZone()
-- Abort condition check. -- Check if we are inside the moving zone.
if self:_CheckAbort(X, Z, self.Platform) then local invalid=playerData.unit:IsNotInZone(validzone)
self:_AbortPattern(playerData, X, Z, self.Platform)
--return -- Issue warning.
if invalid and not playerData.warning then
self:MessageToPlayer(playerData, "You left the valid pattern zone!", "AIRBOSS")
playerData.warning=true
end end
-- Check if we are inside the moving zone. -- Check if we are inside the moving zone.
@ -2764,27 +2757,27 @@ function AIRBOSS:_Platform(playerData)
-- Check if we are in zone. -- Check if we are in zone.
if inzone then if inzone then
-- Debug message.
MESSAGE:New("Platform step reached", 5):ToAllIf(self.Debug) MESSAGE:New("Platform step reached", 5):ToAllIf(self.Debug)
-- Get optimal altitiude. -- Get optimal altitiude.
local altitude, aoa, distance, speed =self:_GetAircraftParameters(playerData) local altitude, aoa, distance, speed =self:_GetAircraftParameters(playerData)
--TODO: check speed. -- Get altitude hint.
local hintAlt=self:_AltitudeCheck(playerData, altitude)
-- Get altitude hint. -- Get altitude hint.
local hintAlt, debriefAlt=self:_AltitudeCheck(playerData, altitude) local hintSpeed=self:_SpeedCheck(playerData, speed)
-- Get altitude hint. -- Message to player.
local hintSpeed, debriefSpeed=self:_AltitudeCheck(playerData, altitude) if playerData.difficulty~=AIRBOSS.Difficulty.HARD then
local hint=string.format("%s\n%s\n%s", playerData.step, hintAlt, hintSpeed)
-- Message to player self:MessageToPlayer(playerData, hint, "MARSHAL", "")
self:_SendMessageToPlayer(hint, 10, playerData) end
-- Debrief.
--self:_AddToSummary(playerData, "Platform 5k", debrief)
-- Next step: Dirty up and level out at 1200 ft. -- Next step: Dirty up and level out at 1200 ft.
playerData.step=AIRBOSS.PatternStep.DIRTYUP playerData.step=AIRBOSS.PatternStep.DIRTYUP
playerData.warning=nil
end end
end end
@ -2793,13 +2786,16 @@ end
-- @param #AIRBOSS.PlayerData playerData Player data table. -- @param #AIRBOSS.PlayerData playerData Player data table.
function AIRBOSS:_DirtyUp(playerData) function AIRBOSS:_DirtyUp(playerData)
-- Get distances between carrier and player unit (parallel and perpendicular to direction of movement of carrier) -- Check if player is in valid zone
local X, Z, rho, phi=self:_GetDistances(playerData.unit) local validzone=self:_GetCase23ValidZone()
-- Abort condition check. -- Check if we are inside the moving zone.
if self:_CheckAbort(X, Z, self.DirtyUp) then local invalid=playerData.unit:IsNotInZone(validzone)
self:_AbortPattern(playerData, X, Z, self.DirtyUp)
--return -- Issue warning.
if invalid and not playerData.warning then
self:MessageToPlayer(playerData, "You left the valid pattern zone!", "AIRBOSS")
playerData.warning=true
end end
-- Check if we are inside the moving zone. -- Check if we are inside the moving zone.
@ -2808,21 +2804,24 @@ function AIRBOSS:_DirtyUp(playerData)
--if self:_CheckLimits(X, Z, self.DirtyUp) then --if self:_CheckLimits(X, Z, self.DirtyUp) then
if inzone then if inzone then
-- Debug message.
MESSAGE:New("Dirty up step reached", 5):ToAllIf(self.Debug) MESSAGE:New("Dirty up step reached", 5):ToAllIf(self.Debug)
--TODO: speed check
-- Get optimal altitiude. -- Get optimal altitiude.
local altitude=self:_GetAircraftParameters(playerData) local altitude, aoa, distance, speed=self:_GetAircraftParameters(playerData)
-- Get altitude. -- Get altitude hint.
local hint, debrief=self:_AltitudeCheck(playerData, altitude) local hintAlt, debrief=self:_AltitudeCheck(playerData, altitude)
-- Message to player -- Get speed hint.
self:_SendMessageToPlayer(hint, 10, playerData) -- TODO: Not sure if we already need to be onspeed AoA at this point?
local hintSpeed=self:_SpeedCheck(playerData, speed)
-- Debrief. -- Message to player.
self:_AddToSummary(playerData, "Dirty Up", debrief) if playerData.difficulty~=AIRBOSS.Difficulty.HARD then
local hint=string.format("%s\n%s\n%s", playerData.step, hintAlt, hintSpeed)
self:MessageToPlayer(playerData, hint, "MARSHAL", "")
end
-- Next step: -- Next step:
if self.case==2 then if self.case==2 then
@ -2832,6 +2831,7 @@ function AIRBOSS:_DirtyUp(playerData)
-- CASE III: Intercept glide slope and follow bullseye (ICLS). -- CASE III: Intercept glide slope and follow bullseye (ICLS).
playerData.step=AIRBOSS.PatternStep.BULLSEYE playerData.step=AIRBOSS.PatternStep.BULLSEYE
end end
playerData.warning=nil
end end
end end
@ -2840,13 +2840,16 @@ end
-- @param #AIRBOSS.PlayerData playerData Player data table. -- @param #AIRBOSS.PlayerData playerData Player data table.
function AIRBOSS:_Bullseye(playerData) function AIRBOSS:_Bullseye(playerData)
-- Get distances between carrier and player unit (parallel and perpendicular to direction of movement of carrier) -- Check if player is in valid zone
local X, Z, rho, phi=self:_GetDistances(playerData.unit) local validzone=self:_GetCase23ValidZone()
-- Abort condition check. -- Check if we are inside the moving zone.
if self:_CheckAbort(X, Z, self.Bullseye) then local invalid=playerData.unit:IsNotInZone(validzone)
self:_AbortPattern(playerData, X, Z, self.Bullseye)
--return -- Issue warning.
if invalid and not playerData.warning then
self:MessageToPlayer(playerData, "You left the valid pattern zone!", "AIRBOSS")
playerData.warning=true
end end
-- Check if we are inside the moving zone. -- Check if we are inside the moving zone.
@ -2856,24 +2859,27 @@ function AIRBOSS:_Bullseye(playerData)
--if self:_CheckLimits(X, Z, self.Bullseye) then --if self:_CheckLimits(X, Z, self.Bullseye) then
if inzone then if inzone then
-- Debug message.
MESSAGE:New("Bullseye step reached", 5):ToAllIf(self.Debug) MESSAGE:New("Bullseye step reached", 5):ToAllIf(self.Debug)
-- Get optimal altitiude. -- Get optimal altitiude.
local altitude=self:_GetAircraftParameters(playerData) local altitude, aoa, distance, speed=self:_GetAircraftParameters(playerData)
-- Get altitude. -- Get altitude hint.
local hint, debrief=self:_AltitudeCheck(playerData, altitude) local hintAlt=self:_AltitudeCheck(playerData, altitude)
-- Message to player -- Get altitude hint.
self:_SendMessageToPlayer(hint, 10, playerData) local hintAoA=self:_AoACheck(playerData, aoa)
-- Debrief. -- Message to player.
self:_AddToSummary(playerData, "Bullseye", debrief) if playerData.difficulty~=AIRBOSS.Difficulty.HARD then
local hint=string.format("%s\n%s\n%s", playerData.step, hintAlt, hintAoA)
self:MessageToPlayer(playerData, hint, "MARSHAL", "")
end
-- Next step: Final approach in the groove.
--playerData.step=AIRBOSS.PatternStep.FINAL
-- Next step: Groove Call the ball. -- Next step: Groove Call the ball.
playerData.step=AIRBOSS.PatternStep.GROOVE_XX playerData.step=AIRBOSS.PatternStep.GROOVE_XX
playerData.warning=nil
end end
end end
@ -2898,14 +2904,20 @@ function AIRBOSS:_Upwind(playerData)
-- Get optimal altitude, distance and speed. -- Get optimal altitude, distance and speed.
local alt, aoa, dist, speed=self:_GetAircraftParameters(playerData) local alt, aoa, dist, speed=self:_GetAircraftParameters(playerData)
-- Get altitude. -- Get altitude hint.
local hint, debrief=self:_AltitudeCheck(playerData, alt) local hintAlt=self:_AltitudeCheck(playerData, alt)
-- Message to player -- Get speed hint.
self:_SendMessageToPlayer(hint, 10, playerData) local hintSpeed=self:_AltitudeCheck(playerData, speed)
-- Message to player.
if playerData.difficulty~=AIRBOSS.Difficulty.HARD then
local hint=string.format("%s\n%s\n%s", playerData.step, hintAlt, hintSpeed)
self:MessageToPlayer(playerData, hint, "MARSHAL", "")
end
-- Debrief. -- Debrief.
self:_AddToSummary(playerData, "Entering the Break", debrief) --self:_AddToSummary(playerData, "Entering the Break", debrief)
-- Next step: Early Break. -- Next step: Early Break.
playerData.step=AIRBOSS.PatternStep.EARLYBREAK playerData.step=AIRBOSS.PatternStep.EARLYBREAK
@ -2943,8 +2955,11 @@ function AIRBOSS:_Break(playerData, part)
-- Grade altitude. -- Grade altitude.
local hint, debrief=self:_AltitudeCheck(playerData, altitude) local hint, debrief=self:_AltitudeCheck(playerData, altitude)
-- Send message to player. -- Message to player.
self:_SendMessageToPlayer(hint, 10, playerData) if playerData.difficulty~=AIRBOSS.Difficulty.HARD then
local hint=string.format("%s %s", playerData.step, hint)
self:MessageToPlayer(playerData, hint, "MARSHAL", "")
end
-- Debrief -- Debrief
if part=="early" then if part=="early" then
@ -2980,7 +2995,7 @@ function AIRBOSS:_CheckForLongDownwind(playerData)
self:RadioTransmission(self.LSOradio, self.radiocall.LONGINGROOVE) self:RadioTransmission(self.LSOradio, self.radiocall.LONGINGROOVE)
-- Debrief. -- Debrief.
self:_AddToSummary(playerData, "Downwind", "Long in the groove.") self:_AddToSummary(playerData, "Downwind", "Long in the groove - Pattern Wave Off!")
--grade="LIG PATTERN WAVE OFF - CUT 1 PT" --grade="LIG PATTERN WAVE OFF - CUT 1 PT"
playerData.lig=true playerData.lig=true
@ -3021,12 +3036,18 @@ function AIRBOSS:_Abeam(playerData)
-- Grade distance to carrier. -- Grade distance to carrier.
local hintDist, debriefDist=self:_DistanceCheck(playerData, dist) --math.abs(Z) local hintDist, debriefDist=self:_DistanceCheck(playerData, dist) --math.abs(Z)
-- Compile full hint. -- Paddles contact.
local hint=string.format("%s\n%s\n%s", hintAlt, hintAoA, hintDist) -- TODO: radio message.
local debrief=string.format("%s\n%s\n%s", debriefAlt, debriefAoA, debriefDist) self:MessageToPlayer(playerData, "Paddles, contact.", "LSO", "")
-- Send message to playerr. -- Message to player.
self:_SendMessageToPlayer(hint, 10, playerData) if playerData.difficulty~=AIRBOSS.Difficulty.HARD then
local hint=string.format("%s\n%s\n%s\n%s", playerData.step, hintAlt, hintAoA, hintDist)
self:MessageToPlayer(playerData, hint, "LSO", "")
end
-- Compile full hint.
local debrief=string.format("%s\n%s\n%s", debriefAlt, debriefAoA, debriefDist)
-- Add to debrief. -- Add to debrief.
self:_AddToSummary(playerData, "Abeam Position", debrief) self:_AddToSummary(playerData, "Abeam Position", debrief)
@ -3065,12 +3086,14 @@ function AIRBOSS:_Ninety(playerData)
-- Grade AoA. -- Grade AoA.
local hintAoA, debriefAoA=self:_AoACheck(playerData, aoa) local hintAoA, debriefAoA=self:_AoACheck(playerData, aoa)
-- Compile full hint.
local hint=string.format("%s\n%s", hintAlt, hintAoA)
local debrief=string.format("%s\n%s", debriefAlt, debriefAoA)
-- Message to player. -- Message to player.
self:_SendMessageToPlayer(hint, 10, playerData) if playerData.difficulty~=AIRBOSS.Difficulty.HARD then
local hint=string.format("%s\n%s\n%s", playerData.step, hintAlt, hintAoA)
self:MessageToPlayer(playerData, hint, "LSO", "")
end
-- Debrief.
local debrief=string.format("%s\n%s", debriefAlt, debriefAoA)
-- Add to debrief. -- Add to debrief.
self:_AddToSummary(playerData, "At the 90", debrief) self:_AddToSummary(playerData, "At the 90", debrief)
@ -3080,7 +3103,7 @@ function AIRBOSS:_Ninety(playerData)
elseif relheading>90 and self:_CheckLimits(X, Z, self.Wake) then elseif relheading>90 and self:_CheckLimits(X, Z, self.Wake) then
-- Message to player. -- Message to player.
self:_SendMessageToPlayer("You are already at the wake and have not passed the 90! Turn faster next time!", 10, playerData) self:MessageToPlayer(playerData, "You are already at the wake and have not passed the 90! Turn faster next time!", "LSO", "")
--TODO: pattern WO? --TODO: pattern WO?
end end
end end
@ -3111,12 +3134,14 @@ function AIRBOSS:_Wake(playerData)
-- Grade AoA. -- Grade AoA.
local hintAoA, debriefAoA=self:_AoACheck(playerData, aoa) local hintAoA, debriefAoA=self:_AoACheck(playerData, aoa)
-- Compile full hint.
local hint=string.format("%s\n%s", hintAlt, hintAoA)
local debrief=string.format("%s\n%s", debriefAlt, debriefAoA)
-- Message to player. -- Message to player.
self:_SendMessageToPlayer(hint, 10, playerData) if playerData.difficulty~=AIRBOSS.Difficulty.HARD then
local hint=string.format("%s\n%s\n%s", playerData.step, hintAlt, hintAoA)
self:MessageToPlayer(playerData, hint, "LSO", "")
end
-- Debrief.
local debrief=string.format("%s\n%s", debriefAlt, debriefAoA)
-- Add to debrief. -- Add to debrief.
self:_AddToSummary(playerData, "At the Wake", debrief) self:_AddToSummary(playerData, "At the Wake", debrief)
@ -3161,14 +3186,14 @@ function AIRBOSS:_Final(playerData)
-- AoA feed back -- AoA feed back
local hintAoA, debriefAoA=self:_AoACheck(playerData, aoa) local hintAoA, debriefAoA=self:_AoACheck(playerData, aoa)
-- Compile full hint.
local hint=string.format("%s\n%s", hintAlt, hintAoA)
local debrief=string.format("%s\n%s", debriefAlt, debriefAoA)
-- Message to player. -- Message to player.
self:_SendMessageToPlayer(hint, 10, playerData) if playerData.difficulty~=AIRBOSS.Difficulty.HARD then
local hint=string.format("%s\n%s\n%s", playerData.step, hintAlt, hintAoA)
self:MessageToPlayer(playerData, hint, "LSO", "")
end
-- Add to debrief. -- Add to debrief.
local debrief=string.format("%s\n%s", debriefAlt, debriefAoA)
self:_AddToSummary(playerData, "Enter Groove", debrief) self:_AddToSummary(playerData, "Enter Groove", debrief)
-- Gather pilot data. -- Gather pilot data.
@ -3266,7 +3291,8 @@ function AIRBOSS:_Groove(playerData)
elseif rho<=RIM and playerData.step==AIRBOSS.PatternStep.GROOVE_IM then elseif rho<=RIM and playerData.step==AIRBOSS.PatternStep.GROOVE_IM then
-- Debug. -- Debug.
self:_SendMessageToPlayer("IM", 5, playerData) local text=string.format("FF IM=%d", rho)
MESSAGE:New(text, 5):ToAllIf(self.Debug)
self:I(self.lid..string.format("FF IM=%d", rho)) self:I(self.lid..string.format("FF IM=%d", rho))
-- Store data. -- Store data.
@ -3281,8 +3307,9 @@ function AIRBOSS:_Groove(playerData)
if playerData.waveoff==false then if playerData.waveoff==false then
-- Debug -- Debug
self:_SendMessageToPlayer("IC", 5, playerData) local text=string.format("FF IC=%d", rho)
self:I(self.lid..string.format("FF IC=%d", rho)) MESSAGE:New(text, 5):ToAllIf(self.Debug)
self:I(self.lid..text)
-- Store data. -- Store data.
playerData.groove.IC=groovedata playerData.groove.IC=groovedata
@ -3311,8 +3338,9 @@ function AIRBOSS:_Groove(playerData)
elseif rho<=RAR and playerData.step==AIRBOSS.PatternStep.GROOVE_AR then elseif rho<=RAR and playerData.step==AIRBOSS.PatternStep.GROOVE_AR then
-- Debug. -- Debug.
self:_SendMessageToPlayer("AR", 8, playerData) local text=string.format("FF AR=%d", rho)
self:I(self.lid..string.format("FF AR=%d", rho)) MESSAGE:New(text, 5):ToAllIf(self.Debug)
self:I(self.lid..text)
-- Store data. -- Store data.
playerData.groove.AR=groovedata playerData.groove.AR=groovedata
@ -3435,14 +3463,16 @@ function AIRBOSS:_Trapped(playerData, X)
-- Info to player. -- Info to player.
local text=string.format("Trapped! %d-wire.", wire) local text=string.format("Trapped! %d-wire.", wire)
self:_SendMessageToPlayer(text, 10, playerData) self:MessageToPlayer(playerData, text, "LSO", "")
local text2=string.format("Distance X=%.1f meters resulted in a %d-wire estimate.", X, wire) -- Debug message.
MESSAGE:New(text,30):ToAllIf(self.Debug) local text=string.format("Distance X=%.1f meters resulted in a %d-wire estimate.", X, wire)
self:I(self.lid..text2) MESSAGE:New(text, 30):ToAllIf(self.Debug)
self:I(self.lid..text)
-- Debrief.
local hint = string.format("Trapped catching the %d-wire.", wire) local hint = string.format("Trapped catching the %d-wire.", wire)
self:_AddToSummary(playerData, "Recovered", hint) self:_AddToSummary(playerData, "Goove: IW", hint)
else else
--Boltered! --Boltered!
@ -3868,11 +3898,9 @@ function AIRBOSS:_LSOadvice(playerData, glideslopeError, lineupError)
text=text.."Unknown AoA state." text=text.."Unknown AoA state."
end end
-- Text not used.
text=text..string.format(" AoA = %.1f", aoa) text=text..string.format(" AoA = %.1f", aoa)
-- LSO Message to player.
--self:_SendMessageToPlayer(text, 5, playerData, false)
-- Set last time. -- Set last time.
playerData.Tlso=timer.getTime() playerData.Tlso=timer.getTime()
end end
@ -4266,13 +4294,13 @@ function AIRBOSS:_AltitudeCheck(playerData, altopt)
local hint local hint
if _error>badscore then if _error>badscore then
hint=string.format("You're high. ") hint=string.format("You're high.")
elseif _error>lowscore then elseif _error>lowscore then
hint= string.format("You're slightly high. ") hint= string.format("You're slightly high.")
elseif _error<-badscore then elseif _error<-badscore then
hint=string.format("You're low. ") hint=string.format("You're low. ")
elseif _error<-lowscore then elseif _error<-lowscore then
hint=string.format("You're slightly low. ") hint=string.format("You're slightly low.")
else else
hint=string.format("Good altitude. ") hint=string.format("Good altitude. ")
end end
@ -4315,15 +4343,15 @@ function AIRBOSS:_DistanceCheck(playerData, optdist)
local hint local hint
if _error>badscore then if _error>badscore then
hint=string.format("You're too far from the boat! ") hint=string.format("You're too far from the boat!")
elseif _error>lowscore then elseif _error>lowscore then
hint=string.format("You're slightly too far from the boat. ") hint=string.format("You're slightly too far from the boat.")
elseif _error<-badscore then elseif _error<-badscore then
hint=string.format( "You're too close to the boat! ") hint=string.format( "You're too close to the boat!")
elseif _error<-lowscore then elseif _error<-lowscore then
hint=string.format("You're slightly too far from the boat. ") hint=string.format("You're slightly too far from the boat.")
else else
hint=string.format("Perfect distance to the boat. ") hint=string.format("Good distance to the boat.")
end end
-- Extend or decrease depending on skill. -- Extend or decrease depending on skill.
@ -4583,10 +4611,7 @@ function AIRBOSS:RadioTransmission(radio, call, loud, delay)
radio:Broadcast(true) radio:Broadcast(true)
-- "Subtitle". -- "Subtitle".
for _,_player in pairs(self.players) do self:MessageToAll(call.subtitle, radio:GetAlias(), "", call.duration)
local playerData=_player --#AIRBOSS.PlayerData
self:_SendMessageToPlayer(call.subtitle, call.duration, playerData)
end
else else
@ -4604,34 +4629,6 @@ function AIRBOSS:RadioTransmission(radio, call, loud, delay)
end end
end end
--- Send message to player client.
-- @param #AIRBOSS self
-- @param #string message The message to send.
-- @param #number duration Display message duration.
-- @param #AIRBOSS.PlayerData playerData Player data.
-- @param #boolean clear If true, clear screen from previous messages.
-- @param #string sender The person who sends the message. Default is carrier alias.
-- @param #number delay Delay in seconds, before the message is send.
function AIRBOSS:_SendMessageToPlayer(message, duration, playerData, clear, sender, delay)
if playerData and message and message~="" then
-- Format message.
local text=string.format("%s, %s", playerData.callsign, message)
self:I(self.lid..text)
if delay and delay>0 then
SCHEDULER:New(nil, self._SendMessageToPlayer, {self, message, duration, playerData, clear, sender}, delay)
else
if playerData.client then
MESSAGE:New(text, duration, sender, clear):ToClient(playerData.client)
end
end
end
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
@ -4671,6 +4668,26 @@ function AIRBOSS:MessageToPlayer(playerData, message, sender, receiver, duration
end end
--- Send text message to all players in the CCA.
-- Message format will be "SENDER: RECCEIVER, MESSAGE".
-- @param #AIRBOSS self
-- @param #string message The message to send.
-- @param #string sender The person who sends the message or nil.
-- @param #string receiver The person who receives the message. Default player's onboard number. Set to "" for no receiver.
-- @param #number duration Display message duration. Default 10 seconds.
-- @param #boolean clear If true, clear screen from previous messages.
-- @param #number delay Delay in seconds, before the message is displayed.
function AIRBOSS:MessageToAll(message, sender, receiver, duration, clear, delay)
for _,_player in pairs(self.players) do
local player=_player --#AIRBOSS.PlayerData
if player.unit:IsInZone(self.zoneCCA) then
self:MessageToPlayer(player,message,sender,receiver,duration,clear,delay)
end
end
end
--- Check if aircraft is capable of landing on an aircraft carrier. --- Check if aircraft is capable of landing on an aircraft carrier.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @param Wrapper.Unit#UNIT unit Aircraft unit. (Will also work with groups as given parameter.) -- @param Wrapper.Unit#UNIT unit Aircraft unit. (Will also work with groups as given parameter.)

View File

@ -35,7 +35,9 @@
-- @field #number altitude Tanker orbit pattern altitude. -- @field #number altitude Tanker orbit pattern altitude.
-- @field #number distStern Race-track distance astern. -- @field #number distStern Race-track distance astern.
-- @field #number distBow Race-track distance bow. -- @field #number distBow Race-track distance bow.
-- @field #number dTupdate Time interval for updating pattern position wrt new tanker position. -- @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 dTupdate Minimum time interval in seconds before the next pattern update can happen.
-- @field #number Tupdate Last time the pattern was updated. -- @field #number Tupdate Last time the pattern was updated.
-- @field #number takeoff Takeoff type (cold, hot, air). -- @field #number takeoff Takeoff type (cold, hot, air).
-- @field #number lowfuel Low fuel threshold in percent. -- @field #number lowfuel Low fuel threshold in percent.
@ -58,11 +60,11 @@
-- --
-- # Simple Script -- # Simple Script
-- --
-- In the mission editor you have to set up a carrier unit, which will act as "mother". In the following, this unit will be named "USS Stennis". -- In the mission editor you have to set up a carrier unit, which will act as "mother". In the following, this unit will be named **"USS Stennis"**.
-- --
-- Secondly, you need to define a recovery tanker group in the mission editor and set it to "LATE ACTIVATED". The name of the group we'll use is "Texaco". -- Secondly, you need to define a recovery tanker group in the mission editor and set it to **"LATE ACTIVATED"**. The name of the group we'll use is **"Texaco"**.
-- --
-- The basic script is very simple and consists of only two lines. -- The basic script is very simple and consists of only two lines:
-- --
-- TexacoStennis=RECOVERYTANKER:New(UNIT:FindByName("USS Stennis"), "Texaco") -- TexacoStennis=RECOVERYTANKER:New(UNIT:FindByName("USS Stennis"), "Texaco")
-- TexacoStennis:Start() -- TexacoStennis:Start()
@ -78,11 +80,11 @@
-- --
-- Once the tanker runs out of fuel itself, it will return to the carrier and be respawned. -- Once the tanker runs out of fuel itself, it will return to the carrier and be respawned.
-- --
-- # Fine Tuning -- # Options and Fine Tuning
-- --
-- Several parameters can be customized by the mission designer. -- Several parameters can be customized by the mission designer.
-- --
-- ## Adjusting the Takeoff Type -- ## Takeoff Type
-- --
-- By default, the tanker is spawned with running engies on the carrier. The mission designer has set option to set the take off type via the @{#RECOVERYTANKER.SetTakeoff} function. -- By default, the tanker is spawned with running engies on the carrier. The mission designer has set option to set the take off type via the @{#RECOVERYTANKER.SetTakeoff} function.
-- Or via shortcuts -- Or via shortcuts
@ -104,9 +106,9 @@
-- If only the first spawning should happen on the carrier, one use the @{#RECOVERYTANKER.SetRespawnInAir}() function to command that all subsequent spawning -- If only the first spawning should happen on the carrier, one use the @{#RECOVERYTANKER.SetRespawnInAir}() function to command that all subsequent spawning
-- will happen in air. -- will happen in air.
-- --
-- If the helo should no be respawned at all, one can set @{#RECOVERYTANKER.SetRespawnOff}(). -- If the helo should not be respawned at all, one can set @{#RECOVERYTANKER.SetRespawnOff}().
-- --
-- ## Adjusting the Pattern -- ## Pattern Parameters
-- --
-- The racetrack pattern parameters can be fine tuned via the following functions: -- The racetrack pattern parameters can be fine tuned via the following functions:
-- --
@ -114,10 +116,69 @@
-- * @{#RECOVERYTANKER.SetSpeed}(*speed*), where *speed* is the pattern speed in knots. Default is 272 knots. -- * @{#RECOVERYTANKER.SetSpeed}(*speed*), where *speed* is the pattern speed in knots. Default is 272 knots.
-- * @{#RECOVERYTANKER.SetRacetrackDistances}(*distbow*, *diststern*), where *distbow* and *diststern* are the distances ahead and astern the boat, respectively. -- * @{#RECOVERYTANKER.SetRacetrackDistances}(*distbow*, *diststern*), where *distbow* and *diststern* are the distances ahead and astern the boat, respectively.
-- --
-- ## TACAN
--
-- A TACAN beacon for the tanker can be activated via scripting, i.e. no need to do this within the mission editor.
--
-- The beacon is create with the @{#RECOVERYTANKER.SetTACAN}(*channel*, *mode*, *morse*) function, where *channel* is the TACAN channel (a number), *mode* the TACAN mode (either "X"
-- or "Y") and *morse* a three letter string that is send as morse code to identify the tanker:
--
-- TexacoStennis:SetTACAN(10, "Y", "TKR")
--
-- will activate a TACAN beacon 10Y with more code "TKR".
--
-- If you do not set a TACAN beacon explicitly, it is automatically create on channel 1, mode "Y" and morse code "TKR".
--
-- In order to completely disable the TACAN beacon, you can use the @{#RECOVERYTANKER.SetTACANoff}() function in your script.
--
-- Note to self, I am not sure, if an AA TACAN station *must* be of mode "Y" in order to work. It seems that this was the case in earlier DCS versions.
--
-- ## Pattern Update
--
-- The pattern of the tanker is updated if at least one of the two following conditions apply:
--
-- * The aircraft carrier changes its position by more than ~10 km (see @{#RECOVERYTANKER.SetPatternUpdateDistance}) and/or
-- * The aircraft carrier changes its heading by more than 5 degrees (see @{#RECOVERYTANKER.SetPatternUpdateHeading})
--
-- **Note** that updating the pattern always leads to a small disruption in the perfect racetrack pattern of the tanker. This is because a new waypoint and new racetrack points
-- need to be set as DCS task. This is also the reason why the pattern is not contantly updated but rather when the position or heading of the carrier changes significantly.
--
-- The maximum update frequency is set to 15 minutes. You can adjust this by @{#RECOVERYTANKER.SetPatternUpdateInterval}.
--
-- # Finite State Model
--
-- The implementation uses a Finite State Model (FSM). This allows the mission designer to hook in to certain events.
--
-- * @{#RECOVERYTANKER.Start}: This event starts the FMS process and initialized parameters and spawns the tanker. DCS event handling is started.
-- * @{#RECOVERYTANKER.Status}: This event is called in regular intervals (~60 seconds) and checks the status of the tanker and carrier. It triggers other events if necessary.
-- * @{#RECOVERYTANKER.PatternUpdate}: This event commands the tanker to update its pattern
-- * @{#RECOVERYTANKER.RTB}: This events sends the tanker to its home base (usually the carrier). This is called once the tanker runs low on gas.
-- * @{#RECOVERYTANKER.RefuelStart}: This event is called when a tanker starts to refuel another unit.
-- * @{#RECOVERYTANKER.RefuelStop}: This event is called when a tanker stopped to refuel another unit.
-- * @{#RECOVERYTANKER.Run}: This event is called when the tanker resumes normal operations, e.g. after refueling stopped or tanker finished refueling.
-- * @{#RECOVERYTANKER.Stop}: This event stops the FSM by unhandling DCS events.
--
-- The mission designer can capture these events by RECOVERYTANKER.OnAfter*Eventname* functions, e.g. @{#RECOVERYTANKER.OnAfterPatternUpdate}.
--
-- # Debugging
--
-- In case you have problems, it is always a good idea to have a look at your DCS log file. You find it in your "Saved Games" folder, so for example in
-- C:\Users\<yourname>\Saved Games\DCS\Logs\dcs.log
-- All output concerning the @{#RECOVERYTANKER} class should have the string "RECOVERYTANKER" in the corresponding line.
-- Searching for lines that contain the string "error" or "nil" can also give you a hint what's wrong.
--
-- The verbosity of the output can be increased by adding the following lines to your script:
--
-- BASE:TraceOnOff(true)
-- BASE:TraceLevel(1)
-- BASE:TraceClass("RECOVERYTANKER")
--
-- To get even more output you can increase the trace level to 2 or even 3, c.f. @{Core.Base#BASE} for more details.
--
-- @field #RECOVERYTANKER -- @field #RECOVERYTANKER
RECOVERYTANKER = { RECOVERYTANKER = {
ClassName = "RECOVERYTANKER", ClassName = "RECOVERYTANKER",
Debug = true, Debug = false,
carrier = nil, carrier = nil,
carriertype = nil, carriertype = nil,
tankergroupname = nil, tankergroupname = nil,
@ -133,6 +194,8 @@ RECOVERYTANKER = {
distStern = nil, distStern = nil,
distBow = nil, distBow = nil,
dTupdate = nil, dTupdate = nil,
Dupdate = nil,
Hupdate = nil,
Tupdate = nil, Tupdate = nil,
takeoff = nil, takeoff = nil,
lowfuel = nil, lowfuel = nil,
@ -145,16 +208,18 @@ RECOVERYTANKER = {
--- Class version. --- Class version.
-- @field #string version -- @field #string version
RECOVERYTANKER.version="0.9.4" RECOVERYTANKER.version="0.9.4w"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Check if TACAN mode "X" is allowed for AA TACAN stations.
-- TODO: Check if tanker is going back to "Running" state after RTB and respawn.
-- TODO: Is alive check for tanker. -- TODO: Is alive check for tanker.
-- TODO: Trace functions self:T instead of self:I for less output. -- DONE: Write documenation.
-- TODO: Make pattern update parameters (distance, orientation) input parameters. -- DONE: Trace functions self:T instead of self:I for less output.
-- TODO: Write documenation. -- DONE: Make pattern update parameters (distance, orientation) input parameters.
-- DONE: Add FSM event for pattern update. -- DONE: Add FSM event for pattern update.
-- DONE: Smarter pattern update function. E.g. (small) zone around carrier. Only update position when carrier leaves zone or changes heading? -- DONE: Smarter pattern update function. E.g. (small) zone around carrier. Only update position when carrier leaves zone or changes heading?
-- DONE: Set AA TACAN. -- DONE: Set AA TACAN.
@ -191,7 +256,6 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname)
self.carrier:SetState(self.carrier, "RECOVERYTANKER", self) self.carrier:SetState(self.carrier, "RECOVERYTANKER", self)
-- Init default parameters. -- Init default parameters.
self:SetPatternUpdateInterval()
self:SetAltitude() self:SetAltitude()
self:SetSpeed() self:SetSpeed()
self:SetRacetrackDistances(6, 8) self:SetRacetrackDistances(6, 8)
@ -200,6 +264,9 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname)
self:SetLowFuelThreshold() self:SetLowFuelThreshold()
self:SetRespawnOnOff() self:SetRespawnOnOff()
self:SetTACAN() self:SetTACAN()
self:SetPatternUpdateDistance()
self:SetPatternUpdateHeading()
self:SetPatternUpdateInterval()
----------------------- -----------------------
--- FSM Transitions --- --- FSM Transitions ---
@ -211,7 +278,8 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname)
-- Add FSM transitions. -- Add FSM transitions.
-- From State --> Event --> To State -- From State --> Event --> To State
self:AddTransition("Stopped", "Start", "Running") -- Start the FSM. self:AddTransition("Stopped", "Start", "Running") -- Start the FSM.
self:AddTransition("*", "Refuel", "Refueling") -- Tanker starts to refuel. self:AddTransition("*", "RefuelStart", "Refueling") -- Tanker has started to refuel another unit.
self:AddTransition("*", "RefuelStop", "Running") -- Tanker starts to refuel.
self:AddTransition("*", "Run", "Running") -- Tanker starts normal operation again. self:AddTransition("*", "Run", "Running") -- Tanker starts normal operation again.
self:AddTransition("Running", "RTB", "Returning") -- Tanker is returning to base (for fuel). self:AddTransition("Running", "RTB", "Returning") -- Tanker is returning to base (for fuel).
self:AddTransition("*", "Status", "*") -- Status update. self:AddTransition("*", "Status", "*") -- Status update.
@ -229,23 +297,39 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname)
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
--- Triggers the FSM event "Refuel" when the tanker is refueling another aircraft. --- Triggers the FSM event "RefuelStart" when the tanker starts refueling another aircraft.
-- @function [parent=#RECOVERYTANKER] Refuel -- @function [parent=#RECOVERYTANKER] RefuelStart
-- @param #RECOVERYTANKER self -- @param #RECOVERYTANKER self
-- @param Wrapper.Unit#UNIT receiver Unit receiving fuel from the tanker. -- @param Wrapper.Unit#UNIT receiver Unit receiving fuel from the tanker.
--- Triggers delayed the FSM event "Refuel" when the tanker is refueling another aircraft. --- On after "RefuelStart" event user function. Called when a the the tanker started to refuel another unit.
-- @function [parent=#RECOVERYTANKER] __Refuel -- @function [parent=#RECOVERYTANKER] OnAfterRefuelStart
-- @param #RECOVERYTANKER self -- @param #RECOVERYTANKER self
-- @param #number delay Delay in seconds. -- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Unit#UNIT receiver Unit receiving fuel from the tanker. -- @param Wrapper.Unit#UNIT receiver Unit receiving fuel from the tanker.
--- Triggers the FSM event "Run". Simply puts the group into "Running" state, e.g. after refueling ended. --- Triggers the FSM event "RefuelStop" when the tanker stops refueling another aircraft.
-- @function [parent=#RECOVERYTANKER] RefuelStop
-- @param #RECOVERYTANKER self
-- @param Wrapper.Unit#UNIT receiver Unit stoped receiving fuel from the tanker.
--- On after "RefuelStop" event user function. Called when a the the tanker stopped to refuel another unit.
-- @function [parent=#RECOVERYTANKER] OnAfterRefuelStop
-- @param #RECOVERYTANKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Unit#UNIT receiver Unit that received fuel from the tanker.
--- Triggers the FSM event "Run". Simply puts the group into "Running" state.
-- @function [parent=#RECOVERYTANKER] Run -- @function [parent=#RECOVERYTANKER] Run
-- @param #RECOVERYTANKER self -- @param #RECOVERYTANKER self
--- Triggers delayed the FSM event "Run". Simply puts the group into "Running" state, e.g. after refueling ended. --- Triggers delayed the FSM event "Run". Simply puts the group into "Running" state.
-- @function [parent=#RECOVERYTANKER] __Run -- @function [parent=#RECOVERYTANKER] __Run
-- @param #RECOVERYTANKER self -- @param #RECOVERYTANKER self
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
@ -262,6 +346,14 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname)
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
-- @param Wrapper.Airbase#AIRBASE airbase The airbase where the tanker should return to. -- @param Wrapper.Airbase#AIRBASE airbase The airbase where the tanker should return to.
--- On after "RTB" event user function. Called when a the the tanker returns to its home base.
-- @function [parent=#RECOVERYTANKER] OnAfterPatternUpdate
-- @param #RECOVERYTANKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Airbase#AIRBASE airbase The airbase where the tanker should return to.
--- Triggers the FSM event "Status" that updates the tanker status. --- Triggers the FSM event "Status" that updates the tanker status.
-- @function [parent=#RECOVERYTANKER] Status -- @function [parent=#RECOVERYTANKER] Status
@ -282,6 +374,13 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname)
-- @param #RECOVERYTANKER self -- @param #RECOVERYTANKER self
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
--- On after "PatternEvent" event user function. Called when a the pattern of the tanker is updated.
-- @function [parent=#RECOVERYTANKER] OnAfterPatternUpdate
-- @param #RECOVERYTANKER self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Triggers the FSM event "Stop" that stops the recovery tanker. Event handlers are stopped. --- Triggers the FSM event "Stop" that stops the recovery tanker. Event handlers are stopped.
-- @function [parent=#RECOVERYTANKER] Stop -- @function [parent=#RECOVERYTANKER] Stop
@ -328,22 +427,39 @@ function RECOVERYTANKER:SetRacetrackDistances(distbow, diststern)
return self return self
end end
--- Set pattern update interval. Note that this update causes a slight disruption in the race track pattern. --- Set minimum pattern update interval. After a pattern update this time interval has to pass before the next update is allowed.
-- Therefore, the interval should be as long as possible but short enough to keep the tanker overhead the carrier.
-- @param #RECOVERYTANKER self -- @param #RECOVERYTANKER self
-- @param #number interval Interval in minutes. Default is every 30 minutes. -- @param #number interval Min interval in minutes. Default is 15 minutes.
-- @return #RECOVERYTANKER self -- @return #RECOVERYTANKER self
function RECOVERYTANKER:SetPatternUpdateInterval(interval) function RECOVERYTANKER:SetPatternUpdateInterval(interval)
self.dTupdate=(interval or 30)*60 self.dTupdate=(interval or 15)*60
return self
end
--- Set pattern update distance. Tanker will update its pattern when the carrier changes its position by more than this distance.
-- @param #RECOVERYTANKER self
-- @param #number distancechange Distance threshold in km. Default 9.62 km (= 5 NM).
-- @return #RECOVERYTANKER self
function RECOVERYTANKER:SetPatternUpdateDistance(distancechange)
self.Dupdate=(distancechange or 9.62)*1000
return self
end
--- Set pattern update heading. Tanker will update its pattern when the carrier changes its heading by more than this value.
-- @param #RECOVERYTANKER self
-- @param #number headingchange Heading threshold in degrees. Default 5 degrees.
-- @return #RECOVERYTANKER self
function RECOVERYTANKER:SetPatternUpdateHeading(headingchange)
self.Hupdate=headingchange or 5
return self return self
end end
--- Set low fuel state of tanker. When fuel is below this threshold, the tanker will RTB or be respawned if takeoff type is in air. --- Set low fuel state of tanker. When fuel is below this threshold, the tanker will RTB or be respawned if takeoff type is in air.
-- @param #RECOVERYTANKER self -- @param #RECOVERYTANKER self
-- @param #number threshold Low fuel threshold in percent. Default 10. -- @param #number fuelthreshold Low fuel threshold in percent. Default 10 %.
-- @return #RECOVERYTANKER self -- @return #RECOVERYTANKER self
function RECOVERYTANKER:SetLowFuelThreshold(threshold) function RECOVERYTANKER:SetLowFuelThreshold(fuelthreshold)
self.lowfuel=threshold or 10 self.lowfuel=fuelthreshold or 10
return self return self
end end
@ -381,7 +497,7 @@ function RECOVERYTANKER:SetTakeoffCold()
return self return self
end end
--- Set takeoff in air at pattern altitude 30 NM behind the carrier. --- Set takeoff in air at the defined pattern altitude and 20 NM astern the carrier.
-- @param #RECOVERYTANKER self -- @param #RECOVERYTANKER self
-- @return #RECOVERYTANKER self -- @return #RECOVERYTANKER self
function RECOVERYTANKER:SetTakeoffAir() function RECOVERYTANKER:SetTakeoffAir()
@ -389,7 +505,6 @@ function RECOVERYTANKER:SetTakeoffAir()
return self return self
end end
--- Enable respawning of tanker. Note that this is the default behaviour. --- Enable respawning of tanker. Note that this is the default behaviour.
-- @param #RECOVERYTANKER self -- @param #RECOVERYTANKER self
-- @return #RECOVERYTANKER self -- @return #RECOVERYTANKER self
@ -483,6 +598,13 @@ function RECOVERYTANKER:IsRefueling()
return self:is("Refueling") return self:is("Refueling")
end end
--- Check if FMS was stopped.
-- @param #RECOVERYTANKER self
-- @return #boolean If true, is stopped.
function RECOVERYTANKER:IsStopped()
return self:is("Stopped")
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM states -- FSM states
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -524,8 +646,6 @@ function RECOVERYTANKER:onafterStart(From, Event, To)
-- Spawn at coordinate. -- Spawn at coordinate.
self.tanker=Spawn:SpawnFromCoordinate(Carrier) self.tanker=Spawn:SpawnFromCoordinate(Carrier)
-- Initial route.
self:_InitRoute(15, 1)
else else
-- Check if an uncontrolled tanker group was requested. -- Check if an uncontrolled tanker group was requested.
@ -552,11 +672,11 @@ function RECOVERYTANKER:onafterStart(From, Event, To)
end end
end
-- Initialize route. -- Initialize route.
self:_InitRoute(15, 1) self:_InitRoute(15, 1)
end
-- Create tanker beacon. -- Create tanker beacon.
if self.TACANon then if self.TACANon then
self:_ActivateTACAN(2) self:_ActivateTACAN(2)
@ -566,7 +686,7 @@ function RECOVERYTANKER:onafterStart(From, Event, To)
self.orientation=self.carrier:GetOrientationX() self.orientation=self.carrier:GetOrientationX()
self.position=self.carrier:GetCoordinate() self.position=self.carrier:GetCoordinate()
-- Init status check. -- Init status updates in 10 seconds.
self:__Status(10) self:__Status(10)
end end
@ -584,7 +704,7 @@ function RECOVERYTANKER:onafterStatus(From, Event, To)
-- Get fuel of tanker. -- Get fuel of tanker.
local fuel=self.tanker:GetFuel()*100 local fuel=self.tanker:GetFuel()*100
local text=string.format("Recovery tanker %s: state=%s fuel=%.1f", self.tanker:GetName(), self:GetState(), fuel) local text=string.format("Recovery tanker %s: state=%s fuel=%.1f", self.tanker:GetName(), self:GetState(), fuel)
self:I(text) self:T(text)
-- Check if tanker is running and not RTBing or refueling. -- Check if tanker is running and not RTBing or refueling.
if self:IsRunning() then if self:IsRunning() then
@ -599,8 +719,8 @@ function RECOVERYTANKER:onafterStatus(From, Event, To)
if self.respawn then if self.respawn then
-- Debug message. -- Debug message.
local text=string.format("Respawning tanker %s.", self.tanker:GetName()) local text=string.format("Respawning recovery tanker %s in air.", self.tanker:GetName())
self:I(text) self:T(text)
-- Respawn tanker. -- Respawn tanker.
self.tanker:InitHeading(self.tanker:GetHeading()) self.tanker:InitHeading(self.tanker:GetHeading())
@ -643,7 +763,9 @@ function RECOVERYTANKER:onafterStatus(From, Event, To)
end end
-- Call status again in 1 minute. -- Call status again in 1 minute.
if not self:IsStopped() then
self:__Status(-60) self:__Status(-60)
end
end end
--- On after "PatternUpdate" event. Updates the racetrack pattern of the tanker wrt the carrier position. --- On after "PatternUpdate" event. Updates the racetrack pattern of the tanker wrt the carrier position.
@ -654,7 +776,7 @@ end
function RECOVERYTANKER:onafterPatternUpdate(From, Event, To) function RECOVERYTANKER:onafterPatternUpdate(From, Event, To)
-- Debug message. -- Debug message.
self:I(string.format("Updating recovery tanker %s orbit.", self.tanker:GetName())) self:T(string.format("Updating recovery tanker %s orbit.", self.tanker:GetName()))
-- Carrier heading. -- Carrier heading.
local hdg=self.carrier:GetHeading() local hdg=self.carrier:GetHeading()
@ -675,7 +797,6 @@ function RECOVERYTANKER:onafterPatternUpdate(From, Event, To)
p0:MarkToAll("Waypoint P0 " ..self.tanker:GetName()) p0:MarkToAll("Waypoint P0 " ..self.tanker:GetName())
p1:MarkToAll("Racetrack P1 "..self.tanker:GetName()) p1:MarkToAll("Racetrack P1 "..self.tanker:GetName())
p2:MarkToAll("Racetrack P2 "..self.tanker:GetName()) p2:MarkToAll("Racetrack P2 "..self.tanker:GetName())
self.tanker:SmokeRed()
end end
-- Waypoints array. -- Waypoints array.
@ -713,15 +834,15 @@ function RECOVERYTANKER:onafterRTB(From, Event, To, airbase)
airbase=airbase or self.airbase airbase=airbase or self.airbase
-- Debug message. -- Debug message.
local text=string.format("Tanker %s returning to airbase %s.", self.tanker:GetName(), airbase:GetName()) local text=string.format("Recoery tanker %s returning to airbase %s.", self.tanker:GetName(), airbase:GetName())
self:I(text) self:T(text)
-- Waypoint array. -- Waypoint array.
local wp={} local wp={}
-- Set landing waypoint. -- Set landing waypoint.
wp[1]=self.tanker:GetCoordinate():WaypointAirTurningPoint(nil, 300, {}, "Current Position") wp[1]=self.tanker:GetCoordinate():WaypointAirTurningPoint(nil, 300, {}, "Current Position")
wp[2]=airbase:GetCoordinate():WaypointAirLanding(300, airbase, nil, "Land at airbase") wp[2]=airbase:GetCoordinate():SetAltitude(500):WaypointAirLanding(300, airbase, nil, "Land at airbase")
-- Initialize WP and route tanker. -- Initialize WP and route tanker.
self.tanker:WayPointInitialize(wp) self.tanker:WayPointInitialize(wp)
@ -763,7 +884,7 @@ function RECOVERYTANKER:OnEventEngineShutdown(EventData)
if groupname:match(self.tankergroupname) then if groupname:match(self.tankergroupname) then
-- Debug info. -- Debug info.
self:I(string.format("Respawning recovery tanker group %s.", group:GetName())) self:T(string.format("Respawning recovery tanker group %s.", group:GetName()))
-- Respawn tanker. -- Respawn tanker.
self.tanker=group:RespawnAtCurrentAirbase() self.tanker=group:RespawnAtCurrentAirbase()
@ -799,10 +920,10 @@ function RECOVERYTANKER:_RefuelingStart(EventData)
end end
-- Info message. -- Info message.
self:I(string.format("Recovery tanker %s started refueling unit %s", self.tanker:GetName(), unit:GetName())) self:T(string.format("Recovery tanker %s started refueling unit %s", self.tanker:GetName(), unit:GetName()))
-- FMS state "Refueling". -- FMS state "Refueling".
self:Refuel(unit) self:RefuelStart(receiver)
end end
@ -827,10 +948,10 @@ function RECOVERYTANKER:_RefuelingStop(EventData)
end end
-- Info message. -- Info message.
self:I(string.format("Recovery tanker %s stopped refueling unit %s", self.tanker:GetName(), unit:GetName())) self:T(string.format("Recovery tanker %s stopped refueling unit %s", self.tanker:GetName(), unit:GetName()))
-- FSM state "Running". -- FSM state "Running".
self:Run() self:RefuelStop(unit)
end end
end end
@ -871,7 +992,7 @@ function RECOVERYTANKER:_InitRoute(dist, delay)
delay=delay or 1 delay=delay or 1
-- Debug message. -- Debug message.
self:I(string.format("Initializing route for recovery tanker %s.", self.tanker:GetName())) self:T(string.format("Initializing route for recovery tanker %s.", self.tanker:GetName()))
-- Carrier position. -- Carrier position.
local Carrier=self.carrier:GetCoordinate() local Carrier=self.carrier:GetCoordinate()
@ -902,6 +1023,9 @@ function RECOVERYTANKER:_InitRoute(dist, delay)
-- Set route. -- Set route.
self.tanker:Route(wp, delay) self.tanker:Route(wp, delay)
-- Set state to Running. Necessary when tanker was RTB and respawned since it is probably in state "Returning".
self:__Run(1)
-- No update yet, wait until the function is called (avoids checks if pattern update is needed). -- No update yet, wait until the function is called (avoids checks if pattern update is needed).
self.Tupdate=nil self.Tupdate=nil
end end
@ -920,7 +1044,7 @@ function RECOVERYTANKER:_CheckPatternUpdate(dt)
local vC=self.carrier:GetOrientationX() local vC=self.carrier:GetOrientationX()
-- Check if tanker is running and last updated is more than 10 minutes ago. -- Check if tanker is running and last updated is more than 10 minutes ago.
if self:IsRunning() and dt>10*60 then if self:IsRunning() and dt>self.dTupdate then
-- Last saved orientation of carrier. -- Last saved orientation of carrier.
local vP=self.orientation local vP=self.orientation
@ -932,19 +1056,17 @@ function RECOVERYTANKER:_CheckPatternUpdate(dt)
local rhdg=math.deg(math.acos(UTILS.VecDot(vC,vP)/UTILS.VecNorm(vC)/UTILS.VecNorm(vP))) local rhdg=math.deg(math.acos(UTILS.VecDot(vC,vP)/UTILS.VecNorm(vC)/UTILS.VecNorm(vP)))
-- Check if orientation changed. -- Check if orientation changed.
-- TODO: make 5 deg input variable. if math.abs(rhdg)>self.Hupdate then
if math.abs(rhdg)>5 then self:T(string.format("Carrier heading changed by %d degrees. Updating recovery tanker pattern.", rhdg))
self:I(string.format("Carrier heading changed by %d degrees. Updating recovery tanker pattern.", rhdg))
update=true update=true
end end
-- Get distance to saved position. -- Get distance to saved position.
local dist=pos:Get2DDistance(self.position) local dist=pos:Get2DDistance(self.position)
-- Check if carrier moved more than 10 km. -- Check if carrier moved more than ~10 km.
-- TODO: make 10 km input variable. if dist>self.Dupdate then
if dist/1000>10 then self:T(string.format("Carrier position changed by %.1f km. Updating recovery tanker pattern.", dist/1000))
self:I(string.format("Carrier position changed by %.1f km. Updating recovery tanker pattern.", dist/1000))
update=true update=true
end end
@ -979,7 +1101,7 @@ function RECOVERYTANKER:_ActivateTACAN(delay)
if unit:IsAlive() then if unit:IsAlive() then
-- Debug message. -- Debug message.
self:I(string.format("Activating recovery tanker TACAN beacon: channel=%d mode=%s, morse=%s.", self.TACANchannel, self.TACANmode, self.TACANmorse)) self:T(string.format("Activating recovery tanker TACAN beacon: channel=%d mode=%s, morse=%s.", self.TACANchannel, self.TACANmode, self.TACANmorse))
-- Create a new beacon and activate TACAN. -- Create a new beacon and activate TACAN.
self.beacon=BEACON:New(unit) self.beacon=BEACON:New(unit)

View File

@ -84,7 +84,7 @@
-- --
-- The implementation allows to customize quite a few settings easily -- The implementation allows to customize quite a few settings easily
-- --
-- ## Adjusting the Takeoff Type -- ## Takeoff Type
-- --
-- By default, the helo is spawned with running engies on the carrier. The mission designer has set option to set the take off type via the @{#RESCUEHELO.SetTakeoff} function. -- By default, the helo is spawned with running engies on the carrier. The mission designer has set option to set the take off type via the @{#RESCUEHELO.SetTakeoff} function.
-- Or via shortcuts -- Or via shortcuts
@ -108,7 +108,7 @@
-- --
-- If the helo should no be respawned at all, one can set @{#RESCUEHELO.SetRespawnOff}(). -- If the helo should no be respawned at all, one can set @{#RESCUEHELO.SetRespawnOff}().
-- --
-- ## Setting a Home Base -- ## Home Base
-- --
-- It is possible to define a "home base" other than the aircaft carrier. For example, one could imagine a strike group, and the helo will be spawned from -- It is possible to define a "home base" other than the aircaft carrier. For example, one could imagine a strike group, and the helo will be spawned from
-- another ship which has a helo pad. -- another ship which has a helo pad.
@ -123,7 +123,7 @@
-- Once the helo runs out of fuel, it will return to the USS Normandy and not the Stennis for respawning. -- Once the helo runs out of fuel, it will return to the USS Normandy and not the Stennis for respawning.
-- --
-- --
-- # Adjusting the Formation Positon -- ## Formation Positon
-- --
-- The position of the helo relative to the mother ship can be tuned via the functions -- The position of the helo relative to the mother ship can be tuned via the functions
-- --
@ -162,14 +162,14 @@ RESCUEHELO = {
--- Class version. --- Class version.
-- @field #string version -- @field #string version
RESCUEHELO.version="0.9.4" RESCUEHELO.version="0.9.4w"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Add option to stop carrier while rescue operation is in progress?
-- TODO: Write documenation. -- TODO: Write documenation.
-- TODO: Add option to stop carrier while rescue operation is in progress? Done but NOT working!
-- DONE: Add option to deactivate the rescueing. -- DONE: Add option to deactivate the rescueing.
-- DONE: Possibility to add already present/spawned aircraft, e.g. for warehouse. -- DONE: Possibility to add already present/spawned aircraft, e.g. for warehouse.
-- DONE: Add rescue event when aircraft crashes. -- DONE: Add rescue event when aircraft crashes.