Progress, patrolling works. Engagement not yet.

This commit is contained in:
FlightControl 2018-11-07 07:24:45 +01:00
parent 3789674666
commit 0e656c8520
3 changed files with 794 additions and 318 deletions

View File

@ -281,8 +281,8 @@ do -- AI_A2G_DISPATCHER
self:SetDefaultGrouping( 1 )
self:SetDefaultFuelThreshold( 0.15, 0 ) -- 15% of fuel remaining in the tank will trigger the airplane to return to base or refuel.
self:SetDefaultDamageThreshold( 0.4 ) -- When 40% of damage, go RTB.
self:SetDefaultCapTimeInterval( 180, 600 ) -- Between 180 and 600 seconds.
self:SetDefaultCapLimit( 1 ) -- Maximum one CAP per squadron.
self:SetDefaultPatrolTimeInterval( 180, 600 ) -- Between 180 and 600 seconds.
self:SetDefaultPatrolLimit( 1 ) -- Maximum one Patrol per squadron.
self:AddTransition( "Started", "Assign", "Started" )
@ -297,33 +297,33 @@ do -- AI_A2G_DISPATCHER
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param #string PlayerName
self:AddTransition( "*", "CAP", "*" )
self:AddTransition( "*", "Patrol", "*" )
--- CAP Handler OnBefore for AI_A2G_DISPATCHER
-- @function [parent=#AI_A2G_DISPATCHER] OnBeforeCAP
--- Patrol Handler OnBefore for AI_A2G_DISPATCHER
-- @function [parent=#AI_A2G_DISPATCHER] OnBeforePatrol
-- @param #AI_A2G_DISPATCHER self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- CAP Handler OnAfter for AI_A2G_DISPATCHER
-- @function [parent=#AI_A2G_DISPATCHER] OnAfterCAP
--- Patrol Handler OnAfter for AI_A2G_DISPATCHER
-- @function [parent=#AI_A2G_DISPATCHER] OnAfterPatrol
-- @param #AI_A2G_DISPATCHER self
-- @param #string From
-- @param #string Event
-- @param #string To
--- CAP Trigger for AI_A2G_DISPATCHER
-- @function [parent=#AI_A2G_DISPATCHER] CAP
--- Patrol Trigger for AI_A2G_DISPATCHER
-- @function [parent=#AI_A2G_DISPATCHER] Patrol
-- @param #AI_A2G_DISPATCHER self
--- CAP Asynchronous Trigger for AI_A2G_DISPATCHER
-- @function [parent=#AI_A2G_DISPATCHER] __CAP
--- Patrol Asynchronous Trigger for AI_A2G_DISPATCHER
-- @function [parent=#AI_A2G_DISPATCHER] __Patrol
-- @param #AI_A2G_DISPATCHER self
-- @param #number Delay
self:AddTransition( "*", "DEFEND", "*" )
self:AddTransition( "*", "Defend", "*" )
--- GCI Handler OnBefore for AI_A2G_DISPATCHER
-- @function [parent=#AI_A2G_DISPATCHER] OnBeforeGCI
@ -395,7 +395,7 @@ do -- AI_A2G_DISPATCHER
self:SetTacticalDisplay( false )
self.DefenderCAPIndex = 0
self.DefenderPatrolIndex = 0
self:__Start( 5 )
@ -626,8 +626,8 @@ do -- AI_A2G_DISPATCHER
--- Define a border area to simulate a **cold war** scenario.
-- A **cold war** is one where CAP aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border.
-- A **hot war** is one where CAP aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send CAP and GCI aircraft to attack it.
-- A **cold war** is one where Patrol aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border.
-- A **hot war** is one where Patrol aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send Patrol and GCI aircraft to attack it.
-- If it's a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Core.Zone#ZONE_BASE}. This method needs to be used for this.
-- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. Set the noborders parameter to 1
-- @param #AI_A2G_DISPATCHER self
@ -704,45 +704,45 @@ do -- AI_A2G_DISPATCHER
end
--- Set the default CAP time interval for squadrons, which will be used to determine a random CAP timing.
-- The default CAP time interval is between 180 and 600 seconds.
--- Set the default Patrol time interval for squadrons, which will be used to determine a random Patrol timing.
-- The default Patrol time interval is between 180 and 600 seconds.
-- @param #AI_A2G_DISPATCHER self
-- @param #number CapMinSeconds The minimum amount of seconds for the random time interval.
-- @param #number CapMaxSeconds The maximum amount of seconds for the random time interval.
-- @param #number PatrolMinSeconds The minimum amount of seconds for the random time interval.
-- @param #number PatrolMaxSeconds The maximum amount of seconds for the random time interval.
-- @return #AI_A2G_DISPATCHER
-- @usage
--
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
-- A2GDispatcher = AI_A2G_DISPATCHER:New( Detection )
--
-- -- Now Setup the default CAP time interval.
-- A2GDispatcher:SetDefaultCapTimeInterval( 300, 1200 ) -- Between 300 and 1200 seconds.
-- -- Now Setup the default Patrol time interval.
-- A2GDispatcher:SetDefaultPatrolTimeInterval( 300, 1200 ) -- Between 300 and 1200 seconds.
--
function AI_A2G_DISPATCHER:SetDefaultCapTimeInterval( CapMinSeconds, CapMaxSeconds )
function AI_A2G_DISPATCHER:SetDefaultPatrolTimeInterval( PatrolMinSeconds, PatrolMaxSeconds )
self.DefenderDefault.CapMinSeconds = CapMinSeconds
self.DefenderDefault.CapMaxSeconds = CapMaxSeconds
self.DefenderDefault.PatrolMinSeconds = PatrolMinSeconds
self.DefenderDefault.PatrolMaxSeconds = PatrolMaxSeconds
return self
end
--- Set the default CAP limit for squadrons, which will be used to determine how many CAP can be airborne at the same time for the squadron.
-- The default CAP limit is 1 CAP, which means one CAP group being spawned.
--- Set the default Patrol limit for squadrons, which will be used to determine how many Patrol can be airborne at the same time for the squadron.
-- The default Patrol limit is 1 Patrol, which means one Patrol group being spawned.
-- @param #AI_A2G_DISPATCHER self
-- @param #number CapLimit The maximum amount of CAP that can be airborne at the same time for the squadron.
-- @param #number PatrolLimit The maximum amount of Patrol that can be airborne at the same time for the squadron.
-- @return #AI_A2G_DISPATCHER
-- @usage
--
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
-- A2GDispatcher = AI_A2G_DISPATCHER:New( Detection )
--
-- -- Now Setup the default CAP limit.
-- A2GDispatcher:SetDefaultCapLimit( 2 ) -- Maximum 2 CAP per squadron.
-- -- Now Setup the default Patrol limit.
-- A2GDispatcher:SetDefaultPatrolLimit( 2 ) -- Maximum 2 Patrol per squadron.
--
function AI_A2G_DISPATCHER:SetDefaultCapLimit( CapLimit )
function AI_A2G_DISPATCHER:SetDefaultPatrolLimit( PatrolLimit )
self.DefenderDefault.CapLimit = CapLimit
self.DefenderDefault.PatrolLimit = PatrolLimit
return self
end
@ -1046,115 +1046,45 @@ do -- AI_A2G_DISPATCHER
end
--- Set a CAP for a Squadron.
--- Set the squadron Patrol parameters.
-- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name.
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the CAP will be executed.
-- @param #number FloorAltitude The minimum altitude at which the cap can be executed.
-- @param #number CeilingAltitude the maximum altitude at which the cap can be executed.
-- @param #number PatrolMinSpeed The minimum speed at which the cap can be executed.
-- @param #number PatrolMaxSpeed The maximum speed at which the cap can be executed.
-- @param #number EngageMinSpeed The minimum speed at which the engage can be executed.
-- @param #number EngageMaxSpeed The maximum speed at which the engage can be executed.
-- @param #number AltType The altitude type, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude.
-- @return #AI_A2G_DISPATCHER
-- @usage
--
-- -- CAP Squadron execution.
-- CAPZoneEast = ZONE_POLYGON:New( "CAP Zone East", GROUP:FindByName( "CAP Zone East" ) )
-- A2GDispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 )
-- A2GDispatcher:SetSquadronCapInterval( "Mineralnye", 2, 30, 60, 1 )
--
-- CAPZoneWest = ZONE_POLYGON:New( "CAP Zone West", GROUP:FindByName( "CAP Zone West" ) )
-- A2GDispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" )
-- A2GDispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
--
-- CAPZoneMiddle = ZONE:New( "CAP Zone Middle")
-- A2GDispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" )
-- A2GDispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
--
function AI_A2G_DISPATCHER:SetSquadronCap( SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType )
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {}
local DefenderSquadron = self:GetSquadron( SquadronName )
local Cap = self.DefenderSquadrons[SquadronName].Cap
Cap.Name = SquadronName
Cap.Zone = Zone
Cap.FloorAltitude = FloorAltitude
Cap.CeilingAltitude = CeilingAltitude
Cap.PatrolMinSpeed = PatrolMinSpeed
Cap.PatrolMaxSpeed = PatrolMaxSpeed
Cap.EngageMinSpeed = EngageMinSpeed
Cap.EngageMaxSpeed = EngageMaxSpeed
Cap.AltType = AltType
self:SetSquadronCapInterval( SquadronName, self.DefenderDefault.CapLimit, self.DefenderDefault.CapMinSeconds, self.DefenderDefault.CapMaxSeconds, 1 )
self:F( { CAP = { SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType } } )
-- Add the CAP to the EWR network.
local RecceSet = self.Detection:GetDetectionSetGroup()
RecceSet:FilterPrefixes( DefenderSquadron.TemplatePrefixes )
RecceSet:FilterStart()
self.Detection:SetFriendlyPrefixes( DefenderSquadron.TemplatePrefixes )
return self
end
--- Set the squadron CAP parameters.
-- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name.
-- @param #number CapLimit (optional) The maximum amount of CAP groups to be spawned. Note that a CAP is a group, so can consist out of 1 to 4 airplanes. The default is 1 CAP group.
-- @param #number LowInterval (optional) The minimum time boundary in seconds when a new CAP will be spawned. The default is 180 seconds.
-- @param #number HighInterval (optional) The maximum time boundary in seconds when a new CAP will be spawned. The default is 600 seconds.
-- @param #number PatrolLimit (optional) The maximum amount of Patrol groups to be spawned. Note that a Patrol is a group, so can consist out of 1 to 4 airplanes. The default is 1 Patrol group.
-- @param #number LowInterval (optional) The minimum time boundary in seconds when a new Patrol will be spawned. The default is 180 seconds.
-- @param #number HighInterval (optional) The maximum time boundary in seconds when a new Patrol will be spawned. The default is 600 seconds.
-- @param #number Probability Is not in use, you can skip this parameter.
-- @return #AI_A2G_DISPATCHER
-- @usage
--
-- -- CAP Squadron execution.
-- CAPZoneEast = ZONE_POLYGON:New( "CAP Zone East", GROUP:FindByName( "CAP Zone East" ) )
-- A2GDispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 )
-- A2GDispatcher:SetSquadronCapInterval( "Mineralnye", 2, 30, 60, 1 )
--
-- CAPZoneWest = ZONE_POLYGON:New( "CAP Zone West", GROUP:FindByName( "CAP Zone West" ) )
-- A2GDispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" )
-- A2GDispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
--
-- CAPZoneMiddle = ZONE:New( "CAP Zone Middle")
-- A2GDispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" )
-- A2GDispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
-- -- Patrol Squadron execution.
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
-- A2GDispatcher:SetSquadronSeadPatrol( "Mineralnye", PatrolZoneEast, 4000, 10000, 500, 600, 800, 900 )
-- A2GDispatcher:SetSquadronPatrolInterval( "Mineralnye", 2, 30, 60, 1 )
--
function AI_A2G_DISPATCHER:SetSquadronCapInterval( SquadronName, CapLimit, LowInterval, HighInterval, Probability )
function AI_A2G_DISPATCHER:SetSquadronPatrolInterval( SquadronName, PatrolLimit, LowInterval, HighInterval, Probability, DefenseTaskType )
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {}
local DefenderSquadron = self:GetSquadron( SquadronName )
local Cap = self.DefenderSquadrons[SquadronName].Cap
if Cap then
Cap.LowInterval = LowInterval or 180
Cap.HighInterval = HighInterval or 600
Cap.Probability = Probability or 1
Cap.CapLimit = CapLimit or 1
Cap.Scheduler = Cap.Scheduler or SCHEDULER:New( self )
local Scheduler = Cap.Scheduler -- Core.Scheduler#SCHEDULER
local ScheduleID = Cap.ScheduleID
local Variance = ( Cap.HighInterval - Cap.LowInterval ) / 2
local Repeat = Cap.LowInterval + Variance
local Patrol = DefenderSquadron[DefenseTaskType]
if Patrol then
Patrol.LowInterval = LowInterval or 180
Patrol.HighInterval = HighInterval or 600
Patrol.Probability = Probability or 1
Patrol.PatrolLimit = PatrolLimit or 1
Patrol.Scheduler = Patrol.Scheduler or SCHEDULER:New( self )
local Scheduler = Patrol.Scheduler -- Core.Scheduler#SCHEDULER
local ScheduleID = Patrol.ScheduleID
local Variance = ( Patrol.HighInterval - Patrol.LowInterval ) / 2
local Repeat = Patrol.LowInterval + Variance
local Randomization = Variance / Repeat
local Start = math.random( 1, Cap.HighInterval )
local Start = math.random( 1, Patrol.HighInterval )
if ScheduleID then
Scheduler:Stop( ScheduleID )
end
Cap.ScheduleID = Scheduler:Schedule( self, self.SchedulerCAP, { SquadronName }, Start, Repeat, Randomization )
Patrol.ScheduleID = Scheduler:Schedule( self, self.SchedulerPatrol, { SquadronName }, Start, Repeat, Randomization )
else
error( "This squadron does not exist:" .. SquadronName )
end
@ -1165,16 +1095,16 @@ do -- AI_A2G_DISPATCHER
-- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name.
-- @return #AI_A2G_DISPATCHER
function AI_A2G_DISPATCHER:GetCAPDelay( SquadronName )
function AI_A2G_DISPATCHER:GetPatrolDelay( SquadronName )
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {}
self.DefenderSquadrons[SquadronName].Patrol = self.DefenderSquadrons[SquadronName].Patrol or {}
local DefenderSquadron = self:GetSquadron( SquadronName )
local Cap = self.DefenderSquadrons[SquadronName].Cap
if Cap then
return math.random( Cap.LowInterval, Cap.HighInterval )
local Patrol = self.DefenderSquadrons[SquadronName].Patrol
if Patrol then
return math.random( Patrol.LowInterval, Patrol.HighInterval )
else
error( "This squadron does not exist:" .. SquadronName )
end
@ -1184,28 +1114,27 @@ do -- AI_A2G_DISPATCHER
-- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name.
-- @return #table DefenderSquadron
function AI_A2G_DISPATCHER:CanCAP( SquadronName )
function AI_A2G_DISPATCHER:CanPatrol( SquadronName, DefenseTaskType )
self:F({SquadronName = SquadronName})
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {}
local DefenderSquadron = self:GetSquadron( SquadronName )
if DefenderSquadron.Captured == false then -- We can only spawn new CAP if the base has not been captured.
if DefenderSquadron.Captured == false then -- We can only spawn new Patrol if the base has not been captured.
if ( not DefenderSquadron.ResourceCount ) or ( DefenderSquadron.ResourceCount and DefenderSquadron.ResourceCount > 0 ) then -- And, if there are sufficient resources.
local Cap = DefenderSquadron.Cap
if Cap then
local CapCount = self:CountCapAirborne( SquadronName )
self:F( { CapCount = CapCount } )
if CapCount < Cap.CapLimit then
local Patrol = DefenderSquadron[DefenseTaskType]
if Patrol and Patrol.Patrol == true then
local PatrolCount = self:CountPatrolAirborne( SquadronName, DefenseTaskType )
self:F( { PatrolCount = PatrolCount, PatrolLimit = Patrol.PatrolLimit, PatrolProbability = Patrol.Probability } )
if PatrolCount < Patrol.PatrolLimit then
local Probability = math.random()
if Probability <= Cap.Probability then
return DefenderSquadron
if Probability <= Patrol.Probability then
return DefenderSquadron, Patrol
end
end
else
self:F( "No patrol for " .. SquadronName )
end
end
end
@ -1217,7 +1146,7 @@ do -- AI_A2G_DISPATCHER
-- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name.
-- @return #table DefenderSquadron
function AI_A2G_DISPATCHER:CanDEFEND( SquadronName, DefenseTaskType )
function AI_A2G_DISPATCHER:CanDefend( SquadronName, DefenseTaskType )
self:F({SquadronName = SquadronName, DefenseTaskType})
local DefenderSquadron = self:GetSquadron( SquadronName )
@ -1225,7 +1154,7 @@ do -- AI_A2G_DISPATCHER
if DefenderSquadron.Captured == false then -- We can only spawn new defense if the home airbase has not been captured.
if ( not DefenderSquadron.ResourceCount ) or ( DefenderSquadron.ResourceCount and DefenderSquadron.ResourceCount > 0 ) then -- And, if there are sufficient resources.
if DefenderSquadron[DefenseTaskType] then
if DefenderSquadron[DefenseTaskType] and DefenderSquadron[DefenseTaskType].Defend == true then
return DefenderSquadron, DefenderSquadron[DefenseTaskType]
end
end
@ -1257,11 +1186,55 @@ do -- AI_A2G_DISPATCHER
Sead.Name = SquadronName
Sead.EngageMinSpeed = EngageMinSpeed
Sead.EngageMaxSpeed = EngageMaxSpeed
Sead.Defend = true
self:F( { Sead = Sead } )
end
--- Set a Sead patrol for a Squadron.
-- The Sead patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded.
-- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name.
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed.
-- @param #number FloorAltitude The minimum altitude at which the cap can be executed.
-- @param #number CeilingAltitude the maximum altitude at which the cap can be executed.
-- @param #number PatrolMinSpeed The minimum speed at which the cap can be executed.
-- @param #number PatrolMaxSpeed The maximum speed at which the cap can be executed.
-- @param #number EngageMinSpeed The minimum speed at which the engage can be executed.
-- @param #number EngageMaxSpeed The maximum speed at which the engage can be executed.
-- @param #number AltType The altitude type, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude.
-- @return #AI_A2G_DISPATCHER
-- @usage
--
-- -- Sead Patrol Squadron execution.
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
-- A2GDispatcher:SetSquadronSeadPatrol( "Mineralnye", PatrolZoneEast, 4000, 10000, 500, 600, 800, 900 )
--
function AI_A2G_DISPATCHER:SetSquadronSeadPatrol( SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType )
local DefenderSquadron = self:GetSquadron( SquadronName )
DefenderSquadron.SEAD = DefenderSquadron.SEAD or {}
local SeadPatrol = DefenderSquadron.SEAD
SeadPatrol.Name = SquadronName
SeadPatrol.Zone = Zone
SeadPatrol.FloorAltitude = FloorAltitude
SeadPatrol.CeilingAltitude = CeilingAltitude
SeadPatrol.PatrolMinSpeed = PatrolMinSpeed
SeadPatrol.PatrolMaxSpeed = PatrolMaxSpeed
SeadPatrol.EngageMinSpeed = EngageMinSpeed
SeadPatrol.EngageMaxSpeed = EngageMaxSpeed
SeadPatrol.AltType = AltType
SeadPatrol.Patrol = true
self:SetSquadronPatrolInterval( SquadronName, self.DefenderDefault.PatrolLimit, self.DefenderDefault.PatrolMinSeconds, self.DefenderDefault.PatrolMaxSeconds, 1, "SEAD" )
self:F( { Sead = SeadPatrol } )
end
---
-- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name.
@ -1285,10 +1258,54 @@ do -- AI_A2G_DISPATCHER
Cas.Name = SquadronName
Cas.EngageMinSpeed = EngageMinSpeed
Cas.EngageMaxSpeed = EngageMaxSpeed
Cas.Defend = true
self:F( { Cas = Cas } )
end
--- Set a Cas patrol for a Squadron.
-- The Cas patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded.
-- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name.
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed.
-- @param #number FloorAltitude The minimum altitude at which the cap can be executed.
-- @param #number CeilingAltitude the maximum altitude at which the cap can be executed.
-- @param #number PatrolMinSpeed The minimum speed at which the cap can be executed.
-- @param #number PatrolMaxSpeed The maximum speed at which the cap can be executed.
-- @param #number EngageMinSpeed The minimum speed at which the engage can be executed.
-- @param #number EngageMaxSpeed The maximum speed at which the engage can be executed.
-- @param #number AltType The altitude type, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude.
-- @return #AI_A2G_DISPATCHER
-- @usage
--
-- -- Cas Patrol Squadron execution.
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
-- A2GDispatcher:SetSquadronCasPatrol( "Mineralnye", PatrolZoneEast, 4000, 10000, 500, 600, 800, 900 )
--
function AI_A2G_DISPATCHER:SetSquadronCasPatrol( SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType )
local DefenderSquadron = self:GetSquadron( SquadronName )
DefenderSquadron.CAS = DefenderSquadron.CAS or {}
local CasPatrol = DefenderSquadron.CAS
CasPatrol.Name = SquadronName
CasPatrol.Zone = Zone
CasPatrol.FloorAltitude = FloorAltitude
CasPatrol.CeilingAltitude = CeilingAltitude
CasPatrol.PatrolMinSpeed = PatrolMinSpeed
CasPatrol.PatrolMaxSpeed = PatrolMaxSpeed
CasPatrol.EngageMinSpeed = EngageMinSpeed
CasPatrol.EngageMaxSpeed = EngageMaxSpeed
CasPatrol.AltType = AltType
CasPatrol.Patrol = true
self:SetSquadronPatrolInterval( SquadronName, self.DefenderDefault.PatrolLimit, self.DefenderDefault.PatrolMinSeconds, self.DefenderDefault.PatrolMaxSeconds, 1, "CAS" )
self:F( { Cas = CasPatrol } )
end
---
-- @param #AI_A2G_DISPATCHER self
@ -1313,10 +1330,54 @@ do -- AI_A2G_DISPATCHER
Bai.Name = SquadronName
Bai.EngageMinSpeed = EngageMinSpeed
Bai.EngageMaxSpeed = EngageMaxSpeed
Bai.Defend = true
self:F( { Bai = Bai } )
end
--- Set a Bai patrol for a Squadron.
-- The Bai patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded.
-- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name.
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed.
-- @param #number FloorAltitude The minimum altitude at which the cap can be executed.
-- @param #number CeilingAltitude the maximum altitude at which the cap can be executed.
-- @param #number PatrolMinSpeed The minimum speed at which the cap can be executed.
-- @param #number PatrolMaxSpeed The maximum speed at which the cap can be executed.
-- @param #number EngageMinSpeed The minimum speed at which the engage can be executed.
-- @param #number EngageMaxSpeed The maximum speed at which the engage can be executed.
-- @param #number AltType The altitude type, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude.
-- @return #AI_A2G_DISPATCHER
-- @usage
--
-- -- Bai Patrol Squadron execution.
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
-- A2GDispatcher:SetSquadronBaiPatrol( "Mineralnye", PatrolZoneEast, 4000, 10000, 500, 600, 800, 900 )
--
function AI_A2G_DISPATCHER:SetSquadronBaiPatrol( SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType )
local DefenderSquadron = self:GetSquadron( SquadronName )
DefenderSquadron.BAI = DefenderSquadron.BAI or {}
local BaiPatrol = DefenderSquadron.BAI
BaiPatrol.Name = SquadronName
BaiPatrol.Zone = Zone
BaiPatrol.FloorAltitude = FloorAltitude
BaiPatrol.CeilingAltitude = CeilingAltitude
BaiPatrol.PatrolMinSpeed = PatrolMinSpeed
BaiPatrol.PatrolMaxSpeed = PatrolMaxSpeed
BaiPatrol.EngageMinSpeed = EngageMinSpeed
BaiPatrol.EngageMaxSpeed = EngageMaxSpeed
BaiPatrol.AltType = AltType
BaiPatrol.Patrol = true
self:SetSquadronPatrolInterval( SquadronName, self.DefenderDefault.PatrolLimit, self.DefenderDefault.PatrolMinSeconds, self.DefenderDefault.PatrolMaxSeconds, 1, "BAI" )
self:F( { Bai = BaiPatrol } )
end
--- Defines the default amount of extra planes that will take-off as part of the defense system.
-- @param #AI_A2G_DISPATCHER self
@ -1399,7 +1460,7 @@ do -- AI_A2G_DISPATCHER
--- Sets the default grouping of new airplanes spawned.
-- Grouping will trigger how new airplanes will be grouped if more than one airplane is spawned for defense.
-- @param #AI_A2G_DISPATCHER self
-- @param #number Grouping The level of grouping that will be applied of the CAP or GCI defenders.
-- @param #number Grouping The level of grouping that will be applied of the Patrol or GCI defenders.
-- @usage:
--
-- local A2GDispatcher = AI_A2G_DISPATCHER:New( ... )
@ -1421,7 +1482,7 @@ do -- AI_A2G_DISPATCHER
-- Grouping will trigger how new airplanes will be grouped if more than one airplane is spawned for defense.
-- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The name of the squadron.
-- @param #number Grouping The level of grouping that will be applied of the CAP or GCI defenders.
-- @param #number Grouping The level of grouping that will be applied of the Patrol or GCI defenders.
-- @usage:
--
-- local A2GDispatcher = AI_A2G_DISPATCHER:New( ... )
@ -2064,48 +2125,23 @@ do -- AI_A2G_DISPATCHER
end
--- Creates an SWEEP task when there are targets for it.
-- @param #AI_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
-- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units.
-- @return #nil If there are no targets to be set.
function AI_A2G_DISPATCHER:EvaluateSWEEP( DetectedItem )
self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone
if DetectedItem.IsDetected == false then
-- Here we're doing something advanced... We're copying the DetectedSet.
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
---
-- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:CountCapAirborne( SquadronName )
function AI_A2G_DISPATCHER:CountPatrolAirborne( SquadronName, DefenseTaskType )
local CapCount = 0
local PatrolCount = 0
local DefenderSquadron = self.DefenderSquadrons[SquadronName]
if DefenderSquadron then
for AIGroup, DefenderTask in pairs( self:GetDefenderTasks() ) do
if DefenderTask.SquadronName == SquadronName then
if DefenderTask.Type == "CAP" then
if DefenderTask.Type == DefenseTaskType then
if AIGroup:IsAlive() then
-- Check if the CAP is patrolling or engaging. If not, this is not a valid CAP, even if it is alive!
-- The CAP could be damaged, lost control, or out of fuel!
-- Check if the Patrol is patrolling or engaging. If not, this is not a valid Patrol, even if it is alive!
-- The Patrol could be damaged, lost control, or out of fuel!
if DefenderTask.Fsm:Is( "Patrolling" ) or DefenderTask.Fsm:Is( "Engaging" ) or DefenderTask.Fsm:Is( "Refuelling" )
or DefenderTask.Fsm:Is( "Started" ) then
CapCount = CapCount + 1
PatrolCount = PatrolCount + 1
end
end
end
@ -2113,7 +2149,7 @@ do -- AI_A2G_DISPATCHER
end
end
return CapCount
return PatrolCount
end
@ -2173,7 +2209,7 @@ do -- AI_A2G_DISPATCHER
local DefenderTask = self:GetDefenderTask( FriendlyGroup )
if DefenderTask then
-- The Task should be SEAD
if DefenderTask.Type == "SEAD" or DefenderTask.Type == "CAS" or DefenderTask.Type == "BAI" then
if true then -- TODO: fix this to the correct DefenderTaskType
-- If there is no target, then add the AIGroup to the ResultAIGroups for Engagement to the AttackerSet
if DefenderTask.Target == nil then
if DefenderTask.Fsm:Is( "Returning" )
@ -2196,50 +2232,6 @@ do -- AI_A2G_DISPATCHER
end
---
-- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:CountDefendersToBeEngaged( AttackerDetection, DefenderCount )
local Friendlies = nil
local AttackerSet = AttackerDetection.Set
local AttackerCount = AttackerSet:Count()
local DefenderFriendlies = self:GetAIFriendliesNearBy( AttackerDetection )
for FriendlyDistance, AIFriendly in UTILS.spairs( DefenderFriendlies or {} ) do
-- We only allow to ENGAGE targets as long as the Units on both sides are balanced.
if AttackerCount > DefenderCount then
local Friendly = AIFriendly:GetGroup() -- Wrapper.Group#GROUP
if Friendly and Friendly:IsAlive() then
-- Ok, so we have a friendly near the potential target.
-- Now we need to check if the AIGroup has a Task.
local DefenderTask = self:GetDefenderTask( Friendly )
if DefenderTask then
-- The Task should be CAP or GCI
if DefenderTask.Type == "CAP" or DefenderTask.Type == "GCI" then
-- If there is no target, then add the AIGroup to the ResultAIGroups for Engagement to the AttackerSet
if DefenderTask.Target == nil then
if DefenderTask.Fsm:Is( "Returning" )
or DefenderTask.Fsm:Is( "Patrolling" ) then
Friendlies = Friendlies or {}
Friendlies[Friendly] = Friendly
DefenderCount = DefenderCount + Friendly:GetSize()
self:F( { Friendly = Friendly:GetName(), FriendlyDistance = FriendlyDistance } )
end
end
end
end
end
else
break
end
end
return Friendlies
end
---
-- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:ResourceActivate( DefenderSquadron, DefendersNeeded )
@ -2251,7 +2243,7 @@ do -- AI_A2G_DISPATCHER
if self:IsSquadronVisible( SquadronName ) then
-- Here we CAP the new planes.
-- Here we Patrol the new planes.
-- The Resources table is filled in advance.
local TemplateID = math.random( 1, #DefenderSquadron.Spawn ) -- Choose the template.
@ -2261,20 +2253,20 @@ do -- AI_A2G_DISPATCHER
-- New we will form the group to spawn in.
-- We search for the first free resource matching the template.
local DefenderUnitIndex = 1
local DefenderCAPTemplate = nil
local DefenderPatrolTemplate = nil
local DefenderName = nil
for GroupName, DefenderGroup in pairs( DefenderSquadron.Resources[TemplateID] or {} ) do
self:F( { GroupName = GroupName } )
local DefenderTemplate = _DATABASE:GetGroupTemplate( GroupName )
if DefenderUnitIndex == 1 then
DefenderCAPTemplate = UTILS.DeepCopy( DefenderTemplate )
self.DefenderCAPIndex = self.DefenderCAPIndex + 1
DefenderCAPTemplate.name = SquadronName .. "#" .. self.DefenderCAPIndex .. "#" .. GroupName
DefenderName = DefenderCAPTemplate.name
DefenderPatrolTemplate = UTILS.DeepCopy( DefenderTemplate )
self.DefenderPatrolIndex = self.DefenderPatrolIndex + 1
DefenderPatrolTemplate.name = SquadronName .. "#" .. self.DefenderPatrolIndex .. "#" .. GroupName
DefenderName = DefenderPatrolTemplate.name
else
-- Add the unit in the template to the DefenderCAPTemplate.
-- Add the unit in the template to the DefenderPatrolTemplate.
local DefenderUnitTemplate = DefenderTemplate.units[1]
DefenderCAPTemplate.units[DefenderUnitIndex] = DefenderUnitTemplate
DefenderPatrolTemplate.units[DefenderUnitIndex] = DefenderUnitTemplate
end
DefenderUnitIndex = DefenderUnitIndex + 1
DefenderSquadron.Resources[TemplateID][GroupName] = nil
@ -2284,15 +2276,15 @@ do -- AI_A2G_DISPATCHER
end
if DefenderCAPTemplate then
if DefenderPatrolTemplate then
local TakeoffMethod = self:GetSquadronTakeoff( SquadronName )
local SpawnGroup = GROUP:Register( DefenderName )
DefenderCAPTemplate.lateActivation = nil
DefenderCAPTemplate.uncontrolled = nil
DefenderPatrolTemplate.lateActivation = nil
DefenderPatrolTemplate.uncontrolled = nil
local Takeoff = self:GetSquadronTakeoff( SquadronName )
DefenderCAPTemplate.route.points[1].type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
DefenderCAPTemplate.route.points[1].action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action
local Defender = _DATABASE:Spawn( DefenderCAPTemplate )
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 )
return Defender, DefenderGrouping
@ -2316,72 +2308,65 @@ do -- AI_A2G_DISPATCHER
---
-- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:onafterCAP( From, Event, To, SquadronName )
function AI_A2G_DISPATCHER:onafterPatrol( From, Event, To, SquadronName, DefenseTaskType )
self:F({SquadronName = SquadronName})
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {}
local DefenderSquadron = self:CanCAP( SquadronName )
local DefenderSquadron, Patrol = self:CanPatrol( SquadronName, DefenseTaskType )
if DefenderSquadron then
local Cap = DefenderSquadron.Cap
if Cap then
if Patrol then
local DefenderCAP, DefenderGrouping = self:ResourceActivate( DefenderSquadron )
if DefenderCAP then
local Fsm = AI_A2G_CAP:New( DefenderCAP, Cap.Zone, Cap.FloorAltitude, Cap.CeilingAltitude, Cap.PatrolMinSpeed, Cap.PatrolMaxSpeed, Cap.EngageMinSpeed, Cap.EngageMaxSpeed, Cap.AltType )
Fsm:SetDispatcher( self )
Fsm:SetHomeAirbase( DefenderSquadron.Airbase )
Fsm:SetFuelThreshold( DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold, 60 )
Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold )
Fsm:SetDisengageRadius( self.DisengageRadius )
Fsm:SetTanker( DefenderSquadron.TankerName or self.DefenderDefault.TankerName )
Fsm:Start()
self:SetDefenderTask( SquadronName, DefenderCAP, "CAP", Fsm )
local DefenderPatrol, DefenderGrouping = self:ResourceActivate( DefenderSquadron )
if DefenderPatrol then
function Fsm:onafterTakeoff( Defender, From, Event, To )
self:F({"CAP Birth", Defender:GetName()})
--self:GetParent(self).onafterBirth( self, Defender, From, Event, To )
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
local Fsm = AI_A2G_PATROL:New( DefenderPatrol, Patrol.Zone, Patrol.FloorAltitude, Patrol.CeilingAltitude, Patrol.PatrolMinSpeed, Patrol.PatrolMaxSpeed, Patrol.EngageMinSpeed, Patrol.EngageMaxSpeed, Patrol.AltType )
Fsm:SetDispatcher( self )
Fsm:SetHomeAirbase( DefenderSquadron.Airbase )
Fsm:SetFuelThreshold( DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold, 60 )
Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold )
Fsm:SetDisengageRadius( self.DisengageRadius )
Fsm:SetTanker( DefenderSquadron.TankerName or self.DefenderDefault.TankerName )
Fsm:Start()
if Squadron then
Fsm:__Patrol( 2 ) -- Start Patrolling
end
self:SetDefenderTask( SquadronName, DefenderPatrol, DefenseTaskType, Fsm )
function Fsm:onafterTakeoff( Defender, From, Event, To )
self:F({"Patrol Birth", Defender:GetName()})
--self:GetParent(self).onafterBirth( self, Defender, From, Event, To )
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
if Squadron then
Fsm:__Patrol( 2 ) -- Start Patrolling
end
function Fsm:onafterRTB( Defender, From, Event, To )
self:F({"CAP RTB", Defender:GetName()})
self:GetParent(self).onafterRTB( self, Defender, From, Event, To )
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
Dispatcher:ClearDefenderTaskTarget( Defender )
end
function Fsm:onafterRTB( Defender, From, Event, To )
self:F({"Patrol RTB", Defender:GetName()})
self:GetParent(self).onafterRTB( self, Defender, From, Event, To )
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
Dispatcher:ClearDefenderTaskTarget( Defender )
end
--- @param #AI_A2G_DISPATCHER self
function Fsm:onafterHome( Defender, From, Event, To, Action )
self:F({"Patrol Home", Defender:GetName()})
self:GetParent(self).onafterHome( self, Defender, From, Event, To )
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
if Action and Action == "Destroy" then
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
Defender:Destroy()
end
--- @param #AI_A2G_DISPATCHER self
function Fsm:onafterHome( Defender, From, Event, To, Action )
self:F({"CAP Home", Defender:GetName()})
self:GetParent(self).onafterHome( self, Defender, From, Event, To )
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
if Action and Action == "Destroy" then
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
Defender:Destroy()
end
if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2G_DISPATCHER.Landing.NearAirbase then
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
Defender:Destroy()
self:ParkDefender( Squadron, Defender )
end
if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2G_DISPATCHER.Landing.NearAirbase then
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
Defender:Destroy()
self:ParkDefender( Squadron, Defender )
end
end
end
@ -2392,7 +2377,7 @@ do -- AI_A2G_DISPATCHER
---
-- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:onafterENGAGE( From, Event, To, AttackerDetection, Defenders )
function AI_A2G_DISPATCHER:onafterEngage( From, Event, To, AttackerDetection, Defenders )
if Defenders then
@ -2409,7 +2394,7 @@ do -- AI_A2G_DISPATCHER
---
-- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:onafterDEFEND( From, Event, To, AttackerDetection, DefendersMissing, DefenderFriendlies, DefenseTaskType )
function AI_A2G_DISPATCHER:onafterDefend( From, Event, To, AttackerDetection, DefendersMissing, DefenderFriendlies, DefenseTaskType )
self:F( { From, Event, To, AttackerDetection.Index, DefendersMissing, DefenderFriendlies } )
@ -2469,7 +2454,7 @@ do -- AI_A2G_DISPATCHER
if ClosestDefenderSquadronName then
local DefenderSquadron, Defense = self:CanDEFEND( ClosestDefenderSquadronName, DefenseTaskType )
local DefenderSquadron, Defense = self:CanDefend( ClosestDefenderSquadronName, DefenseTaskType )
if Defense then
@ -2757,7 +2742,7 @@ do -- AI_A2G_DISPATCHER
local DistanceProbability = ( self.DefenseDistance / EvaluateDistance * self.DefenseReactivity )
local DefenseProbability = math.random()
self:F({DistanceProbability=DistanceProbability,DefenseProbability=DefenseProbability})
self:F( { DistanceProbability = DistanceProbability, DefenseProbability = DefenseProbability } )
if DefenseProbability <= DistanceProbability / ( 300 / 30 ) then
DefenseCoordinate = EvaluateCoordinate
@ -2770,7 +2755,7 @@ do -- AI_A2G_DISPATCHER
local DefendersMissing, Friendlies = self:Evaluate_SEAD( DetectedItem ) -- Returns a SET_UNIT with the SEAD targets to be engaged...
if DefendersMissing and DefendersMissing > 0 then
self:F( { SeadGroups = Friendlies } )
self:__DEFEND( Delay, DetectedItem, DefendersMissing, Friendlies, "SEAD", DefenseCoordinate )
self:__Defend( Delay, DetectedItem, DefendersMissing, Friendlies, "SEAD", DefenseCoordinate )
Delay = Delay + 1
end
end
@ -2779,7 +2764,7 @@ do -- AI_A2G_DISPATCHER
local DefendersMissing, Friendlies = self:Evaluate_CAS( DetectedItem ) -- Returns a SET_UNIT with the CAS targets to be engaged...
if DefendersMissing and DefendersMissing > 0 then
self:F( { CasGroups = Friendlies } )
self:__DEFEND( Delay, DetectedItem, DefendersMissing, Friendlies, "CAS", DefenseCoordinate )
self:__Defend( Delay, DetectedItem, DefendersMissing, Friendlies, "CAS", DefenseCoordinate )
Delay = Delay + 1
end
end
@ -2788,7 +2773,7 @@ do -- AI_A2G_DISPATCHER
local DefendersMissing, Friendlies = self:Evaluate_BAI( DetectedItem ) -- Returns a SET_UNIT with the CAS targets to be engaged...
if DefendersMissing and DefendersMissing > 0 then
self:F( { BaiGroups = Friendlies } )
self:__DEFEND( Delay, DetectedItem, DefendersMissing, Friendlies, "BAI", DefenseCoordinate )
self:__Defend( Delay, DetectedItem, DefendersMissing, Friendlies, "BAI", DefenseCoordinate )
Delay = Delay + 1
end
end
@ -2952,11 +2937,13 @@ do
return FriendliesCount, FriendlyTypesReport
end
--- Schedules a new CAP for the given SquadronName.
--- Schedules a new Patrol for the given SquadronName.
-- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name.
function AI_A2G_DISPATCHER:SchedulerCAP( SquadronName )
self:CAP( SquadronName )
function AI_A2G_DISPATCHER:SchedulerPatrol( SquadronName )
local PatrolTaskTypes = { "SEAD", "CAS", "BAI" }
local PatrolTaskType = PatrolTaskTypes[math.random(1,3)]
self:Patrol( SquadronName, PatrolTaskType )
end
end
@ -3122,7 +3109,7 @@ do
--
-- You can change or add a CAP zone by using the inherited methods from AI\_A2G\_DISPATCHER:
--
-- The method @{#AI_A2G_DISPATCHER.SetSquadronCap}() defines a CAP execution for a squadron.
-- The method @{#AI_A2G_DISPATCHER.SetSquadronPatrol}() defines a CAP execution for a squadron.
--
-- Setting-up a CAP zone also requires specific parameters:
--
@ -3133,14 +3120,14 @@ do
--
-- These define how the squadron will perform the CAP while partrolling. Different terrain types requires different types of CAP.
--
-- The @{#AI_A2G_DISPATCHER.SetSquadronCapInterval}() method specifies **how much** and **when** CAP flights will takeoff.
-- The @{#AI_A2G_DISPATCHER.SetSquadronPatrolInterval}() method specifies **how much** and **when** CAP flights will takeoff.
--
-- It is recommended not to overload the air defense with CAP flights, as these will decrease the performance of the overall system.
--
-- For example, the following setup will create a CAP for squadron "Sochi":
--
-- A2GDispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" )
-- A2GDispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 )
-- A2GDispatcher:SetSquadronPatrol( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" )
-- A2GDispatcher:SetSquadronPatrolInterval( "Sochi", 2, 30, 120, 1 )
--
-- ### 2.4) Each airbase will perform GCI when required, with the following parameters:
--
@ -3227,8 +3214,8 @@ do
-- @param #AI_A2G_GCICAP self
-- @param #string EWRPrefixes A list of prefixes that of groups that setup the Early Warning Radar network.
-- @param #string TemplatePrefixes A list of template prefixes.
-- @param #string CapPrefixes A list of CAP zone prefixes (polygon zones).
-- @param #number CapLimit A number of how many CAP maximum will be spawned.
-- @param #string PatrolPrefixes A list of CAP zone prefixes (polygon zones).
-- @param #number PatrolLimit A number of how many CAP maximum will be spawned.
-- @param #number GroupingRadius The radius in meters wherein detected planes are being grouped as one target area.
-- For airplanes, 6000 (6km) is recommended, and is also the default value of this parameter.
-- @param #number EngageRadius The radius in meters wherein detected airplanes will be engaged by airborne defenders without a task.
@ -3308,7 +3295,7 @@ do
--
-- A2GDispatcher = AI_A2G_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, nil, nil, nil, nil, nil, 30 )
--
function AI_A2G_GCICAP:New( EWRPrefixes, TemplatePrefixes, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, ResourceCount )
function AI_A2G_GCICAP:New( EWRPrefixes, TemplatePrefixes, PatrolPrefixes, PatrolLimit, GroupingRadius, EngageRadius, GciRadius, ResourceCount )
local EWRSetGroup = SET_GROUP:New()
EWRSetGroup:FilterPrefixes( EWRPrefixes )
@ -3370,7 +3357,7 @@ do
-- CAP will be launched from there.
self.CAPTemplates = SET_GROUP:New()
self.CAPTemplates:FilterPrefixes( CapPrefixes )
self.CAPTemplates:FilterPrefixes( PatrolPrefixes )
self.CAPTemplates:FilterOnce()
self:I( "Setting up CAP ..." )
@ -3396,8 +3383,8 @@ do
end
if AirbaseClosest then
self:I( { CAPAirbase = AirbaseClosest:GetName() } )
self:SetSquadronCap( AirbaseClosest:GetName(), CAPZone, 6000, 10000, 500, 800, 800, 1200, "RADIO" )
self:SetSquadronCapInterval( AirbaseClosest:GetName(), CapLimit, 300, 600, 1 )
self:SetSquadronPatrol( AirbaseClosest:GetName(), CAPZone, 6000, 10000, 500, 800, 800, 1200, "RADIO" )
self:SetSquadronPatrolInterval( AirbaseClosest:GetName(), PatrolLimit, 300, 600, 1 )
end
end
@ -3432,8 +3419,8 @@ do
-- @param #string EWRPrefixes A list of prefixes that of groups that setup the Early Warning Radar network.
-- @param #string TemplatePrefixes A list of template prefixes.
-- @param #string BorderPrefix A Border Zone Prefix.
-- @param #string CapPrefixes A list of CAP zone prefixes (polygon zones).
-- @param #number CapLimit A number of how many CAP maximum will be spawned.
-- @param #string PatrolPrefixes A list of CAP zone prefixes (polygon zones).
-- @param #number PatrolLimit A number of how many CAP maximum will be spawned.
-- @param #number GroupingRadius The radius in meters wherein detected planes are being grouped as one target area.
-- For airplanes, 6000 (6km) is recommended, and is also the default value of this parameter.
-- @param #number EngageRadius The radius in meters wherein detected airplanes will be engaged by airborne defenders without a task.
@ -3522,9 +3509,9 @@ do
--
-- A2GDispatcher = AI_A2G_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", nil, nil, nil, nil, nil, 30 )
--
function AI_A2G_GCICAP:NewWithBorder( EWRPrefixes, TemplatePrefixes, BorderPrefix, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, ResourceCount )
function AI_A2G_GCICAP:NewWithBorder( EWRPrefixes, TemplatePrefixes, BorderPrefix, PatrolPrefixes, PatrolLimit, GroupingRadius, EngageRadius, GciRadius, ResourceCount )
local self = AI_A2G_GCICAP:New( EWRPrefixes, TemplatePrefixes, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, ResourceCount )
local self = AI_A2G_GCICAP:New( EWRPrefixes, TemplatePrefixes, PatrolPrefixes, PatrolLimit, GroupingRadius, EngageRadius, GciRadius, ResourceCount )
if BorderPrefix then
self:SetBorderZone( ZONE_POLYGON:New( BorderPrefix, GROUP:FindByName( BorderPrefix ) ) )

View File

@ -0,0 +1,488 @@
--- **AI** -- Models the process of A2G patrolling and engaging ground targets for airplanes and helicopters.
--
-- ===
--
-- ### Author: **FlightControl**
--
-- ===
--
-- @module AI.AI_A2G_Patrol
-- @image AI_Combat_Air_Patrol.JPG
--- @type AI_A2G_PATROL
-- @extends AI.AI_A2A_Patrol#AI_A2A_PATROL
--- The AI_A2G_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
--
-- ![Process](..\Presentations\AI_CAP\Dia3.JPG)
--
-- The AI_A2G_PATROL is assigned a @{Wrapper.Group} and this must be done before the AI_A2G_PATROL process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_CAP\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_CAP\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_CAP\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia9.JPG)
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- ![Process](..\Presentations\AI_CAP\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_CAP\Dia13.JPG)
--
-- ## 1. AI_A2G_PATROL constructor
--
-- * @{#AI_A2G_PATROL.New}(): Creates a new AI_A2G_PATROL object.
--
-- ## 2. AI_A2G_PATROL is a FSM
--
-- ![Process](..\Presentations\AI_CAP\Dia2.JPG)
--
-- ### 2.1 AI_A2G_PATROL States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 2.2 AI_A2G_PATROL Events
--
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_A2G_PATROL.Engage}**: Let the AI engage the bogeys.
-- * **@{#AI_A2G_PATROL.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_A2G_PATROL.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_A2G_PATROL.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
--
-- ## 3. Set the Range of Engagement
--
-- ![Range](..\Presentations\AI_CAP\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_CAP#AI_A2G_PATROL.SetEngageRange}() to define that range.
--
-- ## 4. Set the Zone of Engagement
--
-- ![Zone](..\Presentations\AI_CAP\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_PATROL.SetEngageZone}() to define that Zone.
--
-- ===
--
-- @field #AI_A2G_PATROL
AI_A2G_PATROL = {
ClassName = "AI_A2G_PATROL",
}
--- Creates a new AI_A2G_PATROL object
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #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
self.Accomplished = false
self.Engaging = false
self.EngageMinSpeed = EngageMinSpeed
self.EngageMaxSpeed = EngageMaxSpeed
self:AddTransition( { "Patrolling", "Engaging", "Returning", "Airborne" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2G_PATROL.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_A2G_PATROL] OnBeforeEngage
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Engage.
-- @function [parent=#AI_A2G_PATROL] OnAfterEngage
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Engage.
-- @function [parent=#AI_A2G_PATROL] Engage
-- @param #AI_A2G_PATROL self
--- Asynchronous Event Trigger for Event Engage.
-- @function [parent=#AI_A2G_PATROL] __Engage
-- @param #AI_A2G_PATROL self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_A2G_PATROL] OnLeaveEngaging
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_A2G_PATROL] OnEnterEngaging
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2G_PATROL.
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_A2G_PATROL] OnBeforeFired
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Fired.
-- @function [parent=#AI_A2G_PATROL] OnAfterFired
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Fired.
-- @function [parent=#AI_A2G_PATROL] Fired
-- @param #AI_A2G_PATROL self
--- Asynchronous Event Trigger for Event Fired.
-- @function [parent=#AI_A2G_PATROL] __Fired
-- @param #AI_A2G_PATROL self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2G_PATROL.
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_A2G_PATROL] OnBeforeDestroy
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Destroy.
-- @function [parent=#AI_A2G_PATROL] OnAfterDestroy
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_A2G_PATROL] Destroy
-- @param #AI_A2G_PATROL self
--- Asynchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_A2G_PATROL] __Destroy
-- @param #AI_A2G_PATROL self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2G_PATROL.
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_A2G_PATROL] OnBeforeAbort
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Abort.
-- @function [parent=#AI_A2G_PATROL] OnAfterAbort
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Abort.
-- @function [parent=#AI_A2G_PATROL] Abort
-- @param #AI_A2G_PATROL self
--- Asynchronous Event Trigger for Event Abort.
-- @function [parent=#AI_A2G_PATROL] __Abort
-- @param #AI_A2G_PATROL self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2G_PATROL.
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2G_PATROL] OnBeforeAccomplish
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2G_PATROL] OnAfterAccomplish
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_A2G_PATROL] Accomplish
-- @param #AI_A2G_PATROL self
--- Asynchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_A2G_PATROL] __Accomplish
-- @param #AI_A2G_PATROL self
-- @param #number Delay The delay in seconds.
return self
end
--- onafter State Transition for Event Patrol.
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol 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_PATROL:onafterStart( AIPatrol, From, Event, To )
self:GetParent( self ).onafterStart( self, AIPatrol, From, Event, To )
AIPatrol:HandleEvent( EVENTS.Takeoff, nil, self )
end
--- Set the Engage Zone which defines where the AI will engage bogies.
-- @param #AI_A2G_PATROL self
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP.
-- @return #AI_A2G_PATROL self
function AI_A2G_PATROL:SetEngageZone( EngageZone )
self:F2()
if EngageZone then
self.EngageZone = EngageZone
else
self.EngageZone = nil
end
end
--- Set the Engage Range when the AI will engage with airborne enemies.
-- @param #AI_A2G_PATROL self
-- @param #number EngageRange The Engage Range.
-- @return #AI_A2G_PATROL self
function AI_A2G_PATROL:SetEngageRange( EngageRange )
self:F2()
if EngageRange then
self.EngageRange = EngageRange
else
self.EngageRange = nil
end
end
--- onafter State Transition for Event Patrol.
-- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol 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_PATROL:onafterPatrol( AIPatrol, From, Event, To )
-- Call the parent Start event handler
self:GetParent(self).onafterPatrol( self, AIPatrol, From, Event, To )
self:HandleEvent( EVENTS.Dead )
end
-- todo: need to fix this global function
--- @param Wrapper.Group#GROUP AIPatrol
function AI_A2G_PATROL.AttackRoute( AIPatrol, Fsm )
AIPatrol:F( { "AI_A2G_PATROL.AttackRoute:", AIPatrol:GetName() } )
if AIPatrol:IsAlive() then
Fsm:__Engage( 0.5 )
end
end
--- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object 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_PATROL:onbeforeEngage( AIPatrol, From, Event, To )
if self.Accomplished == true then
return false
end
end
--- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol 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_PATROL:onafterAbort( AIPatrol, From, Event, To )
AIPatrol:ClearTasks()
self:__Route( 0.5 )
end
--- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The AIPatrol Object 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_PATROL:onafterEngage( AIPatrol, From, Event, To, AttackSetUnit )
self:F( { AIPatrol, From, Event, To, AttackSetUnit} )
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
local FirstAttackUnit = self.AttackSetUnit:GetFirst() -- Wrapper.Unit#UNIT
if FirstAttackUnit and FirstAttackUnit:IsAlive() then -- If there is no attacker anymore, stop the engagement.
if AIPatrol:IsAlive() then
local EngageRoute = {}
--- Calculate the target route point.
local CurrentCoord = AIPatrol:GetCoordinate()
local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
--- Create a route point of type air.
local ToPatrolRoutePoint = CurrentCoord:Translate( 5000, ToInterceptAngle ):WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } )
self:T2( { self.MinSpeed, self.MaxSpeed, 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
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
AttackTasks[#AttackTasks+1] = AIPatrol:TaskAttackUnit( AttackUnit )
end
end
if #AttackTasks == 0 then
self:E("No targets found -> Going back to Patrolling")
self:__Abort( 0.5 )
else
AIPatrol:OptionROEOpenFire()
AIPatrol:OptionROTEvadeFire()
AttackTasks[#AttackTasks+1] = AIPatrol:TaskFunction( "AI_A2G_PATROL.AttackRoute", self )
EngageRoute[#EngageRoute].task = AIPatrol:TaskCombo( AttackTasks )
end
AIPatrol:Route( EngageRoute, 0.5 )
end
else
self:E("No targets found -> Going back to Patrolling")
self:__Abort( 0.5 )
end
end
--- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object 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_PATROL:onafterAccomplish( AIPatrol, From, Event, To )
self.Accomplished = true
self:SetDetectionOff()
end
--- @param #AI_A2G_PATROL self
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_A2G_PATROL:onafterDestroy( AIPatrol, From, Event, To, EventData )
if EventData.IniUnit then
self.AttackUnits[EventData.IniUnit] = nil
end
end
--- @param #AI_A2G_PATROL self
-- @param Core.Event#EVENTDATA EventData
function AI_A2G_PATROL:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then
self:__Destroy( 1, EventData )
end
end
end
--- @param Wrapper.Group#GROUP AIPatrol
function AI_A2G_PATROL.Resume( AIPatrol, Fsm )
AIPatrol:I( { "AI_A2G_PATROL.Resume:", AIPatrol:GetName() } )
if AIPatrol:IsAlive() then
Fsm:__Reset( 1 )
Fsm:__Route( 5 )
end
end

View File

@ -68,6 +68,7 @@ AI/AI_A2A_Gci.lua
AI/AI_A2A_Dispatcher.lua
AI/AI_A2G.lua
AI/AI_A2G_Engage.lua
AI/AI_A2G_Patrol.lua
AI/AI_A2G_Dispatcher.lua
AI/AI_Patrol.lua
AI/AI_Cap.lua