From 0b7ac754c521e720396ebe49909ba6f5ec8c8130 Mon Sep 17 00:00:00 2001 From: zlinman <63908222+zlinman@users.noreply.github.com> Date: Mon, 4 May 2020 12:53:09 +0200 Subject: [PATCH] only type error corrected, spaces deleted --- Moose Development/Moose/AI/AI_A2A_Cap.lua | 81 +- .../Moose/AI/AI_A2A_Dispatcher.lua | 2479 ++++++++--------- Moose Development/Moose/AI/AI_A2A_Gci.lua | 76 +- .../Moose/Actions/Act_Account.lua | 132 +- .../Moose/Actions/Act_Assign.lua | 142 +- .../Moose/Actions/Act_Assist.lua | 110 +- Moose Development/Moose/Actions/Act_Route.lua | 196 +- 7 files changed, 1605 insertions(+), 1611 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index a5bee30e4..08062cc11 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -1,10 +1,10 @@ --- **AI** -- (R2.2) - Models the process of Combat Air Patrol (CAP) for airplanes. -- -- === --- +-- -- ### Author: **FlightControl** --- --- === +-- +-- === -- -- @module AI.AI_A2A_Cap -- @image AI_Combat_Air_Patrol.JPG @@ -14,54 +14,54 @@ -- @extends AI.AI_Air_Engage#AI_AIR_ENGAGE ---- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group} +--- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. --- +-- -- ![Process](..\Presentations\AI_CAP\Dia3.JPG) --- +-- -- The AI_A2A_CAP is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_CAP process can be started using the **Start** event. --- +-- -- ![Process](..\Presentations\AI_CAP\Dia4.JPG) --- +-- -- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. -- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. --- +-- -- ![Process](..\Presentations\AI_CAP\Dia5.JPG) --- +-- -- This cycle will continue. --- +-- -- ![Process](..\Presentations\AI_CAP\Dia6.JPG) --- +-- -- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. -- -- ![Process](..\Presentations\AI_CAP\Dia9.JPG) --- +-- -- When enemies are detected, the AI will automatically engage the enemy. --- +-- -- ![Process](..\Presentations\AI_CAP\Dia10.JPG) --- +-- -- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. --- +-- -- ![Process](..\Presentations\AI_CAP\Dia13.JPG) --- +-- -- ## 1. AI_A2A_CAP constructor --- +-- -- * @{#AI_A2A_CAP.New}(): Creates a new AI_A2A_CAP object. --- +-- -- ## 2. AI_A2A_CAP is a FSM --- +-- -- ![Process](..\Presentations\AI_CAP\Dia2.JPG) --- +-- -- ### 2.1 AI_A2A_CAP States --- +-- -- * **None** ( Group ): The process is not started yet. -- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. -- * **Engaging** ( Group ): The AI is engaging the bogeys. -- * **Returning** ( Group ): The AI is returning to Base.. --- +-- -- ### 2.2 AI_A2A_CAP Events --- +-- -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. -- * **@{#AI_A2A_CAP.Engage}**: Let the AI engage the bogeys. @@ -74,25 +74,25 @@ -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- -- ## 3. Set the Range of Engagement --- +-- -- ![Range](..\Presentations\AI_CAP\Dia11.JPG) --- --- An optional range can be set in meters, +-- +-- An optional range can be set in meters, -- that will define when the AI will engage with the detected airborne enemy targets. -- The range can be beyond or smaller than the range of the Patrol Zone. -- The range is applied at the position of the AI. -- Use the method @{AI.AI_CAP#AI_A2A_CAP.SetEngageRange}() to define that range. -- -- ## 4. Set the Zone of Engagement --- +-- -- ![Zone](..\Presentations\AI_CAP\Dia12.JPG) --- --- An optional @{Zone} can be set, +-- +-- An optional @{Zone} can be set, -- that will define when the AI will engage with the detected airborne enemy targets. -- Use the method @{AI.AI_Cap#AI_A2A_CAP.SetEngageZone}() to define that Zone. --- +-- -- === --- +-- -- @field #AI_A2A_CAP AI_A2A_CAP = { ClassName = "AI_A2A_CAP", @@ -124,7 +124,7 @@ function AI_A2A_CAP:New2( AICap, EngageMinSpeed, EngageMaxSpeed, EngageFloorAlti self:SetFuelThreshold( .2, 60 ) self:SetDamageThreshold( 0.4 ) self:SetDisengageRadius( 70000 ) - + return self end @@ -160,35 +160,35 @@ function AI_A2A_CAP:onafterStart( AICap, From, Event, To ) end ---- Set the Engage Zone which defines where the AI will engage bogies. +--- Set the Engage Zone which defines where the AI will engage bogies. -- @param #AI_A2A_CAP self -- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP. -- @return #AI_A2A_CAP self function AI_A2A_CAP:SetEngageZone( EngageZone ) self:F2() - if EngageZone then + if EngageZone then self.EngageZone = EngageZone else self.EngageZone = nil end end ---- Set the Engage Range when the AI will engage with airborne enemies. +--- Set the Engage Range when the AI will engage with airborne enemies. -- @param #AI_A2A_CAP self -- @param #number EngageRange The Engage Range. -- @return #AI_A2A_CAP self function AI_A2A_CAP:SetEngageRange( EngageRange ) self:F2() - if EngageRange then + if EngageRange then self.EngageRange = EngageRange else self.EngageRange = nil end end ---- Evaluate the attack and create an AttackUnitTask list. +--- Evaluate the attack and create an AttackUnitTask list. -- @param #AI_A2A_CAP self -- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack. -- @param Wrappper.Group#GROUP DefenderGroup The group of defenders. @@ -202,12 +202,11 @@ function AI_A2A_CAP:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageA local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT if AttackUnit and AttackUnit:IsAlive() and AttackUnit:IsAir() then -- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition() - -- Maybe the detected set also contains + -- Maybe the detected set also contains self:T( { "Attacking Task:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } ) AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit ) end end - + return AttackUnitTasks end - diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 0e13f7c2c..cdf8d52ad 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -1,9 +1,9 @@ --- **AI** - (R2.2) - Manages the process of an automatic A2A defense system based on an EWR network targets and coordinating CAP and GCI. --- +-- -- === --- +-- -- Features: --- +-- -- * Setup quickly an A2A defense system for a coalition. -- * Setup (CAP) Control Air Patrols at defined zones to enhance your A2A defenses. -- * Setup (GCI) Ground Control Intercept at defined airbases to enhance your A2A defenses. @@ -18,170 +18,170 @@ -- * Setup specific settings for specific squadrons. -- * Quickly setup an A2A defense system using @{#AI_A2A_GCICAP}. -- * Setup a more advanced defense system using @{#AI_A2A_DISPATCHER}. --- +-- -- === --- +-- -- ## Missions: --- +-- -- [AID-A2A - AI A2A Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching) --- +-- -- === --- +-- -- ## YouTube Channel: --- +-- -- [DCS WORLD - MOOSE - A2A GCICAP - Build an automatic A2A Defense System](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0S4KMNUUJpaUs6zZHjLKNx) --- +-- -- === --- +-- -- # QUICK START GUIDE --- +-- -- There are basically two classes available to model an A2A defense system. --- +-- -- AI\_A2A\_DISPATCHER is the main A2A defense class that models the A2A defense system. -- AI\_A2A\_GCICAP derives or inherits from AI\_A2A\_DISPATCHER and is a more **noob** user friendly class, but is less flexible. --- +-- -- Before you start using the AI\_A2A\_DISPATCHER or AI\_A2A\_GCICAP ask youself the following questions. --- +-- -- ## 0. Do I need AI\_A2A\_DISPATCHER or do I need AI\_A2A\_GCICAP? --- +-- -- AI\_A2A\_GCICAP, automates a lot of the below questions using the mission editor and requires minimal lua scripting. -- But the AI\_A2A\_GCICAP provides less flexibility and a lot of options are defaulted. -- With AI\_A2A\_DISPATCHER you can setup a much more **fine grained** A2A defense mechanism, but some more (easy) lua scripting is required. --- +-- -- ## 1. Which Coalition am I modeling an A2A defense system for? blue or red? --- +-- -- One AI\_A2A\_DISPATCHER object can create a defense system for **one coalition**, which is blue or red. -- If you want to create a **mutual defense system**, for both blue and red, then you need to create **two** AI\_A2A\_DISPATCHER **objects**, -- each governing their defense system. --- --- +-- +-- -- ## 2. Which type of EWR will I setup? Grouping based per AREA, per TYPE or per UNIT? (Later others will follow). --- +-- -- The MOOSE framework leverages the @{Detection} classes to perform the EWR detection. -- Several types of @{Detection} classes exist, and the most common characteristics of these classes is that they: --- +-- -- * Perform detections from multiple FACs as one co-operating entity. -- * Communicate with a Head Quarters, which consolidates each detection. -- * Groups detections based on a method (per area, per type or per unit). -- * Communicates detections. --- +-- -- ## 3. Which EWR units will be used as part of the detection system? Only Ground or also Airborne? --- --- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. +-- +-- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. -- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). -- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. --- The position of these units is very important as they need to provide enough coverage +-- The position of these units is very important as they need to provide enough coverage -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. --- +-- -- ## 4. Is a border required? --- --- Is this a cold car or a hot war situation? In case of a cold war situation, a border can be set that will only trigger defenses +-- +-- Is this a cold war or a hot war situation? In case of a cold war situation, a border can be set that will only trigger defenses -- if the border is crossed by enemy units. --- +-- -- ## 5. What maximum range needs to be checked to allow defenses to engage any attacker? --- +-- -- A good functioning defense will have a "maximum range" evaluated to the enemy when CAP will be engaged or GCI will be spawned. --- +-- -- ## 6. Which Airbases, Carrier Ships, Farps will take part in the defense system for the Coalition? --- +-- -- Carefully plan which airbases will take part in the coalition. Color each airbase in the color of the coalition. --- +-- -- ## 7. Which Squadrons will I create and which name will I give each Squadron? --- +-- -- The defense system works with Squadrons. Each Squadron must be given a unique name, that forms the **key** to the defense system. -- Several options and activities can be set per Squadron. --- +-- -- ## 8. Where will the Squadrons be located? On Airbases? On Carrier Ships? On Farps? --- +-- -- Squadrons are placed as the "home base" on an airfield, carrier or farp. -- Carefully plan where each Squadron will be located as part of the defense system. --- +-- -- ## 9. Which plane models will I assign for each Squadron? Do I need one plane model or more plane models per squadron? --- +-- -- Per Squadron, one or multiple plane models can be allocated as **Templates**. -- These are late activated groups with one airplane or helicopter that start with a specific name, called the **template prefix**. -- The A2A defense system will select from the given templates a random template to spawn a new plane (group). --- +-- -- ## 10. Which payloads, skills and skins will these plane models have? --- --- Per Squadron, even if you have one plane model, you can still allocate multiple templates of one plane model, --- each having different payloads, skills and skins. +-- +-- Per Squadron, even if you have one plane model, you can still allocate multiple templates of one plane model, +-- each having different payloads, skills and skins. -- The A2A defense system will select from the given templates a random template to spawn a new plane (group). --- +-- -- ## 11. For each Squadron, which will perform CAP? --- +-- -- Per Squadron, evaluate which Squadrons will perform CAP. -- Not all Squadrons need to perform CAP. --- +-- -- ## 12. For each Squadron doing CAP, in which ZONE(s) will the CAP be performed? --- +-- -- Per CAP, evaluate **where** the CAP will be performed, in other words, define the **zone**. -- Near the border or a bit further away? --- +-- -- ## 13. For each Squadron doing CAP, which zone types will I create? --- +-- -- Per CAP zone, evaluate whether you want: --- +-- -- * simple trigger zones -- * polygon zones -- * moving zones --- +-- -- Depending on the type of zone selected, a different @{Zone} object needs to be created from a ZONE_ class. --- +-- -- ## 14. For each Squadron doing CAP, what are the time intervals and CAP amounts to be performed? --- +-- -- For each CAP: --- +-- -- * **How many** CAP you want to have airborne at the same time? -- * **How frequent** you want the defense mechanism to check whether to start a new CAP? --- +-- -- ## 15. For each Squadron, which will perform GCI? --- +-- -- For each Squadron, evaluate which Squadrons will perform GCI? -- Not all Squadrons need to perform GCI. --- +-- -- ## 16. For each Squadron, which takeoff method will I use? --- +-- -- For each Squadron, evaluate which takeoff method will be used: --- +-- -- * Straight from the air -- * From the runway -- * From a parking spot with running engines -- * From a parking spot with cold engines --- +-- -- **The default takeoff method is staight in the air.** --- +-- -- ## 17. For each Squadron, which landing method will I use? --- +-- -- For each Squadron, evaluate which landing method will be used: --- +-- -- * Despawn near the airbase when returning -- * Despawn after landing on the runway -- * Despawn after engine shutdown after landing --- +-- -- **The default landing method is despawn when near the airbase when returning.** --- +-- -- ## 18. For each Squadron, which overhead will I use? --- +-- -- For each Squadron, depending on the airplane type (modern, old) and payload, which overhead is required to provide any defense? -- In other words, if **X** attacker airplanes are detected, how many **Y** defense airplanes need to be spawned per squadron? -- The **Y** is dependent on the type of airplane (era), payload, fuel levels, skills etc. -- The overhead is a **factor** that will calculate dynamically how many **Y** defenses will be required based on **X** attackers detected. --- +-- -- **The default overhead is 1. A value greater than 1, like 1.5 will increase the overhead with 50%, a value smaller than 1, like 0.5 will decrease the overhead with 50%.** --- +-- -- ## 19. For each Squadron, which grouping will I use? --- +-- -- When multiple targets are detected, how will defense airplanes be grouped when multiple defense airplanes are spawned for multiple attackers? -- Per one, two, three, four? --- +-- -- **The default grouping is 1. That means, that each spawned defender will act individually.** --- +-- -- === --- +-- -- ### Authors: **FlightControl** rework of GCICAP + introduction of new concepts (squadrons). -- ### Authors: **Stonehouse**, **SNAFU** in terms of the advice, documentation, and the original GCICAP script. --- +-- -- @module AI.AI_A2A_Dispatcher -- @image AI_Air_To_Air_Dispatching.JPG @@ -193,657 +193,657 @@ do -- AI_A2A_DISPATCHER -- @type AI_A2A_DISPATCHER -- @extends Tasking.DetectionManager#DETECTION_MANAGER - --- Create an automatic air defence system for a coalition. - -- + --- Create an automatic air defence system for a coalition. + -- -- === - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia3.JPG) - -- - -- It includes automatic spawning of Combat Air Patrol aircraft (CAP) and Ground Controlled Intercept aircraft (GCI) in response to enemy air movements that are detected by a ground based radar network. + -- + -- It includes automatic spawning of Combat Air Patrol aircraft (CAP) and Ground Controlled Intercept aircraft (GCI) in response to enemy air movements that are detected by a ground based radar network. -- CAP flights will take off and proceed to designated CAP zones where they will remain on station until the ground radars direct them to intercept detected enemy aircraft or they run short of fuel and must return to base (RTB). When a CAP flight leaves their zone to perform an interception or return to base a new CAP flight will spawn to take their place. -- If all CAP flights are engaged or RTB then additional GCI interceptors will scramble to intercept unengaged enemy aircraft under ground radar control. - -- With a little time and with a little work it provides the mission designer with a convincing and completely automatic air defence system. + -- With a little time and with a little work it provides the mission designer with a convincing and completely automatic air defence system. -- In short it is a plug in very flexible and configurable air defence module for DCS World. - -- + -- -- Note that in order to create a two way A2A defense system, two AI\_A2A\_DISPATCHER defense system may need to be created, for each coalition one. -- This is a good implementation, because maybe in the future, more coalitions may become available in DCS world. - -- + -- -- === -- -- # USAGE GUIDE - -- + -- -- ## 1. AI\_A2A\_DISPATCHER constructor: - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_1.JPG) - -- - -- + -- + -- -- The @{#AI_A2A_DISPATCHER.New}() method creates a new AI\_A2A\_DISPATCHER instance. - -- + -- -- ### 1.1. Define the **EWR network**: - -- + -- -- As part of the AI\_A2A\_DISPATCHER :New() constructor, an EWR network must be given as the first parameter. -- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy. - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia5.JPG) - -- - -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. + -- + -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. -- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). -- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. - -- The position of these units is very important as they need to provide enough coverage + -- The position of these units is very important as they need to provide enough coverage -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia7.JPG) - -- - -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. - -- For example if they are a long way forward and can detect enemy planes on the ground and taking off - -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. - -- Having the radars further back will mean a slower escalation because fewer targets will be detected and - -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. - -- It all depends on what the desired effect is. - -- + -- + -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. + -- For example if they are a long way forward and can detect enemy planes on the ground and taking off + -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. + -- Having the radars further back will mean a slower escalation because fewer targets will be detected and + -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. + -- It all depends on what the desired effect is. + -- -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional.Detection#DETECTION_BASE} object that is given as the input parameter of the AI\_A2A\_DISPATCHER class. - -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, + -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, -- increasing or decreasing the radar coverage of the Early Warning System. - -- + -- -- See the following example to setup an EWR network containing EWR stations and AWACS. - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_2.JPG) -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_3.JPG) - -- + -- -- -- Define a SET_GROUP object that builds a collection of groups that define the EWR network. -- -- Here we build the network with all the groups that have a name starting with DF CCCP AWACS and DF CCCP EWR. -- DetectionSetGroup = SET_GROUP:New() -- DetectionSetGroup:FilterPrefixes( { "DF CCCP AWACS", "DF CCCP EWR" } ) -- DetectionSetGroup:FilterStart() - -- + -- -- -- Setup the detection and group targets to a 30km range! -- Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 ) -- -- -- Setup the A2A dispatcher, and initialize it. -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- -- The above example creates a SET_GROUP instance, and stores this in the variable (object) **DetectionSetGroup**. -- **DetectionSetGroup** is then being configured to filter all active groups with a group name starting with **DF CCCP AWACS** or **DF CCCP EWR** to be included in the Set. -- **DetectionSetGroup** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set. - -- + -- -- Then a new Detection object is created from the class DETECTION_AREAS. A grouping radius of 30000 is choosen, which is 30km. -- The **Detection** object is then passed to the @{#AI_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A defense detection mechanism. - -- + -- -- You could build a **mutual defense system** like this: - -- + -- -- A2ADispatcher_Red = AI_A2A_DISPATCHER:New( EWR_Red ) -- A2ADispatcher_Blue = AI_A2A_DISPATCHER:New( EWR_Blue ) - -- + -- -- ### 1.2. Define the detected **target grouping radius**: - -- + -- -- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed. -- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. - -- Fast planes like in the 80s, need a larger radius than WWII planes. + -- Fast planes like in the 80s, need a larger radius than WWII planes. -- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft. - -- + -- -- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate -- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small! - -- + -- -- ## 3. Set the **Engage Radius**: - -- - -- Define the **Engage Radius** to **engage any target by airborne friendlies**, + -- + -- Define the **Engage Radius** to **engage any target by airborne friendlies**, -- which are executing **cap** or **returning** from an intercept mission. - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia10.JPG) - -- - -- If there is a target area detected and reported, - -- then any friendlies that are airborne near this target area, + -- + -- If there is a target area detected and reported, + -- then any friendlies that are airborne near this target area, -- will be commanded to (re-)engage that target when available (if no other tasks were commanded). - -- - -- For example, if **50000** or **50km** is given as a value, then any friendly that is airborne within **50km** from the detected target, + -- + -- For example, if **50000** or **50km** is given as a value, then any friendly that is airborne within **50km** from the detected target, -- will be considered to receive the command to engage that target area. - -- + -- -- You need to evaluate the value of this parameter carefully: - -- + -- -- * If too small, more intercept missions may be triggered upon detected target areas. -- * If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. - -- - -- The **default** Engage Radius is defined as **100000** or **100km**. + -- + -- The **default** Engage Radius is defined as **100000** or **100km**. -- Use the method @{#AI_A2A_DISPATCHER.SetEngageRadius}() to set a specific Engage Radius. -- **The Engage Radius is defined for ALL squadrons which are operational.** - -- + -- -- Demonstration Mission: [AID-019 - AI_A2A - Engage Range Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-019%20-%20AI_A2A%20-%20Engage%20Range%20Test) - -- + -- -- In this example an Engage Radius is set to various values. - -- + -- -- -- Set 50km as the radius to engage any target by airborne friendlies. -- A2ADispatcher:SetEngageRadius( 50000 ) - -- + -- -- -- Set 100km as the radius to engage any target by airborne friendlies. -- A2ADispatcher:SetEngageRadius() -- 100000 is the default value. - -- - -- + -- + -- -- ## 4. Set the **Ground Controlled Intercept Radius** or **Gci radius**: - -- + -- -- When targets are detected that are still really far off, you don't want the AI_A2A_DISPATCHER to launch intercepts just yet. - -- You want it to wait until a certain Gci range is reached, which is the **distance of the closest airbase to target** + -- You want it to wait until a certain Gci range is reached, which is the **distance of the closest airbase to target** -- being **smaller** than the **Ground Controlled Intercept radius** or **Gci radius**. - -- - -- The **default** Gci radius is defined as **200000** or **200km**. Override the default Gci radius when the era of the warfare is early, or, + -- + -- The **default** Gci radius is defined as **200000** or **200km**. Override the default Gci radius when the era of the warfare is early, or, -- when you don't want to let the AI_A2A_DISPATCHER react immediately when a certain border or area is not being crossed. - -- + -- -- Use the method @{#AI_A2A_DISPATCHER.SetGciRadius}() to set a specific controlled ground intercept radius. -- **The Ground Controlled Intercept radius is defined for ALL squadrons which are operational.** - -- + -- -- Demonstration Mission: [AID-013 - AI_A2A - Intercept Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-013%20-%20AI_A2A%20-%20Intercept%20Test) - -- + -- -- In these examples, the Gci Radius is set to various values: - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- -- Set 100km as the radius to ground control intercept detected targets from the nearest airbase. -- A2ADispatcher:SetGciRadius( 100000 ) - -- + -- -- -- Set 200km as the radius to ground control intercept. -- A2ADispatcher:SetGciRadius() -- 200000 is the default value. - -- + -- -- ## 5. Set the **borders**: - -- - -- According to the tactical and strategic design of the mission broadly decide the shape and extent of red and blue territories. + -- + -- According to the tactical and strategic design of the mission broadly decide the shape and extent of red and blue territories. -- They should be laid out such that a border area is created between the two coalitions. - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia4.JPG) - -- + -- -- **Define a border area to simulate a cold war scenario.** -- Use the method @{#AI_A2A_DISPATCHER.SetBorderZone}() to create a border zone for the dispatcher. - -- + -- -- A **cold war** is one where CAP aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border. -- A **hot war** is one where CAP aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send CAP and GCI aircraft to attack it. - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia9.JPG) - -- + -- -- 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}. - -- 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. + -- 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. - -- + -- -- Demonstration Mission: [AID-009 - AI_A2A - Border Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-009 - AI_A2A - Border Test) - -- + -- -- In this example a border is set for the CCCP A2A dispatcher: - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_4.JPG) - -- + -- -- -- Setup the A2A dispatcher, and initialize it. -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- -- -- Setup the border. - -- -- Initialize the dispatcher, setting up a border zone. This is a polygon, + -- -- Initialize the dispatcher, setting up a border zone. This is a polygon, -- -- which takes the waypoints of a late activated group with the name CCCP Border as the boundaries of the border area. -- -- Any enemy crossing this border will be engaged. - -- + -- -- CCCPBorderZone = ZONE_POLYGON:New( "CCCP Border", GROUP:FindByName( "CCCP Border" ) ) -- A2ADispatcher:SetBorderZone( CCCPBorderZone ) - -- - -- ## 6. Squadrons: - -- + -- + -- ## 6. Squadrons: + -- -- The AI\_A2A\_DISPATCHER works with **Squadrons**, that need to be defined using the different methods available. - -- - -- Use the method @{#AI_A2A_DISPATCHER.SetSquadron}() to **setup a new squadron** active at an airfield, + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetSquadron}() to **setup a new squadron** active at an airfield, -- while defining which plane types are being used by the squadron and how many resources are available. - -- + -- -- Squadrons: - -- + -- -- * Have name (string) that is the identifier or key of the squadron. -- * Have specific plane types. -- * Are located at one airbase. -- * 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\_A2A\_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. - -- + -- -- This example defines a couple of squadrons. Note the templates defined within the Mission Editor. - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_5.JPG) -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_6.JPG) - -- + -- -- -- Setup the squadrons. -- A2ADispatcher:SetSquadron( "Mineralnye", AIRBASE.Caucasus.Mineralnye_Vody, { "SQ CCCP SU-27" }, 20 ) -- A2ADispatcher:SetSquadron( "Maykop", AIRBASE.Caucasus.Maykop_Khanskaya, { "SQ CCCP MIG-31" }, 20 ) -- A2ADispatcher:SetSquadron( "Mozdok", AIRBASE.Caucasus.Mozdok, { "SQ CCCP MIG-31" }, 20 ) -- A2ADispatcher:SetSquadron( "Sochi", AIRBASE.Caucasus.Sochi_Adler, { "SQ CCCP SU-27" }, 20 ) -- A2ADispatcher:SetSquadron( "Novo", AIRBASE.Caucasus.Novorossiysk, { "SQ CCCP SU-27" }, 20 ) - -- + -- -- ### 6.1. Set squadron take-off methods - -- + -- -- Use the various SetSquadronTakeoff... methods to control how squadrons are taking-off from the airfield: - -- + -- -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoff}() is the generic configuration method to control takeoff from the air, hot, cold or from the runway. See the method for further details. -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffInAir}() will spawn new aircraft from the squadron directly in the air. -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromParkingCold}() will spawn new aircraft in without running engines at a parking spot at the airfield. -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromParkingHot}() will spawn new aircraft in with running engines at a parking spot at the airfield. -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromRunway}() will spawn new aircraft at the runway at the airfield. - -- + -- -- **The default landing method is to spawn new aircraft directly in the air.** - -- + -- -- Use these methods to fine-tune for specific airfields that are known to create bottlenecks, or have reduced airbase efficiency. -- The more and the longer aircraft need to taxi at an airfield, the more risk there is that: - -- + -- -- * aircraft will stop waiting for each other or for a landing aircraft before takeoff. -- * aircraft may get into a "dead-lock" situation, where two aircraft are blocking each other. -- * aircraft may collide at the airbase. -- * aircraft may be awaiting the landing of a plane currently in the air, but never lands ... - -- + -- -- Currently within the DCS engine, the airfield traffic coordination is erroneous and contains a lot of bugs. -- If you experience while testing problems with aircraft take-off or landing, please use one of the above methods as a solution to workaround these issues! - -- + -- -- This example sets the default takeoff method to be from the runway. -- And for a couple of squadrons overrides this default method. - -- + -- -- -- Setup the Takeoff methods - -- + -- -- -- The default takeoff -- A2ADispatcher:SetDefaultTakeOffFromRunway() - -- + -- -- -- The individual takeoff per squadron -- A2ADispatcher:SetSquadronTakeoff( "Mineralnye", AI_A2A_DISPATCHER.Takeoff.Air ) -- A2ADispatcher:SetSquadronTakeoffInAir( "Sochi" ) -- A2ADispatcher:SetSquadronTakeoffFromRunway( "Mozdok" ) -- A2ADispatcher:SetSquadronTakeoffFromParkingCold( "Maykop" ) - -- A2ADispatcher:SetSquadronTakeoffFromParkingHot( "Novo" ) - -- - -- + -- A2ADispatcher:SetSquadronTakeoffFromParkingHot( "Novo" ) + -- + -- -- ### 6.1. Set Squadron takeoff altitude when spawning new aircraft in the air. - -- + -- -- In the case of the @{#AI_A2A_DISPATCHER.SetSquadronTakeoffInAir}() there is also an other parameter that can be applied. -- That is modifying or setting the **altitude** from where planes spawn in the air. -- Use the method @{#AI_A2A_DISPATCHER.SetSquadronTakeoffInAirAltitude}() to set the altitude for a specific squadron. -- The default takeoff altitude can be modified or set using the method @{#AI_A2A_DISPATCHER.SetSquadronTakeoffInAirAltitude}(). -- As part of the method @{#AI_A2A_DISPATCHER.SetSquadronTakeoffInAir}() a parameter can be specified to set the takeoff altitude. -- If this parameter is not specified, then the default altitude will be used for the squadron. - -- + -- -- ### 6.2. Set squadron landing methods - -- + -- -- In analogy with takeoff, the landing methods are to control how squadrons land at the airfield: - -- + -- -- * @{#AI_A2A_DISPATCHER.SetSquadronLanding}() is the generic configuration method to control landing, namely despawn the aircraft near the airfield in the air, right after landing, or at engine shutdown. -- * @{#AI_A2A_DISPATCHER.SetSquadronLandingNearAirbase}() will despawn the returning aircraft in the air when near the airfield. -- * @{#AI_A2A_DISPATCHER.SetSquadronLandingAtRunway}() will despawn the returning aircraft directly after landing at the runway. -- * @{#AI_A2A_DISPATCHER.SetSquadronLandingAtEngineShutdown}() will despawn the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines. - -- + -- -- You can use these methods to minimize the airbase coodination overhead and to increase the airbase efficiency. -- When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the -- A2A defense system, as no new CAP or GCI planes can takeoff. -- Note that the method @{#AI_A2A_DISPATCHER.SetSquadronLandingNearAirbase}() will only work for returning aircraft, not for damaged or out of fuel aircraft. -- Damaged or out-of-fuel aircraft are returning to the nearest friendly airbase and will land, and are out of control from ground control. - -- + -- -- This example defines the default landing method to be at the runway. -- And for a couple of squadrons overrides this default method. - -- + -- -- -- Setup the Landing methods - -- + -- -- -- The default landing method -- A2ADispatcher:SetDefaultLandingAtRunway() - -- + -- -- -- The individual landing per squadron -- A2ADispatcher:SetSquadronLandingAtRunway( "Mineralnye" ) -- A2ADispatcher:SetSquadronLandingNearAirbase( "Sochi" ) -- A2ADispatcher:SetSquadronLandingAtEngineShutdown( "Mozdok" ) -- A2ADispatcher:SetSquadronLandingNearAirbase( "Maykop" ) -- A2ADispatcher:SetSquadronLanding( "Novo", AI_A2A_DISPATCHER.Landing.AtRunway ) - -- - -- + -- + -- -- ### 6.3. Set squadron grouping - -- + -- -- Use the method @{#AI_A2A_DISPATCHER.SetSquadronGrouping}() to set the grouping of CAP or GCI flights that will take-off when spawned. - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia12.JPG) - -- + -- -- In the case of GCI, the @{#AI_A2A_DISPATCHER.SetSquadronGrouping}() method has additional behaviour. When there aren't enough CAP flights airborne, a GCI will be initiated for the remaining - -- targets to be engaged. Depending on the grouping parameter, the spawned flights for GCI are grouped into this setting. - -- For example with a group setting of 2, if 3 targets are detected and cannot be engaged by CAP or any airborne flight, + -- targets to be engaged. Depending on the grouping parameter, the spawned flights for GCI are grouped into this setting. + -- For example with a group setting of 2, if 3 targets are detected and cannot be engaged by CAP or any airborne flight, -- a GCI needs to be started, the GCI flights will be grouped as follows: Group 1 of 2 flights and Group 2 of one flight! - -- + -- -- Even more ... If one target has been detected, and the overhead is 1.5, grouping is 1, then two groups of planes will be spawned, with one unit each! - -- + -- -- The **grouping value is set for a Squadron**, and can be **dynamically adjusted** during mission execution, so to adjust the defense flights grouping when the tactical situation changes. - -- + -- -- ### 6.4. Overhead and Balance the effectiveness of the air defenses in case of GCI. - -- + -- -- The effectiveness can be set with the **overhead parameter**. This is a number that is used to calculate the amount of Units that dispatching command will allocate to GCI in surplus of detected amount of units. - -- The **default value** of the overhead parameter is 1.0, which means **equal balance**. - -- + -- The **default value** of the overhead parameter is 1.0, which means **equal balance**. + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia11.JPG) - -- + -- -- However, depending on the (type of) aircraft (strength and payload) in the squadron and the amount of resources available, this parameter can be changed. - -- + -- -- The @{#AI_A2A_DISPATCHER.SetSquadronOverhead}() method can be used to tweak the defense strength, - -- taking into account the plane types of the squadron. - -- + -- taking into account the plane types of the squadron. + -- -- For example, a MIG-31 with full long-distance A2A missiles payload, may still be less effective than a F-15C with short missiles... -- So in this case, one may want to use the @{#AI_A2A_DISPATCHER.SetOverhead}() 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: - -- + -- The overhead must be given as a decimal value with 1 as the neutral value, which means that overhead values: + -- -- * Higher than 1.0, for example 1.5, will increase the defense unit amounts. For 4 planes detected, 6 planes will be spawned. -- * Lower than 1, for example 0.75, will decrease the defense unit amounts. For 4 planes detected, only 3 planes will be spawned. - -- - -- 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 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. + -- -- For example ... If one target has been detected, and the overhead is 1.5, grouping is 1, then two groups of planes will be spawned, with one unit each! - -- + -- -- The **overhead value is set for a Squadron**, and can be **dynamically adjusted** during mission execution, so to adjust the defense overhead when the tactical situation changes. -- -- ## 6.5. Squadron fuel treshold. - -- + -- -- When an airplane gets **out of fuel** to a certain %-tage, which is by default **15% (0.15)**, there are two possible actions that can be taken: -- - The defender will go RTB, and will be replaced with a new defender if possible. -- - The defender will refuel at a tanker, if a tanker has been specified for the squadron. - -- + -- -- Use the method @{#AI_A2A_DISPATCHER.SetSquadronFuelThreshold}() to set the **squadron fuel treshold** of spawned airplanes for all squadrons. - -- + -- -- ## 7. Setup a squadron for CAP - -- + -- -- ### 7.1. Set the CAP zones - -- + -- -- CAP zones are patrol areas where Combat Air Patrol (CAP) flights loiter until they either return to base due to low fuel or are assigned an interception task by ground control. - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia6.JPG) - -- - -- * As the CAP flights wander around within the zone waiting to be tasked, these zones need to be large enough that the aircraft are not constantly turning + -- + -- * As the CAP flights wander around within the zone waiting to be tasked, these zones need to be large enough that the aircraft are not constantly turning -- but do not have to be big and numerous enough to completely cover a border. - -- + -- -- * CAP zones can be of any type, and are derived from the @{Core.Zone#ZONE_BASE} class. Zones can be @{Core.Zone#ZONE}, @{Core.Zone#ZONE_POLYGON}, @{Core.Zone#ZONE_UNIT}, @{Core.Zone#ZONE_GROUP}, etc. -- This allows to setup **static, moving and/or complex zones** wherein aircraft will perform the CAP. - -- - -- * Typically 20000-50000 metres width is used and they are spaced so that aircraft in the zone waiting for tasks don't have to far to travel to protect their coalitions important targets. - -- These targets are chosen as part of the mission design and might be an important airfield or town etc. - -- Zone size is also determined somewhat by territory size, plane types + -- + -- * Typically 20000-50000 metres width is used and they are spaced so that aircraft in the zone waiting for tasks don't have to far to travel to protect their coalitions important targets. + -- These targets are chosen as part of the mission design and might be an important airfield or town etc. + -- Zone size is also determined somewhat by territory size, plane types -- (eg WW2 aircraft might mean smaller zones or more zones because they are slower and take longer to intercept enemy aircraft). - -- - -- * In a **cold war** it is important to make sure a CAP zone doesn't intrude into enemy territory as otherwise CAP flights will likely cross borders + -- + -- * In a **cold war** it is important to make sure a CAP zone doesn't intrude into enemy territory as otherwise CAP flights will likely cross borders -- and spark a full scale conflict which will escalate rapidly. - -- - -- * CAP flights do not need to be in the CAP zone before they are "on station" and ready for tasking. - -- + -- + -- * CAP flights do not need to be in the CAP zone before they are "on station" and ready for tasking. + -- -- * Typically if a CAP flight is tasked and therefore leaves their zone empty while they go off and intercept their target another CAP flight will spawn to take their place. - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia7.JPG) - -- + -- -- The following example illustrates how CAP zones are coded: - -- - -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_8.JPG) - -- + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_8.JPG) + -- -- -- CAP Squadron execution. -- CAPZoneEast = ZONE_POLYGON:New( "CAP Zone East", GROUP:FindByName( "CAP Zone East" ) ) -- A2ADispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 ) -- A2ADispatcher:SetSquadronCapInterval( "Mineralnye", 2, 30, 60, 1 ) - -- - -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_7.JPG) - -- + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_7.JPG) + -- -- CAPZoneWest = ZONE_POLYGON:New( "CAP Zone West", GROUP:FindByName( "CAP Zone West" ) ) -- A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" ) -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) - -- - -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_9.JPG) - -- + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_9.JPG) + -- -- CAPZoneMiddle = ZONE:New( "CAP Zone Middle") -- A2ADispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" ) -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) - -- + -- -- Note the different @{Zone} MOOSE classes being used to create zones of different types. Please click the @{Zone} link for more information about the different zone types. -- Zones can be circles, can be setup in the mission editor using trigger zones, but can also be setup in the mission editor as polygons and in this case GROUP objects are being used! - -- + -- -- ## 7.2. Set the squadron to execute CAP: - -- + -- -- The method @{#AI_A2A_DISPATCHER.SetSquadronCap}() defines a CAP execution for a squadron. - -- + -- -- Setting-up a CAP zone also requires specific parameters: - -- + -- -- * The minimum and maximum altitude -- * The minimum speed and maximum patrol speed -- * The minimum and maximum engage speed -- * The type of altitude measurement - -- - -- These define how the squadron will perform the CAP while partrolling. Different terrain types requires different types of CAP. - -- + -- + -- These define how the squadron will perform the CAP while partrolling. Different terrain types requires different types of CAP. + -- -- The @{#AI_A2A_DISPATCHER.SetSquadronCapInterval}() method specifies **how much** and **when** CAP flights will takeoff. - -- - -- It is recommended not to overload the air defense with CAP flights, as these will decrease the performance of the overall system. - -- + -- + -- It is recommended not to overload the air defense with CAP flights, as these will decrease the performance of the overall system. + -- -- For example, the following setup will create a CAP for squadron "Sochi": - -- + -- -- A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" ) -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) - -- + -- -- ## 7.3. Squadron tanker to refuel when executing CAP and defender is out of fuel. - -- + -- -- Instead of sending CAP to RTB when out of fuel, you can let CAP refuel in mid air using a tanker. -- This greatly increases the efficiency of your CAP operations. - -- + -- -- In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected. -- Then, use the method @{#AI_A2A_DISPATCHER.SetDefaultTanker}() to set the default tanker for the refuelling. -- You can also specify a specific tanker for refuelling for a squadron by using the method @{#AI_A2A_DISPATCHER.SetSquadronTanker}(). - -- + -- -- When the tanker specified is alive and in the air, the tanker will be used for refuelling. - -- + -- -- For example, the following setup will create a CAP for squadron "Gelend" with a refuel task for the squadron: - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_10.JPG) - -- + -- -- -- Define the CAP -- A2ADispatcher:SetSquadron( "Gelend", AIRBASE.Caucasus.Gelendzhik, { "SQ CCCP SU-30" }, 20 ) -- A2ADispatcher:SetSquadronCap( "Gelend", ZONE:New( "PatrolZoneGelend" ), 4000, 8000, 600, 800, 1000, 1300 ) - -- A2ADispatcher:SetSquadronCapInterval( "Gelend", 2, 30, 600, 1 ) + -- A2ADispatcher:SetSquadronCapInterval( "Gelend", 2, 30, 600, 1 ) -- A2ADispatcher:SetSquadronGci( "Gelend", 900, 1200 ) - -- + -- -- -- Setup the Refuelling for squadron "Gelend", at tanker (group) "TankerGelend" when the fuel in the tank of the CAP defenders is less than 80%. -- A2ADispatcher:SetSquadronFuelThreshold( "Gelend", 0.8 ) -- A2ADispatcher:SetSquadronTanker( "Gelend", "TankerGelend" ) - -- + -- -- ## 7.4 Set up race track pattern - -- + -- -- By default, flights patrol randomly within the CAP zone. It is also possible to let them fly a race track pattern using the - -- @{#AI_A2A_DISPATCHER.SetDefaultCapRacetrack}(*LeglengthMin*, *LeglengthMax*, *HeadingMin*, *HeadingMax*, *DurationMin*, *DurationMax*) or + -- @{#AI_A2A_DISPATCHER.SetDefaultCapRacetrack}(*LeglengthMin*, *LeglengthMax*, *HeadingMin*, *HeadingMax*, *DurationMin*, *DurationMax*) or -- @{#AI_A2A_DISPATCHER.SetSquadronCapRacetrack}(*SquadronName*, *LeglengthMin*, *LeglengthMax*, *HeadingMin*, *HeadingMax*, *DurationMin*, *DurationMax*) functions. -- The first function enables this for all squadrons, the latter only for specific squadrons. For example, - -- + -- -- -- Enable race track pattern for CAP squadron "Mineralnye". -- A2ADispatcher:SetSquadronCapRacetrack("Mineralnye", 10000, 20000, 90, 180, 10*60, 20*60) - -- + -- -- In this case the squadron "Mineralnye" will a race track pattern at a random point in the CAP zone. The leg length will be randomly selected between 10,000 and 20,000 meters. The heading -- of the race track will randomly selected between 90 (West to East) and 180 (North to South) degrees. -- After a random duration between 10 and 20 minutes, the flight will get a new random orbit location. - -- - -- Note that all parameters except the squadron name are optional. If not specified, default values are taken. Speed and altitude are taken from the - -- + -- + -- Note that all parameters except the squadron name are optional. If not specified, default values are taken. Speed and altitude are taken from the + -- -- Also note that the center of the race track pattern is chosen randomly within the patrol zone and can be close the the boarder of the zone. Hence, it cannot be guaranteed that the -- whole pattern lies within the patrol zone. - -- + -- -- ## 8. Setup a squadron for GCI: - -- + -- -- The method @{#AI_A2A_DISPATCHER.SetSquadronGci}() defines a GCI execution for a squadron. - -- + -- -- Setting-up a GCI readiness also requires specific parameters: - -- + -- -- * The minimum speed and maximum patrol speed - -- + -- -- Essentially this controls how many flights of GCI aircraft can be active at any time. -- Note allowing large numbers of active GCI flights can adversely impact mission performance on low or medium specification hosts/servers. - -- GCI needs to be setup at strategic airbases. Too far will mean that the aircraft need to fly a long way to reach the intruders, + -- GCI needs to be setup at strategic airbases. Too far will mean that the aircraft need to fly a long way to reach the intruders, -- too short will mean that the intruders may have alraedy passed the ideal interception point! - -- + -- -- For example, the following setup will create a GCI for squadron "Sochi": - -- + -- -- A2ADispatcher:SetSquadronGci( "Mozdok", 900, 1200 ) - -- + -- -- ## 9. Other configuration options - -- + -- -- ### 9.1. Set a tactical display panel: - -- + -- -- Every 30 seconds, a tactical display panel can be shown that illustrates what the status is of the different groups controlled by AI\_A2A\_DISPATCHER. -- Use the method @{#AI_A2A_DISPATCHER.SetTacticalDisplay}() to switch on the tactical display panel. The default will not show this panel. -- Note that there may be some performance impact if this panel is shown. - -- + -- -- ## 10. Defaults settings. - -- + -- -- This provides a good overview of the different parameters that are setup or hardcoded by default. -- For some default settings, a method is available that allows you to tweak the defaults. - -- + -- -- ## 10.1. Default takeoff method. - -- + -- -- The default **takeoff method** is set to **in the air**, which means that new spawned airplanes will be spawned directly in the air above the airbase by default. - -- + -- -- **The default takeoff method can be set for ALL squadrons that don't have an individual takeoff method configured.** - -- + -- -- * @{#AI_A2A_DISPATCHER.SetDefaultTakeoff}() is the generic configuration method to control takeoff by default from the air, hot, cold or from the runway. See the method for further details. -- * @{#AI_A2A_DISPATCHER.SetDefaultTakeoffInAir}() will spawn by default new aircraft from the squadron directly in the air. -- * @{#AI_A2A_DISPATCHER.SetDefaultTakeoffFromParkingCold}() will spawn by default new aircraft in without running engines at a parking spot at the airfield. -- * @{#AI_A2A_DISPATCHER.SetDefaultTakeoffFromParkingHot}() will spawn by default new aircraft in with running engines at a parking spot at the airfield. -- * @{#AI_A2A_DISPATCHER.SetDefaultTakeoffFromRunway}() will spawn by default new aircraft at the runway at the airfield. - -- + -- -- ## 10.2. Default landing method. - -- + -- -- The default **landing method** is set to **near the airbase**, which means that returning airplanes will be despawned directly in the air by default. - -- + -- -- The default landing method can be set for ALL squadrons that don't have an individual landing method configured. - -- + -- -- * @{#AI_A2A_DISPATCHER.SetDefaultLanding}() is the generic configuration method to control by default landing, namely despawn the aircraft near the airfield in the air, right after landing, or at engine shutdown. -- * @{#AI_A2A_DISPATCHER.SetDefaultLandingNearAirbase}() will despawn by default the returning aircraft in the air when near the airfield. -- * @{#AI_A2A_DISPATCHER.SetDefaultLandingAtRunway}() will despawn by default the returning aircraft directly after landing at the runway. -- * @{#AI_A2A_DISPATCHER.SetDefaultLandingAtEngineShutdown}() will despawn by default the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines. - -- + -- -- ## 10.3. Default overhead. - -- + -- -- The default **overhead** is set to **1**. That essentially means that there isn't any overhead set by default. - -- + -- -- The default overhead value can be set for ALL squadrons that don't have an individual overhead value configured. -- -- Use the @{#AI_A2A_DISPATCHER.SetDefaultOverhead}() method can be used to set the default overhead or defense strength for ALL squadrons. -- -- ## 10.4. Default grouping. - -- + -- -- The default **grouping** is set to **one airplane**. That essentially means that there won't be any grouping applied by default. - -- + -- -- The default grouping value can be set for ALL squadrons that don't have an individual grouping value configured. - -- + -- -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultGrouping}() to set the **default grouping** of spawned airplanes for all squadrons. - -- + -- -- ## 10.5. Default RTB fuel treshold. - -- + -- -- When an airplane gets **out of fuel** to a certain %-tage, which is **15% (0.15)**, it will go RTB, and will be replaced with a new airplane when applicable. - -- + -- -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultFuelThreshold}() to set the **default fuel treshold** of spawned airplanes for all squadrons. - -- + -- -- ## 10.6. Default RTB damage treshold. - -- + -- -- When an airplane is **damaged** to a certain %-tage, which is **40% (0.40)**, it will go RTB, and will be replaced with a new airplane when applicable. - -- + -- -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultDamageThreshold}() to set the **default damage treshold** of spawned airplanes for all squadrons. - -- + -- -- ## 10.7. Default settings for CAP. - -- + -- -- ### 10.7.1. Default CAP Time Interval. - -- + -- -- CAP is time driven, and will evaluate in random time intervals if a new CAP needs to be spawned. -- The **default CAP time interval** is between **180** and **600** seconds. - -- - -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultCapTimeInterval}() to set the **default CAP time interval** of spawned airplanes for all squadrons. + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultCapTimeInterval}() to set the **default CAP time interval** of spawned airplanes for all squadrons. -- Note that you can still change the CAP limit and CAP time intervals for each CAP individually using the @{#AI_A2A_DISPATCHER.SetSquadronCapTimeInterval}() method. - -- + -- -- ### 10.7.2. Default CAP limit. - -- + -- -- Multiple CAP can be airborne at the same time for one squadron, which is controlled by the **CAP limit**. -- The **default CAP limit** is 1 CAP per squadron to be airborne at the same time. -- Note that the default CAP limit is used when a Squadron CAP is defined, and cannot be changed afterwards. -- So, ensure that you set the default CAP limit **before** you spawn the Squadron CAP. - -- - -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultCapTimeInterval}() to set the **default CAP time interval** of spawned airplanes for all squadrons. + -- + -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultCapTimeInterval}() to set the **default CAP time interval** of spawned airplanes for all squadrons. -- Note that you can still change the CAP limit and CAP time intervals for each CAP individually using the @{#AI_A2A_DISPATCHER.SetSquadronCapTimeInterval}() method. - -- + -- -- ## 10.7.3. Default tanker for refuelling when executing CAP. - -- + -- -- Instead of sending CAP to RTB when out of fuel, you can let CAP refuel in mid air using a tanker. -- This greatly increases the efficiency of your CAP operations. - -- + -- -- In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected. -- Then, use the method @{#AI_A2A_DISPATCHER.SetDefaultTanker}() to set the tanker for the dispatcher. -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultFuelThreshold}() to set the %-tage left in the defender airplane tanks when a refuel action is needed. - -- + -- -- When the tanker specified is alive and in the air, the tanker will be used for refuelling. - -- + -- -- For example, the following setup will set the default refuel tanker to "Tanker": - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_DISPATCHER-ME_11.JPG) - -- + -- -- -- Define the CAP -- A2ADispatcher:SetSquadron( "Sochi", AIRBASE.Caucasus.Sochi_Adler, { "SQ CCCP SU-34" }, 20 ) -- A2ADispatcher:SetSquadronCap( "Sochi", ZONE:New( "PatrolZone" ), 4000, 8000, 600, 800, 1000, 1300 ) - -- A2ADispatcher:SetSquadronCapInterval("Sochi", 2, 30, 600, 1 ) + -- A2ADispatcher:SetSquadronCapInterval("Sochi", 2, 30, 600, 1 ) -- A2ADispatcher:SetSquadronGci( "Sochi", 900, 1200 ) - -- + -- -- -- Set the default tanker for refuelling to "Tanker", when the default fuel treshold has reached 90% fuel left. -- A2ADispatcher:SetDefaultFuelThreshold( 0.9 ) -- A2ADispatcher:SetDefaultTanker( "Tanker" ) - -- + -- -- ## 10.8. Default settings for GCI. - -- + -- -- ## 10.8.1. Optimal intercept point calculation. - -- - -- When intruders are detected, the intrusion path of the attackers can be monitored by the EWR. + -- + -- When intruders are detected, the intrusion path of the attackers can be monitored by the EWR. -- Although defender planes might be on standby at the airbase, it can still take some time to get the defenses up in the air if there aren't any defenses airborne. -- This time can easily take 2 to 3 minutes, and even then the defenders still need to fly towards the target, which takes also time. - -- + -- -- Therefore, an optimal **intercept point** is calculated which takes a couple of parameters: - -- + -- -- * The average bearing of the intruders for an amount of seconds. -- * The average speed of the intruders for an amount of seconds. -- * An assumed time it takes to get planes operational at the airbase. - -- + -- -- The **intercept point** will determine: - -- + -- -- * If there are any friendlies close to engage the target. These can be defenders performing CAP or defenders in RTB. -- * The optimal airbase from where defenders will takeoff for GCI. - -- + -- -- Use the method @{#AI_A2A_DISPATCHER.SetIntercept}() to modify the assumed intercept delay time to calculate a valid interception. - -- + -- -- ## 10.8.2. Default Disengage Radius. - -- + -- -- The radius to **disengage any target** when the **distance** of the defender to the **home base** is larger than the specified meters. -- The default Disengage Radius is **300km** (300000 meters). Note that the Disengage Radius is applicable to ALL squadrons! - -- + -- -- Use the method @{#AI_A2A_DISPATCHER.SetDisengageRadius}() to modify the default Disengage Radius to another distance setting. - -- + -- -- ## 11. Airbase capture: - -- + -- -- Different squadrons can be located at one airbase. -- If the airbase gets captured, that is, when there is an enemy unit near the airbase, and there aren't anymore friendlies at the airbase, the airbase will change coalition ownership. -- As a result, the GCI and CAP will stop! -- However, the squadron will still stay alive. Any airplane that is airborne will continue its operations until all airborne airplanes -- of the squadron will be destroyed. This to keep consistency of air operations not to confuse the players. - -- + -- -- ## 12. Q & A: - -- + -- -- ### 12.1. Which countries will be selected for each coalition? - -- - -- Which countries are assigned to a coalition influences which units are available to the coalition. - -- For example because the mission calls for a EWR radar on the blue side the Ukraine might be chosen as a blue country - -- so that the 55G6 EWR radar unit is available to blue. - -- Some countries assign different tasking to aircraft, for example Germany assigns the CAP task to F-4E Phantoms but the USA does not. - -- Therefore if F4s are wanted as a coalition's CAP or GCI aircraft Germany will need to be assigned to that coalition. - -- + -- + -- Which countries are assigned to a coalition influences which units are available to the coalition. + -- For example because the mission calls for a EWR radar on the blue side the Ukraine might be chosen as a blue country + -- so that the 55G6 EWR radar unit is available to blue. + -- Some countries assign different tasking to aircraft, for example Germany assigns the CAP task to F-4E Phantoms but the USA does not. + -- Therefore if F4s are wanted as a coalition's CAP or GCI aircraft Germany will need to be assigned to that coalition. + -- -- ### 12.2. Country, type, load out, skill and skins for CAP and GCI aircraft? - -- + -- -- * Note these can be from any countries within the coalition but must be an aircraft with one of the main tasks being "CAP". -- * Obviously skins which are selected must be available to all players that join the mission otherwise they will see a default skin. - -- * Load outs should be appropriate to a CAP mission eg perhaps drop tanks for CAP flights and extra missiles for GCI flights. + -- * Load outs should be appropriate to a CAP mission eg perhaps drop tanks for CAP flights and extra missiles for GCI flights. -- * These decisions will eventually lead to template aircraft units being placed as late activation units that the script will use as templates for spawning CAP and GCI flights. Up to 4 different aircraft configurations can be chosen for each coalition. The spawned aircraft will inherit the characteristics of the template aircraft. - -- * The selected aircraft type must be able to perform the CAP tasking for the chosen country. - -- - -- + -- * The selected aircraft type must be able to perform the CAP tasking for the chosen country. + -- + -- -- @field #AI_A2A_DISPATCHER AI_A2A_DISPATCHER = { ClassName = "AI_A2A_DISPATCHER", @@ -882,10 +882,10 @@ do -- AI_A2A_DISPATCHER --- Enumerator for spawns at airbases -- @type AI_A2A_DISPATCHER.Takeoff -- @extends Wrapper.Group#GROUP.Takeoff - + --- @field #AI_A2A_DISPATCHER.Takeoff Takeoff AI_A2A_DISPATCHER.Takeoff = GROUP.Takeoff - + --- Defnes Landing location. -- @field Landing AI_A2A_DISPATCHER.Landing = { @@ -893,7 +893,7 @@ do -- AI_A2A_DISPATCHER AtRunway = 2, AtEngineShutdown = 3, } - + --- AI_A2A_DISPATCHER constructor. -- This is defining the A2A DISPATCHER for one coaliton. -- The Dispatcher works with a @{Functional.Detection#DETECTION_BASE} object that is taking of the detection of targets using the EWR units. @@ -902,33 +902,33 @@ do -- AI_A2A_DISPATCHER -- @param Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network. -- @return #AI_A2A_DISPATCHER self -- @usage - -- + -- -- -- Setup the Detection, using DETECTION_AREAS. -- -- First define the SET of GROUPs that are defining the EWR network. -- -- Here with prefixes DF CCCP AWACS, DF CCCP EWR. -- DetectionSetGroup = SET_GROUP:New() -- DetectionSetGroup:FilterPrefixes( { "DF CCCP AWACS", "DF CCCP EWR" } ) -- DetectionSetGroup:FilterStart() - -- + -- -- -- Define the DETECTION_AREAS, using the DetectionSetGroup, with a 30km grouping radius. -- Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 ) - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) -- - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) -- + -- function AI_A2A_DISPATCHER:New( Detection ) -- Inherits from DETECTION_MANAGER local self = BASE:Inherit( self, DETECTION_MANAGER:New( nil, Detection ) ) -- #AI_A2A_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. - + -- TODO: Check detection through radar. self.Detection:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) --self.Detection:InitDetectRadar( true ) @@ -938,7 +938,7 @@ do -- AI_A2A_DISPATCHER self:SetGciRadius() self:SetIntercept( 300 ) -- A default intercept delay time of 300 seconds. self:SetDisengageRadius( 300000 ) -- The default Disengage Radius is 300 km. - + self:SetDefaultTakeoff( AI_A2A_DISPATCHER.Takeoff.Air ) self:SetDefaultTakeoffInAirAltitude( 500 ) -- Default takeoff is 500 meters above the ground. self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.NearAirbase ) @@ -948,10 +948,10 @@ do -- AI_A2A_DISPATCHER self:SetDefaultDamageThreshold( 0.4 ) -- When 40% of damage, go RTB. self:SetDefaultCapTimeInterval( 180, 600 ) -- Between 180 and 600 seconds. self:SetDefaultCapLimit( 1 ) -- Maximum one CAP per squadron. - - + + self:AddTransition( "Started", "Assign", "Started" ) - + --- OnAfter Transition Handler for Event Assign. -- @function [parent=#AI_A2A_DISPATCHER] OnAfterAssign -- @param #AI_A2A_DISPATCHER self @@ -961,7 +961,7 @@ do -- AI_A2A_DISPATCHER -- @param Tasking.Task_A2A#AI_A2A Task -- @param Wrapper.Unit#UNIT TaskUnit -- @param #string PlayerName - + self:AddTransition( "*", "CAP", "*" ) --- CAP Handler OnBefore for AI_A2A_DISPATCHER @@ -971,23 +971,23 @@ do -- AI_A2A_DISPATCHER -- @param #string Event -- @param #string To -- @return #boolean - + --- CAP Handler OnAfter for AI_A2A_DISPATCHER -- @function [parent=#AI_A2A_DISPATCHER] OnAfterCAP -- @param #AI_A2A_DISPATCHER self -- @param #string From -- @param #string Event -- @param #string To - + --- CAP Trigger for AI_A2A_DISPATCHER -- @function [parent=#AI_A2A_DISPATCHER] CAP -- @param #AI_A2A_DISPATCHER self - + --- CAP Asynchronous Trigger for AI_A2A_DISPATCHER -- @function [parent=#AI_A2A_DISPATCHER] __CAP -- @param #AI_A2A_DISPATCHER self -- @param #number Delay - + self:AddTransition( "*", "GCI", "*" ) --- GCI Handler OnBefore for AI_A2A_DISPATCHER @@ -997,7 +997,7 @@ do -- AI_A2A_DISPATCHER -- @param #string Event -- @param #string To -- @return #boolean - + --- GCI Handler OnAfter for AI_A2A_DISPATCHER -- @function [parent=#AI_A2A_DISPATCHER] OnAfterGCI -- @param #AI_A2A_DISPATCHER self @@ -1007,14 +1007,14 @@ do -- AI_A2A_DISPATCHER -- @param Functional.Detection#DETECTION_BASE.DetectedItem AttackerDetection Detected item. -- @param #number DefendersMissing Number of missing defenders. -- @param #table DefenderFriendlies Friendly defenders. - + --- GCI Trigger for AI_A2A_DISPATCHER -- @function [parent=#AI_A2A_DISPATCHER] GCI -- @param #AI_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem AttackerDetection Detected item. -- @param #number DefendersMissing Number of missing defenders. -- @param #table DefenderFriendlies Friendly defenders. - + --- GCI Asynchronous Trigger for AI_A2A_DISPATCHER -- @function [parent=#AI_A2A_DISPATCHER] __GCI -- @param #AI_A2A_DISPATCHER self @@ -1022,9 +1022,9 @@ do -- AI_A2A_DISPATCHER -- @param Functional.Detection#DETECTION_BASE.DetectedItem AttackerDetection Detected item. -- @param #number DefendersMissing Number of missing defenders. -- @param #table DefenderFriendlies Friendly defenders. - + self:AddTransition( "*", "ENGAGE", "*" ) - + --- ENGAGE Handler OnBefore for AI_A2A_DISPATCHER -- @function [parent=#AI_A2A_DISPATCHER] OnBeforeENGAGE -- @param #AI_A2A_DISPATCHER self @@ -1034,7 +1034,7 @@ do -- AI_A2A_DISPATCHER -- @param Functional.Detection#DETECTION_BASE.DetectedItem AttackerDetection Detected item. -- @param #table Defenders Defenders table. -- @return #boolean - + --- ENGAGE Handler OnAfter for AI_A2A_DISPATCHER -- @function [parent=#AI_A2A_DISPATCHER] OnAfterENGAGE -- @param #AI_A2A_DISPATCHER self @@ -1049,38 +1049,38 @@ do -- AI_A2A_DISPATCHER -- @param #AI_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem AttackerDetection Detected item. -- @param #table Defenders Defenders table. - + --- ENGAGE Asynchronous Trigger for AI_A2A_DISPATCHER -- @function [parent=#AI_A2A_DISPATCHER] __ENGAGE -- @param #AI_A2A_DISPATCHER self -- @param #number Delay -- @param Functional.Detection#DETECTION_BASE.DetectedItem AttackerDetection Detected item. -- @param #table Defenders Defenders table. - - + + -- 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 intercept 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.DefenderCAPIndex = 0 - + self:__Start( 5 ) - + return self end @@ -1102,45 +1102,45 @@ do -- AI_A2A_DISPATCHER end end end - + --- Park defender. -- @param #AI_A2A_DISPATCHER self -- @param #AI_A2A_DISPATCHER.Squadron DefenderSquadron The squadron. function AI_A2A_DISPATCHER:ParkDefender( 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 - + local Grouping=DefenderSquadron.Grouping or self.DefenderDefault.Grouping - + Grouping=1 - + Spawn:InitGrouping(Grouping) - + 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 - + self.uncontrolled=self.uncontrolled or {} self.uncontrolled[DefenderSquadron.Name]=self.uncontrolled[DefenderSquadron.Name] or {} - + table.insert(self.uncontrolled[DefenderSquadron.Name], {group=SpawnGroup, name=GroupName, grouping=Grouping}) end - + end @@ -1150,9 +1150,9 @@ do -- AI_A2A_DISPATCHER function AI_A2A_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 @@ -1167,7 +1167,7 @@ do -- AI_A2A_DISPATCHER -- @param #AI_A2A_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function AI_A2A_DISPATCHER:OnEventCrashOrDead( EventData ) - self.Detection:ForgetDetectedUnit( EventData.IniUnitName ) + self.Detection:ForgetDetectedUnit( EventData.IniUnitName ) end --- Event land. @@ -1195,9 +1195,9 @@ do -- AI_A2A_DISPATCHER DefenderUnit:Destroy() return end - end + end end - + --- Event engine shutdown. -- @param #AI_A2A_DISPATCHER self -- @param Core.Event#EVENTDATA EventData @@ -1217,40 +1217,40 @@ do -- AI_A2A_DISPATCHER DefenderUnit:Destroy() self:ParkDefender( Squadron ) end - end + end end - + --- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission. - -- If there is a target area detected and reported, then any friendlies that are airborne near this target area, + -- If there is a target area detected and reported, then any friendlies that are airborne near this target area, -- will be commanded to (re-)engage that target when available (if no other tasks were commanded). - -- - -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, + -- + -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, -- will be considered to receive the command to engage that target area. - -- + -- -- You need to evaluate the value of this parameter carefully: - -- + -- -- * If too small, more intercept missions may be triggered upon detected target areas. -- * If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. - -- + -- -- **Use the method @{#AI_A2A_DISPATCHER.SetEngageRadius}() to modify the default Engage Radius for ALL squadrons.** - -- + -- -- Demonstration Mission: [AID-019 - AI_A2A - Engage Range Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-019%20-%20AI_A2A%20-%20Engage%20Range%20Test) - -- + -- -- @param #AI_A2A_DISPATCHER self -- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target. -- @return #AI_A2A_DISPATCHER -- @usage - -- + -- -- -- Set 50km as the radius to engage any target by airborne friendlies. -- A2ADispatcher:SetEngageRadius( 50000 ) - -- + -- -- -- Set 100km as the radius to engage any target by airborne friendlies. -- A2ADispatcher:SetEngageRadius() -- 100000 is the default value. - -- + -- function AI_A2A_DISPATCHER:SetEngageRadius( EngageRadius ) self.Detection:SetFriendliesRange( EngageRadius or 100000 ) - + return self end @@ -1259,57 +1259,57 @@ do -- AI_A2A_DISPATCHER -- @param #number DisengageRadius (Optional, Default = 300000) The radius in meters to disengage a target when too far from the home base. -- @return #AI_A2A_DISPATCHER -- @usage - -- + -- -- -- Set 50km as the Disengage Radius. -- A2ADispatcher:SetDisengageRadius( 50000 ) - -- + -- -- -- Set 100km as the Disengage Radius. -- A2ADispatcher:SetDisngageRadius() -- 300000 is the default value. - -- + -- function AI_A2A_DISPATCHER:SetDisengageRadius( DisengageRadius ) self.DisengageRadius = DisengageRadius or 300000 - + return self end - - + + --- Define the radius to check if a target can be engaged by an ground controlled intercept. -- When targets are detected that are still really far off, you don't want the AI_A2A_DISPATCHER to launch intercepts just yet. - -- You want it to wait until a certain Gci range is reached, which is the **distance of the closest airbase to target** + -- You want it to wait until a certain Gci range is reached, which is the **distance of the closest airbase to target** -- being **smaller** than the **Ground Controlled Intercept radius** or **Gci radius**. - -- - -- The **default** Gci radius is defined as **200000** or **200km**. Override the default Gci radius when the era of the warfare is early, or, + -- + -- The **default** Gci radius is defined as **200000** or **200km**. Override the default Gci radius when the era of the warfare is early, or, -- when you don't want to let the AI_A2A_DISPATCHER react immediately when a certain border or area is not being crossed. - -- + -- -- Use the method @{#AI_A2A_DISPATCHER.SetGciRadius}() to set a specific controlled ground intercept radius. -- **The Ground Controlled Intercept radius is defined for ALL squadrons which are operational.** - -- + -- -- Demonstration Mission: [AID-013 - AI_A2A - Intercept Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-013%20-%20AI_A2A%20-%20Intercept%20Test) - -- + -- -- @param #AI_A2A_DISPATCHER self -- @param #number GciRadius (Optional, Default = 200000) The radius to ground control intercept detected targets from the nearest airbase. -- @return #AI_A2A_DISPATCHER self -- @usage - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- -- Set 100km as the radius to ground control intercept detected targets from the nearest airbase. -- A2ADispatcher:SetGciRadius( 100000 ) - -- + -- -- -- Set 200km as the radius to ground control intercept. -- A2ADispatcher:SetGciRadius() -- 200000 is the default value. - -- + -- function AI_A2A_DISPATCHER:SetGciRadius( GciRadius ) - self.GciRadius = GciRadius or 200000 - + self.GciRadius = GciRadius or 200000 + return self end - - - + + + --- Define a border area to simulate a **cold war** scenario. -- A **cold war** is one where CAP aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border. -- A **hot war** is one where CAP aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send CAP and GCI aircraft to attack it. @@ -1319,29 +1319,29 @@ do -- AI_A2A_DISPATCHER -- @param Core.Zone#ZONE_BASE BorderZone An object derived from ZONE_BASE, or a list of objects derived from ZONE_BASE. -- @return #AI_A2A_DISPATCHER self -- @usage - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- -- Set one ZONE_POLYGON object as the border for the A2A dispatcher. -- local BorderZone = ZONE_POLYGON( "CCCP Border", GROUP:FindByName( "CCCP Border" ) ) -- The GROUP object is a late activate helicopter unit. -- A2ADispatcher:SetBorderZone( BorderZone ) - -- + -- -- or - -- + -- -- -- Set two ZONE_POLYGON objects as the border for the A2A 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. -- A2ADispatcher:SetBorderZone( { BorderZone1, BorderZone2 } ) - -- - -- + -- + -- function AI_A2A_DISPATCHER:SetBorderZone( BorderZone ) self.Detection:SetAcceptZones( BorderZone ) return self end - + --- Display a tactical report every 30 seconds about which aircraft are: -- * Patrolling -- * Engaging @@ -1353,19 +1353,19 @@ do -- AI_A2A_DISPATCHER -- @param #boolean TacticalDisplay Provide a value of **true** to display every 30 seconds a tactical overview. -- @return #AI_A2A_DISPATCHER self -- @usage - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- -- Now Setup the Tactical Display for debug mode. -- A2ADispatcher:SetTacticalDisplay( true ) - -- + -- function AI_A2A_DISPATCHER:SetTacticalDisplay( TacticalDisplay ) - + self.TacticalDisplay = TacticalDisplay - + return self - end + end --- Set the default damage treshold when defenders will RTB. @@ -1374,19 +1374,19 @@ do -- AI_A2A_DISPATCHER -- @param #number DamageThreshold A decimal number between 0 and 1, that expresses the %-tage of the damage treshold before going RTB. -- @return #AI_A2A_DISPATCHER self -- @usage - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- -- Now Setup the default damage treshold. -- A2ADispatcher:SetDefaultDamageThreshold( 0.90 ) -- Go RTB when the airplane 90% damaged. - -- + -- function AI_A2A_DISPATCHER:SetDefaultDamageThreshold( DamageThreshold ) - + self.DefenderDefault.DamageThreshold = DamageThreshold - + return self - end + end --- Set the default CAP time interval for squadrons, which will be used to determine a random CAP timing. @@ -1396,18 +1396,18 @@ do -- AI_A2A_DISPATCHER -- @param #number CapMaxSeconds The maximum amount of seconds for the random time interval. -- @return #AI_A2A_DISPATCHER self -- @usage - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- -- Now Setup the default CAP time interval. -- A2ADispatcher:SetDefaultCapTimeInterval( 300, 1200 ) -- Between 300 and 1200 seconds. - -- + -- function AI_A2A_DISPATCHER:SetDefaultCapTimeInterval( CapMinSeconds, CapMaxSeconds ) - + self.DefenderDefault.CapMinSeconds = CapMinSeconds self.DefenderDefault.CapMaxSeconds = CapMaxSeconds - + return self end @@ -1418,33 +1418,33 @@ do -- AI_A2A_DISPATCHER -- @param #number CapLimit The maximum amount of CAP that can be airborne at the same time for the squadron. -- @return #AI_A2A_DISPATCHER self -- @usage - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- -- Now Setup the default CAP limit. -- A2ADispatcher:SetDefaultCapLimit( 2 ) -- Maximum 2 CAP per squadron. - -- + -- function AI_A2A_DISPATCHER:SetDefaultCapLimit( CapLimit ) - + self.DefenderDefault.CapLimit = CapLimit - + return self - end + end --- Set intercept. -- @param #AI_A2A_DISPATCHER self -- @param #number InterceptDelay Delay in seconds before intercept. -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetIntercept( InterceptDelay ) - + self.DefenderDefault.InterceptDelay = InterceptDelay - + local Detection = self.Detection -- Functional.Detection#DETECTION_AREAS Detection:SetIntercept( true, InterceptDelay ) - + return self - end + end --- Calculates which AI friendlies are nearby the area @@ -1452,9 +1452,9 @@ do -- AI_A2A_DISPATCHER -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @return #table A list of the friendlies nearby. function AI_A2A_DISPATCHER:GetAIFriendliesNearBy( DetectedItem ) - + local FriendliesNearBy = self.Detection:GetFriendliesDistance( DetectedItem ) - + return FriendliesNearBy end @@ -1464,7 +1464,7 @@ do -- AI_A2A_DISPATCHER function AI_A2A_DISPATCHER:GetDefenderTasks() return self.DefenderTasks or {} end - + --- Get defender task. -- @param #AI_A2A_DISPATCHER self -- @param Wrapper.Group#GROUP Defender The defender group. @@ -1480,15 +1480,15 @@ do -- AI_A2A_DISPATCHER function AI_A2A_DISPATCHER:GetDefenderTaskFsm( Defender ) return self:GetDefenderTask( Defender ).Fsm end - + --- Get target of defender. -- @param #AI_A2A_DISPATCHER self -- @param Wrapper.Group#GROUP Defender The defender group. - -- @return Target + -- @return Target function AI_A2A_DISPATCHER:GetDefenderTaskTarget( Defender ) return self:GetDefenderTask( Defender ).Target end - + --- -- @param #AI_A2A_DISPATCHER self -- @param Wrapper.Group#GROUP Defender The defender group. @@ -1503,8 +1503,8 @@ do -- AI_A2A_DISPATCHER function AI_A2A_DISPATCHER:ClearDefenderTask( Defender ) if Defender and Defender:IsAlive() and self.DefenderTasks[Defender] then local Target = self.DefenderTasks[Defender].Target - local Message = "Clearing (" .. self.DefenderTasks[Defender].Type .. ") " - Message = Message .. Defender:GetName() + 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 @@ -1518,13 +1518,13 @@ do -- AI_A2A_DISPATCHER -- @param #AI_A2A_DISPATCHER self -- @param Wrapper.Group#GROUP Defender The defender group. function AI_A2A_DISPATCHER:ClearDefenderTaskTarget( Defender ) - + local DefenderTask = self:GetDefenderTask( Defender ) - + if Defender and Defender:IsAlive() and DefenderTask then local Target = DefenderTask.Target - local Message = "Clearing (" .. DefenderTask.Type .. ") " - Message = Message .. Defender:GetName() + local Message = "Clearing (" .. DefenderTask.Type .. ") " + Message = Message .. Defender:GetName() if Target then Message = Message .. ( Target and ( " from " .. Target.Index .. " [" .. Target.Set:Count() .. "]" ) ) or "" end @@ -1534,8 +1534,8 @@ do -- AI_A2A_DISPATCHER DefenderTask.Target = nil end -- if Defender and DefenderTask then --- if DefenderTask.Fsm:Is( "Fuel" ) --- or DefenderTask.Fsm:Is( "LostControl") +-- if DefenderTask.Fsm:Is( "Fuel" ) +-- or DefenderTask.Fsm:Is( "LostControl") -- or DefenderTask.Fsm:Is( "Damaged" ) then -- self:ClearDefenderTask( Defender ) -- end @@ -1543,7 +1543,7 @@ do -- AI_A2A_DISPATCHER return self end - + --- Set defender task. -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName Name of the squadron. @@ -1553,9 +1553,9 @@ do -- AI_A2A_DISPATCHER -- @param Functional.Detection#DETECTION_BASE.DetectedItem Target The defender detected item. -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetDefenderTask( SquadronName, Defender, Type, Fsm, Target ) - + self:F( { SquadronName = SquadronName, Defender = Defender:GetName(), Type=Type, Target=Target } ) - + self.DefenderTasks[Defender] = self.DefenderTasks[Defender] or {} self.DefenderTasks[Defender].Type = Type self.DefenderTasks[Defender].Fsm = Fsm @@ -1566,17 +1566,17 @@ do -- AI_A2A_DISPATCHER end return self end - - + + --- Set defender task target. -- @param #AI_A2A_DISPATCHER self -- @param Wrapper.Group#GROUP Defender The defender group. -- @param Functional.Detection#DETECTION_BASE.DetectedItem AttackerDetection The detection object. -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetDefenderTaskTarget( Defender, AttackerDetection ) - - local Message = "(" .. self.DefenderTasks[Defender].Type .. ") " - Message = Message .. Defender:GetName() + + 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 @@ -1586,90 +1586,90 @@ do -- AI_A2A_DISPATCHER end - --- This is the main method to define Squadrons programmatically. + --- 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\_A2A\_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_A2A_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. + -- + -- @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"`. + -- + -- @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"`. + -- + -- @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 A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- @usage - -- -- This will create squadron "Squadron1" at "Batumi" airbase, and will use plane types "SQ1" and has 40 planes in stock... + -- -- This will create squadron "Squadron1" at "Batumi" airbase, and will use plane types "SQ1" and has 40 planes in stock... -- A2ADispatcher: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 A2A 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. -- A2ADispatcher: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... -- A2ADispatcher:SetSquadron( "104th", "Batumi", "Mig-29", 10 ) -- A2ADispatcher: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. -- A2ADispatcher:SetSquadron( "104th", "Batumi", "Mig-29" ) -- A2ADispatcher:SetSquadron( "23th", "Batumi", "Su-27" ) - -- - -- + -- + -- -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetSquadron( SquadronName, AirbaseName, TemplatePrefixes, ResourceCount ) - - - self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} local DefenderSquadron = self.DefenderSquadrons[SquadronName] --#AI_A2A_DISPATCHER.Squadron - + 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 @@ -1688,25 +1688,25 @@ do -- AI_A2A_DISPATCHER self:SetSquadronLanguage( SquadronName, "EN" ) -- Squadrons speak English by default. self:F( { Squadron = {SquadronName, AirbaseName, TemplatePrefixes, ResourceCount } } ) - + return self end - + --- Get an item from the Squadron table. -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName Name of the squadron. -- @return #AI_A2A_DISPATCHER.Squadron Defender squadron table. function AI_A2A_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. @@ -1714,27 +1714,27 @@ do -- AI_A2A_DISPATCHER -- @param #string SquadronName The squadron name. -- @return #AI_A2A_DISPATCHER self -- @usage - -- + -- -- -- Set the Squadron visible before startup of dispatcher. -- A2ADispatcher:SetSquadronVisible( "Mineralnye" ) - -- + -- function AI_A2A_DISPATCHER:SetSquadronVisible( SquadronName ) - - self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} - + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + local DefenderSquadron = self:GetSquadron( SquadronName ) --#AI_A2A_DISPATCHER.Squadron - + DefenderSquadron.Uncontrolled = true - + -- For now, grouping is forced to 1 due to other parts of the class which would not work well with grouping>1. DefenderSquadron.Grouping=1 - + -- Get free parking for fighter aircraft. local nfreeparking=DefenderSquadron.Airbase:GetFreeParkingSpotsNumber(AIRBASE.TerminalType.FighterAircraft, true) - + -- Take number of free parking spots if no resource count was specifed. DefenderSquadron.ResourceCount=DefenderSquadron.ResourceCount or nfreeparking - + -- Check that resource count is not larger than free parking spots. DefenderSquadron.ResourceCount=math.min(DefenderSquadron.ResourceCount, nfreeparking) @@ -1751,22 +1751,22 @@ do -- AI_A2A_DISPATCHER -- @param #string SquadronName The squadron name. -- @return #boolean true if visible. -- @usage - -- + -- -- -- Set the Squadron visible before startup of dispatcher. -- local IsVisible = A2ADispatcher:IsSquadronVisible( "Mineralnye" ) - -- + -- function AI_A2A_DISPATCHER:IsSquadronVisible( SquadronName ) - - self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} - + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + local DefenderSquadron = self:GetSquadron( SquadronName ) --#AI_A2A_DISPATCHER.Squadron if DefenderSquadron then return DefenderSquadron.Uncontrolled == true end - + return nil - + end --- Set a CAP for a Squadron. @@ -1785,31 +1785,31 @@ do -- AI_A2A_DISPATCHER -- @param #number PatrolAltType The altitude type to patrol, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude. -- @return #AI_A2A_DISPATCHER -- @usage - -- + -- -- -- CAP Squadron execution. -- CAPZoneEast = ZONE_POLYGON:New( "CAP Zone East", GROUP:FindByName( "CAP Zone East" ) ) -- -- Setup a CAP, engaging between 800 and 900 km/h, altitude 30 (above the sea), radio altitude measurement, -- -- patrolling speed between 500 and 600 km/h, altitude between 4000 and 10000 meters, barometric altitude measurement. -- A2ADispatcher:SetSquadronCapV2( "Mineralnye", 800, 900, 30, 30, "RADIO", CAPZoneEast, 500, 600, 4000, 10000, "BARO" ) -- A2ADispatcher:SetSquadronCapInterval( "Mineralnye", 2, 30, 60, 1 ) - -- + -- -- CAPZoneWest = ZONE_POLYGON:New( "CAP Zone West", GROUP:FindByName( "CAP Zone West" ) ) -- -- Setup a CAP, engaging between 800 and 1200 km/h, altitude between 4000 and 10000 meters, radio altitude measurement, -- -- patrolling speed between 600 and 800 km/h, altitude between 4000 and 8000, barometric altitude measurement. -- A2ADispatcher:SetSquadronCapV2( "Sochi", 800, 1200, 2000, 3000, "RADIO", CAPZoneWest, 600, 800, 4000, 8000, "BARO" ) -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) - -- + -- -- CAPZoneMiddle = ZONE:New( "CAP Zone Middle") -- -- Setup a CAP, engaging between 800 and 1200 km/h, altitude between 5000 and 8000 meters, barometric altitude measurement, -- -- patrolling speed between 600 and 800 km/h, altitude between 4000 and 8000, radio altitude. -- A2ADispatcher:SetSquadronCapV2( "Maykop", 800, 1200, 5000, 8000, "BARO", CAPZoneMiddle, 600, 800, 4000, 8000, "RADIO" ) -- A2ADispatcher:SetSquadronCapInterval( "Maykop", 2, 30, 120, 1 ) - -- + -- function AI_A2A_DISPATCHER:SetSquadronCap2( SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, Zone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType ) - self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {} - + local DefenderSquadron = self:GetSquadron( SquadronName ) local Cap = self.DefenderSquadrons[SquadronName].Cap @@ -1829,18 +1829,18 @@ do -- AI_A2A_DISPATCHER self:SetSquadronCapInterval( SquadronName, self.DefenderDefault.CapLimit, self.DefenderDefault.CapMinSeconds, self.DefenderDefault.CapMaxSeconds, 1 ) self:I( { CAP = { SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, Zone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, EngageAltType } } ) - + -- Add the CAP to the EWR network. - + local RecceSet = self.Detection:GetDetectionSet() RecceSet:FilterPrefixes( DefenderSquadron.TemplatePrefixes ) RecceSet:FilterStart() - + self.Detection:SetFriendlyPrefixes( DefenderSquadron.TemplatePrefixes ) - + return self end - + --- Set a CAP for a Squadron. -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The squadron name. @@ -1854,26 +1854,26 @@ do -- AI_A2A_DISPATCHER -- @param #number AltType The altitude type, which is a string "BARO" defining Barometric or "RADIO" defining radio controlled altitude. -- @return #AI_A2A_DISPATCHER -- @usage - -- + -- -- -- CAP Squadron execution. -- CAPZoneEast = ZONE_POLYGON:New( "CAP Zone East", GROUP:FindByName( "CAP Zone East" ) ) -- A2ADispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 ) -- A2ADispatcher:SetSquadronCapInterval( "Mineralnye", 2, 30, 60, 1 ) - -- + -- -- CAPZoneWest = ZONE_POLYGON:New( "CAP Zone West", GROUP:FindByName( "CAP Zone West" ) ) -- A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" ) -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) - -- + -- -- CAPZoneMiddle = ZONE:New( "CAP Zone Middle") -- A2ADispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" ) -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) - -- + -- function AI_A2A_DISPATCHER:SetSquadronCap( SquadronName, Zone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, AltType ) - return self:SetSquadronCap2( SquadronName, EngageMinSpeed, EngageMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, AltType, Zone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, AltType ) + return self:SetSquadronCap2( SquadronName, EngageMinSpeed, EngageMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, AltType, Zone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, AltType ) end - - --- Set the squadron CAP parameters. + + --- Set the squadron CAP parameters. -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The squadron name. -- @param #number CapLimit (optional) The maximum amount of CAP groups to be spawned. Note that a CAP is a group, so can consist out of 1 to 4 airplanes. The default is 1 CAP group. @@ -1882,23 +1882,23 @@ do -- AI_A2A_DISPATCHER -- @param #number Probability Is not in use, you can skip this parameter. -- @return #AI_A2A_DISPATCHER -- @usage - -- + -- -- -- CAP Squadron execution. -- CAPZoneEast = ZONE_POLYGON:New( "CAP Zone East", GROUP:FindByName( "CAP Zone East" ) ) -- A2ADispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 ) -- A2ADispatcher:SetSquadronCapInterval( "Mineralnye", 2, 30, 60, 1 ) - -- + -- -- CAPZoneWest = ZONE_POLYGON:New( "CAP Zone West", GROUP:FindByName( "CAP Zone West" ) ) -- A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" ) -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) - -- + -- -- CAPZoneMiddle = ZONE:New( "CAP Zone Middle") -- A2ADispatcher:SetSquadronCap( "Maykop", CAPZoneMiddle, 4000, 8000, 600, 800, 800, 1200, "RADIO" ) -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) - -- + -- function AI_A2A_DISPATCHER:SetSquadronCapInterval( SquadronName, CapLimit, LowInterval, HighInterval, Probability ) - - self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {} local DefenderSquadron = self:GetSquadron( SquadronName ) @@ -1909,32 +1909,32 @@ do -- AI_A2A_DISPATCHER Cap.HighInterval = HighInterval or 600 Cap.Probability = Probability or 1 Cap.CapLimit = CapLimit or 1 - Cap.Scheduler = Cap.Scheduler or SCHEDULER:New( self ) + Cap.Scheduler = Cap.Scheduler or SCHEDULER:New( self ) local Scheduler = Cap.Scheduler -- Core.Scheduler#SCHEDULER local ScheduleID = Cap.ScheduleID local Variance = ( Cap.HighInterval - Cap.LowInterval ) / 2 local Repeat = Cap.LowInterval + Variance local Randomization = Variance / Repeat local Start = math.random( 1, Cap.HighInterval ) - + if ScheduleID then Scheduler:Stop( ScheduleID ) end - + Cap.ScheduleID = Scheduler:Schedule( self, self.SchedulerCAP, { SquadronName }, Start, Repeat, Randomization ) else error( "This squadron does not exist:" .. SquadronName ) end end - + --- -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The squadron name. -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:GetCAPDelay( SquadronName ) - - self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {} local DefenderSquadron = self:GetSquadron( SquadronName ) @@ -1953,16 +1953,16 @@ do -- AI_A2A_DISPATCHER -- @return #AI_A2A_DISPATCHER.Squadron DefenderSquadron function AI_A2A_DISPATCHER:CanCAP( SquadronName ) self:F({SquadronName = SquadronName}) - - self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {} local DefenderSquadron = self:GetSquadron( SquadronName ) if DefenderSquadron.Captured == false then -- We can only spawn new CAP if the base has not been captured. - + if ( not DefenderSquadron.ResourceCount ) or ( DefenderSquadron.ResourceCount and DefenderSquadron.ResourceCount > 0 ) then -- And, if there are sufficient resources. - + local Cap = DefenderSquadron.Cap if Cap then local CapCount = self:CountCapAirborne( SquadronName ) @@ -1988,10 +1988,10 @@ do -- AI_A2A_DISPATCHER -- @param #number HeadingMax Max heading of the race track in degrees. Default 180 deg, i.e. counter clockwise from North to South. -- @param #number DurationMin (Optional) Min duration in seconds before switching the orbit position. Default is keep same orbit until RTB or engage. -- @param #number DurationMax (Optional) Max duration in seconds before switching the orbit position. Default is keep same orbit until RTB or engage. - -- @param #table CapCoordinates Table of coordinates of first race track point. Second point is determined by leg length and heading. + -- @param #table CapCoordinates Table of coordinates of first race track point. Second point is determined by leg length and heading. -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetDefaultCapRacetrack(LeglengthMin, LeglengthMax, HeadingMin, HeadingMax, DurationMin, DurationMax, CapCoordinates) - + self.DefenderDefault.Racetrack=true self.DefenderDefault.RacetrackLengthMin=LeglengthMin self.DefenderDefault.RacetrackLengthMax=LeglengthMax @@ -2003,7 +2003,7 @@ do -- AI_A2A_DISPATCHER return self end - + --- Set race track pattern when squadron is performing CAP. -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName Name of the squadron. @@ -2013,12 +2013,12 @@ do -- AI_A2A_DISPATCHER -- @param #number HeadingMax Max heading of the race track in degrees. Default 180 deg, i.e. from North to South. -- @param #number DurationMin (Optional) Min duration in seconds before switching the orbit position. Default is keep same orbit until RTB or engage. -- @param #number DurationMax (Optional) Max duration in seconds before switching the orbit position. Default is keep same orbit until RTB or engage. - -- @param #table CapCoordinates Table of coordinates of first race track point. Second point is determined by leg length and heading. + -- @param #table CapCoordinates Table of coordinates of first race track point. Second point is determined by leg length and heading. -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetSquadronCapRacetrack(SquadronName, LeglengthMin, LeglengthMax, HeadingMin, HeadingMax, DurationMin, DurationMax, CapCoordinates) - + local DefenderSquadron = self:GetSquadron( SquadronName ) - + if DefenderSquadron then DefenderSquadron.Racetrack=true DefenderSquadron.RacetrackLengthMin=LeglengthMin @@ -2029,9 +2029,9 @@ do -- AI_A2A_DISPATCHER DefenderSquadron.RacetrackDurationMax=DurationMax DefenderSquadron.RacetrackCoordinates=CapCoordinates end - + return self - end + end --- Check if squadron can do GCI. @@ -2040,14 +2040,14 @@ do -- AI_A2A_DISPATCHER -- @return #table DefenderSquadron function AI_A2A_DISPATCHER:CanGCI( SquadronName ) self:F({SquadronName = SquadronName}) - - self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} self.DefenderSquadrons[SquadronName].Gci = self.DefenderSquadrons[SquadronName].Gci or {} local DefenderSquadron = self:GetSquadron( SquadronName ) if DefenderSquadron.Captured == false then -- We can only spawn new CAP if the base has not been captured. - + if ( not DefenderSquadron.ResourceCount ) or ( DefenderSquadron.ResourceCount and DefenderSquadron.ResourceCount > 0 ) then -- And, if there are sufficient resources. local Gci = DefenderSquadron.Gci if Gci then @@ -2066,19 +2066,19 @@ do -- AI_A2A_DISPATCHER -- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement. -- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement. -- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO". - -- @usage - -- + -- @usage + -- -- -- GCI Squadron execution. -- A2ADispatcher:SetSquadronGci2( "Mozdok", 900, 1200, 5000, 5000, "BARO" ) -- A2ADispatcher:SetSquadronGci2( "Novo", 900, 2100, 30, 30, "RADIO" ) -- A2ADispatcher:SetSquadronGci2( "Maykop", 900, 1200, 100, 300, "RADIO" ) - -- + -- -- @return #AI_A2A_DISPATCHER function AI_A2A_DISPATCHER:SetSquadronGci2( SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType ) - - self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} self.DefenderSquadrons[SquadronName].Gci = self.DefenderSquadrons[SquadronName].Gci or {} - + local Intercept = self.DefenderSquadrons[SquadronName].Gci Intercept.Name = SquadronName Intercept.EngageMinSpeed = EngageMinSpeed @@ -2086,70 +2086,70 @@ do -- AI_A2A_DISPATCHER Intercept.EngageFloorAltitude = EngageFloorAltitude Intercept.EngageCeilingAltitude = EngageCeilingAltitude Intercept.EngageAltType = EngageAltType - + self:I( { GCI = { SquadronName, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType } } ) end - + --- Set squadron GCI. -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The squadron name. -- @param #number EngageMinSpeed The minimum speed [km/h] at which the GCI can be executed. -- @param #number EngageMaxSpeed The maximum speed [km/h] at which the GCI can be executed. - -- @usage - -- + -- @usage + -- -- -- GCI Squadron execution. -- A2ADispatcher:SetSquadronGci( "Mozdok", 900, 1200 ) -- A2ADispatcher:SetSquadronGci( "Novo", 900, 2100 ) -- A2ADispatcher:SetSquadronGci( "Maykop", 900, 1200 ) - -- + -- -- @return #AI_A2A_DISPATCHER function AI_A2A_DISPATCHER:SetSquadronGci( SquadronName, EngageMinSpeed, EngageMaxSpeed ) - - self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} self.DefenderSquadrons[SquadronName].Gci = self.DefenderSquadrons[SquadronName].Gci or {} - + local Intercept = self.DefenderSquadrons[SquadronName].Gci Intercept.Name = SquadronName Intercept.EngageMinSpeed = EngageMinSpeed Intercept.EngageMaxSpeed = EngageMaxSpeed - + self:F( { GCI = { SquadronName, EngageMinSpeed, EngageMaxSpeed } } ) end - + --- Defines the default amount of extra planes that will take-off as part of the defense system. -- @param #AI_A2A_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_A2A_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 A2A 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: - -- + -- 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 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 A2ADispatcher = AI_A2A_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. - -- + -- -- A2ADispatcher:SetDefaultOverhead( 1.5 ) - -- + -- -- @return #AI_A2A_DISPATCHER function AI_A2A_DISPATCHER:SetDefaultOverhead( Overhead ) self.DefenderDefault.Overhead = Overhead - + return self end @@ -2161,35 +2161,35 @@ do -- AI_A2A_DISPATCHER -- The default overhead is 1, so equal balance. The @{#AI_A2A_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 A2A 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: - -- + -- 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 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 A2ADispatcher = AI_A2A_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. - -- + -- -- A2ADispatcher:SetSquadronOverhead( "SquadronName", 1.5 ) - -- + -- -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetSquadronOverhead( SquadronName, Overhead ) local DefenderSquadron = self:GetSquadron( SquadronName ) DefenderSquadron.Overhead = Overhead - + return self end @@ -2197,20 +2197,20 @@ do -- AI_A2A_DISPATCHER --- Sets the default grouping of new airplanes spawned. -- Grouping will trigger how new airplanes will be grouped if more than one airplane is spawned for defense. -- @param #AI_A2A_DISPATCHER self - -- @param #number Grouping The level of grouping that will be applied of the CAP or GCI defenders. + -- @param #number Grouping The level of grouping that will be applied of the CAP or GCI defenders. -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Set a grouping by default per 2 airplanes. -- A2ADispatcher:SetDefaultGrouping( 2 ) - -- - -- + -- + -- -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetDefaultGrouping( Grouping ) - + self.DefenderDefault.Grouping = Grouping - + return self end @@ -2219,21 +2219,21 @@ do -- AI_A2A_DISPATCHER -- Grouping will trigger how new airplanes will be grouped if more than one airplane is spawned for defense. -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The name of the squadron. - -- @param #number Grouping The level of grouping that will be applied of the CAP or GCI defenders. + -- @param #number Grouping The level of grouping that will be applied of the CAP or GCI defenders. -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Set a grouping per 2 airplanes. -- A2ADispatcher:SetSquadronGrouping( "SquadronName", 2 ) - -- - -- + -- + -- -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetSquadronGrouping( SquadronName, Grouping ) - + local DefenderSquadron = self:GetSquadron( SquadronName ) DefenderSquadron.Grouping = Grouping - + return self end @@ -2242,28 +2242,28 @@ do -- AI_A2A_DISPATCHER -- @param #AI_A2A_DISPATCHER self -- @param #number Takeoff From the airbase hot, from the airbase cold, in the air, from the runway. -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights by default take-off in the air. -- A2ADispatcher:SetDefaultTakeoff( AI_A2A_Dispatcher.Takeoff.Air ) - -- + -- -- -- Let new flights by default take-off from the runway. -- A2ADispatcher:SetDefaultTakeoff( AI_A2A_Dispatcher.Takeoff.Runway ) - -- + -- -- -- Let new flights by default take-off from the airbase hot. -- A2ADispatcher:SetDefaultTakeoff( AI_A2A_Dispatcher.Takeoff.Hot ) - -- + -- -- -- Let new flights by default take-off from the airbase cold. -- A2ADispatcher:SetDefaultTakeoff( AI_A2A_Dispatcher.Takeoff.Cold ) - -- - -- + -- + -- -- @return #AI_A2A_DISPATCHER self - -- + -- function AI_A2A_DISPATCHER:SetDefaultTakeoff( Takeoff ) self.DefenderDefault.Takeoff = Takeoff - + return self end @@ -2272,112 +2272,112 @@ do -- AI_A2A_DISPATCHER -- @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 A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights take-off in the air. -- A2ADispatcher:SetSquadronTakeoff( "SquadronName", AI_A2A_Dispatcher.Takeoff.Air ) - -- + -- -- -- Let new flights take-off from the runway. -- A2ADispatcher:SetSquadronTakeoff( "SquadronName", AI_A2A_Dispatcher.Takeoff.Runway ) - -- + -- -- -- Let new flights take-off from the airbase hot. -- A2ADispatcher:SetSquadronTakeoff( "SquadronName", AI_A2A_Dispatcher.Takeoff.Hot ) - -- + -- -- -- Let new flights take-off from the airbase cold. -- A2ADispatcher:SetSquadronTakeoff( "SquadronName", AI_A2A_Dispatcher.Takeoff.Cold ) - -- - -- + -- + -- -- @return #AI_A2A_DISPATCHER self - -- + -- function AI_A2A_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_A2A_DISPATCHER self -- @return #number Takeoff From the airbase hot, from the airbase cold, in the air, from the runway. -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights by default take-off in the air. -- local TakeoffMethod = A2ADispatcher:GetDefaultTakeoff() -- if TakeOffMethod == , AI_A2A_Dispatcher.Takeoff.InAir then -- ... -- end - -- + -- function AI_A2A_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_A2A_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 A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights take-off in the air. -- local TakeoffMethod = A2ADispatcher:GetSquadronTakeoff( "SquadronName" ) -- if TakeOffMethod == , AI_A2A_Dispatcher.Takeoff.InAir then -- ... -- end - -- + -- function AI_A2A_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_A2A_DISPATCHER self -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights by default take-off in the air. -- A2ADispatcher:SetDefaultTakeoffInAir() - -- + -- -- @return #AI_A2A_DISPATCHER self - -- + -- function AI_A2A_DISPATCHER:SetDefaultTakeoffInAir() self:SetDefaultTakeoff( AI_A2A_DISPATCHER.Takeoff.Air ) - + return self end - + --- Sets flights to take-off in the air, as part of the defense system. -- @param #AI_A2A_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 A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights take-off in the air. -- A2ADispatcher:SetSquadronTakeoffInAir( "SquadronName" ) - -- + -- -- @return #AI_A2A_DISPATCHER self - -- + -- function AI_A2A_DISPATCHER:SetSquadronTakeoffInAir( SquadronName, TakeoffAltitude ) self:SetSquadronTakeoff( SquadronName, AI_A2A_DISPATCHER.Takeoff.Air ) - + if TakeoffAltitude then self:SetSquadronTakeoffInAirAltitude( SquadronName, TakeoffAltitude ) end - + return self end @@ -2385,57 +2385,57 @@ do -- AI_A2A_DISPATCHER --- Sets flights by default to take-off from the runway, as part of the defense system. -- @param #AI_A2A_DISPATCHER self -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights by default take-off from the runway. -- A2ADispatcher:SetDefaultTakeoffFromRunway() - -- + -- -- @return #AI_A2A_DISPATCHER self - -- + -- function AI_A2A_DISPATCHER:SetDefaultTakeoffFromRunway() self:SetDefaultTakeoff( AI_A2A_DISPATCHER.Takeoff.Runway ) - + return self end - + --- Sets flights to take-off from the runway, as part of the defense system. -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The name of the squadron. -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights take-off from the runway. -- A2ADispatcher:SetSquadronTakeoffFromRunway( "SquadronName" ) - -- + -- -- @return #AI_A2A_DISPATCHER self - -- + -- function AI_A2A_DISPATCHER:SetSquadronTakeoffFromRunway( SquadronName ) self:SetSquadronTakeoff( SquadronName, AI_A2A_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_A2A_DISPATCHER self -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights by default take-off at a hot parking spot. -- A2ADispatcher:SetDefaultTakeoffFromParkingHot() - -- + -- -- @return #AI_A2A_DISPATCHER self - -- + -- function AI_A2A_DISPATCHER:SetDefaultTakeoffFromParkingHot() self:SetDefaultTakeoff( AI_A2A_DISPATCHER.Takeoff.Hot ) - + return self end @@ -2443,77 +2443,77 @@ do -- AI_A2A_DISPATCHER -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The name of the squadron. -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights take-off in the air. -- A2ADispatcher:SetSquadronTakeoffFromParkingHot( "SquadronName" ) - -- + -- -- @return #AI_A2A_DISPATCHER self - -- + -- function AI_A2A_DISPATCHER:SetSquadronTakeoffFromParkingHot( SquadronName ) self:SetSquadronTakeoff( SquadronName, AI_A2A_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_A2A_DISPATCHER self -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights take-off from a cold parking spot. -- A2ADispatcher:SetDefaultTakeoffFromParkingCold() - -- + -- -- @return #AI_A2A_DISPATCHER self - -- + -- function AI_A2A_DISPATCHER:SetDefaultTakeoffFromParkingCold() self:SetDefaultTakeoff( AI_A2A_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_A2A_DISPATCHER self -- @param #string SquadronName The name of the squadron. -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights take-off from a cold parking spot. -- A2ADispatcher:SetSquadronTakeoffFromParkingCold( "SquadronName" ) - -- + -- -- @return #AI_A2A_DISPATCHER self - -- + -- function AI_A2A_DISPATCHER:SetSquadronTakeoffFromParkingCold( SquadronName ) self:SetSquadronTakeoff( SquadronName, AI_A2A_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_A2A_DISPATCHER self -- @param #number TakeoffAltitude The altitude in meters above the ground. -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Set the default takeoff altitude when taking off in the air. -- A2ADispatcher:SetDefaultTakeoffInAirAltitude( 2000 ) -- This makes planes start at 2000 meters above the ground. - -- + -- -- @return #AI_A2A_DISPATCHER self - -- + -- function AI_A2A_DISPATCHER:SetDefaultTakeoffInAirAltitude( TakeoffAltitude ) self.DefenderDefault.TakeoffAltitude = TakeoffAltitude - + return self end @@ -2522,244 +2522,244 @@ do -- AI_A2A_DISPATCHER -- @param #string SquadronName The name of the squadron. -- @param #number TakeoffAltitude The altitude in meters above the ground. -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Set the default takeoff altitude when taking off in the air. -- A2ADispatcher:SetSquadronTakeoffInAirAltitude( "SquadronName", 2000 ) -- This makes planes start at 2000 meters above the ground. - -- + -- -- @return #AI_A2A_DISPATCHER self - -- + -- function AI_A2A_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_A2A_DISPATCHER self -- @param #number Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights by default despawn near the airbase when returning. -- A2ADispatcher:SetDefaultLanding( AI_A2A_Dispatcher.Landing.NearAirbase ) - -- + -- -- -- Let new flights by default despawn after landing land at the runway. -- A2ADispatcher:SetDefaultLanding( AI_A2A_Dispatcher.Landing.AtRunway ) - -- + -- -- -- Let new flights by default despawn after landing and parking, and after engine shutdown. -- A2ADispatcher:SetDefaultLanding( AI_A2A_Dispatcher.Landing.AtEngineShutdown ) - -- + -- -- @return #AI_A2A_DISPATCHER self function AI_A2A_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_A2A_DISPATCHER self -- @param #string SquadronName The name of the squadron. -- @param #number Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights despawn near the airbase when returning. -- A2ADispatcher:SetSquadronLanding( "SquadronName", AI_A2A_Dispatcher.Landing.NearAirbase ) - -- + -- -- -- Let new flights despawn after landing land at the runway. -- A2ADispatcher:SetSquadronLanding( "SquadronName", AI_A2A_Dispatcher.Landing.AtRunway ) - -- + -- -- -- Let new flights despawn after landing and parking, and after engine shutdown. -- A2ADispatcher:SetSquadronLanding( "SquadronName", AI_A2A_Dispatcher.Landing.AtEngineShutdown ) - -- + -- -- @return #AI_A2A_DISPATCHER self function AI_A2A_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_A2A_DISPATCHER self -- @return #number Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights by default despawn near the airbase when returning. -- local LandingMethod = A2ADispatcher:GetDefaultLanding( AI_A2A_Dispatcher.Landing.NearAirbase ) -- if LandingMethod == AI_A2A_Dispatcher.Landing.NearAirbase then -- ... -- end - -- + -- function AI_A2A_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_A2A_DISPATCHER self -- @param #string SquadronName The name of the squadron. -- @return #number Landing The landing method which can be NearAirbase, AtRunway, AtEngineShutdown -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let new flights despawn near the airbase when returning. -- local LandingMethod = A2ADispatcher:GetSquadronLanding( "SquadronName", AI_A2A_Dispatcher.Landing.NearAirbase ) -- if LandingMethod == AI_A2A_Dispatcher.Landing.NearAirbase then -- ... -- end - -- + -- function AI_A2A_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_A2A_DISPATCHER self -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let flights by default to land near the airbase and despawn. -- A2ADispatcher:SetDefaultLandingNearAirbase() - -- + -- -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetDefaultLandingNearAirbase() self:SetDefaultLanding( AI_A2A_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_A2A_DISPATCHER self -- @param #string SquadronName The name of the squadron. -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let flights to land near the airbase and despawn. -- A2ADispatcher:SetSquadronLandingNearAirbase( "SquadronName" ) - -- + -- -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetSquadronLandingNearAirbase( SquadronName ) self:SetSquadronLanding( SquadronName, AI_A2A_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_A2A_DISPATCHER self -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let flights by default land at the runway and despawn. -- A2ADispatcher:SetDefaultLandingAtRunway() - -- + -- -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetDefaultLandingAtRunway() self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.AtRunway ) - + return self end - + --- Sets flights to land and despawn at the runway, as part of the defense system. -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The name of the squadron. -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let flights land at the runway and despawn. -- A2ADispatcher:SetSquadronLandingAtRunway( "SquadronName" ) - -- + -- -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetSquadronLandingAtRunway( SquadronName ) self:SetSquadronLanding( SquadronName, AI_A2A_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_A2A_DISPATCHER self -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let flights by default land and despawn at engine shutdown. -- A2ADispatcher:SetDefaultLandingAtEngineShutdown() - -- + -- -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown() self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.AtEngineShutdown ) - + return self end - + --- Sets flights to land and despawn at engine shutdown, as part of the defense system. -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The name of the squadron. -- @usage: - -- + -- -- local A2ADispatcher = AI_A2A_DISPATCHER:New( ... ) - -- + -- -- -- Let flights land and despawn at engine shutdown. -- A2ADispatcher:SetSquadronLandingAtEngineShutdown( "SquadronName" ) - -- + -- -- @return #AI_A2A_DISPATCHER self function AI_A2A_DISPATCHER:SetSquadronLandingAtEngineShutdown( SquadronName ) self:SetSquadronLanding( SquadronName, AI_A2A_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_A2A_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_A2A_DISPATCHER self -- @usage - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- -- Now Setup the default fuel treshold. -- A2ADispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. - -- + -- function AI_A2A_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold ) - + self.DefenderDefault.FuelThreshold = FuelThreshold - + return self - end + end --- Set the fuel treshold for the squadron when defenders will RTB or Refuel in the air. @@ -2769,41 +2769,41 @@ do -- AI_A2A_DISPATCHER -- @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_A2A_DISPATCHER self -- @usage - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- -- Now Setup the default fuel treshold. -- A2ADispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. - -- + -- function AI_A2A_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold ) - + local DefenderSquadron = self:GetSquadron( SquadronName ) DefenderSquadron.FuelThreshold = FuelThreshold - + return self - end + end --- Set the default tanker where defenders will Refuel in the air. -- @param #AI_A2A_DISPATCHER self -- @param #string TankerName A string defining the group name of the Tanker as defined within the Mission Editor. -- @return #AI_A2A_DISPATCHER self -- @usage - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- -- Now Setup the default fuel treshold. -- A2ADispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. - -- + -- -- -- Now Setup the default tanker. -- A2ADispatcher:SetDefaultTanker( "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor. function AI_A2A_DISPATCHER:SetDefaultTanker( TankerName ) - + self.DefenderDefault.TankerName = TankerName - + return self - end + end --- Set the squadron tanker where defenders will Refuel in the air. @@ -2812,22 +2812,22 @@ do -- AI_A2A_DISPATCHER -- @param #string TankerName A string defining the group name of the Tanker as defined within the Mission Editor. -- @return #AI_A2A_DISPATCHER -- @usage - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- -- Now Setup the squadron fuel treshold. -- A2ADispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. - -- + -- -- -- Now Setup the squadron tanker. -- A2ADispatcher:SetSquadronTanker( "SquadronName", "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor. function AI_A2A_DISPATCHER:SetSquadronTanker( SquadronName, TankerName ) - + local DefenderSquadron = self:GetSquadron( SquadronName ) DefenderSquadron.TankerName = TankerName - + return self - end + end --- Set the squadron language. @@ -2836,26 +2836,26 @@ do -- AI_A2A_DISPATCHER -- @param #string Language A string defining the language to be embedded within the miz file. -- @return #AI_A2A_DISPATCHER -- @usage - -- + -- -- -- Now Setup the A2A dispatcher, and initialize it using the Detection object. - -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) - -- + -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) + -- -- -- Set for English. -- A2ADispatcher:SetSquadronLanguage( "SquadronName", "EN" ) -- This squadron speaks English. - -- + -- -- -- Set for Russian. -- A2ADispatcher:SetSquadronLanguage( "SquadronName", "RU" ) -- This squadron speaks Russian. function AI_A2A_DISPATCHER:SetSquadronLanguage( SquadronName, Language ) - + local DefenderSquadron = self:GetSquadron( SquadronName ) DefenderSquadron.Language = Language - + if DefenderSquadron.RadioQueue then DefenderSquadron.RadioQueue:SetLanguage( Language ) end - + return self - end + end --- Set the frequency of communication and the mode of communication for voice overs. @@ -2865,10 +2865,10 @@ do -- AI_A2A_DISPATCHER -- @param #number RadioModulation The modulation of communication. -- @param #number RadioPower The power in Watts of communication. function AI_A2A_DISPATCHER:SetSquadronRadioFrequency( SquadronName, RadioFrequency, RadioModulation, RadioPower ) - + local DefenderSquadron = self:GetSquadron( SquadronName ) DefenderSquadron.RadioFrequency = RadioFrequency - DefenderSquadron.RadioModulation = RadioModulation or radio.modulation.AM + DefenderSquadron.RadioModulation = RadioModulation or radio.modulation.AM DefenderSquadron.RadioPower = RadioPower or 100 if DefenderSquadron.RadioQueue then @@ -2880,9 +2880,9 @@ do -- AI_A2A_DISPATCHER DefenderSquadron.RadioQueue = RADIOSPEECH:New( DefenderSquadron.RadioFrequency, DefenderSquadron.RadioModulation ) DefenderSquadron.RadioQueue.power = DefenderSquadron.RadioPower DefenderSquadron.RadioQueue:Start( 0.5 ) - + DefenderSquadron.RadioQueue:SetLanguage( DefenderSquadron.Language ) - end + end --- Add defender to squadron. Resource count will get smaller. -- @param #AI_A2A_DISPATCHER self @@ -2921,17 +2921,17 @@ do -- AI_A2A_DISPATCHER self.Defenders = self.Defenders or {} local DefenderName = Defender:GetName() self:F( { DefenderName = DefenderName } ) - return self.Defenders[ DefenderName ] + return self.Defenders[ DefenderName ] end - + --- Creates an SWEEP task when there are targets for it. -- @param #AI_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. function AI_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem ) self:F( { DetectedItem.ItemID } ) - + local DetectedSet = DetectedItem.Set local DetectedZone = DetectedItem.Zone @@ -2942,10 +2942,10 @@ do -- AI_A2A_DISPATCHER local TargetSetUnit = SET_UNIT:New() TargetSetUnit:SetDatabase( DetectedSet ) TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - + return TargetSetUnit end - + return nil end @@ -2956,7 +2956,7 @@ do -- AI_A2A_DISPATCHER function AI_A2A_DISPATCHER:CountCapAirborne( SquadronName ) local CapCount = 0 - + local DefenderSquadron = self.DefenderSquadrons[SquadronName] if DefenderSquadron then for AIGroup, DefenderTask in pairs( self:GetDefenderTasks() ) do @@ -2978,8 +2978,8 @@ do -- AI_A2A_DISPATCHER return CapCount end - - + + --- Count number of engaging defender groups. -- @param #AI_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem AttackerDetection Detection object. @@ -2988,21 +2988,21 @@ do -- AI_A2A_DISPATCHER -- First, count the active AIGroups Units, targetting the DetectedSet local DefenderCount = 0 - + local DetectedSet = AttackerDetection.Set --DetectedSet:Flush() - + local DefenderTasks = self:GetDefenderTasks() - + for DefenderGroup, DefenderTask in pairs( DefenderTasks ) do local Defender = DefenderGroup -- Wrapper.Group#GROUP local DefenderTaskTarget = DefenderTask.Target --Functional.Detection#DETECTION_BASE.DetectedItem local DefenderSquadronName = DefenderTask.SquadronName - + if DefenderTaskTarget and DefenderTaskTarget.Index == AttackerDetection.Index then local Squadron = self:GetSquadron( DefenderSquadronName ) local SquadronOverhead = Squadron.Overhead or self.DefenderDefault.Overhead - + local DefenderSize = Defender:GetInitialSize() if DefenderSize then DefenderCount = DefenderCount + DefenderSize / SquadronOverhead @@ -3017,24 +3017,24 @@ do -- AI_A2A_DISPATCHER return DefenderCount end - + --- Count defenders to be engaged if number of attackers larger than number of defenders. -- @param #AI_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem AttackerDetection Detected item. -- @param #number DefenderCount Number of defenders. -- @return #table Table of friendly groups. function AI_A2A_DISPATCHER:CountDefendersToBeEngaged( AttackerDetection, DefenderCount ) - + local Friendlies = nil local AttackerSet = AttackerDetection.Set local AttackerCount = AttackerSet:Count() local DefenderFriendlies = self:GetAIFriendliesNearBy( AttackerDetection ) - + for FriendlyDistance, AIFriendly in UTILS.spairs( DefenderFriendlies or {} ) do -- We only allow to ENGAGE targets as long as the Units on both sides are balanced. - if AttackerCount > DefenderCount then + if AttackerCount > DefenderCount then local Friendly = AIFriendly:GetGroup() -- Wrapper.Group#GROUP if Friendly and Friendly:IsAlive() then -- Ok, so we have a friendly near the potential target. @@ -3053,7 +3053,7 @@ do -- AI_A2A_DISPATCHER end end end - end + end end else break @@ -3071,40 +3071,40 @@ do -- AI_A2A_DISPATCHER -- @return Wrapper.Group#GROUP The defender group. -- @return #boolean Grouping. function AI_A2A_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 - + --env.info(string.format("FF resource activate: Squadron=%s grouping=%d needed=%d visible=%s", SquadronName, DefenderGrouping, DefendersNeeded, tostring(self:IsSquadronVisible( SquadronName )))) - + if self:IsSquadronVisible( SquadronName ) then - + local n=#self.uncontrolled[SquadronName] - + if n>0 then -- Random number 1,...n local id=math.random(n) - + -- Pick a random defender group. local Defender=self.uncontrolled[SquadronName][id].group --Wrapper.Group#GROUP - + -- Start uncontrolled group. Defender:StartUncontrolled() - + -- Get grouping. DefenderGrouping=self.uncontrolled[SquadronName][id].grouping - + -- Add defender to squadron. self:AddDefenderToSquadron( DefenderSquadron, Defender, DefenderGrouping ) - + -- Remove defender from uncontrolled table. table.remove(self.uncontrolled[SquadronName], id) - + return Defender, DefenderGrouping else return nil,0 @@ -3117,7 +3117,7 @@ do -- AI_A2A_DISPATCHER --[[ -- 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 @@ -3141,9 +3141,9 @@ do -- AI_A2A_DISPATCHER if DefenderUnitIndex > DefenderGrouping then break end - - end - + + end + if DefenderCAPTemplate then local TakeoffMethod = self:GetSquadronTakeoff( SquadronName ) local SpawnGroup = GROUP:Register( DefenderName ) @@ -3153,38 +3153,38 @@ do -- AI_A2A_DISPATCHER DefenderCAPTemplate.route.points[1].type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type DefenderCAPTemplate.route.points[1].action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action local Defender = _DATABASE:Spawn( DefenderCAPTemplate ) - + self:AddDefenderToSquadron( DefenderSquadron, Defender, DefenderGrouping ) return Defender, DefenderGrouping end ]] - + else - + ---------------------------- --- Squadron not visible --- ---------------------------- - + 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 - + --- On after "CAP" event. -- @param #AI_A2A_DISPATCHER self -- @param #string From From state. @@ -3192,23 +3192,23 @@ do -- AI_A2A_DISPATCHER -- @param #string To To state. -- @param #string SquadronName Name of the squadron. function AI_A2A_DISPATCHER:onafterCAP( From, Event, To, SquadronName ) - + self:F({SquadronName = SquadronName}) - self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} + self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {} self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {} - + local DefenderSquadron = self:CanCAP( SquadronName ) - + if DefenderSquadron then - + local Cap = DefenderSquadron.Cap - + if Cap then - local DefenderCAP, DefenderGrouping = self:ResourceActivate( DefenderSquadron ) - + local DefenderCAP, DefenderGrouping = self:ResourceActivate( DefenderSquadron ) + if DefenderCAP then - + local AI_A2A_Fsm = AI_A2A_CAP:New2( DefenderCAP, Cap.EngageMinSpeed, Cap.EngageMaxSpeed, Cap.EngageFloorAltitude, Cap.EngageCeilingAltitude, Cap.EngageAltType, Cap.Zone, Cap.PatrolMinSpeed, Cap.PatrolMaxSpeed, Cap.PatrolFloorAltitude, Cap.PatrolCeilingAltitude, Cap.PatrolAltType ) AI_A2A_Fsm:SetDispatcher( self ) AI_A2A_Fsm:SetHomeAirbase( DefenderSquadron.Airbase ) @@ -3226,19 +3226,19 @@ do -- AI_A2A_DISPATCHER DefenderSquadron.RacetrackCoordinates or self.DefenderDefault.RacetrackCoordinates) end AI_A2A_Fsm:Start() - + self:SetDefenderTask( SquadronName, DefenderCAP, "CAP", AI_A2A_Fsm ) function AI_A2A_Fsm:onafterTakeoff( DefenderGroup, From, Event, To ) - -- Issue GetCallsign() returns nil, see https://github.com/FlightControl-Master/MOOSE/issues/1228 + -- Issue GetCallsign() returns nil, see https://github.com/FlightControl-Master/MOOSE/issues/1228 if DefenderGroup and DefenderGroup:IsAlive() then self:F({"CAP Takeoff", DefenderGroup:GetName()}) --self:GetParent(self).onafterBirth( self, Defender, From, Event, To ) - + local DefenderName = DefenderGroup:GetCallsign() local Dispatcher = AI_A2A_Fsm:GetDispatcher() -- #AI_A2A_DISPATCHER local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) - + if Squadron then Dispatcher:MessageToPlayers( Squadron, DefenderName .. " Wheels up.", DefenderGroup ) AI_A2A_Fsm:__Patrol( 2 ) -- Start Patrolling @@ -3250,14 +3250,14 @@ do -- AI_A2A_DISPATCHER if DefenderGroup and DefenderGroup:IsAlive() then self:F({"CAP PatrolRoute", DefenderGroup:GetName()}) self:GetParent(self).onafterPatrolRoute( self, DefenderGroup, From, Event, To ) - + local DefenderName = DefenderGroup:GetCallsign() local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) if Squadron then Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", patrolling.", DefenderGroup ) end - + Dispatcher:ClearDefenderTaskTarget( DefenderGroup ) end end @@ -3265,9 +3265,9 @@ do -- AI_A2A_DISPATCHER function AI_A2A_Fsm:onafterRTB( DefenderGroup, From, Event, To ) if DefenderGroup and DefenderGroup:IsAlive() then self:F({"CAP RTB", DefenderGroup:GetName()}) - + self:GetParent(self).onafterRTB( self, DefenderGroup, From, Event, To ) - + local DefenderName = DefenderGroup:GetCallsign() local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) @@ -3277,21 +3277,21 @@ do -- AI_A2A_DISPATCHER Dispatcher:ClearDefenderTaskTarget( DefenderGroup ) end end - + --- @param #AI_A2A_DISPATCHER self function AI_A2A_Fsm:onafterHome( Defender, From, Event, To, Action ) if Defender and Defender:IsAlive() then self:F({"CAP Home", Defender:GetName()}) self:GetParent(self).onafterHome( self, Defender, From, Event, To ) - + local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER local Squadron = Dispatcher:GetSquadronFromDefender( Defender ) - + if Action and Action == "Destroy" then Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender ) Defender:Destroy() end - + if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2A_DISPATCHER.Landing.NearAirbase then Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender ) Defender:Destroy() @@ -3302,7 +3302,7 @@ do -- AI_A2A_DISPATCHER end end end - + end @@ -3315,7 +3315,7 @@ do -- AI_A2A_DISPATCHER -- @param #table Defenders Defenders table. function AI_A2A_DISPATCHER:onafterENGAGE( From, Event, To, AttackerDetection, Defenders ) self:F("ENGAGING Detection ID="..tostring(AttackerDetection.ID)) - + if Defenders then for DefenderID, Defender in pairs( Defenders ) do @@ -3327,7 +3327,7 @@ do -- AI_A2A_DISPATCHER self:SetDefenderTaskTarget( Defender, AttackerDetection ) end - + end end @@ -3340,38 +3340,38 @@ do -- AI_A2A_DISPATCHER -- @param #number DefendersMissing Number of missing defenders. -- @param #table DefenderFriendlies Friendly defenders. function AI_A2A_DISPATCHER:onafterGCI( From, Event, To, AttackerDetection, DefendersMissing, DefenderFriendlies ) - + self:F("GCI Detection ID="..tostring(AttackerDetection.ID)) self:F( { From, Event, To, AttackerDetection.Index, DefendersMissing, DefenderFriendlies } ) local AttackerSet = AttackerDetection.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 - + local Fsm = self:GetDefenderTaskFsm( DefenderGroup ) Fsm:__EngageRoute( 0.1, AttackerSet ) -- Engage on the TargetSetUnit - + self:SetDefenderTaskTarget( DefenderGroup, AttackerDetection ) - + DefenderCount = DefenderCount + DefenderGroup:GetSize() 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 @@ -3379,7 +3379,7 @@ do -- AI_A2A_DISPATCHER self:F( { GCI = DefenderSquadron.Gci } ) for InterceptID, Intercept in pairs( DefenderSquadron.Gci or {} ) do - + self:F( { DefenderSquadron } ) local SpawnCoord = DefenderSquadron.Airbase:GetCoordinate() -- Core.Point#COORDINATE local AttackerCoord = AttackerUnit:GetCoordinate() @@ -3389,9 +3389,9 @@ do -- AI_A2A_DISPATCHER local InterceptDistance = SpawnCoord:Get2DDistance( InterceptCoord ) local AirbaseDistance = SpawnCoord: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.GciRadius then ClosestDistance = InterceptDistance @@ -3401,42 +3401,42 @@ do -- AI_A2A_DISPATCHER end end end - + if ClosestDefenderSquadronName then - + local DefenderSquadron = self:CanGCI( ClosestDefenderSquadronName ) - + if DefenderSquadron then - + local Gci = self.DefenderSquadrons[ClosestDefenderSquadronName].Gci - + if Gci 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 } ) - + -- 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 - - local DefenderGCI, DefenderGrouping = self:ResourceActivate( DefenderSquadron, DefendersNeeded ) - + + local DefenderGCI, DefenderGrouping = self:ResourceActivate( DefenderSquadron, DefendersNeeded ) + DefendersNeeded = DefendersNeeded - DefenderGrouping - + if DefenderGCI then - + DefenderCount = DefenderCount - DefenderGrouping / DefenderOverhead - + local Fsm = AI_A2A_GCI:New2( DefenderGCI, Gci.EngageMinSpeed, Gci.EngageMaxSpeed, Gci.EngageFloorAltitude, Gci.EngageCeilingAltitude, Gci.EngageAltType ) Fsm:SetDispatcher( self ) Fsm:SetHomeAirbase( DefenderSquadron.Airbase ) @@ -3444,20 +3444,20 @@ do -- AI_A2A_DISPATCHER Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold ) Fsm:SetDisengageRadius( self.DisengageRadius ) Fsm:Start() - - + + self:SetDefenderTask( ClosestDefenderSquadronName, DefenderGCI, "GCI", Fsm, AttackerDetection ) - - + + function Fsm:onafterTakeoff( DefenderGroup, From, Event, To ) self:F({"GCI Birth", DefenderGroup:GetName()}) --self:GetParent(self).onafterBirth( self, Defender, From, Event, To ) - + local DefenderName = DefenderGroup:GetCallsign() local Dispatcher = Fsm:GetDispatcher() -- #AI_A2A_DISPATCHER local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) local DefenderTarget = Dispatcher:GetDefenderTaskTarget( DefenderGroup ) - + if DefenderTarget then if Squadron.Language == "EN" then Dispatcher:MessageToPlayers( Squadron, DefenderName .. " wheels up.", DefenderGroup ) @@ -3468,18 +3468,18 @@ do -- AI_A2A_DISPATCHER Fsm:EngageRoute( DefenderTarget.Set ) -- Engage on the TargetSetUnit end end - + function Fsm:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit ) self:F({"GCI Route", DefenderGroup:GetName()}) - + local DefenderName = DefenderGroup:GetCallsign() local Dispatcher = Fsm:GetDispatcher() -- #AI_A2A_DISPATCHER local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) - + if Squadron and AttackSetUnit:Count() > 0 then local FirstUnit = AttackSetUnit:GetFirst() local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE - + if Squadron.Language == "EN" then Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", intercepting bogeys at " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup ) elseif Squadron.Language == "RU" then @@ -3490,18 +3490,18 @@ do -- AI_A2A_DISPATCHER end self:GetParent( Fsm ).onafterEngageRoute( self, DefenderGroup, From, Event, To, AttackSetUnit ) end - + function Fsm:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit ) self:F({"GCI Engage", DefenderGroup:GetName()}) - + local DefenderName = DefenderGroup:GetCallsign() local Dispatcher = Fsm:GetDispatcher() -- #AI_A2A_DISPATCHER local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) - + if Squadron and AttackSetUnit:Count() > 0 then local FirstUnit = AttackSetUnit:GetFirst() local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE - + if Squadron.Language == "EN" then Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", engaging bogeys at " .. Coordinate:ToStringA2A( DefenderGroup, nil, Squadron.Language ), DefenderGroup ) elseif Squadron.Language == "RU" then @@ -3510,11 +3510,11 @@ do -- AI_A2A_DISPATCHER end self:GetParent( Fsm ).onafterEngage( self, DefenderGroup, From, Event, To, AttackSetUnit ) end - + function Fsm:onafterRTB( DefenderGroup, From, Event, To ) self:F({"GCI RTB", DefenderGroup:GetName()}) self:GetParent(self).onafterRTB( self, DefenderGroup, From, Event, To ) - + local DefenderName = DefenderGroup:GetCallsign() local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) @@ -3528,12 +3528,12 @@ do -- AI_A2A_DISPATCHER end Dispatcher:ClearDefenderTaskTarget( DefenderGroup ) end - + --- @param #AI_A2A_DISPATCHER self function Fsm:onafterLostControl( Defender, From, Event, To ) self:F({"GCI LostControl", Defender:GetName()}) self:GetParent(self).onafterHome( self, Defender, From, Event, To ) - + local Dispatcher = Fsm:GetDispatcher() -- #AI_A2A_DISPATCHER local Squadron = Dispatcher:GetSquadronFromDefender( Defender ) if Defender:IsAboveRunway() then @@ -3541,12 +3541,12 @@ do -- AI_A2A_DISPATCHER Defender:Destroy() end end - + --- @param #AI_A2A_DISPATCHER self function Fsm:onafterHome( DefenderGroup, From, Event, To, Action ) self:F({"GCI Home", DefenderGroup:GetName()}) self:GetParent(self).onafterHome( self, DefenderGroup, From, Event, To ) - + local DefenderName = DefenderGroup:GetCallsign() local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup ) @@ -3556,19 +3556,19 @@ do -- AI_A2A_DISPATCHER elseif Squadron.Language == "RU" then Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", захватывающие самолеты в посадка на базу.", DefenderGroup ) end - + if Action and Action == "Destroy" then Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup ) DefenderGroup:Destroy() end - + if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2A_DISPATCHER.Landing.NearAirbase then Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup ) DefenderGroup:Destroy() Dispatcher:ParkDefender( Squadron ) end end - + end -- if DefenderGCI then end -- while ( DefendersNeeded > 0 ) do end @@ -3594,25 +3594,25 @@ do -- AI_A2A_DISPATCHER -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units or nil. function AI_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem ) self:F( { DetectedItem.ItemID } ) - + -- First, count the active AIGroups Units, targetting the DetectedSet local DefenderCount = self:CountDefendersEngaged( DetectedItem ) local DefenderGroups = self:CountDefendersToBeEngaged( DetectedItem, DefenderCount ) self:F( { DefenderCount = DefenderCount } ) - + -- Only allow ENGAGE when: -- 1. There are friendly units near the detected attackers. -- 2. There is sufficient fuel -- 3. There is sufficient ammo -- 4. The plane is not damaged - if DefenderGroups and DetectedItem.IsDetected == true then + if DefenderGroups and DetectedItem.IsDetected == true then return DefenderGroups end - + return nil end - + --- Creates an GCI task when there are targets for it. -- @param #AI_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem The detected item. @@ -3620,7 +3620,7 @@ do -- AI_A2A_DISPATCHER -- @return #table Table of friendly groups. function AI_A2A_DISPATCHER:EvaluateGCI( DetectedItem ) self:F( { DetectedItem.ItemID } ) - + local AttackerSet = DetectedItem.Set local AttackerCount = AttackerSet:Count() @@ -3632,10 +3632,10 @@ do -- AI_A2A_DISPATCHER local Friendlies = self:CountDefendersToBeEngaged( DetectedItem, DefenderCount ) if DetectedItem.IsDetected == true then - + return DefendersMissing, Friendlies end - + return nil, nil end @@ -3643,36 +3643,36 @@ do -- AI_A2A_DISPATCHER --- Assigns A2G AI Tasks in relation to the detected items. -- @param #AI_A2G_DISPATCHER self function AI_A2A_DISPATCHER:Order( DetectedItem ) - + local detection=self.Detection -- Functional.Detection#DETECTION_AREAS - + local ShortestDistance = 999999999 - + -- Get coordinate (or nil). local AttackCoordinate = detection:GetDetectedItemCoordinate( DetectedItem ) - + -- Issue https://github.com/FlightControl-Master/MOOSE/issues/1232 if AttackCoordinate then - + for DefenderSquadronName, DefenderSquadron in pairs( self.DefenderSquadrons ) do - + self:T( { DefenderSquadron = DefenderSquadron.Name } ) - + local Airbase = DefenderSquadron.Airbase local AirbaseCoordinate = Airbase:GetCoordinate() - + local EvaluateDistance = AttackCoordinate:Get2DDistance( AirbaseCoordinate ) - + if EvaluateDistance <= ShortestDistance then ShortestDistance = EvaluateDistance end end - + end - + return ShortestDistance end - + --- Shows the tactical display. -- @param #AI_A2A_DISPATCHER self @@ -3682,17 +3682,17 @@ do -- AI_A2A_DISPATCHER local AreaMsg = {} local TaskMsg = {} local ChangeMsg = {} - + local TaskReport = REPORT:New() - + local Report = REPORT:New( "Tactical Overview:" ) local DefenderGroupCount = 0 -- Now that all obsolete tasks are removed, loop through the detected targets. --for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do - for DetectedItemID, DetectedItem in UTILS.spairs( Detection:GetDetectedItems(), function( t, a, b ) return self:Order(t[a]) < self:Order(t[b]) end ) do - + for DetectedItemID, DetectedItem in UTILS.spairs( Detection:GetDetectedItems(), function( t, a, b ) return self:Order(t[a]) < self:Order(t[b]) end ) do + local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT local DetectedCount = DetectedSet:Count() @@ -3704,7 +3704,7 @@ do -- AI_A2A_DISPATCHER local DetectedID = DetectedItem.ID local DetectionIndex = DetectedItem.Index local DetectedItemChanged = DetectedItem.Changed - + -- Show tactical situation Report:Add( string.format( "\n- Target %s (%s): (#%d) %s" , DetectedItem.ItemID, DetectedItem.Index, DetectedItem.Set:Count(), DetectedItem.Set:GetObjectNames() ) ) for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do @@ -3714,15 +3714,15 @@ do -- AI_A2A_DISPATCHER DefenderGroupCount = DefenderGroupCount + 1 local Fuel = Defender:GetFuelMin() * 100 local Damage = Defender:GetLife() / Defender:GetLife0() * 100 - Report:Add( string.format( " - %s*%d/%d (%s - %s): (#%d) F: %3d, D:%3d - %s", + Report:Add( string.format( " - %s*%d/%d (%s - %s): (#%d) F: %3d, D:%3d - %s", Defender:GetName(), Defender:GetSize(), Defender:GetInitialSize(), - DefenderTask.Type, - DefenderTask.Fsm:GetState(), - Defender:GetSize(), + DefenderTask.Type, + DefenderTask.Fsm:GetState(), + Defender:GetSize(), Fuel, - Damage, + Damage, Defender:HasTask() == true and "Executing" or "Idle" ) ) end end @@ -3740,15 +3740,15 @@ do -- AI_A2A_DISPATCHER local Fuel = Defender:GetFuelMin() * 100 local Damage = Defender:GetLife() / Defender:GetLife0() * 100 DefenderGroupCount = DefenderGroupCount + 1 - Report:Add( string.format( " - %s*%d/%d (%s - %s): (#%d) F: %3d, D:%3d - %s", - Defender:GetName(), + Report:Add( string.format( " - %s*%d/%d (%s - %s): (#%d) F: %3d, D:%3d - %s", + Defender:GetName(), Defender:GetSize(), Defender:GetInitialSize(), - DefenderTask.Type, - DefenderTask.Fsm:GetState(), + DefenderTask.Type, + DefenderTask.Fsm:GetState(), Defender:GetSize(), Fuel, - Damage, + Damage, Defender:HasTask() == true and "Executing" or "Idle" ) ) end end @@ -3757,9 +3757,9 @@ do -- AI_A2A_DISPATCHER self:F( Report:Text( "\n" ) ) trigger.action.outText( Report:Text( "\n" ), 25 ) - + return true - + end --- Assigns A2A AI Tasks in relation to the detected items. @@ -3767,14 +3767,14 @@ do -- AI_A2A_DISPATCHER -- @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_A2A_DISPATCHER:ProcessDetected( Detection ) - + local AreaMsg = {} local TaskMsg = {} local ChangeMsg = {} - + local TaskReport = REPORT:New() - + for AIGroup, DefenderTask in pairs( self:GetDefenderTasks() ) do local AIGroup = AIGroup -- Wrapper.Group#GROUP if not AIGroup:IsAlive() then @@ -3810,7 +3810,7 @@ do -- AI_A2A_DISPATCHER -- Closest detected targets to be considered first! --for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do for DetectedItemID, DetectedItem in UTILS.spairs( Detection:GetDetectedItems(), function( t, a, b ) return self:Order(t[a]) < self:Order(t[b]) end ) do - + local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT local DetectedCount = DetectedSet:Count() @@ -3822,8 +3822,8 @@ do -- AI_A2A_DISPATCHER local DetectedID = DetectedItem.ID local DetectionIndex = DetectedItem.Index local DetectedItemChanged = DetectedItem.Changed - - do + + do local Friendlies = self:EvaluateENGAGE( DetectedItem ) -- Returns a SetUnit if there are targets to be GCIed... if Friendlies then self:F( { AIGroups = Friendlies } ) @@ -3843,7 +3843,7 @@ do -- AI_A2A_DISPATCHER if self.TacticalDisplay then self:ShowTacticalDisplay( Detection ) end - + return true end @@ -3856,10 +3856,10 @@ do -- @param Functional.Detection#DETECTION_BASE.DetectedItem 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_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem ) - + local DetectedSet = DetectedItem.Set local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem ) - + local PlayerTypes = {} local PlayersCount = 0 @@ -3878,13 +3878,13 @@ do 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 ) ) @@ -3892,8 +3892,8 @@ do else PlayerTypesReport:Add( "-" ) end - - + + return PlayersCount, PlayerTypesReport end @@ -3902,10 +3902,10 @@ do -- @param Functional.Detection#DETECTION_BASE.DetectedItem 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_A2A_DISPATCHER:GetFriendliesNearBy( DetectedItem ) - + local DetectedSet = DetectedItem.Set local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem ) - + local FriendlyTypes = {} local FriendliesCount = 0 @@ -3922,13 +3922,13 @@ do 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 ) ) @@ -3936,8 +3936,8 @@ do else FriendlyTypesReport:Add( "-" ) end - - + + return FriendliesCount, FriendlyTypesReport end @@ -3955,256 +3955,256 @@ do --- @type AI_A2A_GCICAP -- @extends #AI_A2A_DISPATCHER - --- Create an automatic air defence system for a coalition setting up GCI and CAP air defenses. + --- Create an automatic air defence system for a coalition setting up GCI and CAP air defenses. -- The class derives from @{#AI_A2A_DISPATCHER} and thus, all the methods that are defined in the @{#AI_A2A_DISPATCHER} class, can be used also in AI\_A2A\_GCICAP. - -- + -- -- === - -- + -- -- # Demo Missions - -- + -- -- ### [AI\_A2A\_GCICAP for Caucasus](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-200%20-%20AI_A2A%20-%20GCICAP%20Demonstration) -- ### [AI\_A2A\_GCICAP for NTTR](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-210%20-%20NTTR%20AI_A2A_GCICAP%20Demonstration) -- ### [AI\_A2A\_GCICAP for Normandy](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-220%20-%20NORMANDY%20AI_A2A_GCICAP%20Demonstration) - -- + -- -- ### [AI\_A2A\_GCICAP for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching) -- -- === - -- + -- -- # YouTube Channel - -- + -- -- ### [DCS WORLD - MOOSE - A2A GCICAP - Build an automatic A2A Defense System](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0S4KMNUUJpaUs6zZHjLKNx) - -- + -- -- === - -- + -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia3.JPG) - -- - -- AI\_A2A\_GCICAP includes automatic spawning of Combat Air Patrol aircraft (CAP) and Ground Controlled Intercept aircraft (GCI) in response to enemy - -- air movements that are detected by an airborne or ground based radar network. - -- + -- + -- AI\_A2A\_GCICAP includes automatic spawning of Combat Air Patrol aircraft (CAP) and Ground Controlled Intercept aircraft (GCI) in response to enemy + -- air movements that are detected by an airborne or ground based radar network. + -- -- With a little time and with a little work it provides the mission designer with a convincing and completely automatic air defence system. - -- - -- The AI_A2A_GCICAP provides a lightweight configuration method using the mission editor. Within a very short time, and with very little coding, - -- the mission designer is able to configure a complete A2A defense system for a coalition using the DCS Mission Editor available functions. - -- Using the DCS Mission Editor, you define borders of the coalition which are guarded by GCICAP, + -- + -- The AI_A2A_GCICAP provides a lightweight configuration method using the mission editor. Within a very short time, and with very little coding, + -- the mission designer is able to configure a complete A2A defense system for a coalition using the DCS Mission Editor available functions. + -- Using the DCS Mission Editor, you define borders of the coalition which are guarded by GCICAP, -- configure airbases to belong to the coalition, define squadrons flying certain types of planes or payloads per airbase, and define CAP zones. - -- **Very little lua needs to be applied, a one liner**, which is fully explained below, which can be embedded - -- right in a DO SCRIPT trigger action or in a larger DO SCRIPT FILE trigger action. - -- - -- CAP flights will take off and proceed to designated CAP zones where they will remain on station until the ground radars direct them to intercept - -- detected enemy aircraft or they run short of fuel and must return to base (RTB). - -- + -- **Very little lua needs to be applied, a one liner**, which is fully explained below, which can be embedded + -- right in a DO SCRIPT trigger action or in a larger DO SCRIPT FILE trigger action. + -- + -- CAP flights will take off and proceed to designated CAP zones where they will remain on station until the ground radars direct them to intercept + -- detected enemy aircraft or they run short of fuel and must return to base (RTB). + -- -- When a CAP flight leaves their zone to perform a GCI or return to base a new CAP flight will spawn to take its place. -- If all CAP flights are engaged or RTB then additional GCI interceptors will scramble to intercept unengaged enemy aircraft under ground radar control. - -- + -- -- In short it is a plug in very flexible and configurable air defence module for DCS World. - -- + -- -- === - -- + -- -- # The following actions need to be followed when using AI\_A2A\_GCICAP in your mission: - -- - -- ## 1) Configure a working AI\_A2A\_GCICAP defense system for ONE coalition. - -- - -- ### 1.1) Define which airbases are for which coalition. - -- + -- + -- ## 1) Configure a working AI\_A2A\_GCICAP defense system for ONE coalition. + -- + -- ### 1.1) Define which airbases are for which coalition. + -- -- ![Mission Editor Action](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_GCICAP-ME_1.JPG) - -- + -- -- Color the airbases red or blue. You can do this by selecting the airbase on the map, and select the coalition blue or red. - -- - -- ### 1.2) Place groups of units given a name starting with a **EWR prefix** of your choice to build your EWR network. - -- + -- + -- ### 1.2) Place groups of units given a name starting with a **EWR prefix** of your choice to build your EWR network. + -- -- ![Mission Editor Action](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_GCICAP-ME_2.JPG) - -- - -- **All EWR groups starting with the EWR prefix (text) will be included in the detection system.** - -- + -- + -- **All EWR groups starting with the EWR prefix (text) will be included in the detection system.** + -- -- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy. - -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. + -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. -- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). - -- Additionally, ANY other radar capable unit can be part of the EWR network! + -- Additionally, ANY other radar capable unit can be part of the EWR network! -- Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. - -- The position of these units is very important as they need to provide enough coverage + -- The position of these units is very important as they need to provide enough coverage -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. - -- - -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. - -- For example if they are a long way forward and can detect enemy planes on the ground and taking off - -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. - -- Having the radars further back will mean a slower escalation because fewer targets will be detected and - -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. - -- It all depends on what the desired effect is. - -- - -- EWR networks are **dynamically maintained**. By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, + -- + -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. + -- For example if they are a long way forward and can detect enemy planes on the ground and taking off + -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. + -- Having the radars further back will mean a slower escalation because fewer targets will be detected and + -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. + -- It all depends on what the desired effect is. + -- + -- EWR networks are **dynamically maintained**. By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, -- increasing or decreasing the radar coverage of the Early Warning System. - -- - -- ### 1.3) Place Airplane or Helicopter Groups with late activation switched on above the airbases to define Squadrons. - -- + -- + -- ### 1.3) Place Airplane or Helicopter Groups with late activation switched on above the airbases to define Squadrons. + -- -- ![Mission Editor Action](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_GCICAP-ME_3.JPG) - -- - -- These are **templates**, with a given name starting with a **Template prefix** above each airbase that you wanna have a squadron. - -- These **templates** need to be within 1.5km from the airbase center. They don't need to have a slot at the airplane, they can just be positioned above the airbase, + -- + -- These are **templates**, with a given name starting with a **Template prefix** above each airbase that you wanna have a squadron. + -- These **templates** need to be within 1.5km from the airbase center. They don't need to have a slot at the airplane, they can just be positioned above the airbase, -- without a route, and should only have ONE unit. - -- + -- -- ![Mission Editor Action](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_GCICAP-ME_4.JPG) - -- - -- **All airplane or helicopter groups that are starting with any of the choosen Template Prefixes will result in a squadron created at the airbase.** - -- - -- ### 1.4) Place floating helicopters to create the CAP zones defined by its route points. - -- + -- + -- **All airplane or helicopter groups that are starting with any of the choosen Template Prefixes will result in a squadron created at the airbase.** + -- + -- ### 1.4) Place floating helicopters to create the CAP zones defined by its route points. + -- -- ![Mission Editor Action](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_GCICAP-ME_5.JPG) - -- - -- **All airplane or helicopter groups that are starting with any of the choosen Template Prefixes will result in a squadron created at the airbase.** - -- - -- The helicopter indicates the start of the CAP zone. - -- The route points define the form of the CAP zone polygon. - -- + -- + -- **All airplane or helicopter groups that are starting with any of the choosen Template Prefixes will result in a squadron created at the airbase.** + -- + -- The helicopter indicates the start of the CAP zone. + -- The route points define the form of the CAP zone polygon. + -- -- ![Mission Editor Action](..\Presentations\AI_A2A_DISPATCHER\AI_A2A_GCICAP-ME_6.JPG) - -- + -- -- **The place of the helicopter is important, as the airbase closest to the helicopter will be the airbase from where the CAP planes will take off for CAP.** - -- + -- -- ## 2) There are a lot of defaults set, which can be further modified using the methods in @{#AI_A2A_DISPATCHER}: - -- + -- -- ### 2.1) Planes are taking off in the air from the airbases. - -- + -- -- This prevents airbases to get cluttered with airplanes taking off, it also reduces the risk of human players colliding with taxiiing airplanes, -- resulting in the airbase to halt operations. - -- + -- -- You can change the way how planes take off by using the inherited methods from AI\_A2A\_DISPATCHER: - -- + -- -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoff}() is the generic configuration method to control takeoff from the air, hot, cold or from the runway. See the method for further details. -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffInAir}() will spawn new aircraft from the squadron directly in the air. -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromParkingCold}() will spawn new aircraft in without running engines at a parking spot at the airfield. -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromParkingHot}() will spawn new aircraft in with running engines at a parking spot at the airfield. -- * @{#AI_A2A_DISPATCHER.SetSquadronTakeoffFromRunway}() will spawn new aircraft at the runway at the airfield. - -- + -- -- Use these methods to fine-tune for specific airfields that are known to create bottlenecks, or have reduced airbase efficiency. -- The more and the longer aircraft need to taxi at an airfield, the more risk there is that: - -- + -- -- * aircraft will stop waiting for each other or for a landing aircraft before takeoff. -- * aircraft may get into a "dead-lock" situation, where two aircraft are blocking each other. -- * aircraft may collide at the airbase. -- * aircraft may be awaiting the landing of a plane currently in the air, but never lands ... - -- + -- -- Currently within the DCS engine, the airfield traffic coordination is erroneous and contains a lot of bugs. -- If you experience while testing problems with aircraft take-off or landing, please use one of the above methods as a solution to workaround these issues! - -- + -- -- ### 2.2) Planes return near the airbase or will land if damaged. - -- + -- -- When damaged airplanes return to the airbase, they will be routed and will dissapear in the air when they are near the airbase. -- There are exceptions to this rule, airplanes that aren't "listening" anymore due to damage or out of fuel, will return to the airbase and land. - -- + -- -- You can change the way how planes land by using the inherited methods from AI\_A2A\_DISPATCHER: - -- + -- -- * @{#AI_A2A_DISPATCHER.SetSquadronLanding}() is the generic configuration method to control landing, namely despawn the aircraft near the airfield in the air, right after landing, or at engine shutdown. -- * @{#AI_A2A_DISPATCHER.SetSquadronLandingNearAirbase}() will despawn the returning aircraft in the air when near the airfield. -- * @{#AI_A2A_DISPATCHER.SetSquadronLandingAtRunway}() will despawn the returning aircraft directly after landing at the runway. -- * @{#AI_A2A_DISPATCHER.SetSquadronLandingAtEngineShutdown}() will despawn the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines. - -- + -- -- You can use these methods to minimize the airbase coodination overhead and to increase the airbase efficiency. -- When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the -- A2A defense system, as no new CAP or GCI planes can takeoff. -- Note that the method @{#AI_A2A_DISPATCHER.SetSquadronLandingNearAirbase}() will only work for returning aircraft, not for damaged or out of fuel aircraft. -- Damaged or out-of-fuel aircraft are returning to the nearest friendly airbase and will land, and are out of control from ground control. - -- - -- ### 2.3) CAP operations setup for specific airbases, will be executed with the following parameters: - -- - -- * The altitude will range between 6000 and 10000 meters. - -- * The CAP speed will vary between 500 and 800 km/h. + -- + -- ### 2.3) CAP operations setup for specific airbases, will be executed with the following parameters: + -- + -- * The altitude will range between 6000 and 10000 meters. + -- * The CAP speed will vary between 500 and 800 km/h. -- * The engage speed between 800 and 1200 km/h. - -- + -- -- You can change or add a CAP zone by using the inherited methods from AI\_A2A\_DISPATCHER: - -- + -- -- The method @{#AI_A2A_DISPATCHER.SetSquadronCap}() defines a CAP execution for a squadron. - -- + -- -- Setting-up a CAP zone also requires specific parameters: - -- + -- -- * The minimum and maximum altitude -- * The minimum speed and maximum patrol speed -- * The minimum and maximum engage speed -- * The type of altitude measurement - -- - -- These define how the squadron will perform the CAP while partrolling. Different terrain types requires different types of CAP. - -- + -- + -- These define how the squadron will perform the CAP while partrolling. Different terrain types requires different types of CAP. + -- -- The @{#AI_A2A_DISPATCHER.SetSquadronCapInterval}() method specifies **how much** and **when** CAP flights will takeoff. - -- - -- It is recommended not to overload the air defense with CAP flights, as these will decrease the performance of the overall system. - -- + -- + -- It is recommended not to overload the air defense with CAP flights, as these will decrease the performance of the overall system. + -- -- For example, the following setup will create a CAP for squadron "Sochi": - -- + -- -- A2ADispatcher:SetSquadronCap( "Sochi", CAPZoneWest, 4000, 8000, 600, 800, 800, 1200, "BARO" ) -- A2ADispatcher:SetSquadronCapInterval( "Sochi", 2, 30, 120, 1 ) - -- + -- -- ### 2.4) Each airbase will perform GCI when required, with the following parameters: - -- + -- -- * The engage speed is between 800 and 1200 km/h. - -- + -- -- You can change or add a GCI parameters by using the inherited methods from AI\_A2A\_DISPATCHER: - -- + -- -- The method @{#AI_A2A_DISPATCHER.SetSquadronGci}() defines a GCI execution for a squadron. - -- + -- -- Setting-up a GCI readiness also requires specific parameters: - -- + -- -- * The minimum speed and maximum patrol speed - -- + -- -- Essentially this controls how many flights of GCI aircraft can be active at any time. -- Note allowing large numbers of active GCI flights can adversely impact mission performance on low or medium specification hosts/servers. - -- GCI needs to be setup at strategic airbases. Too far will mean that the aircraft need to fly a long way to reach the intruders, + -- GCI needs to be setup at strategic airbases. Too far will mean that the aircraft need to fly a long way to reach the intruders, -- too short will mean that the intruders may have alraedy passed the ideal interception point! - -- + -- -- For example, the following setup will create a GCI for squadron "Sochi": - -- + -- -- A2ADispatcher:SetSquadronGci( "Mozdok", 900, 1200 ) - -- + -- -- ### 2.5) Grouping or detected targets. - -- + -- -- Detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate -- group being detected. - -- + -- -- Targets will be grouped within a radius of 30km by default. - -- + -- -- The radius indicates that detected targets need to be grouped within a radius of 30km. -- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. - -- Fast planes like in the 80s, need a larger radius than WWII planes. + -- Fast planes like in the 80s, need a larger radius than WWII planes. -- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft. - -- + -- -- ## 3) Additional notes: - -- + -- -- In order to create a two way A2A defense system, **two AI\_A2A\_GCICAP defense systems must need to be created**, for each coalition one. -- Each defense system needs its own EWR network setup, airplane templates and CAP configurations. - -- + -- -- This is a good implementation, because maybe in the future, more coalitions may become available in DCS world. - -- + -- -- ## 4) Coding examples how to use the AI\_A2A\_GCICAP class: - -- + -- -- ### 4.1) An easy setup: - -- + -- -- -- Setup the AI_A2A_GCICAP dispatcher for one coalition, and initialize it. -- GCI_Red = AI_A2A_GCICAP:New( "EWR CCCP", "SQUADRON CCCP", "CAP CCCP", 2 ) - -- -- + -- -- -- The following parameters were given to the :New method of AI_A2A_GCICAP, and mean the following: - -- + -- -- * `"EWR CCCP"`: Groups of the blue coalition are placed that define the EWR network. These groups start with the name `EWR CCCP`. -- * `"SQUADRON CCCP"`: Late activated Groups objects of the red coalition are placed above the relevant airbases that will contain these templates in the squadron. -- These late activated Groups start with the name `SQUADRON CCCP`. Each Group object contains only one Unit, and defines the weapon payload, skin and skill level. -- * `"CAP CCCP"`: CAP Zones are defined using floating, late activated Helicopter Group objects, where the route points define the route of the polygon of the CAP Zone. -- These Helicopter Group objects start with the name `CAP CCCP`, and will be the locations wherein CAP will be performed. - -- * `2` Defines how many CAP airplanes are patrolling in each CAP zone defined simulateneously. - -- - -- + -- * `2` Defines how many CAP airplanes are patrolling in each CAP zone defined simulateneously. + -- + -- -- ### 4.2) A more advanced setup: - -- + -- -- -- Setup the AI_A2A_GCICAP dispatcher for the blue coalition. - -- - -- A2A_GCICAP_Blue = AI_A2A_GCICAP:New( { "BLUE EWR" }, { "104th", "105th", "106th" }, { "104th CAP" }, 4 ) - -- + -- + -- A2A_GCICAP_Blue = AI_A2A_GCICAP:New( { "BLUE EWR" }, { "104th", "105th", "106th" }, { "104th CAP" }, 4 ) + -- -- The following parameters for the :New method have the following meaning: - -- + -- -- * `{ "BLUE EWR" }`: An array of the group name prefixes of the groups of the blue coalition are placed that define the EWR network. These groups start with the name `BLUE EWR`. - -- * `{ "104th", "105th", "106th" } `: An array of the group name prefixes of the Late activated Groups objects of the blue coalition are + -- * `{ "104th", "105th", "106th" } `: An array of the group name prefixes of the Late activated Groups objects of the blue coalition are -- placed above the relevant airbases that will contain these templates in the squadron. - -- These late activated Groups start with the name `104th` or `105th` or `106th`. - -- * `{ "104th CAP" }`: An array of the names of the CAP zones are defined using floating, late activated helicopter group objects, + -- These late activated Groups start with the name `104th` or `105th` or `106th`. + -- * `{ "104th CAP" }`: An array of the names of the CAP zones are defined using floating, late activated helicopter group objects, -- where the route points define the route of the polygon of the CAP Zone. -- These Helicopter Group objects start with the name `104th CAP`, and will be the locations wherein CAP will be performed. - -- * `4` Defines how many CAP airplanes are patrolling in each CAP zone defined simulateneously. - -- + -- * `4` Defines how many CAP airplanes are patrolling in each CAP zone defined simulateneously. + -- -- @field #AI_A2A_GCICAP AI_A2A_GCICAP = { ClassName = "AI_A2A_GCICAP", @@ -4218,73 +4218,73 @@ do -- @param #string TemplatePrefixes A list of template prefixes. -- @param #string CapPrefixes A list of CAP zone prefixes (polygon zones). -- @param #number CapLimit A number of how many CAP maximum will be spawned. - -- @param #number GroupingRadius The radius in meters wherein detected planes are being grouped as one target area. + -- @param #number GroupingRadius The radius in meters wherein detected planes are being grouped as one target area. -- For airplanes, 6000 (6km) is recommended, and is also the default value of this parameter. -- @param #number EngageRadius The radius in meters wherein detected airplanes will be engaged by airborne defenders without a task. -- @param #number GciRadius The radius in meters wherein detected airplanes will GCI. -- @param #number ResourceCount The amount of resources that will be allocated to each squadron. -- @return #AI_A2A_GCICAP -- @usage - -- + -- -- -- Setup a new GCICAP dispatcher object. Each squadron has unlimited resources. -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. -- -- The CAP Zone prefix is "CAP Zone". -- -- The CAP Limit is 2. - -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2 ) - -- + -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2 ) + -- -- @usage - -- + -- -- -- Setup a new GCICAP dispatcher object. Each squadron has unlimited resources. -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. -- -- The CAP Zone prefix is "CAP Zone". -- -- The CAP Limit is 2. -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. - -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000 ) - -- + -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000 ) + -- -- @usage - -- + -- -- -- Setup a new GCICAP dispatcher object. Each squadron has unlimited resources. -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. -- -- The CAP Zone prefix is "CAP Zone". -- -- The CAP Limit is 2. -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. - -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, + -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, -- -- will be considered a defense task if the target is within 60km from the defender. - -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000, 60000 ) - -- + -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000, 60000 ) + -- -- @usage - -- + -- -- -- Setup a new GCICAP dispatcher object. Each squadron has unlimited resources. -- -- The EWR network group prefix is DF CCCP. All groups starting with DF CCCP will be part of the EWR network. -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. -- -- The CAP Zone prefix is "CAP Zone". -- -- The CAP Limit is 2. -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. - -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, + -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, -- -- will be considered a defense task if the target is within 60km from the defender. -- -- The GCI Radius is set to 150000. Any target detected within 150km will be considered for GCI engagement. - -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000, 60000, 150000 ) - -- + -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000, 60000, 150000 ) + -- -- @usage - -- + -- -- -- Setup a new GCICAP dispatcher object. Each squadron has 30 resources. -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. -- -- The CAP Zone prefix is "CAP Zone". -- -- The CAP Limit is 2. -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. - -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, + -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, -- -- will be considered a defense task if the target is within 60km from the defender. -- -- The GCI Radius is set to 150000. Any target detected within 150km will be considered for GCI engagement. -- -- The amount of resources for each squadron is set to 30. Thus about 30 resources are allocated to each squadron created. - -- - -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000, 60000, 150000, 30 ) - -- + -- + -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, { "CAP Zone" }, 2, 20000, 60000, 150000, 30 ) + -- -- @usage - -- + -- -- -- Setup a new GCICAP dispatcher object. Each squadron has 30 resources. -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. @@ -4294,9 +4294,9 @@ do -- -- The Engage Radius is set nil. The default Engage Radius will be used to consider a defenser being assigned to a task. -- -- The GCI Radius is nil. Any target detected within the default GCI Radius will be considered for GCI engagement. -- -- The amount of resources for each squadron is set to 30. Thus about 30 resources are allocated to each squadron created. - -- - -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, nil, nil, nil, nil, nil, 30 ) - -- + -- + -- A2ADispatcher = AI_A2A_GCICAP:New( { "DF CCCP" }, { "SQ CCCP" }, nil, nil, nil, nil, nil, 30 ) + -- function AI_A2A_GCICAP:New( EWRPrefixes, TemplatePrefixes, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, ResourceCount ) local EWRSetGroup = SET_GROUP:New() @@ -4306,14 +4306,14 @@ do local Detection = DETECTION_AREAS:New( EWRSetGroup, GroupingRadius or 30000 ) local self = BASE:Inherit( self, AI_A2A_DISPATCHER:New( Detection ) ) -- #AI_A2A_GCICAP - + self:SetEngageRadius( EngageRadius ) self:SetGciRadius( GciRadius ) -- Determine the coalition of the EWRNetwork, this will be the coalition of the GCICAP. local EWRFirst = EWRSetGroup:GetFirst() -- Wrapper.Group#GROUP local EWRCoalition = EWRFirst:GetCoalition() - + -- Determine the airbases belonging to the coalition. local AirbaseNames = {} -- #list<#string> for AirbaseID, AirbaseData in pairs( _DATABASE.AIRBASES ) do @@ -4322,25 +4322,25 @@ do if Airbase:GetCoalition() == EWRCoalition then table.insert( AirbaseNames, AirbaseName ) end - end - + end + self.Templates = SET_GROUP :New() :FilterPrefixes( TemplatePrefixes ) :FilterOnce() -- Setup squadrons - + self:I( { Airbases = AirbaseNames } ) - self:I( "Defining Templates for Airbases ..." ) + self:I( "Defining Templates for Airbases ..." ) for AirbaseID, AirbaseName in pairs( AirbaseNames ) do local Airbase = _DATABASE:FindAirbase( AirbaseName ) -- Wrapper.Airbase#AIRBASE local AirbaseName = Airbase:GetName() local AirbaseCoord = Airbase:GetCoordinate() local AirbaseZone = ZONE_RADIUS:New( "Airbase", AirbaseCoord:GetVec2(), 3000 ) local Templates = nil - self:I( { Airbase = AirbaseName } ) + self:I( { Airbase = AirbaseName } ) for TemplateID, Template in pairs( self.Templates:GetSet() ) do local Template = Template -- Wrapper.Group#GROUP local TemplateCoord = Template:GetCoordinate() @@ -4356,20 +4356,20 @@ do end -- Setup CAP. - -- Find for each CAP the nearest airbase to the (start or center) of the zone. + -- Find for each CAP the nearest airbase to the (start or center) of the zone. -- CAP will be launched from there. - + self.CAPTemplates = SET_GROUP:New() self.CAPTemplates:FilterPrefixes( CapPrefixes ) self.CAPTemplates:FilterOnce() - - self:I( "Setting up CAP ..." ) + + self:I( "Setting up CAP ..." ) for CAPID, CAPTemplate in pairs( self.CAPTemplates:GetSet() ) do local CAPZone = ZONE_POLYGON:New( CAPTemplate:GetName(), CAPTemplate ) -- Now find the closest airbase from the ZONE (start or center) local AirbaseDistance = 99999999 local AirbaseClosest = nil -- Wrapper.Airbase#AIRBASE - self:I( { CAPZoneGroup = CAPID } ) + self:I( { CAPZoneGroup = CAPID } ) for AirbaseID, AirbaseName in pairs( AirbaseNames ) do local Airbase = _DATABASE:FindAirbase( AirbaseName ) -- Wrapper.Airbase#AIRBASE local AirbaseName = Airbase:GetName() @@ -4377,7 +4377,7 @@ do local Squadron = self.DefenderSquadrons[AirbaseName] if Squadron then local Distance = AirbaseCoord:Get2DDistance( CAPZone:GetCoordinate() ) - self:I( { AirbaseDistance = Distance } ) + self:I( { AirbaseDistance = Distance } ) if Distance < AirbaseDistance then AirbaseDistance = Distance AirbaseClosest = Airbase @@ -4385,35 +4385,35 @@ do end end if AirbaseClosest then - self:I( { CAPAirbase = AirbaseClosest:GetName() } ) + self:I( { CAPAirbase = AirbaseClosest:GetName() } ) self:SetSquadronCap( AirbaseClosest:GetName(), CAPZone, 6000, 10000, 500, 800, 800, 1200, "RADIO" ) self:SetSquadronCapInterval( AirbaseClosest:GetName(), CapLimit, 300, 600, 1 ) - end - end + end + end -- Setup GCI. -- GCI is setup for all Squadrons. - self:I( "Setting up GCI ..." ) + self:I( "Setting up GCI ..." ) for AirbaseID, AirbaseName in pairs( AirbaseNames ) do local Airbase = _DATABASE:FindAirbase( AirbaseName ) -- Wrapper.Airbase#AIRBASE local AirbaseName = Airbase:GetName() local Squadron = self.DefenderSquadrons[AirbaseName] - self:F( { Airbase = AirbaseName } ) + self:F( { Airbase = AirbaseName } ) if Squadron then - self:I( { GCIAirbase = AirbaseName } ) + self:I( { GCIAirbase = AirbaseName } ) self:SetSquadronGci( AirbaseName, 800, 1200 ) end end - + self:__Start( 5 ) - + 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 ) - + return self end @@ -4424,24 +4424,24 @@ do -- @param #string BorderPrefix A Border Zone Prefix. -- @param #string CapPrefixes A list of CAP zone prefixes (polygon zones). -- @param #number CapLimit A number of how many CAP maximum will be spawned. - -- @param #number GroupingRadius The radius in meters wherein detected planes are being grouped as one target area. + -- @param #number GroupingRadius The radius in meters wherein detected planes are being grouped as one target area. -- For airplanes, 6000 (6km) is recommended, and is also the default value of this parameter. -- @param #number EngageRadius The radius in meters wherein detected airplanes will be engaged by airborne defenders without a task. -- @param #number GciRadius The radius in meters wherein detected airplanes will GCI. -- @param #number ResourceCount The amount of resources that will be allocated to each squadron. -- @return #AI_A2A_GCICAP -- @usage - -- + -- -- -- Setup a new GCICAP dispatcher object with a border. Each squadron has unlimited resources. -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. -- -- The CAP Zone prefix is "CAP Zone". -- -- The CAP Limit is 2. - -- - -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2 ) - -- + -- + -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2 ) + -- -- @usage - -- + -- -- -- Setup a new GCICAP dispatcher object with a border. Each squadron has unlimited resources. -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. @@ -4449,11 +4449,11 @@ do -- -- The CAP Zone prefix is "CAP Zone". -- -- The CAP Limit is 2. -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. - -- - -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000 ) - -- + -- + -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000 ) + -- -- @usage - -- + -- -- -- Setup a new GCICAP dispatcher object with a border. Each squadron has unlimited resources. -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. @@ -4461,13 +4461,13 @@ do -- -- The CAP Zone prefix is "CAP Zone". -- -- The CAP Limit is 2. -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. - -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, + -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, -- -- will be considered a defense task if the target is within 60km from the defender. - -- - -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000, 60000 ) - -- + -- + -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000, 60000 ) + -- -- @usage - -- + -- -- -- Setup a new GCICAP dispatcher object with a border. Each squadron has unlimited resources. -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. @@ -4475,14 +4475,14 @@ do -- -- The CAP Zone prefix is "CAP Zone". -- -- The CAP Limit is 2. -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. - -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, + -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, -- -- will be considered a defense task if the target is within 60km from the defender. -- -- The GCI Radius is set to 150000. Any target detected within 150km will be considered for GCI engagement. - -- - -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000, 60000, 150000 ) - -- + -- + -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000, 60000, 150000 ) + -- -- @usage - -- + -- -- -- Setup a new GCICAP dispatcher object with a border. Each squadron has 30 resources. -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. @@ -4490,15 +4490,15 @@ do -- -- The CAP Zone prefix is "CAP Zone". -- -- The CAP Limit is 2. -- -- The Grouping Radius is set to 20000. Thus all planes within a 20km radius will be grouped as a group of targets. - -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, + -- -- The Engage Radius is set to 60000. Any defender without a task, and in healthy condition, -- -- will be considered a defense task if the target is within 60km from the defender. -- -- The GCI Radius is set to 150000. Any target detected within 150km will be considered for GCI engagement. -- -- The amount of resources for each squadron is set to 30. Thus about 30 resources are allocated to each squadron created. - -- - -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000, 60000, 150000, 30 ) - -- + -- + -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", { "CAP Zone" }, 2, 20000, 60000, 150000, 30 ) + -- -- @usage - -- + -- -- -- Setup a new GCICAP dispatcher object with a border. Each squadron has 30 resources. -- -- The EWR network group prefix is "DF CCCP". All groups starting with "DF CCCP" will be part of the EWR network. -- -- The Squadron Templates prefix is "SQ CCCP". All groups starting with "SQ CCCP" will be considered as airplane templates. @@ -4509,9 +4509,9 @@ do -- -- The Engage Radius is set nil. The default Engage Radius will be used to consider a defenser being assigned to a task. -- -- The GCI Radius is nil. Any target detected within the default GCI Radius will be considered for GCI engagement. -- -- The amount of resources for each squadron is set to 30. Thus about 30 resources are allocated to each squadron created. - -- - -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", nil, nil, nil, nil, nil, 30 ) - -- + -- + -- A2ADispatcher = AI_A2A_GCICAP:NewWithBorder( { "DF CCCP" }, { "SQ CCCP" }, "Border", nil, nil, nil, nil, nil, 30 ) + -- function AI_A2A_GCICAP:NewWithBorder( EWRPrefixes, TemplatePrefixes, BorderPrefix, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, ResourceCount ) local self = AI_A2A_GCICAP:New( EWRPrefixes, TemplatePrefixes, CapPrefixes, CapLimit, GroupingRadius, EngageRadius, GciRadius, ResourceCount ) @@ -4519,10 +4519,9 @@ do if BorderPrefix then self:SetBorderZone( ZONE_POLYGON:New( BorderPrefix, GROUP:FindByName( BorderPrefix ) ) ) end - + return self end end - diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua index f0ddb1d13..4a3f4570b 100644 --- a/Moose Development/Moose/AI/AI_A2A_Gci.lua +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -1,12 +1,12 @@ --- **AI** -- (R2.2) - Models the process of Ground Controlled Interception (GCI) for airplanes. -- -- This is a class used in the @{AI_A2A_Dispatcher}. --- +-- -- === --- +-- -- ### Author: **FlightControl** --- --- === +-- +-- === -- -- @module AI.AI_A2A_GCI -- @image AI_Ground_Control_Intercept.JPG @@ -18,52 +18,52 @@ --- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders. --- +-- -- ![Process](..\Presentations\AI_GCI\Dia3.JPG) --- +-- -- The AI_A2A_GCI is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_GCI process can be started using the **Start** event. --- +-- -- ![Process](..\Presentations\AI_GCI\Dia4.JPG) --- +-- -- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. -- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. --- +-- -- ![Process](..\Presentations\AI_GCI\Dia5.JPG) --- +-- -- This cycle will continue. --- +-- -- ![Process](..\Presentations\AI_GCI\Dia6.JPG) --- +-- -- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event. -- -- ![Process](..\Presentations\AI_GCI\Dia9.JPG) --- +-- -- When enemies are detected, the AI will automatically engage the enemy. --- +-- -- ![Process](..\Presentations\AI_GCI\Dia10.JPG) --- +-- -- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. --- +-- -- ![Process](..\Presentations\AI_GCI\Dia13.JPG) --- +-- -- ## 1. AI_A2A_GCI constructor --- +-- -- * @{#AI_A2A_GCI.New}(): Creates a new AI_A2A_GCI object. --- +-- -- ## 2. AI_A2A_GCI is a FSM --- +-- -- ![Process](..\Presentations\AI_GCI\Dia2.JPG) --- +-- -- ### 2.1 AI_A2A_GCI States --- +-- -- * **None** ( Group ): The process is not started yet. -- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone. -- * **Engaging** ( Group ): The AI is engaging the bogeys. -- * **Returning** ( Group ): The AI is returning to Base.. --- +-- -- ### 2.2 AI_A2A_GCI Events --- +-- -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. -- * **@{#AI_A2A_GCI.Engage}**: Let the AI engage the bogeys. @@ -76,25 +76,25 @@ -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- -- ## 3. Set the Range of Engagement --- +-- -- ![Range](..\Presentations\AI_GCI\Dia11.JPG) --- --- An optional range can be set in meters, +-- +-- An optional range can be set in meters, -- that will define when the AI will engage with the detected airborne enemy targets. -- The range can be beyond or smaller than the range of the Patrol Zone. -- The range is applied at the position of the AI. -- Use the method @{AI.AI_GCI#AI_A2A_GCI.SetEngageRange}() to define that range. -- -- ## 4. Set the Zone of Engagement --- +-- -- ![Zone](..\Presentations\AI_GCI\Dia12.JPG) --- --- An optional @{Zone} can be set, +-- +-- An optional @{Zone} can be set, -- that will define when the AI will engage with the detected airborne enemy targets. -- Use the method @{AI.AI_Cap#AI_A2A_GCI.SetEngageZone}() to define that Zone. --- +-- -- === --- +-- -- @field #AI_A2A_GCI AI_A2A_GCI = { ClassName = "AI_A2A_GCI", @@ -116,7 +116,7 @@ function AI_A2A_GCI:New2( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFlo local AI_Air = AI_AIR:New( AIIntercept ) local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air, AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType ) local self = BASE:Inherit( self, AI_Air_Engage ) -- #AI_A2A_GCI - + self:SetFuelThreshold( .2, 60 ) self:SetDamageThreshold( 0.4 ) self:SetDisengageRadius( 70000 ) @@ -150,7 +150,7 @@ function AI_A2A_GCI:onafterStart( AIIntercept, From, Event, To ) end ---- Evaluate the attack and create an AttackUnitTask list. +--- Evaluate the attack and create an AttackUnitTask list. -- @param #AI_A2A_GCI self -- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack. -- @param Wrappper.Group#GROUP DefenderGroup The group of defenders. @@ -165,14 +165,10 @@ function AI_A2A_GCI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageA self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } ) if AttackUnit:IsAlive() and AttackUnit:IsAir() then -- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition() - -- Maybe the detected set also contains + -- Maybe the detected set also contains AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit ) end end - + return AttackUnitTasks end - - - - diff --git a/Moose Development/Moose/Actions/Act_Account.lua b/Moose Development/Moose/Actions/Act_Account.lua index b0d26c9ea..835b52871 100644 --- a/Moose Development/Moose/Actions/Act_Account.lua +++ b/Moose Development/Moose/Actions/Act_Account.lua @@ -1,34 +1,34 @@ --- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Wrapper.Unit}s. --- +-- -- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG) --- --- === --- +-- +-- === +-- -- @module Actions.Account -- @image MOOSE.JPG do -- ACT_ACCOUNT - + --- # @{#ACT_ACCOUNT} FSM class, extends @{Core.Fsm#FSM_PROCESS} - -- - -- ## ACT_ACCOUNT state machine: - -- + -- + -- ## ACT_ACCOUNT state machine: + -- -- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. -- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. - -- Each derived class follows exactly the same process, using the same events and following the same state transitions, + -- Each derived class follows exactly the same process, using the same events and following the same state transitions, -- but will have **different implementation behaviour** upon each event or state transition. - -- - -- ### ACT_ACCOUNT States - -- + -- + -- ### ACT_ACCOUNT States + -- -- * **Asigned**: The player is assigned. -- * **Waiting**: Waiting for an event. -- * **Report**: Reporting. -- * **Account**: Account for an event. -- * **Accounted**: All events have been accounted for, end of the process. -- * **Failed**: Failed the process. - -- - -- ### ACT_ACCOUNT Events - -- + -- + -- ### ACT_ACCOUNT Events + -- -- * **Start**: Start the process. -- * **Wait**: Wait for an event. -- * **Report**: Report the status of the accounting. @@ -36,32 +36,32 @@ do -- ACT_ACCOUNT -- * **More**: More targets. -- * **NoMore (*)**: No more targets. -- * **Fail (*)**: The action process has failed. - -- + -- -- (*) End states of the process. - -- + -- -- ### ACT_ACCOUNT state transition methods: - -- + -- -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. -- There are 2 moments when state transition methods will be called by the state machine: - -- - -- * **Before** the state transition. - -- The state transition method needs to start with the name **OnBefore + the name of the state**. + -- + -- * **Before** the state transition. + -- The state transition method needs to start with the name **OnBefore + the name of the state**. -- If the state transition method returns false, then the processing of the state transition will not be done! - -- If you want to change the behaviour of the AIControllable at this event, return false, + -- If you want to change the behaviour of the AIControllable at this event, return false, -- but then you'll need to specify your own logic using the AIControllable! - -- - -- * **After** the state transition. - -- The state transition method needs to start with the name **OnAfter + the name of the state**. + -- + -- * **After** the state transition. + -- The state transition method needs to start with the name **OnAfter + the name of the state**. -- These state transition methods need to provide a return value, which is specified at the function description. - -- + -- -- @type ACT_ACCOUNT -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Core.Fsm#FSM_PROCESS - ACT_ACCOUNT = { + ACT_ACCOUNT = { ClassName = "ACT_ACCOUNT", TargetSetUnit = nil, } - + --- Creates a new DESTROY process. -- @param #ACT_ACCOUNT self -- @return #ACT_ACCOUNT @@ -69,7 +69,7 @@ do -- ACT_ACCOUNT -- Inherits from BASE local self = BASE:Inherit( self, FSM_PROCESS:New() ) -- Core.Fsm#FSM_PROCESS - + self:AddTransition( "Assigned", "Start", "Waiting" ) self:AddTransition( "*", "Wait", "Waiting" ) self:AddTransition( "*", "Report", "Report" ) @@ -79,16 +79,16 @@ do -- ACT_ACCOUNT self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "More", "Wait" ) self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "NoMore", "Accounted" ) self:AddTransition( "*", "Fail", "Failed" ) - + self:AddEndState( "Failed" ) - - self:SetStartState( "Assigned" ) - + + self:SetStartState( "Assigned" ) + return self end --- Process Events - + --- StateMachine callback function -- @param #ACT_ACCOUNT self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -104,7 +104,7 @@ do -- ACT_ACCOUNT self:__Wait( 1 ) end - + --- StateMachine callback function -- @param #ACT_ACCOUNT self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -112,17 +112,17 @@ do -- ACT_ACCOUNT -- @param #string From -- @param #string To function ACT_ACCOUNT:onenterWaiting( ProcessUnit, From, Event, To ) - + if self.DisplayCount >= self.DisplayInterval then self:Report() self.DisplayCount = 1 else self.DisplayCount = self.DisplayCount + 1 end - + return true -- Process always the event. end - + --- StateMachine callback function -- @param #ACT_ACCOUNT self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -130,30 +130,30 @@ do -- ACT_ACCOUNT -- @param #string From -- @param #string To function ACT_ACCOUNT:onafterEvent( ProcessUnit, From, Event, To, Event ) - + self:__NoMore( 1 ) end - + end -- ACT_ACCOUNT do -- ACT_ACCOUNT_DEADS --- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Core.Fsm.Account#ACT_ACCOUNT} - -- + -- -- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units. -- The process is given a @{Set} of units that will be tracked upon successful destruction. -- The process will end after each target has been successfully destroyed. -- Each successful dead will trigger an Account state transition that can be scored, modified or administered. - -- - -- + -- + -- -- ## ACT_ACCOUNT_DEADS constructor: - -- + -- -- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object. - -- + -- -- @type ACT_ACCOUNT_DEADS -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends #ACT_ACCOUNT - ACT_ACCOUNT_DEADS = { + ACT_ACCOUNT_DEADS = { ClassName = "ACT_ACCOUNT_DEADS", } @@ -165,24 +165,24 @@ do -- ACT_ACCOUNT_DEADS function ACT_ACCOUNT_DEADS:New() -- Inherits from BASE local self = BASE:Inherit( self, ACT_ACCOUNT:New() ) -- #ACT_ACCOUNT_DEADS - + self.DisplayInterval = 30 self.DisplayCount = 30 self.DisplayMessage = true self.DisplayTime = 10 -- 10 seconds is the default self.DisplayCategory = "HQ" -- Targets is the default display category - + return self end - + function ACT_ACCOUNT_DEADS:Init( FsmAccount ) - - self.Task = self:GetTask() + + self.Task = self:GetTask() self.TaskName = self.Task:GetName() end --- Process Events - + --- StateMachine callback function -- @param #ACT_ACCOUNT_DEADS self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -190,12 +190,12 @@ do -- ACT_ACCOUNT_DEADS -- @param #string From -- @param #string To function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To ) - + local MessageText = "Your group with assigned " .. self.TaskName .. " task has " .. Task.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed." self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information ) end - - + + --- StateMachine callback function -- @param #ACT_ACCOUNT_DEADS self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -206,7 +206,7 @@ do -- ACT_ACCOUNT_DEADS -- @param Core.Event#EVENTDATA EventData function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To, EventData ) self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } ) - + if Task.TargetSetUnit:FindUnit( EventData.IniUnitName ) then local PlayerName = ProcessUnit:GetPlayerName() local PlayerHit = self.PlayerHits and self.PlayerHits[EventData.IniUnitName] @@ -228,14 +228,14 @@ do -- ACT_ACCOUNT_DEADS -- @param Core.Event#EVENTDATA EventData function ACT_ACCOUNT_DEADS:onenterAccountForPlayer( ProcessUnit, Task, From, Event, To, EventData ) self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } ) - + local TaskGroup = ProcessUnit:GetGroup() Task.TargetSetUnit:Remove( EventData.IniUnitName ) - + local MessageText = "You have destroyed a target.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information ) - + local PlayerName = ProcessUnit:GetPlayerName() Task:AddProgress( PlayerName, "Destroyed " .. EventData.IniTypeName, timer.getTime(), 1 ) @@ -256,10 +256,10 @@ do -- ACT_ACCOUNT_DEADS -- @param Core.Event#EVENTDATA EventData function ACT_ACCOUNT_DEADS:onenterAccountForOther( ProcessUnit, Task, From, Event, To, EventData ) self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } ) - + local TaskGroup = ProcessUnit:GetGroup() Task.TargetSetUnit:Remove( EventData.IniUnitName ) - + local MessageText = "One of the task targets has been destroyed.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information ) @@ -270,9 +270,9 @@ do -- ACT_ACCOUNT_DEADS end end - + --- DCS Events - + --- @param #ACT_ACCOUNT_DEADS self -- @param Core.Event#EVENTDATA EventData function ACT_ACCOUNT_DEADS:OnEventHit( EventData ) @@ -282,8 +282,8 @@ do -- ACT_ACCOUNT_DEADS self.PlayerHits = self.PlayerHits or {} self.PlayerHits[EventData.TgtDCSUnitName] = EventData.IniPlayerName end - end - + end + --- @param #ACT_ACCOUNT_DEADS self -- @param Core.Event#EVENTDATA EventData function ACT_ACCOUNT_DEADS:onfuncEventDead( EventData ) @@ -295,7 +295,7 @@ do -- ACT_ACCOUNT_DEADS end --- DCS Events - + --- @param #ACT_ACCOUNT_DEADS self -- @param Core.Event#EVENTDATA EventData function ACT_ACCOUNT_DEADS:onfuncEventCrash( EventData ) diff --git a/Moose Development/Moose/Actions/Act_Assign.lua b/Moose Development/Moose/Actions/Act_Assign.lua index 9a4c907db..c6748cba8 100644 --- a/Moose Development/Moose/Actions/Act_Assign.lua +++ b/Moose Development/Moose/Actions/Act_Assign.lua @@ -1,82 +1,82 @@ --- (SP) (MP) (FSM) Accept or reject process for player (task) assignments. --- +-- -- === --- +-- -- # @{#ACT_ASSIGN} FSM template class, extends @{Core.Fsm#FSM_PROCESS} --- +-- -- ## ACT_ASSIGN state machine: --- +-- -- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. -- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. --- Each derived class follows exactly the same process, using the same events and following the same state transitions, +-- Each derived class follows exactly the same process, using the same events and following the same state transitions, -- but will have **different implementation behaviour** upon each event or state transition. --- +-- -- ### ACT_ASSIGN **Events**: --- +-- -- These are the events defined in this class: --- +-- -- * **Start**: Start the tasking acceptance process. -- * **Assign**: Assign the task. -- * **Reject**: Reject the task.. --- +-- -- ### ACT_ASSIGN **Event methods**: --- +-- -- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. -- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine: --- +-- -- * **Immediate**: The event method has exactly the name of the event. --- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. --- +-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. +-- -- ### ACT_ASSIGN **States**: --- +-- -- * **UnAssigned**: The player has not accepted the task. -- * **Assigned (*)**: The player has accepted the task. -- * **Rejected (*)**: The player has not accepted the task. -- * **Waiting**: The process is awaiting player feedback. -- * **Failed (*)**: The process has failed. --- +-- -- (*) End states of the process. --- +-- -- ### ACT_ASSIGN state transition methods: --- +-- -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. -- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Before** the state transition. --- The state transition method needs to start with the name **OnBefore + the name of the state**. +-- +-- * **Before** the state transition. +-- The state transition method needs to start with the name **OnBefore + the name of the state**. -- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, +-- If you want to change the behaviour of the AIControllable at this event, return false, -- but then you'll need to specify your own logic using the AIControllable! --- --- * **After** the state transition. --- The state transition method needs to start with the name **OnAfter + the name of the state**. +-- +-- * **After** the state transition. +-- The state transition method needs to start with the name **OnAfter + the name of the state**. -- These state transition methods need to provide a return value, which is specified at the function description. --- +-- -- === --- +-- -- # 1) @{#ACT_ASSIGN_ACCEPT} class, extends @{Core.Fsm.Assign#ACT_ASSIGN} --- +-- -- The ACT_ASSIGN_ACCEPT class accepts by default a task for a player. No player intervention is allowed to reject the task. --- +-- -- ## 1.1) ACT_ASSIGN_ACCEPT constructor: --- +-- -- * @{#ACT_ASSIGN_ACCEPT.New}(): Creates a new ACT_ASSIGN_ACCEPT object. --- +-- -- === --- +-- -- # 2) @{#ACT_ASSIGN_MENU_ACCEPT} class, extends @{Core.Fsm.Assign#ACT_ASSIGN} --- +-- -- The ACT_ASSIGN_MENU_ACCEPT class accepts a task when the player accepts the task through an added menu option. -- This assignment type is useful to conditionally allow the player to choose whether or not he would accept the task. -- The assignment type also allows to reject the task. --- +-- -- ## 2.1) ACT_ASSIGN_MENU_ACCEPT constructor: -- ----------------------------------------- --- +-- -- * @{#ACT_ASSIGN_MENU_ACCEPT.New}(): Creates a new ACT_ASSIGN_MENU_ACCEPT object. --- +-- -- === --- +-- -- @module Actions.Assign -- @image MOOSE.JPG @@ -89,11 +89,11 @@ do -- ACT_ASSIGN -- @field Wrapper.Unit#UNIT ProcessUnit -- @field Core.Zone#ZONE_BASE TargetZone -- @extends Core.Fsm#FSM_PROCESS - ACT_ASSIGN = { + ACT_ASSIGN = { ClassName = "ACT_ASSIGN", } - - + + --- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted. -- @param #ACT_ASSIGN self -- @return #ACT_ASSIGN The task acceptance process. @@ -106,16 +106,16 @@ do -- ACT_ASSIGN self:AddTransition( "Waiting", "Assign", "Assigned" ) self:AddTransition( "Waiting", "Reject", "Rejected" ) self:AddTransition( "*", "Fail", "Failed" ) - + self:AddEndState( "Assigned" ) self:AddEndState( "Rejected" ) self:AddEndState( "Failed" ) - - self:SetStartState( "UnAssigned" ) - + + self:SetStartState( "UnAssigned" ) + return self end - + end -- ACT_ASSIGN @@ -128,26 +128,26 @@ do -- ACT_ASSIGN_ACCEPT -- @field Wrapper.Unit#UNIT ProcessUnit -- @field Core.Zone#ZONE_BASE TargetZone -- @extends #ACT_ASSIGN - ACT_ASSIGN_ACCEPT = { + ACT_ASSIGN_ACCEPT = { ClassName = "ACT_ASSIGN_ACCEPT", } - - + + --- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted. -- @param #ACT_ASSIGN_ACCEPT self -- @param #string TaskBriefing function ACT_ASSIGN_ACCEPT:New( TaskBriefing ) - + local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_ACCEPT self.TaskBriefing = TaskBriefing - + return self end function ACT_ASSIGN_ACCEPT:Init( FsmAssign ) - - self.TaskBriefing = FsmAssign.TaskBriefing + + self.TaskBriefing = FsmAssign.TaskBriefing end --- StateMachine callback function @@ -157,8 +157,8 @@ do -- ACT_ASSIGN_ACCEPT -- @param #string From -- @param #string To function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To ) - - self:__Assign( 1 ) + + self:__Assign( 1 ) end --- StateMachine callback function @@ -168,10 +168,10 @@ do -- ACT_ASSIGN_ACCEPT -- @param #string From -- @param #string To function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup ) - + self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) end - + end -- ACT_ASSIGN_ACCEPT @@ -183,7 +183,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT -- @field Wrapper.Unit#UNIT ProcessUnit -- @field Core.Zone#ZONE_BASE TargetZone -- @extends #ACT_ASSIGN - ACT_ASSIGN_MENU_ACCEPT = { + ACT_ASSIGN_MENU_ACCEPT = { ClassName = "ACT_ASSIGN_MENU_ACCEPT", } @@ -197,7 +197,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_MENU_ACCEPT self.TaskBriefing = TaskBriefing - + return self end @@ -207,12 +207,12 @@ do -- ACT_ASSIGN_MENU_ACCEPT -- @param #string TaskBriefing -- @return #ACT_ASSIGN_MENU_ACCEPT self function ACT_ASSIGN_MENU_ACCEPT:Init( TaskBriefing ) - + self.TaskBriefing = TaskBriefing return self end - + --- StateMachine callback function -- @param #ACT_ASSIGN_MENU_ACCEPT self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -222,30 +222,30 @@ do -- ACT_ASSIGN_MENU_ACCEPT function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To ) self:GetCommandCenter():MessageToGroup( "Task " .. self.Task:GetName() .. " has been assigned to you and your group!\nRead the briefing and use the Radio Menu (F10) / Task ... CONFIRMATION menu to accept or reject the task.\nYou have 2 minutes to accept, or the task assignment will be cancelled!", ProcessUnit:GetGroup(), 120 ) - - local TaskGroup = ProcessUnit:GetGroup() + + local TaskGroup = ProcessUnit:GetGroup() self.Menu = MENU_GROUP:New( TaskGroup, "Task " .. self.Task:GetName() .. " CONFIRMATION" ) self.MenuAcceptTask = MENU_GROUP_COMMAND:New( TaskGroup, "Accept task " .. self.Task:GetName(), self.Menu, self.MenuAssign, self, TaskGroup ) self.MenuRejectTask = MENU_GROUP_COMMAND:New( TaskGroup, "Reject task " .. self.Task:GetName(), self.Menu, self.MenuReject, self, TaskGroup ) - + self:__Reject( 120, TaskGroup ) end - + --- Menu function. -- @param #ACT_ASSIGN_MENU_ACCEPT self function ACT_ASSIGN_MENU_ACCEPT:MenuAssign( TaskGroup ) - + self:__Assign( -1, TaskGroup ) end - + --- Menu function. -- @param #ACT_ASSIGN_MENU_ACCEPT self function ACT_ASSIGN_MENU_ACCEPT:MenuReject( TaskGroup ) - + self:__Reject( -1, TaskGroup ) end - + --- StateMachine callback function -- @param #ACT_ASSIGN_MENU_ACCEPT self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -253,10 +253,10 @@ do -- ACT_ASSIGN_MENU_ACCEPT -- @param #string From -- @param #string To function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, Task, From, Event, To, TaskGroup ) - + self.Menu:Remove() end - + --- StateMachine callback function -- @param #ACT_ASSIGN_MENU_ACCEPT self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -265,7 +265,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT -- @param #string To function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, Task, From, Event, To, TaskGroup ) self:F( { TaskGroup = TaskGroup } ) - + self.Menu:Remove() --TODO: need to resolve this problem ... it has to do with the events ... --self.Task:UnAssignFromUnit( ProcessUnit )needs to become a callback funtion call upon the event @@ -279,7 +279,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT -- @param #string From -- @param #string To function ACT_ASSIGN_MENU_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup ) - + --self.Task:AssignToGroup( TaskGroup ) self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) end diff --git a/Moose Development/Moose/Actions/Act_Assist.lua b/Moose Development/Moose/Actions/Act_Assist.lua index f9cd5fc3a..fb0e81d6b 100644 --- a/Moose Development/Moose/Actions/Act_Assist.lua +++ b/Moose Development/Moose/Actions/Act_Assist.lua @@ -1,65 +1,65 @@ --- (SP) (MP) (FSM) Route AI or players through waypoints or to zones. --- +-- -- ## ACT_ASSIST state machine: --- +-- -- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. -- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. --- Each derived class follows exactly the same process, using the same events and following the same state transitions, +-- Each derived class follows exactly the same process, using the same events and following the same state transitions, -- but will have **different implementation behaviour** upon each event or state transition. --- +-- -- ### ACT_ASSIST **Events**: --- +-- -- These are the events defined in this class: --- +-- -- * **Start**: The process is started. -- * **Next**: The process is smoking the targets in the given zone. --- +-- -- ### ACT_ASSIST **Event methods**: --- +-- -- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. -- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine: --- +-- -- * **Immediate**: The event method has exactly the name of the event. --- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. --- +-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. +-- -- ### ACT_ASSIST **States**: --- +-- -- * **None**: The controllable did not receive route commands. -- * **AwaitSmoke (*)**: The process is awaiting to smoke the targets in the zone. -- * **Smoking (*)**: The process is smoking the targets in the zone. -- * **Failed (*)**: The process has failed. --- +-- -- (*) End states of the process. --- +-- -- ### ACT_ASSIST state transition methods: --- +-- -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. -- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Before** the state transition. --- The state transition method needs to start with the name **OnBefore + the name of the state**. +-- +-- * **Before** the state transition. +-- The state transition method needs to start with the name **OnBefore + the name of the state**. -- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, +-- If you want to change the behaviour of the AIControllable at this event, return false, -- but then you'll need to specify your own logic using the AIControllable! --- --- * **After** the state transition. --- The state transition method needs to start with the name **OnAfter + the name of the state**. +-- +-- * **After** the state transition. +-- The state transition method needs to start with the name **OnAfter + the name of the state**. -- These state transition methods need to provide a return value, which is specified at the function description. --- +-- -- === --- +-- -- # 1) @{#ACT_ASSIST_SMOKE_TARGETS_ZONE} class, extends @{Core.Fsm.Route#ACT_ASSIST} --- +-- -- The ACT_ASSIST_SMOKE_TARGETS_ZONE class implements the core functions to smoke targets in a @{Zone}. --- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour. +-- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour. -- At random intervals, a new target is smoked. --- +-- -- # 1.1) ACT_ASSIST_SMOKE_TARGETS_ZONE constructor: --- +-- -- * @{#ACT_ASSIST_SMOKE_TARGETS_ZONE.New}(): Creates a new ACT_ASSIST_SMOKE_TARGETS_ZONE object. --- +-- -- === --- +-- -- @module Actions.Assist -- @image MOOSE.JPG @@ -69,7 +69,7 @@ do -- ACT_ASSIST --- ACT_ASSIST class -- @type ACT_ASSIST -- @extends Core.Fsm#FSM_PROCESS - ACT_ASSIST = { + ACT_ASSIST = { ClassName = "ACT_ASSIST", } @@ -86,15 +86,15 @@ do -- ACT_ASSIST self:AddTransition( "Smoking", "Next", "AwaitSmoke" ) self:AddTransition( "*", "Stop", "Success" ) self:AddTransition( "*", "Fail", "Failed" ) - + self:AddEndState( "Failed" ) self:AddEndState( "Success" ) - - self:SetStartState( "None" ) + + self:SetStartState( "None" ) return self end - + --- Task Events --- StateMachine callback function @@ -104,17 +104,17 @@ do -- ACT_ASSIST -- @param #string From -- @param #string To function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To ) - + local ProcessGroup = ProcessUnit:GetGroup() local MissionMenu = self:GetMission():GetMenu( ProcessGroup ) - + local function MenuSmoke( MenuParam ) local self = MenuParam.self local SmokeColor = MenuParam.SmokeColor self.SmokeColor = SmokeColor self:__Next( 1 ) end - + self.Menu = MENU_GROUP:New( ProcessGroup, "Target acquisition", MissionMenu ) self.MenuSmokeBlue = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop blue smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Blue } ) self.MenuSmokeGreen = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop green smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Green } ) @@ -130,10 +130,10 @@ do -- ACT_ASSIST -- @param #string From -- @param #string To function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To ) - + self.Menu:Remove() -- When stopped, remove the menus end - + end do -- ACT_ASSIST_SMOKE_TARGETS_ZONE @@ -143,17 +143,17 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE -- @field Core.Set#SET_UNIT TargetSetUnit -- @field Core.Zone#ZONE_BASE TargetZone -- @extends #ACT_ASSIST - ACT_ASSIST_SMOKE_TARGETS_ZONE = { + ACT_ASSIST_SMOKE_TARGETS_ZONE = { ClassName = "ACT_ASSIST_SMOKE_TARGETS_ZONE", } - + -- function ACT_ASSIST_SMOKE_TARGETS_ZONE:_Destructor() -- self:E("_Destructor") --- +-- -- self.Menu:Remove() -- self:EventRemoveAll() -- end - + --- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. -- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self -- @param Core.Set#SET_UNIT TargetSetUnit @@ -163,29 +163,29 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE self.TargetSetUnit = TargetSetUnit self.TargetZone = TargetZone - + return self end function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( FsmSmoke ) - + self.TargetSetUnit = FsmSmoke.TargetSetUnit self.TargetZone = FsmSmoke.TargetZone end - + --- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. -- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self -- @param Core.Set#SET_UNIT TargetSetUnit -- @param Core.Zone#ZONE_BASE TargetZone -- @return #ACT_ASSIST_SMOKE_TARGETS_ZONE self function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( TargetSetUnit, TargetZone ) - + self.TargetSetUnit = TargetSetUnit self.TargetZone = TargetZone - + return self end - + --- StateMachine callback function -- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit @@ -193,7 +193,7 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE -- @param #string From -- @param #string To function ACT_ASSIST_SMOKE_TARGETS_ZONE:onenterSmoking( ProcessUnit, From, Event, To ) - + self.TargetSetUnit:ForEachUnit( --- @param Wrapper.Unit#UNIT SmokeUnit function( SmokeUnit ) @@ -203,12 +203,12 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE if SmokeUnit:IsAlive() then SmokeUnit:Smoke( self.SmokeColor, 150 ) end - end, {}, math.random( 10, 60 ) + end, {}, math.random( 10, 60 ) ) end end ) - + end - -end \ No newline at end of file + +end diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index d719f1f7b..f2c5ebcda 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -1,20 +1,20 @@ --- (SP) (MP) (FSM) Route AI or players through waypoints or to zones. --- +-- -- === --- +-- -- # @{#ACT_ROUTE} FSM class, extends @{Core.Fsm#FSM_PROCESS} --- +-- -- ## ACT_ROUTE state machine: --- +-- -- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. -- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below. --- Each derived class follows exactly the same process, using the same events and following the same state transitions, +-- Each derived class follows exactly the same process, using the same events and following the same state transitions, -- but will have **different implementation behaviour** upon each event or state transition. --- +-- -- ### ACT_ROUTE **Events**: --- +-- -- These are the events defined in this class: --- +-- -- * **Start**: The process is started. The process will go into the Report state. -- * **Report**: The process is reporting to the player the route to be followed. -- * **Route**: The process is routing the controllable. @@ -22,56 +22,56 @@ -- * **Arrive**: The controllable has arrived at a route point. -- * **More**: There are more route points that need to be followed. The process will go back into the Report state. -- * **NoMore**: There are no more route points that need to be followed. The process will go into the Success state. --- +-- -- ### ACT_ROUTE **Event methods**: --- +-- -- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process. -- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine: --- +-- -- * **Immediate**: The event method has exactly the name of the event. --- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. --- +-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed. +-- -- ### ACT_ROUTE **States**: --- +-- -- * **None**: The controllable did not receive route commands. -- * **Arrived (*)**: The controllable has arrived at a route point. -- * **Aborted (*)**: The controllable has aborted the route path. -- * **Routing**: The controllable is understay to the route point. -- * **Pausing**: The process is pausing the routing. AI air will go into hover, AI ground will stop moving. Players can fly around. --- * **Success (*)**: All route points were reached. +-- * **Success (*)**: All route points were reached. -- * **Failed (*)**: The process has failed. --- +-- -- (*) End states of the process. --- +-- -- ### ACT_ROUTE state transition methods: --- +-- -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. -- There are 2 moments when state transition methods will be called by the state machine: --- --- * **Before** the state transition. --- The state transition method needs to start with the name **OnBefore + the name of the state**. +-- +-- * **Before** the state transition. +-- The state transition method needs to start with the name **OnBefore + the name of the state**. -- If the state transition method returns false, then the processing of the state transition will not be done! --- If you want to change the behaviour of the AIControllable at this event, return false, +-- If you want to change the behaviour of the AIControllable at this event, return false, -- but then you'll need to specify your own logic using the AIControllable! --- --- * **After** the state transition. --- The state transition method needs to start with the name **OnAfter + the name of the state**. +-- +-- * **After** the state transition. +-- The state transition method needs to start with the name **OnAfter + the name of the state**. -- These state transition methods need to provide a return value, which is specified at the function description. --- +-- -- === --- +-- -- # 1) @{#ACT_ROUTE_ZONE} class, extends @{Core.Fsm.Route#ACT_ROUTE} --- +-- -- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Wrapper.Controllable} player @{Wrapper.Unit} to a @{Zone}. --- The player receives on perioding times messages with the coordinates of the route to follow. +-- The player receives on perioding times messages with the coordinates of the route to follow. -- Upon arrival at the zone, a confirmation of arrival is sent, and the process will be ended. --- +-- -- # 1.1) ACT_ROUTE_ZONE constructor: --- +-- -- * @{#ACT_ROUTE_ZONE.New}(): Creates a new ACT_ROUTE_ZONE object. --- +-- -- === --- +-- -- @module Actions.Route -- @image MOOSE.JPG @@ -85,11 +85,11 @@ do -- ACT_ROUTE -- @field Core.Zone#ZONE_BASE Zone -- @field Core.Point#COORDINATE Coordinate -- @extends Core.Fsm#FSM_PROCESS - ACT_ROUTE = { + ACT_ROUTE = { ClassName = "ACT_ROUTE", } - - + + --- Creates a new routing state machine. The process will route a CLIENT to a ZONE until the CLIENT is within that ZONE. -- @param #ACT_ROUTE self -- @return #ACT_ROUTE self @@ -97,7 +97,7 @@ do -- ACT_ROUTE -- Inherits from BASE local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ROUTE" ) ) -- Core.Fsm#FSM_PROCESS - + self:AddTransition( "*", "Reset", "None" ) self:AddTransition( "None", "Start", "Routing" ) self:AddTransition( "*", "Report", "*" ) @@ -109,23 +109,23 @@ do -- ACT_ROUTE self:AddTransition( "*", "Fail", "Failed" ) self:AddTransition( "", "", "" ) self:AddTransition( "", "", "" ) - + self:AddEndState( "Arrived" ) self:AddEndState( "Failed" ) self:AddEndState( "Cancelled" ) - + self:SetStartState( "None" ) - - self:SetRouteMode( "C" ) - + + self:SetRouteMode( "C" ) + return self end - + --- Set a Cancel Menu item. -- @param #ACT_ROUTE self -- @return #ACT_ROUTE function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime, MenuTag ) - + self.CancelMenuGroupCommand = MENU_GROUP_COMMAND:New( MenuGroup, MenuText, @@ -135,47 +135,47 @@ do -- ACT_ROUTE ):SetTime( MenuTime ):SetTag( MenuTag ) ParentMenu:SetTime( MenuTime ) - + ParentMenu:Remove( MenuTime, MenuTag ) return self end - + --- Set the route mode. -- There are 2 route modes supported: - -- + -- -- * SetRouteMode( "B" ): Route mode is Bearing and Range. -- * SetRouteMode( "C" ): Route mode is LL or MGRS according coordinate system setup. - -- + -- -- @param #ACT_ROUTE self -- @return #ACT_ROUTE function ACT_ROUTE:SetRouteMode( RouteMode ) - + self.RouteMode = RouteMode - return self + return self end - + --- Get the routing text to be displayed. -- The route mode determines the text displayed. -- @param #ACT_ROUTE self -- @param Wrapper.Unit#UNIT Controllable -- @return #string function ACT_ROUTE:GetRouteText( Controllable ) - + local RouteText = "" local Coordinate = nil -- Core.Point#COORDINATE - + if self.Coordinate then Coordinate = self.Coordinate end - + if self.Zone then Coordinate = self.Zone:GetPointVec3( self.Altitude ) Coordinate:SetHeading( self.Heading ) end - + local Task = self:GetTask() -- This is to dermine that the coordinates are for a specific task mode (A2A or A2G). local CC = self:GetTask():GetMission():GetCommandCenter() @@ -209,7 +209,7 @@ do -- ACT_ROUTE return RouteText end - + function ACT_ROUTE:MenuCancel() self:F("Cancelled") self.CancelMenuGroupCommand:Remove() @@ -225,11 +225,11 @@ do -- ACT_ROUTE -- @param #string From -- @param #string To function ACT_ROUTE:onafterStart( ProcessUnit, From, Event, To ) - + self:__Route( 1 ) end - + --- Check if the controllable has arrived. -- @param #ACT_ROUTE self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -237,7 +237,7 @@ do -- ACT_ROUTE function ACT_ROUTE:onfuncHasArrived( ProcessUnit ) return false end - + --- StateMachine callback function -- @param #ACT_ROUTE self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -245,7 +245,7 @@ do -- ACT_ROUTE -- @param #string From -- @param #string To function ACT_ROUTE:onbeforeRoute( ProcessUnit, From, Event, To ) - + if ProcessUnit:IsAlive() then local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic if self.DisplayCount >= self.DisplayInterval then @@ -257,18 +257,18 @@ do -- ACT_ROUTE else self.DisplayCount = self.DisplayCount + 1 end - + if HasArrived then self:__Arrive( 1 ) else self:__Route( 1 ) end - + return HasArrived -- if false, then the event will not be executed... end - + return false - + end end -- ACT_ROUTE @@ -280,12 +280,12 @@ do -- ACT_ROUTE_POINT -- @type ACT_ROUTE_POINT -- @field Tasking.Task#TASK TASK -- @extends #ACT_ROUTE - ACT_ROUTE_POINT = { + ACT_ROUTE_POINT = { ClassName = "ACT_ROUTE_POINT", } - --- Creates a new routing state machine. + --- Creates a new routing state machine. -- The task will route a controllable to a Coordinate until the controllable is within the Range. -- @param #ACT_ROUTE_POINT self -- @param Core.Point#COORDINATE The Coordinate to Target. @@ -296,29 +296,29 @@ do -- ACT_ROUTE_POINT self.Coordinate = Coordinate self.Range = Range or 0 - + self.DisplayInterval = 30 self.DisplayCount = 30 self.DisplayMessage = true self.DisplayTime = 10 -- 10 seconds is the default - + return self end - - --- Creates a new routing state machine. + + --- Creates a new routing state machine. -- The task will route a controllable to a Coordinate until the controllable is within the Range. -- @param #ACT_ROUTE_POINT self function ACT_ROUTE_POINT:Init( FsmRoute ) - + self.Coordinate = FsmRoute.Coordinate self.Range = FsmRoute.Range or 0 - + self.DisplayInterval = 30 self.DisplayCount = 30 self.DisplayMessage = true self.DisplayTime = 10 -- 10 seconds is the default self:SetStartState("None") - end + end --- Set Coordinate -- @param #ACT_ROUTE_POINT self @@ -326,7 +326,7 @@ do -- ACT_ROUTE_POINT function ACT_ROUTE_POINT:SetCoordinate( Coordinate ) self:F2( { Coordinate } ) self.Coordinate = Coordinate - end + end --- Get Coordinate -- @param #ACT_ROUTE_POINT self @@ -334,7 +334,7 @@ do -- ACT_ROUTE_POINT function ACT_ROUTE_POINT:GetCoordinate() self:F2( { self.Coordinate } ) return self.Coordinate - end + end --- Set Range around Coordinate -- @param #ACT_ROUTE_POINT self @@ -342,16 +342,16 @@ do -- ACT_ROUTE_POINT function ACT_ROUTE_POINT:SetRange( Range ) self:F2( { Range } ) self.Range = Range or 10000 - end - + end + --- Get Range around Coordinate -- @param #ACT_ROUTE_POINT self -- @return #number The Range to consider the arrival. Default is 10000 meters. function ACT_ROUTE_POINT:GetRange() self:F2( { self.Range } ) return self.Range - end - + end + --- Method override to check if the controllable has arrived. -- @param #ACT_ROUTE_POINT self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -360,7 +360,7 @@ do -- ACT_ROUTE_POINT if ProcessUnit:IsAlive() then local Distance = self.Coordinate:Get2DDistance( ProcessUnit:GetCoordinate() ) - + if Distance <= self.Range then local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", you have arrived." self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information ) @@ -370,9 +370,9 @@ do -- ACT_ROUTE_POINT return false end - + --- Task Events - + --- StateMachine callback function -- @param #ACT_ROUTE_POINT self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -380,9 +380,9 @@ do -- ACT_ROUTE_POINT -- @param #string From -- @param #string To function ACT_ROUTE_POINT:onafterReport( ProcessUnit, From, Event, To ) - + local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", " .. self:GetRouteText( ProcessUnit ) - + self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update ) end @@ -397,7 +397,7 @@ do -- ACT_ROUTE_ZONE -- @field Wrapper.Unit#UNIT ProcessUnit -- @field Core.Zone#ZONE_BASE Zone -- @extends #ACT_ROUTE - ACT_ROUTE_ZONE = { + ACT_ROUTE_ZONE = { ClassName = "ACT_ROUTE_ZONE", } @@ -409,25 +409,25 @@ do -- ACT_ROUTE_ZONE local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE self.Zone = Zone - + self.DisplayInterval = 30 self.DisplayCount = 30 self.DisplayMessage = true self.DisplayTime = 10 -- 10 seconds is the default - + return self end - + function ACT_ROUTE_ZONE:Init( FsmRoute ) - + self.Zone = FsmRoute.Zone - + self.DisplayInterval = 30 self.DisplayCount = 30 self.DisplayMessage = true self.DisplayTime = 10 -- 10 seconds is the default - end - + end + --- Set Zone -- @param #ACT_ROUTE_ZONE self -- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to. @@ -437,14 +437,14 @@ do -- ACT_ROUTE_ZONE self.Zone = Zone self.Altitude = Altitude self.Heading = Heading - end + end --- Get Zone -- @param #ACT_ROUTE_ZONE self -- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to. function ACT_ROUTE_ZONE:GetZone() - return self.Zone - end + return self.Zone + end --- Method override to check if the controllable has arrived. -- @param #ACT_ROUTE self @@ -459,9 +459,9 @@ do -- ACT_ROUTE_ZONE return ProcessUnit:IsInZone( self.Zone ) end - + --- Task Events - + --- StateMachine callback function -- @param #ACT_ROUTE_ZONE self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -470,7 +470,7 @@ do -- ACT_ROUTE_ZONE -- @param #string To function ACT_ROUTE_ZONE:onafterReport( ProcessUnit, From, Event, To ) self:F( { ProcessUnit = ProcessUnit } ) - + local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", " .. self:GetRouteText( ProcessUnit ) self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update ) end