CHIEF: SetResponseOnTarget
TRANSPORT: improved transfer of loaded cargo
This commit is contained in:
Frank 2022-07-11 23:22:58 +02:00
parent bcb574e618
commit 469cc3d508
8 changed files with 236 additions and 47 deletions

View File

@ -92,7 +92,15 @@ function BRIGADE:New(WarehouseName, BrigadeName)
-- Defaults
self:SetRetreatZones()
-- Turn ship into NAVYGROUP.
if self:IsShip() then
local wh=self.warehouse --Wrapper.Unit#UNIT
local group=wh:GetGroup()
self.warehouseOpsGroup=NAVYGROUP:New(group) --Ops.NavyGroup#NAVYGROUP
self.warehouseOpsElement=self.warehouseOpsGroup:GetElementByName(wh:GetName())
end
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("*", "ArmyOnMission", "*") -- An ARMYGROUP was send on a Mission (AUFTRAG).

View File

@ -256,6 +256,17 @@ CHIEF.Strategy = {
-- @field #string MissionType Mission Type.
-- @field #number Performance Performance: a number between 0 and 100, where 100 is best performance.
--- Asset numbers for detected targets.
-- @type CHIEF.AssetNumber
-- @field #number nAssetMin Min number of assets.
-- @field #number nAssetMax Max number of assets.
-- @field #number threatlevel Threat level.
-- @field #string targetCategory Target category.
-- @field #string missionType Mission type.
-- @field #number nUnits Number of enemy units.
-- @field #string defcon Defense condition.
-- @field #string strategy Strategy.
--- Strategic zone.
-- @type CHIEF.StrategicZone
-- @field Ops.OpsZone#OPSZONE opszone OPS zone.
@ -773,6 +784,153 @@ function CHIEF:DeleteFromResource(Resource, MissionType)
return self
end
--- Set number of assets requested for detected targets.
-- @param #CHIEF self
-- @param #number NassetsMin Min number of assets. Should be at least 1. Default 1.
-- @param #number NassetsMax Max number of assets. Default is same as `NassetsMin`.
-- @param #number ThreatLevel Only apply this setting if the target threat level is greater or equal this number. Default 0.
-- @param #string TargetCategory Only apply this setting if the target is of this category, e.g. `TARGET.Category.AIRCRAFT`.
-- @param #string MissionType Only apply this setting for this mission type, e.g. `AUFTRAG.Type.INTERCEPT`.
-- @param #string Nunits Only apply this setting if the number of enemy units is greater or equal this number.
-- @param #string Defcon Only apply this setting if this defense condition is in place.
-- @param #string Strategy Only apply this setting if this strategy is in currently. place.
-- @return #CHIEF self
function CHIEF:SetResponseOnTarget(NassetsMin, NassetsMax, ThreatLevel, TargetCategory, MissionType, Nunits, Defcon, Strategy)
local bla={} --#CHIEF.AssetNumber
bla.nAssetMin=NassetsMin or 1
bla.nAssetMax=NassetsMax or bla.nAssetMin
bla.threatlevel=ThreatLevel or 0
bla.targetCategory=TargetCategory
bla.missionType=MissionType
bla.nUnits=Nunits or 1
bla.defcon=Defcon
bla.strategy=Strategy
self.assetNumbers=self.assetNumbers or {}
-- Add to table.
table.insert(self.assetNumbers, bla)
end
--- Add mission type and number of required assets to resource.
-- @param #CHIEF self
-- @param Ops.Target#TARGET Target The target.
-- @param #string MissionType Mission type.
-- @return #number Number of min assets.
-- @return #number Number of max assets.
function CHIEF:_GetAssetsForTarget(Target, MissionType)
-- Threat level.
local threatlevel=Target:GetThreatLevelMax()
-- Number of units.
local nUnits=Target.N0
-- Target category.
local targetcategory=Target:GetCategory()
-- Debug info.
self:T(self.lid..string.format("Getting number of assets for target with TL=%d, Category=%s, nUnits=%s, MissionType=%s", threatlevel, targetcategory, nUnits, tostring(MissionType)))
-- Candidates.
local candidates={}
local threatlevelMatch=nil
for _,_assetnumber in pairs(self.assetNumbers or {}) do
local assetnumber=_assetnumber --#CHIEF.AssetNumber
if (threatlevelMatch==nil and threatlevel>=assetnumber.threatlevel) or (threatlevelMatch~=nil and threatlevelMatch==threatlevel) then
if threatlevelMatch==nil then
threatlevelMatch=threatlevel
end
-- Number of other parameters matching.
local nMatch=0
-- Assume cand.
local cand=true
if assetnumber.targetCategory~=nil then
if assetnumber.targetCategory==targetcategory then
nMatch=nMatch+1
else
cand=false
end
end
if MissionType and assetnumber.missionType~=nil then
if assetnumber.missionType==MissionType then
nMatch=nMatch+1
else
cand=false
end
end
if assetnumber.nUnits~=nil then
if assetnumber.nUnits>=nUnits then
nMatch=nMatch+1
else
cand=false
end
end
if assetnumber.defcon~=nil then
if assetnumber.defcon==self.Defcon then
nMatch=nMatch+1
else
cand=false
end
end
if assetnumber.strategy~=nil then
if assetnumber.strategy==self.strategy then
nMatch=nMatch+1
else
cand=false
end
end
-- Add to candidates.
if cand then
table.insert(candidates, {assetnumber=assetnumber, nMatch=nMatch})
end
end
end
if #candidates>0 then
-- Return greater match.
local function _sort(a,b)
return a.nMatch>b.nMatch
end
-- Sort table by matches.
table.sort(candidates, _sort)
-- Pick the candidate with most matches.
local candidate=candidates[1]
-- Asset number.
local an=candidate.assetnumber --#CHIEF.AssetNumber
-- Debug message.
self:T(self.lid..string.format("Picking candidate with %d matches: NassetsMin=%d, NassetsMax=%d, ThreatLevel=%d, TargetCategory=%s, MissionType=%s, Defcon=%s, Strategy=%s",
candidate.nMatch, an.nAssetMin, an.nAssetMax, an.threatlevel, tostring(an.targetCategory), tostring(an.missionType), tostring(an.defcon), tostring(an.strategy)))
-- Return number of assetes.
return an.nAssetMin, an.nAssetMax
else
return 1, 1
end
end
--- Get defence condition.
-- @param #CHIEF self
-- @param #string Current Defence condition. See @{#CHIEF.DEFCON}, e.g. `CHIEF.DEFCON.RED`.
@ -2084,24 +2242,6 @@ function CHIEF:CheckTargetQueue()
local Legions=nil
if #MissionPerformances>0 then
--TODO: Number of required assets. How many do we want? Should depend on:
-- * number of enemy units
-- * target threatlevel
-- * how many assets are still in stock
-- * is it inside of our border
-- * add damping factor
local NassetsMin=1
local NassetsMax=1
if threatlevel>=8 and target.N0 >=10 then
NassetsMax=3
elseif threatlevel>=5 then
NassetsMax=2
else
NassetsMax=1
end
for _,_mp in pairs(MissionPerformances) do
local mp=_mp --#CHIEF.MissionPerformance
@ -2112,6 +2252,9 @@ function CHIEF:CheckTargetQueue()
--env.info(string.format("FF chief %s nolimit=%s", mp.MissionType, tostring(NoLimit)))
if notlimited then
-- Get min/max number of assets.
local NassetsMin, NassetsMax=self:_GetAssetsForTarget(target, mp.MissionType)
-- Debug info.
self:T2(self.lid..string.format("Recruiting assets for mission type %s [performance=%d] of target %s", mp.MissionType, mp.Performance, target:GetName()))

View File

@ -1084,7 +1084,7 @@ function COHORT:RecruitAssets(MissionType, Npayloads)
table.insert(assets, asset)
end
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.ALERT5) and AUFTRAG.CheckMissionCapability(MissionType, asset.payload.capabilities) then
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.ALERT5) and AUFTRAG.CheckMissionCapability(MissionType, asset.payload.capabilities) and MissionType~=AUFTRAG.Type.ALERT5 then
-- Check if the payload of this asset is compatible with the mission.
self:T(self.lid..string.format("Adding asset on ALERT 5 mission for %s mission", MissionType))

