mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
3298 lines
139 KiB
Lua
3298 lines
139 KiB
Lua
--- **AI** - Intermediary class that realized the common methods for the A2G and A2A dispatchers.
|
||
--
|
||
-- ===
|
||
--
|
||
-- Features:
|
||
--
|
||
-- * Common methods for A2G and A2A dispatchers.
|
||
--
|
||
-- ===
|
||
--
|
||
-- ### Author: **FlightControl** rework of GCICAP + introduction of new concepts (squadrons).
|
||
--
|
||
-- @module AI.AI_Air_Dispatcher
|
||
-- @image AI_Air_To_Ground_Dispatching.JPG
|
||
|
||
|
||
|
||
do -- AI_AIR_DISPATCHER
|
||
|
||
--- AI_AIR_DISPATCHER class.
|
||
-- @type AI_AIR_DISPATCHER
|
||
-- @extends Tasking.DetectionManager#DETECTION_MANAGER
|
||
|
||
--- Intermediary class that realized the common methods for the A2G and A2A dispatchers.
|
||
--
|
||
-- ===
|
||
--
|
||
-- @field #AI_AIR_DISPATCHER
|
||
AI_AIR_DISPATCHER = {
|
||
ClassName = "AI_AIR_DISPATCHER",
|
||
Detection = nil,
|
||
}
|
||
|
||
--- Definition of a Squadron.
|
||
-- @type AI_AIR_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_AIR_DISPATCHER.DefenseCoordinates
|
||
-- @map <#string,Core.Point#COORDINATE> A list of all defense coordinates mapped per defense coordinate name.
|
||
|
||
--- Enumerator for spawns at airbases
|
||
-- @type AI_AIR_DISPATCHER.Takeoff
|
||
-- @extends Wrapper.Group#GROUP.Takeoff
|
||
|
||
--- @field #AI_AIR_DISPATCHER.Takeoff Takeoff
|
||
AI_AIR_DISPATCHER.Takeoff = GROUP.Takeoff
|
||
|
||
--- Defnes Landing location.
|
||
-- @field Landing
|
||
AI_AIR_DISPATCHER.Landing = {
|
||
NearAirbase = 1,
|
||
AtRunway = 2,
|
||
AtEngineShutdown = 3,
|
||
}
|
||
|
||
--- A defense queue item description
|
||
-- @type AI_AIR_DISPATCHER.DefenseQueueItem
|
||
-- @field Squadron
|
||
-- @field #AI_AIR_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_AIR_DISPATCHER.DefenseQueue
|
||
-- @list<#AI_AIR_DISPATCHER.DefenseQueueItem> DefenseQueueItem A list of all defenses being queued ...
|
||
|
||
--- @field #AI_AIR_DISPATCHER.DefenseQueue DefenseQueue
|
||
AI_AIR_DISPATCHER.DefenseQueue = {}
|
||
|
||
|
||
|
||
|
||
--- AI_AIR_DISPATCHER constructor.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network.
|
||
-- @return #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:New( Detection )
|
||
|
||
-- Inherits from DETECTION_MANAGER
|
||
local self = BASE:Inherit( self, DETECTION_MANAGER:New( nil, Detection ) ) -- #AI_AIR_DISPATCHER
|
||
|
||
self.Detection = Detection -- Functional.Detection#DETECTION_AREAS
|
||
|
||
-- This table models the DefenderSquadron templates.
|
||
self.DefenderSquadrons = {} -- The Defender Squadrons.
|
||
self.DefenderSpawns = {}
|
||
self.DefenderTasks = {} -- The Defenders Tasks.
|
||
self.DefenderDefault = {} -- The Defender Default Settings over all Squadrons.
|
||
|
||
self:SetDefenseRadius()
|
||
self:SetIntercept( 300 ) -- A default intercept delay time of 300 seconds.
|
||
|
||
self:SetDefaultTakeoff( AI_AIR_DISPATCHER.Takeoff.Air )
|
||
self:SetDefaultTakeoffInAirAltitude( 500 ) -- Default takeoff is 500 meters above the ground.
|
||
self:SetDefaultLanding( AI_AIR_DISPATCHER.Landing.NearAirbase )
|
||
self:SetDefaultEngageRadius( 150000 )
|
||
|
||
self:SetDefaultOverhead( 1 )
|
||
self:SetDefaultGrouping( 1 )
|
||
|
||
self:SetDefaultFuelThreshold( 0.15, 0 ) -- 15% of fuel remaining in the tank will trigger the airplane to return to base or refuel.
|
||
self:SetDefaultDamageThreshold( 0.4 ) -- When 40% of damage, go RTB.
|
||
self:SetDefaultPatrolTimeInterval( 180, 600 ) -- Between 180 and 600 seconds.
|
||
self:SetDefaultPatrolLimit( 1 ) -- Maximum one Patrol per squadron.
|
||
|
||
|
||
self:AddTransition( "Started", "Assign", "Started" )
|
||
|
||
--- OnAfter Transition Handler for Event Assign.
|
||
-- @function [parent=#AI_AIR_DISPATCHER] OnAfterAssign
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string From The From State string.
|
||
-- @param #string Event The Event string.
|
||
-- @param #string To The To State string.
|
||
-- @param Tasking.Task_A2G#AI_A2G Task
|
||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||
-- @param #string PlayerName
|
||
|
||
self:AddTransition( "*", "Patrol", "*" )
|
||
|
||
--- Patrol Handler OnBefore for AI_AIR_DISPATCHER
|
||
-- @function [parent=#AI_AIR_DISPATCHER] OnBeforePatrol
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string From
|
||
-- @param #string Event
|
||
-- @param #string To
|
||
-- @return #boolean
|
||
|
||
--- Patrol Handler OnAfter for AI_AIR_DISPATCHER
|
||
-- @function [parent=#AI_AIR_DISPATCHER] OnAfterPatrol
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string From
|
||
-- @param #string Event
|
||
-- @param #string To
|
||
|
||
--- Patrol Trigger for AI_AIR_DISPATCHER
|
||
-- @function [parent=#AI_AIR_DISPATCHER] Patrol
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
|
||
--- Patrol Asynchronous Trigger for AI_AIR_DISPATCHER
|
||
-- @function [parent=#AI_AIR_DISPATCHER] __Patrol
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number Delay
|
||
|
||
self:AddTransition( "*", "Defend", "*" )
|
||
|
||
--- Defend Handler OnBefore for AI_AIR_DISPATCHER
|
||
-- @function [parent=#AI_AIR_DISPATCHER] OnBeforeDefend
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string From
|
||
-- @param #string Event
|
||
-- @param #string To
|
||
-- @return #boolean
|
||
|
||
--- Defend Handler OnAfter for AI_AIR_DISPATCHER
|
||
-- @function [parent=#AI_AIR_DISPATCHER] OnAfterDefend
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string From
|
||
-- @param #string Event
|
||
-- @param #string To
|
||
|
||
--- Defend Trigger for AI_AIR_DISPATCHER
|
||
-- @function [parent=#AI_AIR_DISPATCHER] Defend
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
|
||
--- Defend Asynchronous Trigger for AI_AIR_DISPATCHER
|
||
-- @function [parent=#AI_AIR_DISPATCHER] __Defend
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number Delay
|
||
|
||
self:AddTransition( "*", "Engage", "*" )
|
||
|
||
--- Engage Handler OnBefore for AI_AIR_DISPATCHER
|
||
-- @function [parent=#AI_AIR_DISPATCHER] OnBeforeEngage
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string From
|
||
-- @param #string Event
|
||
-- @param #string To
|
||
-- @return #boolean
|
||
|
||
--- Engage Handler OnAfter for AI_AIR_DISPATCHER
|
||
-- @function [parent=#AI_AIR_DISPATCHER] OnAfterEngage
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string From
|
||
-- @param #string Event
|
||
-- @param #string To
|
||
|
||
--- Engage Trigger for AI_AIR_DISPATCHER
|
||
-- @function [parent=#AI_AIR_DISPATCHER] Engage
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
|
||
--- Engage Asynchronous Trigger for AI_AIR_DISPATCHER
|
||
-- @function [parent=#AI_AIR_DISPATCHER] __Engage
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number Delay
|
||
|
||
|
||
-- Subscribe to the CRASH event so that when planes are shot
|
||
-- by a Unit from the dispatcher, they will be removed from the detection...
|
||
-- This will avoid the detection to still "know" the shot unit until the next detection.
|
||
-- Otherwise, a new defense or engage may happen for an already shot plane!
|
||
|
||
|
||
self:HandleEvent( EVENTS.Crash, self.OnEventCrashOrDead )
|
||
self:HandleEvent( EVENTS.Dead, self.OnEventCrashOrDead )
|
||
--self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCrashOrDead )
|
||
|
||
|
||
self:HandleEvent( EVENTS.Land )
|
||
self:HandleEvent( EVENTS.EngineShutdown )
|
||
|
||
-- Handle the situation where the airbases are captured.
|
||
self:HandleEvent( EVENTS.BaseCaptured )
|
||
|
||
self:SetTacticalDisplay( false )
|
||
|
||
self.DefenderPatrolIndex = 0
|
||
|
||
self.TakeoffScheduleID = self:ScheduleRepeat( 10, 10, 0, nil, self.ResourceTakeoff, self )
|
||
|
||
self:__Start( 5 )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:onafterStart( From, Event, To )
|
||
|
||
self:GetParent( self ).onafterStart( self, From, Event, To )
|
||
|
||
-- Spawn the resources.
|
||
for SquadronName, DefenderSquadron in pairs( self.DefenderSquadrons ) do
|
||
DefenderSquadron.Resource = {}
|
||
for Resource = 1, DefenderSquadron.ResourceCount or 0 do
|
||
self:ResourcePark( DefenderSquadron )
|
||
end
|
||
end
|
||
end
|
||
|
||
|
||
--- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:ResourcePark( DefenderSquadron )
|
||
local TemplateID = math.random( 1, #DefenderSquadron.Spawn )
|
||
local Spawn = DefenderSquadron.Spawn[ TemplateID ] -- Core.Spawn#SPAWN
|
||
Spawn:InitGrouping( 1 )
|
||
local SpawnGroup
|
||
if self:IsSquadronVisible( DefenderSquadron.Name ) then
|
||
SpawnGroup = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, SPAWN.Takeoff.Cold )
|
||
local GroupName = SpawnGroup:GetName()
|
||
DefenderSquadron.Resources = DefenderSquadron.Resources or {}
|
||
DefenderSquadron.Resources[TemplateID] = DefenderSquadron.Resources[TemplateID] or {}
|
||
DefenderSquadron.Resources[TemplateID][GroupName] = {}
|
||
DefenderSquadron.Resources[TemplateID][GroupName] = SpawnGroup
|
||
end
|
||
end
|
||
|
||
|
||
--- @param #AI_AIR_DISPATCHER self
|
||
-- @param Core.Event#EVENTDATA EventData
|
||
function AI_AIR_DISPATCHER:OnEventBaseCaptured( EventData )
|
||
|
||
local AirbaseName = EventData.PlaceName -- The name of the airbase that was captured.
|
||
|
||
self:I( "Captured " .. AirbaseName )
|
||
|
||
-- Now search for all squadrons located at the airbase, and sanatize them.
|
||
for SquadronName, Squadron in pairs( self.DefenderSquadrons ) do
|
||
if Squadron.AirbaseName == AirbaseName then
|
||
Squadron.ResourceCount = -999 -- The base has been captured, and the resources are eliminated. No more spawning.
|
||
Squadron.Captured = true
|
||
self:I( "Squadron " .. SquadronName .. " captured." )
|
||
end
|
||
end
|
||
end
|
||
|
||
--- @param #AI_AIR_DISPATCHER self
|
||
-- @param Core.Event#EVENTDATA EventData
|
||
function AI_AIR_DISPATCHER:OnEventCrashOrDead( EventData )
|
||
self.Detection:ForgetDetectedUnit( EventData.IniUnitName )
|
||
end
|
||
|
||
--- @param #AI_AIR_DISPATCHER self
|
||
-- @param Core.Event#EVENTDATA EventData
|
||
function AI_AIR_DISPATCHER:OnEventLand( EventData )
|
||
self:F( "Landed" )
|
||
local DefenderUnit = EventData.IniUnit
|
||
local Defender = EventData.IniGroup
|
||
local Squadron = self:GetSquadronFromDefender( Defender )
|
||
if Squadron then
|
||
self:F( { SquadronName = Squadron.Name } )
|
||
local LandingMethod = self:GetSquadronLanding( Squadron.Name )
|
||
|
||
if LandingMethod == AI_AIR_DISPATCHER.Landing.AtRunway then
|
||
local DefenderSize = Defender:GetSize()
|
||
if DefenderSize == 1 then
|
||
self:RemoveDefenderFromSquadron( Squadron, Defender )
|
||
end
|
||
DefenderUnit:Destroy()
|
||
self:ResourcePark( Squadron, Defender )
|
||
return
|
||
end
|
||
if DefenderUnit:GetLife() ~= DefenderUnit:GetLife0() then
|
||
-- Damaged units cannot be repaired anymore.
|
||
DefenderUnit:Destroy()
|
||
return
|
||
end
|
||
end
|
||
end
|
||
|
||
--- @param #AI_AIR_DISPATCHER self
|
||
-- @param Core.Event#EVENTDATA EventData
|
||
function AI_AIR_DISPATCHER:OnEventEngineShutdown( EventData )
|
||
local DefenderUnit = EventData.IniUnit
|
||
local Defender = EventData.IniGroup
|
||
local Squadron = self:GetSquadronFromDefender( Defender )
|
||
if Squadron then
|
||
self:F( { SquadronName = Squadron.Name } )
|
||
local LandingMethod = self:GetSquadronLanding( Squadron.Name )
|
||
if LandingMethod == AI_AIR_DISPATCHER.Landing.AtEngineShutdown and
|
||
not DefenderUnit:InAir() then
|
||
local DefenderSize = Defender:GetSize()
|
||
if DefenderSize == 1 then
|
||
self:RemoveDefenderFromSquadron( Squadron, Defender )
|
||
end
|
||
DefenderUnit:Destroy()
|
||
self:ResourcePark( Squadron, Defender )
|
||
end
|
||
end
|
||
end
|
||
|
||
|
||
--- Define the defense radius to check if a target can be engaged by a squadron group.
|
||
-- When targets are detected that are still really far off, you don't want the dispatcher to launch defenders, as they might need to travel too far.
|
||
-- You want it to wait until a certain defend radius is reached, which is calculated as:
|
||
-- 1. the **distance of the closest airbase to target**, being smaller than the **Defend Radius**.
|
||
-- 2. the **distance to any defense reference point**.
|
||
--
|
||
-- The **default** defense radius is defined as **400000** or **40km**. Override the default defense radius when the era of the warfare is early, or,
|
||
-- when you don't want to let the AI_AIR_DISPATCHER react immediately when a certain border or area is not being crossed.
|
||
--
|
||
-- Use the method @{#AI_AIR_DISPATCHER.SetDefendRadius}() to set a specific defend radius for all squadrons,
|
||
-- **the Defense Radius is defined for ALL squadrons which are operational.**
|
||
--
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number DefenseRadius (Optional, Default = 200000) The defense radius to engage detected targets from the nearest capable and available squadron airbase.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
|
||
-- A2GDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||
--
|
||
-- -- Set 100km as the radius to defend from detected targets from the nearest airbase.
|
||
-- A2GDispatcher:SetDefendRadius( 100000 )
|
||
--
|
||
-- -- Set 200km as the radius to defend.
|
||
-- A2GDispatcher:SetDefendRadius() -- 200000 is the default value.
|
||
--
|
||
function AI_AIR_DISPATCHER:SetDefenseRadius( DefenseRadius )
|
||
|
||
self.DefenseRadius = DefenseRadius or 100000
|
||
|
||
self.Detection:SetAcceptRange( self.DefenseRadius )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
|
||
|
||
--- Define a border area to simulate a **cold war** scenario.
|
||
-- A **cold war** is one where Patrol aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border.
|
||
-- A **hot war** is one where Patrol aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send Patrol and GCI aircraft to attack it.
|
||
-- If it's a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Core.Zone#ZONE_BASE}. This method needs to be used for this.
|
||
-- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. Set the noborders parameter to 1
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param Core.Zone#ZONE_BASE BorderZone An object derived from ZONE_BASE, or a list of objects derived from ZONE_BASE.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
|
||
-- A2GDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||
--
|
||
-- -- Set one ZONE_POLYGON object as the border for the A2G dispatcher.
|
||
-- local BorderZone = ZONE_POLYGON( "CCCP Border", GROUP:FindByName( "CCCP Border" ) ) -- The GROUP object is a late activate helicopter unit.
|
||
-- A2GDispatcher:SetBorderZone( BorderZone )
|
||
--
|
||
-- or
|
||
--
|
||
-- -- Set two ZONE_POLYGON objects as the border for the A2G dispatcher.
|
||
-- local BorderZone1 = ZONE_POLYGON( "CCCP Border1", GROUP:FindByName( "CCCP Border1" ) ) -- The GROUP object is a late activate helicopter unit.
|
||
-- local BorderZone2 = ZONE_POLYGON( "CCCP Border2", GROUP:FindByName( "CCCP Border2" ) ) -- The GROUP object is a late activate helicopter unit.
|
||
-- A2GDispatcher:SetBorderZone( { BorderZone1, BorderZone2 } )
|
||
--
|
||
--
|
||
function AI_AIR_DISPATCHER:SetBorderZone( BorderZone )
|
||
|
||
self.Detection:SetAcceptZones( BorderZone )
|
||
|
||
return self
|
||
end
|
||
|
||
--- Display a tactical report every 30 seconds about which aircraft are:
|
||
-- * Patrolling
|
||
-- * Engaging
|
||
-- * Returning
|
||
-- * Damaged
|
||
-- * Out of Fuel
|
||
-- * ...
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #boolean TacticalDisplay Provide a value of **true** to display every 30 seconds a tactical overview.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
|
||
-- A2GDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||
--
|
||
-- -- Now Setup the Tactical Display for debug mode.
|
||
-- A2GDispatcher:SetTacticalDisplay( true )
|
||
--
|
||
function AI_AIR_DISPATCHER:SetTacticalDisplay( TacticalDisplay )
|
||
|
||
self.TacticalDisplay = TacticalDisplay
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Set the default damage treshold when defenders will RTB.
|
||
-- The default damage treshold is by default set to 40%, which means that when the airplane is 40% damaged, it will go RTB.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number DamageThreshold A decimal number between 0 and 1, that expresses the %-tage of the damage treshold before going RTB.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
|
||
-- A2GDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||
--
|
||
-- -- Now Setup the default damage treshold.
|
||
-- A2GDispatcher:SetDefaultDamageThreshold( 0.90 ) -- Go RTB when the airplane 90% damaged.
|
||
--
|
||
function AI_AIR_DISPATCHER:SetDefaultDamageThreshold( DamageThreshold )
|
||
|
||
self.DefenderDefault.DamageThreshold = DamageThreshold
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Set the default Patrol time interval for squadrons, which will be used to determine a random Patrol timing.
|
||
-- The default Patrol time interval is between 180 and 600 seconds.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number PatrolMinSeconds The minimum amount of seconds for the random time interval.
|
||
-- @param #number PatrolMaxSeconds The maximum amount of seconds for the random time interval.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
|
||
-- A2GDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||
--
|
||
-- -- Now Setup the default Patrol time interval.
|
||
-- A2GDispatcher:SetDefaultPatrolTimeInterval( 300, 1200 ) -- Between 300 and 1200 seconds.
|
||
--
|
||
function AI_AIR_DISPATCHER:SetDefaultPatrolTimeInterval( PatrolMinSeconds, PatrolMaxSeconds )
|
||
|
||
self.DefenderDefault.PatrolMinSeconds = PatrolMinSeconds
|
||
self.DefenderDefault.PatrolMaxSeconds = PatrolMaxSeconds
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Set the default Patrol limit for squadrons, which will be used to determine how many Patrol can be airborne at the same time for the squadron.
|
||
-- The default Patrol limit is 1 Patrol, which means one Patrol group being spawned.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number PatrolLimit The maximum amount of Patrol that can be airborne at the same time for the squadron.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
|
||
-- A2GDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||
--
|
||
-- -- Now Setup the default Patrol limit.
|
||
-- A2GDispatcher:SetDefaultPatrolLimit( 2 ) -- Maximum 2 Patrol per squadron.
|
||
--
|
||
function AI_AIR_DISPATCHER:SetDefaultPatrolLimit( PatrolLimit )
|
||
|
||
self.DefenderDefault.PatrolLimit = PatrolLimit
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Set the default engage limit for squadrons, which will be used to determine how many air units will engage at the same time with the enemy.
|
||
-- The default eatrol limit is 1, which means one patrol group maximum per squadron.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number EngageLimit The maximum engages that can be done at the same time per squadron.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
|
||
-- A2GDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||
--
|
||
-- -- Now Setup the default Patrol limit.
|
||
-- A2GDispatcher:SetDefaultEngageLimit( 2 ) -- Maximum 2 engagements with the enemy per squadron.
|
||
--
|
||
function AI_AIR_DISPATCHER:SetDefaultEngageLimit( EngageLimit )
|
||
|
||
self.DefenderDefault.EngageLimit = EngageLimit
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
|
||
function AI_AIR_DISPATCHER:SetIntercept( InterceptDelay )
|
||
|
||
self.DefenderDefault.InterceptDelay = InterceptDelay
|
||
|
||
local Detection = self.Detection -- Functional.Detection#DETECTION_AREAS
|
||
Detection:SetIntercept( true, InterceptDelay )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Calculates which defender friendlies are nearby the area, to help protect the area.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param DetectedItem
|
||
-- @return #table A list of the defender friendlies nearby, sorted by distance.
|
||
function AI_AIR_DISPATCHER:GetDefenderFriendliesNearBy( DetectedItem )
|
||
|
||
-- local DefenderFriendliesNearBy = self.Detection:GetFriendliesDistance( DetectedItem )
|
||
|
||
local DefenderFriendliesNearBy = {}
|
||
|
||
local DetectionCoordinate = self.Detection:GetDetectedItemCoordinate( DetectedItem )
|
||
|
||
local ScanZone = ZONE_RADIUS:New( "ScanZone", DetectionCoordinate:GetVec2(), self.DefenseRadius )
|
||
|
||
ScanZone:Scan( Object.Category.UNIT, { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
|
||
|
||
local DefenderUnits = ScanZone:GetScannedUnits()
|
||
|
||
for DefenderUnitID, DefenderUnit in pairs( DefenderUnits ) do
|
||
local DefenderUnit = UNIT:FindByName( DefenderUnit:getName() )
|
||
|
||
DefenderFriendliesNearBy[#DefenderFriendliesNearBy+1] = DefenderUnit
|
||
end
|
||
|
||
|
||
return DefenderFriendliesNearBy
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:GetDefenderTasks()
|
||
return self.DefenderTasks or {}
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:GetDefenderTask( Defender )
|
||
return self.DefenderTasks[Defender]
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:GetDefenderTaskFsm( Defender )
|
||
return self:GetDefenderTask( Defender ).Fsm
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:GetDefenderTaskTarget( Defender )
|
||
return self:GetDefenderTask( Defender ).Target
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:GetDefenderTaskSquadronName( Defender )
|
||
return self:GetDefenderTask( Defender ).SquadronName
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:ClearDefenderTask( Defender )
|
||
if Defender:IsAlive() and self.DefenderTasks[Defender] then
|
||
local Target = self.DefenderTasks[Defender].Target
|
||
local Message = "Clearing (" .. self.DefenderTasks[Defender].Type .. ") "
|
||
Message = Message .. Defender:GetName()
|
||
if Target then
|
||
Message = Message .. ( Target and ( " from " .. Target.Index .. " [" .. Target.Set:Count() .. "]" ) ) or ""
|
||
end
|
||
self:F( { Target = Message } )
|
||
end
|
||
self.DefenderTasks[Defender] = nil
|
||
return self
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:ClearDefenderTaskTarget( Defender )
|
||
|
||
local DefenderTask = self:GetDefenderTask( Defender )
|
||
|
||
if Defender:IsAlive() and DefenderTask then
|
||
local Target = DefenderTask.Target
|
||
local Message = "Clearing (" .. DefenderTask.Type .. ") "
|
||
Message = Message .. Defender:GetName()
|
||
if Target then
|
||
Message = Message .. ( Target and ( " from " .. Target.Index .. " [" .. Target.Set:Count() .. "]" ) ) or ""
|
||
end
|
||
self:F( { Target = Message } )
|
||
end
|
||
if Defender and DefenderTask and DefenderTask.Target then
|
||
DefenderTask.Target = nil
|
||
end
|
||
-- if Defender and DefenderTask then
|
||
-- if DefenderTask.Fsm:Is( "Fuel" )
|
||
-- or DefenderTask.Fsm:Is( "LostControl")
|
||
-- or DefenderTask.Fsm:Is( "Damaged" ) then
|
||
-- self:ClearDefenderTask( Defender )
|
||
-- end
|
||
-- end
|
||
return self
|
||
end
|
||
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:SetDefenderTask( SquadronName, Defender, Type, Fsm, Target, Size )
|
||
|
||
self:F( { SquadronName = SquadronName, Defender = Defender:GetName() } )
|
||
|
||
self.DefenderTasks[Defender] = self.DefenderTasks[Defender] or {}
|
||
self.DefenderTasks[Defender].Type = Type
|
||
self.DefenderTasks[Defender].Fsm = Fsm
|
||
self.DefenderTasks[Defender].SquadronName = SquadronName
|
||
self.DefenderTasks[Defender].Size = Size
|
||
|
||
if Target then
|
||
self:SetDefenderTaskTarget( Defender, Target )
|
||
end
|
||
return self
|
||
end
|
||
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param Wrapper.Group#GROUP AIGroup
|
||
function AI_AIR_DISPATCHER:SetDefenderTaskTarget( Defender, AttackerDetection )
|
||
|
||
local Message = "(" .. self.DefenderTasks[Defender].Type .. ") "
|
||
Message = Message .. Defender:GetName()
|
||
Message = Message .. ( AttackerDetection and ( " target " .. AttackerDetection.Index .. " [" .. AttackerDetection.Set:Count() .. "]" ) ) or ""
|
||
self:F( { AttackerDetection = Message } )
|
||
if AttackerDetection then
|
||
self.DefenderTasks[Defender].Target = AttackerDetection
|
||
end
|
||
return self
|
||
end
|
||
|
||
|
||
--- This is the main method to define Squadrons programmatically.
|
||
-- Squadrons:
|
||
--
|
||
-- * Have a **name or key** that is the identifier or key of the squadron.
|
||
-- * Have **specific plane types** defined by **templates**.
|
||
-- * Are **located at one specific airbase**. Multiple squadrons can be located at one airbase through.
|
||
-- * Optionally have a limited set of **resources**. The default is that squadrons have unlimited resources.
|
||
--
|
||
-- The name of the squadron given acts as the **squadron key** in the AI\_A2G\_DISPATCHER:Squadron...() methods.
|
||
--
|
||
-- Additionally, squadrons have specific configuration options to:
|
||
--
|
||
-- * Control how new aircraft are **taking off** from the airfield (in the air, cold, hot, at the runway).
|
||
-- * Control how returning aircraft are **landing** at the airfield (in the air near the airbase, after landing, after engine shutdown).
|
||
-- * Control the **grouping** of new aircraft spawned at the airfield. If there is more than one aircraft to be spawned, these may be grouped.
|
||
-- * Control the **overhead** or defensive strength of the squadron. Depending on the types of planes and amount of resources, the mission designer can choose to increase or reduce the amount of planes spawned.
|
||
--
|
||
-- For performance and bug workaround reasons within DCS, squadrons have different methods to spawn new aircraft or land returning or damaged aircraft.
|
||
--
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
--
|
||
-- @param #string SquadronName A string (text) that defines the squadron identifier or the key of the Squadron.
|
||
-- It can be any name, for example `"104th Squadron"` or `"SQ SQUADRON1"`, whatever.
|
||
-- As long as you remember that this name becomes the identifier of your squadron you have defined.
|
||
-- You need to use this name in other methods too!
|
||
--
|
||
-- @param #string AirbaseName The airbase name where you want to have the squadron located.
|
||
-- You need to specify here EXACTLY the name of the airbase as you see it in the mission editor.
|
||
-- Examples are `"Batumi"` or `"Tbilisi-Lochini"`.
|
||
-- EXACTLY the airbase name, between quotes `""`.
|
||
-- To ease the airbase naming when using the LDT editor and IntelliSense, the @{Wrapper.Airbase#AIRBASE} class contains enumerations of the airbases of each map.
|
||
--
|
||
-- * Caucasus: @{Wrapper.Airbase#AIRBASE.Caucaus}
|
||
-- * Nevada or NTTR: @{Wrapper.Airbase#AIRBASE.Nevada}
|
||
-- * Normandy: @{Wrapper.Airbase#AIRBASE.Normandy}
|
||
--
|
||
-- @param #string TemplatePrefixes A string or an array of strings specifying the **prefix names of the templates** (not going to explain what is templates here again).
|
||
-- Examples are `{ "104th", "105th" }` or `"104th"` or `"Template 1"` or `"BLUE PLANES"`.
|
||
-- Just remember that your template (groups late activated) need to start with the prefix you have specified in your code.
|
||
-- If you have only one prefix name for a squadron, you don't need to use the `{ }`, otherwise you need to use the brackets.
|
||
--
|
||
-- @param #number ResourceCount (optional) A number that specifies how many resources are in stock of the squadron. If not specified, the squadron will have infinite resources available.
|
||
--
|
||
-- @usage
|
||
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
|
||
-- A2GDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||
--
|
||
-- @usage
|
||
-- -- This will create squadron "Squadron1" at "Batumi" airbase, and will use plane types "SQ1" and has 40 planes in stock...
|
||
-- A2GDispatcher:SetSquadron( "Squadron1", "Batumi", "SQ1", 40 )
|
||
--
|
||
-- @usage
|
||
-- -- This will create squadron "Sq 1" at "Batumi" airbase, and will use plane types "Mig-29" and "Su-27" and has 20 planes in stock...
|
||
-- -- Note that in this implementation, the A2G dispatcher will select a random plane type when a new plane (group) needs to be spawned for defenses.
|
||
-- -- Note the usage of the {} for the airplane templates list.
|
||
-- A2GDispatcher:SetSquadron( "Sq 1", "Batumi", { "Mig-29", "Su-27" }, 40 )
|
||
--
|
||
-- @usage
|
||
-- -- This will create 2 squadrons "104th" and "23th" at "Batumi" airbase, and will use plane types "Mig-29" and "Su-27" respectively and each squadron has 10 planes in stock...
|
||
-- A2GDispatcher:SetSquadron( "104th", "Batumi", "Mig-29", 10 )
|
||
-- A2GDispatcher:SetSquadron( "23th", "Batumi", "Su-27", 10 )
|
||
--
|
||
-- @usage
|
||
-- -- This is an example like the previous, but now with infinite resources.
|
||
-- -- The ResourceCount parameter is not given in the SetSquadron method.
|
||
-- A2GDispatcher:SetSquadron( "104th", "Batumi", "Mig-29" )
|
||
-- A2GDispatcher:SetSquadron( "23th", "Batumi", "Su-27" )
|
||
--
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetSquadron( SquadronName, AirbaseName, TemplatePrefixes, ResourceCount )
|
||
|
||
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
|
||
|
||
local DefenderSquadron = self.DefenderSquadrons[SquadronName]
|
||
|
||
DefenderSquadron.Name = SquadronName
|
||
DefenderSquadron.Airbase = AIRBASE:FindByName( AirbaseName )
|
||
DefenderSquadron.AirbaseName = DefenderSquadron.Airbase:GetName()
|
||
if not DefenderSquadron.Airbase then
|
||
error( "Cannot find airbase with name:" .. AirbaseName )
|
||
end
|
||
|
||
DefenderSquadron.Spawn = {}
|
||
if type( TemplatePrefixes ) == "string" then
|
||
local SpawnTemplate = TemplatePrefixes
|
||
self.DefenderSpawns[SpawnTemplate] = self.DefenderSpawns[SpawnTemplate] or SPAWN:New( SpawnTemplate ) -- :InitCleanUp( 180 )
|
||
DefenderSquadron.Spawn[1] = self.DefenderSpawns[SpawnTemplate]
|
||
else
|
||
for TemplateID, SpawnTemplate in pairs( TemplatePrefixes ) do
|
||
self.DefenderSpawns[SpawnTemplate] = self.DefenderSpawns[SpawnTemplate] or SPAWN:New( SpawnTemplate ) -- :InitCleanUp( 180 )
|
||
DefenderSquadron.Spawn[#DefenderSquadron.Spawn+1] = self.DefenderSpawns[SpawnTemplate]
|
||
end
|
||
end
|
||
DefenderSquadron.ResourceCount = ResourceCount
|
||
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
|
||
end
|
||
|
||
--- Get an item from the Squadron table.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @return #table
|
||
function AI_AIR_DISPATCHER:GetSquadron( SquadronName )
|
||
|
||
local DefenderSquadron = self.DefenderSquadrons[SquadronName]
|
||
|
||
if not DefenderSquadron then
|
||
error( "Unknown Squadron:" .. SquadronName )
|
||
end
|
||
|
||
return DefenderSquadron
|
||
end
|
||
|
||
|
||
--- Set the Squadron visible before startup of the dispatcher.
|
||
-- All planes will be spawned as uncontrolled on the parking spot.
|
||
-- They will lock the parking spot.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Set the Squadron visible before startup of dispatcher.
|
||
-- A2GDispatcher:SetSquadronVisible( "Mineralnye" )
|
||
--
|
||
-- TODO: disabling because of bug in queueing.
|
||
-- function AI_AIR_DISPATCHER:SetSquadronVisible( SquadronName )
|
||
--
|
||
-- self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
|
||
--
|
||
-- local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
--
|
||
-- DefenderSquadron.Uncontrolled = true
|
||
-- self:SetSquadronTakeoffFromParkingCold( SquadronName )
|
||
-- self:SetSquadronLandingAtEngineShutdown( SquadronName )
|
||
--
|
||
-- for SpawnTemplate, DefenderSpawn in pairs( self.DefenderSpawns ) do
|
||
-- DefenderSpawn:InitUnControlled()
|
||
-- end
|
||
--
|
||
-- end
|
||
|
||
--- Check if the Squadron is visible before startup of the dispatcher.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @return #bool true if visible.
|
||
-- @usage
|
||
--
|
||
-- -- Set the Squadron visible before startup of dispatcher.
|
||
-- local IsVisible = A2GDispatcher:IsSquadronVisible( "Mineralnye" )
|
||
--
|
||
function AI_AIR_DISPATCHER:IsSquadronVisible( SquadronName )
|
||
|
||
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
|
||
if DefenderSquadron then
|
||
return DefenderSquadron.Uncontrolled == true
|
||
end
|
||
|
||
return nil
|
||
|
||
end
|
||
|
||
--- @param #AI_AIR_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_AIR_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.
|
||
--
|
||
-- - @{#AI_AIR_DISPATCHER:SetSquadronSeadPatrolInterval} for SEAD tasks.
|
||
-- - @{#AI_AIR_DISPATCHER:SetSquadronSeadPatrolInterval} for CAS tasks.
|
||
-- - @{#AI_AIR_DISPATCHER:SetSquadronSeadPatrolInterval} for BAI tasks.
|
||
--
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param #number PatrolLimit (optional) The maximum amount of Patrol groups to be spawned. Note that a Patrol is a group, so can consist out of 1 to 4 airplanes. The default is 1 Patrol group.
|
||
-- @param #number LowInterval (optional) The minimum time boundary in seconds when a new Patrol will be spawned. The default is 180 seconds.
|
||
-- @param #number HighInterval (optional) The maximum time boundary in seconds when a new Patrol will be spawned. The default is 600 seconds.
|
||
-- @param #number Probability Is not in use, you can skip this parameter.
|
||
-- @param #string DefenseTaskType Should contain "SEAD", "CAS" or "BAI".
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Patrol Squadron execution.
|
||
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
|
||
-- A2GDispatcher:SetSquadronSeadPatrol( "Mineralnye", PatrolZoneEast, 4000, 10000, 500, 600, 800, 900 )
|
||
-- A2GDispatcher:SetSquadronPatrolInterval( "Mineralnye", 2, 30, 60, 1, "SEAD" )
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronPatrolInterval( SquadronName, PatrolLimit, LowInterval, HighInterval, Probability, DefenseTaskType )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
|
||
local Patrol = DefenderSquadron[DefenseTaskType]
|
||
if Patrol then
|
||
Patrol.LowInterval = LowInterval or 180
|
||
Patrol.HighInterval = HighInterval or 600
|
||
Patrol.Probability = Probability or 1
|
||
Patrol.PatrolLimit = PatrolLimit or 1
|
||
Patrol.Scheduler = Patrol.Scheduler or SCHEDULER:New( self )
|
||
local Scheduler = Patrol.Scheduler -- Core.Scheduler#SCHEDULER
|
||
local ScheduleID = Patrol.ScheduleID
|
||
local Variance = ( Patrol.HighInterval - Patrol.LowInterval ) / 2
|
||
local Repeat = Patrol.LowInterval + Variance
|
||
local Randomization = Variance / Repeat
|
||
local Start = math.random( 1, Patrol.HighInterval )
|
||
|
||
if ScheduleID then
|
||
Scheduler:Stop( ScheduleID )
|
||
end
|
||
|
||
Patrol.ScheduleID = Scheduler:Schedule( self, self.SchedulerPatrol, { SquadronName }, Start, Repeat, Randomization )
|
||
else
|
||
error( "This squadron does not exist:" .. SquadronName )
|
||
end
|
||
|
||
end
|
||
|
||
|
||
|
||
--- Set the squadron Patrol parameters for SEAD tasks.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param #number PatrolLimit (optional) The maximum amount of Patrol groups to be spawned. Note that a Patrol is a group, so can consist out of 1 to 4 airplanes. The default is 1 Patrol group.
|
||
-- @param #number LowInterval (optional) The minimum time boundary in seconds when a new Patrol will be spawned. The default is 180 seconds.
|
||
-- @param #number HighInterval (optional) The maximum time boundary in seconds when a new Patrol will be spawned. The default is 600 seconds.
|
||
-- @param #number Probability Is not in use, you can skip this parameter.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Patrol Squadron execution.
|
||
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
|
||
-- A2GDispatcher:SetSquadronSeadPatrol( "Mineralnye", PatrolZoneEast, 4000, 10000, 500, 600, 800, 900 )
|
||
-- A2GDispatcher:SetSquadronSeadPatrolInterval( "Mineralnye", 2, 30, 60, 1 )
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronSeadPatrolInterval( SquadronName, PatrolLimit, LowInterval, HighInterval, Probability )
|
||
|
||
self:SetSquadronPatrolInterval( SquadronName, PatrolLimit, LowInterval, HighInterval, Probability, "SEAD" )
|
||
|
||
end
|
||
|
||
|
||
--- Set the squadron Patrol parameters for CAS tasks.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param #number PatrolLimit (optional) The maximum amount of Patrol groups to be spawned. Note that a Patrol is a group, so can consist out of 1 to 4 airplanes. The default is 1 Patrol group.
|
||
-- @param #number LowInterval (optional) The minimum time boundary in seconds when a new Patrol will be spawned. The default is 180 seconds.
|
||
-- @param #number HighInterval (optional) The maximum time boundary in seconds when a new Patrol will be spawned. The default is 600 seconds.
|
||
-- @param #number Probability Is not in use, you can skip this parameter.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Patrol Squadron execution.
|
||
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
|
||
-- A2GDispatcher:SetSquadronCasPatrol( "Mineralnye", PatrolZoneEast, 4000, 10000, 500, 600, 800, 900 )
|
||
-- A2GDispatcher:SetSquadronCasPatrolInterval( "Mineralnye", 2, 30, 60, 1 )
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronCasPatrolInterval( SquadronName, PatrolLimit, LowInterval, HighInterval, Probability )
|
||
|
||
self:SetSquadronPatrolInterval( SquadronName, PatrolLimit, LowInterval, HighInterval, Probability, "CAS" )
|
||
|
||
end
|
||
|
||
|
||
--- Set the squadron Patrol parameters for BAI tasks.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param #number PatrolLimit (optional) The maximum amount of Patrol groups to be spawned. Note that a Patrol is a group, so can consist out of 1 to 4 airplanes. The default is 1 Patrol group.
|
||
-- @param #number LowInterval (optional) The minimum time boundary in seconds when a new Patrol will be spawned. The default is 180 seconds.
|
||
-- @param #number HighInterval (optional) The maximum time boundary in seconds when a new Patrol will be spawned. The default is 600 seconds.
|
||
-- @param #number Probability Is not in use, you can skip this parameter.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Patrol Squadron execution.
|
||
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
|
||
-- A2GDispatcher:SetSquadronBaiPatrol( "Mineralnye", PatrolZoneEast, 4000, 10000, 500, 600, 800, 900 )
|
||
-- A2GDispatcher:SetSquadronBaiPatrolInterval( "Mineralnye", 2, 30, 60, 1 )
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronBaiPatrolInterval( SquadronName, PatrolLimit, LowInterval, HighInterval, Probability )
|
||
|
||
self:SetSquadronPatrolInterval( SquadronName, PatrolLimit, LowInterval, HighInterval, Probability, "BAI" )
|
||
|
||
end
|
||
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:GetPatrolDelay( SquadronName )
|
||
|
||
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
|
||
self.DefenderSquadrons[SquadronName].Patrol = self.DefenderSquadrons[SquadronName].Patrol or {}
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
|
||
local Patrol = self.DefenderSquadrons[SquadronName].Patrol
|
||
if Patrol then
|
||
return math.random( Patrol.LowInterval, Patrol.HighInterval )
|
||
else
|
||
error( "This squadron does not exist:" .. SquadronName )
|
||
end
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @return #table DefenderSquadron
|
||
function AI_AIR_DISPATCHER:CanPatrol( SquadronName, DefenseTaskType )
|
||
self:F({SquadronName = SquadronName})
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
|
||
if DefenderSquadron.Captured == false then -- We can only spawn new Patrol if the base has not been captured.
|
||
|
||
if ( not DefenderSquadron.ResourceCount ) or ( DefenderSquadron.ResourceCount and DefenderSquadron.ResourceCount > 0 ) then -- And, if there are sufficient resources.
|
||
|
||
local Patrol = DefenderSquadron[DefenseTaskType]
|
||
if Patrol and Patrol.Patrol == true then
|
||
local PatrolCount = self:CountPatrolAirborne( SquadronName, DefenseTaskType )
|
||
self:F( { PatrolCount = PatrolCount, PatrolLimit = Patrol.PatrolLimit, PatrolProbability = Patrol.Probability } )
|
||
if PatrolCount < Patrol.PatrolLimit then
|
||
local Probability = math.random()
|
||
if Probability <= Patrol.Probability then
|
||
return DefenderSquadron, Patrol
|
||
end
|
||
end
|
||
else
|
||
self:F( "No patrol for " .. SquadronName )
|
||
end
|
||
end
|
||
end
|
||
return nil
|
||
end
|
||
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @return #table DefenderSquadron
|
||
function AI_AIR_DISPATCHER:CanDefend( SquadronName, DefenseTaskType )
|
||
self:F({SquadronName = SquadronName, DefenseTaskType})
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
|
||
if DefenderSquadron.Captured == false then -- We can only spawn new defense if the home airbase has not been captured.
|
||
|
||
if ( not DefenderSquadron.ResourceCount ) or ( DefenderSquadron.ResourceCount and DefenderSquadron.ResourceCount > 0 ) then -- And, if there are sufficient resources.
|
||
if DefenderSquadron[DefenseTaskType] and ( DefenderSquadron[DefenseTaskType].Defend == true ) then
|
||
return DefenderSquadron, DefenderSquadron[DefenseTaskType]
|
||
end
|
||
end
|
||
end
|
||
return nil
|
||
end
|
||
|
||
--- Set the squadron engage limit 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.
|
||
--
|
||
-- - @{#AI_AIR_DISPATCHER:SetSquadronSeadEngageLimit} for SEAD tasks.
|
||
-- - @{#AI_AIR_DISPATCHER:SetSquadronSeadEngageLimit} for CAS tasks.
|
||
-- - @{#AI_AIR_DISPATCHER:SetSquadronSeadEngageLimit} for BAI tasks.
|
||
--
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param #number EngageLimit The maximum amount of groups to engage with the enemy for this squadron.
|
||
-- @param #string DefenseTaskType Should contain "SEAD", "CAS" or "BAI".
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Patrol Squadron execution.
|
||
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
|
||
-- A2GDispatcher:SetSquadronEngageLimit( "Mineralnye", 2, "SEAD" ) -- Engage maximum 2 groups with the enemy for SEAD defense.
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronEngageLimit( SquadronName, EngageLimit, DefenseTaskType )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
|
||
local Defense = DefenderSquadron[DefenseTaskType]
|
||
if Defense then
|
||
Defense.EngageLimit = EngageLimit or 1
|
||
else
|
||
error( "This squadron does not exist:" .. SquadronName )
|
||
end
|
||
|
||
end
|
||
|
||
|
||
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param #number EngageMinSpeed (optional, default = 50% of max speed) The minimum speed at which the SEAD task can be executed.
|
||
-- @param #number EngageMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the SEAD task can be executed.
|
||
-- @param DCS#Altitude EngageFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the engagement.
|
||
-- @param DCS#Altitude EngageCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the engagement.
|
||
-- @usage
|
||
--
|
||
-- -- SEAD Squadron execution.
|
||
-- A2GDispatcher:SetSquadronSead( "Mozdok", 900, 1200 )
|
||
-- A2GDispatcher:SetSquadronSead( "Novo", 900, 2100 )
|
||
-- A2GDispatcher:SetSquadronSead( "Maykop", 900, 1200 )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetSquadronSead( SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
|
||
DefenderSquadron.SEAD = DefenderSquadron.SEAD or {}
|
||
|
||
local Sead = DefenderSquadron.SEAD
|
||
Sead.Name = SquadronName
|
||
Sead.EngageMinSpeed = EngageMinSpeed
|
||
Sead.EngageMaxSpeed = EngageMaxSpeed
|
||
Sead.EngageFloorAltitude = EngageFloorAltitude or 500
|
||
Sead.EngageCeilingAltitude = EngageCeilingAltitude or 1000
|
||
Sead.Defend = true
|
||
|
||
self:F( { Sead = Sead } )
|
||
end
|
||
|
||
--- Set the squadron SEAD engage limit.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param #number EngageLimit The maximum amount of groups to engage with the enemy for this squadron.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Patrol Squadron execution.
|
||
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
|
||
-- A2GDispatcher:SetSquadronSeadEngageLimit( "Mineralnye", 2 ) -- Engage maximum 2 groups with the enemy for SEAD defense.
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronSeadEngageLimit( SquadronName, EngageLimit )
|
||
|
||
self:SetSquadronEngageLimit( SquadronName, EngageLimit, "SEAD" )
|
||
|
||
end
|
||
|
||
|
||
|
||
|
||
--- Set a Sead patrol for a Squadron.
|
||
-- The Sead patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed.
|
||
-- @param #number FloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed.
|
||
-- @param #number CeilingAltitude (optional, default = 1500m ) The maximum altitude at which the cap can be executed.
|
||
-- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed.
|
||
-- @param #number PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the cap can be executed.
|
||
-- @param #number EngageMinSpeed (optional, default = 50% of max speed) The minimum speed at which the engage can be executed.
|
||
-- @param #number EngageMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the engage can be executed.
|
||
-- @param #number AltType The altitude type, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Sead Patrol Squadron execution.
|
||
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
|
||
-- A2GDispatcher:SetSquadronSeadPatrol( "Mineralnye", PatrolZoneEast, 4000, 10000, 500, 600, 800, 900 )
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronSeadPatrol( SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
|
||
DefenderSquadron.SEAD = DefenderSquadron.SEAD or {}
|
||
|
||
local SeadPatrol = DefenderSquadron.SEAD
|
||
SeadPatrol.Name = SquadronName
|
||
SeadPatrol.Zone = Zone
|
||
SeadPatrol.PatrolFloorAltitude = FloorAltitude
|
||
SeadPatrol.PatrolCeilingAltitude = CeilingAltitude
|
||
SeadPatrol.EngageFloorAltitude = FloorAltitude
|
||
SeadPatrol.EngageCeilingAltitude = CeilingAltitude
|
||
SeadPatrol.PatrolMinSpeed = PatrolMinSpeed
|
||
SeadPatrol.PatrolMaxSpeed = PatrolMaxSpeed
|
||
SeadPatrol.EngageMinSpeed = EngageMinSpeed
|
||
SeadPatrol.EngageMaxSpeed = EngageMaxSpeed
|
||
SeadPatrol.AltType = AltType
|
||
SeadPatrol.Patrol = true
|
||
|
||
self:SetSquadronPatrolInterval( SquadronName, self.DefenderDefault.PatrolLimit, self.DefenderDefault.PatrolMinSeconds, self.DefenderDefault.PatrolMaxSeconds, 1, "SEAD" )
|
||
|
||
self:F( { Sead = SeadPatrol } )
|
||
end
|
||
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param #number EngageMinSpeed (optional, default = 50% of max speed) The minimum speed at which the CAS task can be executed.
|
||
-- @param #number EngageMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the CAS task can be executed.
|
||
-- @param DCS#Altitude EngageFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the engagement.
|
||
-- @param DCS#Altitude EngageCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the engagement.
|
||
-- @usage
|
||
--
|
||
-- -- CAS Squadron execution.
|
||
-- A2GDispatcher:SetSquadronCas( "Mozdok", 900, 1200 )
|
||
-- A2GDispatcher:SetSquadronCas( "Novo", 900, 2100 )
|
||
-- A2GDispatcher:SetSquadronCas( "Maykop", 900, 1200 )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetSquadronCas( SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
|
||
DefenderSquadron.CAS = DefenderSquadron.CAS or {}
|
||
|
||
local Cas = DefenderSquadron.CAS
|
||
Cas.Name = SquadronName
|
||
Cas.EngageMinSpeed = EngageMinSpeed
|
||
Cas.EngageMaxSpeed = EngageMaxSpeed
|
||
Cas.EngageFloorAltitude = EngageFloorAltitude or 500
|
||
Cas.EngageCeilingAltitude = EngageCeilingAltitude or 1000
|
||
Cas.Defend = true
|
||
|
||
self:F( { Cas = Cas } )
|
||
end
|
||
|
||
|
||
--- Set the squadron CAS engage limit.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param #number EngageLimit The maximum amount of groups to engage with the enemy for this squadron.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Patrol Squadron execution.
|
||
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
|
||
-- A2GDispatcher:SetSquadronCasEngageLimit( "Mineralnye", 2 ) -- Engage maximum 2 groups with the enemy for CAS defense.
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronCasEngageLimit( SquadronName, EngageLimit )
|
||
|
||
self:SetSquadronEngageLimit( SquadronName, EngageLimit, "CAS" )
|
||
|
||
end
|
||
|
||
|
||
|
||
|
||
--- Set a Cas patrol for a Squadron.
|
||
-- The Cas patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed.
|
||
-- @param #number FloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed.
|
||
-- @param #number CeilingAltitude (optional, default = 1500m ) The maximum altitude at which the cap can be executed.
|
||
-- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed.
|
||
-- @param #number PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the cap can be executed.
|
||
-- @param #number EngageMinSpeed (optional, default = 50% of max speed) The minimum speed at which the engage can be executed.
|
||
-- @param #number EngageMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the engage can be executed.
|
||
-- @param #number AltType The altitude type, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Cas Patrol Squadron execution.
|
||
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
|
||
-- A2GDispatcher:SetSquadronCasPatrol( "Mineralnye", PatrolZoneEast, 4000, 10000, 500, 600, 800, 900 )
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronCasPatrol( SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
|
||
DefenderSquadron.CAS = DefenderSquadron.CAS or {}
|
||
|
||
local CasPatrol = DefenderSquadron.CAS
|
||
CasPatrol.Name = SquadronName
|
||
CasPatrol.Zone = Zone
|
||
CasPatrol.PatrolFloorAltitude = FloorAltitude
|
||
CasPatrol.PatrolCeilingAltitude = CeilingAltitude
|
||
CasPatrol.EngageFloorAltitude = FloorAltitude
|
||
CasPatrol.EngageCeilingAltitude = CeilingAltitude
|
||
CasPatrol.PatrolMinSpeed = PatrolMinSpeed
|
||
CasPatrol.PatrolMaxSpeed = PatrolMaxSpeed
|
||
CasPatrol.EngageMinSpeed = EngageMinSpeed
|
||
CasPatrol.EngageMaxSpeed = EngageMaxSpeed
|
||
CasPatrol.AltType = AltType
|
||
CasPatrol.Patrol = true
|
||
|
||
self:SetSquadronPatrolInterval( SquadronName, self.DefenderDefault.PatrolLimit, self.DefenderDefault.PatrolMinSeconds, self.DefenderDefault.PatrolMaxSeconds, 1, "CAS" )
|
||
|
||
self:F( { Cas = CasPatrol } )
|
||
end
|
||
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param #number EngageMinSpeed (optional, default = 50% of max speed) The minimum speed at which the BAI task can be executed.
|
||
-- @param #number EngageMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the BAI task can be executed.
|
||
-- @param DCS#Altitude EngageFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the engagement.
|
||
-- @param DCS#Altitude EngageCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the engagement.
|
||
-- @usage
|
||
--
|
||
-- -- BAI Squadron execution.
|
||
-- A2GDispatcher:SetSquadronBai( "Mozdok", 900, 1200 )
|
||
-- A2GDispatcher:SetSquadronBai( "Novo", 900, 2100 )
|
||
-- A2GDispatcher:SetSquadronBai( "Maykop", 900, 1200 )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetSquadronBai( SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
|
||
DefenderSquadron.BAI = DefenderSquadron.BAI or {}
|
||
|
||
local Bai = DefenderSquadron.BAI
|
||
Bai.Name = SquadronName
|
||
Bai.EngageMinSpeed = EngageMinSpeed
|
||
Bai.EngageMaxSpeed = EngageMaxSpeed
|
||
Bai.EngageFloorAltitude = EngageFloorAltitude or 500
|
||
Bai.EngageCeilingAltitude = EngageCeilingAltitude or 1000
|
||
Bai.Defend = true
|
||
|
||
self:F( { Bai = Bai } )
|
||
end
|
||
|
||
|
||
--- Set the squadron BAI engage limit.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param #number EngageLimit The maximum amount of groups to engage with the enemy for this squadron.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Patrol Squadron execution.
|
||
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
|
||
-- A2GDispatcher:SetSquadronBaiEngageLimit( "Mineralnye", 2 ) -- Engage maximum 2 groups with the enemy for BAI defense.
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronBaiEngageLimit( SquadronName, EngageLimit )
|
||
|
||
self:SetSquadronEngageLimit( SquadronName, EngageLimit, "BAI" )
|
||
|
||
end
|
||
|
||
|
||
--- Set a Bai patrol for a Squadron.
|
||
-- The Bai patrol will start a patrol of the aircraft at a specified zone, and will engage when commanded.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
-- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the Patrol will be executed.
|
||
-- @param #number FloorAltitude (optional, default = 1000m ) The minimum altitude at which the cap can be executed.
|
||
-- @param #number CeilingAltitude (optional, default = 1500m ) The maximum altitude at which the cap can be executed.
|
||
-- @param #number PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed at which the cap can be executed.
|
||
-- @param #number PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the cap can be executed.
|
||
-- @param #number EngageMinSpeed (optional, default = 50% of max speed) The minimum speed at which the engage can be executed.
|
||
-- @param #number EngageMaxSpeed (optional, default = 75% of max speed) The maximum speed at which the engage can be executed.
|
||
-- @param #number AltType The altitude type, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Bai Patrol Squadron execution.
|
||
-- PatrolZoneEast = ZONE_POLYGON:New( "Patrol Zone East", GROUP:FindByName( "Patrol Zone East" ) )
|
||
-- A2GDispatcher:SetSquadronBaiPatrol( "Mineralnye", PatrolZoneEast, 4000, 10000, 500, 600, 800, 900 )
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronBaiPatrol( SquadronName, Zone, FloorAltitude, CeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
|
||
DefenderSquadron.BAI = DefenderSquadron.BAI or {}
|
||
|
||
local BaiPatrol = DefenderSquadron.BAI
|
||
BaiPatrol.Name = SquadronName
|
||
BaiPatrol.Zone = Zone
|
||
BaiPatrol.PatrolFloorAltitude = FloorAltitude
|
||
BaiPatrol.PatrolCeilingAltitude = CeilingAltitude
|
||
BaiPatrol.EngageFloorAltitude = FloorAltitude
|
||
BaiPatrol.EngageCeilingAltitude = CeilingAltitude
|
||
BaiPatrol.PatrolMinSpeed = PatrolMinSpeed
|
||
BaiPatrol.PatrolMaxSpeed = PatrolMaxSpeed
|
||
BaiPatrol.EngageMinSpeed = EngageMinSpeed
|
||
BaiPatrol.EngageMaxSpeed = EngageMaxSpeed
|
||
BaiPatrol.AltType = AltType
|
||
BaiPatrol.Patrol = true
|
||
|
||
self:SetSquadronPatrolInterval( SquadronName, self.DefenderDefault.PatrolLimit, self.DefenderDefault.PatrolMinSeconds, self.DefenderDefault.PatrolMaxSeconds, 1, "BAI" )
|
||
|
||
self:F( { Bai = BaiPatrol } )
|
||
end
|
||
|
||
|
||
--- Defines the default amount of extra planes that will take-off as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number Overhead The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
|
||
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
|
||
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance A2G missiles payload, may still be less effective than a F-15C with short missiles...
|
||
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
|
||
-- The overhead must be given as a decimal value with 1 as the neutral value, which means that Overhead values:
|
||
--
|
||
-- * Higher than 1, will increase the defense unit amounts.
|
||
-- * Lower than 1, will decrease the defense unit amounts.
|
||
--
|
||
-- The amount of defending units is calculated by multiplying the amount of detected attacking planes as part of the detected group
|
||
-- multiplied by the Overhead and rounded up to the smallest integer.
|
||
--
|
||
-- The Overhead value set for a Squadron, can be programmatically adjusted (by using this SetOverhead method), to adjust the defense overhead during mission execution.
|
||
--
|
||
-- See example below.
|
||
--
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- An overhead of 1,5 with 1 planes detected, will allocate 2 planes ( 1 * 1,5 ) = 1,5 => rounded up gives 2.
|
||
-- -- An overhead of 1,5 with 2 planes detected, will allocate 3 planes ( 2 * 1,5 ) = 3 => rounded up gives 3.
|
||
-- -- An overhead of 1,5 with 3 planes detected, will allocate 5 planes ( 3 * 1,5 ) = 4,5 => rounded up gives 5 planes.
|
||
-- -- An overhead of 1,5 with 4 planes detected, will allocate 6 planes ( 4 * 1,5 ) = 6 => rounded up gives 6 planes.
|
||
--
|
||
-- A2GDispatcher:SetDefaultOverhead( 1.5 )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetDefaultOverhead( Overhead )
|
||
|
||
self.DefenderDefault.Overhead = Overhead
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Defines the amount of extra planes that will take-off as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @param #number Overhead The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
|
||
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
|
||
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance A2G missiles payload, may still be less effective than a F-15C with short missiles...
|
||
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
|
||
-- The overhead must be given as a decimal value with 1 as the neutral value, which means that Overhead values:
|
||
--
|
||
-- * Higher than 1, will increase the defense unit amounts.
|
||
-- * Lower than 1, will decrease the defense unit amounts.
|
||
--
|
||
-- The amount of defending units is calculated by multiplying the amount of detected attacking planes as part of the detected group
|
||
-- multiplied by the Overhead and rounded up to the smallest integer.
|
||
--
|
||
-- The Overhead value set for a Squadron, can be programmatically adjusted (by using this SetOverhead method), to adjust the defense overhead during mission execution.
|
||
--
|
||
-- See example below.
|
||
--
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- An overhead of 1,5 with 1 planes detected, will allocate 2 planes ( 1 * 1,5 ) = 1,5 => rounded up gives 2.
|
||
-- -- An overhead of 1,5 with 2 planes detected, will allocate 3 planes ( 2 * 1,5 ) = 3 => rounded up gives 3.
|
||
-- -- An overhead of 1,5 with 3 planes detected, will allocate 5 planes ( 3 * 1,5 ) = 4,5 => rounded up gives 5 planes.
|
||
-- -- An overhead of 1,5 with 4 planes detected, will allocate 6 planes ( 4 * 1,5 ) = 6 => rounded up gives 6 planes.
|
||
--
|
||
-- A2GDispatcher:SetSquadronOverhead( "SquadronName", 1.5 )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetSquadronOverhead( SquadronName, Overhead )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
DefenderSquadron.Overhead = Overhead
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Gets the overhead of planes as part of the defense system, in comparison with the attackers.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @return #number The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
|
||
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
|
||
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance A2G missiles payload, may still be less effective than a F-15C with short missiles...
|
||
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
|
||
-- The overhead must be given as a decimal value with 1 as the neutral value, which means that Overhead values:
|
||
--
|
||
-- * Higher than 1, will increase the defense unit amounts.
|
||
-- * Lower than 1, will decrease the defense unit amounts.
|
||
--
|
||
-- The amount of defending units is calculated by multiplying the amount of detected attacking planes as part of the detected group
|
||
-- multiplied by the Overhead and rounded up to the smallest integer.
|
||
--
|
||
-- The Overhead value set for a Squadron, can be programmatically adjusted (by using this SetOverhead method), to adjust the defense overhead during mission execution.
|
||
--
|
||
-- See example below.
|
||
--
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- An overhead of 1,5 with 1 planes detected, will allocate 2 planes ( 1 * 1,5 ) = 1,5 => rounded up gives 2.
|
||
-- -- An overhead of 1,5 with 2 planes detected, will allocate 3 planes ( 2 * 1,5 ) = 3 => rounded up gives 3.
|
||
-- -- An overhead of 1,5 with 3 planes detected, will allocate 5 planes ( 3 * 1,5 ) = 4,5 => rounded up gives 5 planes.
|
||
-- -- An overhead of 1,5 with 4 planes detected, will allocate 6 planes ( 4 * 1,5 ) = 6 => rounded up gives 6 planes.
|
||
--
|
||
-- local SquadronOverhead = A2GDispatcher:GetSquadronOverhead( "SquadronName" )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:GetSquadronOverhead( SquadronName )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
return DefenderSquadron.Overhead or self.DefenderDefault.Overhead
|
||
end
|
||
|
||
|
||
--- Sets the default grouping of new airplanes spawned.
|
||
-- Grouping will trigger how new airplanes will be grouped if more than one airplane is spawned for defense.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number Grouping The level of grouping that will be applied of the Patrol or GCI defenders.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Set a grouping by default per 2 airplanes.
|
||
-- A2GDispatcher:SetDefaultGrouping( 2 )
|
||
--
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetDefaultGrouping( Grouping )
|
||
|
||
self.DefenderDefault.Grouping = Grouping
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Sets the grouping of new airplanes spawned.
|
||
-- Grouping will trigger how new airplanes will be grouped if more than one airplane is spawned for defense.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @param #number Grouping The level of grouping that will be applied of the Patrol or GCI defenders.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Set a grouping per 2 airplanes.
|
||
-- A2GDispatcher:SetSquadronGrouping( "SquadronName", 2 )
|
||
--
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetSquadronGrouping( SquadronName, Grouping )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
DefenderSquadron.Grouping = Grouping
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Defines the default method at which new flights will spawn and take-off as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number Takeoff From the airbase hot, from the airbase cold, in the air, from the runway.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights by default take-off in the air.
|
||
-- A2GDispatcher:SetDefaultTakeoff( AI_A2G_Dispatcher.Takeoff.Air )
|
||
--
|
||
-- -- Let new flights by default take-off from the runway.
|
||
-- A2GDispatcher:SetDefaultTakeoff( AI_A2G_Dispatcher.Takeoff.Runway )
|
||
--
|
||
-- -- Let new flights by default take-off from the airbase hot.
|
||
-- A2GDispatcher:SetDefaultTakeoff( AI_A2G_Dispatcher.Takeoff.Hot )
|
||
--
|
||
-- -- Let new flights by default take-off from the airbase cold.
|
||
-- A2GDispatcher:SetDefaultTakeoff( AI_A2G_Dispatcher.Takeoff.Cold )
|
||
--
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
--
|
||
function AI_AIR_DISPATCHER:SetDefaultTakeoff( Takeoff )
|
||
|
||
self.DefenderDefault.Takeoff = Takeoff
|
||
|
||
return self
|
||
end
|
||
|
||
--- Defines the method at which new flights will spawn and take-off as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @param #number Takeoff From the airbase hot, from the airbase cold, in the air, from the runway.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights take-off in the air.
|
||
-- A2GDispatcher:SetSquadronTakeoff( "SquadronName", AI_A2G_Dispatcher.Takeoff.Air )
|
||
--
|
||
-- -- Let new flights take-off from the runway.
|
||
-- A2GDispatcher:SetSquadronTakeoff( "SquadronName", AI_A2G_Dispatcher.Takeoff.Runway )
|
||
--
|
||
-- -- Let new flights take-off from the airbase hot.
|
||
-- A2GDispatcher:SetSquadronTakeoff( "SquadronName", AI_A2G_Dispatcher.Takeoff.Hot )
|
||
--
|
||
-- -- Let new flights take-off from the airbase cold.
|
||
-- A2GDispatcher:SetSquadronTakeoff( "SquadronName", AI_A2G_Dispatcher.Takeoff.Cold )
|
||
--
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronTakeoff( SquadronName, Takeoff )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
DefenderSquadron.Takeoff = Takeoff
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Gets the default method at which new flights will spawn and take-off as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @return #number Takeoff From the airbase hot, from the airbase cold, in the air, from the runway.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights by default take-off in the air.
|
||
-- local TakeoffMethod = A2GDispatcher:GetDefaultTakeoff()
|
||
-- if TakeOffMethod == , AI_A2G_Dispatcher.Takeoff.InAir then
|
||
-- ...
|
||
-- end
|
||
--
|
||
function AI_AIR_DISPATCHER:GetDefaultTakeoff( )
|
||
|
||
return self.DefenderDefault.Takeoff
|
||
end
|
||
|
||
--- Gets the method at which new flights will spawn and take-off as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @return #number Takeoff From the airbase hot, from the airbase cold, in the air, from the runway.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights take-off in the air.
|
||
-- local TakeoffMethod = A2GDispatcher:GetSquadronTakeoff( "SquadronName" )
|
||
-- if TakeOffMethod == , AI_A2G_Dispatcher.Takeoff.InAir then
|
||
-- ...
|
||
-- end
|
||
--
|
||
function AI_AIR_DISPATCHER:GetSquadronTakeoff( SquadronName )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
return DefenderSquadron.Takeoff or self.DefenderDefault.Takeoff
|
||
end
|
||
|
||
|
||
--- Sets flights to default take-off in the air, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights by default take-off in the air.
|
||
-- A2GDispatcher:SetDefaultTakeoffInAir()
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
--
|
||
function AI_AIR_DISPATCHER:SetDefaultTakeoffInAir()
|
||
|
||
self:SetDefaultTakeoff( AI_AIR_DISPATCHER.Takeoff.Air )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Sets flights to take-off in the air, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @param #number TakeoffAltitude (optional) The altitude in meters above the ground. If not given, the default takeoff altitude will be used.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights take-off in the air.
|
||
-- A2GDispatcher:SetSquadronTakeoffInAir( "SquadronName" )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronTakeoffInAir( SquadronName, TakeoffAltitude )
|
||
|
||
self:SetSquadronTakeoff( SquadronName, AI_AIR_DISPATCHER.Takeoff.Air )
|
||
|
||
if TakeoffAltitude then
|
||
self:SetSquadronTakeoffInAirAltitude( SquadronName, TakeoffAltitude )
|
||
end
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Sets flights by default to take-off from the runway, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights by default take-off from the runway.
|
||
-- A2GDispatcher:SetDefaultTakeoffFromRunway()
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
--
|
||
function AI_AIR_DISPATCHER:SetDefaultTakeoffFromRunway()
|
||
|
||
self:SetDefaultTakeoff( AI_AIR_DISPATCHER.Takeoff.Runway )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Sets flights to take-off from the runway, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights take-off from the runway.
|
||
-- A2GDispatcher:SetSquadronTakeoffFromRunway( "SquadronName" )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronTakeoffFromRunway( SquadronName )
|
||
|
||
self:SetSquadronTakeoff( SquadronName, AI_AIR_DISPATCHER.Takeoff.Runway )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Sets flights by default to take-off from the airbase at a hot location, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights by default take-off at a hot parking spot.
|
||
-- A2GDispatcher:SetDefaultTakeoffFromParkingHot()
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
--
|
||
function AI_AIR_DISPATCHER:SetDefaultTakeoffFromParkingHot()
|
||
|
||
self:SetDefaultTakeoff( AI_AIR_DISPATCHER.Takeoff.Hot )
|
||
|
||
return self
|
||
end
|
||
|
||
--- Sets flights to take-off from the airbase at a hot location, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights take-off in the air.
|
||
-- A2GDispatcher:SetSquadronTakeoffFromParkingHot( "SquadronName" )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronTakeoffFromParkingHot( SquadronName )
|
||
|
||
self:SetSquadronTakeoff( SquadronName, AI_AIR_DISPATCHER.Takeoff.Hot )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Sets flights to by default take-off from the airbase at a cold location, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights take-off from a cold parking spot.
|
||
-- A2GDispatcher:SetDefaultTakeoffFromParkingCold()
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
--
|
||
function AI_AIR_DISPATCHER:SetDefaultTakeoffFromParkingCold()
|
||
|
||
self:SetDefaultTakeoff( AI_AIR_DISPATCHER.Takeoff.Cold )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Sets flights to take-off from the airbase at a cold location, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights take-off from a cold parking spot.
|
||
-- A2GDispatcher:SetSquadronTakeoffFromParkingCold( "SquadronName" )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronTakeoffFromParkingCold( SquadronName )
|
||
|
||
self:SetSquadronTakeoff( SquadronName, AI_AIR_DISPATCHER.Takeoff.Cold )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Defines the default altitude where airplanes will spawn in the air and take-off as part of the defense system, when the take-off in the air method has been selected.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number TakeoffAltitude The altitude in meters above the ground.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Set the default takeoff altitude when taking off in the air.
|
||
-- A2GDispatcher:SetDefaultTakeoffInAirAltitude( 2000 ) -- This makes planes start at 2000 meters above the ground.
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
--
|
||
function AI_AIR_DISPATCHER:SetDefaultTakeoffInAirAltitude( TakeoffAltitude )
|
||
|
||
self.DefenderDefault.TakeoffAltitude = TakeoffAltitude
|
||
|
||
return self
|
||
end
|
||
|
||
--- Defines the default altitude where airplanes will spawn in the air and take-off as part of the defense system, when the take-off in the air method has been selected.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @param #number TakeoffAltitude The altitude in meters above the ground.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Set the default takeoff altitude when taking off in the air.
|
||
-- A2GDispatcher:SetSquadronTakeoffInAirAltitude( "SquadronName", 2000 ) -- This makes planes start at 2000 meters above the ground.
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronTakeoffInAirAltitude( SquadronName, TakeoffAltitude )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
DefenderSquadron.TakeoffAltitude = TakeoffAltitude
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Defines the default method at which flights will land and despawn as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights by default despawn near the airbase when returning.
|
||
-- A2GDispatcher:SetDefaultLanding( AI_A2G_Dispatcher.Landing.NearAirbase )
|
||
--
|
||
-- -- Let new flights by default despawn after landing land at the runway.
|
||
-- A2GDispatcher:SetDefaultLanding( AI_A2G_Dispatcher.Landing.AtRunway )
|
||
--
|
||
-- -- Let new flights by default despawn after landing and parking, and after engine shutdown.
|
||
-- A2GDispatcher:SetDefaultLanding( AI_A2G_Dispatcher.Landing.AtEngineShutdown )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetDefaultLanding( Landing )
|
||
|
||
self.DefenderDefault.Landing = Landing
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Defines the method at which flights will land and despawn as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @param #number Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights despawn near the airbase when returning.
|
||
-- A2GDispatcher:SetSquadronLanding( "SquadronName", AI_A2G_Dispatcher.Landing.NearAirbase )
|
||
--
|
||
-- -- Let new flights despawn after landing land at the runway.
|
||
-- A2GDispatcher:SetSquadronLanding( "SquadronName", AI_A2G_Dispatcher.Landing.AtRunway )
|
||
--
|
||
-- -- Let new flights despawn after landing and parking, and after engine shutdown.
|
||
-- A2GDispatcher:SetSquadronLanding( "SquadronName", AI_A2G_Dispatcher.Landing.AtEngineShutdown )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetSquadronLanding( SquadronName, Landing )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
DefenderSquadron.Landing = Landing
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Gets the default method at which flights will land and despawn as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @return #number Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights by default despawn near the airbase when returning.
|
||
-- local LandingMethod = A2GDispatcher:GetDefaultLanding( AI_A2G_Dispatcher.Landing.NearAirbase )
|
||
-- if LandingMethod == AI_A2G_Dispatcher.Landing.NearAirbase then
|
||
-- ...
|
||
-- end
|
||
--
|
||
function AI_AIR_DISPATCHER:GetDefaultLanding()
|
||
|
||
return self.DefenderDefault.Landing
|
||
end
|
||
|
||
|
||
--- Gets the method at which flights will land and despawn as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @return #number Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let new flights despawn near the airbase when returning.
|
||
-- local LandingMethod = A2GDispatcher:GetSquadronLanding( "SquadronName", AI_A2G_Dispatcher.Landing.NearAirbase )
|
||
-- if LandingMethod == AI_A2G_Dispatcher.Landing.NearAirbase then
|
||
-- ...
|
||
-- end
|
||
--
|
||
function AI_AIR_DISPATCHER:GetSquadronLanding( SquadronName )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
return DefenderSquadron.Landing or self.DefenderDefault.Landing
|
||
end
|
||
|
||
|
||
--- Sets flights by default to land and despawn near the airbase in the air, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let flights by default to land near the airbase and despawn.
|
||
-- A2GDispatcher:SetDefaultLandingNearAirbase()
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetDefaultLandingNearAirbase()
|
||
|
||
self:SetDefaultLanding( AI_AIR_DISPATCHER.Landing.NearAirbase )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Sets flights to land and despawn near the airbase in the air, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let flights to land near the airbase and despawn.
|
||
-- A2GDispatcher:SetSquadronLandingNearAirbase( "SquadronName" )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetSquadronLandingNearAirbase( SquadronName )
|
||
|
||
self:SetSquadronLanding( SquadronName, AI_AIR_DISPATCHER.Landing.NearAirbase )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Sets flights by default to land and despawn at the runway, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let flights by default land at the runway and despawn.
|
||
-- A2GDispatcher:SetDefaultLandingAtRunway()
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetDefaultLandingAtRunway()
|
||
|
||
self:SetDefaultLanding( AI_AIR_DISPATCHER.Landing.AtRunway )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Sets flights to land and despawn at the runway, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let flights land at the runway and despawn.
|
||
-- A2GDispatcher:SetSquadronLandingAtRunway( "SquadronName" )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetSquadronLandingAtRunway( SquadronName )
|
||
|
||
self:SetSquadronLanding( SquadronName, AI_AIR_DISPATCHER.Landing.AtRunway )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Sets flights by default to land and despawn at engine shutdown, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let flights by default land and despawn at engine shutdown.
|
||
-- A2GDispatcher:SetDefaultLandingAtEngineShutdown()
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetDefaultLandingAtEngineShutdown()
|
||
|
||
self:SetDefaultLanding( AI_AIR_DISPATCHER.Landing.AtEngineShutdown )
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Sets flights to land and despawn at engine shutdown, as part of the defense system.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @usage:
|
||
--
|
||
-- local A2GDispatcher = AI_AIR_DISPATCHER:New( ... )
|
||
--
|
||
-- -- Let flights land and despawn at engine shutdown.
|
||
-- A2GDispatcher:SetSquadronLandingAtEngineShutdown( "SquadronName" )
|
||
--
|
||
-- @return #AI_AIR_DISPATCHER
|
||
function AI_AIR_DISPATCHER:SetSquadronLandingAtEngineShutdown( SquadronName )
|
||
|
||
self:SetSquadronLanding( SquadronName, AI_AIR_DISPATCHER.Landing.AtEngineShutdown )
|
||
|
||
return self
|
||
end
|
||
|
||
--- Set the default fuel treshold when defenders will RTB or Refuel in the air.
|
||
-- The fuel treshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the %-tage of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
|
||
-- A2GDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||
--
|
||
-- -- Now Setup the default fuel treshold.
|
||
-- A2GDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||
--
|
||
function AI_AIR_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold )
|
||
|
||
self.DefenderDefault.FuelThreshold = FuelThreshold
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Set the fuel treshold for the squadron when defenders will RTB or Refuel in the air.
|
||
-- The fuel treshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the %-tage of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
|
||
-- A2GDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||
--
|
||
-- -- Now Setup the default fuel treshold.
|
||
-- A2GDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||
--
|
||
function AI_AIR_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
DefenderSquadron.FuelThreshold = FuelThreshold
|
||
|
||
return self
|
||
end
|
||
|
||
--- Set the default tanker where defenders will Refuel in the air.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string TankerName A string defining the group name of the Tanker as defined within the Mission Editor.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
|
||
-- A2GDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||
--
|
||
-- -- Now Setup the default fuel treshold.
|
||
-- A2GDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||
--
|
||
-- -- Now Setup the default tanker.
|
||
-- A2GDispatcher:SetDefaultTanker( "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor.
|
||
function AI_AIR_DISPATCHER:SetDefaultTanker( TankerName )
|
||
|
||
self.DefenderDefault.TankerName = TankerName
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Set the squadron tanker where defenders will Refuel in the air.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The name of the squadron.
|
||
-- @param #string TankerName A string defining the group name of the Tanker as defined within the Mission Editor.
|
||
-- @return #AI_AIR_DISPATCHER
|
||
-- @usage
|
||
--
|
||
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
|
||
-- A2GDispatcher = AI_AIR_DISPATCHER:New( Detection )
|
||
--
|
||
-- -- Now Setup the squadron fuel treshold.
|
||
-- A2GDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||
--
|
||
-- -- Now Setup the squadron tanker.
|
||
-- A2GDispatcher:SetSquadronTanker( "SquadronName", "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor.
|
||
function AI_AIR_DISPATCHER:SetSquadronTanker( SquadronName, TankerName )
|
||
|
||
local DefenderSquadron = self:GetSquadron( SquadronName )
|
||
DefenderSquadron.TankerName = TankerName
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
|
||
|
||
--- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:AddDefenderToSquadron( Squadron, Defender, Size )
|
||
self.Defenders = self.Defenders or {}
|
||
local DefenderName = Defender:GetName()
|
||
self.Defenders[ DefenderName ] = Squadron
|
||
if Squadron.ResourceCount then
|
||
Squadron.ResourceCount = Squadron.ResourceCount - Size
|
||
end
|
||
self:F( { DefenderName = DefenderName, SquadronResourceCount = Squadron.ResourceCount } )
|
||
end
|
||
|
||
--- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:RemoveDefenderFromSquadron( Squadron, Defender )
|
||
self.Defenders = self.Defenders or {}
|
||
local DefenderName = Defender:GetName()
|
||
if Squadron.ResourceCount then
|
||
Squadron.ResourceCount = Squadron.ResourceCount + Defender:GetSize()
|
||
end
|
||
self.Defenders[ DefenderName ] = nil
|
||
self:F( { DefenderName = DefenderName, SquadronResourceCount = Squadron.ResourceCount } )
|
||
end
|
||
|
||
function AI_AIR_DISPATCHER:GetSquadronFromDefender( Defender )
|
||
self.Defenders = self.Defenders or {}
|
||
local DefenderName = Defender:GetName()
|
||
self:F( { DefenderName = DefenderName } )
|
||
return self.Defenders[ DefenderName ]
|
||
end
|
||
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:CountPatrolAirborne( SquadronName, DefenseTaskType )
|
||
|
||
local PatrolCount = 0
|
||
|
||
local DefenderSquadron = self.DefenderSquadrons[SquadronName]
|
||
if DefenderSquadron then
|
||
for AIGroup, DefenderTask in pairs( self:GetDefenderTasks() ) do
|
||
if DefenderTask.SquadronName == SquadronName then
|
||
if DefenderTask.Type == DefenseTaskType then
|
||
if AIGroup:IsAlive() then
|
||
-- Check if the Patrol is patrolling or engaging. If not, this is not a valid Patrol, even if it is alive!
|
||
-- The Patrol could be damaged, lost control, or out of fuel!
|
||
if DefenderTask.Fsm:Is( "Patrolling" ) or DefenderTask.Fsm:Is( "Engaging" ) or DefenderTask.Fsm:Is( "Refuelling" )
|
||
or DefenderTask.Fsm:Is( "Started" ) then
|
||
PatrolCount = PatrolCount + 1
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
return PatrolCount
|
||
end
|
||
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:CountDefendersEngaged( AttackerDetection, AttackerCount )
|
||
|
||
-- First, count the active AIGroups Units, targetting the DetectedSet
|
||
local DefendersEngaged = 0
|
||
local DefendersTotal = 0
|
||
|
||
local AttackerSet = AttackerDetection.Set
|
||
local DefendersMissing = AttackerCount
|
||
--DetectedSet:Flush()
|
||
|
||
local DefenderTasks = self:GetDefenderTasks()
|
||
for DefenderGroup, DefenderTask in pairs( DefenderTasks ) do
|
||
local Defender = DefenderGroup -- Wrapper.Group#GROUP
|
||
local DefenderTaskTarget = DefenderTask.Target
|
||
local DefenderSquadronName = DefenderTask.SquadronName
|
||
local DefenderSize = DefenderTask.Size
|
||
|
||
-- Count the total of defenders on the battlefield.
|
||
--local DefenderSize = Defender:GetInitialSize()
|
||
if DefenderTask.Target then
|
||
--if DefenderTask.Fsm:Is( "Engaging" ) then
|
||
self:F( "Defender Group Name: " .. Defender:GetName() .. ", Size: " .. DefenderSize )
|
||
DefendersTotal = DefendersTotal + DefenderSize
|
||
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
|
||
self:F( "Defender Group Name: " .. Defender:GetName() .. ", Size: " .. DefenderSize )
|
||
else
|
||
DefendersEngaged = 0
|
||
end
|
||
end
|
||
--end
|
||
end
|
||
|
||
|
||
end
|
||
|
||
for QueueID, QueueItem in pairs( self.DefenseQueue ) do
|
||
local QueueItem = QueueItem -- #AI_AIR_DISPATCHER.DefenseQueueItem
|
||
if QueueItem.AttackerDetection and QueueItem.AttackerDetection.ItemID == AttackerDetection.ItemID then
|
||
DefendersMissing = DefendersMissing - QueueItem.DefendersNeeded / QueueItem.DefenderSquadron.Overhead
|
||
--DefendersEngaged = DefendersEngaged + QueueItem.DefenderGrouping
|
||
self:F( { QueueItemName = QueueItem.Defense, QueueItem_ItemID = QueueItem.AttackerDetection.ItemID, DetectedItem = AttackerDetection.ItemID, DefendersMissing = DefendersMissing } )
|
||
end
|
||
end
|
||
|
||
self:F( { DefenderCount = DefendersEngaged } )
|
||
|
||
return DefendersTotal, DefendersEngaged, DefendersMissing
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:CountDefenders( AttackerDetection, DefenderCount, DefenderTaskType )
|
||
|
||
local Friendlies = nil
|
||
|
||
local AttackerSet = AttackerDetection.Set
|
||
local AttackerCount = AttackerSet:Count()
|
||
|
||
local DefenderFriendlies = self:GetDefenderFriendliesNearBy( AttackerDetection )
|
||
|
||
for FriendlyDistance, DefenderFriendlyUnit in UTILS.spairs( DefenderFriendlies or {} ) do
|
||
-- We only allow to engage targets as long as the units on both sides are balanced.
|
||
if AttackerCount > DefenderCount then
|
||
local FriendlyGroup = DefenderFriendlyUnit:GetGroup() -- Wrapper.Group#GROUP
|
||
if FriendlyGroup and FriendlyGroup:IsAlive() then
|
||
-- Ok, so we have a friendly near the potential target.
|
||
-- Now we need to check if the AIGroup has a Task.
|
||
local DefenderTask = self:GetDefenderTask( FriendlyGroup )
|
||
if DefenderTask then
|
||
-- The Task should be of the same type.
|
||
if DefenderTaskType == DefenderTask.Type then
|
||
-- If there is no target, then add the AIGroup to the ResultAIGroups for Engagement to the AttackerSet
|
||
if DefenderTask.Target == nil then
|
||
if DefenderTask.Fsm:Is( "Returning" )
|
||
or DefenderTask.Fsm:Is( "Patrolling" ) then
|
||
Friendlies = Friendlies or {}
|
||
Friendlies[FriendlyGroup] = FriendlyGroup
|
||
DefenderCount = DefenderCount + FriendlyGroup:GetSize()
|
||
self:F( { Friendly = FriendlyGroup:GetName(), FriendlyDistance = FriendlyDistance } )
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
else
|
||
break
|
||
end
|
||
end
|
||
|
||
return Friendlies
|
||
end
|
||
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:ResourceActivate( DefenderSquadron, DefendersNeeded )
|
||
|
||
local SquadronName = DefenderSquadron.Name
|
||
DefendersNeeded = DefendersNeeded or 4
|
||
local DefenderGrouping = DefenderSquadron.Grouping or self.DefenderDefault.Grouping
|
||
DefenderGrouping = ( DefenderGrouping < DefendersNeeded ) and DefenderGrouping or DefendersNeeded
|
||
|
||
if self:IsSquadronVisible( SquadronName ) then
|
||
|
||
-- Here we Patrol the new planes.
|
||
-- The Resources table is filled in advance.
|
||
local TemplateID = math.random( 1, #DefenderSquadron.Spawn ) -- Choose the template.
|
||
|
||
-- We determine the grouping based on the parameters set.
|
||
self:F( { DefenderGrouping = DefenderGrouping } )
|
||
|
||
-- New we will form the group to spawn in.
|
||
-- We search for the first free resource matching the template.
|
||
local DefenderUnitIndex = 1
|
||
local DefenderPatrolTemplate = nil
|
||
local DefenderName = nil
|
||
for GroupName, DefenderGroup in pairs( DefenderSquadron.Resources[TemplateID] or {} ) do
|
||
self:F( { GroupName = GroupName } )
|
||
local DefenderTemplate = _DATABASE:GetGroupTemplate( GroupName )
|
||
if DefenderUnitIndex == 1 then
|
||
DefenderPatrolTemplate = UTILS.DeepCopy( DefenderTemplate )
|
||
self.DefenderPatrolIndex = self.DefenderPatrolIndex + 1
|
||
--DefenderPatrolTemplate.name = SquadronName .. "#" .. self.DefenderPatrolIndex .. "#" .. GroupName
|
||
DefenderPatrolTemplate.name = GroupName
|
||
DefenderName = DefenderPatrolTemplate.name
|
||
else
|
||
-- Add the unit in the template to the DefenderPatrolTemplate.
|
||
local DefenderUnitTemplate = DefenderTemplate.units[1]
|
||
DefenderPatrolTemplate.units[DefenderUnitIndex] = DefenderUnitTemplate
|
||
end
|
||
DefenderPatrolTemplate.units[DefenderUnitIndex].name = string.format( DefenderPatrolTemplate.name .. '-%02d', DefenderUnitIndex )
|
||
DefenderPatrolTemplate.units[DefenderUnitIndex].unitId = nil
|
||
DefenderUnitIndex = DefenderUnitIndex + 1
|
||
DefenderSquadron.Resources[TemplateID][GroupName] = nil
|
||
if DefenderUnitIndex > DefenderGrouping then
|
||
break
|
||
end
|
||
|
||
end
|
||
|
||
if DefenderPatrolTemplate then
|
||
local TakeoffMethod = self:GetSquadronTakeoff( SquadronName )
|
||
local SpawnGroup = GROUP:Register( DefenderName )
|
||
DefenderPatrolTemplate.lateActivation = nil
|
||
DefenderPatrolTemplate.uncontrolled = nil
|
||
local Takeoff = self:GetSquadronTakeoff( SquadronName )
|
||
DefenderPatrolTemplate.route.points[1].type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
|
||
DefenderPatrolTemplate.route.points[1].action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action
|
||
local Defender = _DATABASE:Spawn( DefenderPatrolTemplate )
|
||
self:AddDefenderToSquadron( DefenderSquadron, Defender, DefenderGrouping )
|
||
Defender:Activate()
|
||
return Defender, DefenderGrouping
|
||
end
|
||
else
|
||
local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ] -- Core.Spawn#SPAWN
|
||
if DefenderGrouping then
|
||
Spawn:InitGrouping( DefenderGrouping )
|
||
else
|
||
Spawn:InitGrouping()
|
||
end
|
||
|
||
local TakeoffMethod = self:GetSquadronTakeoff( SquadronName )
|
||
local Defender = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod, DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude ) -- Wrapper.Group#GROUP
|
||
self:AddDefenderToSquadron( DefenderSquadron, Defender, DefenderGrouping )
|
||
return Defender, DefenderGrouping
|
||
end
|
||
|
||
return nil, nil
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:onafterPatrol( From, Event, To, SquadronName, DefenseTaskType )
|
||
|
||
local DefenderSquadron, Patrol = self:CanPatrol( SquadronName, DefenseTaskType )
|
||
|
||
-- Determine if there are sufficient resources to form a complete group for patrol.
|
||
if DefenderSquadron then
|
||
local DefendersNeeded
|
||
local DefendersGrouping = ( DefenderSquadron.Grouping or self.DefenderDefault.Grouping )
|
||
if DefenderSquadron.ResourceCount == nil then
|
||
DefendersNeeded = DefendersGrouping
|
||
else
|
||
if DefenderSquadron.ResourceCount >= DefendersGrouping then
|
||
DefendersNeeded = DefendersGrouping
|
||
else
|
||
DefendersNeeded = DefenderSquadron.ResourceCount
|
||
end
|
||
end
|
||
|
||
if Patrol then
|
||
self:ResourceQueue( true, DefenderSquadron, DefendersNeeded, Patrol, DefenseTaskType, nil, SquadronName )
|
||
end
|
||
end
|
||
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:ResourceQueue( Patrol, DefenderSquadron, DefendersNeeded, Defense, DefenseTaskType, AttackerDetection, SquadronName )
|
||
|
||
self:F( { DefenderSquadron, DefendersNeeded, Defense, DefenseTaskType, AttackerDetection, SquadronName } )
|
||
|
||
local DefenseQueueItem = {} -- #AI_AIR_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_AIR_DISPATCHER self
|
||
function AI_AIR_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_AIR_DISPATCHER self
|
||
function AI_AIR_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_AIR_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_AIR_DISPATCHER
|
||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " returning." )
|
||
|
||
Dispatcher:ClearDefenderTaskTarget( Defender )
|
||
end
|
||
|
||
--- @param #AI_AIR_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_AIR_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_AIR_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_AIR_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_AIR_DISPATCHER.Landing.NearAirbase then
|
||
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
|
||
Defender:Destroy()
|
||
Dispatcher:ResourcePark( Squadron, Defender )
|
||
end
|
||
end
|
||
end
|
||
|
||
end
|
||
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_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_AIR_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:EngageRoute( DefenderTarget.Set ) -- Engage on the TargetSetUnit
|
||
end
|
||
end
|
||
|
||
function Fsm:OnAfterEngageRoute( Defender, From, Event, To, AttackSetUnit )
|
||
self:F({"Engage Route", Defender:GetName()})
|
||
--self:GetParent(self).onafterBirth( self, Defender, From, Event, To )
|
||
|
||
local DefenderName = Defender:GetName()
|
||
local Dispatcher = Fsm:GetDispatcher() -- #AI_AIR_DISPATCHER
|
||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||
local FirstUnit = AttackSetUnit:GetFirst()
|
||
local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE
|
||
|
||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " on route, bearing " .. Coordinate:ToString( Defender ) )
|
||
end
|
||
|
||
function Fsm:OnAfterEngage( Defender, From, Event, To, AttackSetUnit )
|
||
self:F({"Engage Route", Defender:GetName()})
|
||
--self:GetParent(self).onafterBirth( self, Defender, From, Event, To )
|
||
|
||
local DefenderName = Defender:GetName()
|
||
local Dispatcher = Fsm:GetDispatcher() -- #AI_AIR_DISPATCHER
|
||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||
local FirstUnit = AttackSetUnit:GetFirst()
|
||
local Coordinate = FirstUnit:GetCoordinate()
|
||
|
||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " engaging target, bearing " .. Coordinate:ToString( Defender ) )
|
||
end
|
||
|
||
function Fsm:onafterRTB( Defender, From, Event, To )
|
||
self:F({"Defender RTB", Defender:GetName()})
|
||
|
||
local DefenderName = Defender:GetName()
|
||
local Dispatcher = self:GetDispatcher() -- #AI_AIR_DISPATCHER
|
||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " RTB." )
|
||
|
||
self:GetParent(self).onafterRTB( self, Defender, From, Event, To )
|
||
|
||
Dispatcher:ClearDefenderTaskTarget( Defender )
|
||
end
|
||
|
||
--- @param #AI_AIR_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_AIR_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_AIR_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_AIR_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_AIR_DISPATCHER.Landing.NearAirbase then
|
||
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
|
||
Defender:Destroy()
|
||
Dispatcher:ResourcePark( Squadron, Defender )
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:onafterEngage( From, Event, To, AttackerDetection, Defenders )
|
||
|
||
if Defenders then
|
||
|
||
for DefenderID, Defender in pairs( Defenders or {} ) do
|
||
|
||
local Fsm = self:GetDefenderTaskFsm( Defender )
|
||
Fsm:Engage( AttackerDetection.Set ) -- Engage on the TargetSetUnit
|
||
|
||
self:SetDefenderTaskTarget( Defender, AttackerDetection )
|
||
|
||
end
|
||
end
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:HasDefenseLine( DefenseCoordinate, DetectedItem )
|
||
|
||
local AttackCoordinate = self.Detection:GetDetectedItemCoordinate( DetectedItem )
|
||
local EvaluateDistance = AttackCoordinate:Get2DDistance( DefenseCoordinate )
|
||
|
||
-- Now check if this coordinate is not in a danger zone, meaning, that the attack line is not crossing other coordinates.
|
||
-- (y1 – y2)x + (x2 – x1)y + (x1y2 – x2y1) = 0
|
||
|
||
local c1 = DefenseCoordinate
|
||
local c2 = AttackCoordinate
|
||
|
||
local a = c1.z - c2.z -- Calculate a
|
||
local b = c2.x - c1.x -- Calculate b
|
||
local c = c1.x * c2.z - c2.x * c1.z -- calculate c
|
||
|
||
local ok = true
|
||
|
||
-- Now we check if each coordinate radius of about 30km of each attack is crossing a defense line. If yes, then this is not a good attack!
|
||
for AttackItemID, CheckAttackItem in pairs( self.Detection:GetDetectedItems() ) do
|
||
|
||
-- Only compare other detected coordinates.
|
||
if AttackItemID ~= DetectedItem.ID then
|
||
|
||
local CheckAttackCoordinate = self.Detection:GetDetectedItemCoordinate( CheckAttackItem )
|
||
|
||
local x = CheckAttackCoordinate.x
|
||
local y = CheckAttackCoordinate.z
|
||
local r = 5000
|
||
|
||
-- now we check if the coordinate is intersecting with the defense line.
|
||
|
||
local IntersectDistance = ( math.abs( a * x + b * y + c ) ) / math.sqrt( a * a + b * b )
|
||
self:F( { IntersectDistance = IntersectDistance, x = x, y = y } )
|
||
|
||
local IntersectAttackDistance = CheckAttackCoordinate:Get2DDistance( DefenseCoordinate )
|
||
|
||
self:F( { IntersectAttackDistance=IntersectAttackDistance, EvaluateDistance=EvaluateDistance } )
|
||
|
||
-- If the distance of the attack coordinate is larger than the test radius; then the line intersects, and this is not a good coordinate.
|
||
if IntersectDistance < r and IntersectAttackDistance < EvaluateDistance then
|
||
ok = false
|
||
break
|
||
end
|
||
end
|
||
end
|
||
|
||
return ok
|
||
end
|
||
|
||
---
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
function AI_AIR_DISPATCHER:onafterDefend( From, Event, To, DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, DefenderFriendlies, DefenseTaskType )
|
||
|
||
self:F( { From, Event, To, DetectedItem.Index, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing, DefenderFriendlies = DefenderFriendlies } )
|
||
|
||
DetectedItem.Type = DefenseTaskType -- This is set to report the task type in the status panel.
|
||
|
||
local AttackerSet = DetectedItem.Set
|
||
local AttackerUnit = AttackerSet:GetFirst()
|
||
|
||
if AttackerUnit and AttackerUnit:IsAlive() then
|
||
local AttackerCount = AttackerSet:Count()
|
||
local DefenderCount = 0
|
||
|
||
for DefenderID, DefenderGroup in pairs( DefenderFriendlies or {} ) do
|
||
|
||
-- Here we check if the defenders have a defense line to the attackers.
|
||
-- If the attackers are behind enemy lines or too close to an other defense line; then don´t engage.
|
||
local DefenseCoordinate = DefenderGroup:GetCoordinate()
|
||
local HasDefenseLine = self:HasDefenseLine( DefenseCoordinate, DetectedItem )
|
||
|
||
if HasDefenseLine == true then
|
||
local SquadronName = self:GetDefenderTask( DefenderGroup ).SquadronName
|
||
local SquadronOverhead = self:GetSquadronOverhead( SquadronName )
|
||
|
||
local Fsm = self:GetDefenderTaskFsm( DefenderGroup )
|
||
Fsm:EngageRoute( AttackerSet ) -- Engage on the TargetSetUnit
|
||
|
||
self:SetDefenderTaskTarget( DefenderGroup, DetectedItem )
|
||
|
||
local DefenderGroupSize = DefenderGroup:GetSize()
|
||
DefendersMissing = DefendersMissing - DefenderGroupSize / SquadronOverhead
|
||
DefendersTotal = DefendersTotal + DefenderGroupSize / SquadronOverhead
|
||
end
|
||
|
||
if DefendersMissing <= 0 then
|
||
break
|
||
end
|
||
end
|
||
|
||
self:F( { DefenderCount = DefenderCount, DefendersMissing = DefendersMissing } )
|
||
DefenderCount = DefendersMissing
|
||
|
||
local ClosestDistance = 0
|
||
local ClosestDefenderSquadronName = nil
|
||
|
||
local BreakLoop = false
|
||
|
||
while( DefenderCount > 0 and not BreakLoop ) do
|
||
|
||
self:F( { DefenderSquadrons = self.DefenderSquadrons } )
|
||
|
||
for SquadronName, DefenderSquadron in pairs( self.DefenderSquadrons or {} ) do
|
||
|
||
if DefenderSquadron[DefenseTaskType] then
|
||
|
||
local AirbaseCoordinate = DefenderSquadron.Airbase:GetCoordinate() -- Core.Point#COORDINATE
|
||
local AttackerCoord = AttackerUnit:GetCoordinate()
|
||
local InterceptCoord = DetectedItem.InterceptCoord
|
||
self:F( { InterceptCoord = InterceptCoord } )
|
||
if InterceptCoord then
|
||
local InterceptDistance = AirbaseCoordinate:Get2DDistance( InterceptCoord )
|
||
local AirbaseDistance = AirbaseCoordinate:Get2DDistance( AttackerCoord )
|
||
self:F( { InterceptDistance = InterceptDistance, AirbaseDistance = AirbaseDistance, InterceptCoord = InterceptCoord } )
|
||
|
||
if ClosestDistance == 0 or InterceptDistance < ClosestDistance then
|
||
|
||
-- Only intercept if the distance to target is smaller or equal to the GciRadius limit.
|
||
if AirbaseDistance <= self.DefenseRadius then
|
||
|
||
-- Check if there is a defense line...
|
||
local HasDefenseLine = self:HasDefenseLine( AirbaseCoordinate, DetectedItem )
|
||
if HasDefenseLine == true then
|
||
ClosestDistance = InterceptDistance
|
||
ClosestDefenderSquadronName = SquadronName
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
if ClosestDefenderSquadronName then
|
||
|
||
local DefenderSquadron, Defense = self:CanDefend( ClosestDefenderSquadronName, DefenseTaskType )
|
||
|
||
if Defense then
|
||
|
||
local DefenderOverhead = DefenderSquadron.Overhead or self.DefenderDefault.Overhead
|
||
local DefenderGrouping = DefenderSquadron.Grouping or self.DefenderDefault.Grouping
|
||
local DefendersNeeded = math.ceil( DefenderCount * DefenderOverhead )
|
||
|
||
self:F( { Overhead = DefenderOverhead, SquadronOverhead = DefenderSquadron.Overhead , DefaultOverhead = self.DefenderDefault.Overhead } )
|
||
self:F( { Grouping = DefenderGrouping, SquadronGrouping = DefenderSquadron.Grouping, DefaultGrouping = self.DefenderDefault.Grouping } )
|
||
self:F( { DefendersCount = DefenderCount, DefendersNeeded = DefendersNeeded } )
|
||
|
||
-- Validate that the maximum limit of Defenders has been reached.
|
||
-- If yes, then cancel the engaging of more defenders.
|
||
local DefendersLimit = DefenderSquadron.EngageLimit or self.DefenderDefault.EngageLimit
|
||
if DefendersLimit then
|
||
if DefendersTotal >= DefendersLimit then
|
||
DefendersNeeded = 0
|
||
BreakLoop = true
|
||
else
|
||
-- If the total of amount of defenders + the defenders needed, is larger than the limit of defenders,
|
||
-- then the defenders needed is the difference between defenders total - defenders limit.
|
||
if DefendersTotal + DefendersNeeded > DefendersLimit then
|
||
DefendersNeeded = DefendersLimit - DefendersTotal
|
||
end
|
||
end
|
||
end
|
||
|
||
-- DefenderSquadron.ResourceCount can have the value nil, which expresses unlimited resources.
|
||
-- DefendersNeeded cannot exceed DefenderSquadron.ResourceCount!
|
||
if DefenderSquadron.ResourceCount and DefendersNeeded > DefenderSquadron.ResourceCount then
|
||
DefendersNeeded = DefenderSquadron.ResourceCount
|
||
BreakLoop = true
|
||
end
|
||
|
||
while ( DefendersNeeded > 0 ) do
|
||
self:ResourceQueue( false, DefenderSquadron, DefendersNeeded, Defense, DefenseTaskType, DetectedItem, ClosestDefenderSquadronName )
|
||
DefendersNeeded = DefendersNeeded - DefenderGrouping
|
||
DefenderCount = DefenderCount - DefenderGrouping / DefenderOverhead
|
||
end -- while ( DefendersNeeded > 0 ) do
|
||
else
|
||
-- No more resources, try something else.
|
||
-- Subject for a later enhancement to try to depart from another squadron and disable this one.
|
||
BreakLoop = true
|
||
break
|
||
end
|
||
else
|
||
-- There isn't any closest airbase anymore, break the loop.
|
||
break
|
||
end
|
||
end -- if DefenderSquadron then
|
||
end -- if AttackerUnit
|
||
end
|
||
|
||
|
||
|
||
--- Creates an SEAD task when the targets have radars.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem The detected item.
|
||
-- @return Core.Set#SET_UNIT The set of units of the targets to be engaged.
|
||
-- @return #nil If there are no targets to be set.
|
||
function AI_AIR_DISPATCHER:Evaluate_SEAD( DetectedItem )
|
||
self:F( { DetectedItem.ItemID } )
|
||
|
||
local AttackerSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||
local AttackerCount = AttackerSet:HasSEAD() -- Is the AttackerSet a SEAD group, then the amount of radar emitters will be returned; that need to be attacked.
|
||
|
||
if ( AttackerCount > 0 ) then
|
||
|
||
-- First, count the active defenders, engaging the DetectedItem.
|
||
local DefendersTotal, DefendersEngaged, DefendersMissing = self:CountDefendersEngaged( DetectedItem, AttackerCount )
|
||
|
||
self:F( { AttackerCount = AttackerCount, DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } )
|
||
|
||
local DefenderGroups = self:CountDefenders( DetectedItem, DefendersEngaged, "SEAD" )
|
||
|
||
|
||
|
||
if DetectedItem.IsDetected == true then
|
||
|
||
return DefendersTotal, DefendersEngaged, DefendersMissing, DefenderGroups
|
||
end
|
||
end
|
||
|
||
return nil, nil, nil
|
||
end
|
||
|
||
|
||
--- Creates an CAS task.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem The detected item.
|
||
-- @return Core.Set#SET_UNIT The set of units of the targets to be engaged.
|
||
-- @return #nil If there are no targets to be set.
|
||
function AI_AIR_DISPATCHER:Evaluate_CAS( DetectedItem )
|
||
self:F( { DetectedItem.ItemID } )
|
||
|
||
local AttackerSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||
local AttackerCount = AttackerSet:Count()
|
||
local AttackerRadarCount = AttackerSet:HasSEAD()
|
||
local IsFriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem, Unit.Category.GROUND_UNIT )
|
||
local IsCas = ( AttackerRadarCount == 0 ) and ( IsFriendliesNearBy == true ) -- Is the AttackerSet a CAS group?
|
||
|
||
if IsCas == true then
|
||
|
||
-- First, count the active defenders, engaging the DetectedItem.
|
||
local DefendersTotal, DefendersEngaged, DefendersMissing = self:CountDefendersEngaged( DetectedItem, AttackerCount )
|
||
|
||
self:F( { AttackerCount = AttackerCount, DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } )
|
||
|
||
local DefenderGroups = self:CountDefenders( DetectedItem, DefendersEngaged, "CAS" )
|
||
|
||
if DetectedItem.IsDetected == true then
|
||
|
||
return DefendersTotal, DefendersEngaged, DefendersMissing, DefenderGroups
|
||
end
|
||
end
|
||
|
||
return nil, nil, nil
|
||
end
|
||
|
||
|
||
--- Evaluates an BAI task.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem The detected item.
|
||
-- @return Core.Set#SET_UNIT The set of units of the targets to be engaged.
|
||
-- @return #nil If there are no targets to be set.
|
||
function AI_AIR_DISPATCHER:Evaluate_BAI( DetectedItem )
|
||
self:F( { DetectedItem.ItemID } )
|
||
|
||
local AttackerSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||
local AttackerCount = AttackerSet:Count()
|
||
local AttackerRadarCount = AttackerSet:HasSEAD()
|
||
local IsFriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem, Unit.Category.GROUND_UNIT )
|
||
local IsBai = ( AttackerRadarCount == 0 ) and ( IsFriendliesNearBy == false ) -- Is the AttackerSet a BAI group?
|
||
|
||
if IsBai == true then
|
||
|
||
-- First, count the active defenders, engaging the DetectedItem.
|
||
local DefendersTotal, DefendersEngaged, DefendersMissing = self:CountDefendersEngaged( DetectedItem, AttackerCount )
|
||
|
||
self:F( { AttackerCount = AttackerCount, DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } )
|
||
|
||
local DefenderGroups = self:CountDefenders( DetectedItem, DefendersEngaged, "BAI" )
|
||
|
||
if DetectedItem.IsDetected == true then
|
||
|
||
return DefendersTotal, DefendersEngaged, DefendersMissing, DefenderGroups
|
||
end
|
||
end
|
||
|
||
return nil, nil, nil
|
||
end
|
||
|
||
|
||
|
||
|
||
--- Assigns A2G AI Tasks in relation to the detected items.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object.
|
||
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
|
||
function AI_AIR_DISPATCHER:ProcessDetected( Detection )
|
||
|
||
local AreaMsg = {}
|
||
local TaskMsg = {}
|
||
local ChangeMsg = {}
|
||
|
||
local TaskReport = REPORT:New()
|
||
|
||
|
||
for DefenderGroup, DefenderTask in pairs( self:GetDefenderTasks() ) do
|
||
local DefenderGroup = DefenderGroup -- Wrapper.Group#GROUP
|
||
local DefenderTaskFsm = self:GetDefenderTaskFsm( DefenderGroup )
|
||
--if DefenderTaskFsm:Is( "LostControl" ) then
|
||
-- self:ClearDefenderTask( DefenderGroup )
|
||
--end
|
||
if not DefenderGroup:IsAlive() then
|
||
self:F( { Defender = DefenderGroup:GetName(), DefenderState = DefenderTaskFsm:GetState() } )
|
||
if not DefenderTaskFsm:Is( "Started" ) then
|
||
self:ClearDefenderTask( DefenderGroup )
|
||
end
|
||
else
|
||
if DefenderTask.Target then
|
||
local AttackerItem = Detection:GetDetectedItemByIndex( DefenderTask.Target.Index )
|
||
if not AttackerItem then
|
||
self:F( { "Removing obsolete Target:", DefenderTask.Target.Index } )
|
||
self:ClearDefenderTaskTarget( DefenderGroup )
|
||
else
|
||
if DefenderTask.Target.Set then
|
||
local TargetCount = DefenderTask.Target.Set:Count()
|
||
if TargetCount == 0 then
|
||
self:F( { "All Targets destroyed in Target, removing:", DefenderTask.Target.Index } )
|
||
self:ClearDefenderTask( DefenderGroup )
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
local Report = REPORT:New( "\nTactical Overview" )
|
||
|
||
local DefenderGroupCount = 0
|
||
|
||
local DefendersTotal = 0
|
||
|
||
-- Now that all obsolete tasks are removed, loop through the detected targets.
|
||
for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
|
||
|
||
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
||
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||
local DetectedCount = DetectedSet:Count()
|
||
local DetectedZone = DetectedItem.Zone
|
||
|
||
self:F( { "Target ID", DetectedItem.ItemID } )
|
||
DetectedSet:Flush( self )
|
||
|
||
local DetectedID = DetectedItem.ID
|
||
local DetectionIndex = DetectedItem.Index
|
||
local DetectedItemChanged = DetectedItem.Changed
|
||
|
||
local AttackCoordinate = self.Detection:GetDetectedItemCoordinate( DetectedItem )
|
||
|
||
-- Calculate if for this DetectedItem if a defense needs to be initiated.
|
||
-- This calculation is based on the distance between the defense point and the attackers, and the defensiveness parameter.
|
||
-- The attackers closest to the defense coordinates will be handled first, or course!
|
||
|
||
local EngageCoordinate = nil
|
||
|
||
for DefenseCoordinateName, DefenseCoordinate in pairs( self.DefenseCoordinates ) do
|
||
local DefenseCoordinate = DefenseCoordinate -- Core.Point#COORDINATE
|
||
|
||
local EvaluateDistance = AttackCoordinate:Get2DDistance( DefenseCoordinate )
|
||
|
||
if EvaluateDistance <= self.DefenseRadius then
|
||
|
||
local DistanceProbability = ( self.DefenseRadius / EvaluateDistance * self.DefenseReactivity )
|
||
local DefenseProbability = math.random()
|
||
|
||
self:F( { DistanceProbability = DistanceProbability, DefenseProbability = DefenseProbability } )
|
||
|
||
if DefenseProbability <= DistanceProbability / ( 300 / 30 ) then
|
||
EngageCoordinate = DefenseCoordinate
|
||
break
|
||
end
|
||
end
|
||
end
|
||
|
||
if EngageCoordinate then
|
||
do
|
||
local DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies = self:Evaluate_SEAD( DetectedItem ) -- Returns a SET_UNIT with the SEAD targets to be engaged...
|
||
if DefendersMissing and DefendersMissing > 0 then
|
||
self:F( { DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } )
|
||
self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "SEAD", EngageCoordinate )
|
||
end
|
||
end
|
||
|
||
do
|
||
local DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies = self:Evaluate_CAS( DetectedItem ) -- Returns a SET_UNIT with the CAS targets to be engaged...
|
||
if DefendersMissing and DefendersMissing > 0 then
|
||
self:F( { DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } )
|
||
self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "CAS", EngageCoordinate )
|
||
end
|
||
end
|
||
|
||
do
|
||
local DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies = self:Evaluate_BAI( DetectedItem ) -- Returns a SET_UNIT with the CAS targets to be engaged...
|
||
if DefendersMissing and DefendersMissing > 0 then
|
||
self:F( { DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } )
|
||
self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "BAI", EngageCoordinate )
|
||
end
|
||
end
|
||
end
|
||
|
||
-- do
|
||
-- local DefendersMissing, Friendlies = self:Evaluate_CAS( DetectedItem )
|
||
-- if DefendersMissing and DefendersMissing > 0 then
|
||
-- self:F( { DefendersMissing = DefendersMissing } )
|
||
-- self:CAS( DetectedItem, DefendersMissing, Friendlies )
|
||
-- end
|
||
-- end
|
||
|
||
if self.TacticalDisplay then
|
||
-- Show tactical situation
|
||
local ThreatLevel = DetectedItem.Set:CalculateThreatLevelA2G()
|
||
Report:Add( string.format( " - %1s%s ( %4s ): ( #%d - %4s ) %s" , ( DetectedItem.IsDetected == true ) and "!" or " ", DetectedItem.ItemID, DetectedItem.Index, DetectedItem.Set:Count(), DetectedItem.Type or " --- ", string.rep( "■", ThreatLevel ) ) )
|
||
for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do
|
||
local Defender = Defender -- Wrapper.Group#GROUP
|
||
if DefenderTask.Target and DefenderTask.Target.Index == DetectedItem.Index then
|
||
if Defender:IsAlive() then
|
||
DefenderGroupCount = DefenderGroupCount + 1
|
||
local Fuel = Defender:GetFuelMin() * 100
|
||
local Damage = Defender:GetLife() / Defender:GetLife0() * 100
|
||
Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s",
|
||
Defender:GetName(),
|
||
DefenderTask.Type,
|
||
DefenderTask.Fsm:GetState(),
|
||
Defender:GetSize(),
|
||
Fuel,
|
||
Damage,
|
||
Defender:HasTask() == true and "Executing" or "Idle" ) )
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
if self.TacticalDisplay then
|
||
Report:Add( "\n - No Targets:")
|
||
local TaskCount = 0
|
||
for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do
|
||
TaskCount = TaskCount + 1
|
||
local Defender = Defender -- Wrapper.Group#GROUP
|
||
if not DefenderTask.Target then
|
||
if Defender:IsAlive() then
|
||
local DefenderHasTask = Defender:HasTask()
|
||
local Fuel = Defender:GetFuelMin() * 100
|
||
local Damage = Defender:GetLife() / Defender:GetLife0() * 100
|
||
DefenderGroupCount = DefenderGroupCount + 1
|
||
Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s",
|
||
Defender:GetName(),
|
||
DefenderTask.Type,
|
||
DefenderTask.Fsm:GetState(),
|
||
Defender:GetSize(),
|
||
Fuel,
|
||
Damage,
|
||
Defender:HasTask() == true and "Executing" or "Idle" ) )
|
||
end
|
||
end
|
||
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_AIR_DISPATCHER.DefenseQueueItem
|
||
Report:Add( string.format( " - %s - %s", DefenseQueueItem.SquadronName, DefenseQueueItem.DefenderSquadron.TakeoffTime, DefenseQueueItem.DefenderSquadron.TakeoffInterval) )
|
||
|
||
end
|
||
|
||
Report:Add( string.format( "\n - Squadron Resources: ", #self.DefenseQueue ) )
|
||
for DefenderSquadronName, DefenderSquadron in pairs( self.DefenderSquadrons ) do
|
||
Report:Add( string.format( " - %s - %d", DefenderSquadronName, DefenderSquadron.ResourceCount and DefenderSquadron.ResourceCount or "n/a" ) )
|
||
end
|
||
|
||
self:F( Report:Text( "\n" ) )
|
||
trigger.action.outText( Report:Text( "\n" ), 25 )
|
||
end
|
||
|
||
return true
|
||
end
|
||
|
||
end
|
||
|
||
do
|
||
|
||
--- Calculates which HUMAN friendlies are nearby the area.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param DetectedItem The detected item.
|
||
-- @return #number, Core.Report#REPORT The amount of friendlies and a text string explaining which friendlies of which type.
|
||
function AI_AIR_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem )
|
||
|
||
local DetectedSet = DetectedItem.Set
|
||
local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem )
|
||
|
||
local PlayerTypes = {}
|
||
local PlayersCount = 0
|
||
|
||
if PlayersNearBy then
|
||
local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G()
|
||
for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do
|
||
local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT
|
||
local PlayerName = PlayerUnit:GetPlayerName()
|
||
--self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } )
|
||
if PlayerUnit:IsAirPlane() and PlayerName ~= nil then
|
||
local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel()
|
||
PlayersCount = PlayersCount + 1
|
||
local PlayerType = PlayerUnit:GetTypeName()
|
||
PlayerTypes[PlayerName] = PlayerType
|
||
if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then
|
||
end
|
||
end
|
||
end
|
||
|
||
end
|
||
|
||
--self:F( { PlayersCount = PlayersCount } )
|
||
|
||
local PlayerTypesReport = REPORT:New()
|
||
|
||
if PlayersCount > 0 then
|
||
for PlayerName, PlayerType in pairs( PlayerTypes ) do
|
||
PlayerTypesReport:Add( string.format('"%s" in %s', PlayerName, PlayerType ) )
|
||
end
|
||
else
|
||
PlayerTypesReport:Add( "-" )
|
||
end
|
||
|
||
|
||
return PlayersCount, PlayerTypesReport
|
||
end
|
||
|
||
--- Calculates which friendlies are nearby the area.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param DetectedItem The detected item.
|
||
-- @return #number, Core.Report#REPORT The amount of friendlies and a text string explaining which friendlies of which type.
|
||
function AI_AIR_DISPATCHER:GetFriendliesNearBy( DetectedItem )
|
||
|
||
local DetectedSet = DetectedItem.Set
|
||
local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem )
|
||
|
||
local FriendlyTypes = {}
|
||
local FriendliesCount = 0
|
||
|
||
if FriendlyUnitsNearBy then
|
||
local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G()
|
||
for FriendlyUnitName, FriendlyUnitData in pairs( FriendlyUnitsNearBy ) do
|
||
local FriendlyUnit = FriendlyUnitData -- Wrapper.Unit#UNIT
|
||
if FriendlyUnit:IsAirPlane() then
|
||
local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel()
|
||
FriendliesCount = FriendliesCount + 1
|
||
local FriendlyType = FriendlyUnit:GetTypeName()
|
||
FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and ( FriendlyTypes[FriendlyType] + 1 ) or 1
|
||
if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then
|
||
end
|
||
end
|
||
end
|
||
|
||
end
|
||
|
||
--self:F( { FriendliesCount = FriendliesCount } )
|
||
|
||
local FriendlyTypesReport = REPORT:New()
|
||
|
||
if FriendliesCount > 0 then
|
||
for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do
|
||
FriendlyTypesReport:Add( string.format("%d of %s", FriendlyTypeCount, FriendlyType ) )
|
||
end
|
||
else
|
||
FriendlyTypesReport:Add( "-" )
|
||
end
|
||
|
||
|
||
return FriendliesCount, FriendlyTypesReport
|
||
end
|
||
|
||
--- Schedules a new Patrol for the given SquadronName.
|
||
-- @param #AI_AIR_DISPATCHER self
|
||
-- @param #string SquadronName The squadron name.
|
||
function AI_AIR_DISPATCHER:SchedulerPatrol( SquadronName )
|
||
local PatrolTaskTypes = { "SEAD", "CAS", "BAI" }
|
||
local PatrolTaskType = PatrolTaskTypes[math.random(1,3)]
|
||
self:Patrol( SquadronName, PatrolTaskType )
|
||
end
|
||
|
||
|
||
|
||
|
||
end
|
||
|
||
|