diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 17a014b85..d8982abcf 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -1307,7 +1307,12 @@ function FLIGHTCONTROL:_CreatePlayerMenu(flight, atcmenu) end else if flight:IsAirborne() then - MENU_GROUP_COMMAND_DELAYED:New(group, "Inbound", rootmenu, self._PlayerInbound, self, groupname):SetTime(Tnow):SetTag(Tag) + if self:_InQueue(self.Qinbound, flight.group) then + MENU_GROUP_COMMAND_DELAYED:New(group, "Holding", rootmenu, self._PlayerInbound, self, groupname):SetTime(Tnow):SetTag(Tag) + + else + MENU_GROUP_COMMAND_DELAYED:New(group, "Inbound", rootmenu, self._PlayerHolding, self, groupname):SetTime(Tnow):SetTag(Tag) + end end end @@ -1447,10 +1452,10 @@ function FLIGHTCONTROL:_PlayerInbound(groupname) end - -- TODO: Better check in holding zone and then add to queue. - self:_AddFlightToHoldingQueue(flight) + -- Add flight to inbound queue. + self:_AddFlightToInboundQueue(flight) - local text=string.format("You have been added to the holding queue!") + local text=string.format("You have been added to the inbound queue!\nFly heading XYZ for ABC and report status when entering the holding pattern") MESSAGE:New(text, 5):ToGroup(flight.group) else @@ -1465,6 +1470,44 @@ function FLIGHTCONTROL:_PlayerInbound(groupname) end +--- Player calls holding. +-- @param #FLIGHTCONTROL self +-- @param #string groupname Name of the flight group. +function FLIGHTCONTROL:_PlayerHolding(groupname) + + -- Get flight group. + local flight=_DATABASE:GetFlightGroup(groupname) + + if flight then + + if flight:IsAirborne() then + + if flight.flightcontrol and flight.flightcontrol.airbasename==self.airbasename then + + --TODO: create holding zone and check inside zone. + -- Error you are not airborne! + local text=string.format("Roger, you are added to the holding queue!") + MESSAGE:New(text, 5):ToGroup(flight.group) + + -- Call holding event. + flight:Holding() + + else + + -- Error you are not airborne! + local text=string.format("Negative, you are not controlled by us!") + MESSAGE:New(text, 5):ToGroup(flight.group) + + end + else + -- Error you are not airborne! + local text=string.format("Negative, you must be AIRBORNE to call HOLDING!") + MESSAGE:New(text, 5):ToGroup(flight.group) + end + else + end +end + --- Create player menu. -- @param #FLIGHTCONTROL self -- @param #string groupname Name of the flight group. @@ -1553,6 +1596,30 @@ function FLIGHTCONTROL:_PlayerRequestTakeoff(groupname) 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:GetFlightGroup(groupname) + + if flight then + + if self:_InQueue(self.Qtakeoff, flight.group) then + MESSAGE:New("Afirm, You are removed from takeoff queue", 5):ToAll() + self:_RemoveFlightFromQueue(self.Qtakeoff, flight, "takeoff") + + --TODO: what now? taxi inbound? or just another later attempt to takeoff. + else + MESSAGE:New("Negative, You are NOT in the takeoff queue", 5):ToAll() + end + + end + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Flight and Element Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1692,6 +1759,31 @@ function FLIGHTCONTROL:_CheckFlights() end --TODO: check parking? + + -- Check if parking flights started to taxi. + for _,_flight in pairs(self.Qparking) do + local flight=_flight --Ops.FlightGroup#FLIGHTGROUP + for _,_element in pairs(flight.elements) do + local element=_element --Ops.FlightGroup#FLIGHTGROUP.Element + if element.parking then + -- Get distance to assigned parking spot. + local dist=element.unit:GetCoordinate():Get2DDistance(element.parking.Coordinate) + + -- If distance >10 meters or velocity of unit is >0, we consider the unit as taxiing. + -- TODO: Check distance threshold! If element is taxiing, the parking spot is free again. + -- When the next plane is spawned on this spot, collisions should be avoided! + if dist>10 or element.unit:GetVelocityMPS()>0 then + if element.status==FLIGHTGROUP.ElementStatus.ENGINEON then + self.flight:ElementTaxiing(element) + end + end + + else + self:E(self.lid..string.format("Element %s is in PARKING queue but has no parking spot assigned!", element.name)) + end + end + + end end diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 386035021..5519f4ff7 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -30,6 +30,10 @@ -- @field #table taskqueue Queue of tasks. -- @field #number taskcounter Running number of task ids. -- @field #number taskcurrent ID of current task. If 0, there is no current task assigned. +-- @field #table taskenroute Enroute task of the group. +-- @field #boolean istanker If true, group gets enroute task tanker. +-- @field #boolean isawacs if True, group get enroute task AWACS. +-- @field #boolean eplrs If true, group will activate it's datalink. -- @field Core.Set#SET_UNIT detectedunits Set of detected units. -- @field Wrapper.Airbase#AIRBASE homebase The home base of the flight group. -- @field Wrapper.Airbase#AIRBASE destination The destination base of the flight group. @@ -112,6 +116,10 @@ FLIGHTGROUP = { taskqueue = {}, taskcounter = nil, taskcurrent = nil, + taskenroute = nil, + istanker = nil, + isawacs = nil, + eplrs = nil, detectedunits = {}, homebase = nil, destination = nil, @@ -143,6 +151,7 @@ FLIGHTGROUP = { -- @field #string INUTERO Element was not spawned yet or its status is unknown so far. -- @field #string SPAWNED Element was spawned into the world. -- @field #string PARKING Element is parking after spawned on ramp. +-- @field #string ENGINEON Element started its engines. -- @field #string TAXIING Element is taxiing after engine startup. -- @field #string TAKEOFF Element took of after takeoff event. -- @field #string AIRBORNE Element is airborne. Either after takeoff or after air start. @@ -154,6 +163,7 @@ FLIGHTGROUP.ElementStatus={ INUTERO="inutero", SPAWNED="spawned", PARKING="parking", + ENGINEON="engineon", TAXIING="taxiing", TAKEOFF="takeoff", AIRBORNE="airborne", @@ -526,6 +536,15 @@ function FLIGHTGROUP:AddTaskWaypoint(description, task, waypointindex, prio, dur return self end +--- Set squadron the flight group belongs to. +-- @param #FLIGHTGROUP self +-- @param #boolean If true or nil, group get enroute task tanker. If false, not. +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SetTanker(swtich) + self.istanker=true + return self +end + --- Set squadron the flight group belongs to. -- @param #FLIGHTGROUP self -- @param Ops.Squadron#SQUADRON squadron The squadron object. @@ -1177,7 +1196,7 @@ function FLIGHTGROUP:OnEventEngineShutdown(EventData) if element.unit and element.unit:IsAlive() then local airbase=element.unit:GetCoordinate():GetClosestAirbase() - local parking=self:GetParkingSpot(element, 10) + local parking=self:GetParkingSpot(element, 10, airbase) if airbase and parking then self:ElementArrived(element, airbase, parking) @@ -1305,6 +1324,21 @@ function FLIGHTGROUP:onafterElementParking(From, Event, To, Element) end end +--- On after "ElementEngineOn" event. +-- @param #FLIGHTGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #FLIGHTGROUP.Element Element The flight group element. +function FLIGHTGROUP:onafterElementEngineOn(From, Event, To, Element) + + -- Debug info. + self:I(self.lid..string.format("Element %s started engines", Element.name)) + + -- Set element status. + self:_UpdateStatus(Element, FLIGHTGROUP.ElementStatus.ENGINEON) +end + --- On after "ElementTaxiing" event. -- @param #FLIGHTGROUP self -- @param #string From From state. @@ -1563,6 +1597,18 @@ end -- @param Wrapper.Airbase#AIRBASE airbase The airbase the flight landed. function FLIGHTGROUP:onafterFlightAirborne(From, Event, To, airbase) self:T(self.lid..string.format("Flight airborne %s at %s.", self.groupname,airbase and airbase:GetName() or "unknown airbase")) + + local task=nil + if self.istanker then + task=self.group:EnRouteTaskTanker() + end + if self.isawacs then + task=self.group:EnRouteTaskAWACS() + end + + if task then + self.group:PushTask(task, 0.1) + end -- Update queue. self:__QueueUpdate(-1) @@ -2009,6 +2055,9 @@ function FLIGHTGROUP:onafterHolding(From, Event, To) -- Add flight to holding queue. self.flightcontrol:_RemoveFlightFromQueue(self.flightcontrol.Qinbound, self, "inbound") self.flightcontrol:_AddFlightToHoldingQueue(self) + if not self.ai then + self:_UpdateMenu() + end end end @@ -2877,7 +2926,7 @@ function FLIGHTGROUP:AddWaypointAir(coordinate, wpnumber, speed) self:__UpdateRoute(-1) end ---- Check if a unit is and element of the flightgroup. +--- Check if a unit is an element of the flightgroup. -- @param #FLIGHTGROUP self -- @param #string unitname Name of unit. -- @return #boolean If true, unit is element of the flight group or false if otherwise. @@ -2955,16 +3004,16 @@ function FLIGHTGROUP:_AllSimilarStatus(status) elseif status==FLIGHTGROUP.ElementStatus.PARKING then - -- Element PARKING: Check that the other are not stil SPAWNED + -- Element PARKING: Check that the other are not still SPAWNED if element.status~=status or (element.status==FLIGHTGROUP.ElementStatus.INUTERO or element.status==FLIGHTGROUP.ElementStatus.SPAWNED) then return false end - elseif status==FLIGHTGROUP.ElementStatus.TAXIING then + elseif status==FLIGHTGROUP.ElementStatus.ENGINEON then - -- Element TAXIING: Check that the other are not stil SPAWNED or PARKING + -- Element TAXIING: Check that the other are not still SPAWNED or PARKING if element.status~=status and (element.status==FLIGHTGROUP.ElementStatus.INUTERO or element.status==FLIGHTGROUP.ElementStatus.SPAWNED or @@ -2972,13 +3021,25 @@ function FLIGHTGROUP:_AllSimilarStatus(status) return false end + elseif status==FLIGHTGROUP.ElementStatus.TAXIING then + + -- Element TAXIING: Check that the other are not still SPAWNED or PARKING + if element.status~=status and + (element.status==FLIGHTGROUP.ElementStatus.INUTERO or + element.status==FLIGHTGROUP.ElementStatus.SPAWNED or + element.status==FLIGHTGROUP.ElementStatus.ENGINEON or + element.status==FLIGHTGROUP.ElementStatus.PARKING) then + return false + end + elseif status==FLIGHTGROUP.ElementStatus.TAKEOFF then - -- Element TAKEOFF: Check that the other are not stil SPAWNED, PARKING or TAXIING + -- Element TAKEOFF: Check that the other are not still SPAWNED, PARKING or TAXIING if element.status~=status and (element.status==FLIGHTGROUP.ElementStatus.INUTERO or element.status==FLIGHTGROUP.ElementStatus.SPAWNED or element.status==FLIGHTGROUP.ElementStatus.PARKING or + element.status==FLIGHTGROUP.ElementStatus.ENGINEON or element.status==FLIGHTGROUP.ElementStatus.TAXIING) then self:T3(self.lid..string.format("Status=%s, element %s status=%s ==> returning FALSE", status, element.name, element.status)) return false @@ -2986,11 +3047,12 @@ function FLIGHTGROUP:_AllSimilarStatus(status) elseif status==FLIGHTGROUP.ElementStatus.AIRBORNE then - -- Element AIRBORNE: Check that the other are not stil SPAWNED, PARKING, TAXIING or TAKEOFF + -- Element AIRBORNE: Check that the other are not still SPAWNED, PARKING, TAXIING or TAKEOFF if element.status~=status and (element.status==FLIGHTGROUP.ElementStatus.INUTERO or element.status==FLIGHTGROUP.ElementStatus.SPAWNED or element.status==FLIGHTGROUP.ElementStatus.PARKING or + element.status==FLIGHTGROUP.ElementStatus.ENGINEON or element.status==FLIGHTGROUP.ElementStatus.TAXIING or element.status==FLIGHTGROUP.ElementStatus.TAKEOFF) then return false @@ -2998,7 +3060,7 @@ function FLIGHTGROUP:_AllSimilarStatus(status) elseif status==FLIGHTGROUP.ElementStatus.LANDED then - -- Element LANDED: check that the others are not stil AIRBORNE or LANDING + -- Element LANDED: check that the others are not still AIRBORNE or LANDING if element.status~=status and (element.status==FLIGHTGROUP.ElementStatus.AIRBORNE or element.status==FLIGHTGROUP.ElementStatus.LANDING) then @@ -3007,7 +3069,7 @@ function FLIGHTGROUP:_AllSimilarStatus(status) elseif status==FLIGHTGROUP.ElementStatus.ARRIVED then - -- Element ARRIVED: check that the others are not stil AIRBORNE, LANDING, or LANDED (taxiing). + -- Element ARRIVED: check that the others are not still AIRBORNE, LANDING, or LANDED (taxiing). if element.status~=status and (element.status==FLIGHTGROUP.ElementStatus.AIRBORNE or element.status==FLIGHTGROUP.ElementStatus.LANDING or @@ -3068,6 +3130,13 @@ function FLIGHTGROUP:_UpdateStatus(element, newstatus, airbase) self:FlightParking() end + elseif newstatus==FLIGHTGROUP.ElementStatus.ENGINEON then + --- + -- ENGINEON + --- + + -- No FLIGHT status. Waiting for taxiing. + elseif newstatus==FLIGHTGROUP.ElementStatus.TAXIING then --- -- TAXIING @@ -3528,7 +3597,20 @@ function FLIGHTGROUP:GetParkingSpot(element, maxdist, airbase) airbase=airbase or coord:GetClosestAirbase(nil, self:GetCoalition()) - local _,_,dist,spot=coord:GetClosestParkingSpot(airbase) + -- Avoid calling this routine as we only need the coordinates! + --local _,_,dist,spot=coord:GetClosestParkingSpot(airbase) + + local spot=nil --Wrapper.Airbase#AIRBASE.ParkingSpot + local dist=nil + local distmin=math.huge + for _,_parking in pairs(airbase.parking) do + local parking=_parking --Wrapper.Airbase#AIRBASE.ParkingSpot + local dist=coord:Get2DDistance(parking.Coordinate) + if dist