diff --git a/Moose Development/Moose/Core/F10Menu.lua b/Moose Development/Moose/Core/F10Menu.lua deleted file mode 100644 index de8a04314..000000000 --- a/Moose Development/Moose/Core/F10Menu.lua +++ /dev/null @@ -1,212 +0,0 @@ ----- **Core** - F10 Other Menu. --- --- **Main Features:** --- --- * Add Menus and Commands to the "F10 Other" Menu --- * Create menus and commands at specific locations within the parent menu --- * Events when command functions are executed --- --- --- === --- --- ### Author: **funkyfranky** --- @module Ops.F10Menu --- @image OPS_F10Menu.png - ---- F10Menu class. --- @type F10MENU --- @field #string ClassName Name of the class. --- @field #number verbose Verbosity level. --- @field #string lid Class id string for output to DCS log file. --- @field #string text Text of the menu item. --- @field #table path Path of the menu. --- @field #F10MENU parent Parent menu or `nil`. --- @field #table commands Commands within this menu. --- @field #table submenues Sub menues withing this menu. --- @extends Core.Fsm#FSM - ---- *In preparing for battle I have always found that plans are useless, but planning is indispensable* -- Dwight D Eisenhower --- --- === --- --- # The CHIEF Concept --- --- --- @field #F10MENU -F10MENU = { - ClassName = "F10MENU", - verbose = 0, - lid = nil, - commands = {}, - submenues = {}, -} - ---- Command executing a function. --- @type F10MENU.Command --- @field #number uid Unique ID. --- @field #string text Description. --- @field #function func Function. --- @field #table args Arguments. --- @field #table path Path. - ---- F10 menu class version. --- @field #string version -F10MENU.version="0.0.1" - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO list -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - --- TODO: A lot. - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Constructors -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Create a new F10 menu entry. --- @param #F10MENU self --- @return #F10MENU self -function F10MENU:_New() - - -- Inherit everything from INTEL class. - local self=BASE:Inherit(self, FSM:New()) --#F10MENU - - -- Add FSM transitions. - -- From State --> Event --> To State - self:AddTransition("*", "MissionAssign", "*") -- Assign mission to a COMMANDER. - - ------------------------ - --- Pseudo Functions --- - ------------------------ - - - return self -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- User functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Create a new F10 menu for all players. --- @param #F10MENU self --- @param #string Text Description of menu. --- @param #F10MENU ParentMenu Parent menu to which this menu is added. If not specified, the menu is added to the root menu. --- @return #F10MENU self -function F10MENU:NewForAll(Text, ParentMenu) - - -- Inherit everything from INTEL class. - local self=self:_New() - - self.text=Text - - self.parent=ParentMenu - - if self.parent then - self.parent:_AddSubmenu(self) - end - - local path=self.parent and self.parent:GetPath() or nil - - self.path=missionCommands.addSubMenu(self.text, path) - - return self -end - ---- Removes the F10 menu and its entire contents. --- @param #F10MENU self --- @return #F10MENU self -function F10MENU:Remove() - - for path,_submenu in pairs(self.submenues) do - local submenu=_submenu --#F10MENU - submenu:Remove() - end - -end - ---- Get path. --- @param #F10MENU self --- @return #table Path. -function F10MENU:GetPath() - return self.path -end - ---- Get commands --- @param #F10MENU self --- @return #table Path. -function F10MENU:GetCommands() - return self.commands -end - ---- Get submenues. --- @param #F10MENU self --- @return #table Path. -function F10MENU:GetSubmenues() - return self.submenues -end - - ---- Add a command for all players. --- @param #F10MENU self --- @param #string Text Description. --- @param #function CommandFunction Function to call. --- @param ... Function arguments. --- @return #F10MENU.Command Command. -function F10MENU:AddCommandForAll(Text, CommandFunction, ...) - - local command={} --#F10MENU.Command - command.uid=1 - command.text=Text - command.func=CommandFunction - command.args=... - command.path=missionCommands.addCommand(command.text, self.path, command.func, command.args) - - table.insert(self.commands, command) - - return command -end - ---- Add a command for players of a specific coalition. --- @param #F10MENU self --- @return #F10MENU self -function F10MENU:AddCommandForCoalition() - -end - - ---- Add a command for players of a specific group. --- @param #F10MENU self --- @return #F10MENU self -function F10MENU:AddCommandForGroup() - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Private functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Add a command for players of a specific group. --- @param #F10MENU self --- @param #F10MENU Submenu The submenu to add. --- @return #F10MENU self -function F10MENU:_AddSubmenu(Submenu) - - self.submenues[Submenu.path]=Submenu - -end - ---- Add a command for players of a specific group. --- @param #F10MENU self --- @return #F10MENU self -function F10MENU:_Refresh() - - - - for _,_submenu in pairs(self.submenues) do - local submenu=_submenu --#F10MENU - - - end - -end - diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 0f2b6d565..30f02b45d 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -29,7 +29,6 @@ __Moose.Include( 'Scripts/Moose/Core/Goal.lua' ) __Moose.Include( 'Scripts/Moose/Core/Spot.lua' ) __Moose.Include( 'Scripts/Moose/Core/Astar.lua' ) __Moose.Include( 'Scripts/Moose/Core/MarkerOps_Base.lua' ) -__Moose.Include( 'Scripts/Moose/Core/F10Menu.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' ) @@ -96,7 +95,6 @@ __Moose.Include( 'Scripts/Moose/Ops/CSAR.lua' ) __Moose.Include( 'Scripts/Moose/Ops/CTLD.lua' ) __Moose.Include( 'Scripts/Moose/Ops/OpsZone.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Chief.lua' ) -__Moose.Include( 'Scripts/Moose/Ops/FlightControl.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Air.lua' ) diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua deleted file mode 100644 index 54397657d..000000000 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ /dev/null @@ -1,2276 +0,0 @@ ---- **OPS** - Manage launching and recovery of aircraft at airdromes. --- --- --- --- **Main Features:** --- --- * Manage aircraft recovery. --- --- === --- --- ### Author: **funkyfranky** --- @module OPS.FlightControl --- @image OPS_FlightControl.png - - ---- FLIGHTCONTROL class. --- @type FLIGHTCONTROL --- @field #string ClassName Name of the class. --- @field #number verbose Verbosity level. --- @field #string theatre The DCS map used in the mission. --- @field #string lid Class id string for output to DCS log file. --- @field #string airbasename Name of airbase. --- @field #number airbasetype Type of airbase. --- @field Wrapper.Airbase#AIRBASE airbase Airbase object. --- @field Core.Zone#ZONE zoneAirbase Zone around the airbase. --- @field #table parking Parking spots table. --- @field #table runways Runway table. --- @field #table flights All flights table. --- @field #table clients Table with all clients spawning at this airbase. --- @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. --- @field #number Nlanding Max number of aircraft groups in the landing pattern. --- @field #number dTlanding Time interval in seconds between landing clearance. --- @field #number Nparkingspots Total number of parking spots. --- @field Core.Spawn#SPAWN parkingGuard Parking guard spawner. --- @extends Core.Fsm#FSM - ---- **Ground Control**: Airliner X, Good news, you are clear to taxi to the active. --- **Pilot**: Roger, What's the bad news? --- **Ground Control**: No bad news at the moment, but you probably want to get gone before I find any. --- --- === --- --- # The FLIGHTCONTROL Concept --- --- --- --- @field #FLIGHTCONTROL -FLIGHTCONTROL = { - ClassName = "FLIGHTCONTROL", - verbose = 3, - lid = nil, - theatre = nil, - airbasename = nil, - airbase = nil, - airbasetype = nil, - zoneAirbase = nil, - parking = {}, - runways = {}, - flights = {}, - clients = {}, - atis = nil, - activerwyno = 1, - atcfreq = nil, - atcradio = nil, - atcradiounitname = nil, - Nlanding = nil, - dTlanding = nil, - Nparkingspots = nil, -} - ---- Holding point --- @type FLIGHTCONTROL.HoldingPoint --- @field Core.Point#COORDINATE pos0 First poosition of racetrack holding point. --- @field Core.Point#COORDINATE pos1 Second position of racetrack holding point. --- @field #number angelsmin Smallest holding altitude in angels. --- @field #number angelsmax Largest holding alitude in angels. - ---- Player menu data. --- @type FLIGHTCONTROL.PlayerMenu --- @field Core.Menu#MENU_GROUP root Root menu. --- @field Core.Menu#MENU_GROUP_COMMAND RequestTaxi Request taxi. - ---- Parking spot data. --- @type FLIGHTCONTROL.ParkingSpot --- @field Wrapper.Group#GROUP ParkingGuard Parking guard for this spot. --- @extends Wrapper.Airbase#AIRBASE.ParkingSpot - ---- Parking spot data. --- @type FLIGHTCONTROL.FlightStatus --- @field #string UNKNOWN Flight state is unknown. --- @field #string INBOUND Flight is inbound. --- @field #string HOLDING Flight is holding. --- @field #string LANDING Flight is landing. --- @field #string TAXIINB Flight is taxiing to parking area. --- @field #string ARRIVED Flight arrived at parking spot. --- @field #string TAXIOUT Flight is taxiing to runway for takeoff. --- @field #string READYTO Flight is ready for takeoff. --- @field #string TAKEOFF Flight is taking off. -FLIGHTCONTROL.FlightStatus={ - UNKNOWN="Unknown", - INBOUND="Inbound", - HOLDING="Holding", - LANDING="Landing", - TAXIINB="Taxi Inbound", - ARRIVED="Arrived", - PARKING="Parking", - TAXIOUT="Taxi to runway", - READYTO="Ready For Takeoff", - TAKEOFF="Takeoff", -} - - ---- Runway data. --- @type FLIGHTCONTROL.Runway --- @field #number direction Direction of the runway. --- @field #number length Length of runway in meters. --- @field #number width Width of runway in meters. --- @field Core.Point#COORDINATE position Position of runway start. - - ---- Sound file data. --- @type FLIGHTCONTROL.Soundfile --- @field #string filename Name of the file --- @field #number duration Duration in seconds. - ---- Sound files. --- @type FLIGHTCONTROL.Sound --- @field #FLIGHTCONTROL.Soundfile ActiveRunway -FLIGHTCONTROL.Sound = { - ActiveRunway={filename="ActiveRunway.ogg", duration=0.99}, -} - ---- FlightControl class version. --- @field #string version -FLIGHTCONTROL.version="0.5.0" - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO list -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - --- TODO: Runway destroyed. --- TODO: Define holding zone --- DONE: Add parking guard. --- TODO: Accept and forbit parking spots. --- NOGO: Add FARPS? --- TODO: Add helos. --- TODO: Talk me down option. --- TODO: ATIS option. --- TODO: ATC voice overs. --- TODO: Check runways and clean up. --- DONE: Interface with FLIGHTGROUP. - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Constructor -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Create a new FLIGHTCONTROL class object for an associated airbase. --- @param #FLIGHTCONTROL self --- @param #string airbasename Name of the airbase. --- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:New(airbasename) - - -- Inherit everything from FSM class. - local self=BASE:Inherit(self, FSM:New()) -- #FLIGHTCONTROL - - -- Try to get the airbase. - self.airbase=AIRBASE:FindByName(airbasename) - - -- Name of the airbase. - self.airbasename=airbasename - - -- Set some string id for output to DCS.log file. - self.lid=string.format("FLIGHTCONTROL %s | ", airbasename) - - -- Check if the airbase exists. - if not self.airbase then - self:E(string.format("ERROR: Could not find airbase %s!", tostring(airbasename))) - return nil - end - -- Check if airbase is an airdrome. - if self.airbase:GetAirbaseCategory()~=Airbase.Category.AIRDROME then - self:E(string.format("ERROR: Airbase %s is not an AIRDROME! Script does not handle FARPS or ships.", tostring(airbasename))) - return nil - end - - - -- Airbase category airdrome, FARP, SHIP. - self.airbasetype=self.airbase:GetAirbaseCategory() - - -- Current map. - self.theatre=env.mission.theatre - - -- 5 NM zone around the airbase. - self.zoneAirbase=ZONE_RADIUS:New("FC", self:GetCoordinate():GetVec2(), UTILS.NMToMeters(5)) - - -- Defaults: - self:SetLandingMax() - self:SetLandingInterval() - - - -- Init runways. - self:_InitRunwayData() - - -- Start State. - self:SetStartState("Stopped") - - -- Add FSM transitions. - -- From State --> Event --> To State - self:AddTransition("Stopped", "Start", "Running") -- Start FSM. - self:AddTransition("*", "Status", "*") -- Update status. - - -- Add to data base. - _DATABASE:AddFlightControl(self) - - return self -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- User API Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Set the number of aircraft groups, that are allowed to land simultaniously. --- @param #FLIGHTCONTROL self --- @param #number n Max number of aircraft landing simultaniously. Default 2. --- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:SetLandingMax(n) - - self.Nlanding=n or 2 - - return self -end - ---- Set time interval between landing clearance of groups. --- @param #FLIGHTCONTROL self --- @param #number dt Time interval in seconds. Default 180 sec (3 min). --- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:SetLandingInterval(dt) - - self.dTlanding=dt or 180 - - return self -end - - ---- Set runway. This clears all auto generated runways. --- @param #FLIGHTCONTROL self --- @param #FLIGHTCONTROL.Runway Runway. --- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:SetRunway(runway) - - -- Reset table. - self.runways={} - - -- Set runway. - table.insert(self.runways, runway) - - return self -end - ---- Add runway. --- @param #FLIGHTCONTROL self --- @param #FLIGHTCONTROL.Runway Runway. --- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:AddRunway(runway) - - -- Set runway. - table.insert(self.runways, runway) - - return self -end - ---- Set active runway number. Counting refers to the position in the table entry. --- @param #FLIGHTCONTROL self --- @param #number no Number in the runways table. --- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:SetActiveRunwayNumber(no) - self.activerwyno=no - return self -end - - ---- Set the parking guard group. --- @param #FLIGHTCONTROL self --- @param #string TemplateGroupName Name of the template group. --- @return #FLIGHTCONTROL self -function FLIGHTCONTROL:SetParkingGuard(TemplateGroupName) - - local alias=string.format("Parking Guard %s", self.airbasename) - - -- Need spawn with alias for multiple FCs. - self.parkingGuard=SPAWN:NewWithAlias(TemplateGroupName, alias) - - --self.parkingGuard=SPAWNSTATIC:NewFromStatic("Parking Guard"):InitNamePrefix(alias) - - return self -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Status -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Start FLIGHTCONTROL FSM. Handle events. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:onafterStart() - - -- Events are handled my MOOSE. - self:I(self.lid..string.format("Starting FLIGHTCONTROL v%s for airbase %s of type %d on map %s", FLIGHTCONTROL.version, self.airbasename, self.airbasetype, self.theatre)) - - -- Init parking spots. - self:_InitParkingSpots() - - -- Handle events. - self:HandleEvent(EVENTS.Birth) - self:HandleEvent(EVENTS.EngineStartup) - self:HandleEvent(EVENTS.Takeoff) - self:HandleEvent(EVENTS.Land) - self:HandleEvent(EVENTS.EngineShutdown) - self:HandleEvent(EVENTS.Crash) - - self.atcradio=RADIOQUEUE:New(self.atcfreq or 305, nil, string.format("FC %s", self.airbasename)) - self.atcradio:Start(1, 0.1) - - -- Init status updates. - self:__Status(-1) -end - ---- Update status. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:onafterStatus() - - -- Check status of all registered flights. - self:_CheckFlights() - - -- Check parking spots. - --self:_CheckParking() - - -- Check waiting and landing queue. - self:_CheckQueues() - - -- Get runway. - local runway=self:GetActiveRunway() - - local Nflights= self:CountFlights() - local NQparking=self:CountFlights(FLIGHTCONTROL.FlightStatus.PARKING) - local NQtaxiout=self:CountFlights(FLIGHTCONTROL.FlightStatus.TAXIOUT) - local NQreadyto=self:CountFlights(FLIGHTCONTROL.FlightStatus.READYTO) - local NQtakeoff=self:CountFlights(FLIGHTCONTROL.FlightStatus.TAKEOFF) - local NQinbound=self:CountFlights(FLIGHTCONTROL.FlightStatus.INBOUND) - local NQholding=self:CountFlights(FLIGHTCONTROL.FlightStatus.HOLDING) - local NQlanding=self:CountFlights(FLIGHTCONTROL.FlightStatus.LANDING) - local NQtaxiinb=self:CountFlights(FLIGHTCONTROL.FlightStatus.TAXIINB) - local NQarrived=self:CountFlights(FLIGHTCONTROL.FlightStatus.ARRIVED) - -- ========================================================================================================= - local Nqueues = (NQparking+NQtaxiout+NQreadyto+NQtakeoff) + (NQinbound+NQholding+NQlanding+NQtaxiinb+NQarrived) - - -- Count free parking spots. - --TODO: get and substract number of reserved parking spots. - local nfree=self.Nparkingspots-NQarrived-NQparking - - local Nfree=self:CountParking(AIRBASE.SpotStatus.FREE) - local Noccu=self:CountParking(AIRBASE.SpotStatus.OCCUPIED) - local Nresv=self:CountParking(AIRBASE.SpotStatus.RESERVED) - - if Nfree+Noccu+Nresv~=self.Nparkingspots then - self:E(self.lid..string.format("WARNING: Number of parking spots does not match! Nfree=%d, Noccu=%d, Nreserved=%d != %d total", Nfree, Noccu, Nresv, self.Nparkingspots)) - end - - -- Info text. - if self.verbose>0 then - local text=string.format("State %s - Runway %s - Parking F=%d/O=%d/R=%d of %d - Flights=%s", self:GetState(), runway.idx, Nfree, Noccu, Nresv, self.Nparkingspots, Nflights) - self:I(self.lid..text) - end - - if Nflights==Nqueues then - --Check! - else - self:E(string.format("WARNING: Number of total flights %d!=%d number of flights in all queues!", Nflights, Nqueues)) - end - - if self.verbose>1 then - local text="Queue:" - text=text..string.format("\n- Flights = %d", Nflights) - text=text..string.format("\n---------------------------------------------") - text=text..string.format("\n- Parking = %d", NQparking) - text=text..string.format("\n- Taxi Out = %d", NQtaxiout) - text=text..string.format("\n- Ready TO = %d", NQreadyto) - text=text..string.format("\n- Take off = %d", NQtakeoff) - text=text..string.format("\n---------------------------------------------") - text=text..string.format("\n- Inbound = %d", NQinbound) - text=text..string.format("\n- Holding = %d", NQholding) - text=text..string.format("\n- Landing = %d", NQlanding) - text=text..string.format("\n- Taxi Inb = %d", NQtaxiinb) - text=text..string.format("\n- Arrived = %d", NQarrived) - text=text..string.format("\n---------------------------------------------") - self:I(self.lid..text) - end - - -- Next status update in ~30 seconds. - self:__Status(-20) -end - ---- Start FLIGHTCONTROL FSM. Handle events. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:onafterStop() - - -- Handle events. - self:HandleEvent(EVENTS.Birth) - self:HandleEvent(EVENTS.EngineStartup) - self:HandleEvent(EVENTS.Takeoff) - self:HandleEvent(EVENTS.Land) - self:HandleEvent(EVENTS.EngineShutdown) - self:HandleEvent(EVENTS.Crash) - - self.atcradio:Stop() -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Event Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Event handler for event birth. --- @param #FLIGHTCONTROL self --- @param Core.Event#EVENTDATA EventData -function FLIGHTCONTROL:OnEventBirth(EventData) - self:F3({EvendData=EventData}) - - if EventData and EventData.IniGroupName and EventData.IniUnit then - - -- Debug - self:T2(self.lid..string.format("BIRTH: unit = %s", tostring(EventData.IniUnitName))) - self:T2(self.lid..string.format("BIRTH: group = %s", tostring(EventData.IniGroupName))) - - -- Unit that was born. - local unit=EventData.IniUnit - - -- Check if birth took place at this airfield. - local bornhere=EventData.Place and EventData.Place:GetName()==self.airbasename or false - - -- We delay this, to have all elements of the group in the game. - if unit:IsAir() and bornhere then - - -- We got a player? - local playerunit, playername=self:_GetPlayerUnitAndName(EventData.IniUnitName) - - -- Create flight group. - if playername then - --self:ScheduleOnce(0.5, self._CreateFlightGroup, self, EventData.IniGroup) - end - self:ScheduleOnce(0.5, self._CreateFlightGroup, self, EventData.IniGroup) - - -- Spawn parking guard. - self:SpawnParkingGuard(unit) - - end - - end - -end - ---- Event handler for event land. --- @param #FLIGHTCONTROL self --- @param Core.Event#EVENTDATA EventData -function FLIGHTCONTROL:OnEventLand(EventData) - self:F3({EvendData=EventData}) - - self:T2(self.lid..string.format("LAND: unit = %s", tostring(EventData.IniUnitName))) - self:T2(self.lid..string.format("LAND: group = %s", tostring(EventData.IniGroupName))) - -end - ---- Event handler for event takeoff. --- @param #FLIGHTCONTROL self --- @param Core.Event#EVENTDATA EventData -function FLIGHTCONTROL:OnEventTakeoff(EventData) - self:F3({EvendData=EventData}) - - self:T2(self.lid..string.format("TAKEOFF: unit = %s", tostring(EventData.IniUnitName))) - self:T2(self.lid..string.format("TAKEOFF: group = %s", tostring(EventData.IniGroupName))) - - -- This would be the closest airbase. - local airbase=EventData.Place - - -- Unit that took off. - local unit=EventData.IniUnit - - -- Nil check for airbase. Crashed as player gave me no airbase. - if not (airbase or unit) then - self:E(self.lid.."WARNING: Airbase or IniUnit is nil in takeoff event!") - return - end - -end - ---- Event handler for event engine startup. --- @param #FLIGHTCONTROL self --- @param Core.Event#EVENTDATA EventData -function FLIGHTCONTROL:OnEventEngineStartup(EventData) - self:F3({EvendData=EventData}) - - self:I(self.lid..string.format("ENGINESTARTUP: unit = %s", tostring(EventData.IniUnitName))) - self:T2(self.lid..string.format("ENGINESTARTUP: group = %s", tostring(EventData.IniGroupName))) - - -- Unit that took off. - local unit=EventData.IniUnit - - -- Nil check for unit. - if not unit then - return - end - -end - ---- Event handler for event engine shutdown. --- @param #FLIGHTCONTROL self --- @param Core.Event#EVENTDATA EventData -function FLIGHTCONTROL:OnEventEngineShutdown(EventData) - self:F3({EvendData=EventData}) - - self:I(self.lid..string.format("ENGINESHUTDOWN: unit = %s", tostring(EventData.IniUnitName))) - self:T2(self.lid..string.format("ENGINESHUTDOWN: group = %s", tostring(EventData.IniGroupName))) - - -- Unit that took off. - local unit=EventData.IniUnit - - -- Nil check for unit. - if not unit then - return - end - -end - ---- Event handler for event crash. --- @param #FLIGHTCONTROL self --- @param Core.Event#EVENTDATA EventData -function FLIGHTCONTROL:OnEventCrash(EventData) - self:F3({EvendData=EventData}) - - self:T2(self.lid..string.format("CRASH: unit = %s", tostring(EventData.IniUnitName))) - self:T2(self.lid..string.format("CRASH: group = %s", tostring(EventData.IniGroupName))) - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Queue Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Scan airbase zone. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:_CheckQueues() - - -- Print queue. - if true then - self:_PrintQueue(self.flights, "All flights") - end - - -- Number of holding groups. - local nholding=self:CountFlights(FLIGHTCONTROL.FlightStatus.HOLDING) - - -- Number of groups landing. - local nlanding=self:CountFlights(FLIGHTCONTROL.FlightStatus.LANDING) - - -- Number of parking groups. - local nparking=self:CountFlights(FLIGHTCONTROL.FlightStatus.PARKING) - - -- Number of groups taking off. - local ntakeoff=self:CountFlights(FLIGHTCONTROL.FlightStatus.TAKEOFF) - - - -- Get next flight in line: either holding or parking. - local flight, isholding, parking=self:_GetNextFlight() - - - -- Check if somebody wants something. - if flight then - - if isholding then - - -------------------- - -- Holding flight -- - -------------------- - - -- No other flight is taking off and number of landing flights is below threshold. - if ntakeoff==0 and nlanding=self.dTlanding then - - -- Message. - local text=string.format("Flight %s, you are cleared to land.", flight.groupname) - MESSAGE:New(text, 5, "FLIGHTCONTROL"):ToAll() - - -- Give AI the landing signal. - -- TODO: Humans have to confirm via F10 menu. - if flight.isAI then - self:_LandAI(flight, parking) - end - - -- Set time last flight got landing clearance. - self.Tlanding=timer.getAbsTime() - - end - else - self:I(self.lid..string.format("FYI: Landing clearance for flight %s denied as other flights are taking off (N=%d) or max. landing reached (N=%d/%d).", flight.groupname, ntakeoff, nlanding, self.Nlanding)) - end - - else - - -------------------- - -- Takeoff flight -- - -------------------- - - -- No other flight is taking off or landing. - if ntakeoff==0 and nlanding==0 then - - -- Check if flight is AI. Humans have to request taxi via F10 menu. - if flight.isAI then - - --- - -- AI - --- - - -- Message. - 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. - if flight:IsUncontrolled() then - flight:StartUncontrolled() - end - - -- Add flight to takeoff queue. - self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.TAKEOFF) - - -- Remove parking guards. - for _,_element in pairs(flight.elements) do - local element=_element --Ops.FlightGroup#FLIGHTGROUP.Element - if element and element.parking then - local spot=self:GetParkingSpotByID(element.parking.TerminalID) - self:RemoveParkingGuard(spot) - end - end - - else - - --- - -- PLAYER - --- - - local text=string.format("HUMAN Flight %s, you are cleared for takeoff.", flight.groupname) - self:I(self.lid..text) - MESSAGE:New(text, 5, "FLIGHTCONTROL"):ToAll() - - end - - else - self:I(self.lid..string.format("FYI: Take of for flight %s denied as other flights are taking off (N=%d) or landing (N=%d).", flight.groupname, ntakeoff, nlanding)) - end - end - else - self:I(self.lid..string.format("FYI: No flight in queue for takeoff or landing.")) - end - -end - ---- Get next flight in line, either waiting for landing or waiting for takeoff. --- @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. --- @return #boolean If true, flight is holding and waiting for landing, if false, flight is parking and waiting for takeoff. --- @return #table Parking data for holding flights or nil. -function FLIGHTCONTROL:_GetNextFlight() - - local flightholding=self:_GetNextFightHolding() - local flightparking=self:_GetNextFightParking() - - -- If no flight is waiting for landing just return the takeoff flight or nil. - if not flightholding then - return flightparking, false, nil - end - - -- Get number of alive elements of the holding flight. - local nH=flightholding:GetNelements() - - -- Free parking spots. - local parking=flightholding:GetParking(self.airbase) - - - -- If no flight is waiting for takeoff return the holding flight or nil. - if not flightparking then - if parking then - return flightholding, true, parking - else - self:E(self.lid..string.format("WARNING: No flight parking but no parking spots! nP=%d nH=%d", #parking, nH)) - return nil, nil, nil - end - end - - - - -- We got flights waiting for landing and for takeoff. - if flightholding and flightparking then - - -- Return holding flight if fuel is low. - if flightholding.fuellow then - if parking then - -- Enough parking ==> land - return flightholding, true, parking - else - -- Not enough parking ==> take off - return flightparking, false, nil - end - end - - -- Return the flight which is waiting longer. NOTE that Tholding and Tparking are abs. mission time. So a smaller value means waiting longer. - if flightholding.Tholding0 then - - -- TODO: Could be sorted by distance to active runway! Take the runway spawn point for distance measure. - - -- First come, first serve. - return QreadyTO[1] - - end - - -- Get flights parking. - local Qparking=self:GetFlights(FLIGHTCONTROL.FlightStatus.PARKING) - - -- Check special cases where only up to one flight is waiting for takeoff. - if #Qparking==0 then - return nil - end - - -- Sort flights parking time. - local function _sortByTparking(a, b) - local flightA=a --Ops.FlightGroup#FLIGHTGROUP - local flightB=b --Ops.FlightGroup#FLIGHTGROUP - return flightA.Tparking=0 then - holding=UTILS.SecondsToClock(holding, true) - else - holding="X" - end - local parking=flight:GetParkingTime() - if parking>=0 then - parking=UTILS.SecondsToClock(parking, true) - else - parking="X" - end - - - local nunits=flight.nunits or 1 - - -- Main info. - text=text..string.format("\n[%d] %s (%s*%d): status=%s, ai=%s, fuel=%d, holding=%s, parking=%s", - i, flight.groupname, actype, nunits, flight:GetState(), ai, fuel, holding, parking) - - -- Elements info. - for j,_element in pairs(flight.elements) do - local element=_element --Ops.FlightGroup#FLIGHTGROUP.Element - local life=element.unit:GetLife() - local life0=element.unit:GetLife0() - local park=element.parking and tostring(element.parking.TerminalID) or "N/A" - text=text..string.format("\n (%d) %s (%s): status=%s, ai=%s, airborne=%s life=%d/%d spot=%s", - j, tostring(element.modex), element.name, tostring(element.status), tostring(element.ai), tostring(element.unit:InAir()), life, life0, park) - end - end - end - - -- Display text. - self:I(self.lid..text) - - return text -end - ---- Remove a flight group from a queue. --- @param #FLIGHTCONTROL self --- @param #table queue The queue from which the group will be removed. --- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group that will be removed from queue. --- @param #string queuename Name of the queue. --- @return #boolean True, flight was in Queue and removed. False otherwise. --- @return #number Table index of removed queue element or nil. -function FLIGHTCONTROL:_RemoveFlightFromQueue(queue, flight, queuename) - - queuename=queuename or "unknown" - - -- Loop over all flights in group. - for i,_flight in pairs(queue) do - local qflight=_flight --Ops.FlightGroup#FLIGHTGROUP - - -- Check for name. - if qflight.groupname==flight.groupname then - self:I(self.lid..string.format("Removing flight group %s from %s queue.", flight.groupname, queuename)) - table.remove(queue, i) - - if not flight.isAI then - if flight.flightcontrol and flight.flightcontrol.airbasename==self.airbasename then - flight.flightcontrol=nil - end - flight:_UpdateMenu(0.1) - end - - return true, i - end - end - - self:I(self.lid..string.format("Could NOT remove flight group %s from %s queue.", flight.groupname, queuename)) - return false, nil -end - - ---- Set flight status. --- @param #FLIGHTCONTROL self --- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group. --- @param #string status New status. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:SetFlightStatus(flight, status) - - -- Debug info. - self:I(self.lid..string.format("New Flight Status for %s [%s]: %s-->%s", flight:GetName(), flight:GetState(), tostring(flight.controlstatus), status)) - - -- Set control status - flight.controlstatus=status - - return self -end - ---- Get flight status. --- @param #FLIGHTCONTROL self --- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group. --- @return #string Flight status -function FLIGHTCONTROL:GetFlightStatus(flight) - - if flight then - return flight.controlstatus or "unkonwn" - end - - return "unknown" -end - ---- Check if FC has control over this flight. --- @param #FLIGHTCONTROL self --- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group. --- @return #boolean If true, this FC is controlling this flight group. -function FLIGHTCONTROL:IsControlling(flight) - return flight.flightcontrol and flight.flightcontrol.airbasename==self.airbasename or false -end - - ---- Check if a group is in a queue. --- @param #FLIGHTCONTROL self --- @param #table queue The queue to check. --- @param Wrapper.Group#GROUP group The group to be checked. --- @return #boolean If true, group is in the queue. False otherwise. -function FLIGHTCONTROL:_InQueue(queue, group) - local name=group:GetName() - - for _,_flight in pairs(queue) do - local flight=_flight --Ops.FlightGroup#FLIGHTGROUP - if name==flight.groupname then - return true - end - end - - return false -end - ---- Get flights. --- @param #FLIGHTCONTROL self --- @param #string Status Return only flights in this status. --- @return #table Table of flights. -function FLIGHTCONTROL:GetFlights(Status) - - if Status then - - local flights={} - - for _,_flight in pairs(self.flights) do - local flight=_flight --Ops.FlightGroup#FLIGHTGROUP - - local status=self:GetFlightStatus(flight, Status) - - if status==Status then - table.insert(flights, flight) - end - - end - - return flights - else - return self.flights - end - -end - ---- Count flights in a given status. --- @param #FLIGHTCONTROL self --- @param #string Status Return only flights in this status. --- @return #number -function FLIGHTCONTROL:CountFlights(Status) - - if Status then - - local flights=self:GetFlights(Status) - - return #flights - - else - return #self.flights - end - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Runway Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Initialize data of runways. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:_InitRunwayData() - self.runways=self.airbase:GetRunwayData() -end - ---- Get the active runway based on current wind direction. --- @param #FLIGHTCONTROL self --- @return Wrapper.Airbase#AIRBASE.Runway Active runway. -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 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Init parking spots. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:_InitParkingSpots() - - -- Parking spots of airbase. - local parkingdata=self.airbase:GetParkingSpotsTable() - - -- Init parking spots table. - self.parking={} - - self.Nparkingspots=0 - for _,_spot in pairs(parkingdata) do - local spot=_spot --Wrapper.Airbase#AIRBASE.ParkingSpot - - - -- Mark position. - local text=string.format("Parking ID=%d, Terminal=%d: Free=%s, Client=%s, Dist=%.1f", spot.TerminalID, spot.TerminalType, tostring(spot.Free), tostring(spot.ClientSpot), spot.DistToRwy) - self:I(self.lid..text) - - -- Add to table. - self.parking[spot.TerminalID]=spot - - spot.Marker=MARKER:New(spot.Coordinate, "Spot"):ReadOnly() - spot.Marker.tocoaliton=true - spot.Marker.coalition=self:GetCoalition() - - -- Check if spot is initially free or occupied. - if spot.Free then - - -- Parking spot is free. - self:SetParkingFree(spot) - - else - - -- Scan for the unit sitting here. - local unit=spot.Coordinate:FindClosestUnit(20) - - - if unit then - - local unitname=unit and unit:GetName() or "unknown" - - local isalive=unit:IsAlive() - - env.info(string.format("FF parking spot %d is occupied by unit %s alive=%s", spot.TerminalID, unitname, tostring(isalive))) - - if isalive then - - - self:SetParkingOccupied(spot, unitname) - - self:SpawnParkingGuard(unit) - - else - - -- TODO - env.info(string.format("FF parking spot %d is occupied by NOT ALIVE unit %s", spot.TerminalID, unitname)) - - -- Parking spot is free. - self:SetParkingFree(spot) - - end - - else - self:I(self.lid..string.format("ERROR: Parking spot is NOT FREE but no unit could be found there!")) - end - end - - -- Increase counter - self.Nparkingspots=self.Nparkingspots+1 - end - -end - ---- Get parking spot by its Terminal ID. --- @param #FLIGHTCONTROL self --- @param #number TerminalID --- @return #FLIGHTCONTROL.ParkingSpot Parking spot data table. -function FLIGHTCONTROL:GetParkingSpotByID(TerminalID) - return self.parking[TerminalID] -end - ---- Set parking spot to FREE and update F10 marker. --- @param #FLIGHTCONTROL self --- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot The parking spot data table. -function FLIGHTCONTROL:SetParkingFree(spot) - - local spot=self:GetParkingSpotByID(spot.TerminalID) - - -- Debug info. - self:I(self.lid..string.format("Parking spot %d: %s-->%s", spot.TerminalID, tostring(spot.Status), AIRBASE.SpotStatus.FREE)) - - spot.Status=AIRBASE.SpotStatus.FREE - spot.OccupiedBy=nil - spot.ReservedBy=nil - - self:UpdateParkingMarker(spot) - -end - ---- Set parking spot to RESERVED and update F10 marker. --- @param #FLIGHTCONTROL self --- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot The parking spot data table. --- @param #string unitname Name of the unit occupying the spot. Default "unknown". -function FLIGHTCONTROL:SetParkingReserved(spot, unitname) - - local spot=self:GetParkingSpotByID(spot.TerminalID) - - -- Debug info. - self:I(self.lid..string.format("Parking spot %d: %s-->%s", spot.TerminalID, tostring(spot.Status), AIRBASE.SpotStatus.RESERVED)) - - spot.Status=AIRBASE.SpotStatus.RESERVED - spot.ReservedBy=unitname or "unknown" - - self:UpdateParkingMarker(spot) - -end - ---- Set parking spot to OCCUPIED and update F10 marker. --- @param #FLIGHTCONTROL self --- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot The parking spot data table. --- @param #string unitname Name of the unit occupying the spot. Default "unknown". -function FLIGHTCONTROL:SetParkingOccupied(spot, unitname) - - local spot=self:GetParkingSpotByID(spot.TerminalID) - - -- Debug info. - self:I(self.lid..string.format("Parking spot %d: %s-->%s", spot.TerminalID, tostring(spot.Status), AIRBASE.SpotStatus.OCCUPIED)) - - spot.Status=AIRBASE.SpotStatus.OCCUPIED - spot.OccupiedBy=unitname or "unknown" - - self:UpdateParkingMarker(spot) - -end - ---- Get free parking spots. --- @param #FLIGHTCONTROL self --- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot The parking spot data table. -function FLIGHTCONTROL:UpdateParkingMarker(spot) - - local spot=self:GetParkingSpotByID(spot.TerminalID) - - --env.info(string.format("FF updateing spot %d status=%s", spot.TerminalID, spot.Status)) - - -- Only mark OCCUPIED and RESERVED spots. - if spot.Status==AIRBASE.SpotStatus.FREE then - - if spot.Marker then - spot.Marker:Remove() - end - - else - - local text=string.format("Spot %d (type %d): %s", spot.TerminalID, spot.TerminalType, spot.Status:upper()) - if spot.OccupiedBy then - text=text..string.format("\nOccupied by %s", spot.OccupiedBy) - end - if spot.ReservedBy then - text=text..string.format("\nReserved for %s", spot.ReservedBy) - end - if spot.ClientSpot then - text=text..string.format("\nClient %s", tostring(spot.ClientSpot)) - end - - if spot.Marker then - - if text~=spot.Marker.text then - spot.Marker:UpdateText(text) - end - - else - - spot.Marker=MARKER:New(spot.Coordinate, text):ToAll() - - end - - end -end - ---- Check if parking spot is free. --- @param #FLIGHTCONTROL self --- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot Parking spot data. --- @return #boolean If true, parking spot is free. -function FLIGHTCONTROL:IsParkingFree(spot) - return spot.Status==AIRBASE.SpotStatus.FREE -end - ---- Check if a parking spot is reserved by a flight group. --- @param #FLIGHTCONTROL self --- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot Parking spot to check. --- @return #string Name of element or nil. -function FLIGHTCONTROL:IsParkingOccupied(spot) - - if spot.Status==AIRBASE.SpotStatus.OCCUPIED then - return tostring(spot.OccupiedBy) - else - return false - end -end - ---- Check if a parking spot is reserved by a flight group. --- @param #FLIGHTCONTROL self --- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot Parking spot to check. --- @return #string Name of element or *nil*. -function FLIGHTCONTROL:IsParkingReserved(spot) - - if spot.Status==AIRBASE.SpotStatus.RESERVED then - return tostring(spot.ReservedBy) - else - return false - end - - -- Init all elements as NOT parking anywhere. - for _,_flight in pairs(self.flights) do - local flight=_flight --Ops.FlightGroup#FLIGHTGROUP - -- Loop over all elements. - for _,_element in pairs(flight.elements) do - local element=_element --Ops.FlightGroup#FLIGHTGROUP.Element - local parking=element.parking - if parking and parking.TerminalID==spot.TerminalID then - return element.name - end - end - end - - return nil -end - ---- Get free parking spots. --- @param #FLIGHTCONTROL self --- @param #number terminal Terminal type or nil. --- @return #number Number of free spots. Total if terminal=nil or of the requested terminal type. --- @return #table Table of free parking spots of data type #FLIGHCONTROL.ParkingSpot. -function FLIGHTCONTROL:_GetFreeParkingSpots(terminal) - - local freespots={} - - local n=0 - for _,_parking in pairs(self.parking) do - local parking=_parking --Wrapper.Airbase#AIRBASE.ParkingSpot - - if self:IsParkingFree(parking) then - if terminal==nil or terminal==parking.terminal then - n=n+1 - table.insert(freespots, parking) - end - end - end - - return n,freespots -end - ---- Get closest parking spot. --- @param #FLIGHTCONTROL self --- @param Core.Point#COORDINATE coordinate Reference coordinate. --- @param #number terminaltype (Optional) Check only this terminal type. --- @param #boolean free (Optional) If true, check only free spots. --- @return #FLIGHTCONTROL.ParkingSpot Closest parking spot. -function FLIGHTCONTROL:GetClosestParkingSpot(coordinate, terminaltype, free) - - local distmin=math.huge - local spotmin=nil - - for TerminalID, Spot in pairs(self.parking) do - local spot=Spot --Wrapper.Airbase#AIRBASE.ParkingSpot - - if (not free) or (free==true and not (self:IsParkingReserved(spot) or self:IsParkingOccupied(spot))) then - if terminaltype==nil or terminaltype==spot.TerminalType then - - -- Get distance from coordinate to spot. - local dist=coordinate:Get2DDistance(spot.Coordinate) - - -- Check if distance is smaller. - if dist0 then - MESSAGE:New("Negative ghostrider, other flights are currently landing. Talk to you soon.", 5):ToAll() - self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.READYTO) - elseif Ntakeoff>0 then - MESSAGE:New("Negative ghostrider, other flights are ahead of you. Talk to you soon.", 5):ToAll() - self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.READYTO) - end - - else - MESSAGE:New(string.format("Negative, you must request TAXI before you can request TAKEOFF!"), 5):ToAll() - end - end - -end - ---- Player wants to abort takeoff. --- @param #FLIGHTCONTROL self --- @param #string groupname Name of the flight group. -function FLIGHTCONTROL:_PlayerAbortTakeoff(groupname) - - MESSAGE:New("Abort takeoff", 5):ToAll() - - local flight=_DATABASE:GetOpsGroup(groupname) - - if flight then - - if self:GetFlightStatus(flight)==FLIGHTCONTROL.FlightStatus.TAKEOFF then - - - MESSAGE:New("Afirm, You are removed from takeoff queue", 5):ToAll() - - --TODO: what now? taxi inbound? or just another later attempt to takeoff. - self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.READYTO) - - - else - MESSAGE:New("Negative, You are NOT in the takeoff queue", 5):ToAll() - end - - end - -end - ---- Player wants to abort inbound. --- @param #FLIGHTCONTROL self --- @param #string groupname Name of the flight group. -function FLIGHTCONTROL:_PlayerAbortInbound(groupname) - - MESSAGE:New("Abort inbound", 5):ToAll() - - local flight=_DATABASE:GetOpsGroup(groupname) - - if flight then - - local flightstatus=self:GetFlightStatus(flight) - if flightstatus==FLIGHTCONTROL.FlightStatus.INBOUND or flightstatus==FLIGHTCONTROL.FlightStatus.HOLDING or flightstatus==FLIGHTCONTROL.FlightStatus.LANDING then - - MESSAGE:New("Afirm, You are removed from all queues queue", 5):ToAll() - - --TODO: what now? taxi inbound? or just another later attempt to takeoff. - self:SetFlightStatus(flight,FLIGHTCONTROL.FlightStatus.UNKNOWN) - - -- Remove flight. - self:_RemoveFlight(flight) - - -- Trigger cruise event. - flight:Cruise() - - - else - MESSAGE:New("Negative, You are NOT in the state INBOUND, HOLDING or LANDING!", 5):ToAll() - end - - end - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Flight and Element Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Create a new flight group. --- @param #FLIGHTCONTROL self --- @param Wrapper.Group#GROUP group Aircraft group. --- @return Ops.FlightGroup#FLIGHTGROUP Flight group. -function FLIGHTCONTROL:_CreateFlightGroup(group) - - -- Check if not already in flights - if self:_InQueue(self.flights, group) then - self:E(self.lid..string.format("WARNING: Flight group %s does already exist!", group:GetName())) - return - end - - -- Debug info. - self:I(self.lid..string.format("Creating new flight for group %s of aircraft type %s.", group:GetName(), group:GetTypeName())) - - -- Get flightgroup from data base. - local flight=_DATABASE:GetOpsGroup(group:GetName()) - - -- If it does not exist yet, create one. - if not flight then - flight=FLIGHTGROUP:New(group:GetName()) - end - - --if flight.destination and flight.destination:GetName()==self.airbasename then - if flight.homebase and flight.homebase:GetName()==self.airbasename then - flight:SetFlightControl(self) - end - - flight:SetVerbosity(2) - - return flight -end - ---- Remove flight from all queues. --- @param #FLIGHTCONTROL self --- @param Ops.FlightGroup#FLIGHTGROUP flight The flight to be removed. -function FLIGHTCONTROL:_RemoveFlight(flight) - - self:_RemoveFlightFromQueue(self.flights, flight, "flights") - -end - ---- Get flight from group. --- @param #FLIGHTCONTROL self --- @param Wrapper.Group#GROUP group Group that will be removed from queue. --- @param #table queue The queue from which the group will be removed. --- @return Ops.FlightGroup#FLIGHTGROUP Flight group or nil. --- @return #number Queue index or nil. -function FLIGHTCONTROL:_GetFlightFromGroup(group) - - if group then - - -- Group name - local name=group:GetName() - - -- Loop over all flight groups in queue - for i,_flight in pairs(self.flights) do - local flight=_flight --Ops.FlightGroup#FLIGHTGROUP - - if flight.groupname==name then - return flight, i - end - end - - self:T2(self.lid..string.format("WARNING: Flight group %s could not be found in queue.", name)) - end - - self:T2(self.lid..string.format("WARNING: Flight group could not be found in queue. Group is nil!")) - return nil, nil -end - ---- Get element of flight from its unit name. --- @param #FLIGHTCONTROL self --- @param #string unitname Name of the unit. --- @return #FLIGHTCONTROL.FlightElement Element of the flight or nil. --- @return #number Element index or nil. --- @return Ops.FlightGroup#FLIGHTGROUP The Flight group or nil. -function FLIGHTCONTROL:_GetFlightElement(unitname) - - -- Get the unit. - local unit=UNIT:FindByName(unitname) - - -- Check if unit exists. - if unit then - - -- Get flight element from all flights. - local flight=self:_GetFlightFromGroup(unit:GetGroup()) - - -- Check if fight exists. - if flight then - - -- Loop over all elements in flight group. - for i,_element in pairs(flight.elements) do - local element=_element --#FLIGHTCONTROL.FlightElement - - if element.unit:GetName()==unitname then - return element, i, flight - end - end - - self:T2(self.lid..string.format("WARNING: Flight element %s could not be found in flight group.", unitname, flight.groupname)) - end - end - - return nil, nil, nil -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Check Sanity Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Check status of all registered flights and do some sanity checks. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:_CheckFlights() - - -- First remove all dead flights. - for i=#self.flights,1,-1 do - local flight=self.flights[i] --Ops.FlightGroup#FLIGHTGROUP - if flight:IsDead() then - self:I(self.lid..string.format("Removing DEAD flight %s", tostring(flight.groupname))) - self:_RemoveFlight(flight) - end - end - - --TODO: check parking? - -end - ---- Check status of all registered flights and do some sanity checks. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:_CheckParking() - - for TerminalID,_spot in pairs(self.parking) do - local spot=_spot --Wrapper.Airbase#AIRBASE.ParkingSpot - - if spot.Reserved then - if spot.MarkerID then - spot.Coordinate:RemoveMark(spot.MarkerID) - end - spot.MarkerID=spot.Coordinate:MarkToCoalition(string.format("Parking reserved for %s", tostring(spot.Reserved)), self:GetCoalition()) - end - - -- First remove all dead flights. - for i=1,#self.flights do - local flight=self.flights[i] --Ops.FlightGroup#FLIGHTGROUP - for _,_element in pairs(flight.elements) do - local element=_element --Ops.FlightGroup#FLIGHTGROUP.Element - if element.parking and element.parking.TerminalID==TerminalID then - if spot.MarkerID then - spot.Coordinate:RemoveMark(spot.MarkerID) - end - spot.MarkerID=spot.Coordinate:MarkToCoalition(string.format("Parking spot occupied by %s", tostring(element.name)), self:GetCoalition()) - end - end - end - - end - - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Routing Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Tell AI to land at the airbase. Flight is added to the landing queue. --- @param #FLIGHTCONTROL self --- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group. --- @param #table parking Free parking spots table. -function FLIGHTCONTROL:_LandAI(flight, parking) - - -- Debug info. - self:I(self.lid..string.format("Landing AI flight %s.", flight.groupname)) - - -- Set flight status to LANDING. - self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.LANDING) - - -- Flight is not holding any more. - flight.Tholding=nil - - - local respawn=false - - if respawn then - - -- Get group template. - local Template=flight.group:GetTemplate() - - -- TODO: get landing waypoints from flightgroup. - - -- Set route points. - Template.route.points=wp - - for i,unit in pairs(Template.units) do - local spot=parking[i] --Wrapper.Airbase#AIRBASE.ParkingSpot - - local element=flight:GetElementByName(unit.name) - if element then - - -- Set the parking spot at the destination airbase. - unit.parking_landing=spot.TerminalID - - local text=string.format("FF Reserving parking spot %d for unit %s", spot.TerminalID, tostring(unit.name)) - self:I(self.lid..text) - - -- Set parking to RESERVED. - self:SetParkingReserved(spot, element.name) - - else - env.info("FF error could not get element to assign parking!") - end - end - - -- Debug message. - MESSAGE:New(string.format("Respawning group %s", flight.groupname)):ToAll() - - --Respawn the group. - flight:Respawn(Template) - - else - - -- Give signal to land. - flight:ClearToLand() - - end - -end - ---- Get holding point. --- @param #FLIGHTCONTROL self --- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group. --- @return #FLIGHTCONTROL.HoldingPoint Holding point. -function FLIGHTCONTROL:_GetHoldingpoint(flight) - - local holdingpoint={} --#FLIGHTCONTROL.HoldingPoint - - local runway=self:GetActiveRunway() - - local hdg=runway.heading+90 - local dx=UTILS.NMToMeters(5) - local dz=UTILS.NMToMeters(1) - - local angels=UTILS.FeetToMeters(math.random(6,10)*1000) - - holdingpoint.pos0=runway.position:Translate(dx, hdg):SetAltitude(angels) - holdingpoint.pos1=holdingpoint.pos0:Translate(dz, runway.heading):SetAltitude(angels) - - return holdingpoint -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Radio Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Transmission via RADIOQUEUE. --- @param #FLIGHTCONTROL self --- @param #FLIGHTCONTROL.Soundfile sound FLIGHTCONTROL sound object. --- @param #number interval Interval in seconds after the last transmission finished. --- @param #string subtitle Subtitle of the transmission. --- @param #string path Path to sound file. Default self.soundpath. -function FLIGHTCONTROL:Transmission(sound, interval, subtitle, path) - self.radioqueue:NewTransmission(sound.filename, sound.duration, path or self.soundpath, nil, interval, subtitle, self.subduration) -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Misc Functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Add parking guard in front of a parking aircraft. --- @param #FLIGHTCONTROL self --- @param Wrapper.Unit#UNIT unit The aircraft. -function FLIGHTCONTROL:SpawnParkingGuard(unit) - - if unit and self.parkingGuard then - - -- Position of the unit. - local coordinate=unit:GetCoordinate() - - -- Parking spot. - local spot=self:GetClosestParkingSpot(coordinate) - - -- Current heading of the unit. - local heading=unit:GetHeading() - - -- Length of the unit + 3 meters. - local size, x, y, z=unit:GetObjectSize() - - self:I(self.lid..string.format("Parking guard for %s: heading=%d, distance x=%.1f m", unit:GetName(), heading, x)) - - -- Coordinate for the guard. - local Coordinate=coordinate:Translate(0.75*x+3, heading) - - -- Let him face the aircraft. - local lookat=heading-180 - - -- Set heading and AI off to save resources. - self.parkingGuard:InitHeading(lookat):InitAIOff() - - -- Group that is spawned. - spot.ParkingGuard=self.parkingGuard:SpawnFromCoordinate(Coordinate) - --spot.ParkingGuard=self.parkingGuard:SpawnFromCoordinate(Coordinate, lookat) - - end - -end - ---- Remove parking guard. --- @param #FLIGHTCONTROL self --- @param #FLIGHTCONTROL.ParkingSpot spot --- @param #number delay Delay in seconds. -function FLIGHTCONTROL:RemoveParkingGuard(spot, delay) - - if delay and delay>0 then - self:ScheduleOnce(delay, FLIGHTCONTROL.RemoveParkingGuard, self, spot) - else - - if spot.ParkingGuard then - self:I(self.lid..string.format("Removing parking guard at spot %d", spot.TerminalID)) - spot.ParkingGuard:Destroy() - spot.ParkingGuard=nil - end - - end - -end - - ---- Get coordinate of the airbase. --- @param #FLIGHTCONTROL self --- @return Core.Point#COORDINATE Coordinate of the airbase. -function FLIGHTCONTROL:GetCoordinate() - return self.airbase:GetCoordinate() -end - ---- Get coalition of the airbase. --- @param #FLIGHTCONTROL self --- @return #number Coalition ID. -function FLIGHTCONTROL:GetCoalition() - return self.airbase:GetCoalition() -end - ---- Get country of the airbase. --- @param #FLIGHTCONTROL self --- @return #number Country ID. -function FLIGHTCONTROL:GetCountry() - return self.airbase:GetCountry() -end - ---- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned. --- @param #FLIGHTCONTROL self --- @param #string unitName Name of the player unit. --- @return Wrapper.Unit#UNIT Unit of player or nil. --- @return #string Name of the player or nil. -function FLIGHTCONTROL:_GetPlayerUnitAndName(unitName) - - if unitName then - - -- Get DCS unit from its name. - local DCSunit=Unit.getByName(unitName) - - if DCSunit then - - -- Get player name if any. - local playername=DCSunit:getPlayerName() - - -- Unit object. - local unit=UNIT:Find(DCSunit) - - -- Check if enverything is there. - if DCSunit and unit and playername then - self:T(self.lid..string.format("Found DCS unit %s with player %s", tostring(unitName), tostring(playername))) - return unit, playername - end - - end - - end - - -- Return nil if we could not find a player. - return nil,nil -end - - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 7a1553aff..7085a12e9 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -28,7 +28,6 @@ Core/SpawnStatic.lua Core/Timer.lua Core/Goal.lua Core/Spot.lua -Core/F10Menu.lua Wrapper/Object.lua Wrapper/Identifiable.lua @@ -89,7 +88,6 @@ Ops/Brigade.lua Ops/Intelligence.lua Ops/Commander.lua Ops/Chief.lua -Ops/FlightControl.lua Ops/CSAR.lua Ops/CTLD.lua