mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
OPS LEGION
- Improved asset selection for transports
This commit is contained in:
parent
884c51a69a
commit
6a6cb1961d
@ -800,14 +800,11 @@ end
|
|||||||
|
|
||||||
--- Get assets for a mission.
|
--- Get assets for a mission.
|
||||||
-- @param #COHORT self
|
-- @param #COHORT self
|
||||||
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
|
-- @param #string MissionType Mission type.
|
||||||
-- @param #number Npayloads Number of payloads available.
|
-- @param #number Npayloads Number of payloads available.
|
||||||
-- @return #table Assets that can do the required mission.
|
-- @return #table Assets that can do the required mission.
|
||||||
-- @return #number Number of payloads still available after recruiting the assets.
|
-- @return #number Number of payloads still available after recruiting the assets.
|
||||||
function COHORT:RecruitAssets(Mission, Npayloads)
|
function COHORT:RecruitAssets(MissionType, Npayloads)
|
||||||
|
|
||||||
-- Number of payloads available.
|
|
||||||
Npayloads=Npayloads or self.legion:CountPayloadsInStock(Mission.type, self.aircrafttype, Mission.payloads)
|
|
||||||
|
|
||||||
-- Recruited assets.
|
-- Recruited assets.
|
||||||
local assets={}
|
local assets={}
|
||||||
@ -828,14 +825,14 @@ function COHORT:RecruitAssets(Mission, Npayloads)
|
|||||||
---
|
---
|
||||||
|
|
||||||
-- Check if this asset is currently on a GCICAP mission (STARTED or EXECUTING).
|
-- Check if this asset is currently on a GCICAP mission (STARTED or EXECUTING).
|
||||||
if self.legion:IsAssetOnMission(asset, AUFTRAG.Type.GCICAP) and Mission.type==AUFTRAG.Type.INTERCEPT then
|
if self.legion:IsAssetOnMission(asset, AUFTRAG.Type.GCICAP) and MissionType==AUFTRAG.Type.INTERCEPT then
|
||||||
|
|
||||||
-- Check if the payload of this asset is compatible with the mission.
|
-- 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!
|
-- 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")
|
self:I(self.lid.."Adding asset on GCICAP mission for an INTERCEPT mission")
|
||||||
table.insert(assets, asset)
|
table.insert(assets, asset)
|
||||||
|
|
||||||
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.ALERT5) and self:CheckMissionCapability(Mission.Type, asset.payload.capabilities) then
|
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.ALERT5) and self:CheckMissionCapability(MissionType, asset.payload.capabilities) then
|
||||||
|
|
||||||
-- Check if the payload of this asset is compatible with the mission.
|
-- Check if the payload of this asset is compatible with the mission.
|
||||||
self:I(self.lid.."Adding asset on ALERT 5 mission for XXX mission")
|
self:I(self.lid.."Adding asset on ALERT 5 mission for XXX mission")
|
||||||
@ -876,10 +873,10 @@ function COHORT:RecruitAssets(Mission, Npayloads)
|
|||||||
combatready=false
|
combatready=false
|
||||||
end
|
end
|
||||||
|
|
||||||
if Mission.type==AUFTRAG.Type.INTERCEPT and not flightgroup:CanAirToAir() then
|
if MissionType==AUFTRAG.Type.INTERCEPT and not flightgroup:CanAirToAir() then
|
||||||
combatready=false
|
combatready=false
|
||||||
else
|
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
|
local excludeguns=MissionType==AUFTRAG.Type.BOMBING or MissionType==AUFTRAG.Type.BOMBRUNWAY or MissionType==AUFTRAG.Type.BOMBCARPET or MissionType==AUFTRAG.Type.SEAD or MissionType==AUFTRAG.Type.ANTISHIP
|
||||||
if excludeguns and not flightgroup:CanAirToGround(excludeguns) then
|
if excludeguns and not flightgroup:CanAirToGround(excludeguns) then
|
||||||
combatready=false
|
combatready=false
|
||||||
end
|
end
|
||||||
@ -888,7 +885,7 @@ function COHORT:RecruitAssets(Mission, Npayloads)
|
|||||||
if flightgroup:IsHolding() or flightgroup:IsLanding() or flightgroup:IsLanded() or flightgroup:IsArrived() then
|
if flightgroup:IsHolding() or flightgroup:IsLanding() or flightgroup:IsLanded() or flightgroup:IsArrived() then
|
||||||
combatready=false
|
combatready=false
|
||||||
end
|
end
|
||||||
if asset.payload and not self:CheckMissionCapability(Mission.type, asset.payload.capabilities) then
|
if asset.payload and not self:CheckMissionCapability(MissionType, asset.payload.capabilities) then
|
||||||
combatready=false
|
combatready=false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -659,6 +659,8 @@ function COMMANDER:RecruitAssets(Mission)
|
|||||||
-- Legions we consider for selecting assets.
|
-- Legions we consider for selecting assets.
|
||||||
local legions=Mission.mylegions or self.legions
|
local legions=Mission.mylegions or self.legions
|
||||||
|
|
||||||
|
--TODO: Setting of Mission.squadrons (cohorts) will not work here!
|
||||||
|
|
||||||
-- Legions which have the best assets for the Mission.
|
-- Legions which have the best assets for the Mission.
|
||||||
local Legions={}
|
local Legions={}
|
||||||
|
|
||||||
@ -690,7 +692,7 @@ function COMMANDER:RecruitAssets(Mission)
|
|||||||
if cohort:CanMission(Mission) and npayloads>0 then
|
if cohort:CanMission(Mission) and npayloads>0 then
|
||||||
|
|
||||||
-- Recruit assets from squadron.
|
-- Recruit assets from squadron.
|
||||||
local assets, npayloads=cohort:RecruitAssets(Mission, npayloads)
|
local assets, npayloads=cohort:RecruitAssets(Mission.type, npayloads)
|
||||||
|
|
||||||
Npayloads[cohort.aircrafttype]=npayloads
|
Npayloads[cohort.aircrafttype]=npayloads
|
||||||
|
|
||||||
@ -743,6 +745,7 @@ function COMMANDER:RecruitAssets(Mission)
|
|||||||
-- Now find the best asset for the given payloads.
|
-- Now find the best asset for the given payloads.
|
||||||
self:_OptimizeAssetSelection(Assets, Mission, true)
|
self:_OptimizeAssetSelection(Assets, Mission, true)
|
||||||
|
|
||||||
|
-- Get number of required assets.
|
||||||
local Nassets=Mission:GetRequiredAssets(self)
|
local Nassets=Mission:GetRequiredAssets(self)
|
||||||
|
|
||||||
if #Assets>=Nassets then
|
if #Assets>=Nassets then
|
||||||
|
|||||||
@ -451,6 +451,7 @@ function LEGION:_GetNextMission()
|
|||||||
-- Recruit best assets for the job.
|
-- Recruit best assets for the job.
|
||||||
local recruited=self:RecruitAssets(mission)
|
local recruited=self:RecruitAssets(mission)
|
||||||
|
|
||||||
|
-- Did we find enough assets?
|
||||||
if recruited then
|
if recruited then
|
||||||
return mission
|
return mission
|
||||||
end
|
end
|
||||||
@ -474,52 +475,6 @@ function LEGION:_GetNextTransport()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to get carrier assets from all cohorts.
|
|
||||||
local function getAssets(n, N , weightGroup)
|
|
||||||
|
|
||||||
-- Selected assets.
|
|
||||||
local assets={}
|
|
||||||
|
|
||||||
-- Loop over cohorts.
|
|
||||||
for _,_cohort in pairs(self.cohorts) do
|
|
||||||
local cohort=_cohort --Ops.Cohort#COHORT
|
|
||||||
|
|
||||||
-- Check if chort can do a transport.
|
|
||||||
if cohort:CheckMissionCapability({AUFTRAG.Type.OPSTRANSPORT}) and cohort.cargobayLimit>=weightGroup then
|
|
||||||
|
|
||||||
-- Loop over cohort assets.
|
|
||||||
for _,_asset in pairs(cohort.assets) do
|
|
||||||
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
|
||||||
|
|
||||||
-- Check if asset is currently on a mission (STARTED or QUEUED).
|
|
||||||
if not (asset.spawned or asset.isReserved or asset.requested) then
|
|
||||||
|
|
||||||
-- Add to assets.
|
|
||||||
table.insert(assets, asset)
|
|
||||||
|
|
||||||
--TODO: Optimize Asset Selection!
|
|
||||||
|
|
||||||
--TODO: Check if deploy and (any) pickup zone is an airbase, so airplanes can be used.
|
|
||||||
|
|
||||||
-- Max number of assets reached.
|
|
||||||
if #assets==N then
|
|
||||||
return assets
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- At least min number reached?
|
|
||||||
if #assets>=n then
|
|
||||||
return assets
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--TODO: Sort transports wrt to prio and importance. See mission sorting!
|
--TODO: Sort transports wrt to prio and importance. See mission sorting!
|
||||||
|
|
||||||
-- Look for first task that is not accomplished.
|
-- Look for first task that is not accomplished.
|
||||||
@ -529,142 +484,22 @@ function LEGION:_GetNextTransport()
|
|||||||
-- Check if transport is still queued and ready.
|
-- Check if transport is still queued and ready.
|
||||||
if transport:IsQueued(self) and transport:IsReadyToGo() then
|
if transport:IsQueued(self) and transport:IsReadyToGo() then
|
||||||
|
|
||||||
-- Get all undelivered cargo ops groups.
|
-- Recruit assets for transport.
|
||||||
local cargoOpsGroups=transport:GetCargoOpsGroups(false)
|
local recruited=self:RecruitAssetsForTransport(transport)
|
||||||
|
|
||||||
-- At least one group should be spawned.
|
|
||||||
if #cargoOpsGroups>0 then
|
|
||||||
|
|
||||||
-- Calculate the max weight so we know which cohorts can provide carriers.
|
|
||||||
local weightGroup=0
|
|
||||||
for _,_opsgroup in pairs(cargoOpsGroups) do
|
|
||||||
local opsgroup=_opsgroup --Ops.OpsGroup#OPSGROUP
|
|
||||||
local weight=opsgroup:GetWeightTotal()
|
|
||||||
if weight>weightGroup then
|
|
||||||
weightGroup=weight
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get assets. If not enough assets can be found, nil is returned.
|
|
||||||
local assets=getAssets(transport.nCarriersMin, transport.nCarriersMax, weightGroup)
|
|
||||||
|
|
||||||
if assets then
|
|
||||||
for _,_asset in pairs(assets) do
|
|
||||||
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
|
||||||
asset.isReserved=true
|
|
||||||
transport:AddAsset(asset)
|
|
||||||
end
|
|
||||||
return transport
|
|
||||||
end
|
|
||||||
|
|
||||||
|
-- Did we find enough assets?
|
||||||
|
if recruited then
|
||||||
|
return transport
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- No transport found.
|
-- No transport found.
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Calculate the mission score of an asset.
|
|
||||||
-- @param #LEGION 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 LEGION:CalculateAssetMissionScore(asset, Mission, includePayload)
|
|
||||||
|
|
||||||
-- Mission score.
|
|
||||||
local score=0
|
|
||||||
|
|
||||||
-- Prefer highly skilled assets.
|
|
||||||
if asset.skill==AI.Skill.AVERAGE then
|
|
||||||
score=score+0
|
|
||||||
elseif asset.skill==AI.Skill.GOOD then
|
|
||||||
score=score+10
|
|
||||||
elseif asset.skill==AI.Skill.HIGH then
|
|
||||||
score=score+20
|
|
||||||
elseif asset.skill==AI.Skill.EXCELLENT then
|
|
||||||
score=score+30
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add mission performance to score.
|
|
||||||
score=score+asset.cohort:GetMissionPeformance(Mission.Type)
|
|
||||||
|
|
||||||
-- Add payload performance to score.
|
|
||||||
if includePayload and asset.payload then
|
|
||||||
score=score+self:GetPayloadPeformance(asset.payload, Mission.type)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Target position.
|
|
||||||
local TargetVec2=Mission.type~=AUFTRAG.Type.ALERT5 and Mission:GetTargetVec2() or nil --Mission:GetTargetVec2()
|
|
||||||
|
|
||||||
-- Origin: We take the flightgroups position or the one of the legion.
|
|
||||||
local OrigVec2=asset.flightgroup and asset.flightgroup:GetVec2() or self:GetVec2()
|
|
||||||
|
|
||||||
-- Distance factor.
|
|
||||||
local distance=0
|
|
||||||
if TargetVec2 and OrigVec2 then
|
|
||||||
-- Distance in NM.
|
|
||||||
distance=UTILS.MetersToNM(UTILS.VecDist2D(OrigVec2, TargetVec2))
|
|
||||||
-- Round: 55 NM ==> 5.5 ==> 6, 63 NM ==> 6.3 ==> 6
|
|
||||||
distance=UTILS.Round(distance/10, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Reduce score for legions that are futher away.
|
|
||||||
score=score-distance
|
|
||||||
|
|
||||||
-- 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 #LEGION 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 LEGION:_OptimizeAssetSelection(assets, Mission, includePayload)
|
|
||||||
|
|
||||||
-- Calculate the mission score of all assets.
|
|
||||||
for _,_asset in pairs(assets) do
|
|
||||||
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
|
||||||
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.
|
|
||||||
return (assetA.score>assetB.score)
|
|
||||||
end
|
|
||||||
table.sort(assets, optimize)
|
|
||||||
|
|
||||||
-- Remove distance parameter.
|
|
||||||
local text=string.format("Optimized %d assets for %s mission (payload=%s):", #assets, Mission.type, tostring(includePayload))
|
|
||||||
for i,Asset in pairs(assets) do
|
|
||||||
local asset=Asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
|
||||||
text=text..string.format("\n%s %s: score=%d", asset.squadname, asset.spawngroupname, asset.score)
|
|
||||||
asset.dist=nil
|
|
||||||
asset.score=nil
|
|
||||||
end
|
|
||||||
self:T2(self.lid..text)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- FSM Events
|
-- FSM Events
|
||||||
@ -1239,7 +1074,7 @@ function LEGION:onafterSelfRequest(From, Event, To, groupset, request)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Misc Functions
|
-- Mission Functions
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
--- Create a new flight group after an asset was spawned.
|
--- Create a new flight group after an asset was spawned.
|
||||||
@ -1691,7 +1526,7 @@ function LEGION:_CanMission(Mission)
|
|||||||
local Npayloads=self:IsAirwing() and self:CountPayloadsInStock(Mission.type, cohort.aircrafttype, Mission.payloads) or 999
|
local Npayloads=self:IsAirwing() and self:CountPayloadsInStock(Mission.type, cohort.aircrafttype, Mission.payloads) or 999
|
||||||
|
|
||||||
-- Recruit assets.
|
-- Recruit assets.
|
||||||
local assets=cohort:RecruitAssets(Mission, Npayloads)
|
local assets=cohort:RecruitAssets(Mission.type, Npayloads)
|
||||||
|
|
||||||
-- Total number.
|
-- Total number.
|
||||||
for _,asset in pairs(assets) do
|
for _,asset in pairs(assets) do
|
||||||
@ -1724,8 +1559,11 @@ function LEGION:RecruitAssets(Mission)
|
|||||||
-- Number of payloads in stock per aircraft type.
|
-- Number of payloads in stock per aircraft type.
|
||||||
local Npayloads={}
|
local Npayloads={}
|
||||||
|
|
||||||
|
-- Squadrons for the job. If user assigned to mission or simply all.
|
||||||
|
local cohorts=Mission.squadrons or self.cohorts
|
||||||
|
|
||||||
-- First get payloads for aircraft types of squadrons.
|
-- First get payloads for aircraft types of squadrons.
|
||||||
for _,_cohort in pairs(self.cohorts) do
|
for _,_cohort in pairs(cohorts) do
|
||||||
local cohort=_cohort --Ops.Cohort#COHORT
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
if Npayloads[cohort.aircrafttype]==nil then
|
if Npayloads[cohort.aircrafttype]==nil then
|
||||||
local MissionType=Mission.type
|
local MissionType=Mission.type
|
||||||
@ -1741,7 +1579,7 @@ function LEGION:RecruitAssets(Mission)
|
|||||||
local Assets={}
|
local Assets={}
|
||||||
|
|
||||||
-- Loops over cohorts.
|
-- Loops over cohorts.
|
||||||
for _,_cohort in pairs(self.cohorts) do
|
for _,_cohort in pairs(cohorts) do
|
||||||
local cohort=_cohort --Ops.Cohort#COHORT
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
|
|
||||||
local npayloads=Npayloads[cohort.aircrafttype]
|
local npayloads=Npayloads[cohort.aircrafttype]
|
||||||
@ -1749,7 +1587,7 @@ function LEGION:RecruitAssets(Mission)
|
|||||||
if cohort:CanMission(Mission) and npayloads>0 then
|
if cohort:CanMission(Mission) and npayloads>0 then
|
||||||
|
|
||||||
-- Recruit assets from squadron.
|
-- Recruit assets from squadron.
|
||||||
local assets, npayloads=cohort:RecruitAssets(Mission, npayloads)
|
local assets, npayloads=cohort:RecruitAssets(Mission.type, npayloads)
|
||||||
|
|
||||||
Npayloads[cohort.aircrafttype]=npayloads
|
Npayloads[cohort.aircrafttype]=npayloads
|
||||||
|
|
||||||
@ -1801,6 +1639,7 @@ function LEGION:RecruitAssets(Mission)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Get number of required assets.
|
||||||
local Nassets=Mission:GetRequiredAssets(self)
|
local Nassets=Mission:GetRequiredAssets(self)
|
||||||
|
|
||||||
if #Assets>=Nassets then
|
if #Assets>=Nassets then
|
||||||
@ -1854,6 +1693,346 @@ function LEGION:RecruitAssets(Mission)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Calculate the mission score of an asset.
|
||||||
|
-- @param #LEGION 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 LEGION:CalculateAssetMissionScore(asset, Mission, includePayload)
|
||||||
|
|
||||||
|
-- Mission score.
|
||||||
|
local score=0
|
||||||
|
|
||||||
|
-- Prefer highly skilled assets.
|
||||||
|
if asset.skill==AI.Skill.AVERAGE then
|
||||||
|
score=score+0
|
||||||
|
elseif asset.skill==AI.Skill.GOOD then
|
||||||
|
score=score+10
|
||||||
|
elseif asset.skill==AI.Skill.HIGH then
|
||||||
|
score=score+20
|
||||||
|
elseif asset.skill==AI.Skill.EXCELLENT then
|
||||||
|
score=score+30
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add mission performance to score.
|
||||||
|
score=score+asset.cohort:GetMissionPeformance(Mission.Type)
|
||||||
|
|
||||||
|
-- Add payload performance to score.
|
||||||
|
if includePayload and asset.payload then
|
||||||
|
score=score+self:GetPayloadPeformance(asset.payload, Mission.type)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Target position.
|
||||||
|
local TargetVec2=Mission.type~=AUFTRAG.Type.ALERT5 and Mission:GetTargetVec2() or nil --Mission:GetTargetVec2()
|
||||||
|
|
||||||
|
-- Origin: We take the flightgroups position or the one of the legion.
|
||||||
|
local OrigVec2=asset.flightgroup and asset.flightgroup:GetVec2() or self:GetVec2()
|
||||||
|
|
||||||
|
-- Distance factor.
|
||||||
|
local distance=0
|
||||||
|
if TargetVec2 and OrigVec2 then
|
||||||
|
-- Distance in NM.
|
||||||
|
distance=UTILS.MetersToNM(UTILS.VecDist2D(OrigVec2, TargetVec2))
|
||||||
|
-- Round: 55 NM ==> 5.5 ==> 6, 63 NM ==> 6.3 ==> 6
|
||||||
|
distance=UTILS.Round(distance/10, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reduce score for legions that are futher away.
|
||||||
|
score=score-distance
|
||||||
|
|
||||||
|
-- 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 #LEGION 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 LEGION:_OptimizeAssetSelection(assets, Mission, includePayload)
|
||||||
|
|
||||||
|
-- Calculate the mission score of all assets.
|
||||||
|
for _,_asset in pairs(assets) do
|
||||||
|
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||||
|
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.
|
||||||
|
return (assetA.score>assetB.score)
|
||||||
|
end
|
||||||
|
table.sort(assets, optimize)
|
||||||
|
|
||||||
|
-- Remove distance parameter.
|
||||||
|
local text=string.format("Optimized %d assets for %s mission (payload=%s):", #assets, Mission.type, tostring(includePayload))
|
||||||
|
for i,Asset in pairs(assets) do
|
||||||
|
local asset=Asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||||
|
text=text..string.format("\n%s %s: score=%d", asset.squadname, asset.spawngroupname, asset.score)
|
||||||
|
asset.dist=nil
|
||||||
|
asset.score=nil
|
||||||
|
end
|
||||||
|
self:T2(self.lid..text)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Transport Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Recruit assets for a given OPS transport.
|
||||||
|
-- @param #LEGION self
|
||||||
|
-- @param Ops.OpsTransport#OPSTRANSPORT Transport The OPS transport.
|
||||||
|
-- @return #boolean If `true`, enough assets could be recruited.
|
||||||
|
function LEGION:RecruitAssetsForTransport(Transport)
|
||||||
|
|
||||||
|
-- Get all undelivered cargo ops groups.
|
||||||
|
local cargoOpsGroups=Transport:GetCargoOpsGroups(false)
|
||||||
|
|
||||||
|
local weightGroup=0
|
||||||
|
|
||||||
|
-- At least one group should be spawned.
|
||||||
|
if #cargoOpsGroups>0 then
|
||||||
|
|
||||||
|
-- Calculate the max weight so we know which cohorts can provide carriers.
|
||||||
|
for _,_opsgroup in pairs(cargoOpsGroups) do
|
||||||
|
local opsgroup=_opsgroup --Ops.OpsGroup#OPSGROUP
|
||||||
|
local weight=opsgroup:GetWeightTotal()
|
||||||
|
if weight>weightGroup then
|
||||||
|
weightGroup=weight
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Number of payloads in stock per aircraft type.
|
||||||
|
local Npayloads={}
|
||||||
|
|
||||||
|
-- First get payloads for aircraft types of squadrons.
|
||||||
|
for _,_cohort in pairs(self.cohorts) do
|
||||||
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
|
if Npayloads[cohort.aircrafttype]==nil then
|
||||||
|
Npayloads[cohort.aircrafttype]=self:IsAirwing() and self:CountPayloadsInStock(AUFTRAG.Type.OPSTRANSPORT, cohort.aircrafttype) or 999
|
||||||
|
self:I(self.lid..string.format("Got N=%d payloads for mission type=%s and unit type=%s", Npayloads[cohort.aircrafttype], AUFTRAG.Type.OPSTRANSPORT, cohort.aircrafttype))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The recruited assets.
|
||||||
|
local Assets={}
|
||||||
|
|
||||||
|
-- Loops over cohorts.
|
||||||
|
for _,_cohort in pairs(self.cohorts) do
|
||||||
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
|
|
||||||
|
local npayloads=Npayloads[cohort.aircrafttype]
|
||||||
|
|
||||||
|
if cohort:IsOnDuty() and npayloads>0 and cohort:CheckMissionCapability({AUFTRAG.Type.OPSTRANSPORT}) and cohort.cargobayLimit>=weightGroup then
|
||||||
|
|
||||||
|
-- Recruit assets from squadron.
|
||||||
|
local assets, npayloads=cohort:RecruitAssets(AUFTRAG.Type.OPSTRANSPORT, npayloads)
|
||||||
|
|
||||||
|
Npayloads[cohort.aircrafttype]=npayloads
|
||||||
|
|
||||||
|
for _,asset in pairs(assets) do
|
||||||
|
table.insert(Assets, asset)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Sort asset list. Best ones come first.
|
||||||
|
self:_OptimizeAssetSelectionForTransport(Assets, Transport, false)
|
||||||
|
|
||||||
|
-- If airwing, get the best payload available.
|
||||||
|
if self:IsAirwing() then
|
||||||
|
|
||||||
|
for _,_asset in pairs(Assets) do
|
||||||
|
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||||
|
|
||||||
|
-- Only assets that have no payload. Should be only spawned assets!
|
||||||
|
if not asset.payload then
|
||||||
|
|
||||||
|
-- Fetch payload for asset. This can be nil!
|
||||||
|
asset.payload=self:FetchPayloadFromStock(asset.unittype, AUFTRAG.Type.OPSTRANSPORT)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove assets that dont have a payload.
|
||||||
|
for i=#Assets,1,-1 do
|
||||||
|
local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||||
|
if not asset.payload then
|
||||||
|
table.remove(Assets, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Number of required carriers.
|
||||||
|
local NreqMin,NreqMax=Transport:GetRequiredCarriers()
|
||||||
|
|
||||||
|
-- Number of assets. At most NreqMax.
|
||||||
|
local Nassets=math.min(#Assets, NreqMax)
|
||||||
|
|
||||||
|
if Nassets>=NreqMin then
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Found enough assets
|
||||||
|
---
|
||||||
|
|
||||||
|
-- Add assets to mission.
|
||||||
|
for i=1,Nassets do
|
||||||
|
local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||||
|
asset.isReserved=true
|
||||||
|
Transport:AddAsset(asset)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self:IsAirwing() then
|
||||||
|
|
||||||
|
-- Return payloads of not needed assets.
|
||||||
|
for i=Nassets+1,#Assets do
|
||||||
|
local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||||
|
if not asset.spawned then
|
||||||
|
self:T(self.lid..string.format("Returning payload from asset %s", asset.spawngroupname))
|
||||||
|
self:ReturnPayloadFromAsset(asset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Found enough assets.
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
|
||||||
|
---
|
||||||
|
-- NOT enough assets
|
||||||
|
---
|
||||||
|
|
||||||
|
-- Return payloads of assets.
|
||||||
|
if self:IsAirwing() then
|
||||||
|
for i=1,#Assets do
|
||||||
|
local asset=Assets[i]
|
||||||
|
if not asset.spawned then
|
||||||
|
self:T(self.lid..string.format("Returning payload from asset %s", asset.spawngroupname))
|
||||||
|
self:ReturnPayloadFromAsset(asset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Not enough assets found.
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Optimize chosen assets for the mission at hand.
|
||||||
|
-- @param #LEGION self
|
||||||
|
-- @param #table assets Table of (unoptimized) assets.
|
||||||
|
-- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport.
|
||||||
|
function LEGION:_OptimizeAssetSelectionForTransport(assets, Transport)
|
||||||
|
|
||||||
|
-- Calculate the mission score of all assets.
|
||||||
|
for _,_asset in pairs(assets) do
|
||||||
|
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||||
|
asset.score=self:CalculateAssetTransportScore(asset, Transport)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Sort assets wrt to their mission score. Higher is better.
|
||||||
|
local function optimize(a, b)
|
||||||
|
local assetA=a --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||||
|
local assetB=b --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||||
|
-- Higher score wins. If equal score ==> closer wins.
|
||||||
|
return (assetA.score>assetB.score)
|
||||||
|
end
|
||||||
|
table.sort(assets, optimize)
|
||||||
|
|
||||||
|
-- Remove distance parameter.
|
||||||
|
local text=string.format("Optimized %d assets for transport:", #assets)
|
||||||
|
for i,Asset in pairs(assets) do
|
||||||
|
local asset=Asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||||
|
text=text..string.format("\n%s %s: score=%d", asset.squadname, asset.spawngroupname, asset.score)
|
||||||
|
asset.dist=nil
|
||||||
|
asset.score=nil
|
||||||
|
end
|
||||||
|
self:T2(self.lid..text)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Calculate the mission score of an asset.
|
||||||
|
-- @param #LEGION self
|
||||||
|
-- @param Functional.Warehouse#WAREHOUSE.Assetitem asset Asset
|
||||||
|
-- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport.
|
||||||
|
-- @return #number Mission score.
|
||||||
|
function LEGION:CalculateAssetTransportScore(asset, Transport)
|
||||||
|
|
||||||
|
-- Mission score.
|
||||||
|
local score=0
|
||||||
|
|
||||||
|
-- Prefer highly skilled assets.
|
||||||
|
if asset.skill==AI.Skill.AVERAGE then
|
||||||
|
score=score+0
|
||||||
|
elseif asset.skill==AI.Skill.GOOD then
|
||||||
|
score=score+10
|
||||||
|
elseif asset.skill==AI.Skill.HIGH then
|
||||||
|
score=score+20
|
||||||
|
elseif asset.skill==AI.Skill.EXCELLENT then
|
||||||
|
score=score+30
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add mission performance to score.
|
||||||
|
score=score+asset.cohort:GetMissionPeformance(AUFTRAG.Type.OPSTRANSPORT)
|
||||||
|
|
||||||
|
-- Target position.
|
||||||
|
local TargetVec2=Transport:GetDeployZone():GetVec2()
|
||||||
|
|
||||||
|
-- Origin: We take the flightgroups position or the one of the legion.
|
||||||
|
local OrigVec2=asset.flightgroup and asset.flightgroup:GetVec2() or self:GetVec2()
|
||||||
|
|
||||||
|
-- Distance factor.
|
||||||
|
local distance=0
|
||||||
|
if TargetVec2 and OrigVec2 then
|
||||||
|
-- Distance in NM.
|
||||||
|
distance=UTILS.MetersToNM(UTILS.VecDist2D(OrigVec2, TargetVec2))
|
||||||
|
-- Round: 55 NM ==> 5.5 ==> 6, 63 NM ==> 6.3 ==> 6
|
||||||
|
distance=UTILS.Round(distance/10, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reduce score for legions that are futher away.
|
||||||
|
score=score-distance
|
||||||
|
|
||||||
|
--TODO: Check cargo bay capacity.
|
||||||
|
|
||||||
|
--TODO: Check ALERT 5 for Transports.
|
||||||
|
if asset.spawned then
|
||||||
|
self:T(self.lid.."Adding 25 to asset because it is spawned")
|
||||||
|
score=score+25
|
||||||
|
end
|
||||||
|
|
||||||
|
return score
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Misc Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
--- Check if a mission type is contained in a list of possible types.
|
--- Check if a mission type is contained in a list of possible types.
|
||||||
-- @param #LEGION self
|
-- @param #LEGION self
|
||||||
|
|||||||
@ -4114,6 +4114,7 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission)
|
|||||||
self:_SwitchICLS()
|
self:_SwitchICLS()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- We add a 10 sec delay for ARTY. Found that they need some time to readjust the barrel of their gun. Not sure if necessary for all. Needs some more testing!
|
||||||
local delay=1
|
local delay=1
|
||||||
if Mission.type==AUFTRAG.Type.ARTY then
|
if Mission.type==AUFTRAG.Type.ARTY then
|
||||||
delay=10
|
delay=10
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user