diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index e66a2124e..a4f8778ff 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -15,6 +15,7 @@ -- @type AIRWING -- @field #string ClassName Name of the class. -- @field #boolean Debug Debug mode. Messages to all about status. +-- @field #number verbose Verbosity of output. -- @field #string lid Class id string for output to DCS log file. -- @field #table menu Table of menu items. -- @field #table squadrons Table of squadrons. @@ -64,19 +65,11 @@ AIRWING = { wingcommander = nil, } ---- Squadron data. --- @type AIRWING.Squadron --- @field #string name Name of the squadron. --- @field #table assets Assets of the squadron. --- @field #table missiontypes Mission types that the squadron can do. --- @field #string livery Livery of the squadron. --- @field #table menu The squadron menu entries. --- @field #string skill Skill of squadron team members. - --- Squadron asset. -- @type AIRWING.SquadronAsset -- @field #AIRWING.Payload payload The payload of the asset. -- @field Ops.FlightGroup#FLIGHTGROUP flightgroup The flightgroup object. +-- @field #string squadname Name of the squadron this asset belongs to. -- @extends Functional.Warehouse#WAREHOUSE.Assetitem --- Payload data. @@ -98,13 +91,13 @@ AIRWING = { --- AIRWING class version. -- @field #string version -AIRWING.version="0.1.6" +AIRWING.version="0.1.7" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Spawn in air or hot. +-- TODO: Spawn in air or hot ==> Needs WAREHOUSE update. -- TODO: Make special request to transfer squadrons to anther airwing (or warehouse). -- TODO: Check that airbase has enough parking spots if a request is BIG. Alternatively, split requests. -- DONE: Add squadrons to warehouse. @@ -129,6 +122,7 @@ function AIRWING:New(warehousename, airwingname) -- Inherit everything from WAREHOUSE class. local self=BASE:Inherit(self, WAREHOUSE:New(warehousename, airwingname)) -- #AIRWING + -- Nil check. if not self then BASE:E(string.format("ERROR: Could not find warehouse %s!", warehousename)) return nil @@ -290,6 +284,35 @@ function AIRWING:NewPayload(Unit, MissionTypes, Npayloads, Unlimited, Performanc return nil end +--- Add a mission capability to an existing payload. +-- @param #AIRWING self +-- @param #AIRWING.Payload Payload The payload table to which the capability should be added. +-- @param #table MissionTypes Mission types to be added. +-- @param #number Performance A number between 0 (worst) and 100 (best) to describe the performance of the loadout for the given mission types. Default is 50. +-- @return #AIRWING self +function AIRWING:AddPayloadCapability(Payload, MissionTypes, Performance) + + -- Ensure Missiontypes is a table. + if MissionTypes and type(MissionTypes)~="table" then + MissionTypes={MissionTypes} + end + + Payload.capabilities=Payload.capabilities or {} + + for _,missiontype in pairs(MissionTypes) do + + local capability={} --Ops.Auftrag#AUFTRAG.Capability + capability.MissionType=missiontype + capability.Performance=Performance + + --TODO: check that capability does not already exist! + + table.insert(Payload.capabilities, capability) + end + + return self +end + --- Fetch a payload from the airwing resources for a given unit and mission type. -- The payload with the highest priority is preferred. -- @param #AIRWING self @@ -305,8 +328,8 @@ function AIRWING:FetchPayloadFromStock(UnitType, MissionType) local function sortpayloads(a,b) local pA=a --#AIRWING.Payload local pB=b --#AIRWING.Payload - local performanceA=1 - local performanceB=1 + local performanceA=self:GetPayloadPeformance(pA, MissionType) + local performanceB=self:GetPayloadPeformance(pB, MissionType) return (performanceA>performanceB) or (performanceA==performanceB and pA.unlimited==true) or (performanceA==performanceB and pA.unlimited==true and pB.unlimited==true and pA.navail>pB.navail) end table.sort(self.payloads, sortpayloads) @@ -315,8 +338,9 @@ function AIRWING:FetchPayloadFromStock(UnitType, MissionType) env.info("FF Sorted payloads for mission type X and aircraft type=Y:") for _,_payload in ipairs(self.payloads) do local payload=_payload --#AIRWING.Payload - if payload.aircrafttype==UnitType and self:CheckMissionCapability(MissionType, payload.missiontypes) then - self:I(string.format("FF %s payload for %s: avail=%d prio=%d", MissionType, payload.aircrafttype, payload.navail, payload.capabilities)) + if payload.aircrafttype==UnitType and self:CheckMissionCapability(MissionType, payload.capabilities) then + local performace=self:GetPayloadPeformance(payload, MissionType) + self:I(string.format("FF %s payload for %s: avail=%d performace=%d", MissionType, payload.aircrafttype, payload.navail, performace)) end end end @@ -333,7 +357,7 @@ function AIRWING:FetchPayloadFromStock(UnitType, MissionType) payload.navail=payload.navail-1 end - --self:I(string.format("FETCHING %s payload for %s: avail=%d prio=%d", MissionType, payload.aircrafttype, payload.navail, payload.priority)) + self:T(string.format("FETCHING %s payload for %s: avail=%d performance=%d", MissionType, payload.aircrafttype, payload.navail, self:GetPayloadPeformance(payload, MissionType))) -- Return a copy of the table. return payload @@ -341,6 +365,7 @@ function AIRWING:FetchPayloadFromStock(UnitType, MissionType) end + self:T(self.lid.."Warning could not find a payload for airframe X mission type Y!") return nil end @@ -377,6 +402,7 @@ function AIRWING:AddAssetToSquadron(Squadron, Nassets) if Squadron then + -- Get the template group of the squadron. local Group=GROUP:FindByName(Squadron.templatename) if Group then @@ -422,7 +448,7 @@ end -- @param #AIRWING.SquadronAsset Asset The squadron asset. -- @return Ops.Squadron#SQUADRON The squadron object. function AIRWING:GetSquadronOfAsset(Asset) - return self:GetSquadron(Asset.assignment) + return self:GetSquadron(Asset.squadname) end --- Remove asset from squadron. @@ -603,7 +629,7 @@ function AIRWING:onafterStart(From, Event, To) self:_SetMenuCoalition() for _,_squadron in pairs(self.squadrons) do - local squadron=_squadron --#AIRWING.Squadron + local squadron=_squadron --Ops.Squadron#SQUADRON self:_AddSquadonMenu(squadron) end @@ -632,8 +658,11 @@ function AIRWING:onafterStatus(From, Event, To) -- Count missions not over yet. local nmissions=self:CountMissionsInQueue() + -- Count ALL payloads in stock + local Npayloads=self:CountPayloadsInStock(AUFTRAG.Type) + -- TODO: count payloads, assets total - local text=string.format("Status %s: missions=%d, payloads=%d, squads=%d", fsmstate, nmissions, #self.payloads, #self.squadrons) + local text=string.format("Status %s: missions=%d, payloads=%d (%d), squads=%d", fsmstate, nmissions, #self.payloads, Npayloads, #self.squadrons) self:I(self.lid..text) ------------------ @@ -658,7 +687,7 @@ function AIRWING:onafterStatus(From, Event, To) local skill=squadron.skill and tostring(squadron.skill) or "N/A" -- Squadron text - text=text..string.format("\n* %s %s: Callsign=%s, Modex=%d, Skill=%s", squadron.name, squadron:GetState(), callsign, modex, skill) + text=text..string.format("\n* %s %s: Airframe=%s, Callsign=%s, Modex=%d, Skill=%s", squadron.name, squadron:GetState(), squadron.aircrafttype, callsign, modex, skill) -- Loop over all assets. for j,_asset in pairs(squadron.assets) do @@ -928,19 +957,56 @@ function AIRWING:_GetNextMission() local mission=_mission --Ops.Auftrag#AUFTRAG -- Firstly, check if mission is due? - if mission.status==AUFTRAG.Status.QUEUED and time>=mission.Tstart then + if mission:IsQueued() and time>=mission.Tstart then -- Check if airwing can do the mission and gather required assets. local can, assets=self:CanMission(mission) - -- Debug output. - self:T3({self.lid.."Mission check:", TstartPassed=time>=mission.Tstart, CanMission=can, Nassets=#assets}) - -- Check that mission is still scheduled, time has passed and enough assets are available. - if can then + if can then - -- Optimize the asset selection. Most useful assets will come first. - self:_OptimizeAssetSelection(assets, mission) + -- Optimize the asset selection. Most useful assets will come first. We do not include the payload as some assets have and some might not. + self:_OptimizeAssetSelection(assets, mission, false) + + -- Assign assets to mission. + local remove={} + local gotpayload={} + local n=0 + for i=1,#assets do + local asset=assets[i] --#AIRWING.SquadronAsset + + -- Get payload for the asset. + if not asset.payload then + local payload=self:FetchPayloadFromStock(asset.unittype, mission.type) + if payload then + n=n+1 + asset.payload=payload + table.insert(gotpayload, asset.uid) + else + table.insert(remove, asset.uid) + end + end + end + self:T2(self.lid..string.format("Provided %d assets with payloads", n)) + + -- Now remove assets for which we don't have a payload. + for i=#assets,1,-1 do + local asset=assets[i] --#AIRWING.SquadronAsset + for _,uid in pairs(remove) do + if uid==asset.uid then + table.remove(assets,i) + break + end + end + end + + -- Another check. + if #assets0 then @@ -949,11 +1015,8 @@ function AIRWING:_GetNextMission() mission.assets={} -- Assign assets to mission. - for i=1,mission.nassets do + for i=1,mission.nassets do local asset=assets[i] --#AIRWING.SquadronAsset - - -- Get payload for the asset. - asset.payload=self:FetchPayloadFromStock(asset.unittype, mission.type) -- Should not happen as we just checked! if not asset.payload then @@ -964,6 +1027,17 @@ function AIRWING:_GetNextMission() mission:AddAsset(asset) end + -- Now return the remaining payloads. + for i=mission.nassets+1,#assets do + local asset=assets[i] --#AIRWING.SquadronAsset + for _,uid in pairs(gotpayload) do + if uid==asset.uid then + self:ReturnPayloadFromAsset(asset) + break + end + end + end + return mission end @@ -973,16 +1047,58 @@ function AIRWING:_GetNextMission() return nil end +--- Calcuate the mission score of an asset. +-- @param #AIRWING self +-- @param #AIRWING.SquadronAsset asset Asset +-- @param Ops.Auftrag#AUFTRAG Mission Mission for which the best assets are desired. +-- @param #boolean includePayload If true, include the payload in the calulation if the asset has one attached. +-- @return #number Mission score. +function AIRWING:CalculateAssetMissionScore(asset, Mission, includePayload) + + local score=0 + + -- Prefer highly skilled assets. + if asset.skill==AI.Skill.GOOD then + score=score+10 + elseif asset.skill==AI.Skill.HIGH then + score=score+20 + elseif asset.skill==AI.Skill.EXCELLENT then + score=score+30 + end + + -- Add mission performance to score. + local squad=self:GetSquadronOfAsset(asset) + local missionperformance=squad:GetMissionPeformance(Mission.type) + score=score+missionperformance + + -- Add payload performance to score. + if includePayload and asset.payload then + score=score+self:GetPayloadPeformance(asset.payload, Mission.type) + end + + -- TODO: This could be vastly improved. Need to gather ideas during testing. + -- Calculate ETA? Assets on orbit missions should arrive faster even if they are further away. + -- Max speed of assets. + -- Fuel amount? + -- Range of assets? + + return score +end + --- Optimize chosen assets for the mission at hand. -- @param #AIRWING self -- @param #table assets Table of (unoptimized) assets. --- @param Ops.Auftrag#AUFTRAG Mission Next mission or *nil*. -function AIRWING:_OptimizeAssetSelection(assets, Mission) +-- @param Ops.Auftrag#AUFTRAG Mission Mission for which the best assets are desired. +-- @param #boolean includePayload If true, include the payload in the calulation if the asset has one attached. +function AIRWING:_OptimizeAssetSelection(assets, Mission, includePayload) local TargetCoordinate=Mission:GetTargetCoordinate() local dStock=self:GetCoordinate():Get2DDistance(TargetCoordinate) + -- Calculate distance to mission target. + local distmin=math.huge + local distmax=0 for _,_asset in pairs(assets) do local asset=_asset --#AIRWING.SquadronAsset @@ -991,50 +1107,42 @@ function AIRWING:_OptimizeAssetSelection(assets, Mission) asset.dist=group:GetCoordinate():Get2DDistance(TargetCoordinate) else asset.dist=dStock - end - - end - - local function score(Asset) - local asset=Asset --#AIRWING.SquadronAsset - - score=0 - - -- Prefer highly skilled assets. - if asset.skill==AI.Skill.GOOD then - score=score+10 - elseif asset.skill==AI.Skill.HIGH then - score=score+20 - elseif asset.skill==AI.Skill.EXCELLENT then - score=score+30 end - - + + if asset.distdistmax then + distmax=asset.dist + end + end - -- Sort results table wrt distacance. + -- Calculate the mission score of all assets. + for _,_asset in pairs(assets) do + local asset=_asset --#AIRWING.SquadronAsset + + asset.score=self:CalculateAssetMissionScore(asset, Mission, includePayload) + end + + --- Sort assets wrt to their mission score. Higher is better. local function optimize(a, b) local assetA=a --#AIRWING.SquadronAsset local assetB=b --#AIRWING.SquadronAsset - - --TODO: This could be vastly improved. Need to gather ideas during testing - -- Calculate ETA? Assets on orbit missions should arrive faster even if they are further away. - -- Max speed of assets. - -- Fuel amount? - -- Range of assets? - - --TODO: Need to define a scoring function, which gives a weighted result of more parameters. - - return (assetA.distassetB.score) --or (dA==dB and taskA.prio #Assets then + self:I(self.lid..string.format("INFO: Not enough assets available! Got %d but need at least %d", #Assets, Mission.nassets)) + Can=false + end return Can, Assets end +--- Check if assets for a given mission type are available. +-- @param #AIRWING self +-- @param Ops.Auftrag#AUFTRAG Mission The mission. +-- @return #table Assets that can do the required mission. +function AIRWING:RecruitAssets(Mission) + +end + --- Check if a mission type is contained in a list of possible types. -- @param #AIRWING self @@ -1641,6 +1850,40 @@ function AIRWING:CheckMissionCapability(MissionType, Capabilities) return false end +--- Get payload performance for a given type of misson type. +-- @param #AIRWING self +-- @param #AIRWING.Payload Payload The payload table. +-- @param #string MissionType Type of mission. +-- @return #number Performance or -1. +function AIRWING:GetPayloadPeformance(Payload, MissionType) + + for _,Capability in pairs(Payload.capabilities) do + local capability=Capability --Ops.Auftrag#AUFTRAG.Capability + if capability.MissionType==MissionType then + return capability.Performance + end + end + + return -1 +end + +--- Get mission types a payload can perform. +-- @param #AIRWING self +-- @param #AIRWING.Payload Payload The payload table. +-- @param #string MissionType Type of mission. +-- @return #number Performance or -1. +function AIRWING:GetPayloadMissionTypes(Payload) + + local missiontypes={} + + for _,Capability in pairs(Payload.capabilities) do + local capability=Capability --Ops.Auftrag#AUFTRAG.Capability + table.insert(missiontypes, capability.MissionType) + end + + return missiontypes +end + --- Returns the mission for a given mission ID (Autragsnummer). -- @param #AIRWING self -- @param #number mid Mission ID (Auftragsnummer). @@ -1739,7 +1982,7 @@ function AIRWING:ReportSquadrons() local text="Squadron Report:" for i,_squadron in pairs(self.squadrons) do - local squadron=_squadron --#AIRWING.Squadron + local squadron=_squadron local name=squadron.name @@ -1770,7 +2013,7 @@ end --- Add sub menu for this intruder. -- @param #AIRWING self --- @param #AIRWING.Squadron squadron The squadron data. +-- @param Ops.Squadron#SQUADRON squadron The squadron data. function AIRWING:_AddSquadonMenu(squadron) local Coalition=self:GetCoalition() @@ -1790,7 +2033,7 @@ end --- Report squadron status. -- @param #AIRWING self --- @param #AIRWING.Squadron squadron The squadron object. +-- @param Ops.Squadron#SQUADRON squadron The squadron object. function AIRWING:_ReportSq(squadron) local text=string.format("%s: %s assets:", squadron.name, tostring(squadron.categoryname)) diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index 3710c1e2f..e6daff76b 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -21,9 +21,10 @@ -- @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 #string aircrafttype Type of the airframe the squadron is using. -- @field Wrapper.Group#GROUP templategroup Template group. -- @field #table assets Squadron assets. --- @field #table missiontypes Mission types the squadron can perform. +-- @field #table missiontypes Capabilities (mission types and performances) of the squadron. -- @field #string livery Livery of the squadron. -- @field #number skill Skill of squadron members. -- @field #number modex Modex. @@ -58,6 +59,7 @@ SQUADRON = { lid = nil, name = nil, templatename = nil, + aircrafttype = nil, assets = {}, missiontypes = {}, livery = nil, @@ -78,7 +80,7 @@ SQUADRON = { --- SQUADRON class version. -- @field #string version -SQUADRON.version="0.0.5" +SQUADRON.version="0.0.7" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -111,7 +113,6 @@ function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName) -- Set some string id for output to DCS.log file. self.lid=string.format("SQUADRON %s | ", self.name) - -- Template group. self.templategroup=GROUP:FindByName(self.templatename) @@ -128,6 +129,8 @@ function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName) self.attribute=self.templategroup:GetAttribute() + self.aircrafttype=self.templategroup:GetTypeName() + self.refuelSystem=select(2, self.templategroup:GetUnit(1):IsRefuelable()) self.tankerSystem=select(2, self.templategroup:GetUnit(1):IsTanker()) @@ -197,8 +200,10 @@ end -- -- Valid names are the names of the **livery directories**. Check out the folder in your DCS installation: -- --- * `DCS World OpenBeta\CoreMods\aircraft\\` --- * `DCS World OpenBeta\Bazar\Liveries\\` +-- * For full modules: `DCS World OpenBeta\CoreMods\aircraft\\Liveries\\` +-- * For AI units: `DCS World OpenBeta\Bazar\Liveries\\` +-- +-- The folder name `` is the string you want. -- -- Or personal liveries you have installed somewhere in your saved games folder. -- @@ -238,12 +243,12 @@ function SQUADRON:AddMissonCapability(MissionTypes, Performance) end -- Set table. - self.missiontypes=self.missiontypes or {} --MissionTypes + self.missiontypes=self.missiontypes or {} for _,missiontype in pairs(MissionTypes) do local capability={} --Ops.Auftrag#AUFTRAG.Capability capability.MissionType=missiontype - capability.Performance=Performance + capability.Performance=Performance or 50 table.insert(self.missiontypes, capability) end @@ -255,7 +260,7 @@ end --- Get mission types this squadron is able to perform. -- @param #SQUADRON self -- @return #table Table of mission types. Could be empty {}. -function SQUADRON:GetMissonTypes() +function SQUADRON:GetMissionTypes() local missiontypes={} @@ -270,15 +275,15 @@ end --- Get mission capabilities of this squadron. -- @param #SQUADRON self -- @return #table Table of mission capabilities. -function SQUADRON:GetMissonCapabilities() +function SQUADRON:GetMissionCapabilities() return self.missiontypes end --- Get mission performance for a given type of misson. -- @param #SQUADRON self -- @param #string MissionType Type of mission. --- @return #table Table of mission capabilities. -function SQUADRON:GetMissonPeformance(MissionType) +-- @return #number Performance or -1. +function SQUADRON:GetMissionPeformance(MissionType) for _,Capability in pairs(self.missiontypes) do local capability=Capability --Ops.Auftrag#AUFTRAG.Capability @@ -287,7 +292,7 @@ function SQUADRON:GetMissonPeformance(MissionType) end end - return 0 + return -1 end --- Set max engagement range. @@ -354,6 +359,8 @@ end -- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset. -- @return #SQUADRON self function SQUADRON:AddAsset(Asset) + self:T(self.lid..string.format("Adding asset %s of type %s", Asset.spawngroupname, Asset.unittype)) + Asset.squadname=self.name table.insert(self.assets, Asset) return self end @@ -558,132 +565,146 @@ 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. +--- Check if there is a squadron that can execute a given mission. +-- We check the mission type, the refuelling system, engagement range -- @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={} - + local cando=true + -- On duty?= if not self:IsOnDuty() then self:I(self.lid..string.format("Squad in not OnDuty but in state %s", self:GetState())) - return false, assets + return false end -- Check mission type. WARNING: This assumes that all assets of the squad can do the same mission types! - local cando=self:CheckMissionType(Mission.type, self:GetMissonTypes()) + if not self:CheckMissionType(Mission.type, self:GetMissionTypes()) then + self:I(self.lid..string.format("INFO: Squad cannot do mission type %s", Mission.type)) + end -- Check that tanker mission - if cando and Mission.type==AUFTRAG.Type.TANKER then + if Mission.type==AUFTRAG.Type.TANKER then if Mission.refuelSystem and Mission.refuelSystem==self.tankerSystem then -- Correct refueling system. else self:I(self.lid..string.format("INFO: Wrong refueling system requested=%s != %s=available", tostring(Mission.refuelSystem), tostring(self.tankerSystem))) - cando=false + return false end end - if cando then + -- Distance to target. + local TargetDistance=Mission:GetTargetDistance(self.airwing:GetCoordinate()) + + -- Max engage range. + local engagerange=Mission.engageMaxDistance and math.min(self.engageRange, Mission.engageMaxDistance) or self.engageRange - -- Distance to target. - local TargetDistance=Mission:GetTargetDistance(self.airwing:GetCoordinate()) + -- Set range is valid. Mission engage distance can overrule the squad engage range. + if TargetDistance>engagerange then + self:I(self.lid..string.format("INFO: Squad is not in range. Target dist=%d > %d NM max engage Range", UTILS.MetersToNM(TargetDistance), UTILS.MetersToNM(engagerange))) + return false + end + + return true +end + +--- Get assets for a mission. +-- @param #SQUADRON self +-- @param Ops.Auftrag#AUFTRAG Mission The mission. +-- @return #table Assets that can do the required mission. +function SQUADRON:RecruitAssets(Mission) + + -- Number of payloads available. + local Npayloads=self.airwing:CountPayloadsInStock(Mission.type, self.aircrafttype) + + local assets={} + + -- Loop over assets. + for _,_asset in pairs(self.assets) do + local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset - for _,_asset in pairs(self.assets) do - local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset - - local engagerange=Mission.engageMaxDistance and math.min(self.engageRange, Mission.engageMaxDistance) or self.engageRange - - -- Set range is valid. Mission engage distance can overrule the squad engage range. - if TargetDistance<=engagerange then - - -- Check if asset is currently on a mission (STARTED or QUEUED). - if self.airwing:IsAssetOnMission(asset) then - - --- - -- 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.INTERCEPT then - - -- Check if the payload of this asset is compatible with the mission. - -- Note: we do not check the payload as an asset that is on a PATROL mission should be able to do an intercept as well! - -- TODO: Check if asset actually has weapons left. Difficult! - table.insert(assets, asset) - - end + + -- Check if asset is currently on a mission (STARTED or QUEUED). + if self.airwing:IsAssetOnMission(asset) then + + --- + -- 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.INTERCEPT then + + -- Check if the payload of this asset is compatible with the mission. + -- Note: we do not check the payload as an asset that is on a PATROL mission should be able to do an INTERCEPT as well! + -- TODO: Check if asset actually has weapons left. Difficult! + table.insert(assets, asset) - else + end + + else + + --- + -- Asset as no current mission + --- + + if asset.spawned then + + --- + -- Asset is already SPAWNED (could be uncontrolled on the airfield) + --- + + local combatready=false + local flightgroup=asset.flightgroup + if flightgroup then - --- - -- Asset as no current mission - --- - - if asset.spawned then - - --- - -- Asset is already SPAWNED (could be uncontrolled on the airfield) - --- - - 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 - - --- - -- Asset is still in STOCK - --- - - -- Check that asset is not already requested for another mission. - 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 + 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:CheckMissionCapability(Mission.type, asset.payload.capabilities) then + table.insert(assets, asset) end else - self:I(self.lid..string.format("INFO: Squad is not in range. Target dist=%d > %d NM max engage Range", UTILS.MetersToNM(TargetDistance), UTILS.MetersToNM(engagerange))) - end - end -- loop over assets - - else - self:I(self.lid..string.format("INFO: Squad cannot do mission type %s", Mission.type)) - end -- if cando mission type - - -- Check if required assets are present. - if Mission.nassets and Mission.nassets > #assets then - self:I(self.lid.."INFO: Not enough assets available! Got %d but need at least %d", #assets, Mission.nassets) - cando=false - end + --- + -- Asset is still in STOCK + --- + + -- Check that asset is not already requested for another mission. + 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 + ]] + + if Npayloads>0 then + + -- Add this asset to the selection. + table.insert(assets, asset) + + -- Reduce number of payloads so we only return the number of assets that could do the job. + Npayloads=Npayloads-1 + end + + end + end + end + end -- loop over assets - return cando, assets + return assets end @@ -707,6 +728,23 @@ function SQUADRON:CheckMissionType(MissionType, PossibleTypes) return false end +--- Check if a mission type is contained in a list of possible capabilities. +-- @param #SQUADRON self +-- @param #string MissionType The requested mission type. +-- @param #table Capabilities A table with possible capabilities. +-- @return #boolean If true, the requested mission type is part of the possible mission types. +function SQUADRON:CheckMissionCapability(MissionType, Capabilities) + + for _,cap in pairs(Capabilities) do + local capability=cap --Ops.Auftrag#AUFTRAG.Capability + if capability.MissionType==MissionType then + return true + end + end + + return false +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------