diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 44e87e638..f4a7bf747 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -815,7 +815,7 @@ function AIRWING:onafterStatus(From, Event, To) local mission=_mission --Ops.Auftrag#AUFTRAG local prio=string.format("%d/%s", mission.prio, tostring(mission.importance)) ; if mission.urgent then prio=prio.." (!)" end - local assets=string.format("%d/%d", mission:CountOpsGroups(), mission.nassets) + local assets=string.format("%d/%d", mission:CountOpsGroups(), mission:GetNumberOfRequiredAssets()) local target=string.format("%d/%d Damage=%.1f", mission:CountMissionTargets(), mission:GetTargetInitialNumber(), mission:GetTargetDamage()) local mystatus=mission:GetLegionStatus(self) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 4b7030154..7b8a2b7d1 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -32,6 +32,7 @@ -- @field #string lid Class id string for output to DCS log file. -- @field #number auftragsnummer Auftragsnummer. -- @field #string type Mission type. +-- @field #table categories Mission categories. -- @field #string status Mission status. -- @field #table legions Assigned legions. -- @field #table statusLegion Mission status of all assigned LEGIONs. @@ -95,15 +96,23 @@ -- @field #number artyRadius Radius in meters. -- @field #number artyShots Number of shots fired. -- +-- @field #string alert5MissionType Alert 5 mission type. This is the mission type, the alerted assets will be able to carry out. +-- -- @field Ops.Chief#CHIEF chief The CHIEF managing this mission. -- @field Ops.Commander#COMMANDER commander The COMMANDER managing this mission. -- @field #table assets Warehouse assets assigned for this mission. --- @field #number nassets Number of required warehouse assets. --- @field #table Nassets Number of required warehouse assets for each assigned legion. +-- @field #number NassetsMin Min. number of required warehouse assets. +-- @field #number NassetsMax Max. number of required warehouse assets. +-- @field #number NescortMin Min. number of required escort assets for each group the mission is assigned to. +-- @field #number NescortMax Max. number of required escort assets for each group the mission is assigned to. +-- @field #number Nassets Number of requested warehouse assets. +-- @field #table NassetsLegMin Number of required warehouse assets for each assigned legion. +-- @field #table NassetsLegMax Number of required warehouse assets for each assigned legion. -- @field #table requestID The ID of the queued warehouse request. Necessary to cancel the request if the mission was cancelled before the request is processed. +-- @field #table specialLegions User specified legions assigned for this mission. Only these will be considered for the job! +-- @field #table specialCohorts User specified cohorts assigned for this mission. Only these will be considered for the job! -- @field #table squadrons User specified airwing squadrons assigned for this mission. Only these will be considered for the job! -- @field #table payloads User specified airwing payloads for this mission. Only these will be considered for the job! --- @field #table mylegions User specified legions for this mission. Only these will be considered for the job! -- @field Ops.AirWing#AIRWING.PatrolData patroldata Patrol data. -- -- @field #string missionTask Mission task. See `ENUMS.MissionTask`. @@ -244,9 +253,11 @@ -- -- An arty mission can be created with the @{#AUFTRAG.NewARTY}() function. -- +-- -- # Options and Parameters -- -- +-- -- # Assigning Missions -- -- An AUFTRAG can be assigned to groups, airwings or wingcommanders @@ -261,13 +272,19 @@ -- -- Assigning an AUFTRAG to a navy groups is done via the @{Ops.NavyGroup#NAVYGROUP.AddMission} function. See NAVYGROUP docs for details. -- --- ## Airwing Level +-- ## Legion Level -- -- Adding an AUFTRAG to an airwing is done via the @{Ops.AirWing#AIRWING.AddMission} function. See AIRWING docs for further details. +-- Similarly, an AUFTRAG can be added to a brigade via the @{Ops.Brigade#BRIGADE.AddMission} function -- --- ## Wing Commander Level +-- ## Commander Level -- --- Assigning an AUFTRAG to a wing commander is done via the @{Ops.WingCommander#WINGCOMMANDER.AddMission} function. See WINGCOMMADER docs for details. +-- Assigning an AUFTRAG to acommander is done via the @{Ops.Commander#COMMANDER.AddMission} function. See COMMADER docs for details. +-- +-- ## Chief Level +-- +-- Assigning an AUFTRAG to a wing commander is done via the @{Ops.Chief#CHIEF.AddMission} function. See CHIEF docs for details. +-- -- -- -- # Events @@ -289,7 +306,8 @@ AUFTRAG = { statusLegion = {}, requestID = {}, assets = {}, - Nassets = {}, + NassetsLegMin = {}, + NassetsLegMax = {}, missionFraction = 0.5, enrouteTasks = {}, marker = nil, @@ -444,6 +462,22 @@ AUFTRAG.TargetType={ SETUNIT="SetUnit", } +--- Mission category. +-- @type AUFTRAG.Category +-- @field #string AIRCRAFT Airplanes and helicopters. +-- @field #string AIRPLANE Airplanes. +-- @field #string HELICOPTER Helicopter. +-- @field #string GROUND Ground troops. +-- @field #string NAVAL Naval grous. +AUFTRAG.Category={ + ALL="All", + AIRCRAFT="Aircraft", + AIRPLANE="Airplane", + HELICOPTER="Helicopter", + GROUND="Ground", + NAVAL="Naval", +} + --- Target data. -- @type AUFTRAG.TargetData -- @field Wrapper.Positionable#POSITIONABLE Target Target Object. @@ -483,7 +517,7 @@ AUFTRAG.TargetType={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="0.8.0" +AUFTRAG.version="0.8.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -493,6 +527,7 @@ AUFTRAG.version="0.8.0" -- TODO: Mission success options damaged, destroyed. -- TODO: F10 marker to create new missions. -- TODO: Add recovery tanker mission for boat ops. +-- DONE: Added auftrag category. -- DONE: Missions can be assigned to multiple legions. -- DONE: Option to assign a specific payload for the mission (requires an AIRWING). -- NOPE: Clone mission. How? Deepcopy? ==> Create a new auftrag. @@ -621,6 +656,8 @@ function AUFTRAG:NewANTISHIP(Target, Altitude) mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire + mission.categories={AUFTRAG.Category.AIRCRAFT} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -662,6 +699,8 @@ function AUFTRAG:NewORBIT(Coordinate, Altitude, Speed, Heading, Leg) mission.missionFraction=0.9 mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.AIRCRAFT} mission.DCStask=mission:GetDCSMissionTask() @@ -722,6 +761,8 @@ function AUFTRAG:NewGCICAP(Coordinate, Altitude, Speed, Heading, Leg) mission.missionTask=ENUMS.MissionTask.INTERCEPT mission.optionROT=ENUMS.ROT.PassiveDefense + mission.categories={AUFTRAG.Category.AIRCRAFT} + return mission end @@ -751,6 +792,8 @@ function AUFTRAG:NewTANKER(Coordinate, Altitude, Speed, Heading, Leg, RefuelSyst mission.optionROE=ENUMS.ROE.WeaponHold mission.optionROT=ENUMS.ROT.PassiveDefense + mission.categories={AUFTRAG.Category.AIRCRAFT} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -779,6 +822,8 @@ function AUFTRAG:NewAWACS(Coordinate, Altitude, Speed, Heading, Leg) mission.optionROE=ENUMS.ROE.WeaponHold mission.optionROT=ENUMS.ROT.PassiveDefense + mission.categories={AUFTRAG.Category.AIRCRAFT} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -802,6 +847,8 @@ function AUFTRAG:NewINTERCEPT(Target) mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire + mission.categories={AUFTRAG.Category.AIRCRAFT} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -842,6 +889,8 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire + mission.categories={AUFTRAG.Category.AIRCRAFT} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -881,6 +930,8 @@ function AUFTRAG:NewCAS(ZoneCAS, Altitude, Speed, Coordinate, Heading, Leg, Targ mission.missionTask=ENUMS.MissionTask.CAS mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire + + mission.categories={AUFTRAG.Category.AIRCRAFT} mission.DCStask=mission:GetDCSMissionTask() @@ -915,6 +966,8 @@ function AUFTRAG:NewFACA(Target, Designation, DataLink, Frequency, Modulation) mission.missionFraction=0.5 mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.AIRCRAFT} mission.DCStask=mission:GetDCSMissionTask() @@ -945,6 +998,8 @@ function AUFTRAG:NewBAI(Target, Altitude) mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.PassiveDefense + mission.categories={AUFTRAG.Category.AIRCRAFT} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -974,6 +1029,8 @@ function AUFTRAG:NewSEAD(Target, Altitude) mission.optionROT=ENUMS.ROT.EvadeFire --mission.optionROT=ENUMS.ROT.AllowAbortMission + mission.categories={AUFTRAG.Category.AIRCRAFT} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1002,6 +1059,8 @@ function AUFTRAG:NewSTRIKE(Target, Altitude) mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.PassiveDefense + mission.categories={AUFTRAG.Category.AIRCRAFT} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1033,6 +1092,8 @@ function AUFTRAG:NewBOMBING(Target, Altitude) -- Evaluate result after 5 min. We might need time until the bombs have dropped and targets have been detroyed. mission.dTevaluate=5*60 + mission.categories={AUFTRAG.Category.AIRCRAFT} + -- Get DCS task. mission.DCStask=mission:GetDCSMissionTask() @@ -1069,6 +1130,8 @@ function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude) -- Evaluate result after 5 min. mission.dTevaluate=5*60 + mission.categories={AUFTRAG.Category.AIRCRAFT} + -- Get DCS task. mission.DCStask=mission:GetDCSMissionTask() @@ -1105,6 +1168,8 @@ function AUFTRAG:NewBOMBCARPET(Target, Altitude, CarpetLength) -- Evaluate result after 5 min. mission.dTevaluate=5*60 + mission.categories={AUFTRAG.Category.AIRCRAFT} + -- Get DCS task. mission.DCStask=mission:GetDCSMissionTask() @@ -1123,7 +1188,13 @@ function AUFTRAG:NewESCORT(EscortGroup, OffsetVector, EngageMaxDistance, TargetT local mission=AUFTRAG:New(AUFTRAG.Type.ESCORT) - mission:_TargetFromObject(EscortGroup) + -- If only a string is passed we set a variable and check later if the group exists. + if type(EscortGroup)=="string" then + mission.escortGroupName=EscortGroup + mission:_TargetFromObject() + else + mission:_TargetFromObject(EscortGroup) + end -- DCS task parameters: mission.escortVec3=OffsetVector or {x=-100, y=0, z=200} @@ -1137,6 +1208,8 @@ function AUFTRAG:NewESCORT(EscortGroup, OffsetVector, EngageMaxDistance, TargetT mission.optionROE=ENUMS.ROE.OpenFire -- TODO: what's the best ROE here? Make dependent on ESCORT or FOLLOW! mission.optionROT=ENUMS.ROT.PassiveDefense + mission.categories={AUFTRAG.Category.AIRCRAFT} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1158,13 +1231,15 @@ function AUFTRAG:NewRESCUEHELO(Carrier) mission.optionROE=ENUMS.ROE.WeaponHold mission.optionROT=ENUMS.ROT.NoReaction + mission.categories={AUFTRAG.Category.HELICOPTER} + mission.DCStask=mission:GetDCSMissionTask() return mission end ---- **[AIR ROTARY]** Create a TROOP TRANSPORT mission. +--- **[AIR ROTARY, GROUND]** Create a TROOP TRANSPORT mission. -- @param #AUFTRAG self -- @param Core.Set#SET_GROUP TransportGroupSet The set group(s) to be transported. -- @param Core.Point#COORDINATE DropoffCoordinate Coordinate where the helo will land drop off the the troops. @@ -1197,6 +1272,8 @@ function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupC mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROT=ENUMS.ROT.PassiveDefense + mission.categories={AUFTRAG.Category.HELICOPTER, AUFTRAG.Category.GROUND} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1231,6 +1308,8 @@ function AUFTRAG:NewOPSTRANSPORT(CargoGroupSet, PickupZone, DeployZone) mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROT=ENUMS.ROT.PassiveDefense + mission.categories={AUFTRAG.Category.ALL} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1262,6 +1341,8 @@ function AUFTRAG:NewARTY(Target, Nshots, Radius) -- Evaluate after 8 min. mission.dTevaluate=8*60 + mission.categories={AUFTRAG.Category.GROUND} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1292,6 +1373,8 @@ function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude) mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude) or nil + mission.categories={AUFTRAG.Category.ALL} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1318,6 +1401,8 @@ function AUFTRAG:NewRECON(ZoneSet, Speed, Altitude, Adinfinitum, Randomly) mission.missionFraction=0.5 mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude) or UTILS.FeetToMeters(2000) + + mission.categories={AUFTRAG.Category.ALL} mission.DCStask=mission:GetDCSMissionTask() mission.DCStask.params.adinfitum=Adinfinitum @@ -1340,6 +1425,8 @@ function AUFTRAG:NewAMMOSUPPLY(Zone) mission.optionAlarm=ENUMS.AlarmState.Auto mission.missionFraction=0.9 + + mission.categories={AUFTRAG.Category.GROUND} mission.DCStask=mission:GetDCSMissionTask() @@ -1360,6 +1447,8 @@ function AUFTRAG:NewFUELSUPPLY(Zone) mission.optionAlarm=ENUMS.AlarmState.Auto mission.missionFraction=0.9 + + mission.categories={AUFTRAG.Category.GROUND} mission.DCStask=mission:GetDCSMissionTask() @@ -1383,6 +1472,8 @@ function AUFTRAG:NewALERT5(MissionType) mission.alert5MissionType=MissionType mission.missionFraction=1.0 + + mission.categories={AUFTRAG.Category.AIRCRAFT} mission.DCStask=mission:GetDCSMissionTask() @@ -1686,28 +1777,58 @@ function AUFTRAG:SetRepeatOnSuccess(Nrepeat) return self end ---- Define how many assets are required to do the job. Only valid if the mission is handled by a LEGION (AIRWING, BRIGADE, ...) or higher level. +--- Define how many assets are required to do the job. Only used if the mission is handled by a **LEGION** (AIRWING, BRIGADE, ...) or higher level. -- @param #AUFTRAG self --- @param #number Nassets Number of asset groups. Default 1. +-- @param #number NassetsMin Minimum number of asset groups. Default 1. +-- @param #number NassetsMax Maximum Number of asset groups. Default is same as `NassetsMin`. -- @return #AUFTRAG self -function AUFTRAG:SetRequiredAssets(Nassets) - self.nassets=Nassets or 1 +function AUFTRAG:SetRequiredAssets(NassetsMin, NassetsMax) + + self.NassetsMin=NassetsMin or 1 + + self.NassetsMax=NassetsMax or self.NassetsMin + + -- Ensure that max is at least equal to min. + if self.NassetsMax0) then self:E(self.lid.."ERROR: Mission can only be repeated by a CHIEF, COMMANDER or LEGION! Stopping AUFTRAG") self:Stop() return false @@ -3498,7 +3702,7 @@ function AUFTRAG:_TargetFromObject(Object) if not self.engageTarget then - if Object:IsInstanceOf("TARGET") then + if Object and Object:IsInstanceOf("TARGET") then self.engageTarget=Object @@ -3513,7 +3717,7 @@ function AUFTRAG:_TargetFromObject(Object) -- Target was already specified elsewhere. end - + -- Debug info. --self:T2(self.lid..string.format("Mission Target %s Type=%s, Ntargets=%d, Lifepoints=%d", self.engageTarget.lid, self.engageTarget.lid, self.engageTarget.N0, self.engageTarget:GetLife())) @@ -3848,7 +4052,7 @@ function AUFTRAG:UpdateMarker() local text=string.format("%s %s: %s", self.name, self.type:upper(), self.status:upper()) text=text..string.format("\n%s", self:GetTargetName()) text=text..string.format("\nTargets %d/%d, Life Points=%d/%d", self:CountMissionTargets(), self:GetTargetInitialNumber(), self:GetTargetLife(), self:GetTargetInitialLife()) - text=text..string.format("\nFlights %d/%d", self:CountOpsGroups(), self.nassets) + text=text..string.format("\nOpsGroups %d/%d", self:CountOpsGroups(), self.nassets) if not self.marker then diff --git a/Moose Development/Moose/Ops/Commander.lua b/Moose Development/Moose/Ops/Commander.lua index 055888f08..e0c23bfa0 100644 --- a/Moose Development/Moose/Ops/Commander.lua +++ b/Moose Development/Moose/Ops/Commander.lua @@ -692,19 +692,30 @@ function COMMANDER:CheckMissionQueue() local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem asset.isReserved=true mission:AddAsset(asset) - end - - -- Assign mission to legion(s). - for _,_legion in pairs(legions) do - local legion=_legion --Ops.Legion#LEGION - - -- Debug message. - self:I(self.lid..string.format("Assigning mission %s [%s] to legion %s", mission:GetName(), mission:GetType(), legion.alias)) - - -- Add mission to legion. - self:MissionAssign(legion, mission) - end + + -- Recruit asset for escorting recruited mission assets. + local EscortAvail=self:RecruitAssetsForEscort(mission, assets) + + + if EscortAvail then + + -- Assign mission to legion(s). + for _,_legion in pairs(legions) do + local legion=_legion --Ops.Legion#LEGION + + -- Debug message. + self:I(self.lid..string.format("Assigning mission %s [%s] to legion %s", mission:GetName(), mission:GetType(), legion.alias)) + + -- Add mission to legion. + self:MissionAssign(legion, mission) + + end + + else + -- Recruited assets but no requested escort available. Unrecruit assets! + LEGION.UnRecruitAssets(assets, mission) + end -- Only ONE mission is assigned. return @@ -745,8 +756,7 @@ function COMMANDER:RecruitAssetsForMission(Mission) end -- Number of required assets. - local NreqMin=Mission:GetRequiredAssets() - local NreqMax=NreqMin + local NreqMin, NreqMax=Mission:GetRequiredAssets() -- Target position. local TargetVec2=Mission:GetTargetVec2() @@ -760,6 +770,111 @@ function COMMANDER:RecruitAssetsForMission(Mission) return recruited, assets, legions end +--- Recruit assets performing an escort mission for a given asset. +-- @param #COMMANDER 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 COMMANDER:RecruitAssetsForEscort(Mission, Assets) + + -- Cohorts. + local Cohorts=Mission.squadrons + if not Cohorts then + Cohorts={} + for _,_legion in pairs(Mission.mylegions or self.legions) do + local legion=_legion --Ops.Legion#LEGION + -- Loops over cohorts. + for _,_cohort in pairs(legion.cohorts) do + local cohort=_cohort --Ops.Cohort#COHORT + table.insert(Cohorts, cohort) + end + end + end + + -- 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:I(self.lid..string.format("Reqested escort for mission %s [%s]. Required assets=%d-%d", Mission:GetName(), Mission:GetType(), Mission.NescortMin,Mission.NescortMax)) + + -- Escorts for each asset. + local Escorts={} + + local EscortAvail=true + for _,_asset in pairs(Assets) do + local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem + + -- Recruit escort asset for the mission asset. + local Erecruited, eassets, elegions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.ESCORT, nil, Mission.NescortMin, Mission.NescortMax) + + if Erecruited then + Escorts[asset.spawngroupname]={EscortLegions=elegions, EscortAssets=eassets} + 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 + + for _,_legion in pairs(Elegions) do + local legion=_legion --Ops.Legion#LEGION + + -- Create and ESCORT mission for this asset. + local escort=AUFTRAG:NewESCORT(groupname) + + -- Reserve assts and add to mission. + for _,_asset in pairs(Eassets) do + local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem + asset.isReserved=true + escort:AddAsset(asset) + N=N+1 + end + + -- Add mission. + legion:AddMission(escort) + + -- Request mission. + legion:MissionRequest(escort) + + end + end + + -- Debug info. + self:I(self.lid..string.format("Recruited %d escort assets for mission %s [%s]", N, Mission:GetName(), Mission:GetType())) + + -- Yup! + return true + else + + -- Debug info. + self:I(self.lid..string.format("Could not get at least one escort for mission %s [%s]! Unrecruit all recruited assets", Mission:GetName(), Mission:GetType())) + + -- 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. + return true + end + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Transport Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 66cf48d0e..f42d19909 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -473,17 +473,30 @@ function LEGION:_GetNextMission() -- Recruit best assets for the job. local recruited, assets, legions=self:RecruitAssetsForMission(mission) - + -- Did we find enough assets? if recruited then + + -- Reserve assets and add to mission. for _,_asset in pairs(assets) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem asset.isReserved=true mission:AddAsset(asset) end - - return mission - end + + -- Recruit asset for escorting recruited mission assets. + local EscortAvail=self:RecruitAssetsForEscort(mission, assets) + + -- Is escort required and available? + if EscortAvail then + -- Got a missin. + return mission + 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 @@ -1627,8 +1640,7 @@ end function LEGION:RecruitAssetsForMission(Mission) -- Get required assets. - local NreqMin=Mission:GetRequiredAssets() - local NreqMax=NreqMin + local NreqMin, NreqMax=Mission:GetRequiredAssets() -- Target position vector. local TargetVec2=Mission:GetTargetVec2() @@ -1686,6 +1698,97 @@ function LEGION:RecruitAssetsForTransport(Transport) 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:I(self.lid..string.format("Reqested escort for mission %s [%s]. Required assets=%d-%d", Mission:GetName(), Mission:GetType(), Mission.NescortMin,Mission.NescortMax)) + + -- Escorts for each asset. + local Escorts={} + + local EscortAvail=true + for _,_asset in pairs(Assets) do + local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem + + -- Recruit escort asset for the mission asset. + local Erecruited, eassets, elegions=LEGION.RecruitCohortAssets(self.cohorts, AUFTRAG.Type.ESCORT, nil, Mission.NescortMin, Mission.NescortMax) + + if Erecruited then + Escorts[asset.spawngroupname]={EscortLegions=elegions, EscortAssets=eassets} + 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 + + for _,_legion in pairs(Elegions) do + local legion=_legion --Ops.Legion#LEGION + + -- Create and ESCORT mission for this asset. + local escort=AUFTRAG:NewESCORT(groupname) + + -- Reserve assts and add to mission. + for _,_asset in pairs(Eassets) do + local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem + asset.isReserved=true + escort:AddAsset(asset) + N=N+1 + end + + -- Add mission. + legion:AddMission(escort) + + -- Request mission. + legion:MissionRequest(escort) + + end + end + + -- Debug info. + self:I(self.lid..string.format("Recruited %d escort assets for mission %s [%s]", N, Mission:GetName(), Mission:GetType())) + + -- Yup! + return true + else + + -- Debug info. + self:I(self.lid..string.format("Could not get at least one escort for mission %s [%s]! Unrecruit all recruited assets", Mission:GetName(), Mission:GetType())) + + -- 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. + return true + end + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Recruiting and Optimization Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1827,6 +1930,30 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, 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 + + --- Calculate the mission score of an asset. -- @param Functional.Warehouse#WAREHOUSE.Assetitem asset Asset diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index ee5501cf9..133bcc167 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -3763,9 +3763,6 @@ function OPSGROUP:_GetNextMission() end table.sort(self.missionqueue, _sort) - -- Current time. - local time=timer.getAbsTime() - -- Look for first mission that is SCHEDULED. local vip=math.huge for _,_mission in pairs(self.missionqueue) do @@ -3794,9 +3791,23 @@ function OPSGROUP:_GetNextMission() -- TODO: One could think of opsgroup specific start conditions. A legion also checks if "ready" but it can be other criteria for the group to actually start the mission. -- Good example is the above transport. The legion should start the mission but the group should only start after the transport is finished. + + -- Escort mission. Check that escorted group is alive. + local isEscort=true + if mission.type==AUFTRAG.Type.ESCORT then + local target=mission:GetTargetData() + if not target:IsAlive() then + isEscort=false + end + end + + local isScheduled=mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.SCHEDULED + local isReadyToGo=(mission:IsReadyToGo() or self.legion) + local isImportant=(mission.importance==nil or mission.importance<=vip) + local isTransport=transport -- Check necessary conditions. - if mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.SCHEDULED and (mission:IsReadyToGo() or self.legion) and (mission.importance==nil or mission.importance<=vip) and transport then + if isScheduled and isReadyToGo and isImportant and isTransport and isEscort then return mission end diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 01ec67d9f..32b9eafd1 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -130,13 +130,14 @@ _TARGETID=0 --- TARGET class version. -- @field #string version -TARGET.version="0.5.1" +TARGET.version="0.5.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Add pseudo functions. +-- DONE: Initial object can be nil. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -156,30 +157,20 @@ function TARGET:New(TargetObject) -- Set UID. self.uid=_TARGETID + + if TargetObject then - -- Add object. - self:AddObject(TargetObject) - - -- Get first target. - local Target=self.targets[1] --#TARGET.Object - - if not Target then - self:E("ERROR: No valid TARGET!") - return nil + -- Add object. + self:AddObject(TargetObject) + end -- Defaults. self:SetPriority() self:SetImportance() - - -- Target Name. - self.name=self:GetTargetName(Target) - - -- Target category. - self.category=self:GetTargetCategory(Target) -- Log ID. - self.lid=string.format("TARGET #%03d [%s] | ", _TARGETID, tostring(self.category)) + self.lid=string.format("TARGET #%03d | ", _TARGETID) -- Start state. self:SetStartState("Stopped") @@ -317,8 +308,15 @@ end -- @param #TARGET self -- @return #boolean If true, target is alive. function TARGET:IsAlive() - local is=self:Is("Alive") - return is + + for _,_target in pairs(self.targets) do + local target=_target --Ops.Target#TARGET.Object + if target.Status==TARGET.ObjectStatus.ALIVE then + return true + end + end + + return false end --- Check if TARGET is destroyed. @@ -756,6 +754,13 @@ function TARGET:_AddObject(Object) target.Object=Object table.insert(self.targets, target) + + if self.name==nil then + self.name=self:GetTargetName(target) + end + if self.category==nil then + self.category=self:GetTargetCategory(target) + end end @@ -1051,7 +1056,7 @@ end -- @param #TARGET self -- @return #string Name of the target usually the first object. function TARGET:GetName() - return self.name + return self.name or "Unknown" end --- Get 2D vector. diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 5b130ec61..397832e85 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1407,7 +1407,7 @@ function CONTROLLABLE:TaskEscort( FollowControllable, Vec3, LastWaypointIndex, E DCSTask = { id = 'Escort', params = { - groupId = FollowControllable:GetID(), + groupId = FollowControllable and FollowControllable:GetID() or nil, pos = Vec3, lastWptIndexFlag = LastWaypointIndex and true or false, lastWptIndex = LastWaypointIndex,