diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua index 2da9be005..389be7dd7 100644 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -1,13 +1,13 @@ --- **Tasking** - The TASK_A2A models tasks for players in Air to Air engagements. --- +-- -- === --- +-- -- ### Author: **FlightControl** --- --- ### Contributions: --- +-- +-- ### Contributions: +-- -- === --- +-- -- @module Tasking.Task_A2A -- @image MOOSE.JPG @@ -35,12 +35,12 @@ do -- TASK_A2A -- * @{#TASK_A2A.SetScoreOnDestroy}(): Set a score when a target in scope of the A2A attack, has been destroyed. -- * @{#TASK_A2A.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2A attack, have been destroyed. -- * @{#TASK_A2A.SetPenaltyOnFailed}(): Set a penalty when the A2A attack has failed. - -- + -- -- @field #TASK_A2A TASK_A2A = { - ClassName = "TASK_A2A", + ClassName = "TASK_A2A" } - + --- Instantiates a new TASK_A2A. -- @param #TASK_A2A self -- @param Tasking.Mission#MISSION Mission @@ -54,51 +54,49 @@ do -- TASK_A2A function TASK_A2A:New( Mission, SetAttack, TaskName, TargetSetUnit, TaskType, TaskBriefing ) local self = BASE:Inherit( self, TASK:New( Mission, SetAttack, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2A self:F() - + self.TargetSetUnit = TargetSetUnit self.TaskType = TaskType local Fsm = self:GetUnitProcess() - Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) - Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) - Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) - + Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) + Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) + Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" ) - + Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" ) Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" ) - - Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} ) + + Fsm:AddProcess( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} ) Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" ) Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} ) Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} ) Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" ) - --- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) --- Fsm:AddTransition( "Accounted", "Success", "Success" ) + + -- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) + -- Fsm:AddTransition( "Accounted", "Success", "Success" ) Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Failed", "Fail", "Failed" ) - - ---- @param #FSM_PROCESS self + -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param #TASK_CARGO Task function Fsm:OnLeaveAssigned( TaskUnit, Task ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - + self:SelectAction() end - - --- Test + + --- Test -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_A2A#TASK_A2A Task function Fsm:onafterRouteToRendezVous( TaskUnit, Task ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) -- Determine the first Unit from the self.RendezVousSetUnit - + if Task:GetRendezVousZone( TaskUnit ) then self:__RouteToRendezVousZone( 0.1 ) else @@ -110,36 +108,36 @@ do -- TASK_A2A end end - --- Test + --- Test -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task#TASK_A2A Task function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) -- Determine the first Unit from the self.TargetSetUnit - - self:__Engage( 0.1 ) + + self:__Engage( 0.1 ) end - - --- Test + + --- Test -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task#TASK_A2A Task function Fsm:onafterEngage( TaskUnit, Task ) self:F( { self } ) self:__Account( 0.1 ) - self:__RouteToTarget(0.1 ) + self:__RouteToTarget( 0.1 ) self:__RouteToTargets( -10 ) end - - --- Test + + --- Test -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_A2A#TASK_A2A Task function Fsm:onafterRouteToTarget( TaskUnit, Task ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) -- Determine the first Unit from the self.TargetSetUnit - + if Task:GetTargetZone( TaskUnit ) then self:__RouteToTargetZone( 0.1 ) else @@ -152,8 +150,8 @@ do -- TASK_A2A self:__RouteToTargetPoint( 0.1 ) end end - - --- Test + + --- Test -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_A2A#TASK_A2A Task @@ -165,20 +163,18 @@ do -- TASK_A2A end self:__RouteToTargets( -10 ) end - + return self - + end - + --- @param #TASK_A2A self -- @param Core.Set#SET_UNIT TargetSetUnit The set of targets. function TASK_A2A:SetTargetSetUnit( TargetSetUnit ) - + self.TargetSetUnit = TargetSetUnit end - - --- @param #TASK_A2A self function TASK_A2A:GetPlannedMenuText() return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" @@ -188,34 +184,32 @@ do -- TASK_A2A -- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_A2A:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit ) - + function TASK_A2A:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit ) + local ProcessUnit = self:GetUnitProcess( TaskUnit ) - + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT ActRouteRendezVous:SetCoordinate( RendezVousCoordinate ) ActRouteRendezVous:SetRange( RendezVousRange ) end - + --- @param #TASK_A2A self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. function TASK_A2A:GetRendezVousCoordinate( TaskUnit ) - + local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange() end - - - + --- @param #TASK_A2A self -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_A2A:SetRendezVousZone( RendezVousZone, TaskUnit ) - + local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE @@ -232,18 +226,17 @@ do -- TASK_A2A local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE return ActRouteRendezVous:GetZone() end - + --- @param #TASK_A2A self -- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_A2A:SetTargetCoordinate( TargetCoordinate, TaskUnit ) - + local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT ActRouteTarget:SetCoordinate( TargetCoordinate ) end - --- @param #TASK_A2A self -- @param Wrapper.Unit#UNIT TaskUnit @@ -256,18 +249,16 @@ do -- TASK_A2A return ActRouteTarget:GetCoordinate() end - --- @param #TASK_A2A self -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_A2A:SetTargetZone( TargetZone, Altitude, Heading, TaskUnit ) - + local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE ActRouteTarget:SetZone( TargetZone, Altitude, Heading ) end - --- @param #TASK_A2A self -- @param Wrapper.Unit#UNIT TaskUnit @@ -281,45 +272,43 @@ do -- TASK_A2A end function TASK_A2A:SetGoalTotal() - + self.GoalTotal = self.TargetSetUnit:Count() end function TASK_A2A:GetGoalTotal() - + return self.GoalTotal end --- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats. -- @param #TASK_A2A self function TASK_A2A:ReportOrder( ReportGroup ) - self:UpdateTaskInfo( self.DetectedItem ) - + self:UpdateTaskInfo( self.DetectedItem ) + local Coordinate = self.TaskInfo:GetData( "Coordinate" ) local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) - + return Distance end - - + --- This method checks every 10 seconds if the goal has been reached of the task. -- @param #TASK_A2A self function TASK_A2A:onafterGoal( TaskUnit, From, Event, To ) local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT - + if TargetSetUnit:Count() == 0 then self:Success() end - + self:__Goal( -10 ) end - --- @param #TASK_A2A self function TASK_A2A:UpdateTaskInfo( DetectedItem ) if self:IsStatePlanned() or self:IsStateAssigned() then - local TargetCoordinate = DetectedItem and self.Detection:GetDetectedItemCoordinate( DetectedItem ) or self.TargetSetUnit:GetFirst():GetCoordinate() + local TargetCoordinate = DetectedItem and self.Detection:GetDetectedItemCoordinate( DetectedItem ) or self.TargetSetUnit:GetFirst():GetCoordinate() self.TaskInfo:AddTaskName( 0, "MSOD" ) self.TaskInfo:AddCoordinate( TargetCoordinate, 1, "SOD" ) @@ -343,12 +332,12 @@ do -- TASK_A2A end end self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true ) - self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true ) + self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true ) else local DetectedItemsCount = self.TargetSetUnit:Count() local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames() self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true ) - self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true ) + self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true ) end end end @@ -359,8 +348,8 @@ do -- TASK_A2A -- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center. -- @param Wrapper.Group#GROUP TaskGroup The player group. function TASK_A2A:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup ) - - if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then + + if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then return math.random( 1, 9 ) elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then local Coordinate = self.TaskInfo:GetData( "Coordinate" ) @@ -373,8 +362,7 @@ do -- TASK_A2A return 0 end -end - +end do -- TASK_A2A_INTERCEPT @@ -384,44 +372,39 @@ do -- TASK_A2A_INTERCEPT -- @extends Tasking.Task#TASK --- Defines an intercept task for a human player to be executed. - -- When enemy planes need to be intercepted by human players, use this task type to urgen the players to get out there! - -- - -- The TASK_A2A_INTERCEPT is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks + -- When enemy planes need to be intercepted by human players, use this task type to urge the players to get out there! + -- + -- The TASK_A2A_INTERCEPT is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks -- based on detected airborne enemy targets intruding friendly airspace. - -- + -- -- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is intercepting the targets. -- The task is given a name and a briefing, that is used in the menu structure and in the reporting. - -- + -- -- @field #TASK_A2A_INTERCEPT TASK_A2A_INTERCEPT = { - ClassName = "TASK_A2A_INTERCEPT", + ClassName = "TASK_A2A_INTERCEPT" } - - --- Instantiates a new TASK_A2A_INTERCEPT. -- @param #TASK_A2A_INTERCEPT self -- @param Tasking.Mission#MISSION Mission -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. - -- @param Core.Set#SET_UNIT TargetSetUnit + -- @param Core.Set#SET_UNIT TargetSetUnit -- @param #string TaskBriefing The briefing of the task. -- @return #TASK_A2A_INTERCEPT function TASK_A2A_INTERCEPT:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "INTERCEPT", TaskBriefing ) ) -- #TASK_A2A_INTERCEPT self:F() - + Mission:AddTask( self ) - - self:SetBriefing( - TaskBriefing or - "Intercept incoming intruders.\n" - ) + + self:SetBriefing( TaskBriefing or "Intercept incoming intruders.\n" ) return self end - - --- Set a score when a target in scope of the A2A attack, has been destroyed . + + --- Set a score when a target in scope of the A2A attack, has been destroyed. -- @param #TASK_A2A_INTERCEPT self -- @param #string PlayerName The name of the player. -- @param #number Score The score in points to be granted when task process has been achieved. @@ -433,7 +416,7 @@ do -- TASK_A2A_INTERCEPT local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has intercepted a target.", Score ) - + return self end @@ -449,7 +432,7 @@ do -- TASK_A2A_INTERCEPT local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScore( "Success", "All targets have been successfully intercepted!", Score ) - + return self end @@ -465,14 +448,12 @@ do -- TASK_A2A_INTERCEPT local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScore( "Failed", "The intercept has failed!", Penalty ) - + return self end - end - do -- TASK_A2A_SWEEP --- The TASK_A2A_SWEEP class @@ -484,20 +465,18 @@ do -- TASK_A2A_SWEEP -- A sweep task needs to be given when targets were detected but somehow the detection was lost. -- Most likely, these enemy planes are hidden in the mountains or are flying under radar. -- These enemy planes need to be sweeped by human players, and use this task type to urge the players to get out there and find those enemy fighters. - -- - -- The TASK_A2A_SWEEP is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create sweep tasks + -- + -- The TASK_A2A_SWEEP is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create sweep tasks -- based on detected airborne enemy targets intruding friendly airspace, for which the detection has been lost for more than 60 seconds. - -- + -- -- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is sweeping the targets. -- The task is given a name and a briefing, that is used in the menu structure and in the reporting. - -- + -- -- @field #TASK_A2A_SWEEP TASK_A2A_SWEEP = { - ClassName = "TASK_A2A_SWEEP", + ClassName = "TASK_A2A_SWEEP" } - - --- Instantiates a new TASK_A2A_SWEEP. -- @param #TASK_A2A_SWEEP self -- @param Tasking.Mission#MISSION Mission @@ -509,29 +488,26 @@ do -- TASK_A2A_SWEEP function TASK_A2A_SWEEP:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "SWEEP", TaskBriefing ) ) -- #TASK_A2A_SWEEP self:F() - + Mission:AddTask( self ) - - self:SetBriefing( - TaskBriefing or - "Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n" - ) + + self:SetBriefing( TaskBriefing or "Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n" ) return self - end + end --- @param #TASK_A2A_SWEEP self function TASK_A2A_SWEEP:onafterGoal( TaskUnit, From, Event, To ) local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT - + if TargetSetUnit:Count() == 0 then self:Success() end - + self:__Goal( -10 ) end - --- Set a score when a target in scope of the A2A attack, has been destroyed . + --- Set a score when a target in scope of the A2A attack, has been destroyed. -- @param #TASK_A2A_SWEEP self -- @param #string PlayerName The name of the player. -- @param #number Score The score in points to be granted when task process has been achieved. @@ -543,7 +519,7 @@ do -- TASK_A2A_SWEEP local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has sweeped a target.", Score ) - + return self end @@ -559,7 +535,7 @@ do -- TASK_A2A_SWEEP local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScore( "Success", "All targets have been successfully sweeped!", Score ) - + return self end @@ -575,13 +551,12 @@ do -- TASK_A2A_SWEEP local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScore( "Failed", "The sweep has failed!", Penalty ) - + return self end end - do -- TASK_A2A_ENGAGE --- The TASK_A2A_ENGAGE class @@ -591,42 +566,37 @@ do -- TASK_A2A_ENGAGE --- Defines an engage task for a human player to be executed. -- When enemy planes are close to human players, use this task type is used urge the players to get out there! - -- - -- The TASK_A2A_ENGAGE is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create engage tasks + -- + -- The TASK_A2A_ENGAGE is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create engage tasks -- based on detected airborne enemy targets intruding friendly airspace. - -- + -- -- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is engaging the targets. -- The task is given a name and a briefing, that is used in the menu structure and in the reporting. - -- + -- -- @field #TASK_A2A_ENGAGE TASK_A2A_ENGAGE = { - ClassName = "TASK_A2A_ENGAGE", + ClassName = "TASK_A2A_ENGAGE" } - - --- Instantiates a new TASK_A2A_ENGAGE. -- @param #TASK_A2A_ENGAGE self -- @param Tasking.Mission#MISSION Mission -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. - -- @param Core.Set#SET_UNIT TargetSetUnit + -- @param Core.Set#SET_UNIT TargetSetUnit -- @param #string TaskBriefing The briefing of the task. -- @return #TASK_A2A_ENGAGE self function TASK_A2A_ENGAGE:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "ENGAGE", TaskBriefing ) ) -- #TASK_A2A_ENGAGE self:F() - + Mission:AddTask( self ) - - self:SetBriefing( - TaskBriefing or - "Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n" - ) + + self:SetBriefing( TaskBriefing or "Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n" ) return self - end - + end + --- Set a score when a target in scope of the A2A attack, has been destroyed . -- @param #TASK_A2A_ENGAGE self -- @param #string PlayerName The name of the player. @@ -639,7 +609,7 @@ do -- TASK_A2A_ENGAGE local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has engaged and destroyed a target.", Score ) - + return self end @@ -655,7 +625,7 @@ do -- TASK_A2A_ENGAGE local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScore( "Success", "All targets have been successfully engaged!", Score ) - + return self end @@ -671,7 +641,7 @@ do -- TASK_A2A_ENGAGE local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScore( "Failed", "The target engagement has failed!", Penalty ) - + return self end diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index 76c7225a7..d3b67cbff 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -1,7 +1,7 @@ --- **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. @@ -11,15 +11,15 @@ -- * Define different ranges to engage upon intruders. -- * Keep task achievements. -- * Score task achievements. --- +-- -- === --- +-- -- ### Author: **FlightControl** --- --- ### Contributions: --- +-- +-- ### Contributions: +-- -- === --- +-- -- @module Tasking.Task_A2A_Dispatcher -- @image Task_A2A_Dispatcher.JPG @@ -30,72 +30,72 @@ do -- TASK_A2A_DISPATCHER -- @extends Tasking.DetectionManager#DETECTION_MANAGER --- Orchestrates the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups. - -- + -- -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia3.JPG) - -- + -- -- The EWR will detect units, will group them, and will dispatch @{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. -- + -- 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, + -- 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 ) @@ -104,50 +104,50 @@ do -- TASK_A2A_DISPATCHER -- -- -- 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 choosen, which is 6km. + -- 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. + -- 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, + -- + -- 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, + -- 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 @{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. @@ -160,32 +160,31 @@ do -- TASK_A2A_DISPATCHER -- 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. + -- + -- * @{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. + -- + -- * @{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! - -- + -- -- @field #TASK_A2A_DISPATCHER TASK_A2A_DISPATCHER = { ClassName = "TASK_A2A_DISPATCHER", Mission = nil, Detection = nil, Tasks = {}, - SweepZones = {}, + SweepZones = {} } - - + --- TASK_A2A_DISPATCHER constructor. -- @param #TASK_A2A_DISPATCHER self -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. @@ -193,22 +192,21 @@ do -- TASK_A2A_DISPATCHER -- @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 @@ -220,14 +218,13 @@ do -- TASK_A2A_DISPATCHER -- @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, + -- 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, @@ -239,27 +236,27 @@ do -- TASK_A2A_DISPATCHER -- @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 + 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 @@ -267,26 +264,25 @@ do -- TASK_A2A_DISPATCHER -- @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 @@ -294,25 +290,23 @@ do -- TASK_A2A_DISPATCHER -- @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 - 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 @@ -320,13 +314,12 @@ do -- TASK_A2A_DISPATCHER -- @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 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 @@ -334,16 +327,13 @@ do -- TASK_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 - - - --- 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 @@ -354,24 +344,24 @@ do -- TASK_A2A_DISPATCHER -- @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 @@ -380,7 +370,7 @@ do -- TASK_A2A_DISPATCHER Remove = true end end - + if TaskType == "SWEEP" then if DetectedItem.IsDetected == true then Remove = true @@ -388,18 +378,18 @@ do -- TASK_A2A_DISPATCHER end local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT - --DetectedSet:Flush( self ) - --self:F( { DetectedSetCount = DetectedSet:Count() } ) + -- 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 @@ -408,10 +398,10 @@ do -- TASK_A2A_DISPATCHER -- @param DetectedItem -- @return #number, Core.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 @@ -423,27 +413,26 @@ do -- TASK_A2A_DISPATCHER local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel() FriendliesCount = FriendliesCount + 1 local FriendlyType = FriendlyUnit:GetTypeName() - FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and ( FriendlyTypes[FriendlyType] + 1 ) or 1 + FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and (FriendlyTypes[FriendlyType] + 1) or 1 if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then end end end - + end - --self:F( { FriendliesCount = FriendliesCount } ) - + -- 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 ) ) + FriendlyTypesReport:Add( string.format( "%d of %s", FriendlyTypeCount, FriendlyType ) ) end else FriendlyTypesReport:Add( "-" ) end - - + return FriendliesCount, FriendlyTypesReport end @@ -452,10 +441,10 @@ do -- TASK_A2A_DISPATCHER -- @param DetectedItem -- @return #number, Core.CommandCenter#REPORT function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem ) - + local DetectedSet = DetectedItem.Set local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem ) - + local PlayerTypes = {} local PlayersCount = 0 @@ -464,7 +453,7 @@ do -- TASK_A2A_DISPATCHER for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT local PlayerName = PlayerUnit:GetPlayerName() - --self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } ) + -- self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } ) if PlayerUnit:IsAirPlane() and PlayerName ~= nil then local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel() PlayersCount = PlayersCount + 1 @@ -474,20 +463,19 @@ do -- TASK_A2A_DISPATCHER 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 ) ) + PlayerTypesReport:Add( string.format( '"%s" in %s', PlayerName, PlayerType ) ) end else PlayerTypesReport:Add( "-" ) end - - + return PlayersCount, PlayerTypesReport end @@ -496,24 +484,23 @@ do -- TASK_A2A_DISPATCHER 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 @@ -531,18 +518,18 @@ do -- TASK_A2A_DISPATCHER -- 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 ) - + -- 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. @@ -565,7 +552,7 @@ do -- TASK_A2A_DISPATCHER Task = TASK_A2A_SWEEP:New( Mission, self.SetGroup, string.format( "SWEEP.%03d", DetectedID ), TargetSetUnit ) Task:SetDetection( Detection, DetectedItem ) Task:UpdateTaskInfo( DetectedItem ) - end + end end end @@ -582,7 +569,7 @@ do -- TASK_A2A_DISPATCHER function Task.OnEnterCancelled( Task, From, Event, To ) self:Cancelled( Task ) end - + function Task.OnEnterFailed( Task, From, Event, To ) self:Failed( Task ) end @@ -590,38 +577,38 @@ do -- TASK_A2A_DISPATCHER function Task.OnEnterAborted( Task, From, Event, To ) self:Aborted( Task ) end - + TaskReport:Add( Task:GetName() ) else - self:F("This should not happen") + 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" ) + 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" ) + 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(", ") - + local TaskText = TaskReport:Text( ", " ) + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and (self.FlashNewTask) then + 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 diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index f4ccf40d6..20e5df555 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -1,13 +1,13 @@ --- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements. --- +-- -- === --- +-- -- ### Author: **FlightControl** --- --- ### Contributions: --- +-- +-- ### Contributions: +-- -- === --- +-- -- @module Tasking.Task_A2G -- @image MOOSE.JPG @@ -18,29 +18,29 @@ do -- TASK_A2G -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - --- The TASK_A2G class defines Air To Ground tasks for a @{Set} of Target Units, + --- The TASK_A2G class defines Air To Ground tasks for a @{Set} of Target Units, -- based on the tasking capabilities defined in @{Tasking.Task#TASK}. -- The TASK_A2G is implemented using a @{Core.Fsm#FSM_TASK}, and has the following statuses: - -- + -- -- * **None**: Start of the process -- * **Planned**: The A2G task is planned. -- * **Assigned**: The A2G task is assigned to a @{Wrapper.Group#GROUP}. -- * **Success**: The A2G task is successfully completed. -- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. - -- + -- -- ## 1) Set the scoring of achievements in an A2G attack. - -- + -- -- Scoring or penalties can be given in the following circumstances: - -- + -- -- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed. -- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed. -- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed. - -- + -- -- @field #TASK_A2G TASK_A2G = { - ClassName = "TASK_A2G", + ClassName = "TASK_A2G" } - + --- Instantiates a new TASK_A2G. -- @param #TASK_A2G self -- @param Tasking.Mission#MISSION Mission @@ -54,53 +54,51 @@ do -- TASK_A2G function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType, TaskBriefing ) local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2G self:F() - + self.TargetSetUnit = TargetSetUnit self.TaskType = TaskType - + local Fsm = self:GetUnitProcess() - + Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) - Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) - Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) - + Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) + Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) + Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" ) - + Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" ) Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" ) - - Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} ) + + Fsm:AddProcess( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} ) Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" ) Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} ) Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} ) Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" ) - - --Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) - --Fsm:AddTransition( "Accounted", "Success", "Success" ) + + -- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" ) + -- Fsm:AddTransition( "Accounted", "Success", "Success" ) Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Failed", "Fail", "Failed" ) - - - --- Test + --- Test -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_A2G#TASK_A2G Task function Fsm:onafterAssigned( TaskUnit, Task ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) -- Determine the first Unit from the self.RendezVousSetUnit - + self:RouteToRendezVous() end - - --- Test + + --- Test -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_A2G#TASK_A2G Task function Fsm:onafterRouteToRendezVous( TaskUnit, Task ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) -- Determine the first Unit from the self.RendezVousSetUnit - + if Task:GetRendezVousZone( TaskUnit ) then self:__RouteToRendezVousZone( 0.1 ) else @@ -112,36 +110,36 @@ do -- TASK_A2G end end - --- Test + --- Test -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task#TASK_A2G Task function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) -- Determine the first Unit from the self.TargetSetUnit - - self:__Engage( 0.1 ) + + self:__Engage( 0.1 ) end - - --- Test + + --- Test -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task#TASK_A2G Task function Fsm:onafterEngage( TaskUnit, Task ) self:F( { self } ) self:__Account( 0.1 ) - self:__RouteToTarget(0.1 ) + self:__RouteToTarget( 0.1 ) self:__RouteToTargets( -10 ) end - - --- Test + + --- Test -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_A2G#TASK_A2G Task function Fsm:onafterRouteToTarget( TaskUnit, Task ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) -- Determine the first Unit from the self.TargetSetUnit - + if Task:GetTargetZone( TaskUnit ) then self:__RouteToTargetZone( 0.1 ) else @@ -154,8 +152,8 @@ do -- TASK_A2G self:__RouteToTargetPoint( 0.1 ) end end - - --- Test + + --- Test -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_A2G#TASK_A2G Task @@ -167,20 +165,18 @@ do -- TASK_A2G end self:__RouteToTargets( -10 ) end - + return self - + end --- @param #TASK_A2G self -- @param Core.Set#SET_UNIT TargetSetUnit The set of targets. function TASK_A2G:SetTargetSetUnit( TargetSetUnit ) - + self.TargetSetUnit = TargetSetUnit end - - --- @param #TASK_A2G self function TASK_A2G:GetPlannedMenuText() return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" @@ -190,34 +186,32 @@ do -- TASK_A2G -- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_A2G:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit ) - + function TASK_A2G:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit ) + local ProcessUnit = self:GetUnitProcess( TaskUnit ) - + local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT ActRouteRendezVous:SetCoordinate( RendezVousCoordinate ) ActRouteRendezVous:SetRange( RendezVousRange ) end - + --- @param #TASK_A2G self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map. -- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point. function TASK_A2G:GetRendezVousCoordinate( TaskUnit ) - + local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange() end - - - + --- @param #TASK_A2G self -- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit ) - + local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE @@ -234,18 +228,17 @@ do -- TASK_A2G local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE return ActRouteRendezVous:GetZone() end - + --- @param #TASK_A2G self -- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_A2G:SetTargetCoordinate( TargetCoordinate, TaskUnit ) - + local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT ActRouteTarget:SetCoordinate( TargetCoordinate ) end - --- @param #TASK_A2G self -- @param Wrapper.Unit#UNIT TaskUnit @@ -258,18 +251,16 @@ do -- TASK_A2G return ActRouteTarget:GetCoordinate() end - --- @param #TASK_A2G self -- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map. -- @param Wrapper.Unit#UNIT TaskUnit function TASK_A2G:SetTargetZone( TargetZone, TaskUnit ) - + local ProcessUnit = self:GetUnitProcess( TaskUnit ) local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE ActRouteTarget:SetZone( TargetZone ) end - --- @param #TASK_A2G self -- @param Wrapper.Unit#UNIT TaskUnit @@ -283,47 +274,46 @@ do -- TASK_A2G end function TASK_A2G:SetGoalTotal() - + self.GoalTotal = self.TargetSetUnit:Count() end function TASK_A2G:GetGoalTotal() - + return self.GoalTotal end - + --- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats. -- @param #TASK_A2G self - function TASK_A2G:ReportOrder( ReportGroup ) - self:UpdateTaskInfo( self.DetectedItem ) - + function TASK_A2G:ReportOrder( ReportGroup ) + self:UpdateTaskInfo( self.DetectedItem ) + local Coordinate = self.TaskInfo:GetData( "Coordinate" ) local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) - + return Distance end - - + --- This method checks every 10 seconds if the goal has been reached of the task. -- @param #TASK_A2G self function TASK_A2G:onafterGoal( TaskUnit, From, Event, To ) local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT - + if TargetSetUnit:Count() == 0 then self:Success() end - + self:__Goal( -10 ) end --- @param #TASK_A2G self function TASK_A2G:UpdateTaskInfo( DetectedItem ) - + if self:IsStatePlanned() or self:IsStateAssigned() then - local TargetCoordinate = DetectedItem and self.Detection:GetDetectedItemCoordinate( DetectedItem ) or self.TargetSetUnit:GetFirst():GetCoordinate() + local TargetCoordinate = DetectedItem and self.Detection:GetDetectedItemCoordinate( DetectedItem ) or self.TargetSetUnit:GetFirst():GetCoordinate() self.TaskInfo:AddTaskName( 0, "MSOD" ) self.TaskInfo:AddCoordinate( TargetCoordinate, 1, "SOD" ) - + local ThreatLevel, ThreatText if DetectedItem then ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( DetectedItem ) @@ -331,7 +321,7 @@ do -- TASK_A2G ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G() end self.TaskInfo:AddThreat( ThreatText, ThreatLevel, 10, "MOD", true ) - + if self.Detection then local DetectedItemsCount = self.TargetSetUnit:Count() local ReportTypes = REPORT:New() @@ -344,33 +334,33 @@ do -- TASK_A2G end end self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true ) - self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true ) + self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true ) else local DetectedItemsCount = self.TargetSetUnit:Count() local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames() self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true ) - self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true ) + self.TaskInfo:AddTargets( DetectedItemsCount, DetectedItemsTypes, 20, "D", true ) end self.TaskInfo:AddQFEAtCoordinate( TargetCoordinate, 30, "MOD" ) self.TaskInfo:AddTemperatureAtCoordinate( TargetCoordinate, 31, "MD" ) self.TaskInfo:AddWindAtCoordinate( TargetCoordinate, 32, "MD" ) end - + end - + --- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection. -- @param #TASK_A2G self -- @param #number AutoAssignMethod The method to be applied to the task. -- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center. -- @param Wrapper.Group#GROUP TaskGroup The player group. function TASK_A2G:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup ) - - if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then + + if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then return math.random( 1, 9 ) elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then local Coordinate = self.TaskInfo:GetData( "Coordinate" ) local Distance = Coordinate:Get2DDistance( CommandCenter:GetPositionable():GetCoordinate() ) - self:F({Distance=Distance}) + self:F( { Distance = Distance } ) return math.floor( Distance ) elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then return 1 @@ -379,8 +369,7 @@ do -- TASK_A2G return 0 end -end - +end do -- TASK_A2G_SEAD @@ -397,9 +386,9 @@ do -- TASK_A2G_SEAD -- -- @field #TASK_A2G_SEAD TASK_A2G_SEAD = { - ClassName = "TASK_A2G_SEAD", + ClassName = "TASK_A2G_SEAD" } - + --- Instantiates a new TASK_A2G_SEAD. -- @param #TASK_A2G_SEAD self -- @param Tasking.Mission#MISSION Mission @@ -408,19 +397,16 @@ do -- TASK_A2G_SEAD -- @param Core.Set#SET_UNIT TargetSetUnit -- @param #string TaskBriefing The briefing of the task. -- @return #TASK_A2G_SEAD self - function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing) + function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_A2G_SEAD self:F() - + Mission:AddTask( self ) - - self:SetBriefing( - TaskBriefing or - "Execute a Suppression of Enemy Air Defenses." - ) + + self:SetBriefing( TaskBriefing or "Execute a Suppression of Enemy Air Defenses." ) return self - end + end --- Set a score when a target in scope of the A2G attack, has been destroyed . -- @param #TASK_A2G_SEAD self @@ -434,7 +420,7 @@ do -- TASK_A2G_SEAD local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has SEADed a target.", Score ) - + return self end @@ -450,7 +436,7 @@ do -- TASK_A2G_SEAD local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScore( "Success", "All radar emitting targets have been successfully SEADed!", Score ) - + return self end @@ -466,11 +452,10 @@ do -- TASK_A2G_SEAD local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScore( "Failed", "The SEADing has failed!", Penalty ) - + return self end - end do -- TASK_A2G_BAI @@ -488,10 +473,8 @@ do -- TASK_A2G_BAI -- based on detected enemy ground targets. -- -- @field #TASK_A2G_BAI - TASK_A2G_BAI = { - ClassName = "TASK_A2G_BAI", - } - + TASK_A2G_BAI = { ClassName = "TASK_A2G_BAI" } + --- Instantiates a new TASK_A2G_BAI. -- @param #TASK_A2G_BAI self -- @param Tasking.Mission#MISSION Mission @@ -503,14 +486,11 @@ do -- TASK_A2G_BAI function TASK_A2G_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI", TaskBriefing ) ) -- #TASK_A2G_BAI self:F() - + Mission:AddTask( self ) - - self:SetBriefing( - TaskBriefing or - "Execute a Battlefield Air Interdiction of a group of enemy targets." - ) - + + self:SetBriefing( TaskBriefing or "Execute a Battlefield Air Interdiction of a group of enemy targets." ) + return self end @@ -526,7 +506,7 @@ do -- TASK_A2G_BAI local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Battlefield Air Interdiction (BAI).", Score ) - + return self end @@ -542,7 +522,7 @@ do -- TASK_A2G_BAI local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Battlefield Air Interdiction (BAI) is a success!", Score ) - + return self end @@ -558,15 +538,12 @@ do -- TASK_A2G_BAI local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScore( "Failed", "The Battlefield Air Interdiction (BAI) has failed!", Penalty ) - + return self end end - - - do -- TASK_A2G_CAS --- The TASK_A2G_CAS class @@ -581,10 +558,8 @@ do -- TASK_A2G_CAS -- based on detected enemy ground targets. -- -- @field #TASK_A2G_CAS - TASK_A2G_CAS = { - ClassName = "TASK_A2G_CAS", - } - + TASK_A2G_CAS = { ClassName = "TASK_A2G_CAS" } + --- Instantiates a new TASK_A2G_CAS. -- @param #TASK_A2G_CAS self -- @param Tasking.Mission#MISSION Mission @@ -596,19 +571,13 @@ do -- TASK_A2G_CAS function TASK_A2G_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS", TaskBriefing ) ) -- #TASK_A2G_CAS self:F() - - Mission:AddTask( self ) - - self:SetBriefing( - TaskBriefing or - "Execute a Close Air Support for a group of enemy targets. " .. - "Beware of friendlies at the vicinity! " - ) - + Mission:AddTask( self ) + + self:SetBriefing( TaskBriefing or ( "Execute a Close Air Support for a group of enemy targets. " .. "Beware of friendlies at the vicinity! " ) ) + return self - end - + end --- Set a score when a target in scope of the A2G attack, has been destroyed . -- @param #TASK_A2G_CAS self @@ -622,7 +591,7 @@ do -- TASK_A2G_CAS local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Close Air Support (CAS).", Score ) - + return self end @@ -638,7 +607,7 @@ do -- TASK_A2G_CAS local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Close Air Support (CAS) was a success!", Score ) - + return self end @@ -654,9 +623,8 @@ do -- TASK_A2G_CAS local ProcessUnit = self:GetUnitProcess( TaskUnit ) ProcessUnit:AddScore( "Failed", "The Close Air Support (CAS) has failed!", Penalty ) - + return self end - end diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua index 05bf2d11f..b9aedb1b6 100644 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -1,24 +1,25 @@ ---- **Tasking** -- Dynamically allocates A2G tasks to human players, based on detected ground targets through reconnaissance. --- +--- **Tasking** -- Dynamically allocates A2G tasks to human players, based on detected ground targets through reconnaissance. +-- -- **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 (CAS) Close Air Support tasks for human players. -- * Dynamically assign (BAI) Battlefield Air Interdiction tasks for human players. --- * Dynamically assign (SEAD) Supression of Enemy Air Defense tasks for human players to eliminate G2A missile threats. +-- * Dynamically assign (SEAD) Suppression of Enemy Air Defense tasks for human players to eliminate G2A missile threats. -- * Define and use an EWR (Early Warning Radar) network. -- * Define different ranges to engage upon intruders. -- * Keep task achievements. --- * Score task achievements.-- +-- * Score task achievements. +-- -- === --- +-- -- ### Author: **FlightControl** --- --- ### Contributions: --- +-- +-- ### Contributions: +-- -- === --- +-- -- @module Tasking.Task_A2G_Dispatcher -- @image Task_A2G_Dispatcher.JPG @@ -32,151 +33,151 @@ do -- TASK_A2G_DISPATCHER -- @extends Tasking.DetectionManager#DETECTION_MANAGER --- Orchestrates dynamic **A2G Task Dispatching** based on the detection results of a linked @{Detection} object. - -- + -- -- It uses the Tasking System within the MOOSE framework, which is a multi-player Tasking Orchestration system. -- It provides a truly dynamic battle environment for pilots and ground commanders to engage upon, -- in a true co-operation environment wherein **Multiple Teams** will collaborate in Missions to **achieve a common Mission Goal**. - -- + -- -- The A2G dispatcher will dispatch the A2G Tasks to a defined @{Set} of @{Wrapper.Group}s that will be manned by **Players**. -- We call this the **AttackSet** of the A2G dispatcher. So, the Players are seated in the @{Client}s of the @{Wrapper.Group} @{Set}. - -- + -- -- Depending on the actions of the enemy, preventive tasks are dispatched to the players to orchestrate the engagement in a true co-operation. -- The detection object will group the detected targets by its grouping method, and integrates a @{Set} of @{Wrapper.Group}s that are Recce vehicles or air units. -- We call this the **RecceSet** of the A2G dispatcher. - -- + -- -- Depending on the current detected tactical situation, different task types will be dispatched to the Players seated in the AttackSet.. -- There are currently 3 **Task Types** implemented in the TASK\_A2G\_DISPATCHER: - -- + -- -- - **SEAD Task**: Dispatched when there are ground based Radar Emitters detected within an area. -- - **CAS Task**: Dispatched when there are no ground based Radar Emitters within the area, but there are friendly ground Units within 6 km from the enemy. -- - **BAI Task**: Dispatched when there are no ground based Radar Emitters within the area, and there aren't friendly ground Units within 6 km from the enemy. -- -- # 0. Tactical Situations - -- + -- -- This chapters provides some insights in the tactical situations when certain Task Types are created. -- The Task Types are depending on the enemy positions that were detected, and the current location of friendly units. - -- + -- -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia3.JPG) - -- + -- -- In the demonstration mission [TAD-A2G-000 - AREAS - Detection test], -- the tactical situation is a demonstration how the A2G detection works. -- This example will be taken further in the explanation in the following chapters. - -- + -- -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia4.JPG) - -- + -- -- The red coalition are the players, the blue coalition is the enemy. - -- + -- -- Red reconnaissance vehicles and airborne units are detecting the targets. -- We call this the RecceSet as explained above, which is a Set of Groups that -- have a group name starting with `Recce` (configured in the mission script). - -- + -- -- Red attack units are responsible for executing the mission for the command center. -- We call this the AttackSet, which is a Set of Groups with a group name starting with `Attack` (configured in the mission script). -- These units are setup in this demonstration mission to be ground vehicles and airplanes. -- For demonstration purposes, the attack airplane is stationed on the ground to explain -- the messages and the menus properly. -- Further test missions demonstrate the A2G task dispatcher from within air. - -- + -- -- Depending upon the detection results, the A2G dispatcher will create different tasks. - -- + -- -- # 0.1. SEAD Task - -- + -- -- A SEAD Task is dispatched when there are ground based Radar Emitters detected within an area. - -- + -- -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia9.JPG) - -- + -- -- - Once all Radar Emitting Units have been destroyed, the Task will convert into a BAI or CAS task! -- - A CAS and BAI task may be converted into a SEAD task, once a radar has been detected within the area! - -- + -- -- # 0.2. CAS Task - -- + -- -- A CAS Task is dispatched when there are no ground based Radar Emitters within the area, but there are friendly ground Units within 6 km from the enemy. - -- + -- -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia10.JPG) - -- + -- -- - After the detection of the CAS task, if the friendly Units are destroyed, the CAS task will convert into a BAI task! -- - Only ground Units are taken into account. Airborne units are ships are not considered friendlies that require Close Air Support. - -- + -- -- # 0.3. BAI Task - -- + -- -- A BAI Task is dispatched when there are no ground based Radar Emitters within the area, and there aren't friendly ground Units within 6 km from the enemy. - -- + -- -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia11.JPG) -- -- - A BAI task may be converted into a CAS task if friendly Ground Units approach within 6 km range! -- -- # 1. Player Experience - -- + -- -- The A2G dispatcher is residing under a @{CommandCenter}, which is orchestrating a @{Mission}. -- As a result, you'll find for DCS World missions that implement the A2G dispatcher a **Command Center Menu** and under this one or more **Mission Menus**. - -- + -- -- For example, if there are 2 Command Centers (CC). -- Each CC is controlling a couple of Missions, the Radio Menu Structure could look like this: - -- + -- -- Radio MENU Structure (F10. Other) - -- + -- -- F1. Command Center [Gori] -- F1. Mission "Alpha (Primary)" -- F2. Mission "Beta (Secondary)" -- F3. Mission "Gamma (Tactical)" -- F1. Command Center [Lima] -- F1. Mission "Overlord (High)" - -- - -- Command Center [Gori] is controlling Mission "Alpha", "Beta", "Gamma". Alpha is the Primary mission, Beta the Secondary and there is a Tacical mission Gamma. + -- + -- Command Center [Gori] is controlling Mission "Alpha", "Beta", "Gamma". Alpha is the Primary mission, Beta the Secondary and there is a Tactical mission Gamma. -- Command Center [Lima] is controlling Missions "Overlord", which needs to be executed with High priority. -- -- ## 1.1. Mission Menu (Under the Command Center Menu) - -- + -- -- The Mission Menu controls the information of the mission, including the: - -- + -- -- - **Mission Briefing**: A briefing of the Mission in text, which will be shown as a message. -- - **Mark Task Locations**: A summary of each Task will be shown on the map as a marker. -- - **Create Task Reports**: A menu to create various reports of the current tasks dispatched by the A2G dispatcher. -- - **Create Mission Reports**: A menu to create various reports on the current mission. - -- + -- -- For CC [Lima], Mission "Overlord", the menu structure could look like this: - -- + -- -- Radio MENU Structure (F10. Other) - -- + -- -- F1. Command Center [Lima] -- F1. Mission "Overlord" -- F1. Mission Briefing -- F2. Mark Task Locations on Map -- F3. Task Reports -- F4. Mission Reports - -- + -- -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia5.JPG) - -- + -- -- ### 1.1.1. Mission Briefing Menu - -- + -- -- The Mission Briefing Menu will show in text a summary description of the overall mission objectives and expectations. -- Note that the Mission Briefing is not the briefing of a specific task, but rather provides an overall strategy and tactical situation, - -- and explains the mission goals. - -- - -- + -- and explains the mission goals. + -- + -- -- ### 1.1.2. Mark Task Locations Menu - -- + -- -- The Mark Task Locations Menu will mark the location indications of the Tasks on the map, if this intelligence is known by the Command Center. -- For A2G tasks this information will always be know, but it can be that for other tasks a location intelligence will be less relevant. -- Note that each Planned task and each Engaged task will be marked. Completed, Failed and Cancelled tasks are not marked. -- Depending on the task type, a summary information is shown to bring to the player the relevant information for situational awareness. - -- + -- -- ### 1.1.3. Task Reports Menu - -- + -- -- The Task Reports Menu is a sub menu, that allows to create various reports: - -- + -- -- - **Tasks Summary**: This report will list all the Tasks that are or were active within the mission, indicating its status. -- - **Planned Tasks**: This report will list all the Tasks that are in status Planned, which are Tasks not assigned to any player, and are ready to be executed. -- - **Assigned Tasks**: This report will list all the Tasks that are in status Assigned, which are Tasks assigned to (a) player(s) and are currently executed. -- - **Successful Tasks**: This report will list all the Tasks that are in status Success, which are Tasks executed by (a) player(s) and are completed successfully. -- - **Failed Tasks**: This report will list all the Tasks that are in status Success, which are Tasks executed by (a) player(s) and that have failed. - -- + -- -- The information shown of the tasks will vary according the underlying task type, but are self explanatory. -- -- For CC [Gori], Mission "Alpha", the Task Reports menu structure could look like this: - -- + -- -- Radio MENU Structure (F10. Other) - -- + -- -- F1. Command Center [Gori] -- F1. Mission "Alpha" -- F1. Mission Briefing @@ -188,21 +189,21 @@ do -- TASK_A2G_DISPATCHER -- F4. Successful Tasks -- F5. Failed Tasks -- F4. Mission Reports - -- + -- -- Note that these reports provide an "overview" of the tasks. Detailed information of the task can be retrieved using the Detailed Report on the Task Menu. -- (See later). - -- + -- -- ### 1.1.4. Mission Reports Menu - -- + -- -- The Mission Reports Menu is a sub menu, that provides options to retrieve further information on the current Mission: - -- - -- - **Report Mission Progress**: Shows the progress of the current Mission. Each Task has a %-tage of completion. + -- + -- - **Report Mission Progress**: Shows the progress of the current Mission. Each Task has a % of completion. -- - **Report Players per Task**: Show which players are engaged on which Task within the Mission. - -- + -- -- For CC |Gori|, Mission "Alpha", the Mission Reports menu structure could look like this: - -- + -- -- Radio MENU Structure (F10. Other) - -- + -- -- F1. Command Center [Gori] -- F1. Mission "Alpha" -- F1. Mission Briefing @@ -211,25 +212,25 @@ do -- TASK_A2G_DISPATCHER -- F4. Mission Reports -- F1. Report Mission Progress -- F2. Report Players per Task - -- - -- + -- + -- -- ## 1.2. Task Management Menus - -- + -- -- Very important to remember is: **Multiple Players can be assigned to the same Task, but from the player perspective, the Player can only be assigned to one Task per Mission at the same time!** -- Consider this like the two major modes in which a player can be in. He can be free of tasks or he can be assigned to a Task. -- Depending on whether a Task has been Planned or Assigned to a Player (Group), -- **the Mission Menu will contain extra Menus to control specific Tasks.** - -- + -- -- #### 1.2.1. Join a Planned Task - -- + -- -- If the Player has not yet been assigned to a Task within the Mission, the Mission Menu will contain additionally a: - -- + -- -- - Join Planned Task Menu: This menu structure allows the player to join a planned task (a Task with status Planned). - -- + -- -- For CC |Gori|, Mission "Alpha", the menu structure could look like this: - -- + -- -- Radio MENU Structure (F10. Other) - -- + -- -- F1. Command Center [Gori] -- F1. Mission "Alpha" -- F1. Mission Briefing @@ -237,23 +238,23 @@ do -- TASK_A2G_DISPATCHER -- F3. Task Reports -- F4. Mission Reports -- F5. Join Planned Task - -- + -- -- **The F5. Join Planned Task allows the player to join a Planned Task and take an engagement in the running Mission.** - -- - -- #### 1.2.2. Manage an Assigned Task - -- + -- + -- #### 1.2.2. Manage an Assigned Task + -- -- If the Player has been assigned to one Task within the Mission, the Mission Menu will contain an extra: - -- + -- -- - Assigned Task __TaskName__ Menu: This menu structure allows the player to take actions on the currently engaged task. - -- + -- -- In this example, the Group currently seated by the player is not assigned yet to a Task. -- The Player has the option to assign itself to a Planned Task using menu option F5 under the Mission Menu "Alpha". - -- + -- -- This would be an example menu structure, -- for CC |Gori|, Mission "Alpha", when a player would have joined Task CAS.001: - -- + -- -- Radio MENU Structure (F10. Other) - -- + -- -- F1. Command Center [Gori] -- F1. Mission "Alpha" -- F1. Mission Briefing @@ -261,26 +262,25 @@ do -- TASK_A2G_DISPATCHER -- F3. Task Reports -- F4. Mission Reports -- F5. Assigned Task CAS.001 - -- + -- -- **The F5. Assigned Task __TaskName__ allows the player to control the current Assigned Task and take further actions.** - -- - -- + -- -- ## 1.3. Join Planned Task Menu - -- + -- -- The Join Planned Task Menu contains the different Planned A2G Tasks **in a structured Menu Hierarchy**. - -- The Menu Hierarchy is structuring the Tasks per **Task Type**, and then by **Task Name (ID)**. - -- - -- For example, for CC [Gori], Mission "Alpha", + -- The Menu Hierarchy is structuring the Tasks per **Task Type**, and then by **Task Name (ID)**. + -- + -- For example, for CC [Gori], Mission "Alpha", -- if a Mission "ALpha" contains 5 Planned Tasks, which would be: - -- - -- - 2 CAS Tasks + -- + -- - 2 CAS Tasks -- - 1 BAI Task -- - 2 SEAD Tasks - -- + -- -- the Join Planned Task Menu Hierarchy could look like this: - -- + -- -- Radio MENU Structure (F10. Other) - -- + -- -- F1. Command Center [Gori] -- F1. Mission "Alpha" -- F1. Mission Briefing @@ -296,26 +296,26 @@ do -- TASK_A2G_DISPATCHER -- F1. SEAD.003 -- F2. SEAD.004 -- F3. SEAD.005 - -- + -- -- An example from within a running simulation: - -- + -- -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia6.JPG) - -- + -- -- Each Task Type Menu would have a list of the Task Menus underneath. -- Each Task Menu (eg. `CAS.001`) has a **detailed Task Menu structure to control the specific task**! -- -- ### 1.3.1. Planned Task Menu -- -- Each Planned Task Menu will allow for the following actions: - -- + -- -- - Report Task Details: Provides a detailed report on the Planned Task. -- - Mark Task Location on Map: Mark the approximate location of the Task on the Map, if relevant. -- - Join Task: Join the Task. This is THE menu option to let a Player join the Task, and to engage within the Mission. - -- - -- The Join Planned Task Menu could look like this for for CC |Gori|, Mission "Alpha": - -- + -- + -- The Join Planned Task Menu could look like this for for CC |Gori|, Mission "Alpha": + -- -- Radio MENU Structure (F10. Other) - -- + -- -- F1. Command Center |Gori| -- F1. Mission "Alpha" -- F1. Mission Briefing @@ -328,22 +328,22 @@ do -- TASK_A2G_DISPATCHER -- F1. Report Task Details -- F2. Mark Task Location on Map -- F3. Join Task - -- + -- -- **The Join Task is THE menu option to let a Player join the Task, and to engage within the Mission.** - -- - -- + -- + -- -- ## 1.4. Assigned Task Menu - -- + -- -- The Assigned Task Menu allows to control the **current assigned task** within the Mission. - -- + -- -- Depending on the Type of Task, the following menu options will be available: - -- + -- -- - **Report Task Details**: Provides a detailed report on the Planned Task. -- - **Mark Task Location on Map**: Mark the approximate location of the Task on the Map, if relevant. -- - **Abort Task: Abort the current assigned Task:** This menu option lets the player abort the Task. - -- + -- -- For example, for CC |Gori|, Mission "Alpha", the Assigned Menu could be: - -- + -- -- F1. Command Center |Gori| -- F1. Mission "Alpha" -- F1. Mission Briefing @@ -354,90 +354,89 @@ do -- TASK_A2G_DISPATCHER -- F1. Report Task Details -- F2. Mark Task Location on Map -- F3. Abort Task - -- + -- -- Task abortion will result in the Task to be Cancelled, and the Task **may** be **Replanned**. - -- However, this will depend on the setup of each Mission. - -- + -- However, this will depend on the setup of each Mission. + -- -- ## 1.5. Messages - -- + -- -- During game play, different messages are displayed. -- These messages provide an update of the achievements made, and the state wherein the task is. - -- + -- -- The various reports can be used also to retrieve the current status of the mission and its tasks. - -- + -- -- ![](..\Presentations\TASK_A2G_DISPATCHER\Dia7.JPG) - -- + -- -- The @{Settings} menu provides additional options to control the timing of the messages. -- There are: - -- + -- -- - Status messages, which are quick status updates. The settings menu allows to switch off these messages. -- - Information messages, which are shown a bit longer, as they contain important information. -- - Summary reports, which are quick reports showing a high level summary. -- - Overview reports, which are providing the essential information. It provides an overview of a greater thing, and may take a bit of time to read. -- - Detailed reports, which provide with very detailed information. It takes a bit longer to read those reports, so the display of those could be a bit longer. - -- + -- -- # 2. TASK\_A2G\_DISPATCHER constructor - -- + -- -- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK\_A2G\_DISPATCHER instance. -- -- # 3. Usage -- -- To use the TASK\_A2G\_DISPATCHER class, you need: - -- + -- -- - A @{CommandCenter} object. The master communication channel. -- - A @{Mission} object. Each task belongs to a Mission. -- - A @{Detection} object. There are several detection grouping methods to choose from. -- - A @{Task_A2G_Dispatcher} object. The master A2G task dispatcher. - -- - A @{Set} of @{Wrapper.Group} objects that will detect the emeny, the RecceSet. This is attached to the @{Detection} object. + -- - A @{Set} of @{Wrapper.Group} objects that will detect the enemy, the RecceSet. This is attached to the @{Detection} object. -- - A @{Set} ob @{Wrapper.Group} objects that will attack the enemy, the AttackSet. This is attached to the @{Task_A2G_Dispatcher} object. - -- - -- Below an example mission declaration that is defines a Task A2G Dispatcher object. -- - -- -- Declare the Command Center + -- Below an example mission declaration that is defines a Task A2G Dispatcher object. + -- + -- -- Declare the Command Center -- local HQ = GROUP -- :FindByName( "HQ", "Bravo HQ" ) -- -- local CommandCenter = COMMANDCENTER -- :New( HQ, "Lima" ) - -- + -- -- -- Declare the Mission for the Command Center. -- local Mission = MISSION -- :New( CommandCenter, "Overlord", "High", "Attack Detect Mission Briefing", coalition.side.RED ) - -- + -- -- -- Define the RecceSet that will detect the enemy. -- local RecceSet = SET_GROUP -- :New() -- :FilterPrefixes( "FAC" ) -- :FilterCoalitions("red") -- :FilterStart() - -- + -- -- -- Setup the detection. We use DETECTION_AREAS to detect and group the enemies within areas of 3 km radius. -- local DetectionAreas = DETECTION_AREAS -- :New( RecceSet, 3000 ) -- The RecceSet will detect the enemies. - -- + -- -- -- Setup the AttackSet, which is a SET_GROUP. - -- -- The SET_GROUP is a dynamic collection of GROUP objects. + -- -- The SET_GROUP is a dynamic collection of GROUP objects. -- local AttackSet = SET_GROUP -- :New() -- Create the SET_GROUP object. -- :FilterCoalitions( "red" ) -- Only incorporate the RED coalitions. -- :FilterPrefixes( "Attack" ) -- Only incorporate groups that start with the name Attack. -- :FilterStart() -- Enable the dynamic filtering. From this moment the AttackSet will contain all groups that are red and start with the name Attack. - -- + -- -- -- Now we have everything to setup the main A2G TaskDispatcher. -- TaskDispatcher = TASK_A2G_DISPATCHER - -- :New( Mission, AttackSet, DetectionAreas ) -- We assign the TaskDispatcher under Mission. The AttackSet will engage the enemy and will recieve the dispatched Tasks. The DetectionAreas will report any detected enemies to the TaskDispatcher. - -- - -- + -- :New( Mission, AttackSet, DetectionAreas ) -- We assign the TaskDispatcher under Mission. The AttackSet will engage the enemy and will receive the dispatched Tasks. The DetectionAreas will report any detected enemies to the TaskDispatcher. + -- + -- -- -- @field #TASK_A2G_DISPATCHER TASK_A2G_DISPATCHER = { ClassName = "TASK_A2G_DISPATCHER", Mission = nil, Detection = nil, - Tasks = {}, + Tasks = {} } - - + --- TASK_A2G_DISPATCHER constructor. -- @param #TASK_A2G_DISPATCHER self -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. @@ -445,18 +444,18 @@ do -- TASK_A2G_DISPATCHER -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. -- @return #TASK_A2G_DISPATCHER self function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection ) - + -- Inherits from DETECTION_MANAGER local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER - + self.Detection = Detection self.Mission = Mission - self.FlashNewTask = true --set to false to suppress flash messages - + self.FlashNewTask = true -- set to false to suppress flash messages + self.Detection:FilterCategories( { Unit.Category.GROUND_UNIT } ) - + self:AddTransition( "Started", "Assign", "Started" ) - + --- OnAfter Transition Handler for Event Assign. -- @function [parent=#TASK_A2G_DISPATCHER] OnAfterAssign -- @param #TASK_A2G_DISPATCHER self @@ -466,19 +465,19 @@ do -- TASK_A2G_DISPATCHER -- @param Tasking.Task_A2G#TASK_A2G Task -- @param Wrapper.Unit#UNIT TaskUnit -- @param #string PlayerName - + self:__Start( 5 ) - + return self end - - --- Set flashing player messages on or off + + --- Set flashing player messages on or off -- @param #TASK_A2G_DISPATCHER self -- @param #boolean onoff Set messages on (true) or off (false) function TASK_A2G_DISPATCHER:SetSendMessages( onoff ) - self.FlashNewTask = onoff + self.FlashNewTask = onoff end - + --- Creates a SEAD task when there are targets for it. -- @param #TASK_A2G_DISPATCHER self -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem @@ -486,7 +485,7 @@ do -- TASK_A2G_DISPATCHER -- @return #nil If there are no targets to be set. function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem ) self:F( { DetectedItem.ItemID } ) - + local DetectedSet = DetectedItem.Set local DetectedZone = DetectedItem.Zone @@ -500,10 +499,10 @@ do -- TASK_A2G_DISPATCHER TargetSetUnit:SetDatabase( DetectedSet ) TargetSetUnit:FilterHasSEAD() TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - + return TargetSetUnit end - + return nil end @@ -514,11 +513,10 @@ do -- TASK_A2G_DISPATCHER -- @return #nil If there are no targets to be set. function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem ) self:F( { DetectedItem.ItemID } ) - + local DetectedSet = DetectedItem.Set local DetectedZone = DetectedItem.Zone - -- Determine if the set has ground units. -- There should be ground unit friendlies nearby. Airborne units are valid friendlies types. -- And there shouldn't be any radar. @@ -532,13 +530,13 @@ do -- TASK_A2G_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 - + --- Creates a BAI task when there are targets for it. -- @param #TASK_A2G_DISPATCHER self -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem @@ -546,11 +544,10 @@ do -- TASK_A2G_DISPATCHER -- @return #nil If there are no targets to be set. function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition ) self:F( { DetectedItem.ItemID } ) - + local DetectedSet = DetectedItem.Set local DetectedZone = DetectedItem.Zone - -- Determine if the set has ground units. -- There shouldn't be any ground unit friendlies nearby. -- And there shouldn't be any radar. @@ -564,19 +561,18 @@ do -- TASK_A2G_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 - - + function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex ) self.Mission:RemoveTask( self.Tasks[TaskIndex] ) self.Tasks[TaskIndex] = 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_A2G_DISPATCHER self @@ -586,17 +582,16 @@ do -- TASK_A2G_DISPATCHER -- @param #boolean DetectedItemChange -- @return Tasking.Task#TASK function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged ) - + if Task then - if ( Task:IsStatePlanned() and DetectedItemChanged == true ) or Task:IsStateCancelled() then - --self:F( "Removing Tasking: " .. Task:GetTaskName() ) + if (Task:IsStatePlanned() and DetectedItemChanged == true) or Task:IsStateCancelled() then + -- self:F( "Removing Tasking: " .. Task:GetTaskName() ) self:RemoveTask( TaskIndex ) end end - + return Task end - --- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}. -- @param #TASK_A2G_DISPATCHER self @@ -604,15 +599,15 @@ do -- TASK_A2G_DISPATCHER -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. function TASK_A2G_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! @@ -634,21 +629,21 @@ do -- TASK_A2G_DISPATCHER --- First we need to 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 DetectedZone = DetectedItem.Zone - --self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) - --DetectedSet:Flush( self ) - + -- self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) + -- DetectedSet:Flush( self ) + local DetectedItemID = DetectedItem.ID local TaskIndex = DetectedItem.Index local DetectedItemChanged = DetectedItem.Changed - + self:F( { DetectedItemChanged = DetectedItemChanged, DetectedItemID = DetectedItemID, TaskIndex = TaskIndex } ) - + local Task = self.Tasks[TaskIndex] -- Tasking.Task_A2G#TASK_A2G - + if Task then -- If there is a Task and the task was assigned, then we check if the task was changed ... If it was, we need to reevaluate the targets. if Task:IsStateAssigned() then @@ -660,7 +655,7 @@ do -- TASK_A2G_DISPATCHER Task:SetTargetSetUnit( TargetSetUnit ) Task:SetDetection( Detection, DetectedItem ) Task:UpdateTaskInfo( DetectedItem ) - TargetsReport:Add( Detection:GetChangeText( DetectedItem ) ) + TargetsReport:Add( Detection:GetChangeText( DetectedItem ) ) else Task:Cancel() end @@ -691,18 +686,18 @@ do -- TASK_A2G_DISPATCHER end end end - + -- Now we send to each group the changes, if any. for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - local TargetsText = TargetsReport:Text(", ") - if ( Mission:IsGroupAssigned(TaskGroup) ) and TargetsText ~= "" and self.FlashNewTask then + local TargetsText = TargetsReport:Text( ", " ) + if (Mission:IsGroupAssigned( TaskGroup )) and TargetsText ~= "" and self.FlashNewTask then Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup ) end end end end end - + if Task then if Task:IsStatePlanned() then if DetectedItemChanged == true then -- The detection has changed, thus a new TargetSet is to be evaluated and set @@ -753,7 +748,7 @@ do -- TASK_A2G_DISPATCHER local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... if TargetSetUnit then Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit ) - DetectedItem.DesignateMenuName = string.format( "SEAD.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object + DetectedItem.DesignateMenuName = string.format( "SEAD.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object Task:SetDetection( Detection, DetectedItem ) end @@ -762,7 +757,7 @@ do -- TASK_A2G_DISPATCHER local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed... if TargetSetUnit then Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit ) - DetectedItem.DesignateMenuName = string.format( "CAS.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object + DetectedItem.DesignateMenuName = string.format( "CAS.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object Task:SetDetection( Detection, DetectedItem ) end @@ -771,19 +766,19 @@ do -- TASK_A2G_DISPATCHER local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed... if TargetSetUnit then Task = TASK_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit ) - DetectedItem.DesignateMenuName = string.format( "BAI.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object + DetectedItem.DesignateMenuName = string.format( "BAI.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object Task:SetDetection( Detection, DetectedItem ) end end end - + if Task then self.Tasks[TaskIndex] = Task Task:SetTargetZone( DetectedZone ) Task:SetDispatcher( self ) Task:UpdateTaskInfo( DetectedItem ) Mission:AddTask( Task ) - + function Task.OnEnterSuccess( Task, From, Event, To ) self:Success( Task ) end @@ -791,7 +786,7 @@ do -- TASK_A2G_DISPATCHER function Task.OnEnterCancelled( Task, From, Event, To ) self:Cancelled( Task ) end - + function Task.OnEnterFailed( Task, From, Event, To ) self:Failed( Task ) end @@ -799,31 +794,29 @@ do -- TASK_A2G_DISPATCHER function Task.OnEnterAborted( Task, From, Event, To ) self:Aborted( Task ) end - - + TaskReport:Add( Task:GetName() ) else - self:F("This should not happen") + self:F( "This should not happen" ) end 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(", ") + + local TaskText = TaskReport:Text( ", " ) for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and self.FlashNewTask then + 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