From 4b62fbd497413c349c350ae990ac8d9fa3264c74 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 17 May 2017 19:12:39 +0200 Subject: [PATCH] First A2A class --- Moose Development/Moose/Tasking/Task_A2A.lua | 381 ++++++++++++++++++ .../Moose/Tasking/Task_A2A_Dispatcher.lua | 217 ++++++++++ Moose Development/Moose/Wrapper/Unit.lua | 2 +- Moose Mission Setup/Moose.files | 2 + Moose Mission Setup/Moose.lua | 4 +- 5 files changed, 604 insertions(+), 2 deletions(-) create mode 100644 Moose Development/Moose/Tasking/Task_A2A.lua create mode 100644 Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua new file mode 100644 index 000000000..070d62440 --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -0,0 +1,381 @@ +--- **Tasking** - The TASK_A2A models tasks for players in Air to Air engagements. +-- +-- ![Banner Image](..\Presentations\TASK_A2A\Dia1.JPG) +-- +-- +-- # 1) @{Task_A2A#TASK_A2A} class, extends @{Task#TASK} +-- +-- The @{#TASK_A2A} class defines Air To Air tasks for a @{Set} of Target Units, +-- based on the tasking capabilities defined in @{Task#TASK}. +-- The TASK_A2A is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: +-- +-- * **None**: Start of the process +-- * **Planned**: The A2A task is planned. +-- * **Assigned**: The A2A task is assigned to a @{Group#GROUP}. +-- * **Success**: The A2A task is successfully completed. +-- * **Failed**: The A2A task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. +-- +-- # 1.1) Set the scoring of achievements in an A2A attack. +-- +-- Scoring or penalties can be given in the following circumstances: +-- +-- * @{#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. +-- +-- # 2) @{Task_A2A#TASK_INTERCEPT} class, extends @{Task_A2A#TASK_A2A} +-- +-- The TASK_A2A_INTERCEPT class defines an INTERCEPT task for a @{Set} of Target Units. +-- +-- ==== +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module Task_A2A + +do -- TASK_A2A + + --- The TASK_A2A class + -- @type TASK_A2A + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + TASK_A2A = { + ClassName = "TASK_A2A", + } + + --- Instantiates a new TASK_A2A. + -- @param #TASK_A2A self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetAttack The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Set#SET_UNIT UnitSetTargets + -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. + -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. + -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. + -- @return #TASK_A2A self + 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:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } ) + + 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: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( self.TargetSetUnit, self.TaskType ), { Accounted = "Success" } ) + 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( "Rejected", "Reject", "Aborted" ) + Fsm:AddTransition( "Failed", "Fail", "Failed" ) + + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2A#TASK_A2A Task + function Fsm:onafterRouteToRendezVous( TaskUnit, Task ) + self:E( { 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 + if Task:GetRendezVousCoordinate( TaskUnit ) then + self:__RouteToRendezVousPoint( 0.1 ) + else + self:__ArriveAtRendezVous( 0.1 ) + end + end + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task#TASK_A2A Task + function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.TargetSetUnit + + self:__Engage( 0.1 ) + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task#TASK_A2A Task + function Fsm:onafterEngage( TaskUnit, Task ) + self:E( { self } ) + self:__Account( 0.1 ) + self:__RouteToTarget(0.1 ) + self:__RouteToTargets( -10 ) + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2A#TASK_A2A Task + function Fsm:onafterRouteToTarget( TaskUnit, Task ) + self:E( { 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 + local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT + if TargetUnit then + local Coordinate = TargetUnit:GetCoordinate() + self:T( { TargetCoordinate = Coordinate, Coordinate:GetX(), Coordinate:GetAlt(), Coordinate:GetZ() } ) + Task:SetTargetCoordinate( TargetUnit:GetCoordinate(), TaskUnit ) + end + self:__RouteToTargetPoint( 0.1 ) + end + end + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2A#TASK_A2A Task + function Fsm:onafterRouteToTargets( TaskUnit, Task ) + self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT + if TargetUnit then + Task:SetTargetCoordinate( TargetUnit:GetCoordinate(), TaskUnit ) + end + self:__RouteToTargets( -10 ) + end + + return self + + end + + --- @param #TASK_A2A self + function TASK_A2A:GetPlannedMenuText() + return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" + end + + --- @param #TASK_A2A self + -- @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 ) + + 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 + ActRouteRendezVous:SetZone( RendezVousZone ) + end + + --- @param #TASK_A2A self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map. + function TASK_A2A:GetRendezVousZone( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + 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 + -- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map. + function TASK_A2A:GetTargetCoordinate( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT + 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, TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + ActRouteTarget:SetZone( TargetZone ) + end + + + --- @param #TASK_A2A self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. + function TASK_A2A:GetTargetZone( TaskUnit ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE + return ActRouteTarget:GetZone() + end + + --- Set a score when a target in scope of the A2A attack, has been destroyed . + -- @param #TASK_A2A self + -- @param #string Text The text to display to the player, when the target has been destroyed. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2A + function TASK_A2A:SetScoreOnDestroy( Text, Score, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScoreProcess( "Engaging", "Account", "Account", Text, Score ) + + return self + end + + --- Set a score when all the targets in scope of the A2A attack, have been destroyed. + -- @param #TASK_A2A self + -- @param #string Text The text to display to the player, when all targets hav been destroyed. + -- @param #number Score The score in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2A + function TASK_A2A:SetScoreOnSuccess( Text, Score, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Success", Text, Score ) + + return self + end + + --- Set a penalty when the A2A attack has failed. + -- @param #TASK_A2A self + -- @param #string Text The text to display to the player, when the A2A attack has failed. + -- @param #number Penalty The penalty in points. + -- @param Wrapper.Unit#UNIT TaskUnit + -- @return #TASK_A2A + function TASK_A2A:SetPenaltyOnFailed( Text, Penalty, TaskUnit ) + self:F( { Text, Score, TaskUnit } ) + + local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + ProcessUnit:AddScore( "Failed", Text, Penalty ) + + return self + end + + +end + + +do -- TASK_INTERCEPT + + --- The TASK_INTERCEPT class + -- @type TASK_INTERCEPT + -- @field Set#SET_UNIT TargetSetUnit + -- @extends Tasking.Task#TASK + TASK_INTERCEPT = { + ClassName = "TASK_INTERCEPT", + } + + --- Instantiates a new TASK_INTERCEPT. + -- @param #TASK_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 #string TaskBriefing The briefing of the task. + -- @return #TASK_INTERCEPT self + function TASK_INTERCEPT:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing ) + local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "INTERCEPT", TaskBriefing ) ) -- #TASK_INTERCEPT + self:F() + + Mission:AddTask( self ) + + --TODO: Add BR, Altitude, type of planes... + + local TargetCoord = TargetSetUnit:GetFirst():GetCoordinate() + local TargetPositionText = TargetCoord:ToString() + local TargetThreatLevel = TargetSetUnit:CalculateThreatLevelA2G() + + self:SetBriefing( + TaskBriefing or + "Intercept incoming intruders.\n" .. + "Last Known Coordinates: " .. TargetPositionText .. "\n" .. + "Threat Level: [" .. string.rep( "■", TargetThreatLevel ) .. "]" + ) + + return self + end + +end + diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua new file mode 100644 index 000000000..d950581a7 --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -0,0 +1,217 @@ +--- **Tasking** - The TASK_A2A_DISPATCHER creates and manages player TASK_A2A tasks based on detected targets. +-- +-- === +-- +-- # 1) @{#TASK_A2A_DISPATCHER} class, extends @{#DETECTION_MANAGER} +-- +-- The @{#TASK_A2A_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups. +-- 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: +-- +-- * **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. +-- +-- Other task types will follow... +-- +-- 3.1) TASK_A2A_DISPATCHER constructor: +-- -------------------------------------- +-- The @{#TASK_A2A_DISPATCHER.New}() method creates a new TASK_A2A_DISPATCHER instance. +-- +-- === +-- +-- # **API CHANGE HISTORY** +-- +-- The underlying change log documents the API changes. Please read this carefully. The following notation is used: +-- +-- * **Added** parts are expressed in bold type face. +-- * _Removed_ parts are expressed in italic type face. +-- +-- Hereby the change log: +-- +-- === +-- +-- # **AUTHORS and CONTRIBUTIONS** +-- +-- ### Contributions: +-- +-- ### Authors: +-- +-- * **FlightControl**: Concept, Design & Programming. +-- +-- @module Task_A2A_Dispatcher + +do -- TASK_A2A_DISPATCHER + + --- TASK_A2A_DISPATCHER class. + -- @type TASK_A2A_DISPATCHER + -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. + -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. The Detection object will only function in RADAR mode!!! + -- @field Tasking.Mission#MISSION Mission + -- @extends Tasking.DetectionManager#DETECTION_MANAGER + TASK_A2A_DISPATCHER = { + ClassName = "TASK_A2A_DISPATCHER", + Mission = nil, + Detection = nil, + Tasks = {}, + } + + + --- TASK_A2A_DISPATCHER constructor. + -- @param #TASK_A2A_DISPATCHER self + -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. + -- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. + -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. + -- @return #TASK_A2A_DISPATCHER self + function TASK_A2A_DISPATCHER:New( Mission, SetGroup, Detection ) + + -- Inherits from DETECTION_MANAGER + local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2A_DISPATCHER + + self.Detection = Detection + self.Mission = Mission + + self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER ) + self.Detection:InitDetectRadar( true ) + + self:AddTransition( "Started", "Assign", "Started" ) + + --- OnAfter Transition Handler for Event Assign. + -- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign + -- @param #TASK_A2A_DISPATCHER self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Tasking.Task_A2A#TASK_A2A Task + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #string PlayerName + + self:__Start( 5 ) + + return self + end + + + --- Creates an INTERCEPT task when there are targets for it. + -- @param #TASK_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem + -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return #nil If there are no targets to be set. + function TASK_A2A_DISPATCHER:EvaluateINTERCEPT( DetectedItem ) + self:F( { DetectedItem.ItemID } ) + + local DetectedSet = DetectedItem.Set + local DetectedZone = DetectedItem.Zone + + -- Put here the intercept logic.... + + if true then + + -- Here we're doing something advanced... We're copying the DetectedSet, but making a new Set only with SEADable Radar units in it. + local TargetSetUnit = SET_UNIT:New() + TargetSetUnit:SetDatabase( DetectedSet ) + TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. + + return TargetSetUnit + end + + return nil + end + + + --- Evaluates the removal of the Task from the Mission. + -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". + -- @param #TASK_A2A_DISPATCHER self + -- @param Tasking.Mission#MISSION Mission + -- @param Tasking.Task#TASK Task + -- @param #boolean DetectedItemID + -- @param #boolean DetectedItemChange + -- @return Tasking.Task#TASK + function TASK_A2A_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItemID, DetectedItemChanged ) + + if Task then + if Task:IsStatePlanned() and DetectedItemChanged == true then + self:E( "Removing Tasking: " .. Task:GetTaskName() ) + Mission:RemoveTask( Task ) + self.Tasks[DetectedItemID] = nil + end + end + + return Task + end + + + --- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}. + -- @param #TASK_A2A_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{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:E() + + local AreaMsg = {} + local TaskMsg = {} + local ChangeMsg = {} + + local Mission = self.Mission + + if Mission:IsIDLE() or Mission:IsENGAGED() then + + local TaskReport = REPORT:New() + + --- 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:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) + DetectedSet:Flush() + + local DetectedItemID = DetectedItem.ID + local DetectedItemChanged = DetectedItem.Changed + + local Task = self.Tasks[DetectedItemID] + Task = self:EvaluateRemoveTask( Mission, Task, DetectedItemID, DetectedItemChanged ) -- Task will be removed if it is planned and changed. + + -- Evaluate INTERCEPT + if not Task then + local TargetSetUnit = self:EvaluateINTERCEPT( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed... + if TargetSetUnit then + Task = TASK_INTERCEPT:New( Mission, self.SetGroup, string.format( "INTERCEPT.%03d", DetectedItemID ), TargetSetUnit ) + end + + if Task then + self.Tasks[DetectedItemID] = Task + Task:SetTargetZone( DetectedZone ) + Task:SetDispatcher( self ) + Task:SetInfo( "ThreatLevel", DetectedSet:CalculateThreatLevelA2G() ) + Task:SetInfo( "Detection", Detection:DetectedItemReportSummary( DetectedItemID ) ) + Task:SetInfo( "Changes", Detection:GetChangeText( DetectedItem ) ) + Mission:AddTask( Task ) + else + self:E("This should not happen") + end + + end + + TaskReport:Add( Task:GetName() ) + + -- 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() + + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if not Mission:IsGroupAssigned(TaskGroup) then + Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetName(), TaskReport:Text(", ") ), TaskGroup ) + end + end + + end + + return true + end + +end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index c99c2a7a4..f65693db6 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -640,7 +640,7 @@ function UNIT:GetThreatLevel() "Unarmed", "Tanker", "AWACS", - "Transport Helicpter", + "Transport Helicopter", "UAV", "Bomber", "Strategic Bomber", diff --git a/Moose Mission Setup/Moose.files b/Moose Mission Setup/Moose.files index 5d8c15212..6b2abfad1 100644 --- a/Moose Mission Setup/Moose.files +++ b/Moose Mission Setup/Moose.files @@ -57,6 +57,8 @@ Tasking/Task.lua Tasking/DetectionManager.lua Tasking/Task_A2G_Dispatcher.lua Tasking/Task_A2G.lua +Tasking/Task_A2A_Dispatcher.lua +Tasking/Task_A2A.lua Tasking/Task_Cargo.lua Moose.lua diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index ef7a6b0b3..1010dac77 100644 --- a/Moose Mission Setup/Moose.lua +++ b/Moose Mission Setup/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170510_1031' ) +env.info( 'Moose Generation Timestamp: 20170517_1856' ) local base = _G @@ -74,6 +74,8 @@ __Moose.Include( 'Tasking/Task.lua' ) __Moose.Include( 'Tasking/DetectionManager.lua' ) __Moose.Include( 'Tasking/Task_A2G_Dispatcher.lua' ) __Moose.Include( 'Tasking/Task_A2G.lua' ) +__Moose.Include( 'Tasking/Task_A2A_Dispatcher.lua' ) +__Moose.Include( 'Tasking/Task_A2A.lua' ) __Moose.Include( 'Tasking/Task_Cargo.lua' ) __Moose.Include( 'Moose.lua' ) BASE:TraceOnOff( true )