Merge branch 'develop' into FF/Develop

This commit is contained in:
Frank 2019-01-30 20:11:11 +01:00
commit 331315a83e
11 changed files with 934 additions and 308 deletions

View File

@ -345,7 +345,7 @@ function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
AIPatrol:OptionROEReturnFire() AIPatrol:OptionROEReturnFire()
AIPatrol:OptionROTEvadeFire() AIPatrol:OptionROTEvadeFire()
AIPatrol:Route( PatrolRoute, 0.5 ) AIPatrol:Route( PatrolRoute, 0.5)
end end
end end

View File

@ -74,21 +74,22 @@ function AI_A2G_BAI:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
if DefenderGroup:IsAlive() then if DefenderGroup:IsAlive() then
local EngageAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude )
local EngageSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
-- Determine the distance to the target. -- Determine the distance to the target.
-- If it is less than 10km, then attack without a route. -- If it is less than 10km, then attack without a route.
-- Otherwise perform a route attack. -- Otherwise perform a route attack.
local DefenderCoord = DefenderGroup:GetPointVec3() local DefenderCoord = DefenderGroup:GetPointVec3()
DefenderCoord:SetY( math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude ) ) -- Ground targets don't have an altitude. DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetCoord = self.AttackSetUnit:GetFirst():GetPointVec3() local TargetCoord = self.AttackSetUnit:GetFirst():GetPointVec3()
TargetCoord:SetY( math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude ) ) -- Ground targets don't have an altitude. TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord ) local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
local EngageRoute = {} local EngageRoute = {}
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
--- Calculate the target route point. --- Calculate the target route point.
@ -96,58 +97,66 @@ function AI_A2G_BAI:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
self.PatrolAltType or "RADIO", self.PatrolAltType or "RADIO",
POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed, EngageSpeed,
true true
) )
EngageRoute[#EngageRoute+1] = FromWP EngageRoute[#EngageRoute+1] = FromWP
local ToCoord = self.AttackSetUnit:GetFirst():GetCoordinate() self:SetTargetDistance( TargetCoord ) -- For RTB status check
self:SetTargetDistance( ToCoord ) -- For RTB status check
local FromEngageAngle = ToCoord:GetAngleDegrees( ToCoord:GetDirectionVec3( DefenderCoord ) ) local FromEngageAngle = TargetCoord:GetAngleDegrees( TargetCoord:GetDirectionVec3( DefenderCoord ) )
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 )
--- Create a route point of type air. --- Create a route point of type air.
local ToWP = ToCoord:Translate( 10000, FromEngageAngle ):WaypointAir( local ToWP = TargetCoord:Translate( EngageDistance, FromEngageAngle ):WaypointAir(
self.PatrolAltType or "RADIO", self.PatrolAltType or "RADIO",
POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed, EngageSpeed,
true true
) )
self:F( { Angle = FromEngageAngle, ToTargetSpeed = ToTargetSpeed } )
self:F( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToWP EngageRoute[#EngageRoute+1] = ToWP
local AttackTasks = {} local AttackTasks = {}
self.AttackSetUnit.AttackIndex = self.AttackSetUnit.AttackIndex and self.AttackSetUnit.AttackIndex + 1 or 1
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do local AttackSetUnitPerThreatLevel = self.AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
local AttackUnit = AttackSetUnitPerThreatLevel[self.AttackSetUnit.AttackIndex]
if not AttackUnit then
self.AttackSetUnit.AttackIndex = 1
AttackUnit = AttackSetUnitPerThreatLevel[self.AttackSetUnit.AttackIndex]
end
if AttackUnit then
if AttackUnit:IsAlive() and AttackUnit:IsGround() then if AttackUnit:IsAlive() and AttackUnit:IsGround() then
self:T( { "Engage Unit evaluation:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsGround() } ) self:T( { "BAI Unit:", AttackUnit:GetName() } )
self:T( { "Eliminating Unit:", AttackUnit:GetName() } ) AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, false, false, nil, nil, EngageAltitude )
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
end end
end end
if #AttackTasks == 0 then if #AttackTasks == 0 then
self:E( DefenderGroupName .. ": No targets found -> Going RTB") self:E( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return() self:Return()
self:__RTB( 0.5 ) self:__RTB( self.TaskDelay )
else else
DefenderGroup:OptionROEOpenFire() DefenderGroup:OptionROEOpenFire()
DefenderGroup:OptionROTEvadeFire() DefenderGroup:OptionROTEvadeFire()
DefenderGroup:OptionKeepWeaponsOnThreat()
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self ) AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self )
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks ) EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
end end
DefenderGroup:Route( EngageRoute, 0.5 ) DefenderGroup:Route( EngageRoute, self.TaskDelay )
end end
else else
self:E( DefenderGroupName .. ": No targets found -> Going RTB") self:E( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return() self:Return()
self:__RTB( 0.5 ) self:__RTB( self.TaskDelay )
end end
end end

View File

@ -74,21 +74,18 @@ function AI_A2G_CAS:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
if DefenderGroup:IsAlive() then if DefenderGroup:IsAlive() then
-- Determine the distance to the target. local EngageAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude )
-- If it is less than 10km, then attack without a route. local EngageSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
-- Otherwise perform a route attack.
local DefenderCoord = DefenderGroup:GetPointVec3() local DefenderCoord = DefenderGroup:GetPointVec3()
DefenderCoord:SetY( math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude ) ) -- Ground targets don't have an altitude. DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetCoord = self.AttackSetUnit:GetFirst():GetPointVec3() local TargetCoord = self.AttackSetUnit:GetFirst():GetPointVec3()
TargetCoord:SetY( math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude ) ) -- Ground targets don't have an altitude. TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord ) local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
local EngageRoute = {} local EngageRoute = {}
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
--- Calculate the target route point. --- Calculate the target route point.
@ -96,7 +93,7 @@ function AI_A2G_CAS:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
self.PatrolAltType or "RADIO", self.PatrolAltType or "RADIO",
POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed, EngageSpeed,
true true
) )
@ -105,50 +102,58 @@ function AI_A2G_CAS:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
self:SetTargetDistance( TargetCoord ) -- For RTB status check self:SetTargetDistance( TargetCoord ) -- For RTB status check
local FromEngageAngle = TargetCoord:GetAngleDegrees( TargetCoord:GetDirectionVec3( DefenderCoord ) ) local FromEngageAngle = TargetCoord:GetAngleDegrees( TargetCoord:GetDirectionVec3( DefenderCoord ) )
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 ) local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 )
--- Create a route point of type air. --- Create a route point of type air.
local ToWP = TargetCoord:Translate( EngageDistance, FromEngageAngle ):WaypointAir( local ToWP = TargetCoord:Translate( EngageDistance, FromEngageAngle, true ):WaypointAir(
self.PatrolAltType or "RADIO", self.PatrolAltType or "RADIO",
POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed, EngageSpeed,
true true
) )
self:F( { Angle = FromEngageAngle, ToTargetSpeed = ToTargetSpeed } )
self:F( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToWP EngageRoute[#EngageRoute+1] = ToWP
local AttackTasks = {} local AttackTasks = {}
self.AttackSetUnit.AttackIndex = self.AttackSetUnit.AttackIndex and self.AttackSetUnit.AttackIndex + 1 or 1
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do local AttackSetUnitPerThreatLevel = self.AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
local AttackUnit = AttackSetUnitPerThreatLevel[self.AttackSetUnit.AttackIndex]
if not AttackUnit then
self.AttackSetUnit.AttackIndex = 1
AttackUnit = AttackSetUnitPerThreatLevel[self.AttackSetUnit.AttackIndex]
end
if AttackUnit then
if AttackUnit:IsAlive() and AttackUnit:IsGround() then if AttackUnit:IsAlive() and AttackUnit:IsGround() then
self:T( { "Eliminating Unit:", AttackUnit:GetName() } ) self:F( { "CAS Unit:", AttackUnit:GetName() } )
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit ) AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, false, false, nil, nil, EngageAltitude )
end end
end end
if #AttackTasks == 0 then if #AttackTasks == 0 then
self:E( DefenderGroupName .. ": No targets found -> Going RTB") self:E( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return() self:Return()
self:__RTB( 0.5 ) self:__RTB( self.TaskDelay )
else else
DefenderGroup:OptionROEOpenFire() DefenderGroup:OptionROEOpenFire()
DefenderGroup:OptionROTEvadeFire() DefenderGroup:OptionROTEvadeFire()
DefenderGroup:OptionKeepWeaponsOnThreat()
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self ) AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self )
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks ) EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
end end
DefenderGroup:Route( EngageRoute, 0.5 ) DefenderGroup:Route( EngageRoute, self.TaskDelay )
end end
else else
self:E( DefenderGroupName .. ": No targets found -> Going RTB") self:E( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return() self:Return()
self:__RTB( 0.5 ) self:__RTB( self.TaskDelay )
end end
end end

View File

@ -305,7 +305,7 @@ do -- AI_A2G_DISPATCHER
-- --
-- An reconnaissance network, is used to detect enemy ground targets, potentially group them into areas, and to understand the position, level of threat of the enemy. -- An reconnaissance network, is used to detect enemy ground targets, potentially group them into areas, and to understand the position, level of threat of the enemy.
-- --
-- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia5.JPG) -- ![Banner Image](..\Presentations\AI_A2G_DISPATCHER\Dia5.JPG)
-- --
-- As explained in the introduction, depending on the type of mission you want to achieve, different types of units can be applied to detect ground enemy targets. -- As explained in the introduction, depending on the type of mission you want to achieve, different types of units can be applied to detect ground enemy targets.
-- Ground based units are very useful to act as a reconnaissance, but they lack sometimes the visibility to detect targets at greater range. -- Ground based units are very useful to act as a reconnaissance, but they lack sometimes the visibility to detect targets at greater range.
@ -493,9 +493,9 @@ do -- AI_A2G_DISPATCHER
-- --
-- You need to configure each squadron which task types you want it to perform. Read on ... -- You need to configure each squadron which task types you want it to perform. Read on ...
-- --
-- ### 3.2. Squadrons enemy ground target **Engagement**. -- ### 3.2. Squadrons enemy ground target **engagement types**.
-- --
-- There are two ways how targets can be engaged: directly upon call from the airfield, farp or carrier, or through a patrol. -- There are two ways how targets can be engaged: directly **on call** from the airfield, farp or carrier, or through a **patrol**.
-- --
-- Patrols are extremely handy, as these will airborne your helicopters or airplanes in advance. They will patrol in defined zones outlined, -- Patrols are extremely handy, as these will airborne your helicopters or airplanes in advance. They will patrol in defined zones outlined,
-- and will engage with the targets once commanded. If the patrol zone is close enough to the enemy ground targets, then the time required -- and will engage with the targets once commanded. If the patrol zone is close enough to the enemy ground targets, then the time required
@ -505,7 +505,7 @@ do -- AI_A2G_DISPATCHER
-- --
-- The mission designer needs to carefully balance the need for patrols or the need for engagement on call from the airfields. -- The mission designer needs to carefully balance the need for patrols or the need for engagement on call from the airfields.
-- --
-- ### 3.3. Squadron **on call engagement**. -- ### 3.3. Squadron **on call** engagement.
-- --
-- So to make squadrons engage targets from the airfields, use the following methods: -- So to make squadrons engage targets from the airfields, use the following methods:
-- --
@ -558,12 +558,350 @@ do -- AI_A2G_DISPATCHER
-- A2GDispatcher:SetSquadronBaiPatrol( "Maykop BAI", PatrolZone, 800, 900, 50, 80, 250, 300 ) -- A2GDispatcher:SetSquadronBaiPatrol( "Maykop BAI", PatrolZone, 800, 900, 50, 80, 250, 300 )
-- A2GDispatcher:SetSquadronPatrolInterval( "Maykop BAI", 2, 30, 60, 1, "BAI" ) -- A2GDispatcher:SetSquadronPatrolInterval( "Maykop BAI", 2, 30, 60, 1, "BAI" )
-- --
--
-- ### 3.5. Set squadron take-off methods
--
-- Use the various SetSquadronTakeoff... methods to control how squadrons are taking-off from the home airfield, FARP or ship.
--
-- * @{#AI_A2G_DISPATCHER.SetSquadronTakeoff}() is the generic configuration method to control takeoff from the air, hot, cold or from the runway. See the method for further details.
-- * @{#AI_A2G_DISPATCHER.SetSquadronTakeoffInAir}() will spawn new aircraft from the squadron directly in the air.
-- * @{#AI_A2G_DISPATCHER.SetSquadronTakeoffFromParkingCold}() will spawn new aircraft in without running engines at a parking spot at the airfield.
-- * @{#AI_A2G_DISPATCHER.SetSquadronTakeoffFromParkingHot}() will spawn new aircraft in with running engines at a parking spot at the airfield.
-- * @{#AI_A2G_DISPATCHER.SetSquadronTakeoffFromRunway}() will spawn new aircraft at the runway at the airfield.
--
-- **The default landing method is to spawn new aircraft directly in the air.**
--
-- Use these methods to fine-tune for specific airfields that are known to create bottlenecks, or have reduced airbase efficiency.
-- The more and the longer aircraft need to taxi at an airfield, the more risk there is that:
--
-- * aircraft will stop waiting for each other or for a landing aircraft before takeoff.
-- * aircraft may get into a "dead-lock" situation, where two aircraft are blocking each other.
-- * aircraft may collide at the airbase.
-- * aircraft may be awaiting the landing of a plane currently in the air, but never lands ...
--
-- Currently within the DCS engine, the airfield traffic coordination is erroneous and contains a lot of bugs.
-- If you experience while testing problems with aircraft take-off or landing, please use one of the above methods as a solution to workaround these issues!
--
-- This example sets the default takeoff method to be from the runway.
-- And for a couple of squadrons overrides this default method.
--
-- -- Setup the Takeoff methods
--
-- -- The default takeoff
-- A2ADispatcher:SetDefaultTakeOffFromRunway()
--
-- -- The individual takeoff per squadron
-- A2ADispatcher:SetSquadronTakeoff( "Mineralnye", AI_A2G_DISPATCHER.Takeoff.Air )
-- A2ADispatcher:SetSquadronTakeoffInAir( "Sochi" )
-- A2ADispatcher:SetSquadronTakeoffFromRunway( "Mozdok" )
-- A2ADispatcher:SetSquadronTakeoffFromParkingCold( "Maykop" )
-- A2ADispatcher:SetSquadronTakeoffFromParkingHot( "Novo" )
--
--
-- ### 3.5.1. Set Squadron takeoff altitude when spawning new aircraft in the air.
--
-- In the case of the @{#AI_A2G_DISPATCHER.SetSquadronTakeoffInAir}() there is also an other parameter that can be applied.
-- That is modifying or setting the **altitude** from where planes spawn in the air.
-- Use the method @{#AI_A2G_DISPATCHER.SetSquadronTakeoffInAirAltitude}() to set the altitude for a specific squadron.
-- The default takeoff altitude can be modified or set using the method @{#AI_A2G_DISPATCHER.SetSquadronTakeoffInAirAltitude}().
-- As part of the method @{#AI_A2G_DISPATCHER.SetSquadronTakeoffInAir}() a parameter can be specified to set the takeoff altitude.
-- If this parameter is not specified, then the default altitude will be used for the squadron.
--
-- ### 3.5.2. Set Squadron takeoff interval.
--
-- The different types of available airfields have different amounts of available launching platforms:
--
-- - Airbases typically have a lot of platforms.
-- - FARPs have 4 platforms.
-- - Ships have 2 to 4 platforms.
--
-- Depending on the demand of requested takeoffs by the A2G dispatcher, an airfield can become overloaded. Too many aircraft need to be taken
-- off at the same time, which will result in clutter as described above. In order to better control this behaviour, a takeoff scheduler is implemented,
-- which can be used to control how many aircraft are ordered for takeoff between specific time intervals.
-- The takeff intervals can be specified per squadron, which make sense, as each squadron have a "home" airfield.
--
-- For this purpose, the method @{#AI_A2G_DISPATCHER.SetSquadronTakeOffInterval}() can be used to specify the takeoff intervals of
-- aircraft groups per squadron to avoid cluttering of aircraft at airbases.
-- This is especially useful for FARPs and ships. Each takeoff dispatch is queued by the dispatcher and when the interval time
-- has been reached, a new group will be spawned or activated for takeoff.
--
-- The interval needs to be estimated, and depends on the time needed for the aircraft group to actually depart from the launch platform, and
-- the way how the aircraft are starting up. Cold starts take the longest duration, hot starts a few seconds, and runway takeoff also a few seconds for FARPs and ships.
--
-- See the underlying example:
--
-- -- Imagine a squadron launched from a FARP, with a grouping of 4.
-- -- Aircraft will cold start from the FARP, and thus, a maximum of 4 aircraft can be launched at the same time.
-- -- Additionally, depending on the group composition of the aircraft, defending units will be ordered for takeoff together.
-- -- It takes about 3 to 4 minutes to takeoff helicopters from FARPs in cold start.
-- A2ADispatcher:SetSquadronTakeOffInterval( "Mineralnye", 60 * 4 )
--
--
-- ### 3.6. Set squadron landing methods
--
-- In analogy with takeoff, the landing methods are to control how squadrons land at the airfield:
--
-- * @{#AI_A2G_DISPATCHER.SetSquadronLanding}() is the generic configuration method to control landing, namely despawn the aircraft near the airfield in the air, right after landing, or at engine shutdown.
-- * @{#AI_A2G_DISPATCHER.SetSquadronLandingNearAirbase}() will despawn the returning aircraft in the air when near the airfield.
-- * @{#AI_A2G_DISPATCHER.SetSquadronLandingAtRunway}() will despawn the returning aircraft directly after landing at the runway.
-- * @{#AI_A2G_DISPATCHER.SetSquadronLandingAtEngineShutdown}() will despawn the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines.
--
-- You can use these methods to minimize the airbase coodination overhead and to increase the airbase efficiency.
-- When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the
-- A2A defense system, as no new CAP or GCI planes can takeoff.
-- Note that the method @{#AI_A2G_DISPATCHER.SetSquadronLandingNearAirbase}() will only work for returning aircraft, not for damaged or out of fuel aircraft.
-- Damaged or out-of-fuel aircraft are returning to the nearest friendly airbase and will land, and are out of control from ground control.
--
-- This example defines the default landing method to be at the runway.
-- And for a couple of squadrons overrides this default method.
--
-- -- Setup the Landing methods
--
-- -- The default landing method
-- A2ADispatcher:SetDefaultLandingAtRunway()
--
-- -- The individual landing per squadron
-- A2ADispatcher:SetSquadronLandingAtRunway( "Mineralnye" )
-- A2ADispatcher:SetSquadronLandingNearAirbase( "Sochi" )
-- A2ADispatcher:SetSquadronLandingAtEngineShutdown( "Mozdok" )
-- A2ADispatcher:SetSquadronLandingNearAirbase( "Maykop" )
-- A2ADispatcher:SetSquadronLanding( "Novo", AI_A2G_DISPATCHER.Landing.AtRunway )
--
--
-- ### 3.7. Set squadron **grouping**.
--
-- Use the method @{#AI_A2G_DISPATCHER.SetSquadronGrouping}() to set the grouping of aircraft when spawned in.
--
-- ![Banner Image](..\Presentations\AI_A2G_DISPATCHER\Dia12.JPG)
--
-- In the case of **on call** engagement, the @{#AI_A2G_DISPATCHER.SetSquadronGrouping}() method has additional behaviour.
-- When there aren't enough patrol flights airborne, a on call will be initiated for the remaining
-- targets to be engaged. Depending on the grouping parameter, the spawned flights for on call aircraft are grouped into this setting.
-- For example with a group setting of 2, if 3 targets are detected and cannot be engaged by the available patrols or any airborne flight,
-- an additional on call flight needs to be started.
--
-- The **grouping value is set for a Squadron**, and can be **dynamically adjusted** during mission execution, so to adjust the defense flights grouping when the tactical situation changes.
--
-- ### 3.8. Set the squadron **overhead** to balance the effectiveness of the A2G defenses.
--
-- The effectiveness can be set with the **overhead parameter**. This is a number that is used to calculate the amount of Units that dispatching command will allocate to GCI in surplus of detected amount of units.
-- The **default value** of the overhead parameter is 1.0, which means **equal balance**.
--
-- ![Banner Image](..\Presentations\AI_A2G_DISPATCHER\Dia11.JPG)
--
-- However, depending on the (type of) aircraft (strength and payload) in the squadron and the amount of resources available, this parameter can be changed.
--
-- The @{#AI_A2G_DISPATCHER.SetSquadronOverhead}() method can be used to tweak the defense strength,
-- taking into account the plane types of the squadron.
--
-- For example, a A-10C with full long-distance A2G missiles payload, may still be less effective than a Su-23 with short range A2G missiles...
-- So in this case, one may want to use the @{#AI_A2G_DISPATCHER.SetOverhead}() method to allocate more defending planes as the amount of detected attacking ground units.
-- The overhead must be given as a decimal value with 1 as the neutral value, which means that overhead values:
--
-- * Higher than 1.0, for example 1.5, will increase the defense unit amounts. For 4 attacking ground units detected, 6 aircraft will be spawned.
-- * Lower than 1, for example 0.75, will decrease the defense unit amounts. For 4 attacking ground units detected, only 3 aircraft will be spawned.
--
-- The amount of defending units is calculated by multiplying the amount of detected attacking ground units as part of the detected group
-- multiplied by the overhead parameter, and rounded up to the smallest integer.
--
-- Typically, for A2G defenses, values small than 1 will be used. Here are some good values for a couple of aircraft to support CAS operations:
--
-- - A-10C: 0.15
-- - Su-34: 0.15
-- - A-10A: 0.25
-- - SU-25T: 0.10
--
-- So generically, the amount of missiles that an aircraft can take will determine its attacking effectiveness. The longer the range of the missiles,
-- the less risk that the defender may be destroyed by the enemy, thus, the less aircraft needs to be activated in a defense.
--
-- The **overhead value is set for a Squadron**, and can be **dynamically adjusted** during mission execution, so to adjust the defense overhead when the tactical situation changes.
--
-- ### 3.8. Set the squadron **engage limit**.
--
-- To limit the amount of aircraft to defend against a large group of intruders, an **engage limit** can be defined per squadron.
-- This limit will avoid an extensive amount of aircraft to engage with the enemy if the attacking ground forces are enormous.
--
-- Use the method @{#AI_A2G_DISPATCHER.SetSquadronEngageLimit}() to limit the amount of aircraft that will engage with the enemy, per squadron.
--
-- ## 4. Set the **fuel treshold**.
--
-- When aircraft get **out of fuel** to a certain %-tage, which is by default **15% (0.15)**, there are two possible actions that can be taken:
-- - The aircraft will go RTB, and will be replaced with a new aircraft if possible.
-- - The aircraft will refuel at a tanker, if a tanker has been specified for the squadron.
--
-- Use the method @{#AI_A2G_DISPATCHER.SetSquadronFuelThreshold}() to set the **squadron fuel treshold** of the aircraft for all squadrons.
--
-- ## 6. Other configuration options
--
-- ### 6.1. Set a tactical display panel.
--
-- Every 30 seconds, a tactical display panel can be shown that illustrates what the status is of the different groups controlled by AI_A2G_DISPATCHER.
-- Use the method @{#AI_A2G_DISPATCHER.SetTacticalDisplay}() to switch on the tactical display panel. The default will not show this panel.
-- Note that there may be some performance impact if this panel is shown.
--
-- ## 10. Default settings.
--
-- Default settings configure the standard behaviour of the squadrons.
-- This section a good overview of the different parameters that setup the behaviour of **ALL** the squadrons by default.
-- Note that default behaviour can be tweaked, and thus, this will change the behaviour of all the squadrons.
-- Unless there is a specific behaviour set for a specific squadron, the default configured behaviour will be followed.
--
-- ## 10.1. Default **takeoff** behaviour.
--
-- The default takeoff behaviour is set to **in the air**, which means that new spawned aircraft will be spawned directly in the air above the airbase by default.
--
-- **The default takeoff method can be set for ALL squadrons that don't have an individual takeoff method configured.**
--
-- * @{#AI_A2G_DISPATCHER.SetDefaultTakeoff}() is the generic configuration method to control takeoff by default from the air, hot, cold or from the runway. See the method for further details.
-- * @{#AI_A2G_DISPATCHER.SetDefaultTakeoffInAir}() will spawn by default new aircraft from the squadron directly in the air.
-- * @{#AI_A2G_DISPATCHER.SetDefaultTakeoffFromParkingCold}() will spawn by default new aircraft in without running engines at a parking spot at the airfield.
-- * @{#AI_A2G_DISPATCHER.SetDefaultTakeoffFromParkingHot}() will spawn by default new aircraft in with running engines at a parking spot at the airfield.
-- * @{#AI_A2G_DISPATCHER.SetDefaultTakeoffFromRunway}() will spawn by default new aircraft at the runway at the airfield.
--
-- ## 10.2. Default landing behaviour.
--
-- The default landing behaviour is set to **near the airbase**, which means that returning airplanes will be despawned directly in the air by default.
--
-- The default landing method can be set for ALL squadrons that don't have an individual landing method configured.
--
-- * @{#AI_A2G_DISPATCHER.SetDefaultLanding}() is the generic configuration method to control by default landing, namely despawn the aircraft near the airfield in the air, right after landing, or at engine shutdown.
-- * @{#AI_A2G_DISPATCHER.SetDefaultLandingNearAirbase}() will despawn by default the returning aircraft in the air when near the airfield.
-- * @{#AI_A2G_DISPATCHER.SetDefaultLandingAtRunway}() will despawn by default the returning aircraft directly after landing at the runway.
-- * @{#AI_A2G_DISPATCHER.SetDefaultLandingAtEngineShutdown}() will despawn by default the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines.
--
-- ## 10.3. Default **overhead**.
--
-- The default overhead is set to **0.25**. That essentially means that for each 4 ground enemies there will be 1 aircraft dispatched.
--
-- The default overhead value can be set for ALL squadrons that don't have an individual overhead value configured.
--
-- Use the @{#AI_A2G_DISPATCHER.SetDefaultOverhead}() method can be used to set the default overhead or defense strength for ALL squadrons.
--
-- ## 10.4. Default **grouping**.
--
-- The default grouping is set to **one airplane**. That essentially means that there won't be any grouping applied by default.
--
-- The default grouping value can be set for ALL squadrons that don't have an individual grouping value configured.
--
-- Use the method @{#AI_A2G_DISPATCHER.SetDefaultGrouping}() to set the **default grouping** of spawned airplanes for all squadrons.
--
-- ## 10.5. Default RTB fuel treshold.
--
-- When an airplane gets **out of fuel** to a certain %-tage, which is **15% (0.15)**, it will go RTB, and will be replaced with a new airplane when applicable.
--
-- Use the method @{#AI_A2G_DISPATCHER.SetDefaultFuelThreshold}() to set the **default fuel treshold** of spawned airplanes for all squadrons.
--
-- ## 10.6. Default RTB damage treshold.
--
-- When an airplane is **damaged** to a certain %-tage, which is **40% (0.40)**, it will go RTB, and will be replaced with a new airplane when applicable.
--
-- Use the method @{#AI_A2G_DISPATCHER.SetDefaultDamageThreshold}() to set the **default damage treshold** of spawned airplanes for all squadrons.
--
-- ## 10.7. Default settings for **patrol**.
--
-- ### 10.7.1. Default **patrol time Interval**.
--
-- Patrol dispatching is time event driven, and will evaluate in random time intervals if a new patrol needs to be dispatched.
--
-- The default patrol time interval is between **180** and **600** seconds.
--
-- Use the method @{#AI_A2G_DISPATCHER.SetDefaultPatrolTimeInterval}() to set the **default patrol time interval** of dispatched aircraft for ALL squadrons.
--
-- Note that you can still change the patrol limit and patrol time intervals for each patrol individually using
-- the @{#AI_A2G_DISPATCHER.SetSquadronPatrolTimeInterval}() method.
--
-- ### 10.7.2. Default **patrol limit**.
--
-- Multiple patrol can be airborne at the same time for one squadron, which is controlled by the **patrol limit**.
-- The **default patrol limit** is 1 patrol per squadron to be airborne at the same time.
-- Note that the default patrol limit is used when a squadron patrol is defined, and cannot be changed afterwards.
-- So, ensure that you set the default patrol limit **before** you define or setup the squadron patrol.
--
-- Use the method @{#AI_A2G_DISPATCHER.SetDefaultPatrolTimeInterval}() to set the **default patrol time interval** of dispatched aircraft patrols for all squadrons.
-- Note that you can still change the patrol limit and patrol time intervals for each patrol individually using
-- the @{#AI_A2G_DISPATCHER.SetSquadronPatrolTimeInterval}() method.
--
-- ## 10.7.3. Default tanker for refuelling when executing CAP.
--
-- Instead of sending CAP to RTB when out of fuel, you can let CAP refuel in mid air using a tanker.
-- This greatly increases the efficiency of your CAP operations.
--
-- In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected.
-- Then, use the method @{#AI_A2G_DISPATCHER.SetDefaultTanker}() to set the tanker for the dispatcher.
-- Use the method @{#AI_A2G_DISPATCHER.SetDefaultFuelThreshold}() to set the %-tage left in the defender airplane tanks when a refuel action is needed.
--
-- When the tanker specified is alive and in the air, the tanker will be used for refuelling.
--
-- For example, the following setup will set the default refuel tanker to "Tanker":
--
-- ![Banner Image](..\Presentations\AI_A2G_DISPATCHER\AI_A2G_DISPATCHER-ME_11.JPG)
--
-- -- Define the CAP
-- A2ADispatcher:SetSquadron( "Sochi", AIRBASE.Caucasus.Sochi_Adler, { "SQ CCCP SU-34" }, 20 )
-- A2ADispatcher:SetSquadronCap( "Sochi", ZONE:New( "PatrolZone" ), 4000, 8000, 600, 800, 1000, 1300 )
-- A2ADispatcher:SetSquadronCapInterval("Sochi", 2, 30, 600, 1 )
-- A2ADispatcher:SetSquadronGci( "Sochi", 900, 1200 )
--
-- -- Set the default tanker for refuelling to "Tanker", when the default fuel treshold has reached 90% fuel left.
-- A2ADispatcher:SetDefaultFuelThreshold( 0.9 )
-- A2ADispatcher:SetDefaultTanker( "Tanker" )
--
-- ## 10.8. Default settings for GCI.
--
-- ## 10.8.1. Optimal intercept point calculation.
--
-- When intruders are detected, the intrusion path of the attackers can be monitored by the EWR.
-- Although defender planes might be on standby at the airbase, it can still take some time to get the defenses up in the air if there aren't any defenses airborne.
-- This time can easily take 2 to 3 minutes, and even then the defenders still need to fly towards the target, which takes also time.
--
-- Therefore, an optimal **intercept point** is calculated which takes a couple of parameters:
--
-- * The average bearing of the intruders for an amount of seconds.
-- * The average speed of the intruders for an amount of seconds.
-- * An assumed time it takes to get planes operational at the airbase.
--
-- The **intercept point** will determine:
--
-- * If there are any friendlies close to engage the target. These can be defenders performing CAP or defenders in RTB.
-- * The optimal airbase from where defenders will takeoff for GCI.
--
-- Use the method @{#AI_A2G_DISPATCHER.SetIntercept}() to modify the assumed intercept delay time to calculate a valid interception.
--
-- ## 10.8.2. Default Disengage Radius.
--
-- The radius to **disengage any target** when the **distance** of the defender to the **home base** is larger than the specified meters.
-- The default Disengage Radius is **300km** (300000 meters). Note that the Disengage Radius is applicable to ALL squadrons!
--
-- Use the method @{#AI_A2G_DISPATCHER.SetDisengageRadius}() to modify the default Disengage Radius to another distance setting.
--
-- ## 11. Airbase capture:
--
-- Different squadrons can be located at one airbase.
-- If the airbase gets captured, that is, when there is an enemy unit near the airbase, and there aren't anymore friendlies at the airbase, the airbase will change coalition ownership.
-- As a result, the GCI and CAP will stop!
-- However, the squadron will still stay alive. Any airplane that is airborne will continue its operations until all airborne airplanes
-- of the squadron will be destroyed. This to keep consistency of air operations not to confuse the players.
--
--
--
--
-- @field #AI_A2G_DISPATCHER -- @field #AI_A2G_DISPATCHER
AI_A2G_DISPATCHER = { AI_A2G_DISPATCHER = {
ClassName = "AI_A2G_DISPATCHER", ClassName = "AI_A2G_DISPATCHER",
Detection = nil, Detection = nil,
} }
--- Definition of a Squadron.
-- @type AI_A2G_DISPATCHER.Squadron
-- @field #string Name The Squadron name.
-- @field Wrapper.Airbase#AIRBASE Airbase The home airbase.
-- @field #string AirbaseName The name of the home airbase.
-- @field Core.Spawn#SPAWN Spawn The spawning object.
-- @field #number ResourceCount The number of resources available.
-- @field #list<#string> TemplatePrefixes The list of template prefixes.
-- @field #boolean Captured true if the squadron is captured.
-- @field #number Overhead The overhead for the squadron.
--- List of defense coordinates. --- List of defense coordinates.
-- @type AI_A2G_DISPATCHER.DefenseCoordinates -- @type AI_A2G_DISPATCHER.DefenseCoordinates
@ -587,6 +925,32 @@ do -- AI_A2G_DISPATCHER
AtEngineShutdown = 3, AtEngineShutdown = 3,
} }
--- A defense queue item description
-- @type AI_A2G_DISPATCHER.DefenseQueueItem
-- @field Squadron
-- @field #AI_A2G_DISPATCHER.Squadron DefenderSquadron The squadron in the queue.
-- @field DefendersNeeded
-- @field Defense
-- @field DefenseTaskType
-- @field Functional.Detection#DETECTION_BASE AttackerDetection
-- @field DefenderGrouping
-- @field #string SquadronName The name of the squadron.
--- Queue of planned defenses to be launched.
-- This queue exists because defenses must be launched on FARPS, or in the air, or on an airbase, or on carriers.
-- And some of these platforms have very limited amount of "launching" platforms.
-- Therefore, this queue concept is introduced that queues each defender request.
-- Depending on the location of the launching site, the queued defenders will be launched at varying time intervals.
-- This guarantees that launched defenders are also directly existing ...
-- @type AI_A2G_DISPATCHER.DefenseQueue
-- @list<#AI_A2G_DISPATCHER.DefenseQueueItem> DefenseQueueItem A list of all defenses being queued ...
--- @field #AI_A2G_DISPATCHER.DefenseQueue DefenseQueue
AI_A2G_DISPATCHER.DefenseQueue = {}
--- AI_A2G_DISPATCHER constructor. --- AI_A2G_DISPATCHER constructor.
-- This is defining the A2G DISPATCHER for one coaliton. -- This is defining the A2G DISPATCHER for one coaliton.
-- The Dispatcher works with a @{Functional.Detection#DETECTION_BASE} object that is taking of the detection of targets using the EWR units. -- The Dispatcher works with a @{Functional.Detection#DETECTION_BASE} object that is taking of the detection of targets using the EWR units.
@ -759,6 +1123,8 @@ do -- AI_A2G_DISPATCHER
self:SetDefenseReactivityMedium() self:SetDefenseReactivityMedium()
self.TakeoffScheduleID = self:ScheduleRepeat( 10, 10, 0, nil, self.ResourceTakeoff, self )
self:__Start( 5 ) self:__Start( 5 )
return self return self
@ -774,14 +1140,14 @@ do -- AI_A2G_DISPATCHER
for SquadronName, DefenderSquadron in pairs( self.DefenderSquadrons ) do for SquadronName, DefenderSquadron in pairs( self.DefenderSquadrons ) do
DefenderSquadron.Resource = {} DefenderSquadron.Resource = {}
for Resource = 1, DefenderSquadron.ResourceCount or 0 do for Resource = 1, DefenderSquadron.ResourceCount or 0 do
self:ParkDefender( DefenderSquadron ) self:ResourcePark( DefenderSquadron )
end end
end end
end end
--- @param #AI_A2G_DISPATCHER self --- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:ParkDefender( DefenderSquadron ) function AI_A2G_DISPATCHER:ResourcePark( DefenderSquadron )
local TemplateID = math.random( 1, #DefenderSquadron.Spawn ) local TemplateID = math.random( 1, #DefenderSquadron.Spawn )
local Spawn = DefenderSquadron.Spawn[ TemplateID ] -- Core.Spawn#SPAWN local Spawn = DefenderSquadron.Spawn[ TemplateID ] -- Core.Spawn#SPAWN
Spawn:InitGrouping( 1 ) Spawn:InitGrouping( 1 )
@ -838,7 +1204,7 @@ do -- AI_A2G_DISPATCHER
self:RemoveDefenderFromSquadron( Squadron, Defender ) self:RemoveDefenderFromSquadron( Squadron, Defender )
end end
DefenderUnit:Destroy() DefenderUnit:Destroy()
self:ParkDefender( Squadron, Defender ) self:ResourcePark( Squadron, Defender )
return return
end end
if DefenderUnit:GetLife() ~= DefenderUnit:GetLife0() then if DefenderUnit:GetLife() ~= DefenderUnit:GetLife0() then
@ -865,11 +1231,11 @@ do -- AI_A2G_DISPATCHER
self:RemoveDefenderFromSquadron( Squadron, Defender ) self:RemoveDefenderFromSquadron( Squadron, Defender )
end end
DefenderUnit:Destroy() DefenderUnit:Destroy()
self:ParkDefender( Squadron, Defender ) self:ResourcePark( Squadron, Defender )
end end
end end
end end
do -- Manage the defensive behaviour do -- Manage the defensive behaviour
--- @param #AI_A2G_DISPATCHER self --- @param #AI_A2G_DISPATCHER self
@ -1352,7 +1718,6 @@ do -- AI_A2G_DISPATCHER
-- @return #AI_A2G_DISPATCHER -- @return #AI_A2G_DISPATCHER
function AI_A2G_DISPATCHER:SetSquadron( SquadronName, AirbaseName, TemplatePrefixes, ResourceCount ) function AI_A2G_DISPATCHER:SetSquadron( SquadronName, AirbaseName, TemplatePrefixes, ResourceCount )
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
local DefenderSquadron = self.DefenderSquadrons[SquadronName] local DefenderSquadron = self.DefenderSquadrons[SquadronName]
@ -1379,6 +1744,8 @@ do -- AI_A2G_DISPATCHER
DefenderSquadron.TemplatePrefixes = TemplatePrefixes DefenderSquadron.TemplatePrefixes = TemplatePrefixes
DefenderSquadron.Captured = false -- Not captured. This flag will be set to true, when the airbase where the squadron is located, is captured. DefenderSquadron.Captured = false -- Not captured. This flag will be set to true, when the airbase where the squadron is located, is captured.
self:SetSquadronTakeoffInterval( SquadronName, 0 )
self:F( { Squadron = {SquadronName, AirbaseName, TemplatePrefixes, ResourceCount } } ) self:F( { Squadron = {SquadronName, AirbaseName, TemplatePrefixes, ResourceCount } } )
return self return self
@ -1449,6 +1816,28 @@ do -- AI_A2G_DISPATCHER
end end
--- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The squadron name.
-- @param #number TakeoffInterval Only Takeoff new units each specified interval in seconds in 10 seconds steps.
-- @usage
--
-- -- Set the Squadron Takeoff interval every 60 seconds for squadron "SQ50", which is good for a FARP cold start.
-- A2GDispatcher:SetSquadronTakeoffInterval( "SQ50", 60 )
--
function AI_A2G_DISPATCHER:SetSquadronTakeoffInterval( SquadronName, TakeoffInterval )
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
local DefenderSquadron = self:GetSquadron( SquadronName )
if DefenderSquadron then
DefenderSquadron.TakeoffInterval = TakeoffInterval or 0
DefenderSquadron.TakeoffTime = 0
end
end
--- Set the squadron patrol parameters for a specific task type. --- Set the squadron patrol parameters for a specific task type.
-- Mission designers should not use this method, instead use the below methods. This method is used by the below methods. -- Mission designers should not use this method, instead use the below methods. This method is used by the below methods.
@ -2806,6 +3195,7 @@ do -- AI_A2G_DISPATCHER
if DefenderTaskTarget and DefenderTaskTarget.Index == AttackerDetection.Index then if DefenderTaskTarget and DefenderTaskTarget.Index == AttackerDetection.Index then
local SquadronOverhead = self:GetSquadronOverhead( DefenderSquadronName ) local SquadronOverhead = self:GetSquadronOverhead( DefenderSquadronName )
self:F( { SquadronOverhead = SquadronOverhead } )
if DefenderSize then if DefenderSize then
DefendersEngaged = DefendersEngaged + DefenderSize DefendersEngaged = DefendersEngaged + DefenderSize
DefendersMissing = DefendersMissing - DefenderSize / SquadronOverhead DefendersMissing = DefendersMissing - DefenderSize / SquadronOverhead
@ -2820,6 +3210,15 @@ do -- AI_A2G_DISPATCHER
end end
for QueueID, QueueItem in pairs( self.DefenseQueue ) do
local QueueItem = QueueItem -- #AI_A2G_DISPATCHER.DefenseQueueItem
if QueueItem.AttackerDetection and QueueItem.AttackerDetection.ItemID == AttackerDetection.ItemID then
DefendersMissing = DefendersMissing - QueueItem.DefendersNeeded / QueueItem.DefenderSquadron.Overhead
--DefendersEngaged = DefendersEngaged + QueueItem.DefenderGrouping
end
self:F( { QueueItemName = QueueItem.Defense, QueueItem_ItemID = QueueItem.AttackerDetection.ItemID, DetectedItem = AttackerDetection.ItemID, DefendersMissing = DefendersMissing } )
end
self:F( { DefenderCount = DefendersEngaged } ) self:F( { DefenderCount = DefendersEngaged } )
return DefendersTotal, DefendersEngaged, DefendersMissing return DefendersTotal, DefendersEngaged, DefendersMissing
@ -2864,7 +3263,7 @@ do -- AI_A2G_DISPATCHER
break break
end end
end end
return Friendlies return Friendlies
end end
@ -2950,72 +3349,264 @@ do -- AI_A2G_DISPATCHER
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:onafterPatrol( From, Event, To, SquadronName, DefenseTaskType ) function AI_A2G_DISPATCHER:onafterPatrol( From, Event, To, SquadronName, DefenseTaskType )
self:F({SquadronName = SquadronName})
local DefenderSquadron, Patrol = self:CanPatrol( SquadronName, DefenseTaskType ) local DefenderSquadron, Patrol = self:CanPatrol( SquadronName, DefenseTaskType )
if Patrol then if Patrol then
self:ResourceQueue( true, DefenderSquadron, nil, Patrol, DefenseTaskType, nil, SquadronName )
local DefenderPatrol, DefenderGrouping = self:ResourceActivate( DefenderSquadron )
if DefenderPatrol then
local AI_A2G_PATROL = { SEAD = AI_A2G_SEAD, BAI = AI_A2G_BAI, CAS = AI_A2G_CAS }
local Fsm = AI_A2G_PATROL[DefenseTaskType]:New( DefenderPatrol, Patrol.EngageMinSpeed, Patrol.EngageMaxSpeed, Patrol.EngageFloorAltitude, Patrol.EngageCeilingAltitude, Patrol.Zone, Patrol.PatrolFloorAltitude, Patrol.PatrolCeilingAltitude, Patrol.PatrolMinSpeed, Patrol.PatrolMaxSpeed, 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()
self:SetDefenderTask( SquadronName, DefenderPatrol, DefenseTaskType, Fsm, nil, DefenderGrouping )
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
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
if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2G_DISPATCHER.Landing.NearAirbase then
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
Defender:Destroy()
Dispatcher:ParkDefender( Squadron, Defender )
end
end
end
end end
end end
---
-- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:ResourceQueue( Patrol, DefenderSquadron, DefendersNeeded, Defense, DefenseTaskType, AttackerDetection, SquadronName )
self:F( { DefenderSquadron, DefendersNeeded, Defense, DefenseTaskType, AttackerDetection, SquadronName } )
local DefenseQueueItem = {} -- #AI_A2G_DISPATCHER.DefenderQueueItem
DefenseQueueItem.Patrol = Patrol
DefenseQueueItem.DefenderSquadron = DefenderSquadron
DefenseQueueItem.DefendersNeeded = DefendersNeeded
DefenseQueueItem.Defense = Defense
DefenseQueueItem.DefenseTaskType = DefenseTaskType
DefenseQueueItem.AttackerDetection = AttackerDetection
DefenseQueueItem.SquadronName = SquadronName
table.insert( self.DefenseQueue, DefenseQueueItem )
self:F( { QueueItems = #self.DefenseQueue } )
end
---
-- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:ResourceTakeoff()
for DefenseQueueID, DefenseQueueItem in pairs( self.DefenseQueue ) do
self:F( { DefenseQueueID } )
end
for SquadronName, Squadron in pairs( self.DefenderSquadrons ) do
if #self.DefenseQueue > 0 then
self:F( { SquadronName, Squadron.Name, Squadron.TakeoffTime, Squadron.TakeoffInterval, timer.getTime() } )
local DefenseQueueItem = self.DefenseQueue[1]
self:F( {DefenderSquadron=DefenseQueueItem.DefenderSquadron} )
if DefenseQueueItem.SquadronName == SquadronName then
if Squadron.TakeoffTime + Squadron.TakeoffInterval < timer.getTime() then
Squadron.TakeoffTime = timer.getTime()
if DefenseQueueItem.Patrol == true then
self:ResourcePatrol( DefenseQueueItem.DefenderSquadron, DefenseQueueItem.DefendersNeeded, DefenseQueueItem.Defense, DefenseQueueItem.DefenseTaskType, DefenseQueueItem.AttackerDetection, DefenseQueueItem.SquadronName )
else
self:ResourceEngage( DefenseQueueItem.DefenderSquadron, DefenseQueueItem.DefendersNeeded, DefenseQueueItem.Defense, DefenseQueueItem.DefenseTaskType, DefenseQueueItem.AttackerDetection, DefenseQueueItem.SquadronName )
end
table.remove( self.DefenseQueue, 1 )
end
end
end
end
end
---
-- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:ResourcePatrol( DefenderSquadron, DefendersNeeded, Patrol, DefenseTaskType, AttackerDetection, SquadronName )
self:F({DefenderSquadron=DefenderSquadron})
self:F({DefendersNeeded=DefendersNeeded})
self:F({Patrol=Patrol})
self:F({DefenseTaskType=DefenseTaskType})
self:F({AttackerDetection=AttackerDetection})
self:F({SquadronName=SquadronName})
local DefenderGroup, DefenderGrouping = self:ResourceActivate( DefenderSquadron, DefendersNeeded )
if DefenderGroup then
local AI_A2G_PATROL = { SEAD = AI_A2G_SEAD, BAI = AI_A2G_BAI, CAS = AI_A2G_CAS }
local Fsm = AI_A2G_PATROL[DefenseTaskType]:New( DefenderGroup, Patrol.EngageMinSpeed, Patrol.EngageMaxSpeed, Patrol.EngageFloorAltitude, Patrol.EngageCeilingAltitude, Patrol.Zone, Patrol.PatrolFloorAltitude, Patrol.PatrolCeilingAltitude, Patrol.PatrolMinSpeed, Patrol.PatrolMaxSpeed, 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()
self:SetDefenderTask( SquadronName, DefenderGroup, DefenseTaskType, Fsm, nil, DefenderGrouping )
function Fsm:onafterTakeoff( Defender, From, Event, To )
self:F({"Defender Birth", Defender:GetName()})
--self:GetParent(self).onafterBirth( self, Defender, From, Event, To )
local DefenderName = Defender:GetName()
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
if Squadron then
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " airborne." )
Fsm:Patrol() -- Engage on the TargetSetUnit
end
end
function Fsm:onafterRTB( Defender, From, Event, To )
self:F({"Defender RTB", Defender:GetName()})
self:GetParent(self).onafterRTB( self, Defender, From, Event, To )
local DefenderName = Defender:GetName()
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " returning." )
Dispatcher:ClearDefenderTaskTarget( Defender )
end
--- @param #AI_A2G_DISPATCHER self
function Fsm:onafterLostControl( Defender, From, Event, To )
self:F({"Defender LostControl", Defender:GetName()})
self:GetParent(self).onafterHome( self, Defender, From, Event, To )
local DefenderName = Defender:GetName()
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " lost control." )
if Defender:IsAboveRunway() then
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
Defender:Destroy()
end
end
--- @param #AI_A2G_DISPATCHER self
function Fsm:onafterHome( Defender, From, Event, To, Action )
self:F({"Defender Home", Defender:GetName()})
self:GetParent(self).onafterHome( self, Defender, From, Event, To )
local DefenderName = Defender:GetName()
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " landing." )
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()
Dispatcher:ResourcePark( Squadron, Defender )
end
end
end
end
---
-- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:ResourceEngage( DefenderSquadron, DefendersNeeded, Defense, DefenseTaskType, AttackerDetection, SquadronName )
self:F({DefenderSquadron=DefenderSquadron})
self:F({DefendersNeeded=DefendersNeeded})
self:F({Defense=Defense})
self:F({DefenseTaskType=DefenseTaskType})
self:F({AttackerDetection=AttackerDetection})
self:F({SquadronName=SquadronName})
local DefenderGroup, DefenderGrouping = self:ResourceActivate( DefenderSquadron, DefendersNeeded )
if DefenderGroup then
local AI_A2G = { SEAD = AI_A2G_SEAD, BAI = AI_A2G_BAI, CAS = AI_A2G_CAS }
local Fsm = AI_A2G[DefenseTaskType]:New( DefenderGroup, Defense.EngageMinSpeed, Defense.EngageMaxSpeed, Defense.EngageFloorAltitude, Defense.EngageCeilingAltitude ) -- AI.AI_A2G_ENGAGE
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:Start()
self:SetDefenderTask( SquadronName, DefenderGroup, DefenseTaskType, Fsm, AttackerDetection, DefenderGrouping )
function Fsm:onafterTakeoff( Defender, From, Event, To )
self:F({"Defender Birth", Defender:GetName()})
--self:GetParent(self).onafterBirth( self, Defender, From, Event, To )
local DefenderName = Defender:GetName()
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
local DefenderTarget = Dispatcher:GetDefenderTaskTarget( Defender )
self:F( { DefenderTarget = DefenderTarget } )
if DefenderTarget then
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " airborne." )
Fsm:Engage( DefenderTarget.Set ) -- Engage on the TargetSetUnit
end
end
function Fsm:onafterRTB( Defender, From, Event, To )
self:F({"Defender RTB", Defender:GetName()})
local DefenderName = Defender:GetName()
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " returning." )
self:GetParent(self).onafterRTB( self, Defender, From, Event, To )
Dispatcher:ClearDefenderTaskTarget( Defender )
end
--- @param #AI_A2G_DISPATCHER self
function Fsm:onafterLostControl( Defender, From, Event, To )
self:F({"Defender LostControl", Defender:GetName()})
self:GetParent(self).onafterHome( self, Defender, From, Event, To )
local DefenderName = Defender:GetName()
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " lost control." )
if Defender:IsAboveRunway() then
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
Defender:Destroy()
end
end
--- @param #AI_A2G_DISPATCHER self
function Fsm:onafterHome( Defender, From, Event, To, Action )
self:F({"Defender Home", Defender:GetName()})
self:GetParent(self).onafterHome( self, Defender, From, Event, To )
local DefenderName = Defender:GetName()
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " landing." )
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()
Dispatcher:ResourcePark( Squadron, Defender )
end
end
end
end
--- ---
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
@ -3143,83 +3734,9 @@ do -- AI_A2G_DISPATCHER
end end
while ( DefendersNeeded > 0 ) do while ( DefendersNeeded > 0 ) do
self:ResourceQueue( false, DefenderSquadron, DefendersNeeded, Defense, DefenseTaskType, AttackerDetection, ClosestDefenderSquadronName )
local DefenderGroup, DefenderGrouping = self:ResourceActivate( DefenderSquadron, DefendersNeeded )
DefendersNeeded = DefendersNeeded - DefenderGrouping DefendersNeeded = DefendersNeeded - DefenderGrouping
DefenderCount = DefenderCount - DefenderGrouping / DefenderOverhead
if DefenderGroup then
DefenderCount = DefenderCount - DefenderGrouping / DefenderOverhead
local AI_A2G = { SEAD = AI_A2G_SEAD, BAI = AI_A2G_BAI, CAS = AI_A2G_CAS }
local Fsm = AI_A2G[DefenseTaskType]:New( DefenderGroup, Defense.EngageMinSpeed, Defense.EngageMaxSpeed, Defense.EngageFloorAltitude, Defense.EngageCeilingAltitude ) -- AI.AI_A2G_ENGAGE
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:Start()
self:SetDefenderTask( ClosestDefenderSquadronName, DefenderGroup, DefenseTaskType, Fsm, AttackerDetection, DefenderGrouping )
function Fsm:onafterTakeoff( Defender, From, Event, To )
self:F({"Defender 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 DefenderTarget = Dispatcher:GetDefenderTaskTarget( Defender )
self:F( { DefenderTarget = DefenderTarget } )
if DefenderTarget then
Fsm:Engage( DefenderTarget.Set ) -- Engage on the TargetSetUnit
end
end
function Fsm:onafterRTB( Defender, From, Event, To )
self:F({"Defender 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:onafterLostControl( Defender, From, Event, To )
self:F({"Defender LostControl", Defender:GetName()})
self:GetParent(self).onafterHome( self, Defender, From, Event, To )
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
--if Defender:IsAboveRunway() then
--Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
--Defender:Destroy()
--end
end
--- @param #AI_A2G_DISPATCHER self
function Fsm:onafterHome( Defender, From, Event, To, Action )
self:F({"Defender 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()
Dispatcher:ParkDefender( Squadron, Defender )
end
end
end -- if DefenderGCI then
end -- while ( DefendersNeeded > 0 ) do end -- while ( DefendersNeeded > 0 ) do
else else
-- No more resources, try something else. -- No more resources, try something else.
@ -3257,6 +3774,8 @@ do -- AI_A2G_DISPATCHER
self:F( { AttackerCount = AttackerCount, DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } ) self:F( { AttackerCount = AttackerCount, DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } )
local DefenderGroups = self:CountDefenders( DetectedItem, DefendersEngaged, "SEAD" ) local DefenderGroups = self:CountDefenders( DetectedItem, DefendersEngaged, "SEAD" )
if DetectedItem.IsDetected == true then if DetectedItem.IsDetected == true then
@ -3464,7 +3983,7 @@ do -- AI_A2G_DISPATCHER
if self.TacticalDisplay then if self.TacticalDisplay then
-- Show tactical situation -- Show tactical situation
Report:Add( string.format( "\n - %4s %s ( %s ): ( #%d ) %s" , DetectedItem.Type or " --- ", DetectedItem.ItemID, DetectedItem.Index, DetectedItem.Set:Count(), DetectedItem.Set:GetObjectNames() ) ) Report:Add( string.format( " - %s ( %s ): ( #%d - %4s ) %s" , DetectedItem.ItemID, DetectedItem.Index, DetectedItem.Set:Count(), DetectedItem.Type or " --- ", DetectedItem.Set:GetObjectNames() ) )
for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do
local Defender = Defender -- Wrapper.Group#GROUP local Defender = Defender -- Wrapper.Group#GROUP
if DefenderTask.Target and DefenderTask.Target.Index == DetectedItem.Index then if DefenderTask.Target and DefenderTask.Target.Index == DetectedItem.Index then
@ -3510,6 +4029,13 @@ do -- AI_A2G_DISPATCHER
end end
end end
Report:Add( string.format( "\n - %d Tasks - %d Defender Groups", TaskCount, DefenderGroupCount ) ) Report:Add( string.format( "\n - %d Tasks - %d Defender Groups", TaskCount, DefenderGroupCount ) )
Report:Add( string.format( "\n - %d Queued Aircraft Launches", #self.DefenseQueue ) )
for DefenseQueueID, DefenseQueueItem in pairs( self.DefenseQueue ) do
local DefenseQueueItem = DefenseQueueItem -- #AI_A2G_DISPATCHER.DefenseQueueItem
Report:Add( string.format( " - %s - %s", DefenseQueueItem.SquadronName, DefenseQueueItem.DefenderSquadron.TakeoffTime, DefenseQueueItem.DefenderSquadron.TakeoffInterval) )
end
self:F( Report:Text( "\n" ) ) self:F( Report:Text( "\n" ) )
trigger.action.outText( Report:Text( "\n" ), 25 ) trigger.action.outText( Report:Text( "\n" ), 25 )

View File

@ -295,10 +295,10 @@ end
--- @param Wrapper.Group#GROUP AIControllable --- @param Wrapper.Group#GROUP AIControllable
function AI_A2G_ENGAGE.EngageRoute( AIGroup, Fsm ) function AI_A2G_ENGAGE.EngageRoute( AIGroup, Fsm )
AIGroup:F( { "AI_A2G_ENGAGE.EngageRoute:", AIGroup:GetName() } ) AIGroup:I( { "AI_A2G_ENGAGE.EngageRoute:", AIGroup:GetName() } )
if AIGroup:IsAlive() then if AIGroup:IsAlive() then
Fsm:__Engage( 0.5 ) Fsm:__Engage( Fsm.TaskDelay )
--local Task = AIGroup:TaskOrbitCircle( 4000, 400 ) --local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
--AIGroup:SetTask( Task ) --AIGroup:SetTask( Task )
@ -327,7 +327,7 @@ end
function AI_A2G_ENGAGE:onafterAbort( AIGroup, From, Event, To ) function AI_A2G_ENGAGE:onafterAbort( AIGroup, From, Event, To )
AIGroup:ClearTasks() AIGroup:ClearTasks()
self:Return() self:Return()
self:__RTB( 0.5 ) self:__RTB( self.TaskDelay )
end end
@ -372,7 +372,7 @@ function AI_A2G_ENGAGE:OnEventDead( EventData )
if EventData.IniDCSUnit then if EventData.IniDCSUnit then
if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then
self:__Destroy( 1, EventData ) self:__Destroy( self.TaskDelay, EventData )
end end
end end
end end

View File

@ -234,12 +234,12 @@ function AI_A2G_PATROL:onafterPatrol( AIPatrol, From, Event, To )
self:ClearTargetDistance() self:ClearTargetDistance()
self:__Route( 1 ) self:__Route( self.TaskDelay )
AIPatrol:OnReSpawn( AIPatrol:OnReSpawn(
function( PatrolGroup ) function( PatrolGroup )
self:__Reset( 1 ) self:__Reset( self.TaskDelay )
self:__Route( 5 ) self:__Route( self.TaskDelay )
end end
) )
end end
@ -306,7 +306,7 @@ function AI_A2G_PATROL:onafterRoute( AIPatrol, From, Event, To )
AIPatrol:OptionROEReturnFire() AIPatrol:OptionROEReturnFire()
AIPatrol:OptionROTEvadeFire() AIPatrol:OptionROTEvadeFire()
AIPatrol:Route( PatrolRoute, 0.5 ) AIPatrol:Route( PatrolRoute, self.TaskDelay )
end end
end end
@ -314,10 +314,10 @@ end
--- @param Wrapper.Group#GROUP AIPatrol --- @param Wrapper.Group#GROUP AIPatrol
function AI_A2G_PATROL.Resume( AIPatrol, Fsm ) function AI_A2G_PATROL.Resume( AIPatrol, Fsm )
AIPatrol:I( { "AI_A2G_PATROL.Resume:", AIPatrol:GetName() } ) AIPatrol:F( { "AI_A2G_PATROL.Resume:", AIPatrol:GetName() } )
if AIPatrol:IsAlive() then if AIPatrol:IsAlive() then
Fsm:__Reset( 1 ) Fsm:__Reset( self.TaskDelay )
Fsm:__Route( 5 ) Fsm:__Route( self.TaskDelay )
end end
end end

View File

@ -129,107 +129,93 @@ function AI_A2G_SEAD:onafterEngage( DefenderGroup, From, Event, To, AttackSetUni
-- If it is less than 50km, then attack without a route. -- If it is less than 50km, then attack without a route.
-- Otherwise perform a route attack. -- Otherwise perform a route attack.
local EngageAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude )
local EngageSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
local DefenderCoord = DefenderGroup:GetPointVec3() local DefenderCoord = DefenderGroup:GetPointVec3()
DefenderCoord:SetY( math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude ) ) -- Ground targets don't have an altitude. DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetCoord = self.AttackSetUnit:GetFirst():GetPointVec3() local TargetCoord = self.AttackSetUnit:GetFirst():GetPointVec3()
TargetCoord:SetY( math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude ) ) -- Ground targets don't have an altitude. TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord ) local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
-- if TargetDistance >= 50000 then local EngageRoute = {}
--- Calculate the target route point.
local FromWP = DefenderCoord:WaypointAir(
self.PatrolAltType or "RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
EngageSpeed,
true
)
EngageRoute[#EngageRoute+1] = FromWP
self:SetTargetDistance( TargetCoord ) -- For RTB status check
local FromEngageAngle = TargetCoord:GetAngleDegrees( TargetCoord:GetDirectionVec3( DefenderCoord ) )
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 25000 )
--- Create a route point of type air, 50km from the center of the attack point.
local ToWP = TargetCoord:Translate( EngageDistance, FromEngageAngle, true ):WaypointAir(
self.PatrolAltType or "RADIO",
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
EngageSpeed,
true
)
local EngageRoute = {} EngageRoute[#EngageRoute+1] = ToWP
local AttackTasks = {}
self.AttackSetUnit.AttackIndex = self.AttackSetUnit.AttackIndex and self.AttackSetUnit.AttackIndex + 1 or 1
if self.AttackSetUnit.AttackIndex > self.AttackSetUnit:Count() then
self.AttackSetUnit.AttackIndex = 1
end
local AttackSetUnitPerThreatLevel = self.AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed ) for AttackUnitID, AttackUnit in ipairs( AttackSetUnitPerThreatLevel ) do
if AttackUnitID >= self.AttackSetUnit.AttackIndex then
--- Calculate the target route point. if AttackUnit then
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
local FromWP = DefenderCoord:WaypointAir( local HasRadar = AttackUnit:HasSEAD()
self.PatrolAltType or "RADIO", if HasRadar then
POINT_VEC3.RoutePointType.TurningPoint, self:F( { "SEAD Unit:", AttackUnit:GetName() } )
POINT_VEC3.RoutePointAction.TurningPoint, AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, false, false, nil, nil, EngageAltitude )
ToTargetSpeed, end
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 or "RADIO",
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 end
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 ) if #AttackTasks == 0 then
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return()
self:__RTB( self.TaskDelay )
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, self.TaskDelay )
-- 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 end
else else
self:E( DefenderGroupName .. ": No targets found -> Going RTB") self:E( DefenderGroupName .. ": No targets found -> Going RTB")
self:Return() self:Return()
self:__RTB( 0.5 ) self:__RTB( self.TaskDelay )
end end
end end

View File

@ -51,6 +51,8 @@ AI_AIR = {
ClassName = "AI_AIR", ClassName = "AI_AIR",
} }
AI_AIR.TaskDelay = 0.5 -- The delay of each task given to the AI.
--- Creates a new AI_AIR process. --- Creates a new AI_AIR process.
-- @param #AI_AIR self -- @param #AI_AIR self
-- @param Wrapper.Group#GROUP AIGroup The group object to receive the A2G Process. -- @param Wrapper.Group#GROUP AIGroup The group object to receive the A2G Process.
@ -64,6 +66,8 @@ function AI_AIR:New( AIGroup )
self:SetStartState( "Stopped" ) self:SetStartState( "Stopped" )
self:AddTransition( "*", "Queue", "Queued" )
self:AddTransition( "*", "Start", "Started" ) self:AddTransition( "*", "Start", "Started" )
--- Start Handler OnBefore for AI_AIR --- Start Handler OnBefore for AI_AIR
@ -400,6 +404,8 @@ function AI_AIR:SetDamageThreshold( PatrolDamageThreshold )
return self return self
end end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings. --- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
-- @param #AI_AIR self -- @param #AI_AIR self
-- @return #AI_AIR self -- @return #AI_AIR self
@ -520,7 +526,7 @@ function AI_AIR:onafterStatus()
end end
if RTB == true then if RTB == true then
self:__RTB( 0.5 ) self:__RTB( self.TaskDelay )
end end
if not self:Is("Home") then if not self:Is("Home") then
@ -537,7 +543,7 @@ function AI_AIR.RTBRoute( AIGroup, Fsm )
AIGroup:F( { "AI_AIR.RTBRoute:", AIGroup:GetName() } ) AIGroup:F( { "AI_AIR.RTBRoute:", AIGroup:GetName() } )
if AIGroup:IsAlive() then if AIGroup:IsAlive() then
Fsm:__RTB( 0.5 ) Fsm:RTB()
end end
end end
@ -547,7 +553,7 @@ function AI_AIR.RTBHold( AIGroup, Fsm )
AIGroup:F( { "AI_AIR.RTBHold:", AIGroup:GetName() } ) AIGroup:F( { "AI_AIR.RTBHold:", AIGroup:GetName() } )
if AIGroup:IsAlive() then if AIGroup:IsAlive() then
Fsm:__RTB( 0.5 ) Fsm:__RTB( Fsm.TaskDelay )
Fsm:Return() Fsm:Return()
local Task = AIGroup:TaskOrbitCircle( 4000, 400 ) local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
AIGroup:SetTask( Task ) AIGroup:SetTask( Task )
@ -573,19 +579,29 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
--- Calculate the target route point. --- Calculate the target route point.
local CurrentCoord = AIGroup:GetCoordinate() local FromCoord = AIGroup:GetCoordinate()
local ToTargetCoord = self.HomeAirbase:GetCoordinate() local ToTargetCoord = self.HomeAirbase:GetCoordinate()
local ToTargetSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed ) local ToTargetSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed )
local ToAirbaseAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) ) local ToAirbaseAngle = FromCoord:GetAngleDegrees( FromCoord:GetDirectionVec3( ToTargetCoord ) )
local Distance = CurrentCoord:Get2DDistance( ToTargetCoord ) local Distance = FromCoord:Get2DDistance( ToTargetCoord )
local ToAirbaseCoord = CurrentCoord:Translate( 5000, ToAirbaseAngle ) local ToAirbaseCoord = FromCoord:Translate( 5000, ToAirbaseAngle )
if Distance < 5000 then if Distance < 5000 then
self:E( "RTB and near the airbase!" ) self:E( "RTB and near the airbase!" )
self:Home() self:Home()
return return
end end
--- Create a route point of type air.
local FromRTBRoutePoint = FromCoord:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
--- Create a route point of type air. --- Create a route point of type air.
local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir( local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir(
self.PatrolAltType, self.PatrolAltType,
@ -595,21 +611,19 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
true true
) )
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint EngageRoute[#EngageRoute+1] = FromRTBRoutePoint
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
local Tasks = {}
Tasks[#Tasks+1] = AIGroup:TaskFunction( "AI_AIR.RTBRoute", self )
EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( Tasks )
AIGroup:OptionROEHoldFire() AIGroup:OptionROEHoldFire()
AIGroup:OptionROTEvadeFire() AIGroup:OptionROTEvadeFire()
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
AIGroup:WayPointInitialize( EngageRoute )
local Tasks = {}
Tasks[#Tasks+1] = AIGroup:TaskFunction( "AI_AIR.RTBRoute", self )
EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( Tasks )
--- NOW ROUTE THE GROUP! --- NOW ROUTE THE GROUP!
AIGroup:Route( EngageRoute, 0.5 ) AIGroup:Route( EngageRoute, self.TaskDelay )
end end
@ -656,7 +670,7 @@ function AI_AIR.Resume( AIGroup, Fsm )
AIGroup:I( { "AI_AIR.Resume:", AIGroup:GetName() } ) AIGroup:I( { "AI_AIR.Resume:", AIGroup:GetName() } )
if AIGroup:IsAlive() then if AIGroup:IsAlive() then
Fsm:__RTB( 0.5 ) Fsm:__RTB( Fsm.TaskDelay )
end end
end end
@ -676,10 +690,19 @@ function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
--- Calculate the target route point. --- Calculate the target route point.
local CurrentCoord = AIGroup:GetCoordinate() local FromRefuelCoord = AIGroup:GetCoordinate()
local ToRefuelCoord = Tanker:GetCoordinate() local ToRefuelCoord = Tanker:GetCoordinate()
local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
--- Create a route point of type air.
local FromRefuelRoutePoint = FromRefuelCoord:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToRefuelSpeed,
true
)
--- Create a route point of type air. --- Create a route point of type air.
local ToRefuelRoutePoint = ToRefuelCoord:WaypointAir( local ToRefuelRoutePoint = ToRefuelCoord:WaypointAir(
self.PatrolAltType, self.PatrolAltType,
@ -691,7 +714,7 @@ function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
self:F( { ToRefuelSpeed = ToRefuelSpeed } ) self:F( { ToRefuelSpeed = ToRefuelSpeed } )
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint RefuelRoute[#RefuelRoute+1] = FromRefuelRoutePoint
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
AIGroup:OptionROEHoldFire() AIGroup:OptionROEHoldFire()
@ -702,7 +725,7 @@ function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
Tasks[#Tasks+1] = AIGroup:TaskFunction( self:GetClassName() .. ".Resume", self ) Tasks[#Tasks+1] = AIGroup:TaskFunction( self:GetClassName() .. ".Resume", self )
RefuelRoute[#RefuelRoute].task = AIGroup:TaskCombo( Tasks ) RefuelRoute[#RefuelRoute].task = AIGroup:TaskCombo( Tasks )
AIGroup:Route( RefuelRoute, 0.5 ) AIGroup:Route( RefuelRoute, self.TaskDelay )
else else
self:RTB() self:RTB()
end end
@ -725,7 +748,7 @@ function AI_AIR:OnCrash( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
self:E( self.Controllable:GetUnits() ) self:E( self.Controllable:GetUnits() )
if #self.Controllable:GetUnits() == 1 then if #self.Controllable:GetUnits() == 1 then
self:__Crash( 1, EventData ) self:__Crash( self.TaskDelay, EventData )
end end
end end
end end
@ -735,7 +758,7 @@ end
function AI_AIR:OnEjection( EventData ) function AI_AIR:OnEjection( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
self:__Eject( 1, EventData ) self:__Eject( self.TaskDelay, EventData )
end end
end end
@ -744,6 +767,6 @@ end
function AI_AIR:OnPilotDead( EventData ) function AI_AIR:OnPilotDead( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
self:__PilotDead( 1, EventData ) self:__PilotDead( self.TaskDelay, EventData )
end end
end end

View File

@ -2033,6 +2033,54 @@ do -- SET_UNIT
return self return self
end end
--- Get the SET of the SET_UNIT **sorted per Threat Level**.
--
-- @param #SET_UNIT self
-- @param #number FromThreatLevel The TreatLevel to start the evaluation **From** (this must be a value between 0 and 10).
-- @param #number ToThreatLevel The TreatLevel to stop the evaluation **To** (this must be a value between 0 and 10).
-- @return #SET_UNIT self
-- @usage
--
--
function SET_UNIT:GetSetPerThreatLevel( FromThreatLevel, ToThreatLevel )
self:F2( arg )
local ThreatLevelSet = {}
if self:Count() ~= 0 then
for UnitName, UnitObject in pairs( self.Set ) do
local Unit = UnitObject -- Wrapper.Unit#UNIT
local ThreatLevel = Unit:GetThreatLevel()
ThreatLevelSet[ThreatLevel] = ThreatLevelSet[ThreatLevel] or {}
ThreatLevelSet[ThreatLevel].Set = ThreatLevelSet[ThreatLevel].Set or {}
ThreatLevelSet[ThreatLevel].Set[UnitName] = UnitObject
self:F( { ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set } )
end
local OrderedPerThreatLevelSet = {}
local ThreatLevelIncrement = FromThreatLevel <= ToThreatLevel and 1 or -1
for ThreatLevel = FromThreatLevel, ToThreatLevel, ThreatLevelIncrement do
self:F( { ThreatLevel = ThreatLevel } )
local ThreatLevelItem = ThreatLevelSet[ThreatLevel]
if ThreatLevelItem then
for UnitName, UnitObject in pairs( ThreatLevelItem.Set ) do
table.insert( OrderedPerThreatLevelSet, UnitObject )
end
end
end
return OrderedPerThreatLevelSet
end
end
--- Iterate the SET_UNIT **sorted *per Threat Level** and call an interator function for each **alive** UNIT, providing the UNIT and optional parameters. --- Iterate the SET_UNIT **sorted *per Threat Level** and call an interator function for each **alive** UNIT, providing the UNIT and optional parameters.
-- --
-- @param #SET_UNIT self -- @param #SET_UNIT self

View File

@ -46,6 +46,7 @@ do -- DETECTION MANAGER
--- @type DETECTION_MANAGER --- @type DETECTION_MANAGER
-- @field Core.Set#SET_GROUP SetGroup The groups to which the FAC will report to. -- @field Core.Set#SET_GROUP SetGroup The groups to which the FAC will report to.
-- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects.
-- @field Tasking.CommandCenter#COMMANDCENTER CC The command center that is used to communicate with the players.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- DETECTION_MANAGER class. --- DETECTION_MANAGER class.
@ -218,6 +219,33 @@ do -- DETECTION MANAGER
return self._ReportDisplayTime return self._ReportDisplayTime
end end
--- Set a command center to communicate actions to the players reporting to the command center.
-- @param #DETECTION_MANAGER self
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
-- @return #DETECTION_MANGER self
function DETECTION_MANAGER:SetCommandCenter( CommandCenter )
self.CC = CommandCenter
return self
end
--- Send an information message to the players reporting to the command center.
-- @param #DETECTION_MANAGER self
-- @param #string Message The message to be sent.
-- @return #DETECTION_MANGER self
function DETECTION_MANAGER:MessageToPlayers( Message )
if self.CC then
self.CC:MessageToAll( Message )
end
return self
end
--- Reports the detected items to the @{Core.Set#SET_GROUP}. --- Reports the detected items to the @{Core.Set#SET_GROUP}.
-- @param #DETECTION_MANAGER self -- @param #DETECTION_MANAGER self
-- @param Functional.Detection#DETECTION_BASE Detection -- @param Functional.Detection#DETECTION_BASE Detection

View File

@ -400,7 +400,7 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime )
local Controller = self:_GetController() local Controller = self:_GetController()
--self:I( "Before SetTask" ) --self:I( "Before SetTask" )
Controller:setTask( DCSTask ) Controller:setTask( DCSTask )
--self:I( "After SetTask" ) self:I( { ControllableName = self:GetName(), DCSTask = DCSTask } )
else else
BASE:E( { DCSControllableName .. " is not alive anymore.", DCSTask = DCSTask } ) BASE:E( { DCSControllableName .. " is not alive anymore.", DCSTask = DCSTask } )
end end
@ -408,6 +408,7 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime )
if not WaitTime or WaitTime == 0 then if not WaitTime or WaitTime == 0 then
SetTask( self, DCSTask ) SetTask( self, DCSTask )
self:I( { ControllableName = self:GetName(), DCSTask = DCSTask } )
else else
self.TaskScheduler:Schedule( self, SetTask, { DCSTask }, WaitTime ) self.TaskScheduler:Schedule( self, SetTask, { DCSTask }, WaitTime )
end end
@ -1057,7 +1058,7 @@ end
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #number Altitude The altitude [m] to hold the position. -- @param #number Altitude The altitude [m] to hold the position.
-- @param #number Speed The speed [m/s] flying when holding the position. -- @param #number Speed The speed [m/s] flying when holding the position.
-- @param Core.Point#COORDINATE Coordinate The coordinate where to orbit. -- @param Core.Point#COORDINATE Coordinate (optional) The coordinate where to orbit. If the coordinate is not given, then the current position of the controllable is used.
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
function CONTROLLABLE:TaskOrbitCircle( Altitude, Speed, Coordinate ) function CONTROLLABLE:TaskOrbitCircle( Altitude, Speed, Coordinate )
self:F2( { self.ControllableName, Altitude, Speed } ) self:F2( { self.ControllableName, Altitude, Speed } )