--- **Tasking** - Dynamically allocates A2A tasks to human players, based on detected airborne targets through an EWR network. -- -- **Features:** -- -- * Dynamically assign tasks to human players based on detected targets. -- * Dynamically change the tasks as the tactical situation evolves during the mission. -- * Dynamically assign (CAP) Control Air Patrols tasks for human players to perform CAP. -- * Dynamically assign (GCI) Ground Control Intercept tasks for human players to perform GCI. -- * Dynamically assign Engage tasks for human players to engage on close-by airborne bogeys. -- * Define and use an EWR (Early Warning Radar) network. -- * Define different ranges to engage upon intruders. -- * Keep task achievements. -- * Score task achievements. -- -- === -- -- ### Author: **FlightControl** -- -- ### Contributions: -- -- === -- -- @module Tasking.Task_A2A_Dispatcher -- @image Task_A2A_Dispatcher.JPG do -- TASK_A2A_DISPATCHER --- TASK_A2A_DISPATCHER class. -- @type TASK_A2A_DISPATCHER -- @extends Tasking.DetectionManager#DETECTION_MANAGER --- Orchestrates the dynamic dispatching of tasks upon groups of detected units determined a @{Core.Set} of EWR installation groups. -- -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia3.JPG) -- -- The EWR will detect units, will group them, and will dispatch @{Tasking.Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. -- Find a summary below describing for which situation a task type is created: -- -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia9.JPG) -- -- * **INTERCEPT Task**: Is created when the target is known, is detected and within a danger zone, and there is no friendly airborne in range. -- * **SWEEP Task**: Is created when the target is unknown, was detected and the last position is only known, and within a danger zone, and there is no friendly airborne in range. -- * **ENGAGE Task**: Is created when the target is known, is detected and within a danger zone, and there is a friendly airborne in range, that will receive this task. -- -- ## 1. TASK\_A2A\_DISPATCHER constructor: -- -- The @{#TASK_A2A_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance. -- -- ### 1.1. Define or set the **Mission**: -- -- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter. -- -- local HQ = GROUP:FindByName( "HQ", "Bravo" ) -- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) -- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED ) -- -- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission. -- Create the MISSION object, and hook it under the command center. -- -- ### 1.2. Build a set of the groups seated by human players: -- -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia6.JPG) -- -- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into. -- -- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart() -- -- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission. -- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available. -- -- ### 1.3. Define the **EWR network**: -- -- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third 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\TASK_A2A_DISPATCHER\Dia5.JPG) -- -- 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 -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. -- -- ![Banner Image](..\Presentations\TASK_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. -- -- 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 TASK\_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, -- 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. -- -- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart() -- -- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 ) -- EWRDetection:SetFriendliesRange( 10000 ) -- EWRDetection:SetRefreshTimeInterval(30) -- -- -- Setup the A2A dispatcher, and initialize it. -- A2ADispatcher = TASK_A2A_DISPATCHER:New( Mission, AttackGroups, EWRDetection ) -- -- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**. -- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set. -- **EWRSet** 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 **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is chosen, which is 6 km. -- The **EWRDetection** object is then passed to the @{#TASK_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism. -- -- ### 2. Define the detected **target grouping radius**: -- -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia8.JPG) -- -- 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. -- 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 radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission. -- -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia11.JPG) -- -- So, 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, -- 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. -- -- ## 4. Set **Scoring** and **Messages**: -- -- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Tasking.Task} dispatched by the TASK\_A2A\_DISPATCHER. -- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_, -- when the player reaches certain achievements in the task. -- -- The prototype to handle the **Assign** event needs to be developed as follows: -- -- TaskDispatcher = TASK_A2A_DISPATCHER:New( ... ) -- -- -- @param #TaskDispatcher self -- -- @param #string From Contains the name of the state from where the Event was triggered. -- -- @param #string Event Contains the name of the event that was triggered. In this case Assign. -- -- @param #string To Contains the name of the state that will be transitioned to. -- -- @param Tasking.Task_A2A#TASK_A2A Task The Task object, which is any derived object from TASK_A2A. -- -- @param Wrapper.Unit#UNIT TaskUnit The Unit or Client that contains the Player. -- -- @param #string PlayerName The name of the Player that joined the TaskUnit. -- function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) -- Task:SetScoreOnProgress( PlayerName, 20, TaskUnit ) -- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit ) -- Task:SetScoreOnFail( PlayerName, -100, TaskUnit ) -- end -- -- The **OnAfterAssign** method (function) is added to the TaskDispatcher object. -- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher. -- So, this method will be called only **ONCE** when a player joins a unit in scope of the task. -- -- The TASK class implements various methods to additional **set scoring** for player achievements: -- -- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task. -- Examples of **task progress** can be destroying units, arriving at zones etc. -- -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state. -- This means the **task has been successfully completed**. -- -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state. -- This means the **task has not been successfully completed**, and the scores must be given with a negative value! -- -- # Developer Note -- -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE -- Therefore, this class is considered to be deprecated -- -- @field #TASK_A2A_DISPATCHER TASK_A2A_DISPATCHER = { ClassName = "TASK_A2A_DISPATCHER", Mission = nil, Detection = nil, Tasks = {}, SweepZones = {}, } --- TASK_A2A_DISPATCHER constructor. -- @param #TASK_A2A_DISPATCHER self -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. -- @param Core.Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. -- @return #TASK_A2A_DISPATCHER self function TASK_A2A_DISPATCHER:New( Mission, SetGroup, Detection ) -- Inherits from DETECTION_MANAGER local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2A_DISPATCHER self.Detection = Detection self.Mission = Mission self.FlashNewTask = false -- TODO: Check detection through radar. self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER ) self.Detection:InitDetectRadar( true ) self.Detection:SetRefreshTimeInterval( 30 ) self:AddTransition( "Started", "Assign", "Started" ) --- OnAfter Transition Handler for Event Assign. -- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign -- @param #TASK_A2A_DISPATCHER self -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. -- @param Tasking.Task_A2A#TASK_A2A Task -- @param Wrapper.Unit#UNIT TaskUnit -- @param #string PlayerName self:__Start( 5 ) return self end --- Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission. -- So, 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). -- An ENGAGE task will be created for those pilots. -- 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. -- @param #TASK_A2A_DISPATCHER self -- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target. -- @return #TASK_A2A_DISPATCHER -- @usage -- -- -- Set 50km as the radius to engage any target by airborne friendlies. -- TaskA2ADispatcher:SetEngageRadius( 50000 ) -- -- -- Set 100km as the radius to engage any target by airborne friendlies. -- TaskA2ADispatcher:SetEngageRadius() -- 100000 is the default value. -- function TASK_A2A_DISPATCHER:SetEngageRadius( EngageRadius ) self.Detection:SetFriendliesRange( EngageRadius or 100000 ) return self end --- Set flashing player messages on or off -- @param #TASK_A2A_DISPATCHER self -- @param #boolean onoff Set messages on (true) or off (false) function TASK_A2A_DISPATCHER:SetSendMessages( onoff ) self.FlashNewTask = onoff end --- Creates an INTERCEPT task when there are targets for it. -- @param #TASK_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. function TASK_A2A_DISPATCHER:EvaluateINTERCEPT( DetectedItem ) self:F( { DetectedItem.ItemID } ) local DetectedSet = DetectedItem.Set local DetectedZone = DetectedItem.Zone -- Check if there is at least one UNIT in the DetectedSet is visible. if DetectedItem.IsDetected == true then -- Here we're doing something advanced... We're copying the DetectedSet. local TargetSetUnit = SET_UNIT:New() TargetSetUnit:SetDatabase( DetectedSet ) TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. return TargetSetUnit end return nil end --- Creates an SWEEP task when there are targets for it. -- @param #TASK_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. function TASK_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem ) self:F( { DetectedItem.ItemID } ) local DetectedSet = DetectedItem.Set local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove? if DetectedItem.IsDetected == false then -- Here we're doing something advanced... We're copying the DetectedSet. local TargetSetUnit = SET_UNIT:New() TargetSetUnit:SetDatabase( DetectedSet ) TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. return TargetSetUnit end return nil end --- Creates an ENGAGE task when there are human friendlies airborne near the targets. -- @param #TASK_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. function TASK_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem ) self:F( { DetectedItem.ItemID } ) local DetectedSet = DetectedItem.Set local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove? local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem ) -- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone. if PlayersCount > 0 and DetectedItem.IsDetected == true then -- Here we're doing something advanced... We're copying the DetectedSet. local TargetSetUnit = SET_UNIT:New() TargetSetUnit:SetDatabase( DetectedSet ) TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. return TargetSetUnit end return nil end --- Evaluates the removal of the Task from the Mission. -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". -- @param #TASK_A2A_DISPATCHER self -- @param Tasking.Mission#MISSION Mission -- @param Tasking.Task#TASK Task -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. -- @param #boolean DetectedItemID -- @param #boolean DetectedItemChange -- @return Tasking.Task#TASK function TASK_A2A_DISPATCHER:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, DetectedItemIndex, DetectedItemChanged ) if Task then if Task:IsStatePlanned() then local TaskName = Task:GetName() local TaskType = TaskName:match( "(%u+)%.%d+" ) self:T2( { TaskType = TaskType } ) local Remove = false local IsPlayers = Detection:IsPlayersNearBy( DetectedItem ) if TaskType == "ENGAGE" then if IsPlayers == false then Remove = true end end if TaskType == "INTERCEPT" then if IsPlayers == true then Remove = true end if DetectedItem.IsDetected == false then Remove = true end end if TaskType == "SWEEP" then if DetectedItem.IsDetected == true then Remove = true end end local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT -- DetectedSet:Flush( self ) -- self:F( { DetectedSetCount = DetectedSet:Count() } ) if DetectedSet:Count() == 0 then Remove = true end if DetectedItemChanged == true or Remove then Task = self:RemoveTask( DetectedItemIndex ) end end end return Task end --- Calculates which friendlies are nearby the area -- @param #TASK_A2A_DISPATCHER self -- @param DetectedItem -- @return #number, Tasking.CommandCenter#REPORT function TASK_A2A_DISPATCHER:GetFriendliesNearBy( DetectedItem ) local DetectedSet = DetectedItem.Set local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE ) local FriendlyTypes = {} local FriendliesCount = 0 if FriendlyUnitsNearBy then local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G() for FriendlyUnitName, FriendlyUnitData in pairs( FriendlyUnitsNearBy ) do local FriendlyUnit = FriendlyUnitData -- Wrapper.Unit#UNIT if FriendlyUnit:IsAirPlane() then local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel() FriendliesCount = FriendliesCount + 1 local FriendlyType = FriendlyUnit:GetTypeName() FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and (FriendlyTypes[FriendlyType] + 1) or 1 if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then end end end end -- self:F( { FriendliesCount = FriendliesCount } ) local FriendlyTypesReport = REPORT:New() if FriendliesCount > 0 then for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do FriendlyTypesReport:Add( string.format( "%d of %s", FriendlyTypeCount, FriendlyType ) ) end else FriendlyTypesReport:Add( "-" ) end return FriendliesCount, FriendlyTypesReport end --- Calculates which HUMAN friendlies are nearby the area -- @param #TASK_A2A_DISPATCHER self -- @param DetectedItem -- @return #number, Tasking.CommandCenter#REPORT function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem ) local DetectedSet = DetectedItem.Set local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem ) local PlayerTypes = {} local PlayersCount = 0 if PlayersNearBy then local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G() for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT local PlayerName = PlayerUnit:GetPlayerName() -- self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } ) if PlayerUnit:IsAirPlane() and PlayerName ~= nil then local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel() PlayersCount = PlayersCount + 1 local PlayerType = PlayerUnit:GetTypeName() PlayerTypes[PlayerName] = PlayerType if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then end end end end local PlayerTypesReport = REPORT:New() if PlayersCount > 0 then for PlayerName, PlayerType in pairs( PlayerTypes ) do PlayerTypesReport:Add( string.format( '"%s" in %s', PlayerName, PlayerType ) ) end else PlayerTypesReport:Add( "-" ) end return PlayersCount, PlayerTypesReport end function TASK_A2A_DISPATCHER:RemoveTask( TaskIndex ) self.Mission:RemoveTask( self.Tasks[TaskIndex] ) self.Tasks[TaskIndex] = nil end --- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}. -- @param #TASK_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. function TASK_A2A_DISPATCHER:ProcessDetected( Detection ) self:F() local AreaMsg = {} local TaskMsg = {} local ChangeMsg = {} local Mission = self.Mission if Mission:IsIDLE() or Mission:IsENGAGED() then local TaskReport = REPORT:New() -- Checking the task queue for the dispatcher, and removing any obsolete task! for TaskIndex, TaskData in pairs( self.Tasks ) do local Task = TaskData -- Tasking.Task#TASK if Task:IsStatePlanned() then local DetectedItem = Detection:GetDetectedItemByIndex( TaskIndex ) if not DetectedItem then local TaskText = Task:GetName() for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2A task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup ) end Task = self:RemoveTask( TaskIndex ) end end end -- Now that all obsolete tasks are removed, loop through the detected targets. for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT local DetectedCount = DetectedSet:Count() local DetectedZone = DetectedItem.Zone -- self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) -- DetectedSet:Flush( self ) local DetectedID = DetectedItem.ID local TaskIndex = DetectedItem.Index local DetectedItemChanged = DetectedItem.Changed local Task = self.Tasks[TaskIndex] Task = self:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, TaskIndex, DetectedItemChanged ) -- Task will be removed if it is planned and changed. -- Evaluate INTERCEPT if not Task and DetectedCount > 0 then local TargetSetUnit = self:EvaluateENGAGE( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed... if TargetSetUnit then Task = TASK_A2A_ENGAGE:New( Mission, self.SetGroup, string.format( "ENGAGE.%03d", DetectedID ), TargetSetUnit ) Task:SetDetection( Detection, DetectedItem ) Task:UpdateTaskInfo( DetectedItem ) else local TargetSetUnit = self:EvaluateINTERCEPT( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed... if TargetSetUnit then Task = TASK_A2A_INTERCEPT:New( Mission, self.SetGroup, string.format( "INTERCEPT.%03d", DetectedID ), TargetSetUnit ) Task:SetDetection( Detection, DetectedItem ) Task:UpdateTaskInfo( DetectedItem ) else local TargetSetUnit = self:EvaluateSWEEP( DetectedItem ) -- Returns a SetUnit if TargetSetUnit then Task = TASK_A2A_SWEEP:New( Mission, self.SetGroup, string.format( "SWEEP.%03d", DetectedID ), TargetSetUnit ) Task:SetDetection( Detection, DetectedItem ) Task:UpdateTaskInfo( DetectedItem ) end end end if Task then self.Tasks[TaskIndex] = Task Task:SetTargetZone( DetectedZone, DetectedItem.Coordinate.y, DetectedItem.Coordinate.Heading ) Task:SetDispatcher( self ) Mission:AddTask( Task ) function Task.OnEnterSuccess( Task, From, Event, To ) self:Success( Task ) end function Task.OnEnterCancelled( Task, From, Event, To ) self:Cancelled( Task ) end function Task.OnEnterFailed( Task, From, Event, To ) self:Failed( Task ) end function Task.OnEnterAborted( Task, From, Event, To ) self:Aborted( Task ) end TaskReport:Add( Task:GetName() ) else self:F( "This should not happen" ) end end if Task then local FriendliesCount, FriendliesReport = self:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE ) Task.TaskInfo:AddText( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ), 40, "MOD" ) local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem ) Task.TaskInfo:AddText( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ), 40, "MOD" ) end -- OK, so the tasking has been done, now delete the changes reported for the area. Detection:AcceptChanges( DetectedItem ) end -- TODO set menus using the HQ coordinator Mission:GetCommandCenter():SetMenu() local TaskText = TaskReport:Text( ", " ) for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do if (not Mission:IsGroupAssigned( TaskGroup )) and TaskText ~= "" and (self.FlashNewTask) then Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup ) end end end return true end end