From 3bc387c1c74b0538054fa0355bb3b41d76b4eca6 Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 16 Mar 2020 00:32:26 +0100 Subject: [PATCH] Ops --- .../Moose/Functional/Warehouse.lua | 11 +- Moose Development/Moose/Ops/AirWing.lua | 177 ++++++++---------- Moose Development/Moose/Ops/Auftrag.lua | 60 ++++++ Moose Development/Moose/Ops/FlightGroup.lua | 64 ++++++- Moose Development/Moose/Ops/Squadron.lua | 121 +++++++++++- Moose Development/Moose/Ops/WingCommander.lua | 2 +- 6 files changed, 318 insertions(+), 117 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index e3e271a44..43ab2bd48 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -5637,14 +5637,8 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol end if asset.payload then - unit.payload=asset.payload.pylons - env.info("FF payload") - self:I({playload=unit.payload}) - else - env.info("FF No payload for asset!") + unit.payload=asset.payload.pylons end - env.info("FF Asset info") - self:I(asset) end @@ -5668,9 +5662,6 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol -- Spawn group. local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP - -- Activate group - should only be necessary for late activated groups. - --group:Activate() - return group end diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 201b044d8..15e141f31 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -27,7 +27,9 @@ -- @field #number nflightsCAP Number of CAP flights constantly in the air. -- @field #number nflightsTANKER Number of TANKER flights constantly in the air. -- @field #number nflightsAWACS Number of AWACS flights constantly in the air. --- @field #table cappoints Table of CAP points. +-- @field #table pointsCAP Table of CAP points. +-- @field #table pointsTANKER Table of Tanker points. +-- @field #table pointsAWACS Table of AWACS points. -- @field Ops.WingCommander#WINGCOMMANDER wingcommander The wing commander responsible for this airwing. -- @extends Functional.Warehouse#WAREHOUSE @@ -50,7 +52,9 @@ AIRWING = { squadrons = {}, missionqueue = {}, payloads = {}, - cappoints = {}, + pointsCAP = {}, + pointsTANKER = {}, + pointsAWACS = {}, wingcommander = nil, } @@ -186,6 +190,13 @@ function AIRWING:AddSquadron(Squadron) -- Add assets to squadron. self:AddAssetToSquadron(Squadron, Squadron.Ngroups) + + -- Tanker and AWACS get unlimited payloads. + if Squadron.attribute==GROUP.Attribute.AIR_AWACS then + self:NewPayload(Squadron.templategroup, AUFTRAG.Type.AWACS, 1, true) + elseif Squadron.attribute==GROUP.Attribute.AIR_TANKER then + self:NewPayload(Squadron.templategroup, AUFTRAG.Type.TANKER, 1, true) + end -- Set airwing to squadron. Squadron:SetAirwing(self) @@ -476,7 +487,7 @@ function AIRWING:AddPatrolPointCAP(Coordinate, Heading, LegLength, Altitude, Spe local cappoint=self:NewPatrolPoint(Coordinate, Heading, LegLength, Speed, Altitude, LegLength) - table.insert(self.cappoints, cappoint) + table.insert(self.pointsCAP, cappoint) return self end @@ -522,12 +533,17 @@ function AIRWING:onafterStatus(From, Event, To) -- Check CAP missions. self:CheckCAP() - --self:CheckTANKER() + -- Check TANKER missions. + self:CheckTANKER() - --self:CheckAWACS() + -- Check AWACS missions. + self:CheckAWACS() + + -- Count missions not over yet. + local nmissions=self:CountMissionsInQueue() -- TODO: count payloads, count squadrons, count missions - local text=string.format("Status %s: missions=%d, payloads=%d, squads=%d", fsmstate, #self.missionqueue, #self.payloads, #self.squadrons) + local text=string.format("Status %s: missions=%d, payloads=%d, squads=%d", fsmstate, nmissions, #self.payloads, #self.squadrons) self:I(self.lid..text) ------------------ @@ -562,7 +578,8 @@ function AIRWING:onafterStatus(From, Event, To) local mission=self:GetAssetCurrentMission(asset) local missiontext="" if mission then - missiontext=string.format(" [%s (%s): status=%s]", mission.type, mission.name, mission.status) + local distance=UTILS.MetersToNM(mission:GetTargetDistance(asset.flightgroup.group:GetCoordinate())) + missiontext=string.format(" [%s (%s): status=%s, distance=%.1f NM]", mission.type, mission.name, mission.status, distance) end text=text..string.format("\n -[%d] %s*%d \"%s\": spawned=%s, mission=%s%s", j, typename, asset.nunits, asset.spawngroupname, spawned, tostring(self:IsAssetOnMission(asset)), missiontext) @@ -629,7 +646,7 @@ function AIRWING:CheckCAP() for i=1,self.nflightsCAP-Ncap do - local patrol=self:_GetPatrolData(self.cappoints) + local patrol=self:_GetPatrolData(self.pointsCAP) local missionCAP=AUFTRAG:NewPATROL(patrol.coord, patrol.speed, patrol.heading, patrol.leg, patrol.altitude) @@ -642,6 +659,50 @@ function AIRWING:CheckCAP() return self end +--- Check how many TANKER missions are assigned and add number of missing missions. +-- @param #AIRWING self +-- @return #AIRWING self +function AIRWING:CheckTANKER() + + local N=self:CountMissionsInQueue({AUFTRAG.Type.TANKER}) + + for i=1,self.nflightsTANKER-N do + + local patrol=self:_GetPatrolData(self.pointsTANKER) + + local mission=AUFTRAG:NewTANKER(patrol.coord, patrol.speed, patrol.heading, patrol.leg, patrol.altitude) + + mission.patroldata=patrol + + self:AddMission(mission) + + end + + return self +end + +--- Check how many AWACS missions are assigned and add number of missing missions. +-- @param #AIRWING self +-- @return #AIRWING self +function AIRWING:CheckAWACS() + + local N=self:CountMissionsInQueue({AUFTRAG.Type.AWACS}) + + for i=1,self.nflightsAWACS-N do + + local patrol=self:_GetPatrolData(self.pointsAWACS) + + local mission=AUFTRAG:NewAWACS(patrol.coord, patrol.speed, patrol.heading, patrol.leg, patrol.altitude) + + mission.patroldata=patrol + + self:AddMission(mission) + + end + + return self +end + --- Get next mission. -- @param #AIRWING self @@ -675,7 +736,7 @@ function AIRWING:_GetNextMission() if mission.status==AUFTRAG.Status.QUEUED and time>=mission.Tstart then -- Check if airwing can do the mission and gather required assets. - local can, assets=self:CanMission(mission.type, mission.nassets) + local can, assets=self:CanMission(mission) -- Debug output. self:T3({self.lid.."Mission check:", TstartPassed=time>=mission.Tstart, CanMission=can, Nassets=#assets}) @@ -1089,94 +1150,7 @@ function AIRWING:_CreateFlightGroup(asset) end ---- Check if there is a squadron that can execute a given mission type. Optionally, the number of required assets can be specified. --- @param #AIRWING self --- @param #AIRWING.Squadron Squadron The Squadron. --- @param #string MissionType Type of mission. --- @param #number Nassets Number of required assets for the mission. Use *nil* or 0 for none. Then only the general capability is checked. --- @return #boolean If true, Squadron can do that type of mission. Available assets are not checked. --- @return #table Assets that can do the required mission. -function AIRWING:SquadronCanMission(Squadron, MissionType, Nassets) - - -- Assets available for this mission. - local assets={} - -- WARNING: This assumes that all assets of the squad can do the same mission types! - local cando=self:CheckMissionType(MissionType, Squadron.missiontypes) - - if cando then - - for _,_asset in pairs(Squadron.assets) do - local asset=_asset --#AIRWING.SquadronAsset - - -- Check if has already any missions in the queue. - if self:IsAssetOnMission(asset) then - - --- - -- This asset is already on a mission - --- - - -- Check if this asset is currently on a PATROL mission (STARTED or EXECUTING). - if self:IsAssetOnMission(asset, AUFTRAG.Type.PATROL) and MissionType~=AUFTRAG.Type.PATROL then - - -- Check if the payload of this asset is compatible with the mission. - if self:CheckMissionType(MissionType, asset.payload.missiontypes) then - -- TODO: Check if asset actually has weapons left. Difficult! - table.insert(assets, asset) - end - - end - - else - - --- - -- This asset as no current mission - --- - - if asset.spawned then - - local combatready=false - local flightgroup=asset.flightgroup - if flightgroup then - - if (flightgroup:IsAirborne() or flightgroup:IsWaiting()) and not flightgroup:IsFuelLow() then - combatready=true - end - - end - - -- This asset is "combatready". Let's check if it has the right payload. - if combatready and self:CheckMissionType(MissionType, asset.payload.missiontypes) then - table.insert(assets, asset) - end - - else - - if not asset.requested then - - -- Check if we got a payload and reserve it for this asset. - local payload=self:FetchPayloadFromStock(asset.unittype, MissionType) - if payload then - asset.payload=payload - table.insert(assets, asset) - end - - end - end - - end - - end - - end - - -- Check if required assets are present. - if Nassets and Nassets > #assets then - cando=false - end - - return cando, assets -end --- Check if an asset is currently on a mission (STARTED or EXECUTING). -- @param #AIRWING self @@ -1305,24 +1279,23 @@ end --- Check if assets for a given mission type are available. -- @param #AIRWING self --- @param #string MissionType Type of mission. --- @param #number Nassets Amount of assets required for the mission. Default 1. +-- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @return #boolean If true, enough assets are available. -- @return #table Assets that can do the required mission. -function AIRWING:CanMission(MissionType, Nassets) +function AIRWING:CanMission(Mission) -- Assume we CANNOT and NO assets are available. local Can=false local Assets={} for squadname,_squadron in pairs(self.squadrons) do - local squadron=_squadron --#AIRWING.Squadron + local squadron=_squadron --Ops.Squadron#SQUADRON -- Check if this squadron can. - local can, assets=self:SquadronCanMission(squadron, MissionType, Nassets) + local can, assets=squadron:CanMission(Mission) -- Debug output. - local text=string.format("Mission=%s, squadron=%s, can=%s, assets=%d/%d", MissionType, squadron.name, tostring(can), #assets, Nassets) + local text=string.format("Mission=%s, squadron=%s, can=%s, assets=%d/%d", Mission.type, squadron.name, tostring(can), #assets, Mission.nassets) self:I(self.lid..text) -- If anyone can, we Can. diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 5856255ab..20728cdbe 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -378,6 +378,50 @@ function AUFTRAG:NewPATROL(OrbitCoordinate, OrbitSpeed, Heading, Leg, Altitude) return mission end +--- Create a TANKER mission. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate. +-- @param #number OrbitSpeed Orbit speed in knots. Default 350 kts. +-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). +-- @param #number Leg Length of race-track in NM. Default 10 NM. +-- @param #number Altitude Orbit altitude in feet. +-- @return #AUFTRAG self +function AUFTRAG:NewTANKER(OrbitCoordinate, OrbitSpeed, Heading, Leg, Altitude) + + -- Create ORBIT first. + local mission=self:NewORBIT(OrbitCoordinate, OrbitSpeed, Heading, Leg, Altitude) + + -- Mission type PATROL. + mission.type=AUFTRAG.Type.TANKER + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- Create a AWACS mission. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate. +-- @param #number OrbitSpeed Orbit speed in knots. Default 350 kts. +-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). +-- @param #number Leg Length of race-track in NM. Default 10 NM. +-- @param #number Altitude Orbit altitude in feet. +-- @return #AUFTRAG self +function AUFTRAG:NewAWACS(OrbitCoordinate, OrbitSpeed, Heading, Leg, Altitude) + + -- Create ORBIT first. + local mission=self:NewORBIT(OrbitCoordinate, OrbitSpeed, Heading, Leg, Altitude) + + -- Mission type PATROL. + mission.type=AUFTRAG.Type.AWACS + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + + + --- Create an INTERCEPT mission. -- @param #AUFTRAG self -- @param Core.Set#SET_GROUP TargetGroupSet The set of target groups to intercept. Can also be passed as a simple @{Wrapper.Group#GROUP} object. @@ -484,6 +528,10 @@ function AUFTRAG:NewBAI(TargetGroupSet) mission.engageWeaponType=2956984318 mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL + mission.engageAsGroup=true + + mission.missionFraction=0.75 + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1444,6 +1492,18 @@ function AUFTRAG:GetTargetCoordinate() return nil end +--- Get distance to target. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE FromCoord The coordinate from which the distance is measured. +-- @return #number Distance in meters. +function AUFTRAG:GetTargetDistance(FromCoord) + local TargetCoord=self:GetTargetCoordinate() + if TargetCoord then + return TargetCoord:Get2DDistance(FromCoord) + end + return 0 +end + --- Get coordinate of target. First unit/group of the set is used. -- @param #AUFTRAG self -- @return #string diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index edf36ffc7..3f9866420 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -432,8 +432,8 @@ function FLIGHTGROUP:New(groupname, autostart) self:AddTransition("*", "RTZ", "Inbound") -- Group is returning to destination zone. Not implemented yet! self:AddTransition("Inbound", "Holding", "Holding") -- Group is in holding pattern. - self:AddTransition("*", "Refuel", "Going2Refuel")-- Group is send to refuel at a tanker. Not implemented yet! - self:AddTransition("Going2Refuel", "Refueling", "Refueling") -- Group is send to refuel at a tanker. Not implemented yet! + self:AddTransition("*", "Refuel", "Going4Fuel") -- Group is send to refuel at a tanker. Not implemented yet! + self:AddTransition("Going4Fuel", "Refueled", "Airborne") -- Group is send to refuel at a tanker. Not implemented yet! self:AddTransition("*", "LandAt", "LandingAt") -- Helo group is ordered to land at a specific point. self:AddTransition("LandingAt", "LandedAt", "LandedAt") -- Helo group landed landed at a specific point. @@ -2754,6 +2754,51 @@ function FLIGHTGROUP:onafterWait(From, Event, To, Coord, Altitude, Speed) self:SetTask(TaskOrbit) end + +--- On after "Refuel" event. +-- @param #FLIGHTGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function FLIGHTGROUP:onafterRefuel(From, Event, To) + + -- Debug message. + local text=string.format("Flight group set to refuel at the nearest tanker") + MESSAGE:New(text, 30, "DEBUG"):ToAllIf(self.Debug) + self:I(self.lid..text) + + --TODO: set ROE passive. introduce roe event/state/variable. + + --TODO: cancel current task + + -- Orbit task. + local TaskRefuel=self.group:TaskRefueling() + local TaskFunction=self.group:TaskFunction("FLIGHTGROUP._FinishedRefuelling", self) + + local TaskCombo=self.group:TaskCombo({TaskFunction, TaskRefuel, TaskFunction}) + + -- Set task. + --self:SetTask(TaskCombo) + self:PushTask(TaskCombo) + +end + +--- On after "Refueled" event. +-- @param #FLIGHTGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function FLIGHTGROUP:onafterRefueled(From, Event, To) + -- Debug message. + local text=string.format("Flight group finished refuelling") + MESSAGE:New(text, 30, "DEBUG"):ToAllIf(self.Debug) + self:I(self.lid..text) + + self:__UpdateRoute(-1) + +end + + --- On after "Holding" event. Flight arrived at the holding point. -- @param #FLIGHTGROUP self -- @param #string From From state. @@ -3397,6 +3442,8 @@ function FLIGHTGROUP:RouteToMission(mission, delay) waypointcoord.y=mission.missionAltitude env.info("FF mission altitude [m]="..waypointcoord.y) end + + local speed=UTILS.KmphToKnots(math.min(self.speedmax*0.8, 1000)) -- Add waypoint. self:AddWaypointAir(waypointcoord, nextwaypoint, self.speedmax*0.8, false) @@ -3408,7 +3455,8 @@ function FLIGHTGROUP:RouteToMission(mission, delay) mission:SetFlightWaypointTask(self, waypointtask) -- TODO: better marker text, mission.maker - mission.marker=waypointcoord:MarkToCoalition(mission.name, self:GetCoalition(), true) + --mission.marker=waypointcoord:MarkToCoalition(mission.name, self:GetCoalition(), true) + mission.marker=waypointcoord:MarkToAll(mission.name, true) --self:SetROE(mission.optionROE) @@ -3482,6 +3530,16 @@ function FLIGHTGROUP._ReachedHolding(group, flightgroup) flightgroup:__Holding(-1) end +--- Function called when flight finished refuelling. +-- @param Wrapper.Group#GROUP group Group object. +-- @param #FLIGHTGROUP flightgroup Flight group object. +function FLIGHTGROUP._FinishedRefuelling(group, flightgroup) + flightgroup:T(flightgroup.lid..string.format("Group finished refueling")) + + -- Trigger Holding event. + flightgroup:__Refueled(-1) +end + --- Update route of group, e.g after new waypoints and/or waypoint tasks have been added. -- @param Wrapper.Group#GROUP group The Moose group object. -- @param #FLIGHTGROUP flightgroup The flight group object. diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index 9a26e564d..bcfc2c70d 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -18,12 +18,14 @@ -- @field #string lid Class id string for output to DCS log file. -- @field #string name Name of the squadron. -- @field #string templatename Name of the template group. +-- @field Wrapper.Group#GROUP templategroup Template group. -- @field #table assets Squadron assets. -- @field #table missiontypes Mission types the squadron can perform. -- @field #string livery Livery of the squadron. -- @field #number skill Skill of squadron members. -- @field Ops.AirWing#AIRWING airwing The AIRWING object the squadron belongs to. -- @field #number Ngroups Number of asset flight groups this squadron has. +-- @field #number engageRange Engagement range in meters. -- @extends Core.Fsm#FSM --- Be surprised! @@ -49,6 +51,7 @@ SQUADRON = { skill = nil, airwing = nil, Ngroups = nil, + engageRange = nil, } --- Flight group element. @@ -83,9 +86,23 @@ function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName) -- Inherit everything from FSM class. local self=BASE:Inherit(self, FSM:New()) -- #SQUADRON + -- Name of the template group. self.templatename=TemplateGroupName - self.Ngroups=Ngroups or 3 + -- Template group. + self.templategroup=GROUP:FindByName(self.templatename) + + -- Check if template group exists. + if not self.templategroup then + self:E("ERROR: Template group %s does not exist!", tostring(self.templatename)) + return nil + end + + -- Defaults. + self.Ngroups=Ngroups or 3 + self:SetEngagementRange() + + self.attribute=self.templategroup:GetAttribute() --self.flightgroup=AIGroup self.name=tostring(SquadronName or TemplateGroupName) @@ -192,6 +209,15 @@ function SQUADRON:SetMissonTypes(MissionTypes) return self end +--- Set max engagement range. +-- @param #SQUADRON self +-- @param #number EngageRange Engagement range in NM. Default 250 NM. +-- @return #SQUADRON self +function SQUADRON:SetEngagementRange(EngageRange) + self.engageRange=UTILS.NMToMeters(EngageRange or 250) + return self +end + --- Set airwing. -- @param #SQUADRON self -- @param Ops.AirWing#AIRWING Airwing The airwing. @@ -285,6 +311,99 @@ end -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Check if there is a squadron that can execute a given mission type. Optionally, the number of required assets can be specified. +-- @param #SQUADRON self +-- @param Ops.Auftrag#AUFTRAG Mission The mission. +-- @return #boolean If true, Squadron can do that type of mission. +-- @return #table Assets that can do the required mission. +function SQUADRON:CanMission(Mission) + + -- Assets available for this mission. + local assets={} + + -- WARNING: This assumes that all assets of the squad can do the same mission types! + local cando=self:CheckMissionType(Mission.type, self.missiontypes) + + if cando then + + local TargetDistance=Mission:GetTargetDistance(self.airwing:GetCoordinate()) + + for _,_asset in pairs(self.assets) do + local asset=_asset --#AIRWING.SquadronAsset + + -- Set range is valid. + if TargetDistance<=self.engageRange then + + -- Check if has already any missions in the queue. + if self.airwing:IsAssetOnMission(asset) then + + --- + -- This asset is already on a mission + --- + + -- Check if this asset is currently on a PATROL mission (STARTED or EXECUTING). + if self.airwing:IsAssetOnMission(asset, AUFTRAG.Type.PATROL) and Mission.type~=AUFTRAG.Type.PATROL then + + -- Check if the payload of this asset is compatible with the mission. + if self:CheckMissionType(Mission.type, asset.payload.missiontypes) then + -- TODO: Check if asset actually has weapons left. Difficult! + table.insert(assets, asset) + end + + end + + else + + --- + -- This asset as no current mission + --- + + if asset.spawned then + + local combatready=false + local flightgroup=asset.flightgroup + if flightgroup then + + if (flightgroup:IsAirborne() or flightgroup:IsWaiting()) and not flightgroup:IsFuelLow() then + combatready=true + end + + end + + -- This asset is "combatready". Let's check if it has the right payload. + if combatready and self:CheckMissionType(Mission.type, asset.payload.missiontypes) then + table.insert(assets, asset) + end + + else + + if not asset.requested then + + -- Check if we got a payload and reserve it for this asset. + local payload=self.airwing:FetchPayloadFromStock(asset.unittype, Mission.type) + if payload then + asset.payload=payload + table.insert(assets, asset) + end + + end + end + + end + + end + end + end + + -- Check if required assets are present. + if Mission.nassets and Mission.nassets > #assets then + cando=false + end + + return cando, assets +end + + --- Checks if a mission type is contained in a table of possible types. -- @param #SQUADRON self -- @param #string MissionType The requested mission type. diff --git a/Moose Development/Moose/Ops/WingCommander.lua b/Moose Development/Moose/Ops/WingCommander.lua index 0e2a54f9d..bd836e4e1 100644 --- a/Moose Development/Moose/Ops/WingCommander.lua +++ b/Moose Development/Moose/Ops/WingCommander.lua @@ -337,7 +337,7 @@ function WINGCOMMANDER:GetAirwingForMission(Mission) local airwing=_airwing --Ops.AirWing#AIRWING -- Check if airwing can do this mission. - local can,assets=airwing:CanMission(Mission.type, Mission.nassets) + local can,assets=airwing:CanMission(Mission) -- Can it? if can then