AIRBOSS v0.3.7

Zones.
This commit is contained in:
Frank 2018-11-27 23:37:48 +01:00
parent bd537ece00
commit 3d54d78be8

View File

@ -48,9 +48,6 @@
-- @field Core.Zone#ZONE_UNIT zoneCCA Carrier controlled area (CCA), i.e. a zone of 50 NM radius around the carrier.
-- @field Core.Zone#ZONE_UNIT zoneCCZ Carrier controlled zone (CCZ), i.e. a zone of 5 NM radius around the carrier.
-- @field Core.Zone#ZONE_UNIT zoneInitial Zone usually 3 NM astern of carrier where pilots start their CASE I pattern.
-- @field Core.Zone#ZONE_UNIT zonePlatform Zone astern the carrier where pilots should hit 5000 ft in CASE II/III.
-- @field Core.Zone#ZONE_UNIT zoneDirtyup Zone astern the carrier where pilots should hit 1200 ft and dirty up.
-- @field Core.Zone#ZONE_UNIT zoneBullseye Zone astern the carrier where pilots should intercept the glide slope.
-- @field #table players Table of players.
-- @field #table menuadded Table of units where the F10 radio menu was added.
-- @field #AIRBOSS.Checkpoint Upwind Upwind checkpoint.
@ -119,9 +116,6 @@ AIRBOSS = {
zoneCCA = nil,
zoneCCZ = nil,
zoneInitial = nil,
zonePlatform = nil,
zoneDirtyup = nil,
zoneBullseye = nil,
players = {},
menuadded = {},
Upwind = {},
@ -454,7 +448,7 @@ AIRBOSS.MenuF10={}
--- Airboss class version.
-- @field #string version
AIRBOSS.version="0.3.6"
AIRBOSS.version="0.3.7"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -583,14 +577,6 @@ function AIRBOSS:New(carriername, alias)
-- CASE I/II moving zone: Zone 3 NM astern and 100 m starboard of the carrier with radius of 0.5 km.
self.zoneInitial=ZONE_UNIT:New("Initial Zone", self.carrier, 0.5*1000, {dx=-UTILS.NMToMeters(3), dy=100, relative_to_unit=true})
-- CASE II/III moving zones.
local radial=180+self.carrierparam.rwyangle
local radius=UTILS.NMToMeters(1)
self.zonePlatform = ZONE_UNIT:New("Platform Zone", self.carrier, radius, {rho=UTILS.NMToMeters(19), theta=radial+self.holdingoffset, relative_to_unit=true})
self.zoneArcturn1 = ZONE_UNIT:New("ArcTurn1 Zone", self.carrier, radius, {rho=UTILS.NMToMeters(14), theta=radial+self.holdingoffset, relative_to_unit=true})
self.zoneArcturn2 = ZONE_UNIT:New("ArcTurn2 Zone", self.carrier, radius, {rho=UTILS.NMToMeters(12), theta=radial, relative_to_unit=true})
self.zoneDirtyup = ZONE_UNIT:New("DirtyUp Zone", self.carrier, radius, {rho=UTILS.NMToMeters( 9), theta=radial, relative_to_unit=true})
self.zoneBullseye = ZONE_UNIT:New("Bulleye Zone", self.carrier, radius, {rho=UTILS.NMToMeters( 3), theta=radial, relative_to_unit=true})
-- Smoke zones.
if self.Debug then
@ -598,7 +584,15 @@ function AIRBOSS:New(carriername, alias)
--self.zonePlatform:SmokeZone(SMOKECOLOR.Orange, 90)
--self.zoneDirtyup:SmokeZone(SMOKECOLOR.Blue, 90)
--self.zoneBullseye:SmokeZone(SMOKECOLOR.Red, 90)
--local zp=self:_GetCase23ValidZone():SmokeZone(SMOKECOLOR.Green, 45)
--self.zoneInitial:SmokeZone(SMOKECOLOR.White, 90)
local case=3
self:_GetZoneBullseye(case):SmokeZone(SMOKECOLOR.White, 45)
self:_GetZoneDirtyUp(case):SmokeZone(SMOKECOLOR.Orange, 45)
self:_GetZoneArcIn(case):SmokeZone(SMOKECOLOR.Blue, 45)
self:_GetZoneArcOut(case):SmokeZone(SMOKECOLOR.Blue, 45)
self:_GetZonePlatform(case):SmokeZone(SMOKECOLOR.Red, 45)
self:_GetZoneCorridor(case):SmokeZone(SMOKECOLOR.Green, 45)
end
-- Init default sound files.
@ -626,6 +620,7 @@ function AIRBOSS:New(carriername, alias)
self:AddTransition("*", "Idle", "Idle") -- Carrier is idleing.
self:AddTransition("Idle", "Recover", "Recovering") -- Recover aircraft.
self:AddTransition("*", "Status", "*") -- Update status of players and queues.
self:AddTransition("*", "Case", "*") -- Switch to another case recovery.
self:AddTransition("*", "Stop", "Stopped") -- Stop AIRBOSS script.
@ -659,6 +654,20 @@ function AIRBOSS:New(carriername, alias)
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Case" that switches the recovery case.
-- @function [parent=#AIRBOSS] Case
-- @param #AIRBOSS self
-- @param #number OldCase Old recovery case.
-- @param #number NewCase New recovery case.
--- Triggers the delayed FSM event "Case" that switches the recovery case
-- @function [parent=#AIRBOSS] __Case
-- @param #AIRBOSS self
-- @param #number delay Delay in seconds.
-- @param #number OldCase Old recovery case.
-- @param #number NewCase New recovery case.
--- Triggers the FSM event "Stop" that stops the airboss. Event handlers are stopped.
-- @function [parent=#AIRBOSS] Stop
-- @param #AIRBOSS self
@ -859,7 +868,7 @@ function AIRBOSS:IsIdle()
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM states
-- FSM event functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after Start event. Starts the warehouse. Addes event handlers and schedules status updates of reqests and queue.
@ -1011,6 +1020,44 @@ function AIRBOSS:_CheckRecoveryTimes()
end
--- On before "Case" event.
-- @param #AIRBOSS self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number OldCase The old (current) case.
-- @param #number NewCase The new case.
-- @return #boolean If true, switching to new case recovery is allowed.
function AIRBOSS:onbeforeCase(From, Event, To, OldCase, NewCase)
if NewCase==self.case then
-- Old=New ==> no switch necessary
return false
end
if NewCase<1 or NewCase>3 then
self:E(self.lid.."ERROR: new case is not 1, 2 or 3 but %s", tostring(NewCase))
return false
end
return true
end
--- On after "Case" event.
-- @param #AIRBOSS self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number OldCase The old (current) case.
-- @param #number NewCase The new case.
function AIRBOSS:onbeforeCase(From, Event, To, OldCase, NewCase)
self.case=NewCase
end
--- On before "Recover" event.
-- @param #AIRBOSS self
-- @param #string From From state.
@ -2632,52 +2679,253 @@ function AIRBOSS:_Holding(playerData)
self:MessageToPlayer(playerData, text, "AIRBOSS", nil, 5)
end
--- Get CASE II/III box zone.
--- Get Bullseye zone with radius 1 NM and DME 3 NM from the carrier. Radial depends on recovery case.
-- @param #AIRBOSS self
-- @param #number case Recovery case.
-- @return Core.Zone#ZONE_RADIUS Arc in zone.
function AIRBOSS:_GetZoneBullseye(case)
-- Radius = 1 NM.
local radius=UTILS.NMToMeters(1)
-- Distance = 3 NM
local distance=UTILS.NMToMeters(3)
-- Zone depends on Case recovery.
local radial
if case==2 then
radial=self:GetRadialCase2(false, false)
elseif case==3 then
radial=self:GetRadialCase3(false, false)
else
self:E(self.lid.."ERROR: Bullseye zone only for CASE II or III recoveries!")
return nil
end
-- Get coordinate and vec2.
local coord=self:GetCoordinate():Translate(distance, radial)
local vec2=coord:GetVec2()
-- Create zone.
local zone=ZONE_RADIUS:New("Zone Bullseye", vec2, radius)
return zone
end
--- Get dirty up zone with radius 1 NM and DME 9 NM from the carrier. Radial depends on recovery case.
-- @param #AIRBOSS self
-- @param #number case Recovery case.
-- @return Core.Zone#ZONE_RADIUS Arc in zone.
function AIRBOSS:_GetZoneDirtyUp(case)
-- Radius = 1 NM.
local radius=UTILS.NMToMeters(1)
-- Distance = 19 NM
local distance=UTILS.NMToMeters(9)
-- Zone depends on Case recovery.
local radial
if case==2 then
radial=self:GetRadialCase2(false, false)
elseif case==3 then
radial=self:GetRadialCase3(false, false)
else
self:E(self.lid.."ERROR: Dirty Up zone only for CASE II or III recoveries!")
return nil
end
-- Get coordinate and vec2.
local coord=self:GetCoordinate():Translate(distance, radial)
local vec2=coord:GetVec2()
-- Create zone.
local zone=ZONE_RADIUS:New("Zone Dirty Up", vec2, radius)
return zone
end
--- Get arc out zone with radius 1 NM and DME 12 NM from the carrier. Radial depends on recovery case.
-- @param #AIRBOSS self
-- @param #number case Recovery case.
-- @return Core.Zone#ZONE_RADIUS Arc in zone.
function AIRBOSS:_GetZoneArcOut(case)
-- Radius = 1 NM.
local radius=UTILS.NMToMeters(1)
-- Distance = 12 NM
local distance=UTILS.NMToMeters(12)
-- Zone depends on Case recovery.
local radial
if case==2 then
radial=self:GetRadialCase2(false, false)
elseif case==3 then
radial=self:GetRadialCase3(false, false)
else
self:E(self.lid.."ERROR: Arc out zone only for CASE II or III recoveries!")
return nil
end
-- Get coordinate and vec2.
local coord=self:GetCoordinate():Translate(distance, radial)
local vec2=coord:GetVec2()
-- Create zone.
local zone=ZONE_RADIUS:New("Zone Arc Out", vec2, radius)
return zone
end
--- Get arc in zone with radius 1 NM and DME 14 NM from the carrier. Radial depends on recovery case.
-- @param #AIRBOSS self
-- @param #number case Recovery case.
-- @return Core.Zone#ZONE_RADIUS Arc in zone.
function AIRBOSS:_GetZoneArcIn(case)
-- Radius = 1 NM.
local radius=UTILS.NMToMeters(1)
-- Zone depends on Case recovery.
local radial
if case==2 then
radial=self:GetRadialCase2(false, true)
elseif case==3 then
radial=self:GetRadialCase3(false, true)
else
self:E(self.lid.."ERROR: Arc in zone only for CASE II or III recoveries!")
return nil
end
-- Distance = 14 NM
local distance=UTILS.NMToMeters(12/math.cos(math.rad(self.holdingoffset)))
-- Get coordinate and vec2.
local coord=self:GetCoordinate():Translate(distance, radial)
local vec2=coord:GetVec2()
-- Create zone.
local zone=ZONE_RADIUS:New("Zone Arc In", vec2, radius)
return zone
end
--- Get platform zone with radius 1 NM and DME 19 NM from the carrier. Radial depends on recovery case.
-- @param #AIRBOSS self
-- @param #number case Recovery case.
-- @return Core.Zone#ZONE_RADIUS Circular platform zone.
function AIRBOSS:_GetZonePlatform(case)
-- Radius = 1 NM.
local radius=UTILS.NMToMeters(1)
-- Distance = 19 NM
local distance=UTILS.NMToMeters(19)
-- Zone depends on Case recovery.
local radial
if case==2 then
radial=self:GetRadialCase2(false, true)
elseif case==3 then
radial=self:GetRadialCase3(false, true)
else
self:E(self.lid.."ERROR: Platform zone only for CASE II or III recoveries!")
return nil
end
-- Get coordinate and vec2.
local coord=self:GetCoordinate():Translate(distance, radial)
local vec2=coord:GetVec2()
-- Create zone.
local zone=ZONE_RADIUS:New("Zone Platform", vec2, radius)
return zone
end
--- Get approach corridor zone. Shape depends on recovery case.
-- @param #AIRBOSS self
-- @param #number case Recovery case.
-- @return Core.Zone#ZONE_POLYGON_BASE Box zone.
function AIRBOSS:_GetCase23ValidZone()
function AIRBOSS:_GetZoneCorridor(case)
-- Radial.
local hdg=self:GetRadialCase3(false, false)
local off=self:GetRadialCase3(false, true)
-- Radial and offset.
local radial
local offset
-- Select case.
if case==2 then
radial=self:GetRadialCase2(false, false)
offset=self:GetRadialCase2(false, true)
elseif case==3 then
radial=self:GetRadialCase3(false, false)
offset=self:GetRadialCase3(false, true)
else
radial=self:GetRadialCase3(false, false)
offset=self:GetRadialCase3(false, true)
end
self:I(string.format("FF case %d radial = %d", case, radial))
self:I(string.format("FF case %d offset = %d", case, offset))
self:I("FF radial case 3 = "..hdg)
-- Width of the box.
local w=UTILS.NMToMeters(5)
-- Length of the box.
local l=UTILS.NMToMeters(50)
-- Coordinate of the carrier.
local c0=self:GetCoordinate()
-- Do not jump diagonal because it screws up the smoke zones.
local c1=c0:Translate(w, hdg+90) -- Starboard close
local c2=c1:Translate(l, hdg) -- Starboard far
local c4=c0:Translate(w, hdg-90) -- Port close
local c3=c4:Translate(l, hdg) -- Port far
local beta=hdg-self.holdingoffset
env.info("FF beta = "..beta)
local l=UTILS.NMToMeters(10)
local beta=radial-self.holdingoffset
--env.info("FF beta = "..beta)
local x=(50-9)/math.cos(math.rad(beta))
env.info("FF x [NM] = "..x)
--env.info("FF x [NM] = "..x)
local c={}
c[1]=self:GetCoordinate()
c[2]=c[1]:Translate( UTILS.NMToMeters( 5), hdg-90)
c[3]=c[2]:Translate( UTILS.NMToMeters( 9), hdg)
c[4]=c[3]:Translate( UTILS.NMToMeters( x), -beta)
--[[
c[5]=c[4]:Translate( UTILS.NMToMeters(10), hdg+90)
c[6]=c[5]:Translate(-UTILS.NMToMeters( x), beta)
c[7]=c[6]:Translate(-UTILS.NMToMeters( 5), hdg-90)
]]
c[2]=c[1]:Translate( UTILS.NMToMeters( 1), radial-90)
c[3]=c[2]:Translate( UTILS.NMToMeters(13), radial)
c[4]=c[3]:Translate( UTILS.NMToMeters( 2), radial+90)
c[5]=c[4]:Translate( UTILS.NMToMeters(10), offset)
c[6]=c[5]:Translate( UTILS.NMToMeters( 2), radial+90)
c[7]=c[6]:Translate(-UTILS.NMToMeters(10), offset) --This is more difficult!
c[8]=c[7]:Translate( UTILS.NMToMeters( 2), radial-90)
c[9]=c[8]:Translate(-UTILS.NMToMeters(13), radial)
-- Create an array of a square!
local p={}
for _i,_c in ipairs(c) do
_c:SmokeBlue()
p[_i]=_c:GetVec2()
end
@ -2694,6 +2942,8 @@ end
-- @return Core.Zone#ZONE Holding zone.
function AIRBOSS:_GetHoldingZone(playerData)
--TODO: make indepened of whole playerData. Just use case and stack as input!
-- Player unit and flight.
local unit=playerData.unit
@ -2724,6 +2974,8 @@ function AIRBOSS:_GetHoldingZone(playerData)
-- TODO: Include 15 or 30 degrees offset.
local hdg=self.carrier:GetHeading()-self.holdingoffset
--TODO: case dependend radial!
local hdg=self:GetRadialCase3(false)
-- Create an array of a square!
@ -2816,7 +3068,7 @@ end
function AIRBOSS:_Platform(playerData)
-- Check if player is in valid zone
local validzone=self:_GetCase23ValidZone()
local validzone=self:_GetZoneCorridor(playerData.case)
-- Check if we are inside the moving zone.
local invalid=playerData.unit:IsNotInZone(validzone)
@ -2828,7 +3080,7 @@ function AIRBOSS:_Platform(playerData)
end
-- Check if we are inside the moving zone.
local inzone=playerData.unit:IsInZone(self.zonePlatform)
local inzone=playerData.unit:IsInZone(self:_GetZonePlatform(playerData.case))
-- Check if we are in zone.
if inzone then
@ -2863,7 +3115,7 @@ end
function AIRBOSS:_DirtyUp(playerData)
-- Check if player is in valid zone
local validzone=self:_GetCase23ValidZone()
local validzone=self:_GetZoneCorridor(playerData.case)
-- Check if we are inside the moving zone.
local invalid=playerData.unit:IsNotInZone(validzone)
@ -2875,7 +3127,7 @@ function AIRBOSS:_DirtyUp(playerData)
end
-- Check if we are inside the moving zone.
local inzone=playerData.unit:IsInZone(self.zoneDirtyup)
local inzone=playerData.unit:IsInZone(self:_GetZoneDirtyUp(playerData.case))
--if self:_CheckLimits(X, Z, self.DirtyUp) then
if inzone then
@ -2917,7 +3169,7 @@ end
function AIRBOSS:_Bullseye(playerData)
-- Check if player is in valid zone
local validzone=self:_GetCase23ValidZone()
local validzone=self:_GetZoneCorridor(playerData.case)
-- Check if we are inside the moving zone.
local invalid=playerData.unit:IsNotInZone(validzone)
@ -2929,7 +3181,7 @@ function AIRBOSS:_Bullseye(playerData)
end
-- Check if we are inside the moving zone.
local inzone=playerData.unit:IsInZone(self.zoneBullseye)
local inzone=playerData.unit:IsInZone(self:_GetZoneBullseye(playerData.case))
-- Check that we reached the position.
--if self:_CheckLimits(X, Z, self.Bullseye) then
@ -3724,14 +3976,37 @@ function AIRBOSS:GetFinalBearing(magnetic)
return fb
end
--- Get radial, i.e. the final bearing FB-180 degrees including holding offset for Case II/III recoveries.
--- Get radial with respect to carrier heading and (optionally) holding offset. This is used in Case II recoveries.
-- @param #AIRBOSS self
-- @param #boolean magnetic If true, magnetic radial is returned. Default is true radial.
-- @param #boolean offset If true, inlcude holding offset.
-- @return #number Radial in degrees.
function AIRBOSS:GetRadialCase2(magnetic, offset)
-- Radial wrt to heading of carrier.
local radial=self:GetHeading(magnetic)-180
-- Holding offset angle (+-15 or 30 degrees usually)
if offset then
radial=radial-self.holdingoffset
end
-- Adjust for negative values.
if radial<0 then
radial=radial+360
end
return radial
end
--- Get radial with respect to angled runway and (optionally) holding offset. This is used in Case III recoveries.
-- @param #AIRBOSS self
-- @param #boolean magnetic If true, magnetic radial is returned. Default is true radial.
-- @param #boolean offset If true, inlcude holding offset.
-- @return #number Radial in degrees.
function AIRBOSS:GetRadialCase3(magnetic, offset)
-- Get radial.
-- Radial wrt angled runway.
local radial=self:GetFinalBearing(magnetic)-180
-- Holding offset angle (+-15 or 30 degrees usually)
@ -3780,8 +4055,7 @@ function AIRBOSS:_GetRelativeHeading(unit, runway)
local vP=unit:GetOrientationX()
-- We only want the X-Z plane. Aircraft could fly parallel but ballistic and we dont want the "pitch" angle.
vC.y=0
vP.y=0
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)))
@ -5614,7 +5888,7 @@ function AIRBOSS:_DisplayPlayerStatus(_unitName)
elseif playerData.step==AIRBOSS.PatternStep.PLATFORM then
-- Heading and distance to platform zone.
local flyhdg=playerData.unit:GetCoordinate():HeadingTo(self.zonePlatform:GetCoordinate())
local flyhdg=playerData.unit:GetCoordinate():HeadingTo(self:_GetZonePlatform(playerData.case):GetCoordinate())
local flydist=UTILS.MetersToNM(playerData.unit:GetCoordinate():Get2DDistance(self.zoneInitial:GetCoordinate()))
local fb=self:GetFinalBearing(true)
@ -5685,45 +5959,49 @@ function AIRBOSS:_MarkCase23Zones(_unitName, flare)
local playerData=self.players[_playername] --#AIRBOSS.PlayerData
if playerData then
local case=playerData.case
if case<2 then
case=3
end
-- Initial
local text="Marking CASE II/III zone:\n"
local text=string.format("Marking CASE %d zone:\n", case)
--TODO: Add height!
if flare then
text=text.."* valid area with GREEN flares\n"
local zp=self:_GetCase23ValidZone()
zp:FlareZone(FLARECOLOR.Green, 45)
text=text.."* approach corridor with GREEN flares\n"
self:_GetZoneCorridor(case):FlareZone(FLARECOLOR.Green, 45)
text=text.."* platform with RED flares\n"
self.zonePlatform:FlareZone(FLARECOLOR.Red, 45)
self:_GetZonePlatform(case):FlareZone(FLARECOLOR.Red, 45)
text=text.."* dirty up with YELLOW flares\n"
self.zoneDirtyup:FlareZone(FLARECOLOR.Yellow, 45)
self:_GetZoneDirtyUp(case):FlareZone(FLARECOLOR.Yellow, 45)
if math.abs(self.holdingoffset)>0 then
self.zoneArcturn1:FlareZone(FLARECOLOR.Yellow, 45)
self:_GetZoneArcIn(case):FlareZone(FLARECOLOR.Yellow, 45)
text=text.."* arc turn in with YELLOW flares\n"
self.zoneArcturn2:FlareZone(FLARECOLOR.White, 45)
self:_GetZoneArcOut(case):FlareZone(FLARECOLOR.White, 45)
text=text.."* arc trun out with WHITE flares\n"
end
text=text.."* bullseye with WHITE flares\n"
self.zoneBullseye:FlareZone(FLARECOLOR.White, 45)
self:_GetZoneBullseye(case):FlareZone(FLARECOLOR.White, 45)
else
text=text.."* valid area with GREEN smoke\n"
local zp=self:_GetCase23ValidZone()
zp:SmokeZone(SMOKECOLOR.Green, 45)
text=text.."* approach corridor with GREEN smoke\n"
self:_GetZoneCorridor(case):SmokeZone(SMOKECOLOR.Green, 45)
text=text.."* platform with RED smoke\n"
self.zonePlatform:SmokeZone(SMOKECOLOR.Red, 45)
self:_GetZonePlatform(case):SmokeZone(SMOKECOLOR.Red, 45)
text=text.."* dirty up with ORANGE flares\n"
self:_GetZoneDirtyUp(case):SmokeZone(SMOKECOLOR.Orange, 45)
if math.abs(self.holdingoffset)>0 then
self.zoneArcturn1:SmokeZone(SMOKECOLOR.Red, 45)
self:_GetZoneArcIn(case):SmokeZone(SMOKECOLOR.Red, 45)
text=text.."* arc turn in with YELLOW flares\n"
self.zoneArcturn2:SmokeZone(SMOKECOLOR.Orange, 45)
self:_GetZoneArcOut(case):SmokeZone(SMOKECOLOR.Orange, 45)
text=text.."* arc trun out with WHITE flares\n"
end
self.zoneDirtyup:SmokeZone(SMOKECOLOR.Orange, 45)
text=text.."* bullseye with BLUE smoke\n"
self.zoneBullseye:SmokeZone(SMOKECOLOR.Blue, 45)
self:_GetZoneBullseye(case):SmokeZone(SMOKECOLOR.Blue, 45)
end
-- Send message to player.