--- **Ops** - Legion Warehouse. -- -- Parent class of Airwings, Brigades and Fleets. -- -- === -- -- ### Author: **funkyfranky** -- -- === -- @module Ops.Legion -- @image OPS_Legion.png --- LEGION class. -- @type LEGION -- @field #string ClassName Name of the class. -- @field #number verbose Verbosity of output. -- @field #string lid Class id string for output to DCS log file. -- @field #table missionqueue Mission queue table. -- @field #table transportqueue Transport queue. -- @field #table cohorts Cohorts of this legion. -- @field Ops.Commander#COMMANDER commander Commander of this legion. -- @field Ops.Chief#CHIEF chief Chief of this legion. -- @extends Functional.Warehouse#WAREHOUSE --- *Per aspera ad astra.* -- -- === -- -- # The LEGION Concept -- -- The LEGION class contains all functions that are common for the AIRWING, BRIGADE and FLEET classes, which inherit the LEGION class. -- -- An LEGION consists of multiple COHORTs. These cohorts "live" in a WAREHOUSE, i.e. a physical structure that can be destroyed or captured. -- -- ** The LEGION class is not meant to be used directly. Use AIRWING, BRIGADE or FLEET instead! ** -- -- @field #LEGION LEGION = { ClassName = "LEGION", verbose = 0, lid = nil, missionqueue = {}, transportqueue = {}, cohorts = {}, } --- Random score that is added to the asset score in the selection process. -- @field #number RandomAssetScore LEGION.RandomAssetScore=1 --- LEGION class version. -- @field #string version LEGION.version="0.5.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- DONE: Create FLEED class. -- DONE: Relocate cohorts. -- DONE: Aircraft will not start hot on Alert5. -- DONE: OPS transport. -- DONE: Make general so it can be inherited by AIRWING and BRIGADE classes. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Create a new LEGION class object. -- @param #LEGION self -- @param #string WarehouseName Name of the warehouse STATIC or UNIT object representing the warehouse. -- @param #string LegionName Name of the legion. Must be **unique**! -- @return #LEGION self function LEGION:New(WarehouseName, LegionName) -- Inherit everything from WAREHOUSE class. local self=BASE:Inherit(self, WAREHOUSE:New(WarehouseName, LegionName)) -- #LEGION -- Nil check. if not self then BASE:E(string.format("ERROR: Could not find warehouse %s!", WarehouseName)) return nil end -- Set some string id for output to DCS.log file. self.lid=string.format("LEGION %s | ", self.alias) -- Defaults: self:SetMarker(false) -- Dead and crash events are handled via opsgroups. self:UnHandleEvent(EVENTS.Crash) self:UnHandleEvent(EVENTS.Dead) -- Add FSM transitions. -- From State --> Event --> To State self:AddTransition("*", "MissionRequest", "*") -- Add a (mission) request to the warehouse. self:AddTransition("*", "MissionCancel", "*") -- Cancel mission. self:AddTransition("*", "MissionAssign", "*") -- Recruit assets, add to queue and request immediately. self:AddTransition("*", "TransportRequest", "*") -- Add a (mission) request to the warehouse. self:AddTransition("*", "TransportCancel", "*") -- Cancel transport. self:AddTransition("*", "TransportAssign", "*") -- Recruit assets, add to queue and request immediately. self:AddTransition("*", "OpsOnMission", "*") -- An OPSGROUP was send on a Mission (AUFTRAG). self:AddTransition("*", "LegionAssetReturned", "*") -- An asset returned (from a mission) to the Legion warehouse. ------------------------ --- Pseudo Functions --- ------------------------ --- Triggers the FSM event "Start". Starts the LEGION. Initializes parameters and starts event handlers. -- @function [parent=#LEGION] Start -- @param #LEGION self --- Triggers the FSM event "Start" after a delay. Starts the LEGION. Initializes parameters and starts event handlers. -- @function [parent=#LEGION] __Start -- @param #LEGION self -- @param #number delay Delay in seconds. --- Triggers the FSM event "Stop". Stops the LEGION and all its event handlers. -- @param #LEGION self --- Triggers the FSM event "Stop" after a delay. Stops the LEGION and all its event handlers. -- @function [parent=#LEGION] __Stop -- @param #LEGION self -- @param #number delay Delay in seconds. --- Triggers the FSM event "MissionCancel". -- @function [parent=#LEGION] MissionCancel -- @param #LEGION self -- @param Ops.Auftrag#AUFTRAG Mission The mission. --- Triggers the FSM event "MissionAssign". -- @function [parent=#LEGION] MissionAssign -- @param #LEGION self -- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @param #table Legions The legion(s) from which the mission assets are requested. --- Triggers the FSM event "MissionAssign" after a delay. -- @function [parent=#LEGION] __MissionAssign -- @param #LEGION self -- @param #number delay Delay in seconds. -- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @param #table Legions The legion(s) from which the mission assets are requested. --- On after "MissionAssign" event. -- @function [parent=#LEGION] OnAfterMissionAssign -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @param #table Legions The legion(s) from which the mission assets are requested. --- Triggers the FSM event "MissionRequest". -- @function [parent=#LEGION] MissionRequest -- @param #LEGION self -- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @param #table Assets (Optional) Assets to add. --- Triggers the FSM event "MissionRequest" after a delay. -- @function [parent=#LEGION] __MissionRequest -- @param #LEGION self -- @param #number delay Delay in seconds. -- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @param #table Assets (Optional) Assets to add. --- On after "MissionRequest" event. -- @function [parent=#LEGION] OnAfterMissionRequest -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @param #table Assets (Optional) Assets to add. --- Triggers the FSM event "MissionCancel" after a delay. -- @function [parent=#LEGION] __MissionCancel -- @param #LEGION self -- @param #number delay Delay in seconds. -- @param Ops.Auftrag#AUFTRAG Mission The mission. --- On after "MissionCancel" event. -- @function [parent=#LEGION] OnAfterMissionCancel -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.Auftrag#AUFTRAG Mission The mission. --- Triggers the FSM event "TransportAssign". -- @function [parent=#LEGION] TransportAssign -- @param #LEGION self -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. -- @param #table Legions The legion(s) to which this transport is assigned. --- Triggers the FSM event "TransportAssign" after a delay. -- @function [parent=#LEGION] __TransportAssign -- @param #LEGION self -- @param #number delay Delay in seconds. -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. -- @param #table Legions The legion(s) to which this transport is assigned. --- On after "TransportAssign" event. -- @function [parent=#LEGION] OnAfterTransportAssign -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. -- @param #table Legions The legion(s) to which this transport is assigned. --- Triggers the FSM event "TransportRequest". -- @function [parent=#LEGION] TransportRequest -- @param #LEGION self -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. --- Triggers the FSM event "TransportRequest" after a delay. -- @function [parent=#LEGION] __TransportRequest -- @param #LEGION self -- @param #number delay Delay in seconds. -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. --- On after "TransportRequest" event. -- @function [parent=#LEGION] OnAfterTransportRequest -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. --- Triggers the FSM event "TransportCancel". -- @function [parent=#LEGION] TransportCancel -- @param #LEGION self -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. --- Triggers the FSM event "TransportCancel" after a delay. -- @function [parent=#LEGION] __TransportCancel -- @param #LEGION self -- @param #number delay Delay in seconds. -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. --- On after "TransportCancel" event. -- @function [parent=#LEGION] OnAfterTransportCancel -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. --- Triggers the FSM event "OpsOnMission". -- @function [parent=#LEGION] OpsOnMission -- @param #LEGION self -- @param Ops.OpsGroup#OPSGROUP OpsGroup The OPS group on mission. -- @param Ops.Auftrag#AUFTRAG Mission The mission. --- Triggers the FSM event "OpsOnMission" after a delay. -- @function [parent=#LEGION] __OpsOnMission -- @param #LEGION self -- @param #number delay Delay in seconds. -- @param Ops.OpsGroup#OPSGROUP OpsGroup The OPS group on mission. -- @param Ops.Auftrag#AUFTRAG Mission The mission. --- On after "OpsOnMission" event. -- @function [parent=#LEGION] OnAfterOpsOnMission -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.OpsGroup#OPSGROUP OpsGroup The OPS group on mission. -- @param Ops.Auftrag#AUFTRAG Mission The mission. --- Triggers the FSM event "LegionAssetReturned". -- @function [parent=#LEGION] LegionAssetReturned -- @param #LEGION self -- @param Ops.Cohort#COHORT Cohort The cohort the asset belongs to. -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned. --- Triggers the FSM event "LegionAssetReturned" after a delay. -- @function [parent=#LEGION] __LegionAssetReturned -- @param #LEGION self -- @param #number delay Delay in seconds. -- @param Ops.Cohort#COHORT Cohort The cohort the asset belongs to. -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned. --- On after "LegionAssetReturned" event. Triggered when an asset group returned to its Legion. -- @function [parent=#LEGION] OnAfterLegionAssetReturned -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.Cohort#COHORT Cohort The cohort the asset belongs to. -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned. return self end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- User Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Set verbosity level. -- @param #LEGION self -- @param #number VerbosityLevel Level of output (higher=more). Default 0. -- @return #LEGION self function LEGION:SetVerbosity(VerbosityLevel) self.verbose=VerbosityLevel or 0 return self end --- Add a mission for the legion. It will pick the best available assets for the mission and lauch it when ready. -- @param #LEGION self -- @param Ops.Auftrag#AUFTRAG Mission Mission for this legion. -- @return #LEGION self function LEGION:AddMission(Mission) -- Set status to QUEUED. This event is only allowed for the first legion that calls it. Mission:Queued() -- Set legion status. Mission:SetLegionStatus(self, AUFTRAG.Status.QUEUED) -- Add legion to mission. Mission:AddLegion(self) -- Set target for ALERT 5. if Mission.type==AUFTRAG.Type.ALERT5 then Mission:_TargetFromObject(self:GetCoordinate()) end -- Add mission to queue. table.insert(self.missionqueue, Mission) -- Info text. local text=string.format("Added mission %s (type=%s). Starting at %s. Stopping at %s", tostring(Mission.name), tostring(Mission.type), UTILS.SecondsToClock(Mission.Tstart, true), Mission.Tstop and UTILS.SecondsToClock(Mission.Tstop, true) or "INF") self:T(self.lid..text) return self end --- Remove mission from queue. -- @param #LEGION self -- @param Ops.Auftrag#AUFTRAG Mission Mission to be removed. -- @return #LEGION self function LEGION:RemoveMission(Mission) for i,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG if mission.auftragsnummer==Mission.auftragsnummer then mission:RemoveLegion(self) table.remove(self.missionqueue, i) break end end return self end --- Add transport assignment to queue. -- @param #LEGION self -- @param Ops.OpsTransport#OPSTRANSPORT OpsTransport Transport assignment. -- @return #LEGION self function LEGION:AddOpsTransport(OpsTransport) -- Is not queued at a legion. OpsTransport:Queued() -- Set legion status. OpsTransport:SetLegionStatus(self, AUFTRAG.Status.QUEUED) -- Add mission to queue. table.insert(self.transportqueue, OpsTransport) -- Add this legion to the transport. OpsTransport:AddLegion(self) -- Info text. local text=string.format("Added Transport %s. Starting at %s-%s", tostring(OpsTransport.uid), UTILS.SecondsToClock(OpsTransport.Tstart, true), OpsTransport.Tstop and UTILS.SecondsToClock(OpsTransport.Tstop, true) or "INF") self:T(self.lid..text) return self end --- Add cohort to cohort table of this legion. -- @param #LEGION self -- @param Ops.Cohort#COHORT Cohort The cohort to be added. -- @return #LEGION self function LEGION:AddCohort(Cohort) if self:IsCohort(Cohort.name) then self:E(self.lid..string.format("ERROR: A cohort with name %s already exists in this legion. Cohorts must have UNIQUE names!")) else -- Add cohort to legion. table.insert(self.cohorts, Cohort) end return self end --- Remove cohort from cohor table of this legion. -- @param #LEGION self -- @param Ops.Cohort#COHORT Cohort The cohort to be added. -- @return #LEGION self function LEGION:DelCohort(Cohort) for i=#self.cohorts,1,-1 do local cohort=self.cohorts[i] --Ops.Cohort#COHORT if cohort.name==Cohort.name then self:T(self.lid..string.format("Removing Cohort %s", tostring(cohort.name))) table.remove(self.cohorts, i) end end return self end --- Relocate a cohort to another legion. -- Assets in stock are spawned and routed to the new legion. -- If assets are spawned, running missions will be cancelled. -- Cohort assets will not be available until relocation is finished. -- @param #LEGION self -- @param Ops.Cohort#COHORT Cohort The cohort to be relocated. -- @param Ops.Legion#LEGION Legion The legion where the cohort is relocated to. -- @param #number Delay Delay in seconds before relocation takes place. Default `nil`, *i.e.* ASAP. -- @param #number NcarriersMin Min number of transport carriers in case the troops should be transported. Default `nil` for no transport. -- @param #number NcarriersMax Max number of transport carriers. -- @param #table TransportLegions Legion(s) assigned for transportation. Default is that transport assets can only be recruited from this legion. -- @return #LEGION self function LEGION:RelocateCohort(Cohort, Legion, Delay, NcarriersMin, NcarriersMax, TransportLegions) if Delay and Delay>0 then self:ScheduleOnce(Delay, LEGION.RelocateCohort, self, Cohort, Legion, 0, NcarriersMin, NcarriersMax, TransportLegions) else -- Add cohort to legion. if Legion:IsCohort(Cohort.name) then self:E(self.lid..string.format("ERROR: Cohort %s is already part of new legion %s ==> CANNOT Relocate!", Cohort.name, Legion.alias)) return self else table.insert(Legion.cohorts, Cohort) end -- Check that cohort is part of this legion if not self:IsCohort(Cohort.name) then self:E(self.lid..string.format("ERROR: Cohort %s is NOT part of this legion %s ==> CANNOT Relocate!", Cohort.name, self.alias)) return self end -- Check that legions are different. if self.alias==Legion.alias then self:E(self.lid..string.format("ERROR: old legion %s is same as new legion %s ==> CANNOT Relocate!", self.alias, Legion.alias)) return self end -- Trigger Relocate event. Cohort:Relocate() -- Create a relocation mission. local mission=AUFTRAG:_NewRELOCATECOHORT(Legion, Cohort) if false then --- Disabled for now. -- Add assets to mission. mission:_AddAssets(Cohort.assets) -- Debug info. self:I(self.lid..string.format("Relocating Cohort %s [nassets=%d] to legion %s", Cohort.name, #Cohort.assets, Legion.alias)) -- Assign mission to this legion. self:MissionAssign(mission, {self}) else -- Assign cohort to mission. mission:AssignCohort(Cohort) -- All assets required. mission:SetRequiredAssets(#Cohort.assets) -- Set transportation. if NcarriersMin and NcarriersMin>0 then mission:SetRequiredTransport(Legion.spawnzone, NcarriersMin, NcarriersMax) end -- Assign transport legions. if TransportLegions then for _,legion in pairs(TransportLegions) do mission:AssignTransportLegion(legion) end end -- Set mission range very large. Mission designer should know... mission:SetMissionRange(10000) -- Add mission. self:AddMission(mission) end end return self end --- Get cohort by name. -- @param #LEGION self -- @param #string CohortName Name of the platoon. -- @return Ops.Cohort#COHORT The Cohort object. function LEGION:_GetCohort(CohortName) for _,_cohort in pairs(self.cohorts) do local cohort=_cohort --Ops.Cohort#COHORT if cohort.name==CohortName then return cohort end end return nil end --- Check if cohort is part of this legion. -- @param #LEGION self -- @param #string CohortName Name of the platoon. -- @return #boolean If `true`, cohort is part of this legion. function LEGION:IsCohort(CohortName) for _,_cohort in pairs(self.cohorts) do local cohort=_cohort --Ops.Cohort#COHORT if cohort.name==CohortName then return true end end return false end --- Get name of legion. This is the alias of the warehouse. -- @param #LEGION self -- @return #string Name of legion. function LEGION:GetName() return self.alias end --- Get cohort of an asset. -- @param #LEGION self -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset. -- @return Ops.Cohort#COHORT The Cohort object. function LEGION:_GetCohortOfAsset(Asset) local cohort=self:_GetCohort(Asset.squadname) return cohort end --- Check if a BRIGADE class is calling. -- @param #LEGION self -- @return #boolean If true, this is a BRIGADE. function LEGION:IsBrigade() local is=self.ClassName==BRIGADE.ClassName return is end --- Check if the AIRWING class is calling. -- @param #LEGION self -- @return #boolean If true, this is an AIRWING. function LEGION:IsAirwing() local is=self.ClassName==AIRWING.ClassName return is end --- Check if the FLEET class is calling. -- @param #LEGION self -- @return #boolean If true, this is a FLEET. function LEGION:IsFleet() local is=self.ClassName==FLEET.ClassName return is end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Start & Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Start LEGION FSM. -- @param #LEGION self function LEGION:onafterStart(From, Event, To) -- Start parent Warehouse. self:GetParent(self, LEGION).onafterStart(self, From, Event, To) -- Info. self:T3(self.lid..string.format("Starting LEGION v%s", LEGION.version)) end --- Check mission queue and assign ONE mission. -- @param #LEGION self -- @return #boolean If `true`, a mission was found and requested. function LEGION:CheckMissionQueue() -- Number of missions. local Nmissions=#self.missionqueue -- Treat special cases. if Nmissions==0 then return nil end -- Loop over missions in queue. for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG if mission:IsNotOver() and mission:IsReadyToCancel() then mission:Cancel() end end -- Check that runway is operational and that carrier is not recovering. if self:IsAirwing() then if self:IsRunwayOperational()==false then return nil end local airboss=self.airboss --Ops.Airboss#AIRBOSS if airboss then if not airboss:IsIdle() then return nil end end end -- Sort results table wrt prio and start time. local function _sort(a, b) local taskA=a --Ops.Auftrag#AUFTRAG local taskB=b --Ops.Auftrag#AUFTRAG return (taskA.prio0 then -- Number of current opsgroups. local N=mission.Nassigned-mission.Ndead if N Reinforce=%s", mission.reinforce, N, mission.Nassigned, mission.Ndead, mission.NassetsMin, tostring(reinforce))) end -- Firstly, check if mission is due? if (mission:IsQueued(self) or reinforce) and mission:IsReadyToGo() and (mission.importance==nil or mission.importance<=vip) then -- Recruit best assets for the job. local recruited, assets, legions=self:RecruitAssetsForMission(mission) -- Did we find enough assets? if recruited then -- Add to mission. --mission:_AddAssets(assets) -- Recruit asset for escorting recruited mission assets. local EscortAvail=self:RecruitAssetsForEscort(mission, assets) -- Transport available (or not required). local TransportAvail=true -- Is escort required and available? if EscortAvail then -- Recruit carrier assets for transport. local Transport=nil if mission.NcarriersMin then -- Transport legions. local Legions=mission.transportLegions or {self} -- Assign carrier assets for transport. TransportAvail, Transport=self:AssignAssetsForTransport(Legions, assets, mission.NcarriersMin, mission.NcarriersMax, mission.transportDeployZone, mission.transportDisembarkZone, mission.carrierCategories, mission.carrierAttributes, mission.carrierProperties) end -- Add opstransport to mission. if TransportAvail and Transport then mission.opstransport=Transport end end if EscortAvail and TransportAvail then -- Got a mission. self:MissionRequest(mission, assets) -- Reduce number of reinforcements. if reinforce then mission.reinforce=mission.reinforce-#assets self:I(self.lid..string.format("Reinforced with N=%d Nreinforce=%d", #assets, mission.reinforce)) end return true else -- Recruited assets but no requested escort available. Unrecruit assets! LEGION.UnRecruitAssets(assets, mission) end end -- recruited mission assets end -- mission due? end -- mission loop return nil end --- Check transport queue and assign ONE transport. -- @param #LEGION self -- @return #boolean If `true`, a transport was found and requested. function LEGION:CheckTransportQueue() -- Number of missions. local Ntransports=#self.transportqueue -- Treat special cases. if Ntransports==0 then return nil end -- TODO: Remove transports that are over! -- Sort results table wrt prio and start time. local function _sort(a, b) local taskA=a --Ops.Auftrag#AUFTRAG local taskB=b --Ops.Auftrag#AUFTRAG return (taskA.prio Request and return. self:TransportRequest(transport) return true end end end -- No transport found. return nil end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM Events ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- On after "MissionAssign" event. Mission is added to a LEGION mission queue and already requested. Needs assets to be added to the mission already. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @param #table Legions The LEGIONs. function LEGION:onafterMissionAssign(From, Event, To, Mission, Legions) for _,_Legion in pairs(Legions) do local Legion=_Legion --Ops.Legion#LEGION -- Debug info. self:T(self.lid..string.format("Assigning mission %s (%s) to legion %s", Mission.name, Mission.type, Legion.alias)) -- Add mission to legion. Legion:AddMission(Mission) -- Directly request the mission as the assets have already been selected. Legion:MissionRequest(Mission) end end --- Create a request and add it to the warehouse queue. -- @param #LEGION self -- @param Functional.Warehouse#WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. -- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. -- @param #number nAsset Number of groups requested that match the asset specification. -- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. -- @param #string Assignment A keyword or text that can later be used to identify this request and postprocess the assets. -- @return Functional.Warehouse#WAREHOUSE.Queueitem The request. function LEGION:_AddRequest(AssetDescriptor, AssetDescriptorValue, nAsset, Prio, Assignment) -- Defaults. nAsset=nAsset or 1 Prio=Prio or 50 -- Increase id. self.queueid=self.queueid+1 -- Request queue table item. local request={ uid=self.queueid, prio=Prio, warehouse=self, assetdesc=AssetDescriptor, assetdescval=AssetDescriptorValue, nasset=nAsset, transporttype=WAREHOUSE.TransportType.SELFPROPELLED, ntransport=0, assignment=tostring(Assignment), airbase=self:GetAirbase(), category=self:GetAirbaseCategory(), ndelivered=0, ntransporthome=0, assets={}, toself=true, } --Functional.Warehouse#WAREHOUSE.Queueitem -- Add request to queue. table.insert(self.queue, request) local descval="assetlist" if request.assetdesc==WAREHOUSE.Descriptor.ASSETLIST then else descval=tostring(request.assetdescval) end local text=string.format("Warehouse %s: New request from warehouse %s.\nDescriptor %s=%s, #assets=%s; Transport=%s, #transports=%s.", self.alias, self.alias, request.assetdesc, descval, tostring(request.nasset), request.transporttype, tostring(request.ntransport)) self:_DebugMessage(text, 5) return request end --- On after "MissionRequest" event. Performs a self request to the warehouse for the mission assets. Sets mission status to REQUESTED. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.Auftrag#AUFTRAG Mission The requested mission. -- @param #table Assets (Optional) Assets to add. function LEGION:onafterMissionRequest(From, Event, To, Mission, Assets) -- Debug info. self:T(self.lid..string.format("MissionRequest for mission %s [%s]", Mission:GetName(), Mission:GetType())) -- Take provided assets or that of the mission. Assets=Assets or Mission.assets -- Set mission status from QUEUED to REQUESTED. Mission:Requested() -- Set legion status. Ensures that it is not considered in the next selection. Mission:SetLegionStatus(self, AUFTRAG.Status.REQUESTED) --- -- Some assets might already be spawned and even on a different mission (orbit). -- Need to dived to set into spawned and instock assets and handle the other --- -- Assets to be requested. local Assetlist={} for _,_asset in pairs(Assets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem -- Check that this asset belongs to this Legion warehouse. if asset.wid==self.uid then if asset.spawned then --- -- Spawned Assets --- if asset.flightgroup and not asset.flightgroup:IsMissionInQueue(Mission) then -- Add new mission. asset.flightgroup:AddMission(Mission) --- -- Special Missions --- -- Get current mission. local currM=asset.flightgroup:GetMissionCurrent() if currM then -- Cancel? local cancel=false -- Pause? local pause=false -- Check if mission is INTERCEPT and asset is currently on GCI mission. If so, GCI is paused. if (currM.type==AUFTRAG.Type.GCICAP or currM.type==AUFTRAG.Type.PATROLRACETRACK) and Mission.type==AUFTRAG.Type.INTERCEPT then pause=true elseif (currM.type==AUFTRAG.Type.ONGUARD or currM.type==AUFTRAG.Type.PATROLZONE) and (Mission.type==AUFTRAG.Type.ARTY or Mission.type==AUFTRAG.Type.GROUNDATTACK) then pause=true elseif currM.type==AUFTRAG.Type.NOTHING then pause=true end -- Cancel current ALERT5 mission. if currM.type==AUFTRAG.Type.ALERT5 then cancel=true end -- Cancel current mission for relcation. if Mission.type==AUFTRAG.Type.RELOCATECOHORT then cancel=true -- Get request. local request=currM:_GetRequest(self) if request then self:T2(self.lid.."Removing group from cargoset") request.cargogroupset:Remove(asset.spawngroupname, true) else self:E(self.lid.."ERROR: no request for spawned asset!") end end -- Cancel mission. if cancel then self:T(self.lid..string.format("Cancel current mission %s [%s] to send group on mission %s [%s]", currM.name, currM.type, Mission.name, Mission.type)) asset.flightgroup:MissionCancel(currM) elseif pause then self:T(self.lid..string.format("Pausing current mission %s [%s] to send group on mission %s [%s]", currM.name, currM.type, Mission.name, Mission.type)) asset.flightgroup:PauseMission() end -- Not reserved any more. asset.isReserved=false end -- Add asset to mission. Mission:AddAsset(asset) -- Trigger event. self:__OpsOnMission(2, asset.flightgroup, Mission) else self:T(self.lid.."ERROR: OPSGROUP for asset does NOT exist but it seems to be SPAWNED (asset.spawned=true)!") end else --- -- Stock Assets --- -- These assets need to be requested and spawned. table.insert(Assetlist, asset) end end end -- Add request to legion warehouse. if #Assetlist>0 then --local text=string.format("Requesting assets for mission %s:", Mission.name) for i,_asset in pairs(Assetlist) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem -- Set asset to requested! Important so that new requests do not use this asset! asset.requested=true -- Spawned asset are not requested. if asset.spawned then asset.requested=false end -- Not reserved and more. asset.isReserved=false -- Set mission task so that the group is spawned with the right one. if Mission.missionTask then asset.missionTask=Mission.missionTask end -- Set takeoff type to parking for ALERT5 missions. We dont want them to take off without a proper mission if squadron start is hot. if Mission.type==AUFTRAG.Type.ALERT5 then asset.takeoffType=COORDINATE.WaypointType.TakeOffParking end -- Add asset to mission. Mission:AddAsset(asset) end -- Set assignment. -- TODO: Get/set functions for assignment string. local assignment=string.format("Mission-%d", Mission.auftragsnummer) --local request=Mission:_GetRequest(self) -- Add request to legion warehouse. --self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, nil, nil, Mission.prio, assignment) local request=self:_AddRequest(WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, Mission.prio, assignment) -- Debug Info. self:T(self.lid..string.format("Added request=%d for Nasssets=%d", request.uid, #Assetlist)) -- The queueid has been increased in the onafterAddRequest function. So we can simply use it here. --Mission.requestID[self.alias]=self.queueid Mission:_SetRequestID(self, self.queueid) -- Get request. --local request=self:GetRequestByID(self.queueid) -- Debug info. self:T(self.lid..string.format("Mission %s [%s] got Request ID=%d", Mission:GetName(), Mission:GetType(), self.queueid)) -- Request ship. if request then if self:IsShip() then self:T(self.lid.."Warehouse physical structure is SHIP. Requestes assets will be late activated!") request.lateActivation=true end end end end --- On after "TransportAssign" event. Transport is added to a LEGION transport queue and assets are requested from the LEGION warehouse. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. -- @param #table Legions The legion(s) to which the transport is assigned. function LEGION:onafterTransportAssign(From, Event, To, Transport, Legions) for _,_Legion in pairs(Legions) do local Legion=_Legion --Ops.Legion#LEGION -- Debug info. self:T(self.lid..string.format("Assigning transport %d to legion %s", Transport.uid, Legion.alias)) -- Add mission to legion. Legion:AddOpsTransport(Transport) -- Directly request the mission as the assets have already been selected. Legion:TransportRequest(Transport) end end --- On after "TransportRequest" event. Performs a self request to the warehouse for the transport assets. Sets transport status to REQUESTED. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.OpsTransport#OPSTRANSPORT Opstransport The requested mission. function LEGION:onafterTransportRequest(From, Event, To, OpsTransport) -- List of assets that will be requested. local AssetList={} --TODO: Find spawned assets on ALERT 5 mission OPSTRANSPORT. --local text=string.format("Requesting assets for mission %s:", Mission.name) for i,_asset in pairs(OpsTransport.assets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem -- Check that this asset belongs to this Legion warehouse. if asset.wid==self.uid then -- Set asset to requested! Important so that new requests do not use this asset! asset.requested=true asset.isReserved=false -- Set transport mission task. asset.missionTask=ENUMS.MissionTask.TRANSPORT -- Add asset to list. table.insert(AssetList, asset) end end if #AssetList>0 then -- Set mission status from QUEUED to REQUESTED. OpsTransport:Requested() -- Set legion status. Ensures that it is not considered in the next selection. OpsTransport:SetLegionStatus(self, OPSTRANSPORT.Status.REQUESTED) -- TODO: Get/set functions for assignment string. local assignment=string.format("Transport-%d", OpsTransport.uid) -- Add request to legion warehouse. --self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, AssetList, #AssetList, nil, nil, OpsTransport.prio, assignment) self:_AddRequest(WAREHOUSE.Descriptor.ASSETLIST, AssetList, #AssetList, OpsTransport.prio, assignment) -- The queueid has been increased in the onafterAddRequest function. So we can simply use it here. OpsTransport.requestID[self.alias]=self.queueid end end --- On after "TransportCancel" event. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport to be cancelled. function LEGION:onafterTransportCancel(From, Event, To, Transport) -- Info message. self:T(self.lid..string.format("Cancel transport UID=%d", Transport.uid)) -- Set status to cancelled. Transport:SetLegionStatus(self, OPSTRANSPORT.Status.CANCELLED) for i=#Transport.assets, 1, -1 do local asset=Transport.assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem -- Asset should belong to this legion. if asset.wid==self.uid then local opsgroup=asset.flightgroup if opsgroup then opsgroup:TransportCancel(Transport) end -- Delete awaited transport. local cargos=Transport:GetCargoOpsGroups(false) for _,_cargo in pairs(cargos) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP -- Remover my lift. cargo:_DelMyLift(Transport) -- Legion of cargo group local legion=cargo.legion -- Add asset back to legion. if legion then legion:T(self.lid..string.format("Adding cargo group %s back to legion", cargo:GetName())) legion:__AddAsset(0.1, cargo.group, 1) end end -- Remove asset from mission. Transport:DelAsset(asset) -- Not requested any more (if it was). asset.requested=nil asset.isReserved=nil end end -- Remove queued request (if any). if Transport.requestID[self.alias] then self:_DeleteQueueItemByID(Transport.requestID[self.alias], self.queue) end end --- On after "MissionCancel" event. Cancels the missions of all flightgroups. Deletes request from warehouse queue. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.Auftrag#AUFTRAG Mission The mission to be cancelled. function LEGION:onafterMissionCancel(From, Event, To, Mission) -- Info message. self:T(self.lid..string.format("Cancel mission %s", Mission.name)) -- Set status to cancelled. Mission:SetLegionStatus(self, AUFTRAG.Status.CANCELLED) for i=#Mission.assets, 1, -1 do local asset=Mission.assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem -- Asset should belong to this legion. if asset.wid==self.uid then local opsgroup=asset.flightgroup if opsgroup then opsgroup:MissionCancel(Mission) end -- Remove asset from mission. Mission:DelAsset(asset) -- Not requested any more (if it was). asset.requested=nil asset.isReserved=nil end end -- Remove queued request (if any). local requestID=Mission:_GetRequestID(self) if requestID then self:_DeleteQueueItemByID(requestID, self.queue) end end --- On after "OpsOnMission". -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.OpsGroup#OPSGROUP OpsGroup Ops group on mission -- @param Ops.Auftrag#AUFTRAG Mission The requested mission. function LEGION:onafterOpsOnMission(From, Event, To, OpsGroup, Mission) -- Debug info. self:T2(self.lid..string.format("Group %s on mission %s [%s]", OpsGroup:GetName(), Mission:GetName(), Mission:GetType())) if self:IsAirwing() then -- Trigger event for Airwings. self:FlightOnMission(OpsGroup, Mission) elseif self:IsBrigade() then -- Trigger event for Brigades. self:ArmyOnMission(OpsGroup, Mission) else -- Trigger event for Fleets. self:NavyOnMission(OpsGroup, Mission) end -- Load group as cargo because it cannot swim! We pause the mission. if self:IsBrigade() and self:IsShip() then OpsGroup:PauseMission() self.warehouseOpsGroup:Load(OpsGroup, self.warehouseOpsElement) end -- Trigger event for chief. if self.chief then self.chief:OpsOnMission(OpsGroup, Mission) end -- Trigger event for commander. if self.commander then self.commander:OpsOnMission(OpsGroup, Mission) end end --- On after "NewAsset" event. Asset is added to the given cohort (asset assignment). -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Functional.Warehouse#WAREHOUSE.Assetitem asset The asset that has just been added. -- @param #string assignment The (optional) assignment for the asset. function LEGION:onafterNewAsset(From, Event, To, asset, assignment) -- Call parent WAREHOUSE function first. self:GetParent(self, LEGION).onafterNewAsset(self, From, Event, To, asset, assignment) -- Debug text. local text=string.format("New asset %s with assignment %s and request assignment %s", asset.spawngroupname, tostring(asset.assignment), tostring(assignment)) self:T(self.lid..text) -- Get cohort. local cohort=self:_GetCohort(asset.assignment) -- Check if asset is already part of the squadron. If an asset returns, it will be added again! We check that asset.assignment is also assignment. if cohort then if asset.assignment==assignment then --- -- Asset is added to the COHORT for the first time --- local nunits=#asset.template.units -- Debug text. local text=string.format("Adding asset to cohort %s: assignment=%s, type=%s, attribute=%s, nunits=%d ngroup=%s", cohort.name, assignment, asset.unittype, asset.attribute, nunits, tostring(cohort.ngrouping)) self:T(self.lid..text) -- Adjust number of elements in the group. if cohort.ngrouping then local template=asset.template local N=math.max(#template.units, cohort.ngrouping) -- We need to recalc the total weight and cargo bay. asset.weight=0 asset.cargobaytot=0 -- Handle units. for i=1,N do -- Unit template. local unit = template.units[i] -- If grouping is larger than units present, copy first unit. if i>nunits then table.insert(template.units, UTILS.DeepCopy(template.units[1])) asset.cargobaytot=asset.cargobaytot+asset.cargobay[1] asset.weight=asset.weight+asset.weights[1] template.units[i].x=template.units[1].x+5*(i-nunits) template.units[i].y=template.units[1].y+5*(i-nunits) else if i<=cohort.ngrouping then asset.weight=asset.weight+asset.weights[i] asset.cargobaytot=asset.cargobaytot+asset.cargobay[i] end end -- Remove units if original template contains more than in grouping. if i>cohort.ngrouping then template.units[i]=nil end end -- Set number of units. asset.nunits=cohort.ngrouping -- Debug info. self:T(self.lid..string.format("After regrouping: Nunits=%d, weight=%.1f cargobaytot=%.1f kg", #asset.template.units, asset.weight, asset.cargobaytot)) end -- Set takeoff type. asset.takeoffType=cohort.takeoffType~=nil and cohort.takeoffType or self.takeoffType -- Set parking IDs. asset.parkingIDs=cohort.parkingIDs -- Create callsign and modex (needs to be after grouping). cohort:GetCallsign(asset) cohort:GetModex(asset) -- Set spawn group name. This has to include "AID-" for warehouse. asset.spawngroupname=string.format("%s_AID-%d", cohort.name, asset.uid) -- Add asset to cohort. cohort:AddAsset(asset) else --- -- Asset is returned to the COHORT --- self:T(self.lid..string.format("Asset returned to legion ==> calling LegionAssetReturned event")) -- Set takeoff type in case it was overwritten for an ALERT5 mission. asset.takeoffType=cohort.takeoffType -- Trigger event. self:LegionAssetReturned(cohort, asset) end end end --- On after "LegionAssetReturned" event. Triggered when an asset group returned to its legion. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Ops.Cohort#COHORT Cohort The cohort the asset belongs to. -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned. function LEGION:onafterLegionAssetReturned(From, Event, To, Cohort, Asset) -- Debug message. self:T(self.lid..string.format("Asset %s from Cohort %s returned! asset.assignment=\"%s\"", Asset.spawngroupname, Cohort.name, tostring(Asset.assignment))) -- Stop flightgroup. if Asset.flightgroup and not Asset.flightgroup:IsStopped() then Asset.flightgroup:Stop() end -- Return payload. if Asset.flightgroup:IsFlightgroup() then self:ReturnPayloadFromAsset(Asset) end -- Return tacan channel. if Asset.tacan then Cohort:ReturnTacan(Asset.tacan) end -- Set timestamp. Asset.Treturned=timer.getAbsTime() end --- On after "AssetSpawned" event triggered when an asset group is spawned into the cruel world. -- Creates a new flightgroup element and adds the mission to the flightgroup queue. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Wrapper.Group#GROUP group The group spawned. -- @param Functional.Warehouse#WAREHOUSE.Assetitem asset The asset that was spawned. -- @param Functional.Warehouse#WAREHOUSE.Pendingitem request The request of the dead asset. function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request) self:T({From, Event, To, group:GetName(), asset.assignment, request.assignment}) -- Call parent warehouse function first. self:GetParent(self, LEGION).onafterAssetSpawned(self, From, Event, To, group, asset, request) -- Get the COHORT of the asset. local cohort=self:_GetCohortOfAsset(asset) -- Check if we have a cohort or if this was some other request. if cohort then -- Debug info. self:T(self.lid..string.format("Cohort asset spawned %s", asset.spawngroupname)) -- Create a flight group. local flightgroup=self:_CreateFlightGroup(asset) --- -- Asset --- -- Set asset flightgroup. asset.flightgroup=flightgroup -- Not requested any more. asset.requested=nil -- Did not return yet. asset.Treturned=nil --- -- Cohort --- -- Get TACAN channel. local Tacan=cohort:FetchTacan() if Tacan then asset.tacan=Tacan flightgroup:SwitchTACAN(Tacan, Morse, UnitName, Band) end -- Set radio frequency and modulation local radioFreq, radioModu=cohort:GetRadio() if radioFreq then flightgroup:SwitchRadio(radioFreq, radioModu) end if cohort.fuellow then flightgroup:SetFuelLowThreshold(cohort.fuellow) end if cohort.fuellowRefuel then flightgroup:SetFuelLowRefuel(cohort.fuellowRefuel) end -- Assignment. local assignment=request.assignment -- Set pathfinding for naval groups. if self:IsFleet() then flightgroup:SetPathfinding(self.pathfinding) end if string.find(assignment, "Mission-") then --- -- Mission --- local uid=UTILS.Split(assignment, "-")[2] -- Get Mission (if any). local mission=self:GetMissionByID(uid) local despawnLanding=cohort.despawnAfterLanding~=nil and cohort.despawnAfterLanding or self.despawnAfterLanding if despawnLanding then flightgroup:SetDespawnAfterLanding() end local despawnHolding=cohort.despawnAfterHolding~=nil and cohort.despawnAfterHolding or self.despawnAfterHolding if despawnHolding then flightgroup:SetDespawnAfterHolding() end -- Add mission to flightgroup queue. if mission then if Tacan then --mission:SetTACAN(Tacan, Morse, UnitName, Band) end -- Add mission to flightgroup queue. If mission has an OPSTRANSPORT attached, all added OPSGROUPS are added as CARGO for a transport. flightgroup:AddMission(mission) -- RTZ on out of ammo. if self:IsBrigade() or self:IsFleet() then flightgroup:SetReturnOnOutOfAmmo() end -- Trigger event. self:__OpsOnMission(5, flightgroup, mission) else if Tacan then --flightgroup:SwitchTACAN(Tacan, Morse, UnitName, Band) end end -- Add group to the detection set of the CHIEF (INTEL). local chief=self.chief or (self.commander and self.commander.chief or nil) --Ops.Chief#CHIEF if chief then self:T(self.lid..string.format("Adding group %s to agents of CHIEF", group:GetName())) chief.detectionset:AddGroup(asset.flightgroup.group) end elseif string.find(assignment, "Transport-") then --- -- Transport --- local uid=UTILS.Split(assignment, "-")[2] -- Get Mission (if any). local transport=self:GetTransportByID(uid) -- Add mission to flightgroup queue. if transport then flightgroup:AddOpsTransport(transport) end end end end --- On after "AssetDead" event triggered when an asset group died. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Functional.Warehouse#WAREHOUSE.Assetitem asset The asset that is dead. -- @param Functional.Warehouse#WAREHOUSE.Pendingitem request The request of the dead asset. function LEGION:onafterAssetDead(From, Event, To, asset, request) -- Call parent warehouse function first. self:GetParent(self, LEGION).onafterAssetDead(self, From, Event, To, asset, request) -- Remove group from the detection set of the CHIEF (INTEL). if self.commander and self.commander.chief then self.commander.chief.detectionset:RemoveGroupsByName({asset.spawngroupname}) end -- Remove asset from mission is done via Mission:AssetDead() call from flightgroup onafterFlightDead function -- Remove asset from squadron same end --- On after "Destroyed" event. Remove assets from cohorts. Stop cohorts. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. function LEGION:onafterDestroyed(From, Event, To) -- Debug message. self:T(self.lid.."Legion warehouse destroyed!") -- Cancel all missions. for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG mission:Cancel() end -- Remove all cohort assets. for _,_cohort in pairs(self.cohorts) do local cohort=_cohort --Ops.Cohort#COHORT -- Stop Cohort. This also removes all assets. cohort:Stop() end -- Call parent warehouse function first. self:GetParent(self, LEGION).onafterDestroyed(self, From, Event, To) end --- On after "Request" event. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Functional.Warehouse#WAREHOUSE.Queueitem Request Information table of the request. function LEGION:onafterRequest(From, Event, To, Request) if Request.toself then -- Assets local assets=Request.cargoassets -- Get Mission local Mission=self:GetMissionByID(Request.assignment) if Mission and assets then for _,_asset in pairs(assets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem -- This would be the place to modify the asset table before the asset is spawned. end end end -- Call parent warehouse function after assets have been adjusted. self:GetParent(self, LEGION).onafterRequest(self, From, Event, To, Request) end --- On after "SelfRequest" event. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Core.Set#SET_GROUP groupset The set of asset groups that was delivered to the warehouse itself. -- @param Functional.Warehouse#WAREHOUSE.Pendingitem request Pending self request. function LEGION:onafterSelfRequest(From, Event, To, groupset, request) -- Call parent warehouse function first. self:GetParent(self, LEGION).onafterSelfRequest(self, From, Event, To, groupset, request) -- Get Mission local mission=self:GetMissionByID(request.assignment) for _,_asset in pairs(request.assets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem end for _,_group in pairs(groupset:GetSet()) do local group=_group --Wrapper.Group#GROUP end end --- On after "RequestSpawned" event. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Functional.Warehouse#WAREHOUSE.Pendingitem Request Information table of the request. -- @param Core.Set#SET_GROUP CargoGroupSet Set of cargo groups. -- @param Core.Set#SET_GROUP TransportGroupSet Set of transport groups if any. function LEGION:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet, TransportGroupSet) -- Call parent warehouse function. self:GetParent(self, LEGION).onafterRequestSpawned(self, From, Event, To, Request, CargoGroupSet, TransportGroupSet) end --- On after "Captured" event. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param DCS#coalition.side Coalition which captured the warehouse. -- @param DCS#country.id Country which has captured the warehouse. function LEGION:onafterCaptured(From, Event, To, Coalition, Country) -- Call parent warehouse function. self:GetParent(self, LEGION).onafterCaptured(self, From, Event, To, Coalition, Country) if self.chief then -- Trigger event for chief and commander. self.chief.commander:LegionLost(self, Coalition, Country) self.chief:LegionLost(self, Coalition, Country) -- Remove legion from chief and commander. self.chief:RemoveLegion(self) elseif self.commander then -- Trigger event. self.commander:LegionLost(self, Coalition,Country) -- Remove legion from commander. self.commander:RemoveLegion(self) end end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Mission Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Create a new flight group after an asset was spawned. -- @param #LEGION self -- @param Functional.Warehouse#WAREHOUSE.Assetitem asset The asset. -- @return Ops.FlightGroup#FLIGHTGROUP The created flightgroup object. function LEGION:_CreateFlightGroup(asset) -- Create flightgroup. local opsgroup=nil --Ops.OpsGroup#OPSGROUP if self:IsAirwing() then --- -- FLIGHTGROUP --- opsgroup=FLIGHTGROUP:New(asset.spawngroupname) elseif self:IsBrigade() then --- -- ARMYGROUP --- opsgroup=ARMYGROUP:New(asset.spawngroupname) elseif self:IsFleet() then --- -- NAVYGROUP --- opsgroup=NAVYGROUP:New(asset.spawngroupname) else self:E(self.lid.."ERROR: not airwing or brigade!") end -- Set legion. opsgroup:_SetLegion(self) -- Set cohort. opsgroup.cohort=self:_GetCohortOfAsset(asset) -- Set home base. opsgroup.homebase=self.airbase -- Set home zone. opsgroup.homezone=self.spawnzone -- Set weapon data. if opsgroup.cohort.weaponData then local text="Weapon data for group:" opsgroup.weaponData=opsgroup.weaponData or {} for bittype,_weapondata in pairs(opsgroup.cohort.weaponData) do local weapondata=_weapondata --Ops.OpsGroup#OPSGROUP.WeaponData opsgroup.weaponData[bittype]=UTILS.DeepCopy(weapondata) -- Careful with the units. text=text..string.format("\n- Bit=%s: Rmin=%.1f km, Rmax=%.1f km", bittype, weapondata.RangeMin/1000, weapondata.RangeMax/1000) end self:T3(self.lid..text) end return opsgroup end --- Check if an asset is currently on a mission (STARTED or EXECUTING). -- @param #LEGION self -- @param Functional.Warehouse#WAREHOUSE.Assetitem asset The asset. -- @param #table MissionTypes Types on mission to be checked. Default all. -- @return #boolean If true, asset has at least one mission of that type in the queue. function LEGION:IsAssetOnMission(asset, MissionTypes) if MissionTypes then if type(MissionTypes)~="table" then MissionTypes={MissionTypes} end else -- Check all possible types. MissionTypes=AUFTRAG.Type end if asset.flightgroup and asset.flightgroup:IsAlive() then -- Loop over mission queue. for _,_mission in pairs(asset.flightgroup.missionqueue or {}) do local mission=_mission --Ops.Auftrag#AUFTRAG if mission:IsNotOver() then -- Get flight status. local status=mission:GetGroupStatus(asset.flightgroup) -- Only if mission is started or executing. if (status==AUFTRAG.GroupStatus.STARTED or status==AUFTRAG.GroupStatus.EXECUTING) and AUFTRAG.CheckMissionType(mission.type, MissionTypes) then return true end end end end return false end --- Get the current mission of the asset. -- @param #LEGION self -- @param Functional.Warehouse#WAREHOUSE.Assetitem asset The asset. -- @return Ops.Auftrag#AUFTRAG Current mission or *nil*. function LEGION:GetAssetCurrentMission(asset) if asset.flightgroup then return asset.flightgroup:GetMissionCurrent() end return nil end --- Count payloads in stock. -- @param #LEGION self -- @param #table MissionTypes Types on mission to be checked. Default *all* possible types `AUFTRAG.Type`. -- @param #table UnitTypes Types of units. -- @param #table Payloads Specific payloads to be counted only. -- @return #number Count of available payloads in stock. function LEGION:CountPayloadsInStock(MissionTypes, UnitTypes, Payloads) if MissionTypes then if type(MissionTypes)=="string" then MissionTypes={MissionTypes} end end if UnitTypes then if type(UnitTypes)=="string" then UnitTypes={UnitTypes} end end local function _checkUnitTypes(payload) if UnitTypes then for _,unittype in pairs(UnitTypes) do if unittype==payload.aircrafttype then return true end end else -- Unit type was not specified. return true end return false end local function _checkPayloads(payload) if Payloads then for _,Payload in pairs(Payloads) do if Payload.uid==payload.uid then return true end end else -- Payload was not specified. return nil end return false end local n=0 for _,_payload in pairs(self.payloads or {}) do local payload=_payload --Ops.Airwing#AIRWING.Payload for _,MissionType in pairs(MissionTypes) do local specialpayload=_checkPayloads(payload) local compatible=AUFTRAG.CheckMissionCapability(MissionType, payload.capabilities) local goforit = specialpayload or (specialpayload==nil and compatible) if goforit and _checkUnitTypes(payload) then if payload.unlimited then -- Payload is unlimited. Return a BIG number. return 999 else n=n+payload.navail end end end end return n end --- Count missions in mission queue. -- @param #LEGION self -- @param #table MissionTypes Types on mission to be checked. Default *all* possible types `AUFTRAG.Type`. -- @return #number Number of missions that are not over yet. function LEGION:CountMissionsInQueue(MissionTypes) MissionTypes=MissionTypes or AUFTRAG.Type local N=0 for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG -- Check if this mission type is requested. if mission:IsNotOver() and AUFTRAG.CheckMissionType(mission.type, MissionTypes) then N=N+1 end end return N end --- Count total number of assets of the legion. -- @param #LEGION self -- @param #boolean InStock If `true`, only assets that are in the warehouse stock/inventory are counted. If `false`, only assets that are NOT in stock (i.e. spawned) are counted. If `nil`, all assets are counted. -- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types. -- @param #table Attributes (Optional) Count only assest that have a certain attribute(s), e.g. `GROUP.Attribute.AIR_BOMBER`. -- @return #number Amount of asset groups in stock. function LEGION:CountAssets(InStock, MissionTypes, Attributes) local N=0 for _,_cohort in pairs(self.cohorts) do local cohort=_cohort --Ops.Cohort#COHORT N=N+cohort:CountAssets(InStock, MissionTypes, Attributes) end return N end --- Get OPSGROUPs that are spawned and alive. -- @param #LEGION self -- @param #table MissionTypes (Optional) Get only assest that can perform certain mission type(s). Default is all types. -- @param #table Attributes (Optional) Get only assest that have a certain attribute(s), e.g. `WAREHOUSE.Attribute.AIR_BOMBER`. -- @return Core.Set#SET_OPSGROUP The set of OPSGROUPs. Can be empty if no groups are spawned or alive! function LEGION:GetOpsGroups(MissionTypes, Attributes) local setLegion=SET_OPSGROUP:New() for _,_cohort in pairs(self.cohorts) do local cohort=_cohort --Ops.Cohort#COHORT -- Get cohort set. local setCohort=cohort:GetOpsGroups(MissionTypes, Attributes) -- Debug info. self:T2(self.lid..string.format("Found %d opsgroups of cohort %s", setCohort:Count(), cohort.name)) -- Add to legion set. setLegion:AddSet(setCohort) end return setLegion end --- Count total number of assets in LEGION warehouse stock that also have a payload. -- @param #LEGION self -- @param #boolean Payloads (Optional) Specifc payloads to consider. Default all. -- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types. -- @param #table Attributes (Optional) Count only assest that have a certain attribute(s), e.g. `WAREHOUSE.Attribute.AIR_BOMBER`. -- @return #number Amount of asset groups in stock. function LEGION:CountAssetsWithPayloadsInStock(Payloads, MissionTypes, Attributes) -- Total number counted. local N=0 -- Number of payloads in stock per aircraft type. local Npayloads={} -- First get payloads for aircraft types of squadrons. for _,_cohort in pairs(self.cohorts) do local cohort=_cohort --Ops.Cohort#COHORT if Npayloads[cohort.aircrafttype]==nil then Npayloads[cohort.aircrafttype]=self:CountPayloadsInStock(MissionTypes, cohort.aircrafttype, Payloads) self:T3(self.lid..string.format("Got Npayloads=%d for type=%s",Npayloads[cohort.aircrafttype], cohort.aircrafttype)) end end for _,_cohort in pairs(self.cohorts) do local cohort=_cohort --Ops.Cohort#COHORT -- Number of assets in stock. local n=cohort:CountAssets(true, MissionTypes, Attributes) -- Number of payloads. local p=Npayloads[cohort.aircrafttype] or 0 -- Only the smaller number of assets or paylods is really available. local m=math.min(n, p) -- Add up what we have. Could also be zero. N=N+m -- Reduce number of available payloads. Npayloads[cohort.aircrafttype]=Npayloads[cohort.aircrafttype]-m end return N end --- Count assets on mission. -- @param #LEGION self -- @param #table MissionTypes Types on mission to be checked. Default all. -- @param Ops.Cohort#COHORT Cohort Only count assets of this cohort. Default count assets of all cohorts. -- @return #number Number of pending and queued assets. -- @return #number Number of pending assets. -- @return #number Number of queued assets. function LEGION:CountAssetsOnMission(MissionTypes, Cohort) local Nq=0 local Np=0 for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG -- Check if this mission type is requested. if AUFTRAG.CheckMissionType(mission.type, MissionTypes or AUFTRAG.Type) then for _,_asset in pairs(mission.assets or {}) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem -- Ensure asset belongs to this letion. if asset.wid==self.uid then if Cohort==nil or Cohort.name==asset.squadname then local request, isqueued=self:GetRequestByID(mission.requestID[self.alias]) if isqueued then Nq=Nq+1 else Np=Np+1 end end end end end end return Np+Nq, Np, Nq end --- Get assets on mission. -- @param #LEGION self -- @param #table MissionTypes Types on mission to be checked. Default all. -- @return #table Assets on pending requests. function LEGION:GetAssetsOnMission(MissionTypes) local assets={} local Np=0 for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG -- Check if this mission type is requested. if AUFTRAG.CheckMissionType(mission.type, MissionTypes) then for _,_asset in pairs(mission.assets or {}) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem -- Ensure asset belongs to this legion. if asset.wid==self.uid then table.insert(assets, asset) end end end end return assets end --- Get the unit types of this legion. These are the unit types of all assigned cohorts. -- @param #LEGION self -- @param #boolean onlyactive Count only the active ones. -- @param #table cohorts Table of cohorts. Default all. -- @return #table Table of unit types. function LEGION:GetAircraftTypes(onlyactive, cohorts) -- Get all unit types that can do the job. local unittypes={} -- Loop over all cohorts. for _,_cohort in pairs(cohorts or self.cohorts) do local cohort=_cohort --Ops.Cohort#COHORT if (not onlyactive) or cohort:IsOnDuty() then local gotit=false for _,unittype in pairs(unittypes) do if cohort.aircrafttype==unittype then gotit=true break end end if not gotit then table.insert(unittypes, cohort.aircrafttype) end end end return unittypes end --- Count payloads of all cohorts for all unit types. -- @param #LEGION self -- @param #string MissionType Mission type. -- @param #table Cohorts Cohorts included. -- @param #table Payloads (Optional) Special payloads. -- @return #table Table of payloads for each unit type. function LEGION:_CountPayloads(MissionType, Cohorts, Payloads) -- Number of payloads in stock per aircraft type. local Npayloads={} -- First get payloads for aircraft types of squadrons. for _,_cohort in pairs(Cohorts) do local cohort=_cohort --Ops.Cohort#COHORT -- We only need that element once. if Npayloads[cohort.aircrafttype]==nil then -- Count number of payloads in stock for the cohort aircraft type. Npayloads[cohort.aircrafttype]=cohort.legion:IsAirwing() and self:CountPayloadsInStock(MissionType, cohort.aircrafttype, Payloads) or 999 -- Debug info. self:T2(self.lid..string.format("Got N=%d payloads for mission type=%s and unit type=%s", Npayloads[cohort.aircrafttype], MissionType, cohort.aircrafttype)) end end return Npayloads end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Recruiting Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Recruit assets for a given mission. -- @param #LEGION self -- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @return #boolean If `true` enough assets could be recruited. -- @return #table Recruited assets. -- @return #table Legions of recruited assets. function LEGION:RecruitAssetsForMission(Mission) -- Get required assets. local NreqMin, NreqMax=Mission:GetRequiredAssets() -- Target position vector. local TargetVec2=Mission:GetTargetVec2() -- Payloads. local Payloads=Mission.payloads -- Largest cargo bay available of available carrier assets if mission assets need to be transported. local MaxWeight=nil if Mission.NcarriersMin then local legions={self} local cohorts=self.cohorts if Mission.transportLegions or Mission.transportCohorts then legions=Mission.transportLegions cohorts=Mission.transportCohorts end -- Get transport cohorts. local Cohorts=LEGION._GetCohorts(legions, cohorts) -- Filter cohorts that can actually perform transport missions. local transportcohorts={} for _,_cohort in pairs(Cohorts) do local cohort=_cohort --Ops.Cohort#COHORT -- Check if cohort can perform transport to target. local can=LEGION._CohortCan(cohort, AUFTRAG.Type.OPSTRANSPORT, Mission.carrierCategories, Mission.carrierAttributes, Mission.carrierProperties, nil, TargetVec2) -- MaxWeight of cargo assets is limited by the largets available cargo bay. We don't want to select, e.g., tanks that cannot be transported by APCs or helos. if can and (MaxWeight==nil or cohort.cargobayLimit>MaxWeight) then MaxWeight=cohort.cargobayLimit end end self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight or 0)) end local legions={self} local cohorts=self.cohorts if Mission.specialLegions or Mission.specialCohorts then legions=Mission.specialLegions cohorts=Mission.specialCohorts end -- Get cohorts. local Cohorts=LEGION._GetCohorts(legions, cohorts, Operation, OpsQueue) -- Recuit assets. local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads, Mission.engageRange, Mission.refuelSystem, nil, nil, MaxWeight, nil, Mission.attributes, Mission.properties, {Mission.engageWeaponType}) return recruited, assets, legions end --- Recruit assets for a given OPS transport. -- @param #LEGION self -- @param Ops.OpsTransport#OPSTRANSPORT Transport The OPS transport. -- @return #boolean If `true`, enough assets could be recruited. -- @return #table assets Recruited assets. -- @return #table legions Legions of recruited assets. function LEGION:RecruitAssetsForTransport(Transport) -- Get all undelivered cargo ops groups. local cargoOpsGroups=Transport:GetCargoOpsGroups(false) local weightGroup=0 local TotalWeight=nil -- At least one group should be spawned. if #cargoOpsGroups>0 then -- Calculate the max weight so we know which cohorts can provide carriers. TotalWeight=0 for _,_opsgroup in pairs(cargoOpsGroups) do local opsgroup=_opsgroup --Ops.OpsGroup#OPSGROUP local weight=opsgroup:GetWeightTotal() if weight>weightGroup then weightGroup=weight end TotalWeight=TotalWeight+weight end else -- No cargo groups! return false end -- TODO: Special transport cohorts/legions. -- Target is the deploy zone. local TargetVec2=Transport:GetDeployZone():GetVec2() -- Number of required carriers. local NreqMin,NreqMax=Transport:GetRequiredCarriers() -- Recruit assets and legions. local recruited, assets, legions=LEGION.RecruitCohortAssets(self.cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NreqMin, NreqMax, TargetVec2, nil, nil, nil, weightGroup, TotalWeight) return recruited, assets, legions end --- Recruit assets performing an escort mission for a given asset. -- @param #LEGION self -- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @param #table Assets Table of assets. -- @return #boolean If `true`, enough assets could be recruited or no escort was required in the first place. function LEGION:RecruitAssetsForEscort(Mission, Assets) -- Is an escort requested in the first place? if Mission.NescortMin and Mission.NescortMax and (Mission.NescortMin>0 or Mission.NescortMax>0) then -- Debug info. self:T(self.lid..string.format("Requested escort for mission %s [%s]. Required assets=%d-%d", Mission:GetName(), Mission:GetType(), Mission.NescortMin,Mission.NescortMax)) -- Get special escort legions and/or cohorts. local Cohorts={} for _,_legion in pairs(Mission.escortLegions or {}) do local legion=_legion --Ops.Legion#LEGION for _,_cohort in pairs(legion.cohorts) do local cohort=_cohort --Ops.Cohort#COHORT table.insert(Cohorts, cohort) end end for _,_cohort in pairs(Mission.escortCohorts or {}) do local cohort=_cohort --Ops.Cohort#COHORT table.insert(Cohorts, cohort) end -- No escort cohorts/legions given ==> take own cohorts. if #Cohorts==0 then Cohorts=self.cohorts end -- Call LEGION function but provide COMMANDER as self. local assigned=LEGION.AssignAssetsForEscort(self, Cohorts, Assets, Mission.NescortMin, Mission.NescortMax, Mission.escortMissionType, Mission.escortTargetTypes) return assigned end return true end --- Get cohorts. -- @param #table Legions Special legions. -- @param #table Cohorts Special cohorts. -- @param Ops.Operation#OPERATION Operation Operation. -- @param #table OpsQueue Queue of operations. -- @return #table Cohorts. function LEGION._GetCohorts(Legions, Cohorts, Operation, OpsQueue) OpsQueue=OpsQueue or {} --- Function that check if a legion or cohort is part of an operation. local function CheckOperation(LegionOrCohort) -- No operations ==> no problem! if #OpsQueue==0 then return true end -- Cohort is not dedicated to a running(!) operation. We assume so. local isAvail=true -- Only available... if Operation then isAvail=false end for _,_operation in pairs(OpsQueue) do local operation=_operation --Ops.Operation#OPERATION -- Legion is assigned to this operation. local isOps=operation:IsAssignedCohortOrLegion(LegionOrCohort) if isOps and operation:IsRunning() then -- Is dedicated. isAvail=false if Operation==nil then -- No Operation given and this is dedicated to at least one operation. return false else if Operation.uid==operation.uid then -- Operation given and is part of it. return true end end end end return isAvail end -- Chosen cohorts. local cohorts={} -- Check if there are any special legions and/or cohorts. if (Legions and #Legions>0) or (Cohorts and #Cohorts>0) then -- Add cohorts of special legions. for _,_legion in pairs(Legions or {}) do local legion=_legion --Ops.Legion#LEGION -- Check that runway is operational. local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true -- Legion has to be running. if legion:IsRunning() and Runway then -- Add cohorts of legion. for _,_cohort in pairs(legion.cohorts) do local cohort=_cohort --Ops.Cohort#COHORT if (CheckOperation(cohort.legion) or CheckOperation(cohort)) and not UTILS.IsInTable(cohorts, cohort, "name") then table.insert(cohorts, cohort) end end end end -- Add special cohorts. for _,_cohort in pairs(Cohorts or {}) do local cohort=_cohort --Ops.Cohort#COHORT if CheckOperation(cohort) and not UTILS.IsInTable(cohorts, cohort, "name") then table.insert(cohorts, cohort) end end end return cohorts end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Recruiting and Optimization Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else. -- @param Ops.Cohort#COHORT Cohort The Cohort. -- @param #string MissionType Misson type(s). -- @param #table Categories Group categories. -- @param #table Attributes Group attributes. See `GROUP.Attribute.` -- @param #table Properties DCS attributes. -- @param #table WeaponTypes Bit of weapon types. -- @param DCS#Vec2 TargetVec2 Target position. -- @param RangeMax Max range in meters. -- @param #number RefuelSystem Refueling system (boom or probe). -- @param #number CargoWeight Cargo weight [kg]. This checks the cargo bay of the cohort assets and ensures that it is large enough to carry the given cargo weight. -- @param #number MaxWeight Max weight [kg]. This checks whether the cohort asset group is not too heavy. -- @return #boolean Returns `true` if given cohort can meet all requirements. function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properties, WeaponTypes, TargetVec2, RangeMax, RefuelSystem, CargoWeight, MaxWeight) --- Function to check category. local function CheckCategory(_cohort) local cohort=_cohort --Ops.Cohort#COHORT if Categories and #Categories>0 then for _,category in pairs(Categories) do if category==cohort.category then return true end end else return true end end --- Function to check attribute. local function CheckAttribute(_cohort) local cohort=_cohort --Ops.Cohort#COHORT if Attributes and #Attributes>0 then for _,attribute in pairs(Attributes) do if attribute==cohort.attribute then return true end end else return true end end --- Function to check property. local function CheckProperty(_cohort) local cohort=_cohort --Ops.Cohort#COHORT if Properties and #Properties>0 then for _,Property in pairs(Properties) do for property,value in pairs(cohort.properties) do if Property==property then return true end end end else return true end end --- Function to check weapon type. local function CheckWeapon(_cohort) local cohort=_cohort --Ops.Cohort#COHORT if WeaponTypes and #WeaponTypes>0 then for _,WeaponType in pairs(WeaponTypes) do if WeaponType==ENUMS.WeaponFlag.Auto then return true else for _,_weaponData in pairs(cohort.weaponData or {}) do local weaponData=_weaponData --Ops.OpsGroup#OPSGROUP.WeaponData if weaponData.BitType==WeaponType then return true end end end end return false else return true end end --- Function to check range. local function CheckRange(_cohort) local cohort=_cohort --Ops.Cohort#COHORT -- Distance to target. local TargetDistance=TargetVec2 and UTILS.VecDist2D(TargetVec2, cohort.legion:GetVec2()) or 0 -- Is in range? local Rmax=cohort:GetMissionRange(WeaponTypes) local RangeMax = RangeMax or 0 local InRange=(RangeMax and math.max(RangeMax, Rmax) or Rmax) >= TargetDistance return InRange end --- Function to check weapon type. local function CheckRefueling(_cohort) local cohort=_cohort --Ops.Cohort#COHORT -- Has the requested refuelsystem? --local Refuel=RefuelSystem~=nil and (RefuelSystem==cohort.tankerSystem) or true -- STRANGE: Why did the above line did not give the same result?! Above Refuel is always true! if RefuelSystem then if cohort.tankerSystem then return RefuelSystem==cohort.tankerSystem else return false end else return true end end --- Function to check cargo weight. local function CheckCargoWeight(_cohort) local cohort=_cohort --Ops.Cohort#COHORT if CargoWeight~=nil then return cohort.cargobayLimit>=CargoWeight else return true end end --- Function to check cargo weight. local function CheckMaxWeight(_cohort) local cohort=_cohort --Ops.Cohort#COHORT if MaxWeight~=nil then cohort:T(string.format("Cohort weight=%.1f | max weight=%.1f", cohort.weightAsset, MaxWeight)) return cohort.weightAsset<=MaxWeight else return true end end -- Is capable of the mission type? local can=AUFTRAG.CheckMissionCapability(MissionType, Cohort.missiontypes) if can then can=CheckCategory(Cohort) else Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of mission types", Cohort.name)) return false end if can then if MissionType==AUFTRAG.Type.RELOCATECOHORT then can=Cohort:IsRelocating() else can=Cohort:IsOnDuty() end else Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of category", Cohort.name)) return false end if can then can=CheckAttribute(Cohort) else Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of readyiness", Cohort.name)) return false end if can then can=CheckProperty(Cohort) else Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of attribute", Cohort.name)) return false end if can then can=CheckWeapon(Cohort) else Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of property", Cohort.name)) return false end if can then can=CheckRange(Cohort) else Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of weapon type", Cohort.name)) return false end if can then can=CheckRefueling(Cohort) else Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of range", Cohort.name)) return false end if can then can=CheckCargoWeight(Cohort) else Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of refueling system", Cohort.name)) return false end if can then can=CheckMaxWeight(Cohort) else Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of cargo weight", Cohort.name)) return false end if can then return true else Cohort:T(Cohort.lid..string.format("Cohort %s cannot because of max weight", Cohort.name)) return false end return nil end --- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cannot be recruited by anyone else. -- @param #table Cohorts Cohorts included. -- @param #string MissionTypeRecruit Mission type for recruiting the cohort assets. -- @param #string MissionTypeOpt Mission type for which the assets are optimized. Default is the same as `MissionTypeRecruit`. -- @param #number NreqMin Minimum number of required assets. -- @param #number NreqMax Maximum number of required assets. -- @param DCS#Vec2 TargetVec2 Target position as 2D vector. -- @param #table Payloads Special payloads. -- @param #number RangeMax Max range in meters. -- @param #number RefuelSystem Refuelsystem. -- @param #number CargoWeight Cargo weight for recruiting transport carriers. -- @param #number TotalWeight Total cargo weight in kg. -- @param #number MaxWeight Max weight [kg] of the asset group. -- @param #table Categories Group categories. -- @param #table Attributes Group attributes. See `GROUP.Attribute.` -- @param #table Properties DCS attributes. -- @param #table WeaponTypes Bit of weapon types. -- @return #boolean If `true` enough assets could be recruited. -- @return #table Recruited assets. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else. -- @return #table Legions of recruited assets. function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, TotalWeight, MaxWeight, Categories, Attributes, Properties, WeaponTypes) -- The recruited assets. local Assets={} -- Legions of recruited assets. local Legions={} -- Set MissionTypeOpt to Recruit if nil. if MissionTypeOpt==nil then MissionTypeOpt=MissionTypeRecruit end -- Loops over cohorts. for _,_cohort in pairs(Cohorts) do local cohort=_cohort --Ops.Cohort#COHORT -- Check if cohort can do the mission. local can=LEGION._CohortCan(cohort, MissionTypeRecruit, Categories, Attributes, Properties, WeaponTypes, TargetVec2, RangeMax, RefuelSystem, CargoWeight, MaxWeight) -- Check OnDuty, capable, in range and refueling type (if TANKER). if can then -- Recruit assets from cohort. local assets, npayloads=cohort:RecruitAssets(MissionTypeRecruit, 999) -- Add assets to the list. for _,asset in pairs(assets) do table.insert(Assets, asset) end end end -- Now we have a long list with assets. LEGION._OptimizeAssetSelection(Assets, MissionTypeOpt, TargetVec2, false, TotalWeight) -- Get payloads for air assets. for _,_asset in pairs(Assets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem -- Only assets that have no payload. Should be only spawned assets! if asset.legion:IsAirwing() and not asset.payload then -- Fetch payload for asset. This can be nil! asset.payload=asset.legion:FetchPayloadFromStock(asset.unittype, MissionTypeOpt, Payloads) end end -- Remove assets that dont have a payload. for i=#Assets,1,-1 do local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem if asset.legion:IsAirwing() and not asset.payload then table.remove(Assets, i) end end -- Now find the best asset for the given payloads. LEGION._OptimizeAssetSelection(Assets, MissionTypeOpt, TargetVec2, true, TotalWeight) -- Number of assets. At most NreqMax. local Nassets=math.min(#Assets, NreqMax) if #Assets>=NreqMin then --- -- Found enough assets --- -- Total cargo bay of all carrier assets. local cargobay=0 -- Add assets to mission. for i=1,Nassets do local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem -- Asset is reserved and will not be picked for other missions. asset.isReserved=true -- Add legion. Legions[asset.legion.alias]=asset.legion -- Check if total cargo weight was given. if TotalWeight then -- Number of local N=math.floor(asset.cargobaytot/asset.nunits / CargoWeight)*asset.nunits --env.info(string.format("cargobaytot=%d, cargoweight=%d ==> N=%d", asset.cargobaytot, CargoWeight, N)) -- Sum up total cargo bay of all carrier assets. cargobay=cargobay + N*CargoWeight -- Check if enough carrier assets were found to transport all cargo. if cargobay>=TotalWeight then --env.info(string.format("FF found enough assets to transport all cargo! N=%d [%d], cargobay=%.1f >= %.1f kg total weight", i, Nassets, cargobay, TotalWeight)) Nassets=i break end end end -- Return payloads of not needed assets. for i=#Assets,Nassets+1,-1 do local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem if asset.legion:IsAirwing() and not asset.spawned then asset.legion:T2(asset.legion.lid..string.format("Returning payload from asset %s", asset.spawngroupname)) asset.legion:ReturnPayloadFromAsset(asset) end table.remove(Assets, i) end -- Found enough assets. return true, Assets, Legions else --- -- NOT enough assets --- -- Return payloads of assets. for i=1,#Assets do local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem if asset.legion:IsAirwing() and not asset.spawned then asset.legion:T2(asset.legion.lid..string.format("Returning payload from asset %s", asset.spawngroupname)) asset.legion:ReturnPayloadFromAsset(asset) end end -- Not enough assets found. return false, {}, {} end return false, {}, {} end --- Unrecruit assets. Set `isReserved` to false, return payload to airwing and (optionally) remove from assigned mission. -- @param #table Assets List of assets. -- @param Ops.Auftrag#AUFTRAG Mission (Optional) The mission from which the assets will be deleted. function LEGION.UnRecruitAssets(Assets, Mission) -- Return payloads of assets. for i=1,#Assets do local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem -- Not reserved any more. asset.isReserved=false -- Return payload. if asset.legion:IsAirwing() and not asset.spawned then asset.legion:T2(asset.legion.lid..string.format("Returning payload from asset %s", asset.spawngroupname)) asset.legion:ReturnPayloadFromAsset(asset) end -- Remove from mission. if Mission then Mission:DelAsset(asset) end end end --- Recruit and assign assets performing an escort mission for a given asset list. Note that each asset gets an escort. -- @param #LEGION self -- @param #table Cohorts Cohorts for escorting assets. -- @param #table Assets Table of assets to be escorted. -- @param #number NescortMin Min number of escort groups required per escorted asset. -- @param #number NescortMax Max number of escort groups required per escorted asset. -- @param #string MissionType Mission type. -- @param #string TargetTypes Types of targets that are engaged. -- @param #number EngageRange EngageRange in Nautical Miles. -- @return #boolean If `true`, enough assets could be recruited or no escort was required in the first place. function LEGION:AssignAssetsForEscort(Cohorts, Assets, NescortMin, NescortMax, MissionType, TargetTypes, EngageRange) -- Is an escort requested in the first place? if NescortMin and NescortMax and (NescortMin>0 or NescortMax>0) then -- Debug info. self:T(self.lid..string.format("Requested escort for %d assets from %d cohorts. Required escort assets=%d-%d", #Assets, #Cohorts, NescortMin, NescortMax)) -- Escorts for each asset. local Escorts={} local EscortAvail=true for _,_asset in pairs(Assets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem -- Target vector is the legion of the asset. local TargetVec2=asset.legion:GetVec2() -- We want airplanes for airplanes and helos for everything else. local Categories={Group.Category.HELICOPTER} local targetTypes={"Ground Units"} if asset.category==Group.Category.AIRPLANE then Categories={Group.Category.AIRPLANE} targetTypes={"Air"} end TargetTypes=TargetTypes or targetTypes -- Recruit escort asset for the mission asset. local Erecruited, eassets, elegions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.ESCORT, MissionType, NescortMin, NescortMax, TargetVec2, nil, nil, nil, nil, nil, nil, Categories) if Erecruited then Escorts[asset.spawngroupname]={EscortLegions=elegions, EscortAssets=eassets, ecategory=asset.category} else -- Could not find escort for this asset ==> Escort not possible ==> Break the loop. EscortAvail=false break end end -- ALL escorts could be recruited. if EscortAvail then local N=0 for groupname,value in pairs(Escorts) do local Elegions=value.EscortLegions local Eassets=value.EscortAssets local ecategory=value.ecategory for _,_legion in pairs(Elegions) do local legion=_legion --Ops.Legion#LEGION local OffsetVector=nil --DCS#Vec3 if ecategory==Group.Category.GROUND then -- Overhead at 1000 ft. OffsetVector={} OffsetVector.x=0 OffsetVector.y=UTILS.FeetToMeters(1000) OffsetVector.z=0 elseif MissionType==AUFTRAG.Type.SEAD then -- Overhead slightly higher and right. OffsetVector={} OffsetVector.x=-100 OffsetVector.y= 500 OffsetVector.z= 500 end -- Create and ESCORT mission for this asset. local escort=AUFTRAG:NewESCORT(groupname, OffsetVector, EngageRange, TargetTypes) -- For a SEAD mission, we also adjust the mission task. if MissionType==AUFTRAG.Type.SEAD then escort.missionTask=ENUMS.MissionTask.SEAD -- Add enroute task SEAD. local DCStask=CONTROLLABLE.EnRouteTaskSEAD(nil) table.insert(escort.enrouteTasks, DCStask) end -- Reserve assts and add to mission. for _,_asset in pairs(Eassets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem escort:AddAsset(asset) N=N+1 end -- Assign mission to legion. self:MissionAssign(escort, {legion}) end end -- Debug info. self:T(self.lid..string.format("Recruited %d escort assets", N)) -- Yup! return true else -- Debug info. self:T(self.lid..string.format("Could not get at least one escort!")) -- Could not get at least one escort. Unrecruit all recruited ones. for groupname,value in pairs(Escorts) do local Eassets=value.EscortAssets LEGION.UnRecruitAssets(Eassets) end -- No,no! return false end else -- No escort required. self:T(self.lid..string.format("No escort required! NescortMin=%s, NescortMax=%s", tostring(NescortMin), tostring(NescortMax))) return true end end --- Recruit and assign assets performing an OPSTRANSPORT for a given asset list. -- @param #LEGION self -- @param #table Legions Transport legions. -- @param #table CargoAssets Weight of the heaviest cargo group to be transported. -- @param #number NcarriersMin Min number of carrier assets. -- @param #number NcarriersMax Max number of carrier assets. -- @param Core.Zone#ZONE DeployZone Deploy zone. -- @param Core.Zone#ZONE DisembarkZone (Optional) Disembark zone. -- @param #table Categories Group categories. -- @param #table Attributes Generalizes group attributes. -- @param #table Properties DCS attributes. -- @return #boolean If `true`, enough assets could be recruited and an OPSTRANSPORT object was created. -- @return Ops.OpsTransport#OPSTRANSPORT Transport The transport. function LEGION:AssignAssetsForTransport(Legions, CargoAssets, NcarriersMin, NcarriersMax, DeployZone, DisembarkZone, Categories, Attributes, Properties) -- Is an escort requested in the first place? if NcarriersMin and NcarriersMax and (NcarriersMin>0 or NcarriersMax>0) then -- Get cohorts. local Cohorts=LEGION._GetCohorts(Legions) -- Get all legions and heaviest cargo group weight local CargoLegions={} ; local CargoWeight=nil ; local TotalWeight=0 for _,_asset in pairs(CargoAssets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem CargoLegions[asset.legion.alias]=asset.legion if CargoWeight==nil or asset.weight>CargoWeight then CargoWeight=asset.weight end TotalWeight=TotalWeight+asset.weight end -- Debug info. self:T(self.lid..string.format("Cargo weight=%.1f", CargoWeight)) self:T(self.lid..string.format("Total weight=%.1f", TotalWeight)) -- Target is the deploy zone. local TargetVec2=DeployZone:GetVec2() -- Recruit assets and legions. local TransportAvail, CarrierAssets, CarrierLegions= LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NcarriersMin, NcarriersMax, TargetVec2, nil, nil, nil, CargoWeight, TotalWeight, nil, Categories, Attributes, Properties) if TransportAvail then -- Create and OPSTRANSPORT assignment. local Transport=OPSTRANSPORT:New(nil, nil, DeployZone) if DisembarkZone then Transport:SetDisembarkZone(DisembarkZone) end -- Debug info. self:T(self.lid..string.format("Transport available with %d carrier assets", #CarrierAssets)) -- Add cargo assets to transport. for _,_legion in pairs(CargoLegions) do local legion=_legion --Ops.Legion#LEGION -- Set pickup zone to spawn zone or airbase if the legion has one that is operational. local pickupzone=legion.spawnzone -- Add TZC from legion spawn zone to deploy zone. local tpz=Transport:AddTransportZoneCombo(nil, pickupzone, Transport:GetDeployZone()) -- Set pickup airbase if the legion has an airbase. Could also be the ship itself. tpz.PickupAirbase=legion:IsRunwayOperational() and legion.airbase or nil -- Set embark zone to spawn zone. Transport:SetEmbarkZone(legion.spawnzone, tpz) -- Add cargo assets to transport. for _,_asset in pairs(CargoAssets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem if asset.legion.alias==legion.alias then Transport:AddAssetCargo(asset, tpz) end end end -- Add carrier assets. for _,_asset in pairs(CarrierAssets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem Transport:AddAsset(asset) end -- Assign TRANSPORT to legions. This also sends the request for the assets. self:TransportAssign(Transport, CarrierLegions) -- Got transport. return true, Transport else -- Debug info. self:T(self.lid..string.format("Transport assets could not be allocated ==> Unrecruiting assets")) -- Uncrecruit transport assets. LEGION.UnRecruitAssets(CarrierAssets) return false, nil end return nil, nil end -- No transport requested in the first place. return true, nil end --- Calculate the mission score of an asset. -- @param Functional.Warehouse#WAREHOUSE.Assetitem asset Asset -- @param #string MissionType Mission type for which the best assets are desired. -- @param DCS#Vec2 TargetVec2 Target 2D vector. -- @param #boolean IncludePayload If `true`, include the payload in the calulation if the asset has one attached. -- @param #number TotalWeight The total weight of the cargo to be transported, if applicable. -- @return #number Mission score. function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, IncludePayload, TotalWeight) -- Mission score. local score=0 -- Prefer highly skilled assets. if asset.skill==AI.Skill.AVERAGE then score=score+0 elseif 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. score=score+asset.cohort:GetMissionPeformance(MissionType) -- Add payload performance to score. local function scorePayload(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 0 end if IncludePayload and asset.payload then score=score+scorePayload(asset.payload, MissionType) end -- Origin: We take the OPSGROUP position or the one of the legion. local OrigVec2=asset.flightgroup and asset.flightgroup:GetVec2() or asset.legion:GetVec2() -- Distance factor. local distance=0 if TargetVec2 and OrigVec2 then -- Distance in NM. distance=UTILS.MetersToNM(UTILS.VecDist2D(OrigVec2, TargetVec2)) if asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then -- Round: 55 NM ==> 5.5 ==> 6, 63 NM ==> 6.3 ==> 6 distance=UTILS.Round(distance/10, 0) else -- For ground units the distance is a more important factor distance=UTILS.Round(distance, 0) end end -- Reduce score for legions that are futher away. score=score-distance -- Check for spawned assets. if asset.spawned and asset.flightgroup and asset.flightgroup:IsAlive() then -- Get current mission. local currmission=asset.flightgroup:GetMissionCurrent() if currmission then if currmission.type==AUFTRAG.Type.ALERT5 and currmission.alert5MissionType==MissionType then -- Prefer assets that are on ALERT5 for this mission type. score=score+25 elseif (currmission.type==AUFTRAG.Type.GCICAP or currmission.type==AUFTRAG.Type.PATROLRACETRACK) and MissionType==AUFTRAG.Type.INTERCEPT then -- Prefer assets that are on GCICAP to perform INTERCEPTS. We set this even higher than alert5 because they are already in the air. score=score+35 elseif (currmission.type==AUFTRAG.Type.ONGUARD or currmission.type==AUFTRAG.Type.PATROLZONE) and (MissionType==AUFTRAG.Type.ARTY or MissionType==AUFTRAG.Type.GROUNDATTACK) then score=score+25 elseif currmission.type==AUFTRAG.Type.NOTHING then score=score+30 end end if MissionType==AUFTRAG.Type.OPSTRANSPORT or MissionType==AUFTRAG.Type.AMMOSUPPLY or MissionType==AUFTRAG.Type.AWACS or MissionType==AUFTRAG.Type.FUELSUPPLY or MissionType==AUFTRAG.Type.TANKER then -- TODO: need to check for missions that do not require ammo like transport, recon, awacs, tanker etc. -- We better take a fresh asset. Sometimes spawned assets do something else, which is difficult to check. score=score-10 else -- Combat mission. if asset.flightgroup:IsOutOfAmmo() then -- Assets that are out of ammo are not considered. score=score-1000 end end end -- TRANSPORT specific. if MissionType==AUFTRAG.Type.OPSTRANSPORT then if TotalWeight then -- Add 1 score point for each 10 kg of cargo bay capacity up to the total cargo weight, -- and then subtract 1 score point for each excess 10kg of cargo bay capacity. if asset.cargobaymax < TotalWeight then score=score+UTILS.Round(asset.cargobaymax/10, 0) else score=score+UTILS.Round(TotalWeight/10, 0) end else -- Add 1 score point for each 10 kg of cargo bay. score=score+UTILS.Round(asset.cargobaymax/10, 0) end 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? if asset.legion and asset.legion.verbose>=2 then asset.legion:I(asset.legion.lid..string.format("Asset %s [spawned=%s] score=%d", asset.spawngroupname, tostring(asset.spawned), score)) end return score end --- Optimize chosen assets for the mission at hand. -- @param #table assets Table of (unoptimized) assets. -- @param #string MissionType Mission type. -- @param DCS#Vec2 TargetVec2 Target position as 2D vector. -- @param #boolean IncludePayload If `true`, include the payload in the calulation if the asset has one attached. -- @param #number TotalWeight The total weight of the cargo to be transported, if applicable. function LEGION._OptimizeAssetSelection(assets, MissionType, TargetVec2, IncludePayload, TotalWeight) -- Calculate the mission score of all assets. for _,_asset in pairs(assets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem -- Calculate the asset score. asset.score=LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, IncludePayload, TotalWeight) if IncludePayload then -- Max random asset score. local RandomScoreMax=asset.legion and asset.legion.RandomAssetScore or LEGION.RandomAssetScore -- Random score. local RandomScore=math.random(0, RandomScoreMax) -- Debug info. --env.info(string.format("Asset %s: randomscore=%d, max=%d", asset.spawngroupname, RandomScore, RandomScoreMax)) -- Add a bit of randomness. asset.score=asset.score+RandomScore end end --- Sort assets wrt to their mission score. Higher is better. local function optimize(a, b) local assetA=a --Functional.Warehouse#WAREHOUSE.Assetitem local assetB=b --Functional.Warehouse#WAREHOUSE.Assetitem -- Higher score wins. If equal score ==> closer wins. return (assetA.score>assetB.score) end table.sort(assets, optimize) -- Remove distance parameter. if LEGION.verbose>0 then local text=string.format("Optimized %d assets for %s mission/transport (payload=%s):", #assets, MissionType, tostring(IncludePayload)) for i,Asset in pairs(assets) do local asset=Asset --Functional.Warehouse#WAREHOUSE.Assetitem text=text..string.format("\n%d. %s [%s]: score=%d", i, asset.spawngroupname, asset.squadname, asset.score or -1) asset.score=nil end env.info(text) end end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Returns the mission for a given mission ID (Autragsnummer). -- @param #LEGION self -- @param #number mid Mission ID (Auftragsnummer). -- @return Ops.Auftrag#AUFTRAG Mission table. function LEGION:GetMissionByID(mid) for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG if mission.auftragsnummer==tonumber(mid) then return mission end end return nil end --- Returns the mission for a given ID. -- @param #LEGION self -- @param #number uid Transport UID. -- @return Ops.OpsTransport#OPSTRANSPORT Transport assignment. function LEGION:GetTransportByID(uid) for _,_transport in pairs(self.transportqueue) do local transport=_transport --Ops.OpsTransport#OPSTRANSPORT if transport.uid==tonumber(uid) then return transport end end return nil end --- Returns the mission for a given request ID. -- @param #LEGION self -- @param #number RequestID Unique ID of the request. -- @return Ops.Auftrag#AUFTRAG Mission table or *nil*. function LEGION:GetMissionFromRequestID(RequestID) for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG local mid=mission.requestID[self.alias] if mid and mid==RequestID then return mission end end return nil end --- Returns the mission for a given request. -- @param #LEGION self -- @param Functional.Warehouse#WAREHOUSE.Queueitem Request The warehouse request. -- @return Ops.Auftrag#AUFTRAG Mission table or *nil*. function LEGION:GetMissionFromRequest(Request) return self:GetMissionFromRequestID(Request.uid) end --- Fetch a payload from the airwing resources for a given unit and mission type. -- The payload with the highest priority is preferred. -- @param #LEGION self -- @param #string UnitType The type of the unit. -- @param #string MissionType The mission type. -- @param #table Payloads Specific payloads only to be considered. -- @return Ops.Airwing#AIRWING.Payload Payload table or *nil*. function LEGION:FetchPayloadFromStock(UnitType, MissionType, Payloads) -- Polymorphic. Will return something when called by airwing. return nil end --- Return payload from asset back to stock. -- @param #LEGION self -- @param Functional.Warehouse#WAREHOUSE.Assetitem asset The squadron asset. function LEGION:ReturnPayloadFromAsset(asset) -- Polymorphic. return nil end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------