-- New SEAD, BAI and CAS files.

-- Correct handling of task assignments.
-- Added Weapon stuff in DCS.lua.
-- Dispatcher optimizations.
This commit is contained in:
FlightControl
2018-12-11 19:20:11 +01:00
parent 921ec8732c
commit d098930351
11 changed files with 775 additions and 93 deletions

View File

@@ -489,7 +489,7 @@ function AI_A2A:onafterStatus()
not self:Is( "Fuel" ) and
not self:Is( "Damaged" ) and
not self:Is( "Home" ) then
if self.IdleCount >= 2 then
if self.IdleCount >= 3 then
if Damage ~= InitialLife then
self:Damaged()
else

View File

@@ -0,0 +1,172 @@
--- **AI** -- Models the process of air to ground BAI engagement for airplanes and helicopters.
--
-- This is a class used in the @{AI_A2G_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2G_BAI
-- @image AI_Air_To_Ground_Engage.JPG
--- @type AI_A2G_BAI
-- @extends AI.AI_A2A_Engage#AI_A2A_Engage
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
--
-- ===
--
-- @field #AI_A2G_BAI
AI_A2G_BAI = {
ClassName = "AI_A2G_BAI",
}
--- Creates a new AI_A2G_BAI object
-- @param #AI_A2G_BAI self
-- @param Wrapper.Group#GROUP AIGroup
-- @return #AI_A2G_BAI
function AI_A2G_BAI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2G_ENGAGE:New( AIGroup, EngageMinSpeed, EngageMaxSpeed ) ) -- #AI_A2G_BAI
return self
end
--- onafter event handler for Start event.
-- @param #AI_A2G_BAI self
-- @param Wrapper.Group#GROUP AIGroup The AI group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2G_BAI:onafterStart( AIGroup, From, Event, To )
self:GetParent( self ).onafterStart( self, AIGroup, From, Event, To )
AIGroup:HandleEvent( EVENTS.Takeoff, nil, self )
end
--- @param #AI_A2G_BAI self
-- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2G_BAI:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
local DefenderGroupName = DefenderGroup:GetName()
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
local AttackCount = self.AttackSetUnit:Count()
if AttackCount > 0 then
if DefenderGroup:IsAlive() then
-- Determine the distance to the target.
-- If it is less than 10km, then attack without a route.
-- Otherwise perform a route attack.
local DefenderCoord = DefenderGroup:GetCoordinate()
local TargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
if TargetDistance >= 50000 then
local EngageRoute = {}
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
--- Calculate the target route point.
local FromWP = DefenderCoord:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
EngageRoute[#EngageRoute+1] = FromWP
local ToCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
self:SetTargetDistance( ToCoord ) -- For RTB status check
local FromEngageAngle = ToCoord:GetAngleDegrees( ToCoord:GetDirectionVec3( DefenderCoord ) )
--- Create a route point of type air.
local ToWP = ToCoord:Translate( 50000, FromEngageAngle ):WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
self:F( { Angle = FromEngageAngle, ToTargetSpeed = ToTargetSpeed } )
self:F( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToWP
local AttackTasks = {}
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
self:T( { "Engage Unit evaluation:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsGround() } )
local HasRadar = AttackUnit:HasSEAD()
if HasRadar then
self:T( { "Eliminating Unit:", AttackUnit:GetName() } )
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
end
end
end
if #AttackTasks == 0 then
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
else
DefenderGroup:OptionROEOpenFire()
DefenderGroup:OptionROTEvadeFire()
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self )
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
end
DefenderGroup:Route( EngageRoute, 0 )
else
local AttackTasks = {}
--local AttackUnit = self.AttackSetUnit:GetRandom() -- Wrapper.Unit#UNIT
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
local HasRadar = AttackUnit:HasSEAD()
if HasRadar then
self:T( { "Eliminating Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsGround() } )
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
end
end
end
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self )
local DefenderTask = DefenderGroup:TaskCombo( AttackTasks )
DefenderGroup:SetTask( DefenderTask, 0 )
end
end
else
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
end
end

View File

@@ -0,0 +1,173 @@
--- **AI** -- Models the process of air to ground engagement for airplanes and helicopters.
--
-- This is a class used in the @{AI_A2G_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2G_CAS
-- @image AI_Air_To_Ground_Engage.JPG
--- @type AI_A2G_CAS
-- @extends AI.AI_A2G_Engage#AI_A2G_Engage
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
--
-- ===
--
-- @field #AI_A2G_CAS
AI_A2G_CAS = {
ClassName = "AI_A2G_CAS",
}
--- Creates a new AI_A2G_CAS object
-- @param #AI_A2G_CAS self
-- @param Wrapper.Group#GROUP AIGroup
-- @return #AI_A2G_CAS
function AI_A2G_CAS:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2G_ENGAGE:New( AIGroup, EngageMinSpeed, EngageMaxSpeed ) ) -- #AI_A2G_CAS
return self
end
--- onafter event handler for Start event.
-- @param #AI_A2G_CAS self
-- @param Wrapper.Group#GROUP AIGroup The AI group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2G_CAS:onafterStart( AIGroup, From, Event, To )
self:GetParent( self ).onafterStart( self, AIGroup, From, Event, To )
AIGroup:HandleEvent( EVENTS.Takeoff, nil, self )
end
--- @param #AI_A2G_CAS self
-- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2G_CAS:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
local DefenderGroupName = DefenderGroup:GetName()
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
local AttackCount = self.AttackSetUnit:Count()
if AttackCount > 0 then
if DefenderGroup:IsAlive() then
-- Determine the distance to the target.
-- If it is less than 10km, then attack without a route.
-- Otherwise perform a route attack.
local DefenderCoord = DefenderGroup:GetCoordinate()
local TargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
if TargetDistance >= 50000 then
local EngageRoute = {}
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
--- Calculate the target route point.
local FromWP = DefenderCoord:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
EngageRoute[#EngageRoute+1] = FromWP
local ToCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
self:SetTargetDistance( ToCoord ) -- For RTB status check
local FromEngageAngle = ToCoord:GetAngleDegrees( ToCoord:GetDirectionVec3( DefenderCoord ) )
--- Create a route point of type air.
local ToWP = ToCoord:Translate( 50000, FromEngageAngle ):WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
self:F( { Angle = FromEngageAngle, ToTargetSpeed = ToTargetSpeed } )
self:F( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToWP
local AttackTasks = {}
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
self:T( { "Engage Unit evaluation:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsGround() } )
local HasRadar = AttackUnit:HasSEAD()
if HasRadar then
self:T( { "Eliminating Unit:", AttackUnit:GetName() } )
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
end
end
end
if #AttackTasks == 0 then
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
else
DefenderGroup:OptionROEOpenFire()
DefenderGroup:OptionROTEvadeFire()
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self )
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
end
DefenderGroup:Route( EngageRoute, 0 )
else
local AttackTasks = {}
--local AttackUnit = self.AttackSetUnit:GetRandom() -- Wrapper.Unit#UNIT
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
local HasRadar = AttackUnit:HasSEAD()
if HasRadar then
self:T( { "Eliminating Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsGround() } )
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
end
end
end
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self )
local DefenderTask = DefenderGroup:TaskCombo( AttackTasks )
DefenderGroup:SetTask( DefenderTask, 0 )
end
end
else
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
end
end

View File

@@ -810,6 +810,7 @@ do -- AI_A2G_DISPATCHER
if Squadron then
self:F( { SquadronName = Squadron.Name } )
local LandingMethod = self:GetSquadronLanding( Squadron.Name )
if LandingMethod == AI_A2G_DISPATCHER.Landing.AtRunway then
local DefenderSize = Defender:GetSize()
if DefenderSize == 1 then
@@ -1395,6 +1396,8 @@ do -- AI_A2G_DISPATCHER
local DefenderSquadron = self:GetSquadron( SquadronName )
DefenderSquadron.Uncontrolled = true
self:SetSquadronTakeoffFromParkingCold( SquadronName )
self:SetSquadronLandingAtEngineShutdown( SquadronName )
for SpawnTemplate, DefenderSpawn in pairs( self.DefenderSpawns ) do
DefenderSpawn:InitUnControlled()
@@ -2856,13 +2859,16 @@ do -- AI_A2G_DISPATCHER
if DefenderUnitIndex == 1 then
DefenderPatrolTemplate = UTILS.DeepCopy( DefenderTemplate )
self.DefenderPatrolIndex = self.DefenderPatrolIndex + 1
DefenderPatrolTemplate.name = SquadronName .. "#" .. self.DefenderPatrolIndex .. "#" .. GroupName
--DefenderPatrolTemplate.name = SquadronName .. "#" .. self.DefenderPatrolIndex .. "#" .. GroupName
DefenderPatrolTemplate.name = GroupName
DefenderName = DefenderPatrolTemplate.name
else
-- Add the unit in the template to the DefenderPatrolTemplate.
local DefenderUnitTemplate = DefenderTemplate.units[1]
DefenderPatrolTemplate.units[DefenderUnitIndex] = DefenderUnitTemplate
end
DefenderPatrolTemplate.units[DefenderUnitIndex].name = string.format( DefenderPatrolTemplate.name .. '-%02d', DefenderUnitIndex )
DefenderPatrolTemplate.units[DefenderUnitIndex].unitId = nil
DefenderUnitIndex = DefenderUnitIndex + 1
DefenderSquadron.Resources[TemplateID][GroupName] = nil
if DefenderUnitIndex > DefenderGrouping then
@@ -2880,8 +2886,8 @@ do -- AI_A2G_DISPATCHER
DefenderPatrolTemplate.route.points[1].type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
DefenderPatrolTemplate.route.points[1].action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action
local Defender = _DATABASE:Spawn( DefenderPatrolTemplate )
self:AddDefenderToSquadron( DefenderSquadron, Defender, DefenderGrouping )
Defender:Activate()
return Defender, DefenderGrouping
end
else
@@ -2993,6 +2999,8 @@ do -- AI_A2G_DISPATCHER
self:F( { From, Event, To, AttackerDetection.Index, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing, DefenderFriendlies = DefenderFriendlies } )
AttackerDetection.Type = DefenseTaskType -- This is set to report the task type in the status panel.
local AttackerSet = AttackerDetection.Set
local AttackerUnit = AttackerSet:GetFirst()
@@ -3103,7 +3111,9 @@ do -- AI_A2G_DISPATCHER
DefenderCount = DefenderCount - DefenderGrouping / DefenderOverhead
local Fsm = AI_A2G_ENGAGE:New( DefenderGroup, Defense.EngageMinSpeed, Defense.EngageMaxSpeed )
local AI_A2G_ENGAGE = { SEAD = AI_A2G_SEAD, BAI = AI_A2G_BAI, CAS = AI_A2G_CAS }
local Fsm = AI_A2G_ENGAGE[DefenseTaskType]:New( DefenderGroup, Defense.EngageMinSpeed, Defense.EngageMaxSpeed ) -- AI.AI_A2G_ENGAGE
Fsm:SetDispatcher( self )
Fsm:SetHomeAirbase( DefenderSquadron.Airbase )
Fsm:SetFuelThreshold( DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold, 60 )
@@ -3121,6 +3131,8 @@ do -- AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
local DefenderTarget = Dispatcher:GetDefenderTaskTarget( Defender )
self:F( { DefenderTarget = DefenderTarget } )
if DefenderTarget then
Fsm:__Engage( 2, DefenderTarget.Set ) -- Engage on the TargetSetUnit
end
@@ -3141,10 +3153,10 @@ do -- AI_A2G_DISPATCHER
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
if Defender:IsAboveRunway() then
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
Defender:Destroy()
end
--if Defender:IsAboveRunway() then
--Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
--Defender:Destroy()
--end
end
--- @param #AI_A2G_DISPATCHER self
@@ -3298,8 +3310,8 @@ do -- AI_A2G_DISPATCHER
for DefenderGroup, DefenderTask in pairs( self:GetDefenderTasks() ) do
local DefenderGroup = DefenderGroup -- Wrapper.Group#GROUP
local DefenderTaskFsm = self:GetDefenderTaskFsm( DefenderGroup )
if not DefenderGroup:IsAlive() then
local DefenderTaskFsm = self:GetDefenderTaskFsm( DefenderGroup )
self:F( { Defender = DefenderGroup:GetName(), DefenderState = DefenderTaskFsm:GetState() } )
if not DefenderTaskFsm:Is( "Started" ) then
self:ClearDefenderTask( DefenderGroup )
@@ -3312,10 +3324,10 @@ do -- AI_A2G_DISPATCHER
self:ClearDefenderTaskTarget( DefenderGroup )
else
if DefenderTask.Target.Set then
local AttackerCount = DefenderTask.Target.Set:Count()
if AttackerCount == 0 then
local TargetCount = DefenderTask.Target.Set:Count()
if TargetCount == 0 then
self:F( { "All Targets destroyed in Target, removing:", DefenderTask.Target.Index } )
self:ClearDefenderTaskTarget( DefenderGroup )
self:ClearDefenderTask( DefenderGroup )
end
end
end
@@ -3410,7 +3422,7 @@ do -- AI_A2G_DISPATCHER
if self.TacticalDisplay then
-- Show tactical situation
Report:Add( string.format( "\n - Target %s ( %s ): ( #%d ) %s" , DetectedItem.ItemID, DetectedItem.Index, DetectedItem.Set:Count(), DetectedItem.Set:GetObjectNames() ) )
Report:Add( string.format( "\n - %s %s ( %s ): ( #%d ) %s" , DetectedItem.Type or " --- ", DetectedItem.ItemID, DetectedItem.Index, DetectedItem.Set:Count(), DetectedItem.Set:GetObjectNames() ) )
for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do
local Defender = Defender -- Wrapper.Group#GROUP
if DefenderTask.Target and DefenderTask.Target.Index == DetectedItem.Index then

View File

@@ -14,7 +14,7 @@
--- @type AI_A2G_ENGAGE
-- @extends AI.AI_A2A#AI_A2A
-- @extends AI.AI_A2G#AI_A2G
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
@@ -266,7 +266,8 @@ end
-- @param #string To The To State string.
function AI_A2G_ENGAGE:onafterStart( AIGroup, From, Event, To )
self:GetParent( self ).onafterStart( self, AIGroup, From, Event, To )
self:GetParent( self, AI_A2G_ENGAGE ).onafterStart( self, AIGroup, From, Event, To )
AIGroup:HandleEvent( EVENTS.Takeoff, nil, self )
end
@@ -327,81 +328,14 @@ end
--- @param #AI_A2G_ENGAGE self
-- @param Wrapper.Group#GROUP AIGroup The GroupGroup managed by the FSM.
-- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2G_ENGAGE:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
function AI_A2G_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
self:F( { AIGroup, From, Event, To, AttackSetUnit} )
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
local FirstAttackUnit = self.AttackSetUnit:GetFirst()
if FirstAttackUnit and FirstAttackUnit:IsAlive() then
if AIGroup:IsAlive() then
local EngageRoute = {}
local CurrentCoord = AIGroup:GetCoordinate()
--- Calculate the target route point.
local CurrentCoord = AIGroup:GetCoordinate()
local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
local ToEngageAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
--- Create a route point of type air.
local ToPatrolRoutePoint = CurrentCoord:Translate( 15000, ToEngageAngle ):WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
self:F( { Angle = ToEngageAngle, ToTargetSpeed = ToTargetSpeed } )
self:F( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
local AttackTasks = {}
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
self:T( { "Eliminating Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsGround() } )
AttackTasks[#AttackTasks+1] = AIGroup:TaskAttackUnit( AttackUnit )
end
end
if #AttackTasks == 0 then
self:E("No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
else
AIGroup:OptionROEOpenFire()
AIGroup:OptionROTEvadeFire()
AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self )
EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( AttackTasks )
end
AIGroup:Route( EngageRoute, 0.5 )
end
else
self:E("No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
end
end
--- @param #AI_A2G_ENGAGE self

View File

@@ -112,7 +112,7 @@ AI_A2G_PATROL = {
function AI_A2G_PATROL:New( AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2A_PATROL:New( AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_A2G_PATROL
local self = BASE:Inherit( self, AI_A2G:New( AIPatrol ) ) -- #AI_A2G_PATROL
self.Accomplished = false
self.Engaging = false
@@ -290,7 +290,6 @@ end
-- @param #string To The To State string.
function AI_A2G_PATROL:onafterStart( AIPatrol, From, Event, To )
self:GetParent( self ).onafterStart( self, AIPatrol, From, Event, To )
AIPatrol:HandleEvent( EVENTS.Takeoff, nil, self )
end

View File

@@ -0,0 +1,218 @@
--- **AI** -- Models the process of air to ground SEAD engagement for airplanes and helicopters.
--
-- This is a class used in the @{AI_A2G_Dispatcher}.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2G_SEAD
-- @image AI_Air_To_Ground_Engage.JPG
--- @type AI_A2G_SEAD
-- @extends AI.AI_A2G_Engage#AI_A2G_Engage
--- Implements the core functions to SEAD intruders. Use the Engage trigger to intercept intruders.
--
-- ![Process](..\Presentations\AI_GCI\Dia3.JPG)
--
-- The AI_A2G_SEAD is assigned a @{Wrapper.Group} and this must be done before the AI_A2G_SEAD process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_GCI\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- ![Process](..\Presentations\AI_GCI\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_GCI\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_GCI\Dia9.JPG)
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- ![Process](..\Presentations\AI_GCI\Dia10.JPG)
--
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Process](..\Presentations\AI_GCI\Dia13.JPG)
--
-- ## 1. AI_A2G_SEAD constructor
--
-- * @{#AI_A2G_SEAD.New}(): Creates a new AI_A2G_SEAD object.
--
-- ## 3. Set the Range of Engagement
--
-- ![Range](..\Presentations\AI_GCI\Dia11.JPG)
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{AI.AI_GCI#AI_A2G_SEAD.SetEngageRange}() to define that range.
--
-- ## 4. Set the Zone of Engagement
--
-- ![Zone](..\Presentations\AI_GCI\Dia12.JPG)
--
-- An optional @{Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI.AI_Cap#AI_A2G_SEAD.SetEngageZone}() to define that Zone.
--
-- ===
--
-- @field #AI_A2G_SEAD
AI_A2G_SEAD = {
ClassName = "AI_A2G_SEAD",
}
--- Creates a new AI_A2G_SEAD object
-- @param #AI_A2G_SEAD self
-- @param Wrapper.Group#GROUP AIGroup
-- @return #AI_A2G_SEAD
function AI_A2G_SEAD:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2G_ENGAGE:New( AIGroup, EngageMinSpeed, EngageMaxSpeed ) ) -- #AI_A2G_SEAD
return self
end
--- @param #AI_A2G_SEAD self
-- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2G_SEAD:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
local DefenderGroupName = DefenderGroup:GetName()
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
local AttackCount = self.AttackSetUnit:Count()
if AttackCount > 0 then
if DefenderGroup:IsAlive() then
-- Determine the distance to the target.
-- If it is less than 50km, then attack without a route.
-- Otherwise perform a route attack.
local DefenderCoord = DefenderGroup:GetCoordinate()
local TargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
-- if TargetDistance >= 50000 then
local EngageRoute = {}
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
--- Calculate the target route point.
local FromWP = DefenderCoord:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
EngageRoute[#EngageRoute+1] = FromWP
local ToCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
self:SetTargetDistance( ToCoord ) -- For RTB status check
local FromEngageAngle = ToCoord:GetAngleDegrees( ToCoord:GetDirectionVec3( DefenderCoord ) )
--- Create a route point of type air, 50km from the center of the attack point.
local ToWP = ToCoord:Translate( 50000, FromEngageAngle ):WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
self:F( { Angle = FromEngageAngle, ToTargetSpeed = ToTargetSpeed } )
self:F( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToWP
local AttackTasks = {}
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
self:T( { "Engage Unit evaluation:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsGround() } )
local HasRadar = AttackUnit:HasSEAD()
if HasRadar then
self:T( { "Eliminating Unit:", AttackUnit:GetName() } )
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
end
end
end
if #AttackTasks == 0 then
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
else
DefenderGroup:OptionROEOpenFire()
DefenderGroup:OptionROTVertical()
DefenderGroup:OptionKeepWeaponsOnThreat()
--DefenderGroup:OptionRTBAmmo( Weapon.flag.AnyASM )
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self )
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
end
DefenderGroup:Route( EngageRoute, 2 )
-- else
-- local AttackTasks = {}
-- --local AttackUnit = self.AttackSetUnit:GetRandom() -- Wrapper.Unit#UNIT
-- for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
-- if AttackUnit:IsAlive() and AttackUnit:IsGround() then
-- local HasRadar = AttackUnit:HasSEAD()
-- if HasRadar then
-- self:T( { "Eliminating Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsGround() } )
-- AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
-- AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self )
-- end
-- end
-- end
-- local DefenderTask = DefenderGroup:TaskCombo( AttackTasks )
--
-- DefenderGroup:OptionROEOpenFire()
-- DefenderGroup:OptionROTVertical()
-- DefenderGroup:OptionKeepWeaponsOnThreat()
-- DefenderGroup:OptionRTBAmmo( Weapon.flag.AnyASM )
--
-- DefenderGroup:SetTask( DefenderTask, 0 )
-- end
end
else
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
end
end

View File

@@ -475,7 +475,7 @@ function AI_AIR:onafterStatus()
RTB = true
self:SetStatusOff()
end
-- Check if planes went RTB and are out of control.
-- We only check if planes are out of control, when they are in duty.
if self.Controllable:HasTask() == false then
@@ -484,7 +484,7 @@ function AI_AIR:onafterStatus()
not self:Is( "Fuel" ) and
not self:Is( "Damaged" ) and
not self:Is( "Home" ) then
if self.IdleCount >= 2 then
if self.IdleCount >= 10 then
if Damage ~= InitialLife then
self:Damaged()
else
@@ -547,7 +547,7 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
self:E( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
self:ClearTargetDistance()
AIGroup:ClearTasks()
--AIGroup:ClearTasks()
local EngageRoute = {}