From 93f4b345c5a9f20673899238ef227b29b968775a Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 12 Aug 2021 00:17:55 +0200 Subject: [PATCH] OPS Generalization --- .../Moose/Functional/Warehouse.lua | 2 +- Moose Development/Moose/Ops/AirWing.lua | 1133 +---------------- Moose Development/Moose/Ops/Auftrag.lua | 18 +- Moose Development/Moose/Ops/Brigade.lua | 2 +- Moose Development/Moose/Ops/Cohort.lua | 13 +- Moose Development/Moose/Ops/FlightGroup.lua | 41 +- Moose Development/Moose/Ops/Legion.lua | 63 +- Moose Development/Moose/Ops/OpsGroup.lua | 36 +- Moose Development/Moose/Ops/Squadron.lua | 869 +------------ 9 files changed, 107 insertions(+), 2070 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index b09a22fc1..a2d317024 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -3996,7 +3996,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu local opsgroup=_DATABASE:GetOpsGroup(group:GetName()) if opsgroup then - opsgroup:Despawn(0) + opsgroup:Despawn(0, true) opsgroup:__Stop(-0.01) else -- Setting parameter to false, i.e. creating NO dead or remove unit event, seems to not confuse the dispatcher logic. diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 22737c5dc..169cd0bce 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -38,7 +38,7 @@ -- @field Ops.RescueHelo#RESCUEHELO rescuehelo The rescue helo. -- @field Ops.RecoveryTanker#RECOVERYTANKER recoverytanker The recoverytanker. -- --- @extends Functional.Warehouse#WAREHOUSE +-- @extends Ops.Legion#LEGION --- Be surprised! -- @@ -112,8 +112,6 @@ AIRWING = { verbose = 0, lid = nil, menu = nil, - squadrons = {}, - missionqueue = {}, payloads = {}, payloadcounter = 0, pointsCAP = {}, @@ -123,10 +121,6 @@ AIRWING = { markpoints = false, } ---- Squadron asset. --- @type AIRWING.SquadronAsset --- @extends Functional.Warehouse#WAREHOUSE.Assetitem - --- Payload data. -- @type AIRWING.Payload -- @field #number uid Unique payload ID. @@ -151,7 +145,7 @@ AIRWING = { --- AIRWING class version. -- @field #string version -AIRWING.version="0.7.0" +AIRWING.version="0.8.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -179,8 +173,8 @@ AIRWING.version="0.7.0" -- @return #AIRWING self function AIRWING:New(warehousename, airwingname) - -- Inherit everything from WAREHOUSE class. - local self=BASE:Inherit(self, WAREHOUSE:New(warehousename, airwingname)) -- #AIRWING + -- Inherit everything from LEGION class. + local self=BASE:Inherit(self, LEGION:New(warehousename, airwingname)) -- #AIRWING -- Nil check. if not self then @@ -192,13 +186,8 @@ function AIRWING:New(warehousename, airwingname) self.lid=string.format("AIRWING %s | ", self.alias) -- 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("*", "SquadAssetReturned", "*") -- Flight was spawned with a mission. - - self:AddTransition("*", "FlightOnMission", "*") -- Flight was spawned with a mission. + -- From State --> Event --> To State + self:AddTransition("*", "SquadronAssetReturned", "*") -- Flight was spawned with a mission. -- Defaults: --self:SetVerbosity(0) @@ -263,7 +252,7 @@ end function AIRWING:AddSquadron(Squadron) -- Add squadron to airwing. - table.insert(self.squadrons, Squadron) + table.insert(self.cohorts, Squadron) -- Add assets to squadron. self:AddAssetToSquadron(Squadron, Squadron.Ngroups) @@ -569,7 +558,7 @@ end -- @return Ops.Squadron#SQUADRON The squadron object. function AIRWING:GetSquadron(SquadronName) - for _,_squadron in pairs(self.squadrons) do + for _,_squadron in pairs(self.cohorts) do local squadron=_squadron --Ops.Squadron#SQUADRON if squadron.name==SquadronName then @@ -581,15 +570,6 @@ function AIRWING:GetSquadron(SquadronName) return nil end ---- Set verbosity level. --- @param #AIRWING self --- @param #number VerbosityLevel Level of output (higher=more). Default 0. --- @return #AIRWING self -function AIRWING:SetVerbosity(VerbosityLevel) - self.verbose=VerbosityLevel or 0 - return self -end - --- Get squadron of an asset. -- @param #AIRWING self -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The squadron asset. @@ -608,46 +588,6 @@ function AIRWING:RemoveAssetFromSquadron(Asset) end end ---- Add a mission for the airwing. The airwing will pick the best available assets for the mission and lauch it when ready. --- @param #AIRWING self --- @param Ops.Auftrag#AUFTRAG Mission Mission for this airwing. --- @return #AIRWING self -function AIRWING:AddMission(Mission) - - -- Set status to QUEUED. This also attaches the airwing to this mission. - Mission:Queued(self) - - -- 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 #AIRWING self --- @param Ops.Auftrag#AUFTRAG Mission Mission to be removed. --- @return #AIRWING self -function AIRWING:RemoveMission(Mission) - - for i,_mission in pairs(self.missionqueue) do - local mission=_mission --Ops.Auftrag#AUFTRAG - - if mission.auftragsnummer==Mission.auftragsnummer then - mission.airwing=nil - table.remove(self.missionqueue, i) - break - end - - end - - return self -end - --- Set number of CAP flights constantly carried out. -- @param #AIRWING self -- @param #number n Number of flights. Default 1. @@ -826,7 +766,7 @@ end function AIRWING:onafterStart(From, Event, To) -- Start parent Warehouse. - self:GetParent(self).onafterStart(self, From, Event, To) + self:GetParent(self, AIRWING).onafterStart(self, From, Event, To) -- Info. self:I(self.lid..string.format("Starting AIRWING v%s", AIRWING.version)) @@ -870,7 +810,7 @@ function AIRWING:onafterStatus(From, Event, To) local assets=string.format("%d (OnMission: Total=%d, Active=%d, Queued=%d)", self:CountAssets(), Npq, Np, Nq) -- Output. - local text=string.format("%s: Missions=%d, Payloads=%d (%d), Squads=%d, Assets=%s", fsmstate, Nmissions, Npayloads, #self.payloads, #self.squadrons, assets) + local text=string.format("%s: Missions=%d, Payloads=%d (%d), Squads=%d, Assets=%s", fsmstate, Nmissions, Npayloads, #self.payloads, #self.cohorts, assets) self:I(self.lid..text) end @@ -896,7 +836,7 @@ function AIRWING:onafterStatus(From, Event, To) ------------------- if self.verbose>=3 then local text="Squadrons:" - for i,_squadron in pairs(self.squadrons) do + for i,_squadron in pairs(self.cohorts) do local squadron=_squadron --Ops.Squadron#SQUADRON local callsign=squadron.callsignName and UTILS.GetCallsignName(squadron.callsignName) or "N/A" @@ -1142,459 +1082,11 @@ function AIRWING:GetTankerForFlight(flightgroup) return nil end - ---- Check if mission is not over and ready to cancel. --- @param #AIRWING self -function AIRWING:_CheckMissions() - - -- 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 - -end ---- Get next mission. --- @param #AIRWING self --- @return Ops.Auftrag#AUFTRAG Next mission or *nil*. -function AIRWING:_GetNextMission() - - -- Number of missions. - local Nmissions=#self.missionqueue - - -- Treat special cases. - if Nmissions==0 then - return nil - 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 - self:E(self.lid..string.format("ERROR: mission %s of type %s has already assets attached!", mission.name, mission.type)) - end - mission.assets={} - - -- Assign assets to mission. - for i=1,mission.nassets do - local asset=assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem - - -- Should not happen as we just checked! - if not asset.payload then - self:E(self.lid.."ERROR: No payload for asset! This should not happen!") - end - - -- Add asset to mission. - mission:AddAsset(asset) - end - - -- Now return the remaining payloads. - for i=mission.nassets+1,#assets do - local asset=assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem - for _,uid in pairs(gotpayload) do - if uid==asset.uid then - self:ReturnPayloadFromAsset(asset) - break - end - end - end - - return mission - end - - end -- mission due? - end -- mission loop - - return nil -end - ---- Calculate the mission score of an asset. --- @param #AIRWING self --- @param Functional.Warehouse#WAREHOUSE.Assetitem asset Asset --- @param Ops.Auftrag#AUFTRAG Mission Mission for which the best assets are desired. --- @param #boolean includePayload If true, include the payload in the calulation if the asset has one attached. --- @return #number Mission score. -function AIRWING:CalculateAssetMissionScore(asset, Mission, includePayload) - - local score=0 - - -- Prefer highly skilled assets. - if asset.skill==AI.Skill.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. - local squad=self:GetSquadronOfAsset(asset) - local missionperformance=squad:GetMissionPeformance(Mission.type) - score=score+missionperformance - - -- Add payload performance to score. - if includePayload and asset.payload then - score=score+self:GetPayloadPeformance(asset.payload, Mission.type) - end - - -- Intercepts need to be carried out quickly. We prefer spawned assets. - if Mission.type==AUFTRAG.Type.INTERCEPT then - if asset.spawned then - self:T(self.lid.."Adding 25 to asset because it is spawned") - score=score+25 - 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? - - return score -end - ---- Optimize chosen assets for the mission at hand. --- @param #AIRWING self --- @param #table assets Table of (unoptimized) assets. --- @param Ops.Auftrag#AUFTRAG Mission Mission for which the best assets are desired. --- @param #boolean includePayload If true, include the payload in the calulation if the asset has one attached. -function AIRWING:_OptimizeAssetSelection(assets, Mission, includePayload) - - local TargetVec2=Mission:GetTargetVec2() - - --local dStock=self:GetCoordinate():Get2DDistance(TargetCoordinate) - - local dStock=UTILS.VecDist2D(TargetVec2, self:GetVec2()) - - -- Calculate distance to mission target. - local distmin=math.huge - local distmax=0 - for _,_asset in pairs(assets) do - local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem - - if asset.spawned then - local group=GROUP:FindByName(asset.spawngroupname) - --asset.dist=group:GetCoordinate():Get2DDistance(TargetCoordinate) - asset.dist=UTILS.VecDist2D(group:GetVec2(), TargetVec2) - else - asset.dist=dStock - end - - if asset.distdistmax then - distmax=asset.dist - end - - end - - -- Calculate the mission score of all assets. - for _,_asset in pairs(assets) do - local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem - --self:I(string.format("FF asset %s has payload %s", asset.spawngroupname, asset.payload and "yes" or "no!")) - asset.score=self:CalculateAssetMissionScore(asset, Mission, includePayload) - end - - --- Sort assets wrt to their mission score. Higher is better. - local function optimize(a, b) - local assetA=a --Functional.Warehouse#WAREHOUSE.Assetitem - local assetB=b --Functional.Warehouse#WAREHOUSE.Assetitem - - -- Higher score wins. If equal score ==> closer wins. - -- TODO: Need to include the distance in a smarter way! - return (assetA.score>assetB.score) or (assetA.score==assetB.score and assetA.dist0 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 - - if Mission.missionTask then - asset.missionTask=Mission.missionTask - end - - end - - -- Add request to airwing warehouse. - -- TODO: better Assignment string. - self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, nil, nil, Mission.prio, tostring(Mission.auftragsnummer)) - - -- The queueid has been increased in the onafterAddRequest function. So we can simply use it here. - Mission.requestID=self.queueid - end - -end - ---- On after "MissionCancel" event. Cancels the missions of all flightgroups. Deletes request from warehouse queue. --- @param #AIRWING 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 AIRWING:onafterMissionCancel(From, Event, To, Mission) - - -- Info message. - self:I(self.lid..string.format("Cancel mission %s", Mission.name)) - - local Ngroups = Mission:CountOpsGroups() - - if Mission:IsPlanned() or Mission:IsQueued() or Mission:IsRequested() or Ngroups == 0 then - - Mission:Done() - - else - - for _,_asset in pairs(Mission.assets) do - local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem - - local flightgroup=asset.flightgroup - - if flightgroup then - flightgroup:MissionCancel(Mission) - end - - -- Not requested any more (if it was). - asset.requested=nil - end - - end - - -- Remove queued request (if any). - if Mission.requestID then - self:_DeleteQueueItemByID(Mission.requestID, self.queue) - end - -end - ---- On after "NewAsset" event. Asset is added to the given squadron (asset assignment). --- @param #AIRWING 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 AIRWING:onafterNewAsset(From, Event, To, asset, assignment) - - -- Call parent warehouse function first. - self:GetParent(self).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:T3(self.lid..text) - - -- Get squadron. - local squad=self:GetSquadron(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 squad then - - if asset.assignment==assignment then - - local nunits=#asset.template.units - - -- Debug text. - local text=string.format("Adding asset to squadron %s: assignment=%s, type=%s, attribute=%s, nunits=%d %s", squad.name, assignment, asset.unittype, asset.attribute, nunits, tostring(squad.ngrouping)) - self:T(self.lid..text) - - -- Adjust number of elements in the group. - if squad.ngrouping then - local template=asset.template - - local N=math.max(#template.units, squad.ngrouping) - - -- 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])) - end - - -- Remove units if original template contains more than in grouping. - if squad.ngroupingnunits then - unit=nil - end - end - - asset.nunits=squad.ngrouping - end - - -- Set takeoff type. - asset.takeoffType=squad.takeoffType - - -- Set parking IDs. - asset.parkingIDs=squad.parkingIDs - - -- Create callsign and modex (needs to be after grouping). - squad:GetCallsign(asset) - squad:GetModex(asset) - - -- Set spawn group name. This has to include "AID-" for warehouse. - asset.spawngroupname=string.format("%s_AID-%d", squad.name, asset.uid) - - -- Add asset to squadron. - squad:AddAsset(asset) - - -- TODO - --asset.terminalType=AIRBASE.TerminalType.OpenBig - else - - --env.info("FF squad asset returned") - self:SquadAssetReturned(squad, asset) - - end - - end -end - ---- On after "AssetReturned" event. Triggered when an asset group returned to its airwing. +--- On after "SquadAssetReturned" event. Triggered when an asset group returned to its airwing. -- @param #AIRWING self -- @param #string From From state. -- @param #string Event Event. @@ -1604,326 +1096,12 @@ end function AIRWING:onafterSquadAssetReturned(From, Event, To, Squadron, Asset) -- Debug message. self:T(self.lid..string.format("Asset %s from squadron %s returned! asset.assignment=\"%s\"", Asset.spawngroupname, Squadron.name, tostring(Asset.assignment))) - - -- Stop flightgroup. - if Asset.flightgroup and not Asset.flightgroup:IsStopped() then - Asset.flightgroup:Stop() - end - - -- Return payload. - self:ReturnPayloadFromAsset(Asset) - - -- Return tacan channel. - if Asset.tacan then - Squadron: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 #AIRWING 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 AIRWING:onafterAssetSpawned(From, Event, To, group, asset, request) - - -- Call parent warehouse function first. - self:GetParent(self).onafterAssetSpawned(self, From, Event, To, group, asset, request) - - -- Get the SQUADRON of the asset. - local squadron=self:GetSquadronOfAsset(asset) - - -- Check if we have a squadron or if this was some other request. - if squadron then - - -- 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 - - --- - -- Squadron - --- - - -- Get TACAN channel. - local Tacan=squadron:FetchTacan() - if Tacan then - asset.tacan=Tacan - --flightgroup:SetDefaultTACAN(Tacan,Morse,UnitName,Band,OffSwitch) - flightgroup:SwitchTACAN(Tacan, Morse, UnitName, Band) - end - - -- Set radio frequency and modulation - local radioFreq, radioModu=squadron:GetRadio() - if radioFreq then - flightgroup:SwitchRadio(radioFreq, radioModu) - end - - if squadron.fuellow then - flightgroup:SetFuelLowThreshold(squadron.fuellow) - end - - if squadron.fuellowRefuel then - flightgroup:SetFuelLowRefuel(squadron.fuellowRefuel) - end - - --- - -- Mission - --- - - -- Get Mission (if any). - local mission=self:GetMissionByID(request.assignment) - - -- Add mission to flightgroup queue. - if mission then - - if Tacan then - --mission:SetTACAN(Tacan, Morse, UnitName, Band) - end - - -- Add mission to flightgroup queue. - asset.flightgroup:AddMission(mission) - - -- Trigger event. - self:__FlightOnMission(5, flightgroup, mission) - - else - - if Tacan then - --flightgroup:SwitchTACAN(Tacan, Morse, UnitName, Band) - end - - end - - -- Add group to the detection set of the WINGCOMMANDER. - if self.wingcommander and self.wingcommander.chief then - self.wingcommander.chief.detectionset:AddGroup(asset.flightgroup.group) - end - - end - -end - ---- On after "AssetDead" event triggered when an asset group died. --- @param #AIRWING 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 AIRWING:onafterAssetDead(From, Event, To, asset, request) - - -- Call parent warehouse function first. - self:GetParent(self).onafterAssetDead(self, From, Event, To, asset, request) - - -- Add group to the detection set of the WINGCOMMANDER. - if self.wingcommander and self.wingcommander.chief then - self.wingcommander.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 squadrons. Stop squadrons. Remove airwing from wingcommander. --- @param #AIRWING self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AIRWING:onafterDestroyed(From, Event, To) - - -- Debug message. - self:I(self.lid.."Airwing warehouse destroyed!") - - -- Cancel all missions. - for _,_mission in pairs(self.missionqueue) do - local mission=_mission --Ops.Auftrag#AUFTRAG - mission:Cancel() - end - - -- Remove all squadron assets. - for _,_squadron in pairs(self.squadrons) do - local squadron=_squadron --Ops.Squadron#SQUADRON - -- Stop Squadron. This also removes all assets. - squadron:Stop() - end - - -- Call parent warehouse function first. - self:GetParent(self).onafterDestroyed(self, From, Event, To) - -end - - ---- On after "Request" event. --- @param #AIRWING 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 AIRWING:onafterRequest(From, Event, To, Request) - - -- 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 - - -- Call parent warehouse function after assets have been adjusted. - self:GetParent(self).onafterRequest(self, From, Event, To, Request) - -end - ---- On after "SelfRequest" event. --- @param #AIRWING 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 AIRWING:onafterSelfRequest(From, Event, To, groupset, request) - - -- Call parent warehouse function first. - self:GetParent(self).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 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Create a new flight group after an asset was spawned. --- @param #AIRWING self --- @param Functional.Warehouse#WAREHOUSE.Assetitem asset The asset. --- @return Ops.FlightGroup#FLIGHTGROUP The created flightgroup object. -function AIRWING:_CreateFlightGroup(asset) - - -- Create flightgroup. - local flightgroup=FLIGHTGROUP:New(asset.spawngroupname) - - -- Set airwing. - flightgroup:SetAirwing(self) - - -- Set squadron. - flightgroup.squadron=self:GetSquadronOfAsset(asset) - - -- Set home base. - flightgroup.homebase=self.airbase - - return flightgroup -end - - ---- Check if an asset is currently on a mission (STARTED or EXECUTING). --- @param #AIRWING 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 AIRWING: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 self:CheckMissionType(mission.type, MissionTypes) then - return true - end - - end - - end - - end - - -- Alternative: run over all missions and compare to mission assets. - --[[ - for _,_mission in pairs(self.missionqueue) do - local mission=_mission --Ops.Auftrag#AUFTRAG - - if mission:IsNotOver() then - for _,_asset in pairs(mission.assets) do - local sqasset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem - - if sqasset.uid==asset.uid then - return true - end - - end - end - - end - ]] - - return false -end - ---- Get the current mission of the asset. --- @param #AIRWING self --- @param Functional.Warehouse#WAREHOUSE.Assetitem asset The asset. --- @return Ops.Auftrag#AUFTRAG Current mission or *nil*. -function AIRWING:GetAssetCurrentMission(asset) - - if asset.flightgroup then - return asset.flightgroup:GetMissionCurrent() - end - - return nil -end - --- Count payloads in stock. -- @param #AIRWING self -- @param #table MissionTypes Types on mission to be checked. Default *all* possible types `AUFTRAG.Type`. @@ -2000,253 +1178,6 @@ function AIRWING:CountPayloadsInStock(MissionTypes, UnitTypes, Payloads) return n end ---- Count missions in mission queue. --- @param #AIRWING 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 AIRWING: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 self:CheckMissionType(mission.type, MissionTypes) then - N=N+1 - end - - end - - return N -end - ---- Count total number of assets that are in the warehouse stock (not spawned). --- @param #AIRWING self --- @param #boolean InStock If true, only assets that are in the warehouse stock/inventory 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. `WAREHOUSE.Attribute.AIR_BOMBER`. --- @return #number Amount of asset groups in stock. -function AIRWING:CountAssets(InStock, MissionTypes, Attributes) - - local N=0 - - for _,_squad in pairs(self.squadrons) do - local squad=_squad --Ops.Squadron#SQUADRON - N=N+squad:CountAssets(InStock, MissionTypes, Attributes) - end - - return N -end - ---- Count assets on mission. --- @param #AIRWING self --- @param #table MissionTypes Types on mission to be checked. Default all. --- @param Ops.Squadron#SQUADRON Squadron Only count assets of this squadron. Default count assets of all squadrons. --- @return #number Number of pending and queued assets. --- @return #number Number of pending assets. --- @return #number Number of queued assets. -function AIRWING:CountAssetsOnMission(MissionTypes, Squadron) - - 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 self:CheckMissionType(mission.type, MissionTypes or AUFTRAG.Type) then - - for _,_asset in pairs(mission.assets or {}) do - local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem - - if Squadron==nil or Squadron.name==asset.squadname then - - local request, isqueued=self:GetRequestByID(mission.requestID) - - if isqueued then - Nq=Nq+1 - else - Np=Np+1 - end - - end - - end - end - end - - --env.info(string.format("FF N=%d Np=%d, Nq=%d", Np+Nq, Np, Nq)) - return Np+Nq, Np, Nq -end - ---- Count assets on mission. --- @param #AIRWING self --- @param #table MissionTypes Types on mission to be checked. Default all. --- @return #table Assets on pending requests. -function AIRWING: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 self:CheckMissionType(mission.type, MissionTypes) then - - for _,_asset in pairs(mission.assets or {}) do - local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem - - table.insert(assets, asset) - - end - end - end - - return assets -end - ---- Get the aircraft types of this airwing. --- @param #AIRWING self --- @param #boolean onlyactive Count only the active ones. --- @param #table squadrons Table of squadrons. Default all. --- @return #table Table of unit types. -function AIRWING:GetAircraftTypes(onlyactive, squadrons) - - -- Get all unit types that can do the job. - local unittypes={} - - -- Loop over all squadrons. - for _,_squadron in pairs(squadrons or self.squadrons) do - local squadron=_squadron --Ops.Squadron#SQUADRON - - if (not onlyactive) or squadron:IsOnDuty() then - - local gotit=false - for _,unittype in pairs(unittypes) do - if squadron.aircrafttype==unittype then - gotit=true - break - end - end - if not gotit then - table.insert(unittypes, squadron.aircrafttype) - end - - end - end - - return unittypes -end - ---- Check if assets for a given mission type are available. --- @param #AIRWING self --- @param Ops.Auftrag#AUFTRAG Mission The mission. --- @return #boolean If true, enough assets are available. --- @return #table Assets that can do the required mission. -function AIRWING:CanMission(Mission) - - -- Assume we CAN and NO assets are available. - local Can=true - local Assets={} - - -- Squadrons for the job. If user assigned to mission or simply all. - local squadrons=Mission.squadrons or self.squadrons - - -- Get aircraft unit types for the job. - local unittypes=self:GetAircraftTypes(true, squadrons) - - -- Count all payloads in stock. - local Npayloads=self:CountPayloadsInStock(Mission.type, unittypes, Mission.payloads) - - if Npayloads #Assets then - self:T(self.lid..string.format("INFO: Not enough assets available! Got %d but need at least %d", #Assets, Mission.nassets)) - Can=false - end - - return Can, Assets -end - ---- Check if assets for a given mission type are available. --- @param #AIRWING self --- @param Ops.Auftrag#AUFTRAG Mission The mission. --- @return #table Assets that can do the required mission. -function AIRWING:RecruitAssets(Mission) - -end - - ---- Check if a mission type is contained in a list of possible types. --- @param #AIRWING self --- @param #string MissionType The requested mission type. --- @param #table PossibleTypes A table with possible mission types. --- @return #boolean If true, the requested mission type is part of the possible mission types. -function AIRWING:CheckMissionType(MissionType, PossibleTypes) - - if type(PossibleTypes)=="string" then - PossibleTypes={PossibleTypes} - end - - for _,canmission in pairs(PossibleTypes) do - if canmission==MissionType then - return true - end - end - - return false -end - ---- Check if a mission type is contained in a list of possible capabilities. --- @param #AIRWING self --- @param #string MissionType The requested mission type. --- @param #table Capabilities A table with possible capabilities. --- @return #boolean If true, the requested mission type is part of the possible mission types. -function AIRWING:CheckMissionCapability(MissionType, Capabilities) - - for _,cap in pairs(Capabilities) do - local capability=cap --Ops.Auftrag#AUFTRAG.Capability - if capability.MissionType==MissionType then - return true - end - end - - return false -end - --- Get payload performance for a given type of misson type. -- @param #AIRWING self -- @param #AIRWING.Payload Payload The payload table. @@ -2286,46 +1217,6 @@ function AIRWING:GetPayloadMissionTypes(Payload) return missiontypes end ---- Returns the mission for a given mission ID (Autragsnummer). --- @param #AIRWING self --- @param #number mid Mission ID (Auftragsnummer). --- @return Ops.Auftrag#AUFTRAG Mission table. -function AIRWING: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 request ID. --- @param #AIRWING self --- @param #number RequestID Unique ID of the request. --- @return Ops.Auftrag#AUFTRAG Mission table or *nil*. -function AIRWING:GetMissionFromRequestID(RequestID) - for _,_mission in pairs(self.missionqueue) do - local mission=_mission --Ops.Auftrag#AUFTRAG - if mission.requestID and mission.requestID==RequestID then - return mission - end - end - return nil -end - ---- Returns the mission for a given request. --- @param #AIRWING self --- @param Functional.Warehouse#WAREHOUSE.Queueitem Request The warehouse request. --- @return Ops.Auftrag#AUFTRAG Mission table or *nil*. -function AIRWING:GetMissionFromRequest(Request) - return self:GetMissionFromRequestID(Request.uid) -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index abda972be..2699cb668 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -443,7 +443,7 @@ AUFTRAG.TargetType={ -- @field Core.Point#COORDINATE wpegresscoordinate Egress waypoint coordinate. -- @field Ops.OpsGroup#OPSGROUP.Task waypointtask Waypoint task. -- @field #string status Group mission status. --- @field Ops.AirWing#AIRWING.SquadronAsset asset The squadron asset. +-- @field Functional.Warehouse#WAREHOUSE.Assetitem asset The warehouse asset. --- AUFTRAG class version. @@ -2689,7 +2689,7 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Ops.AirWing#AIRWING.SquadronAsset Asset The asset. +-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset. function AUFTRAG:onafterAssetDead(From, Event, To, Asset) -- Number of groups alive. @@ -3147,7 +3147,7 @@ end --- Add asset to mission. -- @param #AUFTRAG self --- @param Ops.AirWing#AIRWING.SquadronAsset Asset The asset to be added to the mission. +-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be added to the mission. -- @return #AUFTRAG self function AUFTRAG:AddAsset(Asset) @@ -3160,12 +3160,12 @@ end --- Delete asset from mission. -- @param #AUFTRAG self --- @param Ops.AirWing#AIRWING.SquadronAsset Asset The asset to be removed. +-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be removed. -- @return #AUFTRAG self function AUFTRAG:DelAsset(Asset) for i,_asset in pairs(self.assets or {}) do - local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset + local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem if asset.uid==Asset.uid then self:T(self.lid..string.format("Removing asset \"%s\" from mission", tostring(asset.spawngroupname))) @@ -3181,11 +3181,11 @@ end --- Get asset by its spawn group name. -- @param #AUFTRAG self -- @param #string Name Asset spawn group name. --- @return Ops.AirWing#AIRWING.SquadronAsset +-- @return Functional.Warehouse#WAREHOUSE.Assetitem Asset. function AUFTRAG:GetAssetByName(Name) for i,_asset in pairs(self.assets or {}) do - local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset + local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem if asset.spawngroupname==Name then return asset @@ -3196,9 +3196,9 @@ function AUFTRAG:GetAssetByName(Name) return nil end ---- Count alive ops groups assigned for this mission. +--- Count alive OPS groups assigned for this mission. -- @param #AUFTRAG self --- @return #number Number of alive flight groups. +-- @return #number Number of alive OPS groups. function AUFTRAG:CountOpsGroups() local N=0 for _,_groupdata in pairs(self.groupdata) do diff --git a/Moose Development/Moose/Ops/Brigade.lua b/Moose Development/Moose/Ops/Brigade.lua index 98d611dcc..5a6c96c09 100644 --- a/Moose Development/Moose/Ops/Brigade.lua +++ b/Moose Development/Moose/Ops/Brigade.lua @@ -86,7 +86,7 @@ end -- @return #BRIGADE self function BRIGADE:AddPlatoon(Platoon) - -- Add squadron to airwing. + -- Add platoon to brigade. table.insert(self.cohorts, Platoon) -- Add assets to squadron. diff --git a/Moose Development/Moose/Ops/Cohort.lua b/Moose Development/Moose/Ops/Cohort.lua index 6651e14a0..3b88595b1 100644 --- a/Moose Development/Moose/Ops/Cohort.lua +++ b/Moose Development/Moose/Ops/Cohort.lua @@ -119,9 +119,6 @@ function COHORT:New(TemplateGroupName, Ngroups, CohortName) self:SetMissionRange() self:SetSkill(AI.Skill.GOOD) - -- Everyone can ORBIT. - --self:AddMissionCapability(AUFTRAG.Type.ORBIT) - -- Generalized attribute. self.attribute=self.templategroup:GetAttribute() @@ -718,9 +715,9 @@ function COHORT:CanMission(Mission) return false end - -- Check mission type. WARNING: This assumes that all assets of the squad can do the same mission types! + -- Check mission type. WARNING: This assumes that all assets of the cohort can do the same mission types! if not self:CheckMissionType(Mission.type, self:GetMissionTypes()) then - self:T(self.lid..string.format("INFO: Squad cannot do mission type %s (%s, %s)", Mission.type, Mission.name, Mission:GetTargetName())) + self:T(self.lid..string.format("INFO: Cohort cannot do mission type %s (%s, %s)", Mission.type, Mission.name, Mission:GetTargetName())) return false end @@ -742,9 +739,9 @@ function COHORT:CanMission(Mission) -- Max engage range. local engagerange=Mission.engageRange and math.max(self.engageRange, Mission.engageRange) or self.engageRange - -- Set range is valid. Mission engage distance can overrule the squad engage range. + -- Set range is valid. Mission engage distance can overrule the cohort engage range. if TargetDistance>engagerange then - self:I(self.lid..string.format("INFO: Squad is not in range. Target dist=%d > %d NM max mission Range", UTILS.MetersToNM(TargetDistance), UTILS.MetersToNM(engagerange))) + self:I(self.lid..string.format("INFO: Cohort is not in range. Target dist=%d > %d NM max mission Range", UTILS.MetersToNM(TargetDistance), UTILS.MetersToNM(engagerange))) return false end @@ -974,7 +971,7 @@ end --- Check if the platoon attribute matches the given attribute(s). -- @param #COHORT self -- @param #table Attributes The requested attributes. See `WAREHOUSE.Attribute` enum. Can also be passed as a single attribute `#string`. --- @return #boolean If true, the squad has the requested attribute. +-- @return #boolean If true, the cohort has the requested attribute. function COHORT:CheckAttribute(Attributes) if type(Attributes)~="table" then diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 1fa0c414b..2787b615a 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -44,8 +44,6 @@ -- @field #boolean fuelcritical Fuel critical switch. -- @field #number fuelcriticalthresh Critical fuel threshold in percent. -- @field #boolean fuelcriticalrtb RTB on critical fuel switch. --- @field Ops.Squadron#SQUADRON squadron The squadron of this flight group. --- @field Ops.AirWing#AIRWING airwing The airwing the flight group belongs to. -- @field Ops.FlightControl#FLIGHTCONTROL flightcontrol The flightcontrol handling this group. -- @field Ops.Airboss#AIRBOSS airboss The airboss handling this group. -- @field Core.UserFlag#USERFLAG flaghold Flag for holding. @@ -136,7 +134,6 @@ FLIGHTGROUP = { fuelcriticalrtb = false, outofAAMrtb = false, outofAGMrtb = false, - squadron = nil, flightcontrol = nil, flaghold = nil, Tholding = nil, @@ -336,21 +333,11 @@ function FLIGHTGROUP:AddTaskEnrouteEngageTargetsInZone(ZoneRadius, TargetTypes, self:AddTaskEnroute(Task) end ---- Set AIRWING the flight group belongs to. --- @param #FLIGHTGROUP self --- @param Ops.AirWing#AIRWING airwing The AIRWING object. --- @return #FLIGHTGROUP self -function FLIGHTGROUP:SetAirwing(airwing) - self:T(self.lid..string.format("Add flight to AIRWING %s", airwing.alias)) - self.airwing=airwing - return self -end - --- Get airwing the flight group belongs to. -- @param #FLIGHTGROUP self -- @return Ops.AirWing#AIRWING The AIRWING object. function FLIGHTGROUP:GetAirWing() - return self.airwing + return self.legion end --- Set if aircraft is VTOL capable. Unfortunately, there is no DCS way to determine this via scripting. @@ -1891,11 +1878,15 @@ function FLIGHTGROUP:onafterArrived(From, Event, To) -- Add flight to arrived queue. self.flightcontrol:SetFlightStatus(self, FLIGHTCONTROL.FlightStatus.ARRIVED) end + + local airwing=self:GetAirWing() -- Check what to do. - if self.airwing then + if airwing then + -- Add the asset back to the airwing. - self.airwing:AddAsset(self.group, 1) + airwing:AddAsset(self.group, 1) + elseif self.isLandingAtAirbase then local Template=UTILS.DeepCopy(self.template) --DCS#Template @@ -1991,18 +1982,6 @@ function FLIGHTGROUP:onafterDead(From, Event, To) self.flightcontrol=nil end - if self.Ndestroyed==#self.elements then - if self.squadron then - -- All elements were destroyed ==> Asset group is gone. - self.squadron:DelGroup(self.groupname) - end - else - if self.airwing then - -- Not all assets were destroyed (despawn) ==> Add asset back to airwing. - --self.airwing:AddAsset(self.group, 1) - end - end - -- Call OPSGROUP function. self:GetParent(self).onafterDead(self, From, Event, To) @@ -2912,11 +2891,13 @@ function FLIGHTGROUP:onafterFuelLow(From, Event, To) -- Back to destination or home. local airbase=self.destbase or self.homebase + + local airwing=self:GetAirWing() - if self.airwing then + if airwing then -- Get closest tanker from airwing that can refuel this flight. - local tanker=self.airwing:GetTankerForFlight(self) + local tanker=airwing:GetTankerForFlight(self) if tanker and self.fuellowrefuel then diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 098aaecc9..b7f31dfbb 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -616,7 +616,7 @@ function LEGION:onafterOpsOnMission(From, Event, To, OpsGroup, Mission) end ---- On after "NewAsset" event. Asset is added to the given squadron (asset assignment). +--- 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. @@ -632,26 +632,25 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment) local text=string.format("New asset %s with assignment %s and request assignment %s", asset.spawngroupname, tostring(asset.assignment), tostring(assignment)) self:T3(self.lid..text) - -- Get squadron. - --local squad=self:GetSquadron(asset.assignment) - local squad=self:_GetCohort(asset.assignment) + -- 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 squad then + if cohort then if asset.assignment==assignment then local nunits=#asset.template.units -- Debug text. - local text=string.format("Adding asset to squadron %s: assignment=%s, type=%s, attribute=%s, nunits=%d %s", squad.name, assignment, asset.unittype, asset.attribute, nunits, tostring(squad.ngrouping)) + local text=string.format("Adding asset to squadron %s: assignment=%s, type=%s, attribute=%s, nunits=%d %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 squad.ngrouping then + if cohort.ngrouping then local template=asset.template - local N=math.max(#template.units, squad.ngrouping) + local N=math.max(#template.units, cohort.ngrouping) -- Handle units. for i=1,N do @@ -665,36 +664,36 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment) end -- Remove units if original template contains more than in grouping. - if squad.ngroupingnunits then + if cohort.ngroupingnunits then unit=nil end end - asset.nunits=squad.ngrouping + asset.nunits=cohort.ngrouping end -- Set takeoff type. - asset.takeoffType=squad.takeoffType + asset.takeoffType=cohort.takeoffType -- Set parking IDs. - asset.parkingIDs=squad.parkingIDs + asset.parkingIDs=cohort.parkingIDs -- Create callsign and modex (needs to be after grouping). - squad:GetCallsign(asset) - squad:GetModex(asset) + cohort:GetCallsign(asset) + cohort:GetModex(asset) -- Set spawn group name. This has to include "AID-" for warehouse. - asset.spawngroupname=string.format("%s_AID-%d", squad.name, asset.uid) + asset.spawngroupname=string.format("%s_AID-%d", cohort.name, asset.uid) -- Add asset to squadron. - squad:AddAsset(asset) + cohort:AddAsset(asset) -- TODO --asset.terminalType=AIRBASE.TerminalType.OpenBig else --env.info("FF squad asset returned") - self:AssetReturned(squad, asset) + self:AssetReturned(cohort, asset) end @@ -753,10 +752,10 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request) self:GetParent(self, LEGION).onafterAssetSpawned(self, From, Event, To, group, asset, request) -- Get the SQUADRON of the asset. - local squadron=self:_GetCohortOfAsset(asset) + local cohort=self:_GetCohortOfAsset(asset) -- Check if we have a squadron or if this was some other request. - if squadron then + if cohort then -- Create a flight group. local flightgroup=self:_CreateFlightGroup(asset) @@ -779,7 +778,7 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request) --- -- Get TACAN channel. - local Tacan=squadron:FetchTacan() + local Tacan=cohort:FetchTacan() if Tacan then asset.tacan=Tacan --flightgroup:SetDefaultTACAN(Tacan,Morse,UnitName,Band,OffSwitch) @@ -787,17 +786,17 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request) end -- Set radio frequency and modulation - local radioFreq, radioModu=squadron:GetRadio() + local radioFreq, radioModu=cohort:GetRadio() if radioFreq then flightgroup:SwitchRadio(radioFreq, radioModu) end - if squadron.fuellow then - flightgroup:SetFuelLowThreshold(squadron.fuellow) + if cohort.fuellow then + flightgroup:SetFuelLowThreshold(cohort.fuellow) end - if squadron.fuellowRefuel then - flightgroup:SetFuelLowRefuel(squadron.fuellowRefuel) + if cohort.fuellowRefuel then + flightgroup:SetFuelLowRefuel(cohort.fuellowRefuel) end --- @@ -858,7 +857,7 @@ function LEGION:onafterAssetDead(From, Event, To, asset, request) -- Remove asset from squadron same end ---- On after "Destroyed" event. Remove assets from squadrons. Stop squadrons. Remove airwing from wingcommander. +--- On after "Destroyed" event. Remove assets from cohorts. Stop squadrons. -- @param #LEGION self -- @param #string From From state. -- @param #string Event Event. @@ -874,11 +873,11 @@ function LEGION:onafterDestroyed(From, Event, To) mission:Cancel() end - -- Remove all squadron assets. - for _,_squadron in pairs(self.cohorts) do - local squadron=_squadron --Ops.Squadron#SQUADRON - -- Stop Squadron. This also removes all assets. - squadron:Stop() + -- 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. @@ -964,7 +963,7 @@ function LEGION:_CreateFlightGroup(asset) flightgroup:_SetLegion(self) -- Set squadron. - flightgroup.squadron=self:_GetCohortOfAsset(asset) + flightgroup.cohort=self:_GetCohortOfAsset(asset) -- Set home base. flightgroup.homebase=self.airbase diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 8f489f430..1a1e38ddd 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -1329,7 +1329,9 @@ function OPSGROUP:DespawnElement(Element, Delay, NoEventRemoveUnit) return self end ---- Despawn the group. The whole group is despawned and (optionally) a "Remove Unit" event is generated for all current units of the group. +--- Despawn the group. The whole group is despawned and a "`Remove Unit`" event is generated for all current units of the group. +-- If no `Remove Unit` event should be generated, the second optional parameter needs to be set to `true`. +-- If this group belongs to an AIRWING, BRIGADE or FLEET, it will be added to the warehouse stock if the `NoEventRemoveUnit` parameter is `false` or `nil`. -- @param #OPSGROUP self -- @param #number Delay Delay in seconds before the group will be despawned. Default immediately. -- @param #boolean NoEventRemoveUnit If `true`, **no** event "Remove Unit" is generated. @@ -1339,15 +1341,17 @@ function OPSGROUP:Despawn(Delay, NoEventRemoveUnit) if Delay and Delay>0 then self.scheduleIDDespawn=self:ScheduleOnce(Delay, OPSGROUP.Despawn, self, 0, NoEventRemoveUnit) else - - -- Debug info. - self:I(self.lid..string.format("Despawning Group!")) - + if self.legion and not NoEventRemoveUnit then -- Add asset back in 10 seconds. - self.legion:AddAsset(self.group, 1) + self:I(self.lid..string.format("Despawning Group by adding asset to LEGION!")) + self.legion:AddAsset(self.group, 1) + return end + -- Debug info. + self:I(self.lid..string.format("Despawning Group!")) + -- DCS group obejct. local DCSGroup=self:GetDCSGroup() @@ -3754,10 +3758,10 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission) -- Switch to default. self:_SwitchTACAN() - -- Return Squadron TACAN channel. - local squadron=self.squadron --Ops.Squadron#SQUADRON - if squadron then - squadron:ReturnTacan(Mission.tacan.Channel) + -- Return Cohort's TACAN channel. + local cohort=self.cohort --Ops.Cohort#COHORT + if cohort then + cohort:ReturnTacan(Mission.tacan.Channel) end -- Set asset TACAN to nil. @@ -5174,6 +5178,18 @@ function OPSGROUP:onafterDead(From, Event, To) -- No current cargo transport. self.cargoTransport=nil + + if self.Ndestroyed==#self.elements then + if self.cohort then + -- All elements were destroyed ==> Asset group is gone. + self.cohort:DelGroup(self.groupname) + end + else + if self.airwing then + -- Not all assets were destroyed (despawn) ==> Add asset back to airwing. + --self.airwing:AddAsset(self.group, 1) + end + end -- Stop in a sec. --self:__Stop(-5) diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index 74cd5cc31..a86d91f43 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -36,7 +36,6 @@ -- @field #number modexcounter Counter to incease modex number for assets. -- @field #string callsignName Callsign name. -- @field #number callsigncounter Counter to increase callsign names for new assets. --- @field Ops.AirWing#AIRWING airwing The AIRWING object the squadron belongs to. -- @field #number Ngroups Number of asset flight groups this squadron has. -- @field #number engageRange Mission range in meters. -- @field #string attribute Generalized attribute of the squadron template group. @@ -47,7 +46,7 @@ -- @field #number radioModu Radio modulation the squad uses. -- @field #number takeoffType Take of type. -- @field #table parkingIDs Parking IDs for this squadron. --- @extends Core.Fsm#FSM +-- @extends Ops.Cohort#COHORT --- *It is unbelievable what a squadron of twelve aircraft did to tip the balance.* -- Adolf Galland -- @@ -65,31 +64,17 @@ SQUADRON = { ClassName = "SQUADRON", verbose = 0, - lid = nil, - name = nil, - templatename = nil, - aircrafttype = nil, - assets = {}, - missiontypes = {}, - repairtime = 0, - maintenancetime= 0, - livery = nil, - skill = nil, modex = nil, modexcounter = 0, callsignName = nil, callsigncounter= 11, - airwing = nil, - Ngroups = nil, - engageRange = nil, tankerSystem = nil, refuelSystem = nil, - tacanChannel = {}, } --- SQUADRON class version. -- @field #string version -SQUADRON.version="0.7.0" +SQUADRON.version="0.8.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -113,37 +98,11 @@ SQUADRON.version="0.7.0" function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName) -- Inherit everything from FSM class. - local self=BASE:Inherit(self, FSM:New()) -- #SQUADRON + local self=BASE:Inherit(self, COHORT:New(TemplateGroupName, Ngroups, SquadronName)) -- #SQUADRON - -- Name of the template group. - self.templatename=TemplateGroupName - - -- Squadron name. - self.name=tostring(SquadronName or TemplateGroupName) - - -- Set some string id for output to DCS.log file. - self.lid=string.format("SQUADRON %s | ", self.name) - - -- Template group. - self.templategroup=GROUP:FindByName(self.templatename) - - -- Check if template group exists. - if not self.templategroup then - self:E(self.lid..string.format("ERROR: Template group %s does not exist!", tostring(self.templatename))) - return nil - end - - -- Defaults. - self.Ngroups=Ngroups or 3 - self:SetMissionRange() - self:SetSkill(AI.Skill.GOOD) - -- Everyone can ORBIT. self:AddMissionCapability(AUFTRAG.Type.ORBIT) - -- Generalized attribute. - self.attribute=self.templategroup:GetAttribute() - -- Aircraft type. self.aircrafttype=self.templategroup:GetTypeName() @@ -151,58 +110,11 @@ function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName) self.refuelSystem=select(2, self.templategroup:GetUnit(1):IsRefuelable()) self.tankerSystem=select(2, self.templategroup:GetUnit(1):IsTanker()) - - -- Start State. - self:SetStartState("Stopped") - - -- Add FSM transitions. - -- From State --> Event --> To State - self:AddTransition("Stopped", "Start", "OnDuty") -- Start FSM. - self:AddTransition("*", "Status", "*") -- Status update. - - self:AddTransition("OnDuty", "Pause", "Paused") -- Pause squadron. - self:AddTransition("Paused", "Unpause", "OnDuty") -- Unpause squadron. - - self:AddTransition("*", "Stop", "Stopped") -- Stop squadron. - - ------------------------ --- Pseudo Functions --- ------------------------ - --- Triggers the FSM event "Start". Starts the SQUADRON. Initializes parameters and starts event handlers. - -- @function [parent=#SQUADRON] Start - -- @param #SQUADRON self - - --- Triggers the FSM event "Start" after a delay. Starts the SQUADRON. Initializes parameters and starts event handlers. - -- @function [parent=#SQUADRON] __Start - -- @param #SQUADRON self - -- @param #number delay Delay in seconds. - - --- Triggers the FSM event "Stop". Stops the SQUADRON and all its event handlers. - -- @param #SQUADRON self - - --- Triggers the FSM event "Stop" after a delay. Stops the SQUADRON and all its event handlers. - -- @function [parent=#SQUADRON] __Stop - -- @param #SQUADRON self - -- @param #number delay Delay in seconds. - - --- Triggers the FSM event "Status". - -- @function [parent=#SQUADRON] Status - -- @param #SQUADRON self - - --- Triggers the FSM event "Status" after a delay. - -- @function [parent=#SQUADRON] __Status - -- @param #SQUADRON self - -- @param #number delay Delay in seconds. - - - -- Debug trace. - if false then - BASE:TraceOnOff(true) - BASE:TraceClass(self.ClassName) - BASE:TraceLevel(1) - end + -- See COHORT class return self end @@ -211,68 +123,6 @@ end -- User functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Set livery painted on all squadron aircraft. --- Note that the livery name in general is different from the name shown in the mission editor. --- --- Valid names are the names of the **livery directories**. Check out the folder in your DCS installation for: --- --- * Full modules: `DCS World OpenBeta\CoreMods\aircraft\\Liveries\\` --- * AI units: `DCS World OpenBeta\Bazar\Liveries\\` --- --- The folder name `` is the string you want. --- --- Or personal liveries you have installed somewhere in your saved games folder. --- --- @param #SQUADRON self --- @param #string LiveryName Name of the livery. --- @return #SQUADRON self -function SQUADRON:SetLivery(LiveryName) - self.livery=LiveryName - return self -end - ---- Set skill level of all squadron team members. --- @param #SQUADRON self --- @param #string Skill Skill of all flights. --- @usage mysquadron:SetSkill(AI.Skill.EXCELLENT) --- @return #SQUADRON self -function SQUADRON:SetSkill(Skill) - self.skill=Skill - return self -end - ---- Set verbosity level. --- @param #SQUADRON self --- @param #number VerbosityLevel Level of output (higher=more). Default 0. --- @return #SQUADRON self -function SQUADRON:SetVerbosity(VerbosityLevel) - self.verbose=VerbosityLevel or 0 - return self -end - ---- Set turnover and repair time. If an asset returns from a mission to the airwing, it will need some time until the asset is available for further missions. --- @param #SQUADRON self --- @param #number MaintenanceTime Time in minutes it takes until a flight is combat ready again. Default is 0 min. --- @param #number RepairTime Time in minutes it takes to repair a flight for each life point taken. Default is 0 min. --- @return #SQUADRON self -function SQUADRON:SetTurnoverTime(MaintenanceTime, RepairTime) - self.maintenancetime=MaintenanceTime and MaintenanceTime*60 or 0 - self.repairtime=RepairTime and RepairTime*60 or 0 - return self -end - ---- Set radio frequency and modulation the squad uses. --- @param #SQUADRON self --- @param #number Frequency Radio frequency in MHz. Default 251 MHz. --- @param #number Modulation Radio modulation. Default 0=AM. --- @usage mysquadron:SetSkill(AI.Skill.EXCELLENT) --- @return #SQUADRON self -function SQUADRON:SetRadio(Frequency, Modulation) - self.radioFreq=Frequency or 251 - self.radioModu=Modulation or radio.modulation.AM - return self -end - --- Set number of units in groups. -- @param #SQUADRON self -- @param #number nunits Number of units. Must be >=1 and <=4. Default 2. @@ -330,115 +180,6 @@ function SQUADRON:SetTakeoffHot() return self end - ---- Set mission types this squadron is able to perform. --- @param #SQUADRON self --- @param #table MissionTypes Table of mission types. Can also be passed as a #string if only one type. --- @param #number Performance Performance describing how good this mission can be performed. Higher is better. Default 50. Max 100. --- @return #SQUADRON self -function SQUADRON:AddMissionCapability(MissionTypes, Performance) - - -- Ensure Missiontypes is a table. - if MissionTypes and type(MissionTypes)~="table" then - MissionTypes={MissionTypes} - end - - -- Set table. - self.missiontypes=self.missiontypes or {} - - for _,missiontype in pairs(MissionTypes) do - - -- Check not to add the same twice. - if self:CheckMissionCapability(missiontype, self.missiontypes) then - self:E(self.lid.."WARNING: Mission capability already present! No need to add it twice.") - -- TODO: update performance. - else - - local capability={} --Ops.Auftrag#AUFTRAG.Capability - capability.MissionType=missiontype - capability.Performance=Performance or 50 - table.insert(self.missiontypes, capability) - - end - end - - -- Debug info. - self:T2(self.missiontypes) - - return self -end - ---- Get mission types this squadron is able to perform. --- @param #SQUADRON self --- @return #table Table of mission types. Could be empty {}. -function SQUADRON:GetMissionTypes() - - local missiontypes={} - - for _,Capability in pairs(self.missiontypes) do - local capability=Capability --Ops.Auftrag#AUFTRAG.Capability - table.insert(missiontypes, capability.MissionType) - end - - return missiontypes -end - ---- Get mission capabilities of this squadron. --- @param #SQUADRON self --- @return #table Table of mission capabilities. -function SQUADRON:GetMissionCapabilities() - return self.missiontypes -end - ---- Get mission performance for a given type of misson. --- @param #SQUADRON self --- @param #string MissionType Type of mission. --- @return #number Performance or -1. -function SQUADRON:GetMissionPeformance(MissionType) - - for _,Capability in pairs(self.missiontypes) do - local capability=Capability --Ops.Auftrag#AUFTRAG.Capability - if capability.MissionType==MissionType then - return capability.Performance - end - end - - return -1 -end - ---- Set max mission range. Only missions in a circle of this radius around the squadron airbase are executed. --- @param #SQUADRON self --- @param #number Range Range in NM. Default 100 NM. --- @return #SQUADRON self -function SQUADRON:SetMissionRange(Range) - self.engageRange=UTILS.NMToMeters(Range or 100) - return self -end - ---- Set call sign. --- @param #SQUADRON self --- @param #number Callsign Callsign from CALLSIGN.Aircraft, e.g. "Chevy" for CALLSIGN.Aircraft.CHEVY. --- @param #number Index Callsign index, Chevy-**1**. --- @return #SQUADRON self -function SQUADRON:SetCallsign(Callsign, Index) - self.callsignName=Callsign - self.callsignIndex=Index - return self -end - ---- Set modex. --- @param #SQUADRON self --- @param #number Modex A number like 100. --- @param #string Prefix A prefix string, which is put before the `Modex` number. --- @param #string Suffix A suffix string, which is put after the `Modex` number. --- @return #SQUADRON self -function SQUADRON:SetModex(Modex, Prefix, Suffix) - self.modex=Modex - self.modexPrefix=Prefix - self.modexSuffix=Suffix - return self -end - --- Set low fuel threshold. -- @param #SQUADRON self -- @param #number LowFuel Low fuel threshold in percent. Default 25. @@ -466,208 +207,17 @@ end -- @param Ops.AirWing#AIRWING Airwing The airwing. -- @return #SQUADRON self function SQUADRON:SetAirwing(Airwing) - self.airwing=Airwing + self.legion=Airwing return self end ---- Add airwing asset to squadron. +--- Get airwing. -- @param #SQUADRON self --- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset. --- @return #SQUADRON self -function SQUADRON:AddAsset(Asset) - self:T(self.lid..string.format("Adding asset %s of type %s", Asset.spawngroupname, Asset.unittype)) - Asset.squadname=self.name - table.insert(self.assets, Asset) - return self +-- @return Ops.AirWing#AIRWING The airwing. +function SQUADRON:GetAirwing(Airwing) + return self.legion end ---- Remove airwing asset from squadron. --- @param #SQUADRON self --- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset. --- @return #SQUADRON self -function SQUADRON:DelAsset(Asset) - for i,_asset in pairs(self.assets) do - local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset - if Asset.uid==asset.uid then - self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname)) - table.remove(self.assets, i) - break - end - end - return self -end - ---- Remove airwing asset group from squadron. --- @param #SQUADRON self --- @param #string GroupName Name of the asset group. --- @return #SQUADRON self -function SQUADRON:DelGroup(GroupName) - for i,_asset in pairs(self.assets) do - local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset - if GroupName==asset.spawngroupname then - self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname)) - table.remove(self.assets, i) - break - end - end - return self -end - ---- Get name of the squadron --- @param #SQUADRON self --- @return #string Name of the squadron. -function SQUADRON:GetName() - return self.name -end - ---- Get radio frequency and modulation. --- @param #SQUADRON self --- @return #number Radio frequency in MHz. --- @return #number Radio Modulation (0=AM, 1=FM). -function SQUADRON:GetRadio() - return self.radioFreq, self.radioModu -end - ---- Create a callsign for the asset. --- @param #SQUADRON self --- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset. --- @return #SQUADRON self -function SQUADRON:GetCallsign(Asset) - - if self.callsignName then - - Asset.callsign={} - - for i=1,Asset.nunits do - - local callsign={} - - callsign[1]=self.callsignName - callsign[2]=math.floor(self.callsigncounter / 10) - callsign[3]=self.callsigncounter % 10 - if callsign[3]==0 then - callsign[3]=1 - self.callsigncounter=self.callsigncounter+2 - else - self.callsigncounter=self.callsigncounter+1 - end - - Asset.callsign[i]=callsign - - self:T3({callsign=callsign}) - - --TODO: there is also a table entry .name, which is a string. - end - - - end - -end - ---- Create a modex for the asset. --- @param #SQUADRON self --- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset. --- @return #SQUADRON self -function SQUADRON:GetModex(Asset) - - if self.modex then - - Asset.modex={} - - for i=1,Asset.nunits do - - Asset.modex[i]=string.format("%03d", self.modex+self.modexcounter) - - self.modexcounter=self.modexcounter+1 - - self:T3({modex=Asset.modex[i]}) - - end - - end - -end - - ---- Add TACAN channels to the squadron. Note that channels can only range from 1 to 126. --- @param #SQUADRON self --- @param #number ChannelMin Channel. --- @param #number ChannelMax Channel. --- @return #SQUADRON self --- @usage mysquad:AddTacanChannel(64,69) -- adds channels 64, 65, 66, 67, 68, 69 -function SQUADRON:AddTacanChannel(ChannelMin, ChannelMax) - - ChannelMax=ChannelMax or ChannelMin - - if ChannelMin>126 then - self:E(self.lid.."ERROR: TACAN Channel must be <= 126! Will not add to available channels") - return self - end - if ChannelMax>126 then - self:E(self.lid.."WARNING: TACAN Channel must be <= 126! Adjusting ChannelMax to 126") - ChannelMax=126 - end - - for i=ChannelMin,ChannelMax do - self.tacanChannel[i]=true - end - - return self -end - ---- Get an unused TACAN channel. --- @param #SQUADRON self --- @return #number TACAN channel or *nil* if no channel is free. -function SQUADRON:FetchTacan() - - -- Get the smallest free channel if there is one. - local freechannel=nil - for channel,free in pairs(self.tacanChannel) do - if free then - if freechannel==nil or channel=2 and #self.assets>0 then - - local text="" - for j,_asset in pairs(self.assets) do - local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset - - -- Text. - text=text..string.format("\n[%d] %s (%s*%d): ", j, asset.spawngroupname, asset.unittype, asset.nunits) - - if asset.spawned then - - --- - -- Spawned - --- - - -- Mission info. - local mission=self.airwing and self.airwing:GetAssetCurrentMission(asset) or false - if mission then - local distance=asset.flightgroup and UTILS.MetersToNM(mission:GetTargetDistance(asset.flightgroup.group:GetCoordinate())) or 0 - text=text..string.format("Mission %s - %s: Status=%s, Dist=%.1f NM", mission.name, mission.type, mission.status, distance) - else - text=text.."Mission None" - end - - -- Flight status. - text=text..", Flight: " - if asset.flightgroup and asset.flightgroup:IsAlive() then - local status=asset.flightgroup:GetState() - local fuelmin=asset.flightgroup:GetFuelMin() - local fuellow=asset.flightgroup:IsFuelLow() - local fuelcri=asset.flightgroup:IsFuelCritical() - - text=text..string.format("%s Fuel=%d", status, fuelmin) - if fuelcri then - text=text.." (Critical!)" - elseif fuellow then - text=text.." (Low)" - end - - local lifept, lifept0=asset.flightgroup:GetLifePoints() - text=text..string.format(", Life=%d/%d", lifept, lifept0) - - local ammo=asset.flightgroup:GetAmmoTot() - text=text..string.format(", Ammo=%d [G=%d, R=%d, B=%d, M=%d]", ammo.Total,ammo.Guns, ammo.Rockets, ammo.Bombs, ammo.Missiles) - else - text=text.."N/A" - end - - -- Payload info. - local payload=asset.payload and table.concat(self.airwing:GetPayloadMissionTypes(asset.payload), ", ") or "None" - text=text..", Payload={"..payload.."}" - - else - - --- - -- In Stock - --- - - text=text..string.format("In Stock") - - if self:IsRepaired(asset) then - text=text..", Combat Ready" - else - - text=text..string.format(", Repaired in %d sec", self:GetRepairTime(asset)) - - if asset.damage then - text=text..string.format(" (Damage=%.1f)", asset.damage) - end - end - - if asset.Treturned then - local T=timer.getAbsTime()-asset.Treturned - text=text..string.format(", Returned for %d sec", T) - end - - end - end - self:I(self.lid..text) - end - -end - ---- On after "Stop" event. --- @param #SQUADRON self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function SQUADRON:onafterStop(From, Event, To) - - -- Debug info. - self:I(self.lid.."STOPPING Squadron and removing all assets!") - - -- Remove all assets. - for i=#self.assets,1,-1 do - local asset=self.assets[i] - self:DelAsset(asset) - end - - -- Clear call scheduler. - self.CallScheduler:Clear() - -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Check if there is a squadron that can execute a given mission. --- We check the mission type, the refuelling system, engagement range --- @param #SQUADRON self --- @param Ops.Auftrag#AUFTRAG Mission The mission. --- @return #boolean If true, Squadron can do that type of mission. -function SQUADRON:CanMission(Mission) - - local cando=true - - -- On duty?= - if not self:IsOnDuty() then - self:T(self.lid..string.format("Squad in not OnDuty but in state %s. Cannot do mission %s with target %s", self:GetState(), Mission.name, Mission:GetTargetName())) - return false - end - - -- Check mission type. WARNING: This assumes that all assets of the squad can do the same mission types! - if not self:CheckMissionType(Mission.type, self:GetMissionTypes()) then - self:T(self.lid..string.format("INFO: Squad cannot do mission type %s (%s, %s)", Mission.type, Mission.name, Mission:GetTargetName())) - return false - end - - -- Check that tanker mission - if Mission.type==AUFTRAG.Type.TANKER then - - if Mission.refuelSystem and Mission.refuelSystem==self.tankerSystem then - -- Correct refueling system. - else - self:T(self.lid..string.format("INFO: Wrong refueling system requested=%s != %s=available", tostring(Mission.refuelSystem), tostring(self.tankerSystem))) - return false - end - - end - - -- Distance to target. - local TargetDistance=Mission:GetTargetDistance(self.airwing:GetCoordinate()) - - -- Max engage range. - local engagerange=Mission.engageRange and math.max(self.engageRange, Mission.engageRange) or self.engageRange - - -- Set range is valid. Mission engage distance can overrule the squad engage range. - if TargetDistance>engagerange then - self:I(self.lid..string.format("INFO: Squad is not in range. Target dist=%d > %d NM max mission Range", UTILS.MetersToNM(TargetDistance), UTILS.MetersToNM(engagerange))) - return false - end - - return true -end - ---- Count assets in airwing (warehous) stock. --- @param #SQUADRON self --- @param #boolean InStock If true, only assets that are in the warehouse stock/inventory 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. `WAREHOUSE.Attribute.AIR_BOMBER`. --- @return #number Number of assets. -function SQUADRON:CountAssets(InStock, MissionTypes, Attributes) - - local N=0 - for _,_asset in pairs(self.assets) do - local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset - - if MissionTypes==nil or self:CheckMissionCapability(MissionTypes, self.missiontypes) then - if Attributes==nil or self:CheckAttribute(Attributes) then - if asset.spawned then - if not InStock then - N=N+1 --Spawned but we also count the spawned ones. - end - else - N=N+1 --This is in stock. - end - end - end - end - - return N -end - ---- Get assets for a mission. --- @param #SQUADRON self --- @param Ops.Auftrag#AUFTRAG Mission The mission. --- @param #number Nplayloads Number of payloads available. --- @return #table Assets that can do the required mission. -function SQUADRON:RecruitAssets(Mission, Npayloads) - - -- Number of payloads available. - Npayloads=Npayloads or self.airwing:CountPayloadsInStock(Mission.type, self.aircrafttype, Mission.payloads) - - local assets={} - - -- Loop over assets. - for _,_asset in pairs(self.assets) do - local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset - - - -- Check if asset is currently on a mission (STARTED or QUEUED). - if self.airwing:IsAssetOnMission(asset) then - - --- - -- Asset is already on a mission. - --- - - -- Check if this asset is currently on a GCICAP mission (STARTED or EXECUTING). - if self.airwing:IsAssetOnMission(asset, AUFTRAG.Type.GCICAP) and Mission.type==AUFTRAG.Type.INTERCEPT then - - -- Check if the payload of this asset is compatible with the mission. - -- Note: we do not check the payload as an asset that is on a GCICAP mission should be able to do an INTERCEPT as well! - self:I(self.lid.."Adding asset on GCICAP mission for an INTERCEPT mission") - table.insert(assets, asset) - - end - - else - - --- - -- Asset as NO current mission - --- - - if asset.spawned then - - --- - -- Asset is already SPAWNED (could be uncontrolled on the airfield or inbound after another mission) - --- - - local flightgroup=asset.flightgroup - - -- Firstly, check if it has the right payload. - if self:CheckMissionCapability(Mission.type, asset.payload.capabilities) and flightgroup and flightgroup:IsAlive() then - - -- Assume we are ready and check if any condition tells us we are not. - local combatready=true - - if Mission.type==AUFTRAG.Type.INTERCEPT then - combatready=flightgroup:CanAirToAir() - else - local excludeguns=Mission.type==AUFTRAG.Type.BOMBING or Mission.type==AUFTRAG.Type.BOMBRUNWAY or Mission.type==AUFTRAG.Type.BOMBCARPET or Mission.type==AUFTRAG.Type.SEAD or Mission.type==AUFTRAG.Type.ANTISHIP - combatready=flightgroup:CanAirToGround(excludeguns) - end - - -- No more attacks if fuel is already low. Safety first! - if flightgroup:IsFuelLow() then - combatready=false - end - - -- Check if in a state where we really do not want to fight any more. - if flightgroup:IsHolding() or flightgroup:IsLanding() or flightgroup:IsLanded() or flightgroup:IsArrived() or flightgroup:IsDead() or flightgroup:IsStopped() then - combatready=false - end - - -- This asset is "combatready". - if combatready then - self:I(self.lid.."Adding SPAWNED asset to ANOTHER mission as it is COMBATREADY") - table.insert(assets, asset) - end - - end - - else - - --- - -- Asset is still in STOCK - --- - - -- Check that asset is not already requested for another mission. - if Npayloads>0 and self:IsRepaired(asset) and (not asset.requested) then - - -- Add this asset to the selection. - table.insert(assets, asset) - - -- Reduce number of payloads so we only return the number of assets that could do the job. - Npayloads=Npayloads-1 - - end - - end - end - end -- loop over assets - - return assets -end - - ---- Get the time an asset needs to be repaired. --- @param #SQUADRON self --- @param Ops.AirWing#AIRWING.SquadronAsset Asset The asset. --- @return #number Time in seconds until asset is repaired. -function SQUADRON:GetRepairTime(Asset) - - if Asset.Treturned then - - local t=self.maintenancetime - t=t+Asset.damage*self.repairtime - - -- Seconds after returned. - local dt=timer.getAbsTime()-Asset.Treturned - - local T=t-dt - - return T - else - return 0 - end - -end - ---- Checks if a mission type is contained in a table of possible types. --- @param #SQUADRON self --- @param Ops.AirWing#AIRWING.SquadronAsset Asset The asset. --- @return #boolean If true, the requested mission type is part of the possible mission types. -function SQUADRON:IsRepaired(Asset) - - if Asset.Treturned then - local Tnow=timer.getAbsTime() - local Trepaired=Asset.Treturned+self.maintenancetime - if Tnow>=Trepaired then - return true - else - return false - end - - else - return true - end - -end - - ---- Checks if a mission type is contained in a table of possible types. --- @param #SQUADRON self --- @param #string MissionType The requested mission type. --- @param #table PossibleTypes A table with possible mission types. --- @return #boolean If true, the requested mission type is part of the possible mission types. -function SQUADRON:CheckMissionType(MissionType, PossibleTypes) - - if type(PossibleTypes)=="string" then - PossibleTypes={PossibleTypes} - end - - for _,canmission in pairs(PossibleTypes) do - if canmission==MissionType then - return true - end - end - - return false -end - ---- Check if a mission type is contained in a list of possible capabilities. --- @param #SQUADRON self --- @param #table MissionTypes The requested mission type. Can also be passed as a single mission type `#string`. --- @param #table Capabilities A table with possible capabilities. --- @return #boolean If true, the requested mission type is part of the possible mission types. -function SQUADRON:CheckMissionCapability(MissionTypes, Capabilities) - - if type(MissionTypes)~="table" then - MissionTypes={MissionTypes} - end - - for _,cap in pairs(Capabilities) do - local capability=cap --Ops.Auftrag#AUFTRAG.Capability - for _,MissionType in pairs(MissionTypes) do - if capability.MissionType==MissionType then - return true - end - end - end - - return false -end - ---- Check if the squadron attribute matches the given attribute(s). --- @param #SQUADRON self --- @param #table Attributes The requested attributes. See `WAREHOUSE.Attribute` enum. Can also be passed as a single attribute `#string`. --- @return #boolean If true, the squad has the requested attribute. -function SQUADRON:CheckAttribute(Attributes) - - if type(Attributes)~="table" then - Attributes={Attributes} - end - - for _,attribute in pairs(Attributes) do - if attribute==self.attribute then - return true - end - end - - return false -end - - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------