diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 8c51087d4..911c719ea 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -354,10 +354,10 @@ ATIS.Alphabet = { --- Runway correction for converting true to magnetic heading. -- @type ATIS.RunwayM2T --- @field #number Caucasus 0° (East). --- @field #number Nevada +12° (East). --- @field #number Normandy -10° (West). --- @field #number PersianGulf +2° (East). +-- @field #number Caucasus 0� (East). +-- @field #number Nevada +12� (East). +-- @field #number Normandy -10� (West). +-- @field #number PersianGulf +2� (East). ATIS.RunwayM2T={ Caucasus=0, Nevada=12, @@ -709,9 +709,9 @@ function ATIS:SetMapMarks(switch) return self end ---- Set magnetic runway headings as depicted on the runway, *e.g.* "13" for 130° or "25L" for the left runway with magnetic heading 250°. +--- Set magnetic runway headings as depicted on the runway, *e.g.* "13" for 130� or "25L" for the left runway with magnetic heading 250�. -- @param #ATIS self --- @param #table headings Magnetic headings. Inverse (-180°) headings are added automatically. You only need to specify one heading per runway direction. "L"eft and "R" right can also be appended. +-- @param #table headings Magnetic headings. Inverse (-180�) headings are added automatically. You only need to specify one heading per runway direction. "L"eft and "R" right can also be appended. -- @return #ATIS self function ATIS:SetRunwayHeadingsMagnetic(headings) @@ -826,12 +826,12 @@ end -- -- To get *true* from *magnetic* heading one has to add easterly or substract westerly variation, e.g -- --- A magnetic heading of 180° corresponds to a true heading of +-- A magnetic heading of 180� corresponds to a true heading of -- --- * 186° on the Caucaus map --- * 192° on the Nevada map --- * 170° on the Normany map --- * 182° on the Persian Gulf map +-- * 186� on the Caucaus map +-- * 192� on the Nevada map +-- * 170� on the Normany map +-- * 182� on the Persian Gulf map -- -- Likewise, to convert *magnetic* into *true* heading, one has to substract easterly and add westerly variation. -- @@ -1085,7 +1085,7 @@ function ATIS:onafterBroadcast(From, Event, To) local g= 9.80665 --[m/s^2] local M= 0.0289644 --[kg/mol] local T0=coord:GetTemperature(0)+273.15 --[K] Temp at sea level. - local TS=288.15 -- Standard Temperature assumed by Altimeter is 15°C + local TS=288.15 -- Standard Temperature assumed by Altimeter is 15�C local q=qnh*100 -- Calculate Pressure. @@ -1151,27 +1151,7 @@ function ATIS:onafterBroadcast(From, Event, To) --- Runway --- -------------- - -- Get active runway data based on wind direction. - local runact=self.airbase:GetActiveRunway(self.runwaym2t) - - -- Active runway "31". - local runway=self:GetMagneticRunway(windFrom) or runact.idx - - -- Left or right in case there are two runways with the same heading. - local rwyLeft=nil - - -- Check if user explicitly specified a runway. - if self.activerunway then - - -- Get explicit runway heading if specified. - local runwayno=self:GetRunwayWithoutLR(self.activerunway) - if runwayno~="" then - runway=runwayno - end - - -- Was "L"eft or "R"ight given? - rwyLeft=self:GetRunwayLR(self.activerunway) - end + local runway, rwyLeft=self:GetActiveRunway() ------------ --- Time --- @@ -1213,10 +1193,10 @@ function ATIS:onafterBroadcast(From, Event, To) --- Temperature --- ------------------- - -- Temperature in °C (or °F). + -- Temperature in �C (or �F). local temperature=coord:GetTemperature(height+5) - -- Convert to °F. + -- Convert to �F. if self.TDegF then temperature=UTILS.CelciusToFarenheit(temperature) end @@ -1772,17 +1752,47 @@ end -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Get runway from user supplied magnetic heading. +--- Get active runway runway. -- @param #ATIS self --- @return #string Runway magnetic heading divided by ten (and rounded). Eg, "13" for 130°. +-- @return #string Active runway, e.g. "31" for 310 deg. +-- @return #boolean Use Left=true, Right=false, or nil. function ATIS:GetActiveRunway() + + local coord=self.airbase:GetCoordinate() + local height=coord:GetLandHeight() + + -- Get wind direction and speed in m/s. + local windFrom, windSpeed=coord:GetWind(height+10) + + -- Get active runway data based on wind direction. + local runact=self.airbase:GetActiveRunway(self.runwaym2t) + + -- Active runway "31". + local runway=self:GetMagneticRunway(windFrom) or runact.idx + + -- Left or right in case there are two runways with the same heading. + local rwyLeft=nil + + -- Check if user explicitly specified a runway. + if self.activerunway then + -- Get explicit runway heading if specified. + local runwayno=self:GetRunwayWithoutLR(self.activerunway) + if runwayno~="" then + runway=runwayno + end + + -- Was "L"eft or "R"ight given? + rwyLeft=self:GetRunwayLR(self.activerunway) + end + + return runway, rwyLeft end --- Get runway from user supplied magnetic heading. -- @param #ATIS self -- @param #number windfrom Wind direction (from) in degrees. --- @return #string Runway magnetic heading divided by ten (and rounded). Eg, "13" for 130°. +-- @return #string Runway magnetic heading divided by ten (and rounded). Eg, "13" for 130�. function ATIS:GetMagneticRunway(windfrom) local diffmin=nil @@ -1825,7 +1835,7 @@ function ATIS:GetNavPoint(navpoints, runway, left) local navL=self:GetRunwayLR(nav.runway) local hdgD=UTILS.HdgDiff(navy,rwyy) - if hdgD<=15 then --We allow an error of +-15° here. + if hdgD<=15 then --We allow an error of +-15� here. if navL==nil or (navL==true and left==true) or (navL==false and left==false) then return nav end diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index bbb2961af..c3097571a 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -30,6 +30,7 @@ -- @field #table Qlanding Queue of aircraft currently on final approach. -- @field #table Qtakeoff Queue of aircraft about to takeoff. -- @field #table Qparking Queue of aircraft parking. +-- @field Ops.ATIS#ATIS atis ATIS object. -- @field #number activerwyno Number of active runway. -- @field #number atcfreq ATC radio frequency. -- @field Core.RadioQueue#RADIOQUEUE atcradio ATC radio queue. @@ -68,6 +69,7 @@ FLIGHTCONTROL = { Qlanding = {}, Qtakeoff = {}, Qparking = {}, + atis = nil, activerwyno = 1, atcfreq = nil, atcradio = nil, @@ -105,19 +107,19 @@ FLIGHTCONTROL = { --- FlightControl class version. -- @field #string version -FLIGHTCONTROL.version="0.1.1" +FLIGHTCONTROL.version="0.1.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Add FARPS. +-- TODO: Add FARPS? -- TODO: Add helos. --- TODO: Task me down option. +-- TODO: Take me down option. -- TODO: ATIS option. -- TODO: ATC voice overs. -- TODO: Check runways and clean up. --- TODO: Interface with FLIGHTGROUP +-- TODO: Interface with FLIGHTGROUP. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -325,6 +327,7 @@ function FLIGHTCONTROL:onafterStop() self:HandleEvent(EVENTS.EngineShutdown) self:HandleEvent(EVENTS.Crash) + self.atcradio:Stop() end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -449,18 +452,19 @@ function FLIGHTCONTROL:_CheckQueues() self:_PrintQueue(self.Qwaiting, "Holding") self:_PrintQueue(self.Qlanding, "Landing") end + + -- Number of holding groups. + local nholding=#self.Qwaiting -- Number of groups landing. local nlanding=#self.Qlanding + + -- Number of parking groups. + local nparking=#self.Qparking -- Number of groups taking off. local ntakeoff=#self.Qtakeoff - - -- Number of holding groups. - local nholding=#self.Qwaiting - - -- Number of parking groups. - local nparking=#self.Qparking + -- Get next flight in line: either holding or parking. local flight, isholding, parking=self:_GetNextFlight() @@ -506,14 +510,15 @@ function FLIGHTCONTROL:_CheckQueues() -------------------- -- Check if flight is AI. Humans have to request taxi via F10 menu. - if flight.ai then + if flight.ai then -- Message. - -- TODO: Which runway! local text=string.format("Flight %s, you are cleared to taxi to runway.", flight.groupname) self:I(self.lid..text) MESSAGE:New(text, 5, "FLIGHTCONTROL"):ToAll() + -- Start uncontrolled aircraft. + -- TODO: handle case with engines hot. That does not trigger a ENGINE_START event. More a FLIGHTGROUP issue. flight.group:StartUncontrolled() flight:_UpdateRoute(1) @@ -642,7 +647,7 @@ function FLIGHTCONTROL:_GetNextFightHolding() end ---- Get next flight waiting for landing clearance. +--- Get next flight waiting for taxi and takeoff clearance. -- @param #FLIGHTCONTROL self -- @return Ops.FlightGroup#FLIGHTGROUP Marshal flight next in line and ready to enter the pattern. Or nil if no flight is ready. function FLIGHTCONTROL:_GetNextFightParking() @@ -870,6 +875,25 @@ function FLIGHTCONTROL:GetActiveRunway() return self.airbase:GetActiveRunway() end +--- Get the active runway based on current wind direction. +-- @param #FLIGHTCONTROL self +-- @return #string Runway text, e.g. "31L" or "09". +function FLIGHTCONTROL:GetActiveRunwayText() + local rwy="" + local rwyL + if self.atis then + rwy, rwyL=self.atis:GetActiveRunway() + if rwyL==true then + rwy=rwy.."L" + elseif rwyL==false then + rwy=rwy.."R" + end + else + rwy=self.airbase:GetActiveRunway().idx + end + return rwy +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Parking Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1006,7 +1030,7 @@ end --- Create player menu. -- @param #FLIGHTCONTROL self -- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group. --- @param #table atcmenu ATC root menu. +-- @param #table atcmenu ATC root menu table. function FLIGHTCONTROL:_CreatePlayerMenu(flight, atcmenu) local group=flight.group @@ -1023,11 +1047,15 @@ function FLIGHTCONTROL:_CreatePlayerMenu(flight, atcmenu) local rootmenu=flight.menu.atc.root atcmenu[airbasename] = atcmenu[airbasename] or {} - atcmenu[airbasename].root = MENU_GROUP:New(group, airbasename, rootmenu) + -- Root menu + atcmenu[airbasename].root = MENU_GROUP:New(group, airbasename, rootmenu) + + -- Commands atcmenu[airbasename].MyStatus = MENU_GROUP_COMMAND:New(group, "My Status", atcmenu[airbasename].root, self._PlayerMyStatus, self, groupname) atcmenu[airbasename].RequestTaxi = MENU_GROUP_COMMAND:New(group, "Request Taxi", atcmenu[airbasename].root, self._PlayerRequestTaxi, self, groupname) atcmenu[airbasename].RequestTakeoff = MENU_GROUP_COMMAND:New(group, "Request Takeoff", atcmenu[airbasename].root, self._PlayerRequestTakeoff, self, groupname) + atcmenu[airbasename].RequestParking = MENU_GROUP_COMMAND:New(group, "Request Parking", atcmenu[airbasename].root, self._PlayerRequestParking, self, groupname) atcmenu[airbasename].Inbound = MENU_GROUP_COMMAND:New(group, "Inbound", atcmenu[airbasename].root, self._PlayerInbound, self, groupname) end @@ -1065,8 +1093,10 @@ function FLIGHTCONTROL:_PlayerRequestTaxi(groupname) local flight=_DATABASE:GetFlightGroup(groupname) if flight then + + local runway=self:GetActiveRunwayText() - MESSAGE:New("You are cleared to taxi", 5):ToAll() + MESSAGE:New(string.format("You are cleared to taxi to runway %s", runway), 5):ToAll() for _,_element in pairs(flight.elements) do local element=_element --Ops.FlightGroup#FLIGHTGROUP.Element @@ -1089,7 +1119,18 @@ function FLIGHTCONTROL:_PlayerRequestTakeoff(groupname) local flight=_DATABASE:GetFlightGroup(groupname) if flight then - if flight:IsTaxiing() then + if flight:IsTaxiing() then + + if #self.Qlanding==0 and self.Qtakeoff==0 then + MESSAGE:New("You are cleared for takeoff as there is no one else landing or queueing for takeoff", 5):ToAll() + elseif #self.Qlanding>0 then + MESSAGE:New("Negative ghostrider, other flights are currently landing.", 5):ToAll() + elseif #self.Qtakeoff>0 then + MESSAGE:New("Negative ghostrider, other flights are ahead of you.", 5):ToAll() + end + + self:_AddFlightToTakeoffQueue(flight) + else end end diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index f42afaf3e..8cbc9f9c1 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -1405,12 +1405,14 @@ function FLIGHTGROUP:onafterFlightTaxiing(From, Event, To) -- TODO: need a better check for the airbase. local airbase=self.group:GetCoordinate():GetClosestAirbase(nil, self.group:GetCoalition()) - if self.flightcontrol and airbase and self.flightcontrol.airbasename==airbase:GetName() then - -- Add flight to takeoff queue. - self.flightcontrol:_AddFlightToTakeoffQueue(self) - + if self.flightcontrol and airbase and self.flightcontrol.airbasename==airbase:GetName() then -- Remove flight from parking queue. self.flightcontrol:_RemoveFlightFromQueue(self.flightcontrol.Qparking, self, "parking") + + -- Add AI flight to takeoff queue. + if self.ai then + self.flightcontrol:_AddFlightToTakeoffQueue(self) + end end end @@ -3612,6 +3614,7 @@ function FLIGHTGROUP:_UpdateMenu() local position=self.group:GetCoordinate() + -- Get all FLIGHTCONTROLS local fc={} for airbasename,_flightcontrol in pairs(_DATABASE.FLIGHTCONTROLS) do @@ -3623,8 +3626,7 @@ function FLIGHTGROUP:_UpdateMenu() local fcitem={airbasename=airbasename, dist=dist} - table.insert(fc, fcitem) - + table.insert(fc, fcitem) end -- Sort table wrt distance to airbases. @@ -3637,7 +3639,15 @@ function FLIGHTGROUP:_UpdateMenu() playermenu:RemoveSubMenus() --Core.Menu#MENU_GROUP - for i=1,math.min(#fc,8) do + -- If there is a designated FC, we put it first. + local N=8 + if self.flightcontrol then + self.flightcontrol:_CreatePlayerMenu(self, self.menu.atc) + N=7 + end + + -- Max 8 entries in F10 menu. + for i=1,math.min(#fc,N) do local airbasename=fc[i].airbasename local flightcontrol=_DATABASE:GetFlightControl(airbasename) flightcontrol:_CreatePlayerMenu(self, self.menu.atc)