mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Merge branch 'develop' into FF/Develop
This commit is contained in:
commit
331315a83e
@ -345,7 +345,7 @@ function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
||||
AIPatrol:OptionROEReturnFire()
|
||||
AIPatrol:OptionROTEvadeFire()
|
||||
|
||||
AIPatrol:Route( PatrolRoute, 0.5 )
|
||||
AIPatrol:Route( PatrolRoute, 0.5)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -74,80 +74,89 @@ function AI_A2G_BAI:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
|
||||
|
||||
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.
|
||||
-- If it is less than 10km, then attack without a route.
|
||||
-- Otherwise perform a route attack.
|
||||
|
||||
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()
|
||||
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 EngageRoute = {}
|
||||
|
||||
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local FromWP = DefenderCoord:WaypointAir(
|
||||
self.PatrolAltType or "RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
EngageSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = FromWP
|
||||
|
||||
local ToCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetTargetDistance( ToCoord ) -- For RTB status check
|
||||
self:SetTargetDistance( TargetCoord ) -- 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.
|
||||
local ToWP = ToCoord:Translate( 10000, FromEngageAngle ):WaypointAir(
|
||||
local ToWP = TargetCoord:Translate( EngageDistance, FromEngageAngle ):WaypointAir(
|
||||
self.PatrolAltType or "RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
EngageSpeed,
|
||||
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
|
||||
self.AttackSetUnit.AttackIndex = self.AttackSetUnit.AttackIndex and self.AttackSetUnit.AttackIndex + 1 or 1
|
||||
|
||||
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
|
||||
self:T( { "Engage Unit evaluation:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsGround() } )
|
||||
self:T( { "Eliminating Unit:", AttackUnit:GetName() } )
|
||||
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
|
||||
self:T( { "BAI Unit:", AttackUnit:GetName() } )
|
||||
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, false, false, nil, nil, EngageAltitude )
|
||||
end
|
||||
end
|
||||
|
||||
if #AttackTasks == 0 then
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( 0.5 )
|
||||
self:__RTB( self.TaskDelay )
|
||||
else
|
||||
DefenderGroup:OptionROEOpenFire()
|
||||
DefenderGroup:OptionROTEvadeFire()
|
||||
DefenderGroup:OptionKeepWeaponsOnThreat()
|
||||
|
||||
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self )
|
||||
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
|
||||
end
|
||||
|
||||
DefenderGroup:Route( EngageRoute, 0.5 )
|
||||
DefenderGroup:Route( EngageRoute, self.TaskDelay )
|
||||
end
|
||||
else
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( 0.5 )
|
||||
self:__RTB( self.TaskDelay )
|
||||
end
|
||||
end
|
||||
|
||||
@ -74,29 +74,26 @@ function AI_A2G_CAS:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
|
||||
|
||||
if DefenderGroup:IsAlive() then
|
||||
|
||||
-- Determine the distance to the target.
|
||||
-- If it is less than 10km, then attack without a route.
|
||||
-- Otherwise perform a route attack.
|
||||
local EngageAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude )
|
||||
local EngageSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
|
||||
|
||||
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()
|
||||
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 EngageRoute = {}
|
||||
|
||||
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local FromWP = DefenderCoord:WaypointAir(
|
||||
self.PatrolAltType or "RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
EngageSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
@ -105,50 +102,58 @@ function AI_A2G_CAS:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
|
||||
self:SetTargetDistance( TargetCoord ) -- For RTB status check
|
||||
|
||||
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.
|
||||
local ToWP = TargetCoord:Translate( EngageDistance, FromEngageAngle ):WaypointAir(
|
||||
local ToWP = TargetCoord:Translate( EngageDistance, FromEngageAngle, true ):WaypointAir(
|
||||
self.PatrolAltType or "RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
EngageSpeed,
|
||||
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
|
||||
self.AttackSetUnit.AttackIndex = self.AttackSetUnit.AttackIndex and self.AttackSetUnit.AttackIndex + 1 or 1
|
||||
|
||||
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
|
||||
self:T( { "Eliminating Unit:", AttackUnit:GetName() } )
|
||||
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
|
||||
self:F( { "CAS Unit:", AttackUnit:GetName() } )
|
||||
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, false, false, nil, nil, EngageAltitude )
|
||||
end
|
||||
end
|
||||
|
||||
if #AttackTasks == 0 then
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( 0.5 )
|
||||
self:__RTB( self.TaskDelay )
|
||||
else
|
||||
DefenderGroup:OptionROEOpenFire()
|
||||
DefenderGroup:OptionROTEvadeFire()
|
||||
DefenderGroup:OptionKeepWeaponsOnThreat()
|
||||
|
||||
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self )
|
||||
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
|
||||
end
|
||||
|
||||
DefenderGroup:Route( EngageRoute, 0.5 )
|
||||
DefenderGroup:Route( EngageRoute, self.TaskDelay )
|
||||
end
|
||||
else
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( 0.5 )
|
||||
self:__RTB( self.TaskDelay )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -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.
|
||||
--
|
||||
-- 
|
||||
-- 
|
||||
--
|
||||
-- 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.
|
||||
@ -493,9 +493,9 @@ do -- AI_A2G_DISPATCHER
|
||||
--
|
||||
-- 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,
|
||||
-- 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.
|
||||
--
|
||||
-- ### 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:
|
||||
--
|
||||
@ -558,12 +558,350 @@ do -- AI_A2G_DISPATCHER
|
||||
-- A2GDispatcher:SetSquadronBaiPatrol( "Maykop BAI", PatrolZone, 800, 900, 50, 80, 250, 300 )
|
||||
-- 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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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**.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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":
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- -- 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
|
||||
AI_A2G_DISPATCHER = {
|
||||
ClassName = "AI_A2G_DISPATCHER",
|
||||
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.
|
||||
-- @type AI_A2G_DISPATCHER.DefenseCoordinates
|
||||
@ -587,6 +925,32 @@ do -- AI_A2G_DISPATCHER
|
||||
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.
|
||||
-- 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.
|
||||
@ -759,6 +1123,8 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
self:SetDefenseReactivityMedium()
|
||||
|
||||
self.TakeoffScheduleID = self:ScheduleRepeat( 10, 10, 0, nil, self.ResourceTakeoff, self )
|
||||
|
||||
self:__Start( 5 )
|
||||
|
||||
return self
|
||||
@ -774,14 +1140,14 @@ do -- AI_A2G_DISPATCHER
|
||||
for SquadronName, DefenderSquadron in pairs( self.DefenderSquadrons ) do
|
||||
DefenderSquadron.Resource = {}
|
||||
for Resource = 1, DefenderSquadron.ResourceCount or 0 do
|
||||
self:ParkDefender( DefenderSquadron )
|
||||
self:ResourcePark( DefenderSquadron )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @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 Spawn = DefenderSquadron.Spawn[ TemplateID ] -- Core.Spawn#SPAWN
|
||||
Spawn:InitGrouping( 1 )
|
||||
@ -838,7 +1204,7 @@ do -- AI_A2G_DISPATCHER
|
||||
self:RemoveDefenderFromSquadron( Squadron, Defender )
|
||||
end
|
||||
DefenderUnit:Destroy()
|
||||
self:ParkDefender( Squadron, Defender )
|
||||
self:ResourcePark( Squadron, Defender )
|
||||
return
|
||||
end
|
||||
if DefenderUnit:GetLife() ~= DefenderUnit:GetLife0() then
|
||||
@ -865,7 +1231,7 @@ do -- AI_A2G_DISPATCHER
|
||||
self:RemoveDefenderFromSquadron( Squadron, Defender )
|
||||
end
|
||||
DefenderUnit:Destroy()
|
||||
self:ParkDefender( Squadron, Defender )
|
||||
self:ResourcePark( Squadron, Defender )
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1352,7 +1718,6 @@ do -- AI_A2G_DISPATCHER
|
||||
-- @return #AI_A2G_DISPATCHER
|
||||
function AI_A2G_DISPATCHER:SetSquadron( SquadronName, AirbaseName, TemplatePrefixes, ResourceCount )
|
||||
|
||||
|
||||
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
|
||||
|
||||
local DefenderSquadron = self.DefenderSquadrons[SquadronName]
|
||||
@ -1379,6 +1744,8 @@ do -- AI_A2G_DISPATCHER
|
||||
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.
|
||||
|
||||
self:SetSquadronTakeoffInterval( SquadronName, 0 )
|
||||
|
||||
self:F( { Squadron = {SquadronName, AirbaseName, TemplatePrefixes, ResourceCount } } )
|
||||
|
||||
return self
|
||||
@ -1449,6 +1816,28 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
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.
|
||||
-- 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
|
||||
|
||||
local SquadronOverhead = self:GetSquadronOverhead( DefenderSquadronName )
|
||||
self:F( { SquadronOverhead = SquadronOverhead } )
|
||||
if DefenderSize then
|
||||
DefendersEngaged = DefendersEngaged + DefenderSize
|
||||
DefendersMissing = DefendersMissing - DefenderSize / SquadronOverhead
|
||||
@ -2820,6 +3210,15 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
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 } )
|
||||
|
||||
return DefendersTotal, DefendersEngaged, DefendersMissing
|
||||
@ -2950,19 +3349,91 @@ do -- AI_A2G_DISPATCHER
|
||||
-- @param #AI_A2G_DISPATCHER self
|
||||
function AI_A2G_DISPATCHER:onafterPatrol( From, Event, To, SquadronName, DefenseTaskType )
|
||||
|
||||
self:F({SquadronName = SquadronName})
|
||||
|
||||
local DefenderSquadron, Patrol = self:CanPatrol( SquadronName, DefenseTaskType )
|
||||
|
||||
if Patrol then
|
||||
self:ResourceQueue( true, DefenderSquadron, nil, Patrol, DefenseTaskType, nil, SquadronName )
|
||||
end
|
||||
|
||||
local DefenderPatrol, DefenderGrouping = self:ResourceActivate( DefenderSquadron )
|
||||
end
|
||||
|
||||
if DefenderPatrol then
|
||||
---
|
||||
-- @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( DefenderPatrol, Patrol.EngageMinSpeed, Patrol.EngageMaxSpeed, Patrol.EngageFloorAltitude, Patrol.EngageCeilingAltitude, Patrol.Zone, Patrol.PatrolFloorAltitude, Patrol.PatrolCeilingAltitude, Patrol.PatrolMinSpeed, Patrol.PatrolMaxSpeed, Patrol.AltType )
|
||||
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 )
|
||||
@ -2971,34 +3442,58 @@ do -- AI_A2G_DISPATCHER
|
||||
Fsm:SetTanker( DefenderSquadron.TankerName or self.DefenderDefault.TankerName )
|
||||
Fsm:Start()
|
||||
|
||||
self:SetDefenderTask( SquadronName, DefenderPatrol, DefenseTaskType, Fsm, nil, DefenderGrouping )
|
||||
self:SetDefenderTask( SquadronName, DefenderGroup, DefenseTaskType, Fsm, nil, DefenderGrouping )
|
||||
|
||||
function Fsm:onafterTakeoff( Defender, From, Event, To )
|
||||
self:F({"Patrol Birth", Defender:GetName()})
|
||||
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
|
||||
Fsm:__Patrol( 2 ) -- Start Patrolling
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " airborne." )
|
||||
Fsm:Patrol() -- Engage on the TargetSetUnit
|
||||
end
|
||||
end
|
||||
|
||||
function Fsm:onafterRTB( Defender, From, Event, To )
|
||||
self:F({"Patrol RTB", Defender:GetName()})
|
||||
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:onafterHome( Defender, From, Event, To, Action )
|
||||
self:F({"Patrol Home", Defender:GetName()})
|
||||
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 )
|
||||
@ -3008,8 +3503,7 @@ do -- AI_A2G_DISPATCHER
|
||||
if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2G_DISPATCHER.Landing.NearAirbase then
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
|
||||
Defender:Destroy()
|
||||
Dispatcher:ParkDefender( Squadron, Defender )
|
||||
end
|
||||
Dispatcher:ResourcePark( Squadron, Defender )
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -3017,6 +3511,103 @@ do -- AI_A2G_DISPATCHER
|
||||
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
|
||||
function AI_A2G_DISPATCHER:onafterEngage( From, Event, To, AttackerDetection, Defenders )
|
||||
@ -3143,83 +3734,9 @@ do -- AI_A2G_DISPATCHER
|
||||
end
|
||||
|
||||
while ( DefendersNeeded > 0 ) do
|
||||
|
||||
local DefenderGroup, DefenderGrouping = self:ResourceActivate( DefenderSquadron, DefendersNeeded )
|
||||
|
||||
self:ResourceQueue( false, DefenderSquadron, DefendersNeeded, Defense, DefenseTaskType, AttackerDetection, ClosestDefenderSquadronName )
|
||||
DefendersNeeded = DefendersNeeded - DefenderGrouping
|
||||
|
||||
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
|
||||
else
|
||||
-- No more resources, try something else.
|
||||
@ -3258,6 +3775,8 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
local DefenderGroups = self:CountDefenders( DetectedItem, DefendersEngaged, "SEAD" )
|
||||
|
||||
|
||||
|
||||
if DetectedItem.IsDetected == true then
|
||||
|
||||
return DefendersTotal, DefendersEngaged, DefendersMissing, DefenderGroups
|
||||
@ -3464,7 +3983,7 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
if self.TacticalDisplay then
|
||||
-- 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
|
||||
local Defender = Defender -- Wrapper.Group#GROUP
|
||||
if DefenderTask.Target and DefenderTask.Target.Index == DetectedItem.Index then
|
||||
@ -3511,6 +4030,13 @@ do -- AI_A2G_DISPATCHER
|
||||
end
|
||||
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" ) )
|
||||
trigger.action.outText( Report:Text( "\n" ), 25 )
|
||||
end
|
||||
|
||||
@ -295,10 +295,10 @@ end
|
||||
--- @param Wrapper.Group#GROUP AIControllable
|
||||
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
|
||||
Fsm:__Engage( 0.5 )
|
||||
Fsm:__Engage( Fsm.TaskDelay )
|
||||
|
||||
--local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
|
||||
--AIGroup:SetTask( Task )
|
||||
@ -327,7 +327,7 @@ end
|
||||
function AI_A2G_ENGAGE:onafterAbort( AIGroup, From, Event, To )
|
||||
AIGroup:ClearTasks()
|
||||
self:Return()
|
||||
self:__RTB( 0.5 )
|
||||
self:__RTB( self.TaskDelay )
|
||||
end
|
||||
|
||||
|
||||
@ -372,7 +372,7 @@ function AI_A2G_ENGAGE:OnEventDead( EventData )
|
||||
|
||||
if EventData.IniDCSUnit then
|
||||
if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then
|
||||
self:__Destroy( 1, EventData )
|
||||
self:__Destroy( self.TaskDelay, EventData )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -234,12 +234,12 @@ function AI_A2G_PATROL:onafterPatrol( AIPatrol, From, Event, To )
|
||||
|
||||
self:ClearTargetDistance()
|
||||
|
||||
self:__Route( 1 )
|
||||
self:__Route( self.TaskDelay )
|
||||
|
||||
AIPatrol:OnReSpawn(
|
||||
function( PatrolGroup )
|
||||
self:__Reset( 1 )
|
||||
self:__Route( 5 )
|
||||
self:__Reset( self.TaskDelay )
|
||||
self:__Route( self.TaskDelay )
|
||||
end
|
||||
)
|
||||
end
|
||||
@ -306,7 +306,7 @@ function AI_A2G_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
||||
AIPatrol:OptionROEReturnFire()
|
||||
AIPatrol:OptionROTEvadeFire()
|
||||
|
||||
AIPatrol:Route( PatrolRoute, 0.5 )
|
||||
AIPatrol:Route( PatrolRoute, self.TaskDelay )
|
||||
end
|
||||
|
||||
end
|
||||
@ -314,10 +314,10 @@ end
|
||||
--- @param Wrapper.Group#GROUP AIPatrol
|
||||
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
|
||||
Fsm:__Reset( 1 )
|
||||
Fsm:__Route( 5 )
|
||||
Fsm:__Reset( self.TaskDelay )
|
||||
Fsm:__Route( self.TaskDelay )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -129,19 +129,19 @@ function AI_A2G_SEAD:onafterEngage( DefenderGroup, From, Event, To, AttackSetUni
|
||||
-- If it is less than 50km, then attack without a route.
|
||||
-- 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()
|
||||
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()
|
||||
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 )
|
||||
|
||||
-- if TargetDistance >= 50000 then
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
@ -149,40 +149,48 @@ function AI_A2G_SEAD:onafterEngage( DefenderGroup, From, Event, To, AttackSetUni
|
||||
self.PatrolAltType or "RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
EngageSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = FromWP
|
||||
|
||||
local ToCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetTargetDistance( ToCoord ) -- For RTB status check
|
||||
self:SetTargetDistance( TargetCoord ) -- 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 25000 )
|
||||
|
||||
--- Create a route point of type air, 50km from the center of the attack point.
|
||||
local ToWP = ToCoord:Translate( 50000, FromEngageAngle ):WaypointAir(
|
||||
|
||||
local ToWP = TargetCoord:Translate( EngageDistance, FromEngageAngle, true ):WaypointAir(
|
||||
self.PatrolAltType or "RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
EngageSpeed,
|
||||
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
|
||||
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 )
|
||||
|
||||
for AttackUnitID, AttackUnit in ipairs( AttackSetUnitPerThreatLevel ) do
|
||||
if AttackUnitID >= self.AttackSetUnit.AttackIndex then
|
||||
if AttackUnit then
|
||||
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 )
|
||||
self:F( { "SEAD Unit:", AttackUnit:GetName() } )
|
||||
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, false, false, nil, nil, EngageAltitude )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -190,7 +198,7 @@ function AI_A2G_SEAD:onafterEngage( DefenderGroup, From, Event, To, AttackSetUni
|
||||
if #AttackTasks == 0 then
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( 0.5 )
|
||||
self:__RTB( self.TaskDelay )
|
||||
else
|
||||
DefenderGroup:OptionROEOpenFire()
|
||||
DefenderGroup:OptionROTVertical()
|
||||
@ -201,35 +209,13 @@ function AI_A2G_SEAD:onafterEngage( DefenderGroup, From, Event, To, AttackSetUni
|
||||
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
|
||||
end
|
||||
|
||||
DefenderGroup:Route( EngageRoute, 2 )
|
||||
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
|
||||
else
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( 0.5 )
|
||||
self:__RTB( self.TaskDelay )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -51,6 +51,8 @@ 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.
|
||||
-- @param #AI_AIR self
|
||||
-- @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:AddTransition( "*", "Queue", "Queued" )
|
||||
|
||||
self:AddTransition( "*", "Start", "Started" )
|
||||
|
||||
--- Start Handler OnBefore for AI_AIR
|
||||
@ -400,6 +404,8 @@ function AI_AIR:SetDamageThreshold( PatrolDamageThreshold )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
|
||||
-- @param #AI_AIR self
|
||||
-- @return #AI_AIR self
|
||||
@ -520,7 +526,7 @@ function AI_AIR:onafterStatus()
|
||||
end
|
||||
|
||||
if RTB == true then
|
||||
self:__RTB( 0.5 )
|
||||
self:__RTB( self.TaskDelay )
|
||||
end
|
||||
|
||||
if not self:Is("Home") then
|
||||
@ -537,7 +543,7 @@ function AI_AIR.RTBRoute( AIGroup, Fsm )
|
||||
AIGroup:F( { "AI_AIR.RTBRoute:", AIGroup:GetName() } )
|
||||
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( 0.5 )
|
||||
Fsm:RTB()
|
||||
end
|
||||
|
||||
end
|
||||
@ -547,7 +553,7 @@ function AI_AIR.RTBHold( AIGroup, Fsm )
|
||||
|
||||
AIGroup:F( { "AI_AIR.RTBHold:", AIGroup:GetName() } )
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( 0.5 )
|
||||
Fsm:__RTB( Fsm.TaskDelay )
|
||||
Fsm:Return()
|
||||
local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
|
||||
AIGroup:SetTask( Task )
|
||||
@ -573,19 +579,29 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIGroup:GetCoordinate()
|
||||
local FromCoord = AIGroup:GetCoordinate()
|
||||
local ToTargetCoord = self.HomeAirbase:GetCoordinate()
|
||||
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
|
||||
self:E( "RTB and near the airbase!" )
|
||||
self:Home()
|
||||
return
|
||||
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.
|
||||
local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
@ -595,21 +611,19 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
true
|
||||
)
|
||||
|
||||
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: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!
|
||||
AIGroup:Route( EngageRoute, 0.5 )
|
||||
AIGroup:Route( EngageRoute, self.TaskDelay )
|
||||
|
||||
end
|
||||
|
||||
@ -656,7 +670,7 @@ function AI_AIR.Resume( AIGroup, Fsm )
|
||||
|
||||
AIGroup:I( { "AI_AIR.Resume:", AIGroup:GetName() } )
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( 0.5 )
|
||||
Fsm:__RTB( Fsm.TaskDelay )
|
||||
end
|
||||
|
||||
end
|
||||
@ -676,10 +690,19 @@ function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIGroup:GetCoordinate()
|
||||
local FromRefuelCoord = AIGroup:GetCoordinate()
|
||||
local ToRefuelCoord = Tanker:GetCoordinate()
|
||||
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.
|
||||
local ToRefuelRoutePoint = ToRefuelCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
@ -691,7 +714,7 @@ function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
|
||||
|
||||
self:F( { ToRefuelSpeed = ToRefuelSpeed } )
|
||||
|
||||
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||
RefuelRoute[#RefuelRoute+1] = FromRefuelRoutePoint
|
||||
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||
|
||||
AIGroup:OptionROEHoldFire()
|
||||
@ -702,7 +725,7 @@ function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
|
||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( self:GetClassName() .. ".Resume", self )
|
||||
RefuelRoute[#RefuelRoute].task = AIGroup:TaskCombo( Tasks )
|
||||
|
||||
AIGroup:Route( RefuelRoute, 0.5 )
|
||||
AIGroup:Route( RefuelRoute, self.TaskDelay )
|
||||
else
|
||||
self:RTB()
|
||||
end
|
||||
@ -725,7 +748,7 @@ function AI_AIR:OnCrash( EventData )
|
||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||
self:E( self.Controllable:GetUnits() )
|
||||
if #self.Controllable:GetUnits() == 1 then
|
||||
self:__Crash( 1, EventData )
|
||||
self:__Crash( self.TaskDelay, EventData )
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -735,7 +758,7 @@ end
|
||||
function AI_AIR:OnEjection( EventData )
|
||||
|
||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||
self:__Eject( 1, EventData )
|
||||
self:__Eject( self.TaskDelay, EventData )
|
||||
end
|
||||
end
|
||||
|
||||
@ -744,6 +767,6 @@ end
|
||||
function AI_AIR:OnPilotDead( EventData )
|
||||
|
||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||
self:__PilotDead( 1, EventData )
|
||||
self:__PilotDead( self.TaskDelay, EventData )
|
||||
end
|
||||
end
|
||||
|
||||
@ -2033,6 +2033,54 @@ do -- SET_UNIT
|
||||
return self
|
||||
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.
|
||||
--
|
||||
-- @param #SET_UNIT self
|
||||
|
||||
@ -46,6 +46,7 @@ do -- DETECTION MANAGER
|
||||
--- @type DETECTION_MANAGER
|
||||
-- @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 Tasking.CommandCenter#COMMANDCENTER CC The command center that is used to communicate with the players.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- DETECTION_MANAGER class.
|
||||
@ -218,6 +219,33 @@ do -- DETECTION MANAGER
|
||||
return self._ReportDisplayTime
|
||||
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}.
|
||||
-- @param #DETECTION_MANAGER self
|
||||
-- @param Functional.Detection#DETECTION_BASE Detection
|
||||
|
||||
@ -400,7 +400,7 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime )
|
||||
local Controller = self:_GetController()
|
||||
--self:I( "Before SetTask" )
|
||||
Controller:setTask( DCSTask )
|
||||
--self:I( "After SetTask" )
|
||||
self:I( { ControllableName = self:GetName(), DCSTask = DCSTask } )
|
||||
else
|
||||
BASE:E( { DCSControllableName .. " is not alive anymore.", DCSTask = DCSTask } )
|
||||
end
|
||||
@ -408,6 +408,7 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime )
|
||||
|
||||
if not WaitTime or WaitTime == 0 then
|
||||
SetTask( self, DCSTask )
|
||||
self:I( { ControllableName = self:GetName(), DCSTask = DCSTask } )
|
||||
else
|
||||
self.TaskScheduler:Schedule( self, SetTask, { DCSTask }, WaitTime )
|
||||
end
|
||||
@ -1057,7 +1058,7 @@ end
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number Altitude The altitude [m] to hold 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
|
||||
function CONTROLLABLE:TaskOrbitCircle( Altitude, Speed, Coordinate )
|
||||
self:F2( { self.ControllableName, Altitude, Speed } )
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user