View File

@ -108,6 +108,15 @@ function FLEET:New(WarehouseName, FleetName)
-- Defaults
self:SetRetreatZones()
-- Turn ship into NAVYGROUP.
if self:IsShip() then
local wh=self.warehouse --Wrapper.Unit#UNIT
local group=wh:GetGroup()
self.warehouseOpsGroup=NAVYGROUP:New(group) --Ops.NavyGroup#NAVYGROUP
self.warehouseOpsElement=self.warehouseOpsGroup:GetElementByName(wh:GetName())
end
-- Add FSM transitions.
-- From State --> Event --> To State

View File

@ -4322,12 +4322,14 @@ function FLIGHTGROUP:_UpdateMenu(delay)
if player and player.status~=OPSGROUP.ElementStatus.DEAD then
-- Debug text.
local text=string.format("Updating MENU: State=%s, ATC=%s [%s]", self:GetState(),
self.flightcontrol and self.flightcontrol.airbasename or "None", self.flightcontrol and self.flightcontrol:GetFlightStatus(self) or "Unknown")
-- Message to group.
MESSAGE:New(text, 5):ToGroup(self.group)
self:I(self.lid..text)
if self.verbose>=2 then
local text=string.format("Updating MENU: State=%s, ATC=%s [%s]", self:GetState(),
self.flightcontrol and self.flightcontrol.airbasename or "None", self.flightcontrol and self.flightcontrol:GetFlightStatus(self) or "Unknown")
-- Message to group.
MESSAGE:New(text, 5):ToGroup(self.group)
self:I(self.lid..text)
end
-- Get current position of player.
local position=self:GetCoordinate(nil, player.name)

View File

@ -1191,6 +1191,12 @@ function LEGION:onafterOpsOnMission(From, Event, To, OpsGroup, Mission)
self:NavyOnMission(OpsGroup, Mission)
end
-- Load group as cargo because it cannot swim! We pause the mission.
if self:IsBrigade() and self:IsShip() then
OpsGroup:PauseMission()
self.warehouseOpsGroup:Load(OpsGroup, self.warehouseOpsElement)
end
-- Trigger event for chief.
if self.chief then
self.chief:OpsOnMission(OpsGroup, Mission)
@ -2689,15 +2695,15 @@ function LEGION:AssignAssetsForTransport(Legions, CargoAssets, NcarriersMin, Nca
-- Set pickup zone to spawn zone or airbase if the legion has one that is operational.
local pickupzone=legion.spawnzone
if legion.airbase and legion:IsRunwayOperational() then
--pickupzone=ZONE_AIRBASE:New(legion.airbasename, 4000)
end
-- Add TZC from legion spawn zone to deploy zone.
local tpz=Transport:AddTransportZoneCombo(nil, pickupzone, Transport:GetDeployZone())
tpz.PickupAirbase=legion:IsRunwayOperational() and legion.airbase or nil
Transport:SetEmbarkZone(legion.spawnzone, tpz)
-- Set pickup airbase if the legion has an airbase. Could also be the ship itself.
tpz.PickupAirbase=legion:IsRunwayOperational() and legion.airbase or nil
-- Set embark zone to spawn zone.
Transport:SetEmbarkZone(legion.spawnzone, tpz)
-- Add cargo assets to transport.
for _,_asset in pairs(CargoAssets) do

View File

@ -462,7 +462,7 @@ OPSGROUP.CarrierStatus={
-- @type OPSGROUP.CargoStatus
-- @field #string AWAITING Group is awaiting carrier.
-- @field #string NOTCARGO This group is no cargo yet.
-- @field #string ASSIGNED Cargo is assigned to a carrier.
-- @field #string ASSIGNED Cargo is assigned to a carrier. (Not used!)
-- @field #string BOARDING Cargo is boarding a carrier.
-- @field #string LOADED Cargo is loaded into a carrier.
OPSGROUP.CargoStatus={
@ -5397,6 +5397,12 @@ function OPSGROUP:RouteToMission(mission, delay)
self:T(self.lid..string.format("Route To Mission: I am DEAD or STOPPED! Ooops..."))
return
end
-- Check if this group is cargo.
if self:IsCargo() then
self:T(self.lid..string.format("Route To Mission: I am CARGO! You cannot route me..."))
return
end
-- OPSTRANSPORT: Just add the ops transport to the queue.
if mission.type==AUFTRAG.Type.OPSTRANSPORT then
@ -8661,12 +8667,12 @@ function OPSGROUP:onafterLoading(From, Event, To)
-- Check if cargo is not already cargo.
local isNotCargo=cargo.opsgroup:IsNotCargo(true)
-- Check if cargo is holding.
local isHolding=cargo.opsgroup:IsHolding()
-- Check if cargo is holding or loaded
local isHolding=cargo.opsgroup:IsHolding() or cargo.opsgroup:IsLoaded()
-- Check if cargo is in embark/pickup zone.
-- Added InUtero here, if embark zone is moving (ship) and cargo has been spawned late activated and its position is not updated. Not sure if that breaks something else!
local inZone=cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone) --or cargo.opsgroup:IsInUtero()
local inZone=cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone) or cargo.opsgroup:IsInUtero()
-- Check if cargo is currently on a mission.
local isOnMission=cargo.opsgroup:IsOnMission()
@ -8675,9 +8681,13 @@ function OPSGROUP:onafterLoading(From, Event, To)
if isOnMission then
local mission=cargo.opsgroup:GetMissionCurrent()
if mission and mission.opstransport and mission.opstransport.uid==self.cargoTransport.uid then
isOnMission=not cargo.opsgroup:IsHolding()
isOnMission=not isHolding
end
end
end
-- Debug message.
self:T(self.lid..string.format("Loading: canCargo=%s, isCarrier=%s, isNotCargo=%s, isHolding=%s, isOnMission=%s",
tostring(canCargo), tostring(isCarrier), tostring(isNotCargo), tostring(isHolding), tostring(isOnMission)))
-- TODO: Need a better :IsBusy() function or :IsReadyForMission() :IsReadyForBoarding() :IsReadyForTransport()
if canCargo and inZone and isNotCargo and isHolding and (not (cargo.delivered or cargo.opsgroup:IsDead() or isCarrier or isOnMission)) then
@ -8701,10 +8711,7 @@ function OPSGROUP:onafterLoading(From, Event, To)
local carrier=self:FindCarrierForCargo(cargo.opsgroup)
if carrier then
-- Set cargo status.
cargo.opsgroup:_NewCargoStatus(OPSGROUP.CargoStatus.ASSIGNED)
-- Order cargo group to board the carrier.
cargo.opsgroup:Board(self, carrier)
@ -9312,7 +9319,10 @@ function OPSGROUP:onafterUnloaded(From, Event, To, OpsGroupCargo)
OpsGroupCargo:Returned()
end
if self:_CountPausedMissions()>0 then
-- Check if there is a paused mission.
local paused=OpsGroupCargo:_CountPausedMissions()>0
if paused then
OpsGroupCargo:UnpauseMission()
end
@ -9586,9 +9596,6 @@ end
-- @param #OPSGROUP.Element Carrier The OPSGROUP element
function OPSGROUP:onafterBoard(From, Event, To, CarrierGroup, Carrier)
-- Set cargo status.
self:_NewCargoStatus(OPSGROUP.CargoStatus.BOARDING)
-- Army or Navy group.
local CarrierIsArmyOrNavy=CarrierGroup:IsArmygroup() or CarrierGroup:IsNavygroup()
local CargoIsArmyOrNavy=self:IsArmygroup() or self:IsNavygroup()
@ -9605,7 +9612,21 @@ function OPSGROUP:onafterBoard(From, Event, To, CarrierGroup, Carrier)
board=false
end
if board then
if self:IsLoaded() then
-- Debug info.
self:T(self.lid..string.format("Group is loaded currently ==> Moving directly to new carrier - No Unload(), Disembart() events triggered!"))
-- Remove my carrier.
self:_RemoveMyCarrier()
-- Trigger Load event.
CarrierGroup:Load(self)
elseif board then
-- Set cargo status.
self:_NewCargoStatus(OPSGROUP.CargoStatus.BOARDING)
-- Debug info.
self:T(self.lid..string.format("Boarding group=%s [%s], carrier=%s", CarrierGroup:GetName(), CarrierGroup:GetState(), tostring(Carrier.name)))

View File

@ -2171,7 +2171,7 @@ function OPSTRANSPORT:_GetTransportZoneCombo(Carrier)
return nil
end
--- Get an OPSGROUP from a given OPSGROUP or GROUP object. If the object is a GROUUP, an OPSGROUP is created automatically.
--- Get an OPSGROUP from a given OPSGROUP or GROUP object. If the object is a GROUP, an OPSGROUP is created automatically.
-- @param #OPSTRANSPORT self
-- @param Core.Base#BASE Object The object, which can be a GROUP or OPSGROUP.
-- @return Ops.OpsGroup#OPSGROUP Ops Group.