From d28ab004431ee86a1a42bc5936e016e46712ab50 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Fri, 6 Feb 2026 12:48:08 +0100 Subject: [PATCH] Delete Moose Development/Moose/Tasking directory prep master-ng --- .../Moose/Tasking/CommandCenter.lua | 823 ------- .../Moose/Tasking/DetectionManager.lua | 404 ---- Moose Development/Moose/Tasking/Mission.lua | 1213 ---------- Moose Development/Moose/Tasking/Task.lua | 2060 ----------------- Moose Development/Moose/Tasking/TaskInfo.lua | 379 --- Moose Development/Moose/Tasking/Task_A2A.lua | 657 ------ .../Moose/Tasking/Task_A2A_Dispatcher.lua | 622 ----- Moose Development/Moose/Tasking/Task_A2G.lua | 638 ----- .../Moose/Tasking/Task_A2G_Dispatcher.lua | 830 ------- .../Moose/Tasking/Task_CARGO.lua | 1412 ----------- .../Moose/Tasking/Task_Capture_Dispatcher.lua | 404 ---- .../Moose/Tasking/Task_Capture_Zone.lua | 336 --- .../Moose/Tasking/Task_Cargo_CSAR.lua | 400 ---- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 921 -------- .../Moose/Tasking/Task_Cargo_Transport.lua | 365 --- .../Moose/Tasking/Task_Manager.lua | 194 -- 16 files changed, 11658 deletions(-) delete mode 100644 Moose Development/Moose/Tasking/CommandCenter.lua delete mode 100644 Moose Development/Moose/Tasking/DetectionManager.lua delete mode 100644 Moose Development/Moose/Tasking/Mission.lua delete mode 100644 Moose Development/Moose/Tasking/Task.lua delete mode 100644 Moose Development/Moose/Tasking/TaskInfo.lua delete mode 100644 Moose Development/Moose/Tasking/Task_A2A.lua delete mode 100644 Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua delete mode 100644 Moose Development/Moose/Tasking/Task_A2G.lua delete mode 100644 Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua delete mode 100644 Moose Development/Moose/Tasking/Task_CARGO.lua delete mode 100644 Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua delete mode 100644 Moose Development/Moose/Tasking/Task_Capture_Zone.lua delete mode 100644 Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua delete mode 100644 Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua delete mode 100644 Moose Development/Moose/Tasking/Task_Cargo_Transport.lua delete mode 100644 Moose Development/Moose/Tasking/Task_Manager.lua diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua deleted file mode 100644 index 68da47300..000000000 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ /dev/null @@ -1,823 +0,0 @@ ---- **Tasking** - A command center governs multiple missions, and takes care of the reporting and communications. --- --- **Features:** --- --- * Govern multiple missions. --- * Communicate to coalitions, groups. --- * Assign tasks. --- * Manage the menus. --- * Manage reference zones. --- --- === --- --- ### Author: **FlightControl** --- --- ### Contributions: --- --- === --- --- @module Tasking.CommandCenter --- @image Task_Command_Center.JPG - - ---- The COMMANDCENTER class --- @type COMMANDCENTER --- @field Wrapper.Group#GROUP HQ --- @field DCS#coalition CommandCenterCoalition --- @list Missions --- @extends Core.Base#BASE - - ---- Governs multiple missions, the tasking and the reporting. --- --- ![Banner Image](..\Images\deprecated.png) --- --- Command centers govern missions, communicates the task assignments between human players of the coalition, and manages the menu flow. --- It can assign a random task to a player when requested. --- The commandcenter provides the facilitites to communicate between human players online, executing a task. --- --- ## 1. Create a command center object. --- --- * @{#COMMANDCENTER.New}(): Creates a new COMMANDCENTER object. --- --- ## 2. Command center mission management. --- --- Command centers manage missions. These can be added, removed and provides means to retrieve missions. --- These methods are heavily used by the task dispatcher classes. --- --- * @{#COMMANDCENTER.AddMission}(): Adds a mission to the commandcenter control. --- * @{#COMMANDCENTER.RemoveMission}(): Removes a mission to the commandcenter control. --- * @{#COMMANDCENTER.GetMissions}(): Retrieves the missions table controlled by the commandcenter. --- --- ## 3. Communication management between players. --- --- Command center provide means of communication between players. --- Because a command center is a central object governing multiple missions, --- there are several levels at which communication needs to be done. --- Within MOOSE, communication is facilitated using the message system within the DCS simulator. --- --- Messages can be sent between players at various levels: --- --- - On a global level, to all players. --- - On a coalition level, only to the players belonging to the same coalition. --- - On a group level, to the players belonging to the same group. --- --- Messages can be sent to **all players** by the command center using the method @{Tasking.CommandCenter#COMMANDCENTER.MessageToAll}(). --- --- To send messages to **the coalition of the command center**, there are two methods available: --- --- - Use the method @{Tasking.CommandCenter#COMMANDCENTER.MessageToCoalition}() to send a specific message to the coalition, with a given message display duration. --- - You can send a specific type of message using the method @{Tasking.CommandCenter#COMMANDCENTER.MessageTypeToCoalition}(). --- This will send a message of a specific type to the coalition, and as a result its display duration will be flexible according the message display time selection by the human player. --- --- To send messages **to the group** of human players, there are also two methods available: --- --- - Use the method @{Tasking.CommandCenter#COMMANDCENTER.MessageToGroup}() to send a specific message to a group, with a given message display duration. --- - You can send a specific type of message using the method @{Tasking.CommandCenter#COMMANDCENTER.MessageTypeToGroup}(). --- This will send a message of a specific type to the group, and as a result its display duration will be flexible according the message display time selection by the human player . --- --- Messages are considered to be sometimes disturbing for human players, therefore, the settings menu provides the means to activate or deactivate messages. --- For more information on the message types and display timings that can be selected and configured using the menu, refer to the @{Core.Settings} menu description. --- --- ## 4. Command center detailed methods. --- --- Various methods are added to manage command centers. --- --- ### 4.1. Naming and description. --- --- There are 3 methods that can be used to retrieve the description of a command center: --- --- - Use the method @{Tasking.CommandCenter#COMMANDCENTER.GetName}() to retrieve the name of the command center. --- This is the name given as part of the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor. --- The returned name using this method, is not to be used for message communication. --- --- A textual description can be retrieved that provides the command center name to be used within message communication: --- --- - @{Tasking.CommandCenter#COMMANDCENTER.GetShortText}() returns the command center name as `CC [CommandCenterName]`. --- - @{Tasking.CommandCenter#COMMANDCENTER.GetText}() returns the command center name as `Command Center [CommandCenterName]`. --- --- ### 4.2. The coalition of the command center. --- --- The method @{Tasking.CommandCenter#COMMANDCENTER.GetCoalition}() returns the coalition of the command center. --- The return value is an enumeration of the type @{DCS#coalition.side}, which contains the RED, BLUE and NEUTRAL coalition. --- --- ### 4.3. The command center is a real object. --- --- The command center must be represented by a live object within the DCS simulator. As a result, the command center --- can be a @{Wrapper.Unit}, a @{Wrapper.Group}, an @{Wrapper.Airbase} or a @{Wrapper.Static} object. --- --- Using the method @{Tasking.CommandCenter#COMMANDCENTER.GetPositionable}() you retrieve the polymorphic positionable object representing --- the command center, but just be aware that you should be able to use the representable object derivation methods. --- --- ### 5. Command center reports. --- --- Because a command center giverns multiple missions, there are several reports available that are generated by command centers. --- These reports are generated using the following methods: --- --- - @{Tasking.CommandCenter#COMMANDCENTER.ReportSummary}(): Creates a summary report of all missions governed by the command center. --- - @{Tasking.CommandCenter#COMMANDCENTER.ReportDetails}(): Creates a detailed report of all missions governed by the command center. --- - @{Tasking.CommandCenter#COMMANDCENTER.ReportMissionPlayers}(): Creates a report listing the players active at the missions governed by the command center. --- --- ## 6. Reference Zones. --- --- Command Centers may be aware of certain Reference Zones within the battleground. These Reference Zones can refer to --- known areas, recognizable buildings or sites, or any other point of interest. --- Command Centers will use these Reference Zones to help pilots with defining coordinates in terms of navigation --- during the WWII era. --- The Reference Zones are related to the WWII mode that the Command Center will operate in. --- Use the method @{#COMMANDCENTER.SetModeWWII}() to set the mode of communication to the WWII mode. --- --- In WWII mode, the Command Center will receive detected targets, and will select for each target the closest --- nearby Reference Zone. This allows pilots to navigate easier through the battle field readying for combat. --- --- The Reference Zones need to be set by the Mission Designer in the Mission Editor. --- Reference Zones are set by normal trigger zones. One can color the zones in a specific color, --- and the radius of the zones doesn't matter, only the point is important. Place the center of these Reference Zones at --- specific scenery objects or points of interest (like cities, rivers, hills, crossing etc). --- The trigger zones indicating a Reference Zone need to follow a specific syntax. --- The name of each trigger zone expressing a Reference Zone need to start with a classification name of the object, --- followed by a #, followed by a symbolic name of the Reference Zone. --- A few examples: --- --- * A church at Tskinvali would be indicated as: *Church#Tskinvali* --- * A train station near Kobuleti would be indicated as: *Station#Kobuleti* --- --- The COMMANDCENTER class contains a method to indicate which trigger zones need to be used as Reference Zones. --- This is done by using the method @{#COMMANDCENTER.SetReferenceZones}(). --- For the moment, only one Reference Zone class can be specified, but in the future, more classes will become possible. --- --- ## 7. Tasks. --- --- ### 7.1. Automatically assign tasks. --- --- One of the most important roles of the command center is the management of tasks. --- The command center can assign automatically tasks to the players using the @{Tasking.CommandCenter#COMMANDCENTER.SetAutoAssignTasks}() method. --- When this method is used with a parameter true; the command center will scan at regular intervals which players in a slot are not having a task assigned. --- For those players; the tasking is enabled to assign automatically a task. --- An Assign Menu will be accessible for the player under the command center menu, to configure the automatic tasking to switched on or off. --- --- ### 7.2. Automatically accept assigned tasks. --- --- When a task is assigned; the mission designer can decide if players are immediately assigned to the task; or they can accept/reject the assigned task. --- Use the method @{Tasking.CommandCenter#COMMANDCENTER.SetAutoAcceptTasks}() to configure this behaviour. --- If the tasks are not automatically accepted; the player will receive a message that he needs to access the command center menu and --- choose from 2 added menu options either to accept or reject the assigned task within 30 seconds. --- If the task is not accepted within 30 seconds; the task will be cancelled and a new task will be assigned. --- --- --- # Developer Note --- --- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE --- Therefore, this class is considered to be deprecated --- --- @field #COMMANDCENTER -COMMANDCENTER = { - ClassName = "COMMANDCENTER", - CommandCenterName = "", - CommandCenterCoalition = nil, - CommandCenterPositionable = nil, - Name = "", - ReferencePoints = {}, - ReferenceNames = {}, - CommunicationMode = "80", -} - - ---- --- @type COMMANDCENTER.AutoAssignMethods -COMMANDCENTER.AutoAssignMethods = { - ["Random"] = 1, - ["Distance"] = 2, - ["Priority"] = 3, - } - ---- The constructor takes an IDENTIFIABLE as the HQ command center. --- @param #COMMANDCENTER self --- @param Wrapper.Positionable#POSITIONABLE CommandCenterPositionable --- @param #string CommandCenterName --- @return #COMMANDCENTER -function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) - - local self = BASE:Inherit( self, BASE:New() ) -- #COMMANDCENTER - - self.CommandCenterPositionable = CommandCenterPositionable - self.CommandCenterName = CommandCenterName or CommandCenterPositionable:GetName() - self.CommandCenterCoalition = CommandCenterPositionable:GetCoalition() - - self.Missions = {} - - self:SetAutoAssignTasks( false ) - self:SetAutoAcceptTasks( true ) - self:SetAutoAssignMethod( COMMANDCENTER.AutoAssignMethods.Distance ) - self:SetFlashStatus( false ) - self:SetMessageDuration(10) - - self:HandleEvent( EVENTS.Birth, - -- @param #COMMANDCENTER self - -- @param Core.Event#EVENTDATA EventData - function( self, EventData ) - if EventData.IniObjectCategory == 1 then - local EventGroup = GROUP:Find( EventData.IniDCSGroup ) - --self:E( { CommandCenter = self:GetName(), EventGroup = EventGroup:GetName(), HasGroup = self:HasGroup( EventGroup ), EventData = EventData } ) - if EventGroup and EventGroup:IsAlive() and self:HasGroup( EventGroup ) then - local CommandCenterMenu = MENU_GROUP:New( EventGroup, self:GetText() ) - local MenuReporting = MENU_GROUP:New( EventGroup, "Missions Reports", CommandCenterMenu ) - local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Status Report", MenuReporting, self.ReportSummary, self, EventGroup ) - local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Players Report", MenuReporting, self.ReportMissionsPlayers, self, EventGroup ) - --self:ReportSummary( EventGroup ) - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! - Mission:JoinUnit( PlayerUnit, PlayerGroup ) - end - self:SetMenu() - end - end - - end - ) - --- -- When a player enters a client or a unit, the CommandCenter will check for each Mission and each Task in the Mission if the player has things to do. --- -- For these elements, it will= --- -- - Set the correct menu. --- -- - Assign the PlayerUnit to the Task if required. --- -- - Send a message to the other players in the group that this player has joined. --- self:HandleEvent( EVENTS.PlayerEnterUnit, --- -- @param #COMMANDCENTER self --- -- @param Core.Event#EVENTDATA EventData --- function( self, EventData ) --- local PlayerUnit = EventData.IniUnit --- for MissionID, Mission in pairs( self:GetMissions() ) do --- local Mission = Mission -- Tasking.Mission#MISSION --- local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled! --- Mission:JoinUnit( PlayerUnit, PlayerGroup ) --- end --- self:SetMenu() --- end --- ) - - -- Handle when a player leaves a slot and goes back to spectators ... - -- The PlayerUnit will be UnAssigned from the Task. - -- When there is no Unit left running the Task, the Task goes into Abort... - self:HandleEvent( EVENTS.MissionEnd, - -- @param #TASK self - -- @param Core.Event#EVENTDATA EventData - function( self, EventData ) - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:Stop() - end - end - ) - - -- Handle when a player leaves a slot and goes back to spectators ... - -- The PlayerUnit will be UnAssigned from the Task. - -- When there is no Unit left running the Task, the Task goes into Abort... - self:HandleEvent( EVENTS.PlayerLeaveUnit, - -- @param #TASK self - -- @param Core.Event#EVENTDATA EventData - function( self, EventData ) - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - if Mission:IsENGAGED() then - Mission:AbortUnit( PlayerUnit ) - end - end - end - ) - - -- Handle when a player crashes ... - -- The PlayerUnit will be UnAssigned from the Task. - -- When there is no Unit left running the Task, the Task goes into Abort... - self:HandleEvent( EVENTS.Crash, - -- @param #TASK self - -- @param Core.Event#EVENTDATA EventData - function( self, EventData ) - local PlayerUnit = EventData.IniUnit - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - if Mission:IsENGAGED() then - Mission:CrashUnit( PlayerUnit ) - end - end - end - ) - - self:SetMenu() - - _SETTINGS:SetSystemMenu( CommandCenterPositionable ) - - self:SetCommandMenu() - - return self -end - ---- Gets the name of the HQ command center. --- @param #COMMANDCENTER self --- @return #string -function COMMANDCENTER:GetName() - - return self.CommandCenterName -end - ---- Gets the text string of the HQ command center. --- @param #COMMANDCENTER self --- @return #string -function COMMANDCENTER:GetText() - - return "Command Center [" .. self.CommandCenterName .. "]" -end - ---- Gets the short text string of the HQ command center. --- @param #COMMANDCENTER self --- @return #string -function COMMANDCENTER:GetShortText() - - return "CC [" .. self.CommandCenterName .. "]" -end - - ---- Gets the coalition of the command center. --- @param #COMMANDCENTER self --- @return #number Coalition of the command center. -function COMMANDCENTER:GetCoalition() - - return self.CommandCenterCoalition -end - - ---- Gets the POSITIONABLE of the HQ command center. --- @param #COMMANDCENTER self --- @return Wrapper.Positionable#POSITIONABLE -function COMMANDCENTER:GetPositionable() - return self.CommandCenterPositionable -end - ---- Get the Missions governed by the HQ command center. --- @param #COMMANDCENTER self --- @return #list -function COMMANDCENTER:GetMissions() - - return self.Missions or {} -end - ---- Add a MISSION to be governed by the HQ command center. --- @param #COMMANDCENTER self --- @param Tasking.Mission#MISSION Mission --- @return Tasking.Mission#MISSION -function COMMANDCENTER:AddMission( Mission ) - - self.Missions[Mission] = Mission - - return Mission -end - ---- Removes a MISSION to be governed by the HQ command center. --- The given Mission is not nilified. --- @param #COMMANDCENTER self --- @param Tasking.Mission#MISSION Mission --- @return Tasking.Mission#MISSION -function COMMANDCENTER:RemoveMission( Mission ) - - self.Missions[Mission] = nil - - return Mission -end - ---- Set special Reference Zones known by the Command Center to guide airborne pilots during WWII. --- --- These Reference Zones are normal trigger zones, with a special naming. --- The Reference Zones need to be set by the Mission Designer in the Mission Editor. --- Reference Zones are set by normal trigger zones. One can color the zones in a specific color, --- and the radius of the zones doesn't matter, only the center of the zone is important. Place the center of these Reference Zones at --- specific scenery objects or points of interest (like cities, rivers, hills, crossing etc). --- The trigger zones indicating a Reference Zone need to follow a specific syntax. --- The name of each trigger zone expressing a Reference Zone need to start with a classification name of the object, --- followed by a #, followed by a symbolic name of the Reference Zone. --- A few examples: --- --- * A church at Tskinvali would be indicated as: *Church#Tskinvali* --- * A train station near Kobuleti would be indicated as: *Station#Kobuleti* --- --- Taking the above example, this is how this method would be used: --- --- CC:SetReferenceZones( "Church" ) --- CC:SetReferenceZones( "Station" ) --- --- --- @param #COMMANDCENTER self --- @param #string ReferenceZonePrefix The name before the #-mark indicating the class of the Reference Zones. --- @return #COMMANDCENTER -function COMMANDCENTER:SetReferenceZones( ReferenceZonePrefix ) - local MatchPattern = "(.*)#(.*)" - self:F( { MatchPattern = MatchPattern } ) - for ReferenceZoneName in pairs( _DATABASE.ZONENAMES ) do - local ZoneName, ReferenceName = string.match( ReferenceZoneName, MatchPattern ) - self:F( { ZoneName = ZoneName, ReferenceName = ReferenceName } ) - if ZoneName and ReferenceName and ZoneName == ReferenceZonePrefix then - self.ReferencePoints[ReferenceZoneName] = ZONE:New( ReferenceZoneName ) - self.ReferenceNames[ReferenceZoneName] = ReferenceName - end - end - return self -end - ---- Set the commandcenter operations in WWII mode --- This will disable LL, MGRS, BRA, BULLS navigatin messages sent by the Command Center, --- and will be replaced by a navigation using Reference Zones. --- It will also disable the settings at the settings menu for these. --- @param #COMMANDCENTER self --- @return #COMMANDCENTER -function COMMANDCENTER:SetModeWWII() - self.CommunicationMode = "WWII" - return self -end - - ---- Returns if the commandcenter operations is in WWII mode --- @param #COMMANDCENTER self --- @return #boolean true if in WWII mode. -function COMMANDCENTER:IsModeWWII() - return self.CommunicationMode == "WWII" -end - - - - ---- Sets the menu structure of the Missions governed by the HQ command center. --- @param #COMMANDCENTER self -function COMMANDCENTER:SetMenu() - self:F2() - - local MenuTime = timer.getTime() - for MissionID, Mission in pairs( self:GetMissions() or {} ) do - local Mission = Mission -- Tasking.Mission#MISSION - Mission:SetMenu( MenuTime ) - end - - for MissionID, Mission in pairs( self:GetMissions() or {} ) do - Mission = Mission -- Tasking.Mission#MISSION - Mission:RemoveMenu( MenuTime ) - end - -end - ---- Gets the commandcenter menu structure governed by the HQ command center. --- @param #COMMANDCENTER self --- @param Wrapper.Group#Group TaskGroup Task Group. --- @return Core.Menu#MENU_COALITION -function COMMANDCENTER:GetMenu( TaskGroup ) - - local MenuTime = timer.getTime() - - self.CommandCenterMenus = self.CommandCenterMenus or {} - local CommandCenterMenu - - local CommandCenterText = self:GetText() - CommandCenterMenu = MENU_GROUP:New( TaskGroup, CommandCenterText ):SetTime(MenuTime) - self.CommandCenterMenus[TaskGroup] = CommandCenterMenu - - if self.AutoAssignTasks == false then - local AssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task", CommandCenterMenu, self.AssignTask, self, TaskGroup ):SetTime(MenuTime):SetTag("AutoTask") - end - CommandCenterMenu:Remove( MenuTime, "AutoTask" ) - - return self.CommandCenterMenus[TaskGroup] -end - - ---- Assigns a random task to a TaskGroup. --- @param #COMMANDCENTER self --- @return #COMMANDCENTER -function COMMANDCENTER:AssignTask( TaskGroup ) - - local Tasks = {} - local AssignPriority = 99999999 - local AutoAssignMethod = self.AutoAssignMethod - - for MissionID, Mission in pairs( self:GetMissions() ) do - local Mission = Mission -- Tasking.Mission#MISSION - local MissionTasks = Mission:GetGroupTasks( TaskGroup ) - for MissionTaskName, MissionTask in pairs( MissionTasks or {} ) do - local MissionTask = MissionTask -- Tasking.Task#TASK - if MissionTask:IsStatePlanned() or MissionTask:IsStateReplanned() or MissionTask:IsStateAssigned() then - local TaskPriority = MissionTask:GetAutoAssignPriority( self.AutoAssignMethod, self, TaskGroup ) - if TaskPriority < AssignPriority then - AssignPriority = TaskPriority - Tasks = {} - end - if TaskPriority == AssignPriority then - Tasks[#Tasks+1] = MissionTask - end - end - end - end - - local Task = Tasks[ math.random( 1, #Tasks ) ] -- Tasking.Task#TASK - - if Task then - - self:T( "Assigning task " .. Task:GetName() .. " using auto assign method " .. self.AutoAssignMethod .. " to " .. TaskGroup:GetName() .. " with task priority " .. AssignPriority ) - - if not self.AutoAcceptTasks == true then - Task:SetAutoAssignMethod( ACT_ASSIGN_MENU_ACCEPT:New( Task.TaskBriefing ) ) - end - - Task:AssignToGroup( TaskGroup ) - - end - -end - - ---- Sets the menu of the command center. --- This command is called within the :New() method. --- @param #COMMANDCENTER self -function COMMANDCENTER:SetCommandMenu() - - local MenuTime = timer.getTime() - - if self.CommandCenterPositionable and self.CommandCenterPositionable:IsInstanceOf(GROUP) then - local CommandCenterText = self:GetText() - local CommandCenterMenu = MENU_GROUP:New( self.CommandCenterPositionable, CommandCenterText ):SetTime(MenuTime) - - if self.AutoAssignTasks == false then - local AutoAssignTaskMenu = MENU_GROUP_COMMAND:New( self.CommandCenterPositionable, "Assign Task On", CommandCenterMenu, self.SetAutoAssignTasks, self, true ):SetTime(MenuTime):SetTag("AutoTask") - else - local AutoAssignTaskMenu = MENU_GROUP_COMMAND:New( self.CommandCenterPositionable, "Assign Task Off", CommandCenterMenu, self.SetAutoAssignTasks, self, false ):SetTime(MenuTime):SetTag("AutoTask") - end - CommandCenterMenu:Remove( MenuTime, "AutoTask" ) - end - -end - - - ---- Automatically assigns tasks to all TaskGroups. --- One of the most important roles of the command center is the management of tasks. --- When this method is used with a parameter true; the command center will scan at regular intervals which players in a slot are not having a task assigned. --- For those players; the tasking is enabled to assign automatically a task. --- An Assign Menu will be accessible for the player under the command center menu, to configure the automatic tasking to switched on or off. --- @param #COMMANDCENTER self --- @param #boolean AutoAssign true for ON and false or nil for OFF. -function COMMANDCENTER:SetAutoAssignTasks( AutoAssign ) - - self.AutoAssignTasks = AutoAssign or false - - if self.AutoAssignTasks == true then - self.autoAssignTasksScheduleID=self:ScheduleRepeat( 10, 30, 0, nil, self.AssignTasks, self ) - else - self:ScheduleStop() - -- FF this is not the schedule ID - --self:ScheduleStop( self.AssignTasks ) - end - -end - ---- Automatically accept tasks for all TaskGroups. --- When a task is assigned; the mission designer can decide if players are immediately assigned to the task; or they can accept/reject the assigned task. --- If the tasks are not automatically accepted; the player will receive a message that he needs to access the command center menu and --- choose from 2 added menu options either to accept or reject the assigned task within 30 seconds. --- If the task is not accepted within 30 seconds; the task will be cancelled and a new task will be assigned. --- @param #COMMANDCENTER self --- @param #boolean AutoAccept true for ON and false or nil for OFF. -function COMMANDCENTER:SetAutoAcceptTasks( AutoAccept ) - - self.AutoAcceptTasks = AutoAccept or false - -end - - ---- Define the method to be used to assign automatically a task from the available tasks in the mission. --- There are 3 types of methods that can be applied for the moment: --- --- 1. Random - assigns a random task in the mission to the player. --- 2. Distance - assigns a task based on a distance evaluation from the player. The closest are to be assigned first. --- 3. Priority - assigns a task based on the priority as defined by the mission designer, using the SetTaskPriority parameter. --- --- The different task classes implement the logic to determine the priority of automatic task assignment to a player, depending on one of the above methods. --- The method @{Tasking.Task#TASK.GetAutoAssignPriority} calculate the priority of the tasks to be assigned. --- @param #COMMANDCENTER self --- @param #COMMANDCENTER.AutoAssignMethods AutoAssignMethod A selection of an assign method from the COMMANDCENTER.AutoAssignMethods enumeration. -function COMMANDCENTER:SetAutoAssignMethod( AutoAssignMethod ) - - self.AutoAssignMethod = AutoAssignMethod or COMMANDCENTER.AutoAssignMethods.Random - -end - ---- Automatically assigns tasks to all TaskGroups. --- @param #COMMANDCENTER self -function COMMANDCENTER:AssignTasks() - - local GroupSet = self:AddGroups() - - for GroupID, TaskGroup in pairs( GroupSet:GetSet() ) do - local TaskGroup = TaskGroup -- Wrapper.Group#GROUP - - if TaskGroup:IsAlive() then - self:GetMenu( TaskGroup ) - - if self:IsGroupAssigned( TaskGroup ) then - else - -- Only groups with planes or helicopters will receive automatic tasks. - -- TODO Workaround DCS-BUG-3 - https://github.com/FlightControl-Master/MOOSE/issues/696 - if TaskGroup:IsAir() then - self:AssignTask( TaskGroup ) - end - end - end - end - -end - - ---- Get all the Groups active within the command center. --- @param #COMMANDCENTER self --- @return Core.Set#SET_GROUP The set of groups active within the command center. -function COMMANDCENTER:AddGroups() - - local GroupSet = SET_GROUP:New() - - for MissionID, Mission in pairs( self.Missions ) do - local Mission = Mission -- Tasking.Mission#MISSION - GroupSet = Mission:AddGroups( GroupSet ) - end - - return GroupSet -end - - ---- Checks of the TaskGroup has a Task. --- @param #COMMANDCENTER self --- @return #boolean When true, the TaskGroup has a Task, otherwise the returned value will be false. -function COMMANDCENTER:IsGroupAssigned( TaskGroup ) - - local Assigned = false - - for MissionID, Mission in pairs( self.Missions ) do - local Mission = Mission -- Tasking.Mission#MISSION - if Mission:IsGroupAssigned( TaskGroup ) then - Assigned = true - break - end - end - - return Assigned -end - - ---- Checks of the command center has the given MissionGroup. --- @param #COMMANDCENTER self --- @param Wrapper.Group#GROUP MissionGroup The group active within one of the missions governed by the command center. --- @return #boolean -function COMMANDCENTER:HasGroup( MissionGroup ) - - local Has = false - - for MissionID, Mission in pairs( self.Missions ) do - local Mission = Mission -- Tasking.Mission#MISSION - if Mission:HasGroup( MissionGroup ) then - Has = true - break - end - end - - return Has -end - ---- Let the command center send a Message to all players. --- @param #COMMANDCENTER self --- @param #string Message The message text. -function COMMANDCENTER:MessageToAll( Message ) - - self:GetPositionable():MessageToAll( Message, self.MessageDuration, self:GetName() ) - -end - ---- Let the command center send a message to the MessageGroup. --- @param #COMMANDCENTER self --- @param #string Message The message text. --- @param Wrapper.Group#GROUP MessageGroup The group to receive the message. -function COMMANDCENTER:MessageToGroup( Message, MessageGroup ) - - self:GetPositionable():MessageToGroup( Message, self.MessageDuration, MessageGroup, self:GetShortText() ) - -end - ---- Let the command center send a message to the MessageGroup. --- @param #COMMANDCENTER self --- @param #string Message The message text. --- @param Wrapper.Group#GROUP MessageGroup The group to receive the message. --- @param Core.Message#MESSAGE.MessageType MessageType The type of the message, resulting in automatic time duration and prefix of the message. -function COMMANDCENTER:MessageTypeToGroup( Message, MessageGroup, MessageType ) - - self:GetPositionable():MessageTypeToGroup( Message, MessageType, MessageGroup, self:GetShortText() ) - -end - ---- Let the command center send a message to the coalition of the command center. --- @param #COMMANDCENTER self --- @param #string Message The message text. -function COMMANDCENTER:MessageToCoalition( Message ) - - local CCCoalition = self:GetPositionable():GetCoalition() - --TODO: Fix coalition bug! - - self:GetPositionable():MessageToCoalition( Message, self.MessageDuration, CCCoalition, self:GetShortText() ) - -end - - ---- Let the command center send a message of a specified type to the coalition of the command center. --- @param #COMMANDCENTER self --- @param #string Message The message text. --- @param Core.Message#MESSAGE.MessageType MessageType The type of the message, resulting in automatic time duration and prefix of the message. -function COMMANDCENTER:MessageTypeToCoalition( Message, MessageType ) - - local CCCoalition = self:GetPositionable():GetCoalition() - --TODO: Fix coalition bug! - - self:GetPositionable():MessageTypeToCoalition( Message, MessageType, CCCoalition, self:GetShortText() ) - -end - - ---- Let the command center send a report of the status of all missions to a group. --- Each Mission is listed, with an indication how many Tasks are still to be completed. --- @param #COMMANDCENTER self --- @param Wrapper.Group#GROUP ReportGroup The group to receive the report. -function COMMANDCENTER:ReportSummary( ReportGroup ) - self:F( ReportGroup ) - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetName() - Report:Add( string.format( '%s - Report Summary Missions', Name ) ) - - for MissionID, Mission in pairs( self.Missions ) do - local Mission = Mission -- Tasking.Mission#MISSION - Report:Add( " - " .. Mission:ReportSummary( ReportGroup ) ) - end - - self:MessageToGroup( Report:Text(), ReportGroup ) -end - ---- Let the command center send a report of the players of all missions to a group. --- Each Mission is listed, with an indication how many Tasks are still to be completed. --- @param #COMMANDCENTER self --- @param Wrapper.Group#GROUP ReportGroup The group to receive the report. -function COMMANDCENTER:ReportMissionsPlayers( ReportGroup ) - self:F( ReportGroup ) - - local Report = REPORT:New() - - Report:Add( "Players active in all missions." ) - - for MissionID, MissionData in pairs( self.Missions ) do - local Mission = MissionData -- Tasking.Mission#MISSION - Report:Add( " - " .. Mission:ReportPlayersPerTask(ReportGroup) ) - end - - self:MessageToGroup( Report:Text(), ReportGroup ) -end - ---- Let the command center send a report of the status of a task to a group. --- Report the details of a Mission, listing the Mission, and all the Task details. --- @param #COMMANDCENTER self --- @param Wrapper.Group#GROUP ReportGroup The group to receive the report. --- @param Tasking.Task#TASK Task The task to be reported. -function COMMANDCENTER:ReportDetails( ReportGroup, Task ) - self:F( ReportGroup ) - - local Report = REPORT:New() - - for MissionID, Mission in pairs( self.Missions ) do - local Mission = Mission -- Tasking.Mission#MISSION - Report:Add( " - " .. Mission:ReportDetails() ) - end - - self:MessageToGroup( Report:Text(), ReportGroup ) -end - - ---- Let the command center flash a report of the status of the subscribed task to a group. --- @param #COMMANDCENTER self --- @param Flash #boolean -function COMMANDCENTER:SetFlashStatus( Flash ) - self:F() - - self.FlashStatus = Flash and true -end - ---- Duration a command center message is shown. --- @param #COMMANDCENTER self --- @param seconds #number -function COMMANDCENTER:SetMessageDuration(seconds) - self:F() - - self.MessageDuration = 10 or seconds -end diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua deleted file mode 100644 index 16025282a..000000000 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ /dev/null @@ -1,404 +0,0 @@ ---- **Tasking** - This module contains the DETECTION_MANAGER class and derived classes. --- --- === --- --- The @{#DETECTION_MANAGER} class defines the core functions to report detected objects to groups. --- Reportings can be done in several manners, and it is up to the derived classes if DETECTION_MANAGER to model the reporting behaviour. --- --- ![Banner Image](..\Images\deprecated.png) --- --- 1.1) DETECTION_MANAGER constructor: --- ----------------------------------- --- * @{#DETECTION_MANAGER.New}(): Create a new DETECTION_MANAGER instance. --- --- 1.2) DETECTION_MANAGER reporting: --- --------------------------------- --- Derived DETECTION_MANAGER classes will reports detected units using the method @{#DETECTION_MANAGER.ReportDetected}(). This method implements polymorphic behaviour. --- --- The time interval in seconds of the reporting can be changed using the methods @{#DETECTION_MANAGER.SetRefreshTimeInterval}(). --- To control how long a reporting message is displayed, use @{#DETECTION_MANAGER.SetReportDisplayTime}(). --- Derived classes need to implement the method @{#DETECTION_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report. --- --- Reporting can be started and stopped using the methods @{#DETECTION_MANAGER.StartReporting}() and @{#DETECTION_MANAGER.StopReporting}() respectively. --- If an ad-hoc report is requested, use the method @{#DETECTION_MANAGER.ReportNow}(). --- --- The default reporting interval is every 60 seconds. The reporting messages are displayed 15 seconds. --- --- === --- --- 2) @{#DETECTION_REPORTING} class, extends @{#DETECTION_MANAGER} --- === --- The @{#DETECTION_REPORTING} class implements detected units reporting. Reporting can be controlled using the reporting methods available in the @{Tasking.DetectionManager#DETECTION_MANAGER} class. --- --- 2.1) DETECTION_REPORTING constructor: --- ------------------------------- --- The @{#DETECTION_REPORTING.New}() method creates a new DETECTION_REPORTING instance. --- --- --- # Developer Note --- --- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE --- Therefore, this class is considered to be deprecated --- --- === --- --- ### Contributions: Mechanist, Prof_Hilactic, FlightControl - Concept & Testing --- ### Author: FlightControl - Framework Design & Programming --- --- @module Tasking.DetectionManager --- @image Task_Detection_Manager.JPG - -do -- DETECTION MANAGER - - -- @type DETECTION_MANAGER - -- @field Core.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. - -- @field Tasking.CommandCenter#COMMANDCENTER CC The command center that is used to communicate with the players. - -- @extends Core.Fsm#FSM - - --- DETECTION_MANAGER class. - -- @field #DETECTION_MANAGER - DETECTION_MANAGER = { - ClassName = "DETECTION_MANAGER", - SetGroup = nil, - Detection = nil, - } - - -- @field Tasking.CommandCenter#COMMANDCENTER - DETECTION_MANAGER.CC = nil - - --- FAC constructor. - -- @param #DETECTION_MANAGER self - -- @param Core.Set#SET_GROUP SetGroup - -- @param Functional.Detection#DETECTION_BASE Detection - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:New( SetGroup, Detection ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_MANAGER - - self.SetGroup = SetGroup - self.Detection = Detection - - self:SetStartState( "Stopped" ) - self:AddTransition( "Stopped", "Start", "Started" ) - - --- Start Handler OnBefore for DETECTION_MANAGER - -- @function [parent=#DETECTION_MANAGER] OnBeforeStart - -- @param #DETECTION_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @return #boolean - - --- Start Handler OnAfter for DETECTION_MANAGER - -- @function [parent=#DETECTION_MANAGER] OnAfterStart - -- @param #DETECTION_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - - --- Start Trigger for DETECTION_MANAGER - -- @function [parent=#DETECTION_MANAGER] Start - -- @param #DETECTION_MANAGER self - - --- Start Asynchronous Trigger for DETECTION_MANAGER - -- @function [parent=#DETECTION_MANAGER] __Start - -- @param #DETECTION_MANAGER self - -- @param #number Delay - - - - self:AddTransition( "Started", "Stop", "Stopped" ) - - --- Stop Handler OnBefore for DETECTION_MANAGER - -- @function [parent=#DETECTION_MANAGER] OnBeforeStop - -- @param #DETECTION_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @return #boolean - - --- Stop Handler OnAfter for DETECTION_MANAGER - -- @function [parent=#DETECTION_MANAGER] OnAfterStop - -- @param #DETECTION_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - - --- Stop Trigger for DETECTION_MANAGER - -- @function [parent=#DETECTION_MANAGER] Stop - -- @param #DETECTION_MANAGER self - - --- Stop Asynchronous Trigger for DETECTION_MANAGER - -- @function [parent=#DETECTION_MANAGER] __Stop - -- @param #DETECTION_MANAGER self - -- @param #number Delay - - self:AddTransition( "Started", "Success", "Started" ) - - --- Success Handler OnAfter for DETECTION_MANAGER - -- @function [parent=#DETECTION_MANAGER] OnAfterSuccess - -- @param #DETECTION_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Tasking.Task#TASK Task - - - self:AddTransition( "Started", "Failed", "Started" ) - - --- Failed Handler OnAfter for DETECTION_MANAGER - -- @function [parent=#DETECTION_MANAGER] OnAfterFailed - -- @param #DETECTION_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Tasking.Task#TASK Task - - - self:AddTransition( "Started", "Aborted", "Started" ) - - --- Aborted Handler OnAfter for DETECTION_MANAGER - -- @function [parent=#DETECTION_MANAGER] OnAfterAborted - -- @param #DETECTION_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Tasking.Task#TASK Task - - self:AddTransition( "Started", "Cancelled", "Started" ) - - --- Cancelled Handler OnAfter for DETECTION_MANAGER - -- @function [parent=#DETECTION_MANAGER] OnAfterCancelled - -- @param #DETECTION_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Tasking.Task#TASK Task - - - self:AddTransition( "Started", "Report", "Started" ) - - self:SetRefreshTimeInterval( 30 ) - self:SetReportDisplayTime( 25 ) - - Detection:__Start( 3 ) - - return self - end - - function DETECTION_MANAGER:onafterStart( From, Event, To ) - self:Report() - end - - function DETECTION_MANAGER:onafterReport( From, Event, To ) - - self:__Report( -self._RefreshTimeInterval ) - - self:ProcessDetected( self.Detection ) - end - - --- Set the reporting time interval. - -- @param #DETECTION_MANAGER self - -- @param #number RefreshTimeInterval The interval in seconds when a report needs to be done. - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:SetRefreshTimeInterval( RefreshTimeInterval ) - self:F2() - - self._RefreshTimeInterval = RefreshTimeInterval - end - - - --- Set the reporting message display time. - -- @param #DETECTION_MANAGER self - -- @param #number ReportDisplayTime The display time in seconds when a report needs to be done. - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:SetReportDisplayTime( ReportDisplayTime ) - self:F2() - - self._ReportDisplayTime = ReportDisplayTime - end - - --- Get the reporting message display time. - -- @param #DETECTION_MANAGER self - -- @return #number ReportDisplayTime The display time in seconds when a report needs to be done. - function DETECTION_MANAGER:GetReportDisplayTime() - self:F2() - - return self._ReportDisplayTime - end - - - --- Set a command center to communicate actions to the players reporting to the command center. - -- @param #DETECTION_MANAGER self - -- @return #DETECTION_MANGER self - function DETECTION_MANAGER:SetTacticalMenu( DispatcherMainMenuText, DispatcherMenuText ) - - local DispatcherMainMenu = MENU_MISSION:New( DispatcherMainMenuText, nil ) - local DispatcherMenu = MENU_MISSION_COMMAND:New( DispatcherMenuText, DispatcherMainMenu, - function() - self:ShowTacticalDisplay( self.Detection ) - end - ) - - return self - end - - - - - --- Set a command center to communicate actions to the players reporting to the command center. - -- @param #DETECTION_MANAGER self - -- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center. - -- @return #DETECTION_MANGER self - function DETECTION_MANAGER:SetCommandCenter( CommandCenter ) - - self.CC = CommandCenter - - return self - end - - - --- Get the command center to communicate actions to the players. - -- @param #DETECTION_MANAGER self - -- @return Tasking.CommandCenter#COMMANDCENTER The command center. - function DETECTION_MANAGER:GetCommandCenter() - - return self.CC - end - - - --- Send an information message to the players reporting to the command center. - -- @param #DETECTION_MANAGER self - -- @param #table Squadron The squadron table. - -- @param #string Message The message to be sent. - -- @param #string SoundFile The name of the sound file .wav or .ogg. - -- @param #number SoundDuration The duration of the sound. - -- @param #string SoundPath The path pointing to the folder in the mission file. - -- @param Wrapper.Group#GROUP DefenderGroup The defender group sending the message. - -- @return #DETECTION_MANGER self - function DETECTION_MANAGER:MessageToPlayers( Squadron, Message, DefenderGroup ) - - self:F( { Message = Message } ) - --- if not self.PreviousMessage or self.PreviousMessage ~= Message then --- self.PreviousMessage = Message --- if self.CC then --- self.CC:MessageToCoalition( Message ) --- end --- end - - if self.CC then - self.CC:MessageToCoalition( Message ) - end - - Message = Message:gsub( "°", " degrees " ) - Message = Message:gsub( "(%d)%.(%d)", "%1 dot %2" ) - - -- Here we handle the transmission of the voice over. - -- If for a certain reason the Defender does not exist, we use the coordinate of the airbase to send the message from. - local RadioQueue = Squadron.RadioQueue -- Core.RadioSpeech#RADIOSPEECH - if RadioQueue then - local DefenderUnit = DefenderGroup:GetUnit(1) - if DefenderUnit and DefenderUnit:IsAlive() then - RadioQueue:SetSenderUnitName( DefenderUnit:GetName() ) - end - RadioQueue:Speak( Message, Squadron.Language ) - end - - return self - end - - - - --- Reports the detected items to the @{Core.Set#SET_GROUP}. - -- @param #DETECTION_MANAGER self - -- @param Functional.Detection#DETECTION_BASE Detection - -- @return #DETECTION_MANAGER self - function DETECTION_MANAGER:ProcessDetected( Detection ) - - end - -end - - -do -- DETECTION_REPORTING - - --- DETECTION_REPORTING class. - -- @type DETECTION_REPORTING - -- @field Core.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. - -- @extends #DETECTION_MANAGER - DETECTION_REPORTING = { - ClassName = "DETECTION_REPORTING", - } - - - --- DETECTION_REPORTING constructor. - -- @param #DETECTION_REPORTING self - -- @param Core.Set#SET_GROUP SetGroup - -- @param Functional.Detection#DETECTION_AREAS Detection - -- @return #DETECTION_REPORTING self - function DETECTION_REPORTING:New( SetGroup, Detection ) - - -- Inherits from DETECTION_MANAGER - local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #DETECTION_REPORTING - - self:Schedule( 1, 30 ) - return self - end - - --- Creates a string of the detected items in a @{Functional.Detection} object. - -- @param #DETECTION_MANAGER self - -- @param Core.Set#SET_UNIT DetectedSet The detected Set created by the @{Functional.Detection#DETECTION_BASE} object. - -- @return #DETECTION_MANAGER self - function DETECTION_REPORTING:GetDetectedItemsText( DetectedSet ) - self:F2() - - local MT = {} -- Message Text - local UnitTypes = {} - - for DetectedUnitID, DetectedUnitData in pairs( DetectedSet:GetSet() ) do - local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT - if DetectedUnit:IsAlive() then - local UnitType = DetectedUnit:GetTypeName() - - if not UnitTypes[UnitType] then - UnitTypes[UnitType] = 1 - else - UnitTypes[UnitType] = UnitTypes[UnitType] + 1 - end - end - end - - for UnitTypeID, UnitType in pairs( UnitTypes ) do - MT[#MT+1] = UnitType .. " of " .. UnitTypeID - end - - return table.concat( MT, ", " ) - end - - - - --- Reports the detected items to the @{Core.Set#SET_GROUP}. - -- @param #DETECTION_REPORTING self - -- @param Wrapper.Group#GROUP Group The @{Wrapper.Group} object to where the report needs to go. - -- @param Functional.Detection#DETECTION_AREAS Detection The detection created by the @{Functional.Detection#DETECTION_BASE} object. - -- @return #boolean Return true if you want the reporting to continue... false will cancel the reporting loop. - function DETECTION_REPORTING:ProcessDetected( Group, Detection ) - self:F2( Group ) - - local DetectedMsg = {} - for DetectedAreaID, DetectedAreaData in pairs( Detection:GetDetectedAreas() ) do - local DetectedArea = DetectedAreaData -- Functional.Detection#DETECTION_AREAS.DetectedArea - DetectedMsg[#DetectedMsg+1] = " - Group #" .. DetectedAreaID .. ": " .. self:GetDetectedItemsText( DetectedArea.Set ) - end - local FACGroup = Detection:GetDetectionGroups() - FACGroup:MessageToGroup( "Reporting detected target groups:\n" .. table.concat( DetectedMsg, "\n" ), self:GetReportDisplayTime(), Group ) - - return true - end - -end - diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua deleted file mode 100644 index 8cd08f087..000000000 --- a/Moose Development/Moose/Tasking/Mission.lua +++ /dev/null @@ -1,1213 +0,0 @@ ---- **Tasking** - A mission models a goal to be achieved through the execution and completion of tasks by human players. --- --- **Features:** --- --- * A mission has a goal to be achieved, through the execution and completion of tasks of different categories by human players. --- * A mission manages these tasks. --- * A mission has a state, that indicates the fase of the mission. --- * A mission has a menu structure, that facilitates mission reports and tasking menus. --- * A mission can assign a task to a player. --- --- === --- --- ### Author: **FlightControl** --- --- ### Contributions: --- --- === --- --- @module Tasking.Mission --- @image Task_Mission.JPG - ---- --- @type MISSION --- @field #MISSION.Clients _Clients --- @field Core.Menu#MENU_COALITION MissionMenu --- @field #string MissionBriefing --- @extends Core.Fsm#FSM - ---- Models goals to be achieved and can contain multiple tasks to be executed to achieve the goals. --- --- ![Banner Image](..\Images\deprecated.png) --- --- A mission contains multiple tasks and can be of different task types. --- These tasks need to be assigned to human players to be executed. --- --- A mission can have multiple states, which will evolve as the mission progresses during the DCS simulation. --- --- - **IDLE**: The mission is defined, but not started yet. No task has yet been joined by a human player as part of the mission. --- - **ENGAGED**: The mission is ongoing, players have joined tasks to be executed. --- - **COMPLETED**: The goals of the mission has been successfully reached, and the mission is flagged as completed. --- - **FAILED**: For a certain reason, the goals of the mission has not been reached, and the mission is flagged as failed. --- - **HOLD**: The mission was enaged, but for some reason it has been put on hold. --- --- Note that a mission goals need to be checked by a goal check trigger: @{#MISSION.OnBeforeMissionGoals}(), which may return false if the goal has not been reached. --- This goal is checked automatically by the mission object every x seconds. --- --- - @{#MISSION.Start}() or @{#MISSION.__Start}() will start the mission, and will bring it from **IDLE** state to **ENGAGED** state. --- - @{#MISSION.Stop}() or @{#MISSION.__Stop}() will stop the mission, and will bring it from **ENGAGED** state to **IDLE** state. --- - @{#MISSION.Complete}() or @{#MISSION.__Complete}() will complete the mission, and will bring the mission state to **COMPLETED**. --- Note that the mission must be in state **ENGAGED** to be able to complete the mission. --- - @{#MISSION.Fail}() or @{#MISSION.__Fail}() will fail the mission, and will bring the mission state to **FAILED**. --- Note that the mission must be in state **ENGAGED** to be able to fail the mission. --- - @{#MISSION.Hold}() or @{#MISSION.__Hold}() will hold the mission, and will bring the mission state to **HOLD**. --- Note that the mission must be in state **ENGAGED** to be able to hold the mission. --- Re-engage the mission using the engage trigger. --- --- The following sections provide an overview of the most important methods that can be used as part of a mission object. --- Note that the @{Tasking.CommandCenter} system is using most of these methods to manage the missions in its system. --- --- ## 1. Create a mission object. --- --- - @{#MISSION.New}(): Creates a new MISSION object. --- --- ## 2. Mission task management. --- --- Missions maintain tasks, which can be added or removed, or enquired. --- --- - @{#MISSION.AddTask}(): Adds a task to the mission. --- - @{#MISSION.RemoveTask}(): Removes a task from the mission. --- --- ## 3. Mission detailed methods. --- --- Various methods are added to manage missions. --- --- ### 3.1. Naming and description. --- --- There are several methods that can be used to retrieve the properties of a mission: --- --- - Use the method @{#MISSION.GetName}() to retrieve the name of the mission. --- This is the name given as part of the @{#MISSION.New}() constructor. --- --- A textual description can be retrieved that provides the mission name to be used within message communication: --- --- - @{#MISSION.GetShortText}() returns the mission name as `Mission "MissionName"`. --- - @{#MISSION.GetText}() returns the mission name as `Mission "MissionName (MissionPriority)"`. A longer version including the priority text of the mission. --- --- ### 3.2. Get task information. --- --- - @{#MISSION.GetTasks}(): Retrieves a list of the tasks controlled by the mission. --- - @{#MISSION.GetTask}(): Retrieves a specific task controlled by the mission. --- - @{#MISSION.GetTasksRemaining}(): Retrieve a list of the tasks that aren't finished or failed, and are governed by the mission. --- - @{#MISSION.GetGroupTasks}(): Retrieve a list of the tasks that can be assigned to a @{Wrapper.Group}. --- - @{#MISSION.GetTaskTypes}(): Retrieve a list of the different task types governed by the mission. --- --- ### 3.3. Get the command center. --- --- - @{#MISSION.GetCommandCenter}(): Retrieves the @{Tasking.CommandCenter} governing the mission. --- --- ### 3.4. Get the groups active in the mission as a @{Core.Set}. --- --- - @{#MISSION.GetGroups}(): Retrieves a @{Core.Set#SET_GROUP} of all the groups active in the mission (as part of the tasks). --- --- ### 3.5. Get the names of the players. --- --- - @{#MISSION.GetPlayerNames}(): Retrieves the list of the players that were active within th mission.. --- --- ## 4. Menu management. --- --- A mission object is able to manage its own menu structure. Use the @{#MISSION.GetMenu}() and @{#MISSION.SetMenu}() to manage the underlying submenu --- structure managing the tasks of the mission. --- --- ## 5. Reporting management. --- --- Several reports can be generated for a mission, and will return a text string that can be used to display using the @{Core.Message} system. --- --- - @{#MISSION.ReportBriefing}(): Generates the briefing for the mission. --- - @{#MISSION.ReportOverview}(): Generates an overview of the tasks and status of the mission. --- - @{#MISSION.ReportDetails}(): Generates a detailed report of the tasks of the mission. --- - @{#MISSION.ReportSummary}(): Generates a summary report of the tasks of the mission. --- - @{#MISSION.ReportPlayersPerTask}(): Generates a report showing the active players per task. --- - @{#MISSION.ReportPlayersProgress}(): Generates a report showing the task progress per player. --- --- --- # Developer Note --- --- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE --- Therefore, this class is considered to be deprecated --- --- @field #MISSION -MISSION = { - ClassName = "MISSION", - Name = "", - MissionStatus = "PENDING", - AssignedGroups = {}, -} - ---- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. --- @param #MISSION self --- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter --- @param #string MissionName Name of the mission. This name will be used to reference the status of each mission by the players. --- @param #string MissionPriority String indicating the "priority" of the Mission. e.g. "Primary", "Secondary". It is free format and up to the Mission designer to choose. There are no rules behind this field. --- @param #string MissionBriefing String indicating the mission briefing to be shown when a player joins a @{Wrapper.Client#CLIENT}. --- @param DCS#coalition.side MissionCoalition Side of the coalition, i.e. and enumerator @{#DCS.coalition.side} corresponding to RED, BLUE or NEUTRAL. --- @return #MISSION self -function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition ) - - local self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM - - self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) - - self.CommandCenter = CommandCenter - CommandCenter:AddMission( self ) - - self.Name = MissionName - self.MissionPriority = MissionPriority - self.MissionBriefing = MissionBriefing - self.MissionCoalition = MissionCoalition - - self.Tasks = {} - self.TaskNumber = 0 - self.PlayerNames = {} -- These are the players that achieved progress in the mission. - - self:SetStartState( "IDLE" ) - - self:AddTransition( "IDLE", "Start", "ENGAGED" ) - - --- OnLeave Transition Handler for State IDLE. - -- @function [parent=#MISSION] OnLeaveIDLE - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State IDLE. - -- @function [parent=#MISSION] OnEnterIDLE - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnLeave Transition Handler for State ENGAGED. - -- @function [parent=#MISSION] OnLeaveENGAGED - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State ENGAGED. - -- @function [parent=#MISSION] OnEnterENGAGED - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Start. - -- @function [parent=#MISSION] OnBeforeStart - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Start. - -- @function [parent=#MISSION] OnAfterStart - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Start. - -- @function [parent=#MISSION] Start - -- @param #MISSION self - - --- Asynchronous Event Trigger for Event Start. - -- @function [parent=#MISSION] __Start - -- @param #MISSION self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "ENGAGED", "Stop", "IDLE" ) - - --- OnLeave Transition Handler for State IDLE. - -- @function [parent=#MISSION] OnLeaveIDLE - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State IDLE. - -- @function [parent=#MISSION] OnEnterIDLE - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Stop. - -- @function [parent=#MISSION] OnBeforeStop - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Stop. - -- @function [parent=#MISSION] OnAfterStop - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Stop. - -- @function [parent=#MISSION] Stop - -- @param #MISSION self - - --- Asynchronous Event Trigger for Event Stop. - -- @function [parent=#MISSION] __Stop - -- @param #MISSION self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "ENGAGED", "Complete", "COMPLETED" ) - - --- OnLeave Transition Handler for State COMPLETED. - -- @function [parent=#MISSION] OnLeaveCOMPLETED - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State COMPLETED. - -- @function [parent=#MISSION] OnEnterCOMPLETED - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Complete. - -- @function [parent=#MISSION] OnBeforeComplete - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Complete. - -- @function [parent=#MISSION] OnAfterComplete - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Complete. - -- @function [parent=#MISSION] Complete - -- @param #MISSION self - - --- Asynchronous Event Trigger for Event Complete. - -- @function [parent=#MISSION] __Complete - -- @param #MISSION self - -- @param #number Delay The delay in seconds. - - self:AddTransition( "*", "Fail", "FAILED" ) - - --- OnLeave Transition Handler for State FAILED. - -- @function [parent=#MISSION] OnLeaveFAILED - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnEnter Transition Handler for State FAILED. - -- @function [parent=#MISSION] OnEnterFAILED - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- OnBefore Transition Handler for Event Fail. - -- @function [parent=#MISSION] OnBeforeFail - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event Fail. - -- @function [parent=#MISSION] OnAfterFail - -- @param #MISSION self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - - --- Synchronous Event Trigger for Event Fail. - -- @function [parent=#MISSION] Fail - -- @param #MISSION self - - --- Asynchronous Event Trigger for Event Fail. - -- @function [parent=#MISSION] __Fail - -- @param #MISSION self - -- @param #number Delay The delay in seconds. - - - self:AddTransition( "*", "MissionGoals", "*" ) - - --- MissionGoals Handler OnBefore for MISSION - -- @function [parent=#MISSION] OnBeforeMissionGoals - -- @param #MISSION self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @return #boolean - - --- MissionGoals Handler OnAfter for MISSION - -- @function [parent=#MISSION] OnAfterMissionGoals - -- @param #MISSION self - -- @param #string From - -- @param #string Event - -- @param #string To - - --- MissionGoals Trigger for MISSION - -- @function [parent=#MISSION] MissionGoals - -- @param #MISSION self - - --- MissionGoals Asynchronous Trigger for MISSION - -- @function [parent=#MISSION] __MissionGoals - -- @param #MISSION self - -- @param #number Delay - - -- Private implementations - - CommandCenter:SetMenu() - - return self -end - - - - ---- FSM function for a MISSION --- @param #MISSION self --- @param #string From --- @param #string Event --- @param #string To -function MISSION:onenterCOMPLETED( From, Event, To ) - - self:GetCommandCenter():MessageTypeToCoalition( self:GetText() .. " has been completed! Good job guys!", MESSAGE.Type.Information ) -end - ---- Gets the mission name. --- @param #MISSION self --- @return #MISSION self -function MISSION:GetName() - return self.Name -end - - ---- Gets the mission text. --- @param #MISSION self --- @return #MISSION self -function MISSION:GetText() - return string.format( 'Mission "%s (%s)"', self.Name, self.MissionPriority ) -end - - ---- Gets the short mission text. --- @param #MISSION self --- @return #MISSION self -function MISSION:GetShortText() - return string.format( 'Mission "%s"', self.Name ) -end - - ---- Add a Unit to join the Mission. --- For each Task within the Mission, the Unit is joined with the Task. --- If the Unit was not part of a Task in the Mission, false is returned. --- If the Unit is part of a Task in the Mission, true is returned. --- @param #MISSION self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player joining the Mission. --- @param Wrapper.Group#GROUP PlayerGroup The GROUP of the player joining the Mission. --- @return #boolean true if Unit is part of a Task in the Mission. -function MISSION:JoinUnit( PlayerUnit, PlayerGroup ) - self:T( { Mission = self:GetName(), PlayerUnit = PlayerUnit, PlayerGroup = PlayerGroup } ) - - local PlayerUnitAdded = false - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if Task:JoinUnit( PlayerUnit, PlayerGroup ) then - PlayerUnitAdded = true - end - end - - return PlayerUnitAdded -end - ---- Aborts a PlayerUnit from the Mission. --- For each Task within the Mission, the PlayerUnit is removed from Task where it is assigned. --- If the Unit was not part of a Task in the Mission, false is returned. --- If the Unit is part of a Task in the Mission, true is returned. --- @param #MISSION self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player joining the Mission. --- @return #MISSION -function MISSION:AbortUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - local PlayerGroup = PlayerUnit:GetGroup() - Task:AbortGroup( PlayerGroup ) - end - - return self -end - ---- Handles a crash of a PlayerUnit from the Mission. --- For each Task within the Mission, the PlayerUnit is removed from Task where it is assigned. --- If the Unit was not part of a Task in the Mission, false is returned. --- If the Unit is part of a Task in the Mission, true is returned. --- @param #MISSION self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player crashing. --- @return #MISSION -function MISSION:CrashUnit( PlayerUnit ) - self:F( { PlayerUnit = PlayerUnit } ) - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - local PlayerGroup = PlayerUnit:GetGroup() - Task:CrashGroup( PlayerGroup ) - end - - return self -end - ---- Add a scoring to the mission. --- @param #MISSION self --- @return #MISSION self -function MISSION:AddScoring( Scoring ) - self.Scoring = Scoring - return self -end - ---- Get the scoring object of a mission. --- @param #MISSION self --- @return #SCORING Scoring -function MISSION:GetScoring() - return self.Scoring -end - ---- Gets the groups for which TASKS are given in the mission --- @param #MISSION self --- @param Core.Set#SET_GROUP GroupSet --- @return Core.Set#SET_GROUP -function MISSION:GetGroups() - - return self:AddGroups() - -end - ---- Adds the groups for which TASKS are given in the mission --- @param #MISSION self --- @param Core.Set#SET_GROUP GroupSet --- @return Core.Set#SET_GROUP -function MISSION:AddGroups( GroupSet ) - - GroupSet = GroupSet or SET_GROUP:New() - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - GroupSet = Task:AddGroups( GroupSet ) - end - - return GroupSet - -end - - ---- Sets the Planned Task menu. --- @param #MISSION self --- @param #number MenuTime -function MISSION:SetMenu( MenuTime ) - self:F( { self:GetName(), MenuTime } ) - - local MenuCount = {} - --for TaskID, Task in UTILS.spairs( self:GetTasks(), function( t, a, b ) return t[a]:ReportOrder( ReportGroup ) < t[b]:ReportOrder( ReportGroup ) end ) do - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - local TaskType = Task:GetType() - MenuCount[TaskType] = MenuCount[TaskType] or 1 - if MenuCount[TaskType] <= 10 then - Task:SetMenu( MenuTime ) - MenuCount[TaskType] = MenuCount[TaskType] + 1 - end - end -end - ---- Removes the Planned Task menu. --- @param #MISSION self --- @param #number MenuTime -function MISSION:RemoveMenu( MenuTime ) - self:F( { self:GetName(), MenuTime } ) - - for _, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Task:RemoveMenu( MenuTime ) - end -end - - - -do -- Group Assignment - - --- Returns if the @{Tasking.Mission} is assigned to the Group. - -- @param #MISSION self - -- @param Wrapper.Group#GROUP MissionGroup - -- @return #boolean - function MISSION:IsGroupAssigned( MissionGroup ) - - local MissionGroupName = MissionGroup:GetName() - - if self.AssignedGroups[MissionGroupName] == MissionGroup then - self:T2( { "Mission is assigned to:", MissionGroup:GetName() } ) - return true - end - - self:T2( { "Mission is not assigned to:", MissionGroup:GetName() } ) - return false - end - - - --- Set @{Wrapper.Group} assigned to the @{Tasking.Mission}. - -- @param #MISSION self - -- @param Wrapper.Group#GROUP MissionGroup - -- @return #MISSION - function MISSION:SetGroupAssigned( MissionGroup ) - - local MissionName = self:GetName() - local MissionGroupName = MissionGroup:GetName() - - self.AssignedGroups[MissionGroupName] = MissionGroup - self:T( string.format( "Mission %s is assigned to %s", MissionName, MissionGroupName ) ) - - return self - end - - --- Clear the @{Wrapper.Group} assignment from the @{Tasking.Mission}. - -- @param #MISSION self - -- @param Wrapper.Group#GROUP MissionGroup - -- @return #MISSION - function MISSION:ClearGroupAssignment( MissionGroup ) - - local MissionName = self:GetName() - local MissionGroupName = MissionGroup:GetName() - - self.AssignedGroups[MissionGroupName] = nil - --self:E( string.format( "Mission %s is unassigned to %s", MissionName, MissionGroupName ) ) - - return self - end - -end - ---- Gets the COMMANDCENTER. --- @param #MISSION self --- @return Tasking.CommandCenter#COMMANDCENTER -function MISSION:GetCommandCenter() - return self.CommandCenter -end - - ---- Removes a Task menu. --- @param #MISSION self --- @param Tasking.Task#TASK Task --- @return #MISSION self -function MISSION:RemoveTaskMenu( Task ) - - Task:RemoveMenu() -end - - ---- Gets the root mission menu for the TaskGroup. Obsolete?! Originally no reference to TaskGroup parameter! --- @param #MISSION self --- @param Wrapper.Group#GROUP TaskGroup Task group. --- @return Core.Menu#MENU_COALITION self -function MISSION:GetRootMenu( TaskGroup ) -- R2.2 - - local CommandCenter = self:GetCommandCenter() - local CommandCenterMenu = CommandCenter:GetMenu( TaskGroup ) - - local MissionName = self:GetText() - --local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) - - self.MissionMenu = MENU_COALITION:New( self.MissionCoalition, MissionName, CommandCenterMenu ) - - return self.MissionMenu -end - ---- Gets the mission menu for the TaskGroup. --- @param #MISSION self --- @param Wrapper.Group#GROUP TaskGroup Task group. --- @return Core.Menu#MENU_COALITION self -function MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure - - local CommandCenter = self:GetCommandCenter() - local CommandCenterMenu = CommandCenter:GetMenu( TaskGroup ) - - self.MissionGroupMenu = self.MissionGroupMenu or {} - self.MissionGroupMenu[TaskGroup] = self.MissionGroupMenu[TaskGroup] or {} - - local GroupMenu = self.MissionGroupMenu[TaskGroup] - - local MissionText = self:GetText() - self.MissionMenu = MENU_GROUP:New( TaskGroup, MissionText, CommandCenterMenu ) - - GroupMenu.BriefingMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Mission Briefing", self.MissionMenu, self.MenuReportBriefing, self, TaskGroup ) - - GroupMenu.MarkTasks = MENU_GROUP_COMMAND:New( TaskGroup, "Mark Task Locations on Map", self.MissionMenu, self.MarkTargetLocations, self, TaskGroup ) - GroupMenu.TaskReportsMenu = MENU_GROUP:New( TaskGroup, "Task Reports", self.MissionMenu ) - GroupMenu.ReportTasksMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Report Tasks Summary", GroupMenu.TaskReportsMenu, self.MenuReportTasksSummary, self, TaskGroup ) - GroupMenu.ReportPlannedTasksMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Report Planned Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Planned" ) - GroupMenu.ReportAssignedTasksMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Report Assigned Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Assigned" ) - GroupMenu.ReportSuccessTasksMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Report Successful Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Success" ) - GroupMenu.ReportFailedTasksMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Report Failed Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Failed" ) - GroupMenu.ReportHeldTasksMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Report Held Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Hold" ) - - GroupMenu.PlayerReportsMenu = MENU_GROUP:New( TaskGroup, "Statistics Reports", self.MissionMenu ) - GroupMenu.ReportMissionHistory = MENU_GROUP_COMMAND:New( TaskGroup, "Report Mission Progress", GroupMenu.PlayerReportsMenu, self.MenuReportPlayersProgress, self, TaskGroup ) - GroupMenu.ReportPlayersPerTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Report Players per Task", GroupMenu.PlayerReportsMenu, self.MenuReportPlayersPerTask, self, TaskGroup ) - - return self.MissionMenu -end - - - - ---- Get the TASK identified by the TaskNumber from the Mission. This function is useful in GoalFunctions. --- @param #string TaskName The Name of the @{Tasking.Task} within the @{Tasking.Mission}. --- @return Tasking.Task#TASK The Task --- @return #nil Returns nil if no task was found. -function MISSION:GetTask( TaskName ) - self:F( { TaskName } ) - - return self.Tasks[TaskName] -end - - ---- Return the next @{Tasking.Task} ID to be completed within the @{Tasking.Mission}. --- @param #MISSION self --- @param Tasking.Task#TASK Task is the @{Tasking.Task} object. --- @return Tasking.Task#TASK The task added. -function MISSION:GetNextTaskID( Task ) - - self.TaskNumber = self.TaskNumber + 1 - - return self.TaskNumber -end - - ---- Register a @{Tasking.Task} to be completed within the @{Tasking.Mission}. --- Note that there can be multiple @{Tasking.Task}s registered to be completed. --- Each Task can be set a certain Goals. The Mission will not be completed until all Goals are reached. --- @param #MISSION self --- @param Tasking.Task#TASK Task is the @{Tasking.Task} object. --- @return Tasking.Task#TASK The task added. -function MISSION:AddTask( Task ) - - local TaskName = Task:GetTaskName() - self:T( { "==> Adding TASK ", MissionName = self:GetName(), TaskName = TaskName } ) - - self.Tasks[TaskName] = Task - - self:GetCommandCenter():SetMenu() - - return Task -end - - ---- Removes a @{Tasking.Task} to be completed within the @{Tasking.Mission}. --- Note that there can be multiple @{Tasking.Task}s registered to be completed. --- Each Task can be set a certain Goals. The Mission will not be completed until all Goals are reached. --- @param #MISSION self --- @param Tasking.Task#TASK Task is the @{Tasking.Task} object. --- @return #nil The cleaned Task reference. -function MISSION:RemoveTask( Task ) - - local TaskName = Task:GetTaskName() - self:T( { "<== Removing TASK ", MissionName = self:GetName(), TaskName = TaskName } ) - - self:F( TaskName ) - self.Tasks[TaskName] = self.Tasks[TaskName] or { n = 0 } - - -- Ensure everything gets garbarge collected. - self.Tasks[TaskName] = nil - Task = nil - - collectgarbage() - - self:GetCommandCenter():SetMenu() - - return nil -end - ---- Is the @{Tasking.Mission} **COMPLETED**. --- @param #MISSION self --- @return #boolean -function MISSION:IsCOMPLETED() - return self:Is( "COMPLETED" ) -end - ---- Is the @{Tasking.Mission} **IDLE**. --- @param #MISSION self --- @return #boolean -function MISSION:IsIDLE() - return self:Is( "IDLE" ) -end - ---- Is the @{Tasking.Mission} **ENGAGED**. --- @param #MISSION self --- @return #boolean -function MISSION:IsENGAGED() - return self:Is( "ENGAGED" ) -end - ---- Is the @{Tasking.Mission} **FAILED**. --- @param #MISSION self --- @return #boolean -function MISSION:IsFAILED() - return self:Is( "FAILED" ) -end - ---- Is the @{Tasking.Mission} **HOLD**. --- @param #MISSION self --- @return #boolean -function MISSION:IsHOLD() - return self:Is( "HOLD" ) -end - ---- Validates if the Mission has a Group --- @param #MISSION --- @return #boolean true if the Mission has a Group. -function MISSION:HasGroup( TaskGroup ) - local Has = false - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if Task:HasGroup( TaskGroup ) then - Has = true - break - end - end - - return Has -end - --- @param #MISSION self --- @return #number -function MISSION:GetTasksRemaining() - -- Determine how many tasks are remaining. - local TasksRemaining = 0 - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if Task:IsStateSuccess() or Task:IsStateFailed() then - else - TasksRemaining = TasksRemaining + 1 - end - end - return TasksRemaining -end - --- @param #MISSION self --- @return #number -function MISSION:GetTaskTypes() - -- Determine how many tasks are remaining. - local TaskTypeList = {} - local TasksRemaining = 0 - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - local TaskType = Task:GetType() - TaskTypeList[TaskType] = TaskType - end - return TaskTypeList -end - - -function MISSION:AddPlayerName( PlayerName ) - self.PlayerNames = self.PlayerNames or {} - self.PlayerNames[PlayerName] = PlayerName - return self -end - -function MISSION:GetPlayerNames() - return self.PlayerNames -end - - ---- Create a briefing report of the Mission. --- @param #MISSION self --- @return #string -function MISSION:ReportBriefing() - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetText() - - -- Determine the status of the mission. - local Status = "<" .. self:GetState() .. ">" - - Report:Add( string.format( '%s - %s - Mission Briefing Report', Name, Status ) ) - - Report:Add( self.MissionBriefing ) - - return Report:Text() -end - - ------ Create a status report of the Mission. ----- This reports provides a one liner of the mission status. It indicates how many players and how many Tasks. ----- ----- Mission "" - Status "" ----- - Task Types: , ----- - Planned Tasks (xp) ----- - Assigned Tasks(xp) ----- - Success Tasks (xp) ----- - Hold Tasks (xp) ----- - Cancelled Tasks (xp) ----- - Aborted Tasks (xp) ----- - Failed Tasks (xp) ----- --- @param #MISSION self ----- @return #string ---function MISSION:ReportSummary() --- --- local Report = REPORT:New() --- --- -- List the name of the mission. --- local Name = self:GetText() --- --- -- Determine the status of the mission. --- local Status = "<" .. self:GetState() .. ">" --- --- Report:Add( string.format( '%s - Status "%s"', Name, Status ) ) --- --- local TaskTypes = self:GetTaskTypes() --- --- Report:Add( string.format( " - Task Types: %s", table.concat(TaskTypes, ", " ) ) ) --- --- local TaskStatusList = { "Planned", "Assigned", "Success", "Hold", "Cancelled", "Aborted", "Failed" } --- --- for TaskStatusID, TaskStatus in pairs( TaskStatusList ) do --- local TaskCount = 0 --- local TaskPlayerCount = 0 --- -- Determine how many tasks are remaining. --- for TaskID, Task in pairs( self:GetTasks() ) do --- local Task = Task -- Tasking.Task#TASK --- if Task:Is( TaskStatus ) then --- TaskCount = TaskCount + 1 --- TaskPlayerCount = TaskPlayerCount + Task:GetPlayerCount() --- end --- end --- if TaskCount > 0 then --- Report:Add( string.format( " - %02d %s Tasks (%dp)", TaskCount, TaskStatus, TaskPlayerCount ) ) --- end --- end --- --- return Report:Text() ---end - - ---- Create an active player report of the Mission. --- This reports provides a one liner of the mission status. It indicates how many players and how many Tasks. --- --- Mission "" - - Active Players Report --- - Player ": Task , Task --- - Player : Task , Task --- - .. --- --- @param #MISSION self --- @return #string -function MISSION:ReportPlayersPerTask( ReportGroup ) - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetText() - - -- Determine the status of the mission. - local Status = "<" .. self:GetState() .. ">" - - Report:Add( string.format( '%s - %s - Players per Task Report', Name, Status ) ) - - local PlayerList = {} - - -- Determine how many tasks are remaining. - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - local PlayerNames = Task:GetPlayerNames() - for PlayerName, PlayerGroup in pairs( PlayerNames ) do - PlayerList[PlayerName] = Task:GetName() - end - - end - - for PlayerName, TaskName in pairs( PlayerList ) do - Report:Add( string.format( ' - Player (%s): Task "%s"', PlayerName, TaskName ) ) - end - - return Report:Text() -end - ---- Create an Mission Progress report of the Mission. --- This reports provides a one liner per player of the mission achievements per task. --- --- Mission "" - - Active Players Report --- - Player : Task : --- - Player : Task : --- - .. --- --- @param #MISSION self --- @return #string -function MISSION:ReportPlayersProgress( ReportGroup ) - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetText() - - -- Determine the status of the mission. - local Status = "<" .. self:GetState() .. ">" - - Report:Add( string.format( '%s - %s - Players per Task Progress Report', Name, Status ) ) - - local PlayerList = {} - - -- Determine how many tasks are remaining. - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - local TaskName = Task:GetName() - local Goal = Task:GetGoal() - PlayerList[TaskName] = PlayerList[TaskName] or {} - if Goal then - local TotalContributions = Goal:GetTotalContributions() - local PlayerContributions = Goal:GetPlayerContributions() - self:F( { TotalContributions = TotalContributions, PlayerContributions = PlayerContributions } ) - for PlayerName, PlayerContribution in pairs( PlayerContributions ) do - PlayerList[TaskName][PlayerName] = string.format( 'Player (%s): Task "%s": %d%%', PlayerName, TaskName, PlayerContributions[PlayerName] * 100 / TotalContributions ) - end - else - PlayerList[TaskName]["_"] = string.format( 'Player (---): Task "%s": %d%%', TaskName, 0 ) - end - end - - for TaskName, TaskData in pairs( PlayerList ) do - for PlayerName, TaskText in pairs( TaskData ) do - Report:Add( string.format( ' - %s', TaskText ) ) - end - end - - return Report:Text() -end - - ---- Mark all the target locations on the Map. --- @param #MISSION self --- @param Wrapper.Group#GROUP ReportGroup --- @return #string -function MISSION:MarkTargetLocations( ReportGroup ) - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetText() - - -- Determine the status of the mission. - local Status = "<" .. self:GetState() .. ">" - - Report:Add( string.format( '%s - %s - All Tasks are marked on the map. Select a Task from the Mission Menu and Join the Task!!!', Name, Status ) ) - - -- Determine how many tasks are remaining. - for TaskID, Task in UTILS.spairs( self:GetTasks(), function( t, a, b ) return t[a]:ReportOrder( ReportGroup ) < t[b]:ReportOrder( ReportGroup ) end ) do - local Task = Task -- Tasking.Task#TASK - Task:MenuMarkToGroup( ReportGroup ) - end - - return Report:Text() -end - - ---- Create a summary report of the Mission (one line). --- @param #MISSION self --- @param Wrapper.Group#GROUP ReportGroup --- @return #string -function MISSION:ReportSummary( ReportGroup ) - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetText() - - -- Determine the status of the mission. - local Status = "<" .. self:GetState() .. ">" - - Report:Add( string.format( '%s - %s - Task Overview Report', Name, Status ) ) - - -- Determine how many tasks are remaining. - for TaskID, Task in UTILS.spairs( self:GetTasks(), function( t, a, b ) return t[a]:ReportOrder( ReportGroup ) < t[b]:ReportOrder( ReportGroup ) end ) do - local Task = Task -- Tasking.Task#TASK - Report:Add( "- " .. Task:ReportSummary( ReportGroup ) ) - end - - return Report:Text() -end - ---- Create a overview report of the Mission (multiple lines). --- @param #MISSION self --- @return #string -function MISSION:ReportOverview( ReportGroup, TaskStatus ) - - self:F( { TaskStatus = TaskStatus } ) - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetText() - - -- Determine the status of the mission. - local Status = "<" .. self:GetState() .. ">" - - Report:Add( string.format( '%s - %s - %s Tasks Report', Name, Status, TaskStatus ) ) - - -- Determine how many tasks are remaining. - local Tasks = 0 - for TaskID, Task in UTILS.spairs( self:GetTasks(), function( t, a, b ) return t[a]:ReportOrder( ReportGroup ) < t[b]:ReportOrder( ReportGroup ) end ) do - local Task = Task -- Tasking.Task#TASK - if Task:Is( TaskStatus ) then - Report:Add( string.rep( "-", 140 ) ) - Report:Add( Task:ReportOverview( ReportGroup ) ) - end - Tasks = Tasks + 1 - if Tasks >= 8 then - break - end - end - - return Report:Text() -end - ---- Create a detailed report of the Mission, listing all the details of the Task. --- @param #MISSION self --- @return #string -function MISSION:ReportDetails( ReportGroup ) - - local Report = REPORT:New() - - -- List the name of the mission. - local Name = self:GetText() - - -- Determine the status of the mission. - local Status = "<" .. self:GetState() .. ">" - - Report:Add( string.format( '%s - %s - Task Detailed Report', Name, Status ) ) - - -- Determine how many tasks are remaining. - local TasksRemaining = 0 - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - Report:Add( string.rep( "-", 140 ) ) - Report:Add( Task:ReportDetails( ReportGroup ) ) - end - - return Report:Text() -end - ---- Get all the TASKs from the Mission. This function is useful in GoalFunctions. --- @return {TASK,...} Structure of TASKS with the @{Tasking.Task#TASK} number as the key. --- @usage --- -- Get Tasks from the Mission. --- Tasks = Mission:GetTasks() --- env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" ) -function MISSION:GetTasks() - - return self.Tasks or {} -end - ---- Get the relevant tasks of a TaskGroup. --- @param #MISSION --- @param Wrapper.Group#GROUP TaskGroup --- @return #list -function MISSION:GetGroupTasks( TaskGroup ) - - local Tasks = {} - - for TaskID, Task in pairs( self:GetTasks() ) do - local Task = Task -- Tasking.Task#TASK - if Task:HasGroup( TaskGroup ) then - Tasks[#Tasks+1] = Task - end - end - - return Tasks -end - - ---- Reports the briefing. --- @param #MISSION self --- @param Wrapper.Group#GROUP ReportGroup The group to which the report needs to be sent. -function MISSION:MenuReportBriefing( ReportGroup ) - - local Report = self:ReportBriefing() - - self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Briefing ) -end - - ---- Mark all the targets of the Mission on the Map. --- @param #MISSION self --- @param Wrapper.Group#GROUP ReportGroup -function MISSION:MenuMarkTargetLocations( ReportGroup ) - - local Report = self:MarkTargetLocations( ReportGroup ) - - self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview ) -end - - - ---- Report the task summary. --- @param #MISSION self --- @param Wrapper.Group#GROUP ReportGroup -function MISSION:MenuReportTasksSummary( ReportGroup ) - - local Report = self:ReportSummary( ReportGroup ) - - self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview ) -end - - - - --- @param #MISSION self --- @param #string TaskStatus The status --- @param Wrapper.Group#GROUP ReportGroup -function MISSION:MenuReportTasksPerStatus( ReportGroup, TaskStatus ) - - local Report = self:ReportOverview( ReportGroup, TaskStatus ) - - self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview ) -end - - --- @param #MISSION self --- @param Wrapper.Group#GROUP ReportGroup -function MISSION:MenuReportPlayersPerTask( ReportGroup ) - - local Report = self:ReportPlayersPerTask() - - self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview ) -end - --- @param #MISSION self --- @param Wrapper.Group#GROUP ReportGroup -function MISSION:MenuReportPlayersProgress( ReportGroup ) - - local Report = self:ReportPlayersProgress() - - self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview ) -end - - - - - diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua deleted file mode 100644 index 04d6a4598..000000000 --- a/Moose Development/Moose/Tasking/Task.lua +++ /dev/null @@ -1,2060 +0,0 @@ ---- **Tasking** - A task object governs the main engine to administer human taskings. --- --- **Features:** --- --- * A base class for other task classes filling in the details and making a concrete task process. --- * Manage the overall task execution, following-up the progression made by the pilots and actors. --- * Provide a mechanism to set a task status, depending on the progress made within the task. --- * Manage a task briefing. --- * Manage the players executing the task. --- * Manage the task menu system. --- * Manage the task goal and scoring. --- --- === --- --- ![Banner Image](..\Images\deprecated.png) --- --- # 1) Tasking from a player perspective. --- --- Tasking can be controlled by using the "other" menu in the radio menu of the player group. --- --- ![Other Menu](../Tasking/Menu_Main.JPG) --- --- ## 1.1) Command Centers govern multiple Missions. --- --- Depending on the tactical situation, your coalition may have one (or multiple) command center(s). --- These command centers govern one (or multiple) mission(s). --- --- For each command center, there will be a separate **Command Center Menu** that focuses on the missions governed by that command center. --- --- ![Command Center](../Tasking/Menu_CommandCenter.JPG) --- --- In the above example menu structure, there is one command center with the name **`[Lima]`**. --- The command center has one @{Tasking.Mission}, named **`"Overlord"`** with **`High`** priority. --- --- ## 1.2) Missions govern multiple Tasks. --- --- A mission has a mission goal to be achieved by the players within the coalition. --- The mission goal is actually dependent on the tactical situation of the overall battlefield and the conditions set to achieve the goal. --- So a mission can be much more than just shoot stuff ... It can be a combination of different conditions or events to complete a mission goal. --- --- A mission can be in a specific state during the simulation run. For more information about these states, please check the @{Tasking.Mission} section. --- --- To achieve the mission goal, a mission administers @{#TASK}s that are set to achieve the mission goal by the human players. --- Each of these tasks can be **dynamically created** using a task dispatcher, or **coded** by the mission designer. --- Each mission has a separate **Mission Menu**, that focuses on the administration of these tasks. --- --- On top, a mission has a mission briefing, can help to allocate specific points of interest on the map, and provides various reports. --- --- ![Mission](../Tasking/Menu_Mission.JPG) --- --- The above shows a mission menu in detail of **`"Overlord"`**. --- --- The two other menus are related to task assignment. Which will be detailed later. --- --- ### 1.2.1) Mission briefing. --- --- The task briefing will show a message containing a description of the mission goal, and other tactical information. --- --- ![Mission](../Tasking/Report_Briefing.JPG) --- --- ### 1.2.2) Mission Map Locations. --- --- Various points of interest as part of the mission can be indicated on the map using the *Mark Task Locations on Map* menu. --- As a result, the map will contain various points of interest for the player (group). --- --- ![Mission](../Tasking/Report_Mark_Task_Location.JPG) --- --- ### 1.2.3) Mission Task Reports. --- --- Various reports can be generated on the status of each task governed within the mission. --- --- ![Mission](../Tasking/Report_Task_Summary.JPG) --- --- The Task Overview Report will show each task, with its task status and a short coordinate information. --- --- ![Mission](../Tasking/Report_Tasks_Planned.JPG) --- --- The other Task Menus will show for each task more details, for example here the planned tasks report. --- Note that the order of the tasks are shortest distance first to the unit position seated by the player. --- --- ### 1.2.4) Mission Statistics. --- --- Various statistics can be displayed regarding the mission. --- --- ![Mission](../Tasking/Report_Statistics_Progress.JPG) --- --- A statistic report on the progress of the mission. Each task achievement will increase the % to 100% as a goal to complete the task. --- --- ## 1.3) Join a Task. --- --- The mission menu contains a very important option, that is to join a task governed within the mission. --- In order to join a task, select the **Join Planned Task** menu, and a new menu will be given. --- --- ![Mission](../Tasking/Menu_Join_Planned_Tasks.JPG) --- --- A mission governs multiple tasks, as explained earlier. Each task is of a certain task type. --- This task type was introduced to have some sort of task classification system in place for the player. --- A short acronym is shown that indicates the task type. The meaning of each acronym can be found in the task types explanation. --- --- ![Mission](../Tasking/Menu_Join_Tasks.JPG) --- --- When the player selects a task type, a list of the available tasks of that type are listed... --- In this case the **`SEAD`** task type was selected and a list of available **`SEAD`** tasks can be selected. --- --- ![Mission](../Tasking/Menu_Join_Planned_Task.JPG) --- --- A new list of menu options are now displayed that allow to join the task selected, but also to obtain first some more information on the task. --- --- ### 1.3.1) Report Task Details. --- --- ![Mission](../Tasking/Report_Task_Detailed.JPG) --- --- When selected, a message is displayed that shows detailed information on the task, like the coordinate, enemy target information, threat level etc. --- --- ### 1.3.2) Mark Task Location on Map. --- --- ![Mission](../Tasking/Report_Task_Detailed.JPG) --- --- When selected, the target location on the map is indicated with specific information on the task. --- --- ### 1.3.3) Join Task. --- --- ![Mission](../Tasking/Report_Task_Detailed.JPG) --- --- By joining a task, the player will indicate that the task is assigned to him, and the task is started. --- The Command Center will communicate several task details to the player and the coalition of the player. --- --- ## 1.4) Task Control and Actions. --- --- ![Mission](../Tasking/Menu_Main_Task.JPG) --- --- When a player has joined a task, a **Task Action Menu** is available to be used by the player. --- --- ![Mission](../Tasking/Menu_Task.JPG) --- --- The task action menu contains now menu items specific to the task, but also one generic menu item, which is to control the task. --- This **Task Control Menu** allows to display again the task details and the task map location information. --- But it also allows to abort a task! --- --- Depending on the task type, the task action menu can contain more menu items which are specific to the task. --- For example, cargo transportation tasks will contain various additional menu items to select relevant cargo coordinates, --- or to load/unload cargo. --- --- ## 1.5) Automatic task assignment. --- --- ![Command Center](../Tasking/Menu_CommandCenter.JPG) --- --- When we take back the command center menu, you see two additional **Assign Task** menu items. --- The menu **Assign Task On** will automatically allocate a task to the player. --- After the selection of this menu, the menu will change into **Assign Task Off**, --- and will need to be selected again by the player to switch of the automatic task assignment. --- --- The other option is to select **Assign Task**, which will assign a new random task to the player. --- --- When a task is automatically assigned to a player, the task needs to be confirmed as accepted within 30 seconds. --- If this is not the case, the task will be cancelled automatically, and a new random task will be assigned to the player. --- This will continue to happen until the player accepts the task or switches off the automatic task assignment process. --- --- The player can accept the task using the menu **Confirm Task Acceptance** ... --- --- ## 1.6) Task states. --- --- A task has a state, reflecting the progress or completion status of the task: --- --- - **Planned**: Expresses that the task is created, but not yet in execution and is not assigned yet to a pilot. --- - **Assigned**: Expresses that the task is assigned to a group of pilots, and that the task is in execution mode. --- - **Success**: Expresses the successful execution and finalization of the task. --- - **Failed**: Expresses the failure of a task. --- - **Abort**: Expresses that the task is aborted by by the player using the abort menu. --- - **Cancelled**: Expresses that the task is cancelled by HQ or through a logical situation where a cancellation of the task is required. --- --- ### 1.6.1) Task progress. --- --- The task governor takes care of the **progress** and **completion** of the task **goal(s)**. --- Tasks are executed by **human pilots** and actors within a DCS simulation. --- Pilots can use a **menu system** to engage or abort a task, and provides means to --- understand the **task briefing** and goals, and the relevant **task locations** on the map and --- obtain **various reports** related to the task. --- --- ### 1.6.2) Task completion. --- --- As the task progresses, the **task status** will change over time, from Planned state to Completed state. --- **Multiple pilots** can execute the same task, as such, the tasking system provides a **co-operative model** for joint task execution. --- Depending on the task progress, a **scoring** can be allocated to award pilots of the achievements made. --- The scoring is fully flexible, and different levels of awarding can be provided depending on the task type and complexity. --- --- A normal flow of task status would evolve from the **Planned** state, to the **Assigned** state ending either in a **Success** or a **Failed** state. --- --- Planned -> Assigned -> Success --- -> Failed --- -> Cancelled --- --- The state completion is by default set to **Success**, if the goals of the task have been reached, but can be overruled by a goal method. --- --- Depending on the tactical situation, a task can be **Cancelled** by the mission governor. --- It is actually the mission designer who has the flexibility to decide at which conditions a task would be set to **Success**, **Failed** or **Cancelled**. --- This decision all depends on the task goals, and the phase/evolution of the task conditions that would accomplish the goals. --- --- For example, if the task goal is to merely destroy a target, and the target is mid-mission destroyed by another event than the pilot destroying the target, --- the task goal could be set to **Failed**, or .. **Cancelled** ... --- However, it could very well be also acceptable that the task would be flagged as **Success**. --- --- The tasking mechanism governs beside the progress also a scoring mechanism, and in case of goal completion without any active pilot involved --- in the execution of the task, could result in a **Success** task completion status, but no score would be awarded, as there were no players involved. --- --- These different completion states are important for the mission designer to reflect scoring to a player. --- A success could mean a positive score to be given, while a failure could mean a negative score or penalties to be awarded. --- --- # Developer Note --- --- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE --- Therefore, this class is considered to be deprecated --- --- === --- --- ### Author(s): **FlightControl** --- --- ### Contribution(s): --- --- === --- --- @module Tasking.Task --- @image MOOSE.JPG - ---- --- @type TASK --- @field Core.Scheduler#SCHEDULER TaskScheduler --- @field Tasking.Mission#MISSION Mission --- @field Core.Set#SET_GROUP SetGroup The Set of Groups assigned to the Task --- @field Core.Fsm#FSM_PROCESS FsmTemplate --- @field Tasking.Mission#MISSION Mission --- @field Tasking.CommandCenter#COMMANDCENTER CommandCenter --- @field Tasking.TaskInfo#TASKINFO TaskInfo --- @extends Core.Fsm#FSM_TASK - ---- Governs the main engine to administer human taskings. --- --- A task is governed by a @{Tasking.Mission} object. Tasks are of different types. --- The @{#TASK} object is used or derived by more detailed tasking classes that will implement the task execution mechanisms --- and goals. --- --- # 1) Derived task classes. --- --- The following TASK_ classes are derived from @{#TASK}. --- --- TASK --- TASK_A2A --- TASK_A2A_ENGAGE --- TASK_A2A_INTERCEPT --- TASK_A2A_SWEEP --- TASK_A2G --- TASK_A2G_SEAD --- TASK_A2G_CAS --- TASK_A2G_BAI --- TASK_CARGO --- TASK_CARGO_TRANSPORT --- TASK_CARGO_CSAR --- --- ## 1.1) A2A Tasks --- --- - @{Tasking.Task_A2A#TASK_A2A_ENGAGE} - Models an A2A engage task of a target group of airborne intruders mid-air. --- - @{Tasking.Task_A2A#TASK_A2A_INTERCEPT} - Models an A2A ground intercept task of a target group of airborne intruders mid-air. --- - @{Tasking.Task_A2A#TASK_A2A_SWEEP} - Models an A2A sweep task to clean an area of previously detected intruders mid-air. --- --- ## 1.2) A2G Tasks --- --- - @{Tasking.Task_A2G#TASK_A2G_SEAD} - Models an A2G Suppression or Extermination of Air Defenses task to clean an area of air to ground defense threats. --- - @{Tasking.Task_A2G#TASK_A2G_CAS} - Models an A2G Close Air Support task to provide air support to nearby friendlies near the front-line. --- - @{Tasking.Task_A2G#TASK_A2G_BAI} - Models an A2G Battlefield Air Interdiction task to provide air support to nearby friendlies near the front-line. --- --- ## 1.3) Cargo Tasks --- --- - @{Tasking.Task_CARGO#TASK_CARGO_TRANSPORT} - Models the transportation of cargo to deployment zones. --- - @{Tasking.Task_CARGO#TASK_CARGO_CSAR} - Models the rescue of downed friendly pilots from behind enemy lines. --- --- --- # 2) Task status events. --- --- The task statuses can be set by using the following methods: --- --- - @{#TASK.Success}() - Set the task to **Success** state. --- - @{#TASK.Fail}() - Set the task to **Failed** state. --- - @{#TASK.Hold}() - Set the task to **Hold** state. --- - @{#TASK.Abort}() - Set the task to **Aborted** state, aborting the task. The task may be replanned. --- - @{#TASK.Cancel}() - Set the task to **Cancelled** state, cancelling the task. --- --- The mentioned derived TASK_ classes are implementing the task status transitions out of the box. --- So no extra logic needs to be written. --- --- # 3) Goal conditions for a task. --- --- Every 30 seconds, a @{#Task.Goal} trigger method is fired. --- You as a mission designer, can capture the **Goal** event trigger to check your own task goal conditions and take action! --- --- ## 3.1) Goal event handler `OnAfterGoal()`. --- --- And this is a really great feature! Imagine a task which has **several conditions to check** before the task can move into **Success** state. --- You can do this with the OnAfterGoal method. --- --- The following code provides an example of such a goal condition check implementation. --- --- function Task:OnAfterGoal() --- if condition == true then --- self:Success() -- This will flag the task to Success when the condition is true. --- else --- if condition2 == true and condition3 == true then --- self:Fail() -- This will flag the task to Failed, when condition2 and condition3 would be true. --- end --- end --- end --- --- So the @{#TASK.OnAfterGoal}() event handler would be called every 30 seconds automatically, --- and within this method, you can now check the conditions and take respective action. --- --- ## 3.2) Goal event trigger `Goal()`. --- --- If you would need to check a goal at your own defined event timing, then just call the @{#TASK.Goal}() method within your logic. --- The @{#TASK.OnAfterGoal}() event handler would then directly be called and would execute the logic. --- Note that you can also delay the goal check by using the delayed event trigger syntax `:__Goal( Delay )`. --- --- --- # 4) Score task completion. --- --- Upon reaching a certain task status in a task, additional scoring can be given. If the Mission has a scoring system attached, the scores will be added to the mission scoring. --- Use the method @{#TASK.AddScore}() to add scores when a status is reached. --- --- # 5) Task briefing. --- --- A task briefing is a text that is shown to the player when he is assigned to the task. --- The briefing is broadcasted by the command center owning the mission. --- --- The briefing is part of the parameters in the @{#TASK.New}() constructor, --- but can separately be modified later in your mission using the --- @{#TASK.SetBriefing}() method. --- --- --- @field #TASK TASK --- -TASK = { - ClassName = "TASK", - TaskScheduler = nil, - ProcessClasses = {}, -- The container of the Process classes that will be used to create and assign new processes for the task to ProcessUnits. - Processes = {}, -- The container of actual process objects instantiated and assigned to ProcessUnits. - Players = nil, - Scores = {}, - Menu = {}, - SetGroup = nil, - FsmTemplate = nil, - Mission = nil, - CommandCenter = nil, - TimeOut = 0, - AssignedGroups = {}, -} - ---- FSM PlayerAborted event handler prototype for TASK. --- @function [parent=#TASK] OnAfterPlayerAborted --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The Unit of the Player when he went back to spectators or left the mission. --- @param #string PlayerName The name of the Player. - ---- FSM PlayerCrashed event handler prototype for TASK. --- @function [parent=#TASK] OnAfterPlayerCrashed --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The Unit of the Player when he crashed in the mission. --- @param #string PlayerName The name of the Player. - ---- FSM PlayerDead event handler prototype for TASK. --- @function [parent=#TASK] OnAfterPlayerDead --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The Unit of the Player when he died in the mission. --- @param #string PlayerName The name of the Player. - ---- FSM Fail synchronous event function for TASK. --- Use this event to Fail the Task. --- @function [parent=#TASK] Fail --- @param #TASK self - ---- FSM Fail asynchronous event function for TASK. --- Use this event to Fail the Task. --- @function [parent=#TASK] __Fail --- @param #TASK self - ---- FSM Abort synchronous event function for TASK. --- Use this event to Abort the Task. --- @function [parent=#TASK] Abort --- @param #TASK self - ---- FSM Abort asynchronous event function for TASK. --- Use this event to Abort the Task. --- @function [parent=#TASK] __Abort --- @param #TASK self - ---- FSM Success synchronous event function for TASK. --- Use this event to make the Task a Success. --- @function [parent=#TASK] Success --- @param #TASK self - ---- FSM Success asynchronous event function for TASK. --- Use this event to make the Task a Success. --- @function [parent=#TASK] __Success --- @param #TASK self - ---- FSM Cancel synchronous event function for TASK. --- Use this event to Cancel the Task. --- @function [parent=#TASK] Cancel --- @param #TASK self - ---- FSM Cancel asynchronous event function for TASK. --- Use this event to Cancel the Task. --- @function [parent=#TASK] __Cancel --- @param #TASK self - ---- FSM Replan synchronous event function for TASK. --- Use this event to Replan the Task. --- @function [parent=#TASK] Replan --- @param #TASK self - ---- FSM Replan asynchronous event function for TASK. --- Use this event to Replan the Task. --- @function [parent=#TASK] __Replan --- @param #TASK self - - ---- Instantiates a new TASK. Should never be used. Interface Class. --- @param #TASK self --- @param Tasking.Mission#MISSION Mission The mission wherein the Task is registered. --- @param Core.Set#SET_GROUP SetGroupAssign The set of groups for which the Task can be assigned. --- @param #string TaskName The name of the Task --- @param #string TaskType The type of the Task --- @return #TASK self -function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing ) - - local self = BASE:Inherit( self, FSM_TASK:New( TaskName ) ) -- Tasking.Task#TASK - - self:SetStartState( "Planned" ) - self:AddTransition( "Planned", "Assign", "Assigned" ) - self:AddTransition( "Assigned", "AssignUnit", "Assigned" ) - self:AddTransition( "Assigned", "Success", "Success" ) - self:AddTransition( "Assigned", "Hold", "Hold" ) - self:AddTransition( "Assigned", "Fail", "Failed" ) - self:AddTransition( { "Planned", "Assigned" }, "Abort", "Aborted" ) - self:AddTransition( "Assigned", "Cancel", "Cancelled" ) - self:AddTransition( "Assigned", "Goal", "*" ) - - self.Fsm = {} - - local Fsm = self:GetUnitProcess() - Fsm:SetStartState( "Planned" ) - Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "Assigned", Rejected = "Reject" } ) - Fsm:AddTransition( "Assigned", "Assigned", "*" ) - - --- Goal Handler OnBefore for TASK - -- @function [parent=#TASK] OnBeforeGoal - -- @param #TASK self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Wrapper.Unit#UNIT PlayerUnit The @{Wrapper.Unit} of the player. - -- @param #string PlayerName The name of the player. - -- @return #boolean - - --- Goal Handler OnAfter for TASK - -- @function [parent=#TASK] OnAfterGoal - -- @param #TASK self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Wrapper.Unit#UNIT PlayerUnit The @{Wrapper.Unit} of the player. - -- @param #string PlayerName The name of the player. - - --- Goal Trigger for TASK - -- @function [parent=#TASK] Goal - -- @param #TASK self - -- @param Wrapper.Unit#UNIT PlayerUnit The @{Wrapper.Unit} of the player. - -- @param #string PlayerName The name of the player. - - --- Goal Asynchronous Trigger for TASK - -- @function [parent=#TASK] __Goal - -- @param #TASK self - -- @param #number Delay - -- @param Wrapper.Unit#UNIT PlayerUnit The @{Wrapper.Unit} of the player. - -- @param #string PlayerName The name of the player. - - - - self:AddTransition( "*", "PlayerCrashed", "*" ) - self:AddTransition( "*", "PlayerAborted", "*" ) - self:AddTransition( "*", "PlayerRejected", "*" ) - self:AddTransition( "*", "PlayerDead", "*" ) - self:AddTransition( { "Failed", "Aborted", "Cancelled" }, "Replan", "Planned" ) - self:AddTransition( "*", "TimeOut", "Cancelled" ) - - self:F( "New TASK " .. TaskName ) - - self.Processes = {} - - self.Mission = Mission - self.CommandCenter = Mission:GetCommandCenter() - - self.SetGroup = SetGroupAssign - - self:SetType( TaskType ) - self:SetName( TaskName ) - self:SetID( Mission:GetNextTaskID( self ) ) -- The Mission orchestrates the task sequences .. - - self:SetBriefing( TaskBriefing ) - - - self.TaskInfo = TASKINFO:New( self ) - - self.TaskProgress = {} - - return self -end - ---- Get the Task FSM Process Template --- @param #TASK self --- @return Core.Fsm#FSM_PROCESS -function TASK:GetUnitProcess( TaskUnit ) - - if TaskUnit then - return self:GetStateMachine( TaskUnit ) - else - self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() - return self.FsmTemplate - end -end - ---- Sets the Task FSM Process Template --- @param #TASK self --- @param Core.Fsm#FSM_PROCESS -function TASK:SetUnitProcess( FsmTemplate ) - - self.FsmTemplate = FsmTemplate -end - ---- Add a PlayerUnit to join the Task. --- For each Group within the Task, the Unit is checked if it can join the Task. --- If the Unit was not part of the Task, false is returned. --- If the Unit is part of the Task, true is returned. --- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player joining the Mission. --- @param Wrapper.Group#GROUP PlayerGroup The GROUP of the player joining the Mission. --- @return #boolean true if Unit is part of the Task. -function TASK:JoinUnit( PlayerUnit, PlayerGroup ) - self:F( { PlayerUnit = PlayerUnit, PlayerGroup = PlayerGroup } ) - - local PlayerUnitAdded = false - - local PlayerGroups = self:GetGroups() - - -- Is the PlayerGroup part of the PlayerGroups? - if PlayerGroups:IsIncludeObject( PlayerGroup ) then - - -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is added to the Task. - -- If the PlayerGroup is not assigned to the Task, the menu needs to be set. In that case, the PlayerUnit will become the GroupPlayer leader. - if self:IsStatePlanned() or self:IsStateReplanned() then - --self:SetMenuForGroup( PlayerGroup ) - --self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() ) - end - if self:IsStateAssigned() then - local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) - self:F( { IsGroupAssigned = IsGroupAssigned } ) - if IsGroupAssigned then - self:AssignToUnit( PlayerUnit ) - self:MessageToGroups( PlayerUnit:GetPlayerName() .. " joined Task " .. self:GetName() ) - end - end - end - - return PlayerUnitAdded -end - ---- A group rejecting a planned task. --- @param #TASK self --- @param Wrapper.Group#GROUP PlayerGroup The group rejecting the task. --- @return #TASK -function TASK:RejectGroup( PlayerGroup ) - - local PlayerGroups = self:GetGroups() - - -- Is the PlayerGroup part of the PlayerGroups? - if PlayerGroups:IsIncludeObject( PlayerGroup ) then - - -- Check if the PlayerGroup is already assigned or is planned to be assigned to the Task. - -- If yes, the PlayerGroup is aborted from the Task. - -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. - if self:IsStatePlanned() then - - local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) - if IsGroupAssigned then - local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName() - self:GetMission():GetCommandCenter():MessageToGroup( "Task " .. self:GetName() .. " has been rejected! We will select another task.", PlayerGroup ) - self:UnAssignFromGroup( PlayerGroup ) - - self:PlayerRejected( PlayerGroup:GetUnit(1) ) - end - - end - end - - return self -end - - ---- A group aborting the task. --- @param #TASK self --- @param Wrapper.Group#GROUP PlayerGroup The group aborting the task. --- @return #TASK -function TASK:AbortGroup( PlayerGroup ) - - local PlayerGroups = self:GetGroups() - - -- Is the PlayerGroup part of the PlayerGroups? - if PlayerGroups:IsIncludeObject( PlayerGroup ) then - - -- Check if the PlayerGroup is already assigned or is planned to be assigned to the Task. - -- If yes, the PlayerGroup is aborted from the Task. - -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. - if self:IsStateAssigned() then - - local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) - if IsGroupAssigned then - local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName() - self:UnAssignFromGroup( PlayerGroup ) - - -- Now check if the task needs to go to hold... - -- It will go to hold, if there are no players in the mission... - PlayerGroups:Flush( self ) - local IsRemaining = false - for GroupName, AssignedGroup in pairs( PlayerGroups:GetSet() or {} ) do - if self:IsGroupAssigned( AssignedGroup ) == true then - IsRemaining = true - self:F( { Task = self:GetName(), IsRemaining = IsRemaining } ) - break - end - end - - self:F( { Task = self:GetName(), IsRemaining = IsRemaining } ) - if IsRemaining == false then - self:Abort() - end - - self:PlayerAborted( PlayerGroup:GetUnit(1) ) - end - - end - end - - return self -end - - ---- A group crashing and thus aborting from the task. --- @param #TASK self --- @param Wrapper.Group#GROUP PlayerGroup The group aborting the task. --- @return #TASK -function TASK:CrashGroup( PlayerGroup ) - self:F( { PlayerGroup = PlayerGroup } ) - - local PlayerGroups = self:GetGroups() - - -- Is the PlayerGroup part of the PlayerGroups? - if PlayerGroups:IsIncludeObject( PlayerGroup ) then - - -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. - -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. - if self:IsStateAssigned() then - local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) - self:F( { IsGroupAssigned = IsGroupAssigned } ) - if IsGroupAssigned then - local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName() - self:MessageToGroups( PlayerName .. " crashed! " ) - self:UnAssignFromGroup( PlayerGroup ) - - -- Now check if the task needs to go to hold... - -- It will go to hold, if there are no players in the mission... - - PlayerGroups:Flush( self ) - local IsRemaining = false - for GroupName, AssignedGroup in pairs( PlayerGroups:GetSet() or {} ) do - if self:IsGroupAssigned( AssignedGroup ) == true then - IsRemaining = true - self:F( { Task = self:GetName(), IsRemaining = IsRemaining } ) - break - end - end - - self:F( { Task = self:GetName(), IsRemaining = IsRemaining } ) - if IsRemaining == false then - self:Abort() - end - - self:PlayerCrashed( PlayerGroup:GetUnit(1) ) - end - - end - end - - return self -end - - - ---- Gets the Mission to where the TASK belongs. --- @param #TASK self --- @return Tasking.Mission#MISSION -function TASK:GetMission() - - return self.Mission -end - - ---- Gets the SET_GROUP assigned to the TASK. --- @param #TASK self --- @return Core.Set#SET_GROUP -function TASK:GetGroups() - - return self.SetGroup -end - - ---- Gets the SET_GROUP assigned to the TASK. --- @param #TASK self --- @param Core.Set#SET_GROUP GroupSet --- @return Core.Set#SET_GROUP -function TASK:AddGroups( GroupSet ) - - GroupSet = GroupSet or SET_GROUP:New() - - self.SetGroup:ForEachGroup( - -- @param Wrapper.Group#GROUP GroupSet - function( GroupItem ) - GroupSet:Add( GroupItem:GetName(), GroupItem) - end - ) - - return GroupSet -end - -do -- Group Assignment - - --- Returns if the @{#TASK} is assigned to the Group. - -- @param #TASK self - -- @param Wrapper.Group#GROUP TaskGroup - -- @return #boolean - function TASK:IsGroupAssigned( TaskGroup ) - - local TaskGroupName = TaskGroup:GetName() - - if self.AssignedGroups[TaskGroupName] then - --self:T( { "Task is assigned to:", TaskGroup:GetName() } ) - return true - end - - --self:T( { "Task is not assigned to:", TaskGroup:GetName() } ) - return false - end - - - --- Set @{Wrapper.Group} assigned to the @{#TASK}. - -- @param #TASK self - -- @param Wrapper.Group#GROUP TaskGroup - -- @return #TASK - function TASK:SetGroupAssigned( TaskGroup ) - - local TaskName = self:GetName() - local TaskGroupName = TaskGroup:GetName() - - self.AssignedGroups[TaskGroupName] = TaskGroup - self:F( string.format( "Task %s is assigned to %s", TaskName, TaskGroupName ) ) - - -- Set the group to be assigned at mission level. This allows to decide the menu options on mission level for this group. - self:GetMission():SetGroupAssigned( TaskGroup ) - - local SetAssignedGroups = self:GetGroups() - --- SetAssignedGroups:ForEachGroup( --- function( AssignedGroup ) --- if self:IsGroupAssigned(AssignedGroup) then --- self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is assigned to group %s.", TaskName, TaskGroupName ), AssignedGroup ) --- else --- self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is assigned to your group.", TaskName ), AssignedGroup ) --- end --- end --- ) - - return self - end - - --- Clear the @{Wrapper.Group} assignment from the @{#TASK}. - -- @param #TASK self - -- @param Wrapper.Group#GROUP TaskGroup - -- @return #TASK - function TASK:ClearGroupAssignment( TaskGroup ) - - local TaskName = self:GetName() - local TaskGroupName = TaskGroup:GetName() - - self.AssignedGroups[TaskGroupName] = nil - --self:F( string.format( "Task %s is unassigned to %s", TaskName, TaskGroupName ) ) - - -- Set the group to be assigned at mission level. This allows to decide the menu options on mission level for this group. - self:GetMission():ClearGroupAssignment( TaskGroup ) - - local SetAssignedGroups = self:GetGroups() - - SetAssignedGroups:ForEachGroup( - function( AssignedGroup ) - if self:IsGroupAssigned(AssignedGroup) then - --self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from group %s.", TaskName, TaskGroupName ), AssignedGroup ) - else - --self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from your group.", TaskName ), AssignedGroup ) - end - end - ) - - return self - end - -end - -do -- Group Assignment - - -- @param #TASK self - -- @param Actions.Act_Assign#ACT_ASSIGN AcceptClass - function TASK:SetAssignMethod( AcceptClass ) - - local ProcessTemplate = self:GetUnitProcess() - - ProcessTemplate:SetProcess( "Planned", "Accept", AcceptClass ) -- Actions.Act_Assign#ACT_ASSIGN - end - - - --- Assign the @{#TASK} to a @{Wrapper.Group}. - -- @param #TASK self - -- @param Wrapper.Group#GROUP TaskGroup - -- @return #TASK - function TASK:AssignToGroup( TaskGroup ) - self:F( TaskGroup:GetName() ) - - local TaskGroupName = TaskGroup:GetName() - local Mission = self:GetMission() - local CommandCenter = Mission:GetCommandCenter() - - self:SetGroupAssigned( TaskGroup ) - - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - self:F(PlayerName) - if PlayerName ~= nil and PlayerName ~= "" then - self:AssignToUnit( TaskUnit ) - CommandCenter:MessageToGroup( - string.format( 'Task "%s": Briefing for player (%s):\n%s', - self:GetName(), - PlayerName, - self:GetBriefing() - ), TaskGroup - ) - end - end - - CommandCenter:SetMenu() - - self:MenuFlashTaskStatus( TaskGroup, self:GetMission():GetCommandCenter().FlashStatus ) - - return self - end - - --- UnAssign the @{#TASK} from a @{Wrapper.Group}. - -- @param #TASK self - -- @param Wrapper.Group#GROUP TaskGroup - function TASK:UnAssignFromGroup( TaskGroup ) - self:F2( { TaskGroup = TaskGroup:GetName() } ) - - self:ClearGroupAssignment( TaskGroup ) - - local TaskUnits = TaskGroup:GetUnits() - for UnitID, UnitData in pairs( TaskUnits ) do - local TaskUnit = UnitData -- Wrapper.Unit#UNIT - local PlayerName = TaskUnit:GetPlayerName() - if PlayerName ~= nil and PlayerName ~= "" then -- Only remove units that have players! - self:UnAssignFromUnit( TaskUnit ) - end - end - - local Mission = self:GetMission() - local CommandCenter = Mission:GetCommandCenter() - CommandCenter:SetMenu() - - self:MenuFlashTaskStatus( TaskGroup, false ) -- stop message flashing, if any #1383 & #1312 - - end -end - - ---- --- @param #TASK self --- @param Wrapper.Group#GROUP FindGroup --- @return #boolean -function TASK:HasGroup( FindGroup ) - - local SetAttackGroup = self:GetGroups() - return SetAttackGroup:FindGroup( FindGroup:GetName() ) - -end - ---- Assign the @{#TASK} to an alive @{Wrapper.Unit}. --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return #TASK self -function TASK:AssignToUnit( TaskUnit ) - self:F( TaskUnit:GetName() ) - - local FsmTemplate = self:GetUnitProcess() - - -- Assign a new FsmUnit to TaskUnit. - local FsmUnit = self:SetStateMachine( TaskUnit, FsmTemplate:Copy( TaskUnit, self ) ) -- Core.Fsm#FSM_PROCESS - - FsmUnit:SetStartState( "Planned" ) - - FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. - - return self -end - ---- UnAssign the @{#TASK} from an alive @{Wrapper.Unit}. --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return #TASK self -function TASK:UnAssignFromUnit( TaskUnit ) - self:F( TaskUnit:GetName() ) - - self:RemoveStateMachine( TaskUnit ) - - -- If a Task Control Menu had been set, then this will be removed. - self:RemoveTaskControlMenu( TaskUnit ) - return self -end - ---- Sets the TimeOut for the @{#TASK}. If @{#TASK} stayed planned for longer than TimeOut, it gets into Cancelled status. --- @param #TASK self --- @param #integer Timer in seconds --- @return #TASK self -function TASK:SetTimeOut ( Timer ) - self:F( Timer ) - self.TimeOut = Timer - self:__TimeOut( self.TimeOut ) - return self -end - ---- Send a message of the @{#TASK} to the assigned @{Wrapper.Group}s. --- @param #TASK self -function TASK:MessageToGroups( Message ) - self:F( { Message = Message } ) - - local Mission = self:GetMission() - local CC = Mission:GetCommandCenter() - - for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - TaskGroup = TaskGroup -- Wrapper.Group#GROUP - if TaskGroup:IsAlive() == true then - CC:MessageToGroup( Message, TaskGroup, TaskGroup:GetName() ) - end - end -end - - ---- Send the briefing message of the @{#TASK} to the assigned @{Wrapper.Group}s. --- @param #TASK self -function TASK:SendBriefingToAssignedGroups() - self:F2() - - for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if TaskGroup:IsAlive() then - if self:IsGroupAssigned( TaskGroup ) then - TaskGroup:Message( self.TaskBriefing, 60 ) - end - end - end -end - - ---- UnAssign the @{#TASK} from the @{Wrapper.Group}s. --- @param #TASK self -function TASK:UnAssignFromGroups() - self:F2() - - for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if TaskGroup:IsAlive() == true then - if self:IsGroupAssigned(TaskGroup) then - self:UnAssignFromGroup( TaskGroup ) - end - end - end -end - - - ---- Returns if the @{#TASK} has still alive and assigned Units. --- @param #TASK self --- @return #boolean -function TASK:HasAliveUnits() - self:F() - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if TaskGroup:IsAlive() == true then - if self:IsStateAssigned() then - if self:IsGroupAssigned( TaskGroup ) then - for TaskUnitID, TaskUnit in pairs( TaskGroup:GetUnits() ) do - if TaskUnit:IsAlive() then - self:T( { HasAliveUnits = true } ) - return true - end - end - end - end - end - end - - self:T( { HasAliveUnits = false } ) - return false -end - ---- Set the menu options of the @{#TASK} to all the groups in the SetGroup. --- @param #TASK self --- @param #number MenuTime --- @return #TASK -function TASK:SetMenu( MenuTime ) --R2.1 Mission Reports and Task Reports added. Fixes issue #424. - self:F( { self:GetName(), MenuTime } ) - - --self.SetGroup:Flush() - --for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetAliveSet() ) do - for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do - local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP - if TaskGroup:IsAlive() == true and TaskGroup:GetPlayerNames() then - - -- Set Mission Menus - - local Mission = self:GetMission() - local MissionMenu = Mission:GetMenu( TaskGroup ) - if MissionMenu then - self:SetMenuForGroup( TaskGroup, MenuTime ) - end - end - end -end - - - ---- Set the Menu for a Group --- @param #TASK self --- @param #number MenuTime --- @return #TASK -function TASK:SetMenuForGroup( TaskGroup, MenuTime ) - - if self:IsStatePlanned() or self:IsStateAssigned() then - self:SetPlannedMenuForGroup( TaskGroup, MenuTime ) - if self:IsGroupAssigned( TaskGroup ) then - self:SetAssignedMenuForGroup( TaskGroup, MenuTime ) - end - end -end - - ---- Set the planned menu option of the @{#TASK}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @param #string MenuText The menu text. --- @param #number MenuTime --- @return #TASK self -function TASK:SetPlannedMenuForGroup( TaskGroup, MenuTime ) - self:F( TaskGroup:GetName() ) - - local Mission = self:GetMission() - local MissionName = Mission:GetName() - local MissionMenu = Mission:GetMenu( TaskGroup ) - - local TaskType = self:GetType() - local TaskPlayerCount = self:GetPlayerCount() - local TaskPlayerString = string.format( " (%dp)", TaskPlayerCount ) - local TaskText = string.format( "%s", self:GetName() ) - local TaskName = string.format( "%s", self:GetName() ) - - self.MenuPlanned = self.MenuPlanned or {} - self.MenuPlanned[TaskGroup] = MENU_GROUP_DELAYED:New( TaskGroup, "Join Planned Task", MissionMenu, Mission.MenuReportTasksPerStatus, Mission, TaskGroup, "Planned" ):SetTime( MenuTime ):SetTag( "Tasking" ) - local TaskTypeMenu = MENU_GROUP_DELAYED:New( TaskGroup, TaskType, self.MenuPlanned[TaskGroup] ):SetTime( MenuTime ):SetTag( "Tasking" ) - local TaskTypeMenu = MENU_GROUP_DELAYED:New( TaskGroup, TaskText, TaskTypeMenu ):SetTime( MenuTime ):SetTag( "Tasking" ) - - if not Mission:IsGroupAssigned( TaskGroup ) then - --self:F( { "Replacing Join Task menu" } ) - local JoinTaskMenu = MENU_GROUP_COMMAND_DELAYED:New( TaskGroup, string.format( "Join Task" ), TaskTypeMenu, self.MenuAssignToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) - local MarkTaskMenu = MENU_GROUP_COMMAND_DELAYED:New( TaskGroup, string.format( "Mark Task Location on Map" ), TaskTypeMenu, self.MenuMarkToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) - end - - local ReportTaskMenu = MENU_GROUP_COMMAND_DELAYED:New( TaskGroup, string.format( "Report Task Details" ), TaskTypeMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) - - return self -end - ---- Set the assigned menu options of the @{#TASK}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @param #number MenuTime --- @return #TASK self -function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) - self:F( { TaskGroup:GetName(), MenuTime } ) - - local TaskType = self:GetType() - local TaskPlayerCount = self:GetPlayerCount() - local TaskPlayerString = string.format( " (%dp)", TaskPlayerCount ) - local TaskText = string.format( "%s%s", self:GetName(), TaskPlayerString ) --, TaskThreatLevelString ) - local TaskName = string.format( "%s", self:GetName() ) - - for UnitName, TaskUnit in pairs( TaskGroup:GetPlayerUnits() ) do - local TaskUnit = TaskUnit -- Wrapper.Unit#UNIT - if TaskUnit then - local MenuControl = self:GetTaskControlMenu( TaskUnit ) - local TaskControl = MENU_GROUP:New( TaskGroup, "Control Task", MenuControl ):SetTime( MenuTime ):SetTag( "Tasking" ) - if self:IsStateAssigned() then - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Abort Task" ), TaskControl, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) - end - local MarkMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Mark Task Location on Map" ), TaskControl, self.MenuMarkToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) - local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Details" ), TaskControl, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) - if not self.FlashTaskStatus then - local TaskFlashStatusMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Flash Task Details" ), TaskControl, self.MenuFlashTaskStatus, self, TaskGroup, true ):SetTime( MenuTime ):SetTag( "Tasking" ) - else - local TaskFlashStatusMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Stop Flash Task Details" ), TaskControl, self.MenuFlashTaskStatus, self, TaskGroup, nil ):SetTime( MenuTime ):SetTag( "Tasking" ) - end - end - end - - return self -end - ---- Remove the menu options of the @{#TASK} to all the groups in the SetGroup. --- @param #TASK self --- @param #number MenuTime --- @return #TASK -function TASK:RemoveMenu( MenuTime ) - self:F( { self:GetName(), MenuTime } ) - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if TaskGroup:IsAlive() == true then - local TaskGroup = TaskGroup -- Wrapper.Group#GROUP - if TaskGroup:IsAlive() == true and TaskGroup:GetPlayerNames() then - self:RefreshMenus( TaskGroup, MenuTime ) - end - end - end -end - - ---- Remove the menu option of the @{#TASK} for a @{Wrapper.Group}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @param #number MenuTime --- @return #TASK self -function TASK:RefreshMenus( TaskGroup, MenuTime ) - self:F( { TaskGroup:GetName(), MenuTime } ) - - local Mission = self:GetMission() - local MissionName = Mission:GetName() - local MissionMenu = Mission:GetMenu( TaskGroup ) - - local TaskName = self:GetName() - self.MenuPlanned = self.MenuPlanned or {} - local PlannedMenu = self.MenuPlanned[TaskGroup] - - self.MenuAssigned = self.MenuAssigned or {} - local AssignedMenu = self.MenuAssigned[TaskGroup] - - if PlannedMenu then - self.MenuPlanned[TaskGroup] = PlannedMenu:Remove( MenuTime , "Tasking" ) - PlannedMenu:Set() - end - - if AssignedMenu then - self.MenuAssigned[TaskGroup] = AssignedMenu:Remove( MenuTime, "Tasking" ) - AssignedMenu:Set() - end - -end - ---- Remove the assigned menu option of the @{#TASK} for a @{Wrapper.Group}. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @param #number MenuTime --- @return #TASK self -function TASK:RemoveAssignedMenuForGroup( TaskGroup ) - self:F() - - local Mission = self:GetMission() - local MissionName = Mission:GetName() - local MissionMenu = Mission:GetMenu( TaskGroup ) - - if MissionMenu then - MissionMenu:RemoveSubMenus() - end - -end - --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup -function TASK:MenuAssignToGroup( TaskGroup ) - - self:F( "Join Task menu selected") - - self:AssignToGroup( TaskGroup ) -end - --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup -function TASK:MenuMarkToGroup( TaskGroup ) - self:F() - - self:UpdateTaskInfo( self.DetectedItem ) - - local TargetCoordinates = self.TaskInfo:GetData( "Coordinates" ) -- Core.Point#COORDINATE - if TargetCoordinates then - for TargetCoordinateID, TargetCoordinate in pairs( TargetCoordinates ) do - local Report = REPORT:New():SetIndent( 0 ) - self.TaskInfo:Report( Report, "M", TaskGroup, self ) - local MarkText = Report:Text( ", " ) - self:F( { Coordinate = TargetCoordinate, MarkText = MarkText } ) - TargetCoordinate:MarkToGroup( MarkText, TaskGroup ) - --Coordinate:MarkToAll( Briefing ) - end - else - local TargetCoordinate = self.TaskInfo:GetData( "Coordinate" ) -- Core.Point#COORDINATE - if TargetCoordinate then - local Report = REPORT:New():SetIndent( 0 ) - self.TaskInfo:Report( Report, "M", TaskGroup, self ) - local MarkText = Report:Text( ", " ) - self:F( { Coordinate = TargetCoordinate, MarkText = MarkText } ) - TargetCoordinate:MarkToGroup( MarkText, TaskGroup ) - end - end - -end - ---- Report the task status. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup -function TASK:MenuTaskStatus( TaskGroup ) - - if TaskGroup:IsAlive() then - - local ReportText = self:ReportDetails( TaskGroup ) - - self:T( ReportText ) - self:GetMission():GetCommandCenter():MessageTypeToGroup( ReportText, TaskGroup, MESSAGE.Type.Detailed ) - end - -end - ---- Report the task status. --- @param #TASK self -function TASK:MenuFlashTaskStatus( TaskGroup, Flash ) - - self.FlashTaskStatus = Flash - - if self.FlashTaskStatus then - self.FlashTaskScheduler, self.FlashTaskScheduleID = SCHEDULER:New( self, self.MenuTaskStatus, { TaskGroup }, 0, 60) --Issue #1383 never ending flash messages - else - if self.FlashTaskScheduler then - self.FlashTaskScheduler:Stop( self.FlashTaskScheduleID ) - self.FlashTaskScheduler = nil - self.FlashTaskScheduleID = nil - end - end - -end - ---- Report the task status. --- @param #TASK self -function TASK:MenuTaskAbort( TaskGroup ) - - self:AbortGroup( TaskGroup ) -end - - - ---- Returns the @{#TASK} name. --- @param #TASK self --- @return #string TaskName -function TASK:GetTaskName() - return self.TaskName -end - ---- Returns the @{#TASK} briefing. --- @param #TASK self --- @return #string Task briefing. -function TASK:GetTaskBriefing() - return self.TaskBriefing -end - - - - ---- Get the default or currently assigned @{Core.Fsm#FSM_PROCESS} template with key ProcessName. --- @param #TASK self --- @param #string ProcessName --- @return Core.Fsm#FSM_PROCESS -function TASK:GetProcessTemplate( ProcessName ) - - local ProcessTemplate = self.ProcessClasses[ProcessName] - - return ProcessTemplate -end - - - --- TODO: Obsolete? ---- Fail processes from @{#TASK} with key @{Wrapper.Unit}. --- @param #TASK self --- @param #string TaskUnitName --- @return #TASK self -function TASK:FailProcesses( TaskUnitName ) - - for ProcessID, ProcessData in pairs( self.Processes[TaskUnitName] ) do - local Process = ProcessData - Process.Fsm:Fail() - end -end - ---- Add a FiniteStateMachine to @{#TASK} with key @{Wrapper.Unit}. --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @param Core.Fsm#FSM_PROCESS Fsm --- @return #TASK self -function TASK:SetStateMachine( TaskUnit, Fsm ) - self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil, Fsm:GetClassNameAndID() } ) - - self.Fsm[TaskUnit] = Fsm - - return Fsm -end - ---- Gets the FiniteStateMachine of @{#TASK} with key @{Wrapper.Unit}. --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return Core.Fsm#FSM_PROCESS -function TASK:GetStateMachine( TaskUnit ) - self:F2( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) - - return self.Fsm[TaskUnit] -end - ---- Remove FiniteStateMachines from @{#TASK} with key @{Wrapper.Unit}. --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return #TASK self -function TASK:RemoveStateMachine( TaskUnit ) - self:F( { TaskUnit = TaskUnit:GetName(), HasFsm = ( self.Fsm[TaskUnit] ~= nil ) } ) - - --self:F( self.Fsm ) - --for TaskUnitT, Fsm in pairs( self.Fsm ) do - --local Fsm = Fsm -- Core.Fsm#FSM_PROCESS - --self:F( TaskUnitT ) - --self.Fsm[TaskUnit] = nil - --end - - if self.Fsm[TaskUnit] then - self.Fsm[TaskUnit]:Remove() - self.Fsm[TaskUnit] = nil - end - - collectgarbage() - self:F( "Garbage Collected, Processes should be finalized now ...") -end - - ---- Checks if there is a FiniteStateMachine assigned to @{Wrapper.Unit} for @{#TASK}. --- @param #TASK self --- @param Wrapper.Unit#UNIT TaskUnit --- @return #TASK self -function TASK:HasStateMachine( TaskUnit ) - self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } ) - - return ( self.Fsm[TaskUnit] ~= nil ) -end - - ---- Gets the Scoring of the task --- @param #TASK self --- @return Functional.Scoring#SCORING Scoring -function TASK:GetScoring() - return self.Mission:GetScoring() -end - - ---- Gets the Task Index, which is a combination of the Task type, the Task name. --- @param #TASK self --- @return #string The Task ID -function TASK:GetTaskIndex() - - local TaskType = self:GetType() - local TaskName = self:GetName() - - return TaskType .. "." .. TaskName -end - ---- Sets the Name of the Task --- @param #TASK self --- @param #string TaskName -function TASK:SetName( TaskName ) - self.TaskName = TaskName -end - ---- Gets the Name of the Task --- @param #TASK self --- @return #string The Task Name -function TASK:GetName() - return self.TaskName -end - ---- Sets the Type of the Task --- @param #TASK self --- @param #string TaskType -function TASK:SetType( TaskType ) - self.TaskType = TaskType -end - ---- Gets the Type of the Task --- @param #TASK self --- @return #string TaskType -function TASK:GetType() - return self.TaskType -end - ---- Sets the ID of the Task --- @param #TASK self --- @param #string TaskID -function TASK:SetID( TaskID ) - self.TaskID = TaskID -end - ---- Gets the ID of the Task --- @param #TASK self --- @return #string TaskID -function TASK:GetID() - return self.TaskID -end - - ---- Sets a @{#TASK} to status **Success**. --- @param #TASK self -function TASK:StateSuccess() - self:SetState( self, "State", "Success" ) - return self -end - ---- Is the @{#TASK} status **Success**. --- @param #TASK self -function TASK:IsStateSuccess() - return self:Is( "Success" ) -end - ---- Sets a @{#TASK} to status **Failed**. --- @param #TASK self -function TASK:StateFailed() - self:SetState( self, "State", "Failed" ) - return self -end - ---- Is the @{#TASK} status **Failed**. --- @param #TASK self -function TASK:IsStateFailed() - return self:Is( "Failed" ) -end - ---- Sets a @{#TASK} to status **Planned**. --- @param #TASK self -function TASK:StatePlanned() - self:SetState( self, "State", "Planned" ) - return self -end - ---- Is the @{#TASK} status **Planned**. --- @param #TASK self -function TASK:IsStatePlanned() - return self:Is( "Planned" ) -end - ---- Sets a @{#TASK} to status **Aborted**. --- @param #TASK self -function TASK:StateAborted() - self:SetState( self, "State", "Aborted" ) - return self -end - ---- Is the @{#TASK} status **Aborted**. --- @param #TASK self -function TASK:IsStateAborted() - return self:Is( "Aborted" ) -end - ---- Sets a @{#TASK} to status **Cancelled**. --- @param #TASK self -function TASK:StateCancelled() - self:SetState( self, "State", "Cancelled" ) - return self -end - ---- Is the @{#TASK} status **Cancelled**. --- @param #TASK self -function TASK:IsStateCancelled() - return self:Is( "Cancelled" ) -end - ---- Sets a @{#TASK} to status **Assigned**. --- @param #TASK self -function TASK:StateAssigned() - self:SetState( self, "State", "Assigned" ) - return self -end - ---- Is the @{#TASK} status **Assigned**. --- @param #TASK self -function TASK:IsStateAssigned() - return self:Is( "Assigned" ) -end - ---- Sets a @{#TASK} to status **Hold**. --- @param #TASK self -function TASK:StateHold() - self:SetState( self, "State", "Hold" ) - return self -end - ---- Is the @{#TASK} status **Hold**. --- @param #TASK self -function TASK:IsStateHold() - return self:Is( "Hold" ) -end - ---- Sets a @{#TASK} to status **Replanned**. --- @param #TASK self -function TASK:StateReplanned() - self:SetState( self, "State", "Replanned" ) - return self -end - ---- Is the @{#TASK} status **Replanned**. --- @param #TASK self -function TASK:IsStateReplanned() - return self:Is( "Replanned" ) -end - ---- Gets the @{#TASK} status. --- @param #TASK self -function TASK:GetStateString() - return self:GetState( self, "State" ) -end - ---- Sets a @{#TASK} briefing. --- @param #TASK self --- @param #string TaskBriefing --- @return #TASK self -function TASK:SetBriefing( TaskBriefing ) - self:F(TaskBriefing) - self.TaskBriefing = TaskBriefing - return self -end - ---- Gets the @{#TASK} briefing. --- @param #TASK self --- @return #string The briefing text. -function TASK:GetBriefing() - return self.TaskBriefing -end - - - - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName ) - - --- This test is required, because the state transition will be fired also when the state does not change in case of an event. - if From ~= "Assigned" then - - local PlayerNames = self:GetPlayerNames() - local PlayerText = REPORT:New() - for PlayerName, TaskName in pairs( PlayerNames ) do - PlayerText:Add( PlayerName ) - end - - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is assigned to players " .. PlayerText:Text(",") .. ". Good Luck!" ) - - -- Set the total Progress to be achieved. - self:SetGoalTotal() -- Polymorphic to set the initial goal total! - - if self.Dispatcher then - self:F( "Firing Assign event " ) - self.Dispatcher:Assign( self, PlayerUnit, PlayerName ) - end - - self:GetMission():__Start( 1 ) - - -- When the task is assigned, the task goal needs to be checked of the derived classes. - self:__Goal( -10, PlayerUnit, PlayerName ) -- Polymorphic - - self:SetMenu() - - self:F( { "--> Task Assigned", TaskName = self:GetName(), Mission = self:GetMission():GetName() } ) - self:F( { "--> Task Player Names", PlayerNames = PlayerNames } ) - - end -end - - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onenterSuccess( From, Event, To ) - - self:F( { "<-> Task Replanned", TaskName = self:GetName(), Mission = self:GetMission():GetName() } ) - self:F( { "<-> Task Player Names", PlayerNames = self:GetPlayerNames() } ) - - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is successful! Good job!" ) - self:UnAssignFromGroups() - - self:GetMission():__MissionGoals( 1 ) - -end - - ---- FSM function for a TASK --- @param #TASK self --- @param #string From --- @param #string Event --- @param #string To -function TASK:onenterAborted( From, Event, To ) - - self:F( { "<-- Task Aborted", TaskName = self:GetName(), Mission = self:GetMission():GetName() } ) - self:F( { "<-- Task Player Names", PlayerNames = self:GetPlayerNames() } ) - - if From ~= "Aborted" then - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has been aborted! Task may be replanned." ) - self:__Replan( 5 ) - self:SetMenu() - end - -end - - ---- FSM function for a TASK --- @param #TASK self --- @param #string From --- @param #string Event --- @param #string To -function TASK:onenterCancelled( From, Event, To ) - - self:F( { "<-- Task Cancelled", TaskName = self:GetName(), Mission = self:GetMission():GetName() } ) - self:F( { "<-- Player Names", PlayerNames = self:GetPlayerNames() } ) - - if From ~= "Cancelled" then - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has been cancelled! The tactical situation has changed." ) - self:UnAssignFromGroups() - self:SetMenu() - end - -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string From --- @param #string Event --- @param #string To -function TASK:onafterReplan( From, Event, To ) - - self:F( { "Task Replanned", TaskName = self:GetName(), Mission = self:GetMission():GetName() } ) - self:F( { "Task Player Names", PlayerNames = self:GetPlayerNames() } ) - - self:GetMission():GetCommandCenter():MessageToCoalition( "Replanning Task " .. self:GetName() .. "." ) - - self:SetMenu() - -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string From --- @param #string Event --- @param #string To -function TASK:onenterFailed( From, Event, To ) - - self:F( { "Task Failed", TaskName = self:GetName(), Mission = self:GetMission():GetName() } ) - self:F( { "Task Player Names", PlayerNames = self:GetPlayerNames() } ) - - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has failed!" ) - - self:UnAssignFromGroups() -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onstatechange( From, Event, To ) - - if self:IsTrace() then - --MESSAGE:New( "@ Task " .. self.TaskName .. " : " .. From .. " changed to " .. To .. " by " .. Event, 2 ):ToAll() - end - - if self.Scores[To] then - local Scoring = self:GetScoring() - if Scoring then - self:F( { self.Scores[To].ScoreText, self.Scores[To].Score } ) - Scoring:_AddMissionScore( self.Mission, self.Scores[To].ScoreText, self.Scores[To].Score ) - end - end - -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onenterPlanned( From, Event, To) - if not self.TimeOut == 0 then - self.__TimeOut( self.TimeOut ) - end -end - ---- FSM function for a TASK --- @param #TASK self --- @param #string Event --- @param #string From --- @param #string To -function TASK:onbeforeTimeOut( From, Event, To ) - if From == "Planned" then - self:RemoveMenu() - return true - end - return false -end - -do -- Links - - --- Set goal of a task - -- @param #TASK self - -- @param Core.Goal#GOAL Goal - -- @return #TASK - function TASK:SetGoal( Goal ) - self.Goal = Goal - end - - - --- Get goal of a task - -- @param #TASK self - -- @return Core.Goal#GOAL The Goal - function TASK:GetGoal() - return self.Goal - end - - - --- Set dispatcher of a task - -- @param #TASK self - -- @param Tasking.DetectionManager#DETECTION_MANAGER Dispatcher - -- @return #TASK - function TASK:SetDispatcher( Dispatcher ) - self.Dispatcher = Dispatcher - end - - --- Set detection of a task - -- @param #TASK self - -- @param Functional.Detection#DETECTION_BASE Detection - -- @param DetectedItem - -- @return #TASK - function TASK:SetDetection( Detection, DetectedItem ) - - self:F( { DetectedItem, Detection } ) - - self.Detection = Detection - self.DetectedItem = DetectedItem - end - -end - -do -- Reporting - ---- Create a summary report of the Task. --- List the Task Name and Status --- @param #TASK self --- @param Wrapper.Group#GROUP ReportGroup --- @return #string -function TASK:ReportSummary( ReportGroup ) - - self:UpdateTaskInfo( self.DetectedItem ) - - local Report = REPORT:New() - - -- List the name of the Task. - Report:Add( "Task " .. self:GetName() ) - - -- Determine the status of the Task. - Report:Add( "State: <" .. self:GetState() .. ">" ) - - self.TaskInfo:Report( Report, "S", ReportGroup, self ) - - return Report:Text( ', ' ) -end - ---- Create an overiew report of the Task. --- List the Task Name and Status --- @param #TASK self --- @return #string -function TASK:ReportOverview( ReportGroup ) - - self:UpdateTaskInfo( self.DetectedItem ) - - -- List the name of the Task. - local TaskName = self:GetName() - local Report = REPORT:New() - - self.TaskInfo:Report( Report, "O", ReportGroup, self ) - - return Report:Text() -end - ---- Create a count of the players in the Task. --- @param #TASK self --- @return #number The total number of players in the task. -function TASK:GetPlayerCount() --R2.1 Get a count of the players. - - local PlayerCount = 0 - - -- Loop each Unit active in the Task, and find Player Names. - for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do - local PlayerGroup = PlayerGroup -- Wrapper.Group#GROUP - if PlayerGroup:IsAlive() == true then - if self:IsGroupAssigned( PlayerGroup ) then - local PlayerNames = PlayerGroup:GetPlayerNames() - PlayerCount = PlayerCount + ((PlayerNames) and #PlayerNames or 0) -- PlayerNames can be nil when there are no players. - end - end - end - - return PlayerCount -end - - ---- Create a list of the players in the Task. --- @param #TASK self --- @return #map<#string,Wrapper.Group#GROUP> A map of the players -function TASK:GetPlayerNames() --R2.1 Get a map of the players. - - local PlayerNameMap = {} - - -- Loop each Unit active in the Task, and find Player Names. - for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do - local PlayerGroup = PlayerGroup -- Wrapper.Group#GROUP - if PlayerGroup:IsAlive() == true then - if self:IsGroupAssigned( PlayerGroup ) then - local PlayerNames = PlayerGroup:GetPlayerNames() - for PlayerNameID, PlayerName in pairs( PlayerNames or {} ) do - PlayerNameMap[PlayerName] = PlayerGroup - end - end - end - end - - return PlayerNameMap -end - - ---- Create a detailed report of the Task. --- List the Task Status, and the Players assigned to the Task. --- @param #TASK self --- @param Wrapper.Group#GROUP TaskGroup --- @return #string -function TASK:ReportDetails( ReportGroup ) - - self:UpdateTaskInfo( self.DetectedItem ) - - local Report = REPORT:New():SetIndent( 3 ) - - -- List the name of the Task. - local Name = self:GetName() - - -- Determine the status of the Task. - local Status = "<" .. self:GetState() .. ">" - - Report:Add( "Task " .. Name .. " - " .. Status .. " - Detailed Report" ) - - -- Loop each Unit active in the Task, and find Player Names. - local PlayerNames = self:GetPlayerNames() - - local PlayerReport = REPORT:New() - for PlayerName, PlayerGroup in pairs( PlayerNames ) do - PlayerReport:Add( "Players group " .. PlayerGroup:GetCallsign() .. ": " .. PlayerName ) - end - local Players = PlayerReport:Text() - - if Players ~= "" then - Report:AddIndent( "Players assigned:", "-" ) - Report:AddIndent( Players ) - end - - self.TaskInfo:Report( Report, "D", ReportGroup, self ) - - return Report:Text() -end - - -end -- Reporting - - -do -- Additional Task Scoring and Task Progress - - --- Add Task Progress for a Player Name - -- @param #TASK self - -- @param #string PlayerName The name of the player. - -- @param #string ProgressText The text that explains the Progress achieved. - -- @param #number ProgressTime The time the progress was achieved. - -- @oaram #number ProgressPoints The amount of points of magnitude granted. This will determine the shared Mission Success scoring. - -- @return #TASK - function TASK:AddProgress( PlayerName, ProgressText, ProgressTime, ProgressPoints ) - self.TaskProgress = self.TaskProgress or {} - self.TaskProgress[ProgressTime] = self.TaskProgress[ProgressTime] or {} - self.TaskProgress[ProgressTime].PlayerName = PlayerName - self.TaskProgress[ProgressTime].ProgressText = ProgressText - self.TaskProgress[ProgressTime].ProgressPoints = ProgressPoints - self:GetMission():AddPlayerName( PlayerName ) - return self - end - - function TASK:GetPlayerProgress( PlayerName ) - local ProgressPlayer = 0 - for ProgressTime, ProgressData in pairs( self.TaskProgress ) do - if PlayerName == ProgressData.PlayerName then - ProgressPlayer = ProgressPlayer + ProgressData.ProgressPoints - end - end - return ProgressPlayer - end - - --- Set a score when progress has been made by the player. - -- @param #TASK 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. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK - function TASK:SetScoreOnProgress( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountPlayer", "Player " .. PlayerName .. " has achieved progress.", Score ) - - return self - end - - --- Set a score when all the targets in scope of the A2A attack, have been destroyed. - -- @param #TASK self - -- @param #string PlayerName The name of the player. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK - function TASK:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Success", "The task is a success!", Score ) - - return self - end - - --- Set a penalty when the A2A attack has failed. - -- @param #TASK self - -- @param #string PlayerName The name of the player. - -- @param #number Penalty The penalty in points, must be a negative value! - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK - function TASK:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) - self:F( { PlayerName, Penalty, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Failed", "The task is a failure!", Penalty ) - - return self - end - -end - -do -- Task Control Menu - - -- The Task Control Menu is a menu attached to the task at the main menu to quickly be able to do actions in the task. - -- The Task Control Menu can only be shown when the task is assigned to the player. - -- The Task Control Menu is linked to the process executing the task, so no task menu can be set to the main static task definition. - - --- Init Task Control Menu - -- @param #TASK self - -- @param Wrapper.Unit#UNIT TaskUnit The @{Wrapper.Unit} that contains a player. - -- @return Task Control Menu Refresh ID - function TASK:InitTaskControlMenu( TaskUnit ) - - self.TaskControlMenuTime = timer.getTime() - - return self.TaskControlMenuTime - end - - --- Get Task Control Menu - -- @param #TASK self - -- @param Wrapper.Unit#UNIT TaskUnit The @{Wrapper.Unit} that contains a player. - -- @return Core.Menu#MENU_GROUP TaskControlMenu The Task Control Menu - function TASK:GetTaskControlMenu( TaskUnit, TaskName ) - - TaskName = TaskName or "" - - local TaskGroup = TaskUnit:GetGroup() - local TaskPlayerCount = TaskGroup:GetPlayerCount() - - if TaskPlayerCount <= 1 then - self.TaskControlMenu = MENU_GROUP:New( TaskUnit:GetGroup(), "Task " .. self:GetName() .. " control" ):SetTime( self.TaskControlMenuTime ) - else - self.TaskControlMenu = MENU_GROUP:New( TaskUnit:GetGroup(), "Task " .. self:GetName() .. " control for " .. TaskUnit:GetPlayerName() ):SetTime( self.TaskControlMenuTime ) - end - - return self.TaskControlMenu - end - - --- Remove Task Control Menu - -- @param #TASK self - -- @param Wrapper.Unit#UNIT TaskUnit The @{Wrapper.Unit} that contains a player. - function TASK:RemoveTaskControlMenu( TaskUnit ) - - if self.TaskControlMenu then - self.TaskControlMenu:Remove() - self.TaskControlMenu = nil - end - end - - --- Refresh Task Control Menu - -- @param #TASK self - -- @param Wrapper.Unit#UNIT TaskUnit The @{Wrapper.Unit} that contains a player. - -- @param MenuTime The refresh time that was used to refresh the Task Control Menu items. - -- @param MenuTag The tag. - function TASK:RefreshTaskControlMenu( TaskUnit, MenuTime, MenuTag ) - - if self.TaskControlMenu then - self.TaskControlMenu:Remove( MenuTime, MenuTag ) - end - end - -end diff --git a/Moose Development/Moose/Tasking/TaskInfo.lua b/Moose Development/Moose/Tasking/TaskInfo.lua deleted file mode 100644 index 203b94e01..000000000 --- a/Moose Development/Moose/Tasking/TaskInfo.lua +++ /dev/null @@ -1,379 +0,0 @@ ---- **Tasking** - Controls the information of a Task. --- --- === --- --- ### Author: **FlightControl** --- --- ### Contributions: --- --- === --- --- @module Tasking.TaskInfo --- @image MOOSE.JPG - ---- --- @type TASKINFO --- @extends Core.Base#BASE - ---- --- # TASKINFO class, extends @{Core.Base#BASE} --- --- ![Banner Image](..\Images\deprecated.png) --- --- ## The TASKINFO class implements the methods to contain information and display information of a task. --- --- # Developer Note --- --- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE --- Therefore, this class is considered to be deprecated --- --- @field #TASKINFO -TASKINFO = { - ClassName = "TASKINFO", -} - ---- --- @type TASKINFO.Detail #string A string that flags to document which level of detail needs to be shown in the report. --- --- - "M" for Markings on the Map (F10). --- - "S" for Summary Reports. --- - "O" for Overview Reports. --- - "D" for Detailed Reports. -TASKINFO.Detail = "" - ---- Instantiates a new TASKINFO. --- @param #TASKINFO self --- @param Tasking.Task#TASK Task The task owning the information. --- @return #TASKINFO self -function TASKINFO:New( Task ) - - local self = BASE:Inherit( self, BASE:New() ) -- Core.Base#BASE - - self.Task = Task - self.VolatileInfo = SET_BASE:New() - self.PersistentInfo = SET_BASE:New() - - self.Info = self.VolatileInfo - - return self -end - - ---- Add taskinfo. --- @param #TASKINFO self --- @param #string Key The info key. --- @param Data The data of the info. --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddInfo( Key, Data, Order, Detail, Keep, ShowKey, Type ) - self.VolatileInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail, ShowKey = ShowKey, Type = Type } ) - if Keep == true then - self.PersistentInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail, ShowKey = ShowKey, Type = Type } ) - end - return self -end - - ---- Get taskinfo. --- @param #TASKINFO self --- @param #string The info key. --- @return Data The data of the info. --- @return #number Order The display order, which is a number from 0 to 100. --- @return #TASKINFO.Detail Detail The detail Level. -function TASKINFO:GetInfo( Key ) - local Object = self:Get( Key ) - return Object.Data, Object.Order, Object.Detail -end - - ---- Get data. --- @param #TASKINFO self --- @param #string The info key. --- @return Data The data of the info. -function TASKINFO:GetData( Key ) - local Object = self.Info:Get( Key ) - return Object and Object.Data -end - - ---- Add Text. --- @param #TASKINFO self --- @param #string Key The key. --- @param #string Text The text. --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddText( Key, Text, Order, Detail, Keep ) - self:AddInfo( Key, Text, Order, Detail, Keep ) - return self -end - - ---- Add the task name. --- @param #TASKINFO self --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddTaskName( Order, Detail, Keep ) - self:AddInfo( "TaskName", self.Task:GetName(), Order, Detail, Keep ) - return self -end - - - - ---- Add a Coordinate. --- @param #TASKINFO self --- @param Core.Point#COORDINATE Coordinate --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddCoordinate( Coordinate, Order, Detail, Keep, ShowKey, Name ) - self:AddInfo( Name or "Coordinate", Coordinate, Order, Detail, Keep, ShowKey, "Coordinate" ) - return self -end - - ---- Get the Coordinate. --- @param #TASKINFO self --- @return Core.Point#COORDINATE Coordinate -function TASKINFO:GetCoordinate( Name ) - return self:GetData( Name or "Coordinate" ) -end - - - ---- Add Coordinates. --- @param #TASKINFO self --- @param #list Coordinates --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddCoordinates( Coordinates, Order, Detail, Keep ) - self:AddInfo( "Coordinates", Coordinates, Order, Detail, Keep ) - return self -end - - - ---- Add Threat. --- @param #TASKINFO self --- @param #string ThreatText The text of the Threat. --- @param #string ThreatLevel The level of the Threat. --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddThreat( ThreatText, ThreatLevel, Order, Detail, Keep ) - self:AddInfo( "Threat", " [" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]:" .. ThreatText, Order, Detail, Keep ) - return self -end - - ---- Get Threat. --- @param #TASKINFO self --- @return #string The threat -function TASKINFO:GetThreat() - self:GetInfo( "Threat" ) - return self -end - - - ---- Add the Target count. --- @param #TASKINFO self --- @param #number TargetCount The amount of targets. --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddTargetCount( TargetCount, Order, Detail, Keep ) - self:AddInfo( "Counting", string.format( "%d", TargetCount ), Order, Detail, Keep ) - return self -end - ---- Add the Targets. --- @param #TASKINFO self --- @param #number TargetCount The amount of targets. --- @param #string TargetTypes The text containing the target types. --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddTargets( TargetCount, TargetTypes, Order, Detail, Keep ) - self:AddInfo( "Targets", string.format( "%d of %s", TargetCount, TargetTypes ), Order, Detail, Keep ) - return self -end - ---- Get Targets. --- @param #TASKINFO self --- @return #string The targets -function TASKINFO:GetTargets() - self:GetInfo( "Targets" ) - return self -end - - - - ---- Add the QFE at a Coordinate. --- @param #TASKINFO self --- @param Core.Point#COORDINATE Coordinate --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddQFEAtCoordinate( Coordinate, Order, Detail, Keep ) - self:AddInfo( "QFE", Coordinate, Order, Detail, Keep ) - return self -end - ---- Add the Temperature at a Coordinate. --- @param #TASKINFO self --- @param Core.Point#COORDINATE Coordinate --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddTemperatureAtCoordinate( Coordinate, Order, Detail, Keep ) - self:AddInfo( "Temperature", Coordinate, Order, Detail, Keep ) - return self -end - ---- Add the Wind at a Coordinate. --- @param #TASKINFO self --- @param Core.Point#COORDINATE Coordinate --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddWindAtCoordinate( Coordinate, Order, Detail, Keep ) - self:AddInfo( "Wind", Coordinate, Order, Detail, Keep ) - return self -end - ---- Add Cargo. --- @param #TASKINFO self --- @param Cargo.Cargo#CARGO Cargo --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddCargo( Cargo, Order, Detail, Keep ) - self:AddInfo( "Cargo", Cargo, Order, Detail, Keep ) - return self -end - - ---- Add Cargo set. --- @param #TASKINFO self --- @param Core.Set#SET_CARGO SetCargo --- @param #number Order The display order, which is a number from 0 to 100. --- @param #TASKINFO.Detail Detail The detail Level. --- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. --- @return #TASKINFO self -function TASKINFO:AddCargoSet( SetCargo, Order, Detail, Keep ) - - local CargoReport = REPORT:New() - CargoReport:Add( "" ) - SetCargo:ForEachCargo( - -- @param Cargo.Cargo#CARGO Cargo - function( Cargo ) - CargoReport:Add( string.format( ' - %s (%s) %s - status %s ', Cargo:GetName(), Cargo:GetType(), Cargo:GetTransportationMethod(), Cargo:GetCurrentState() ) ) - end - ) - - self:AddInfo( "Cargo", CargoReport:Text(), Order, Detail, Keep ) - - - return self -end - - - ---- Create the taskinfo Report --- @param #TASKINFO self --- @param Core.Report#REPORT Report --- @param #TASKINFO.Detail Detail The detail Level. --- @param Wrapper.Group#GROUP ReportGroup --- @param Tasking.Task#TASK Task --- @return #TASKINFO self -function TASKINFO:Report( Report, Detail, ReportGroup, Task ) - - local Line = 0 - local LineReport = REPORT:New() - - if not self.Task:IsStatePlanned() and not self.Task:IsStateAssigned() then - self.Info = self.PersistentInfo - end - - for Key, Data in UTILS.spairs( self.Info.Set, function( t, a, b ) return t[a].Order < t[b].Order end ) do - - if Data.Detail:find( Detail ) then - local Text = "" - local ShowKey = ( Data.ShowKey == nil or Data.ShowKey == true ) - if Key == "TaskName" then - Key = nil - Text = Data.Data - elseif Data.Type and Data.Type == "Coordinate" then - local Coordinate = Data.Data -- Core.Point#COORDINATE - Text = Coordinate:ToString( ReportGroup:GetUnit(1), nil, Task ) - elseif Key == "Threat" then - local DataText = Data.Data -- #string - Text = DataText - elseif Key == "Counting" then - local DataText = Data.Data -- #string - Text = DataText - elseif Key == "Targets" then - local DataText = Data.Data -- #string - Text = DataText - elseif Key == "QFE" then - local Coordinate = Data.Data -- Core.Point#COORDINATE - Text = Coordinate:ToStringPressure( ReportGroup:GetUnit(1), nil, Task ) - elseif Key == "Temperature" then - local Coordinate = Data.Data -- Core.Point#COORDINATE - Text = Coordinate:ToStringTemperature( ReportGroup:GetUnit(1), nil, Task ) - elseif Key == "Wind" then - local Coordinate = Data.Data -- Core.Point#COORDINATE - Text = Coordinate:ToStringWind( ReportGroup:GetUnit(1), nil, Task ) - elseif Key == "Cargo" then - local DataText = Data.Data -- #string - Text = DataText - elseif Key == "Friendlies" then - local DataText = Data.Data -- #string - Text = DataText - elseif Key == "Players" then - local DataText = Data.Data -- #string - Text = DataText - else - local DataText = Data.Data -- #string - if type(DataText) == "string" then --Issue #1388 - don't just assume this is a string - Text = DataText - end - end - - if Line < math.floor( Data.Order / 10 ) then - if Line == 0 then - Report:AddIndent( LineReport:Text( ", " ), "-" ) - else - Report:AddIndent( LineReport:Text( ", " ) ) - end - LineReport = REPORT:New() - Line = math.floor( Data.Order / 10 ) - end - - if Text ~= "" then - LineReport:Add( ( ( Key and ShowKey == true ) and ( Key .. ": " ) or "" ) .. Text ) - end - - end - end - - Report:AddIndent( LineReport:Text( ", " ) ) -end diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua deleted file mode 100644 index 43d6e1f5a..000000000 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ /dev/null @@ -1,657 +0,0 @@ ---- **Tasking** - The TASK_A2A models tasks for players in Air to Air engagements. --- --- === --- --- ### Author: **FlightControl** --- --- ### Contributions: --- --- === --- --- @module Tasking.Task_A2A --- @image MOOSE.JPG - -do -- TASK_A2A - - --- The TASK_A2A class - -- @type TASK_A2A - -- @field Core.Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - - --- Defines Air To Air tasks for a @{Core.Set} of Target Units, - -- based on the tasking capabilities defined in @{Tasking.Task#TASK}. - -- - -- ![Banner Image](..\Images\deprecated.png) - -- - -- The TASK_A2A is implemented using a @{Core.Fsm#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 @{Wrapper.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) 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. - -- - -- # Developer Note - -- - -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE - -- Therefore, this class is considered to be deprecated - -- - -- @field #TASK_A2A - TASK_A2A = { - ClassName = "TASK_A2A" - } - - --- Instantiates a new TASK_A2A. - -- @param #TASK_A2A self - -- @param Tasking.Mission#MISSION Mission - -- @param Core.Set#SET_GROUP SetAttack The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param Core.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: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(), {} ) - 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" ) - - -- @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 - -- @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 - 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:F( { 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:F( { 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: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 - local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT - if TargetUnit then - local Coordinate = TargetUnit:GetPointVec3() - self:T( { TargetCoordinate = Coordinate, Coordinate:GetX(), Coordinate:GetAlt(), Coordinate:GetZ() } ) - Task:SetTargetCoordinate( Coordinate, 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:F( { 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 - -- @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() .. " )" - 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, 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 - -- @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 - - 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 ) - - 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() - self.TaskInfo:AddTaskName( 0, "MSOD" ) - self.TaskInfo:AddCoordinate( TargetCoordinate, 1, "SOD" ) - - local ThreatLevel, ThreatText - if DetectedItem then - ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( DetectedItem ) - else - 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() - local TargetTypes = {} - for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do - local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit ) - if not TargetTypes[TargetType] then - TargetTypes[TargetType] = TargetType - ReportTypes:Add( TargetType ) - end - end - self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", 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 ) - end - end - end - - --- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection. - -- @param #TASK_A2A 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_A2A:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup ) - - 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() ) - return math.floor( Distance ) - elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then - return 1 - end - - return 0 - end - -end - -do -- TASK_A2A_INTERCEPT - - --- The TASK_A2A_INTERCEPT class - -- @type TASK_A2A_INTERCEPT - -- @field Core.Set#SET_UNIT TargetSetUnit - -- @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 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" - } - - --- 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 #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" ) - - return self - end - - --- 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. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2A_INTERCEPT - function TASK_A2A_INTERCEPT:SetScoreOnProgress( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has intercepted a target.", Score ) - - return self - end - - --- Set a score when all the targets in scope of the A2A attack, have been destroyed. - -- @param #TASK_A2A_INTERCEPT self - -- @param #string PlayerName The name of the player. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2A_INTERCEPT - function TASK_A2A_INTERCEPT:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Success", "All targets have been successfully intercepted!", Score ) - - return self - end - - --- Set a penalty when the A2A attack has failed. - -- @param #TASK_A2A_INTERCEPT self - -- @param #string PlayerName The name of the player. - -- @param #number Penalty The penalty in points, must be a negative value! - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2A_INTERCEPT - function TASK_A2A_INTERCEPT:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) - self:F( { PlayerName, Penalty, TaskUnit } ) - - 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 - -- @type TASK_A2A_SWEEP - -- @field Core.Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - - --- Defines a sweep task for a human player to be executed. - -- 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 - -- 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" - } - - --- Instantiates a new TASK_A2A_SWEEP. - -- @param #TASK_A2A_SWEEP 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_A2A_SWEEP self - 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" ) - - return self - 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. - -- @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. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2A_SWEEP - function TASK_A2A_SWEEP:SetScoreOnProgress( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has sweeped a target.", Score ) - - return self - end - - --- Set a score when all the targets in scope of the A2A attack, have been destroyed. - -- @param #TASK_A2A_SWEEP self - -- @param #string PlayerName The name of the player. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2A_SWEEP - function TASK_A2A_SWEEP:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Success", "All targets have been successfully sweeped!", Score ) - - return self - end - - --- Set a penalty when the A2A attack has failed. - -- @param #TASK_A2A_SWEEP self - -- @param #string PlayerName The name of the player. - -- @param #number Penalty The penalty in points, must be a negative value! - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2A_SWEEP - function TASK_A2A_SWEEP:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) - self:F( { PlayerName, Penalty, TaskUnit } ) - - 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 - -- @type TASK_A2A_ENGAGE - -- @field Core.Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - - --- 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 - -- 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" - } - - --- 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 #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" ) - - return self - 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. - -- @param #number Score The score in points to be granted when task process has been achieved. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2A_ENGAGE - function TASK_A2A_ENGAGE:SetScoreOnProgress( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has engaged and destroyed a target.", Score ) - - return self - end - - --- Set a score when all the targets in scope of the A2A attack, have been destroyed. - -- @param #TASK_A2A_ENGAGE self - -- @param #string PlayerName The name of the player. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2A_ENGAGE - function TASK_A2A_ENGAGE:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Success", "All targets have been successfully engaged!", Score ) - - return self - end - - --- Set a penalty when the A2A attack has failed. - -- @param #TASK_A2A_ENGAGE self - -- @param #string PlayerName The name of the player. - -- @param #number Penalty The penalty in points, must be a negative value! - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2A_ENGAGE - function TASK_A2A_ENGAGE:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) - self:F( { PlayerName, Penalty, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Failed", "The target engagement has failed!", Penalty ) - - return self - end - -end - diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua deleted file mode 100644 index b3c4b0568..000000000 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ /dev/null @@ -1,622 +0,0 @@ ---- **Tasking** - Dynamically allocates A2A tasks to human players, based on detected airborne targets through an EWR network. --- --- **Features:** --- --- * Dynamically assign tasks to human players based on detected targets. --- * Dynamically change the tasks as the tactical situation evolves during the mission. --- * Dynamically assign (CAP) Control Air Patrols tasks for human players to perform CAP. --- * Dynamically assign (GCI) Ground Control Intercept tasks for human players to perform GCI. --- * Dynamically assign Engage tasks for human players to engage on close-by airborne bogeys. --- * Define and use an EWR (Early Warning Radar) network. --- * Define different ranges to engage upon intruders. --- * Keep task achievements. --- * Score task achievements. --- --- === --- --- ### Author: **FlightControl** --- --- ### Contributions: --- --- === --- --- @module Tasking.Task_A2A_Dispatcher --- @image Task_A2A_Dispatcher.JPG - -do -- TASK_A2A_DISPATCHER - - --- TASK_A2A_DISPATCHER class. - -- @type TASK_A2A_DISPATCHER - -- @extends Tasking.DetectionManager#DETECTION_MANAGER - - --- Orchestrates the dynamic dispatching of tasks upon groups of detected units determined a @{Core.Set} of EWR installation groups. - -- - -- ![Banner Image](..\Images\deprecated.png) - -- - -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia3.JPG) - -- - -- The EWR will detect units, will group them, and will dispatch @{Tasking.Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. - -- Find a summary below describing for which situation a task type is created: - -- - -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia9.JPG) - -- - -- * **INTERCEPT Task**: Is created when the target is known, is detected and within a danger zone, and there is no friendly airborne in range. - -- * **SWEEP Task**: Is created when the target is unknown, was detected and the last position is only known, and within a danger zone, and there is no friendly airborne in range. - -- * **ENGAGE Task**: Is created when the target is known, is detected and within a danger zone, and there is a friendly airborne in range, that will receive this task. - -- - -- ## 1. TASK\_A2A\_DISPATCHER constructor: - -- - -- The @{#TASK_A2A_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance. - -- - -- ### 1.1. Define or set the **Mission**: - -- - -- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter. - -- - -- local HQ = GROUP:FindByName( "HQ", "Bravo" ) - -- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED ) - -- - -- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission. - -- Create the MISSION object, and hook it under the command center. - -- - -- ### 1.2. Build a set of the groups seated by human players: - -- - -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia6.JPG) - -- - -- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into. - -- - -- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart() - -- - -- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission. - -- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available. - -- - -- ### 1.3. Define the **EWR network**: - -- - -- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third parameter. - -- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy. - -- - -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia5.JPG) - -- - -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. - -- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). - -- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. - -- The position of these units is very important as they need to provide enough coverage - -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. - -- - -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia7.JPG) - -- - -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. - -- For example if they are a long way forward and can detect enemy planes on the ground and taking off - -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. - -- Having the radars further back will mean a slower escalation because fewer targets will be detected and - -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. - -- It all depends on what the desired effect is. - -- - -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional.Detection#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class. - -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, - -- increasing or decreasing the radar coverage of the Early Warning System. - -- - -- See the following example to setup an EWR network containing EWR stations and AWACS. - -- - -- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart() - -- - -- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 ) - -- EWRDetection:SetFriendliesRange( 10000 ) - -- EWRDetection:SetRefreshTimeInterval(30) - -- - -- -- Setup the A2A dispatcher, and initialize it. - -- A2ADispatcher = TASK_A2A_DISPATCHER:New( Mission, AttackGroups, EWRDetection ) - -- - -- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**. - -- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set. - -- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set. - -- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is chosen, which is 6 km. - -- The **EWRDetection** object is then passed to the @{#TASK_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism. - -- - -- ### 2. Define the detected **target grouping radius**: - -- - -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia8.JPG) - -- - -- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed. - -- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. - -- Fast planes like in the 80s, need a larger radius than WWII planes. - -- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft. - -- - -- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate - -- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small! - -- - -- ## 3. Set the **Engage radius**: - -- - -- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission. - -- - -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia11.JPG) - -- - -- So, if there is a target area detected and reported, - -- then any friendlies that are airborne near this target area, - -- will be commanded to (re-)engage that target when available (if no other tasks were commanded). - -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, - -- will be considered to receive the command to engage that target area. - -- You need to evaluate the value of this parameter carefully. - -- If too small, more intercept missions may be triggered upon detected target areas. - -- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. - -- - -- ## 4. Set **Scoring** and **Messages**: - -- - -- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Tasking.Task} dispatched by the TASK\_A2A\_DISPATCHER. - -- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_, - -- when the player reaches certain achievements in the task. - -- - -- The prototype to handle the **Assign** event needs to be developed as follows: - -- - -- TaskDispatcher = TASK_A2A_DISPATCHER:New( ... ) - -- - -- -- @param #TaskDispatcher self - -- -- @param #string From Contains the name of the state from where the Event was triggered. - -- -- @param #string Event Contains the name of the event that was triggered. In this case Assign. - -- -- @param #string To Contains the name of the state that will be transitioned to. - -- -- @param Tasking.Task_A2A#TASK_A2A Task The Task object, which is any derived object from TASK_A2A. - -- -- @param Wrapper.Unit#UNIT TaskUnit The Unit or Client that contains the Player. - -- -- @param #string PlayerName The name of the Player that joined the TaskUnit. - -- function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) - -- Task:SetScoreOnProgress( PlayerName, 20, TaskUnit ) - -- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit ) - -- Task:SetScoreOnFail( PlayerName, -100, TaskUnit ) - -- end - -- - -- The **OnAfterAssign** method (function) is added to the TaskDispatcher object. - -- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher. - -- So, this method will be called only **ONCE** when a player joins a unit in scope of the task. - -- - -- The TASK class implements various methods to additional **set scoring** for player achievements: - -- - -- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task. - -- Examples of **task progress** can be destroying units, arriving at zones etc. - -- - -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state. - -- This means the **task has been successfully completed**. - -- - -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state. - -- This means the **task has not been successfully completed**, and the scores must be given with a negative value! - -- - -- # Developer Note - -- - -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE - -- Therefore, this class is considered to be deprecated - -- - -- @field #TASK_A2A_DISPATCHER - TASK_A2A_DISPATCHER = { - ClassName = "TASK_A2A_DISPATCHER", - Mission = nil, - Detection = nil, - Tasks = {}, - SweepZones = {}, - } - - --- TASK_A2A_DISPATCHER constructor. - -- @param #TASK_A2A_DISPATCHER self - -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. - -- @param Core.Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. - -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. - -- @return #TASK_A2A_DISPATCHER self - function TASK_A2A_DISPATCHER:New( Mission, SetGroup, Detection ) - - -- Inherits from DETECTION_MANAGER - local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2A_DISPATCHER - - self.Detection = Detection - self.Mission = Mission - self.FlashNewTask = false - - -- TODO: Check detection through radar. - self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER ) - self.Detection:InitDetectRadar( true ) - self.Detection:SetRefreshTimeInterval( 30 ) - - self:AddTransition( "Started", "Assign", "Started" ) - - --- OnAfter Transition Handler for Event Assign. - -- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign - -- @param #TASK_A2A_DISPATCHER self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Tasking.Task_A2A#TASK_A2A Task - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param #string PlayerName - - self:__Start( 5 ) - - return self - end - - --- Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission. - -- So, if there is a target area detected and reported, - -- then any friendlies that are airborne near this target area, - -- will be commanded to (re-)engage that target when available (if no other tasks were commanded). - -- An ENGAGE task will be created for those pilots. - -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, - -- will be considered to receive the command to engage that target area. - -- You need to evaluate the value of this parameter carefully. - -- If too small, more intercept missions may be triggered upon detected target areas. - -- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. - -- @param #TASK_A2A_DISPATCHER self - -- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target. - -- @return #TASK_A2A_DISPATCHER - -- @usage - -- - -- -- Set 50km as the radius to engage any target by airborne friendlies. - -- TaskA2ADispatcher:SetEngageRadius( 50000 ) - -- - -- -- Set 100km as the radius to engage any target by airborne friendlies. - -- TaskA2ADispatcher:SetEngageRadius() -- 100000 is the default value. - -- - function TASK_A2A_DISPATCHER:SetEngageRadius( EngageRadius ) - - self.Detection:SetFriendliesRange( EngageRadius or 100000 ) - - return self - end - - --- Set flashing player messages on or off - -- @param #TASK_A2A_DISPATCHER self - -- @param #boolean onoff Set messages on (true) or off (false) - function TASK_A2A_DISPATCHER:SetSendMessages( onoff ) - self.FlashNewTask = onoff - end - - --- Creates an INTERCEPT task when there are targets for it. - -- @param #TASK_A2A_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem - -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. - -- @return #nil If there are no targets to be set. - function TASK_A2A_DISPATCHER:EvaluateINTERCEPT( DetectedItem ) - self:F( { DetectedItem.ItemID } ) - - local DetectedSet = DetectedItem.Set - local DetectedZone = DetectedItem.Zone - - -- Check if there is at least one UNIT in the DetectedSet is visible. - - if DetectedItem.IsDetected == true then - - -- Here we're doing something advanced... We're copying the DetectedSet. - local TargetSetUnit = SET_UNIT:New() - TargetSetUnit:SetDatabase( DetectedSet ) - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Creates an SWEEP task when there are targets for it. - -- @param #TASK_A2A_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem - -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. - -- @return #nil If there are no targets to be set. - function TASK_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem ) - self:F( { DetectedItem.ItemID } ) - - local DetectedSet = DetectedItem.Set - local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove? - - if DetectedItem.IsDetected == false then - - -- Here we're doing something advanced... We're copying the DetectedSet. - local TargetSetUnit = SET_UNIT:New() - TargetSetUnit:SetDatabase( DetectedSet ) - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Creates an ENGAGE task when there are human friendlies airborne near the targets. - -- @param #TASK_A2A_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem - -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. - -- @return #nil If there are no targets to be set. - function TASK_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem ) - self:F( { DetectedItem.ItemID } ) - - local DetectedSet = DetectedItem.Set - local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove? - - local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem ) - - -- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone. - if PlayersCount > 0 and DetectedItem.IsDetected == true then - - -- Here we're doing something advanced... We're copying the DetectedSet. - local TargetSetUnit = SET_UNIT:New() - TargetSetUnit:SetDatabase( DetectedSet ) - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Evaluates the removal of the Task from the Mission. - -- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned". - -- @param #TASK_A2A_DISPATCHER self - -- @param Tasking.Mission#MISSION Mission - -- @param Tasking.Task#TASK Task - -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. - -- @param #boolean DetectedItemID - -- @param #boolean DetectedItemChange - -- @return Tasking.Task#TASK - function TASK_A2A_DISPATCHER:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, DetectedItemIndex, DetectedItemChanged ) - - if Task then - - if Task:IsStatePlanned() then - local TaskName = Task:GetName() - local TaskType = TaskName:match( "(%u+)%.%d+" ) - - self:T2( { TaskType = TaskType } ) - - local Remove = false - - local IsPlayers = Detection:IsPlayersNearBy( DetectedItem ) - if TaskType == "ENGAGE" then - if IsPlayers == false then - Remove = true - end - end - - if TaskType == "INTERCEPT" then - if IsPlayers == true then - Remove = true - end - if DetectedItem.IsDetected == false then - Remove = true - end - end - - if TaskType == "SWEEP" then - if DetectedItem.IsDetected == true then - Remove = true - end - end - - local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT - -- DetectedSet:Flush( self ) - -- self:F( { DetectedSetCount = DetectedSet:Count() } ) - if DetectedSet:Count() == 0 then - Remove = true - end - - if DetectedItemChanged == true or Remove then - Task = self:RemoveTask( DetectedItemIndex ) - end - end - end - - return Task - end - - --- Calculates which friendlies are nearby the area - -- @param #TASK_A2A_DISPATCHER self - -- @param DetectedItem - -- @return #number, Tasking.CommandCenter#REPORT - function TASK_A2A_DISPATCHER:GetFriendliesNearBy( DetectedItem ) - - local DetectedSet = DetectedItem.Set - local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE ) - - local FriendlyTypes = {} - local FriendliesCount = 0 - - if FriendlyUnitsNearBy then - local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G() - for FriendlyUnitName, FriendlyUnitData in pairs( FriendlyUnitsNearBy ) do - local FriendlyUnit = FriendlyUnitData -- Wrapper.Unit#UNIT - if FriendlyUnit:IsAirPlane() then - local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel() - FriendliesCount = FriendliesCount + 1 - local FriendlyType = FriendlyUnit:GetTypeName() - FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and (FriendlyTypes[FriendlyType] + 1) or 1 - if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then - end - end - end - - end - - -- self:F( { FriendliesCount = FriendliesCount } ) - - local FriendlyTypesReport = REPORT:New() - - if FriendliesCount > 0 then - for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do - FriendlyTypesReport:Add( string.format( "%d of %s", FriendlyTypeCount, FriendlyType ) ) - end - else - FriendlyTypesReport:Add( "-" ) - end - - return FriendliesCount, FriendlyTypesReport - end - - --- Calculates which HUMAN friendlies are nearby the area - -- @param #TASK_A2A_DISPATCHER self - -- @param DetectedItem - -- @return #number, Tasking.CommandCenter#REPORT - function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem ) - - local DetectedSet = DetectedItem.Set - local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem ) - - local PlayerTypes = {} - local PlayersCount = 0 - - if PlayersNearBy then - local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G() - for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do - local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT - local PlayerName = PlayerUnit:GetPlayerName() - -- self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } ) - if PlayerUnit:IsAirPlane() and PlayerName ~= nil then - local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel() - PlayersCount = PlayersCount + 1 - local PlayerType = PlayerUnit:GetTypeName() - PlayerTypes[PlayerName] = PlayerType - if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then - end - end - end - - end - - local PlayerTypesReport = REPORT:New() - - if PlayersCount > 0 then - for PlayerName, PlayerType in pairs( PlayerTypes ) do - PlayerTypesReport:Add( string.format( '"%s" in %s', PlayerName, PlayerType ) ) - end - else - PlayerTypesReport:Add( "-" ) - end - - return PlayersCount, PlayerTypesReport - end - - function TASK_A2A_DISPATCHER:RemoveTask( TaskIndex ) - self.Mission:RemoveTask( self.Tasks[TaskIndex] ) - self.Tasks[TaskIndex] = nil - end - - --- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}. - -- @param #TASK_A2A_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. - -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. - function TASK_A2A_DISPATCHER:ProcessDetected( Detection ) - self:F() - - local AreaMsg = {} - local TaskMsg = {} - local ChangeMsg = {} - - local Mission = self.Mission - - if Mission:IsIDLE() or Mission:IsENGAGED() then - - local TaskReport = REPORT:New() - - -- Checking the task queue for the dispatcher, and removing any obsolete task! - for TaskIndex, TaskData in pairs( self.Tasks ) do - local Task = TaskData -- Tasking.Task#TASK - if Task:IsStatePlanned() then - local DetectedItem = Detection:GetDetectedItemByIndex( TaskIndex ) - if not DetectedItem then - local TaskText = Task:GetName() - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2A task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup ) - end - Task = self:RemoveTask( TaskIndex ) - end - end - end - - -- Now that all obsolete tasks are removed, loop through the detected targets. - for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do - - local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem - local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT - local DetectedCount = DetectedSet:Count() - local DetectedZone = DetectedItem.Zone - -- self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } ) - -- DetectedSet:Flush( self ) - - local DetectedID = DetectedItem.ID - local TaskIndex = DetectedItem.Index - local DetectedItemChanged = DetectedItem.Changed - - local Task = self.Tasks[TaskIndex] - Task = self:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, TaskIndex, DetectedItemChanged ) -- Task will be removed if it is planned and changed. - - -- Evaluate INTERCEPT - if not Task and DetectedCount > 0 then - local TargetSetUnit = self:EvaluateENGAGE( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed... - if TargetSetUnit then - Task = TASK_A2A_ENGAGE:New( Mission, self.SetGroup, string.format( "ENGAGE.%03d", DetectedID ), TargetSetUnit ) - Task:SetDetection( Detection, DetectedItem ) - Task:UpdateTaskInfo( DetectedItem ) - else - local TargetSetUnit = self:EvaluateINTERCEPT( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed... - if TargetSetUnit then - Task = TASK_A2A_INTERCEPT:New( Mission, self.SetGroup, string.format( "INTERCEPT.%03d", DetectedID ), TargetSetUnit ) - Task:SetDetection( Detection, DetectedItem ) - Task:UpdateTaskInfo( DetectedItem ) - else - local TargetSetUnit = self:EvaluateSWEEP( DetectedItem ) -- Returns a SetUnit - if TargetSetUnit then - Task = TASK_A2A_SWEEP:New( Mission, self.SetGroup, string.format( "SWEEP.%03d", DetectedID ), TargetSetUnit ) - Task:SetDetection( Detection, DetectedItem ) - Task:UpdateTaskInfo( DetectedItem ) - end - end - end - - if Task then - self.Tasks[TaskIndex] = Task - Task:SetTargetZone( DetectedZone, DetectedItem.Coordinate.y, DetectedItem.Coordinate.Heading ) - Task:SetDispatcher( self ) - Mission:AddTask( Task ) - - function Task.OnEnterSuccess( Task, From, Event, To ) - self:Success( Task ) - end - - function Task.OnEnterCancelled( Task, From, Event, To ) - self:Cancelled( Task ) - end - - function Task.OnEnterFailed( Task, From, Event, To ) - self:Failed( Task ) - end - - function Task.OnEnterAborted( Task, From, Event, To ) - self:Aborted( Task ) - end - - TaskReport:Add( Task:GetName() ) - else - self:F( "This should not happen" ) - end - - end - - if Task then - local FriendliesCount, FriendliesReport = self:GetFriendliesNearBy( DetectedItem, Unit.Category.AIRPLANE ) - Task.TaskInfo:AddText( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ), 40, "MOD" ) - local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem ) - Task.TaskInfo:AddText( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ), 40, "MOD" ) - end - - -- OK, so the tasking has been done, now delete the changes reported for the area. - Detection:AcceptChanges( DetectedItem ) - end - - -- TODO set menus using the HQ coordinator - Mission:GetCommandCenter():SetMenu() - - local TaskText = TaskReport:Text( ", " ) - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if (not Mission:IsGroupAssigned( TaskGroup )) and TaskText ~= "" and (self.FlashNewTask) then - Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup ) - end - end - - end - - return true - end - -end diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua deleted file mode 100644 index f39c4968c..000000000 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ /dev/null @@ -1,638 +0,0 @@ ---- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements. --- --- === --- --- ### Author: **FlightControl** --- --- ### Contributions: --- --- === --- --- @module Tasking.Task_A2G --- @image MOOSE.JPG - -do -- TASK_A2G - - --- The TASK_A2G class - -- @type TASK_A2G - -- @field Core.Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - - --- The TASK_A2G class defines Air To Ground tasks for a @{Core.Set} of Target Units, - -- based on the tasking capabilities defined in @{Tasking.Task#TASK}. - -- - -- ![Banner Image](..\Images\deprecated.png) - -- - -- 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. - -- - -- # Developer Note - -- - -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE - -- Therefore, this class is considered to be deprecated - -- - -- @field #TASK_A2G - TASK_A2G = { - ClassName = "TASK_A2G" - } - - --- Instantiates a new TASK_A2G. - -- @param #TASK_A2G 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 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_A2G self - 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: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: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_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 - -- @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 - 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_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 ) - end - - --- 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:__RouteToTargets( -10 ) - end - - --- 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 - local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT - if TargetUnit then - local Coordinate = TargetUnit:GetPointVec3() - self:T( { TargetCoordinate = Coordinate, Coordinate:GetX(), Coordinate:GetY(), Coordinate:GetZ() } ) - Task:SetTargetCoordinate( Coordinate, TaskUnit ) - end - self:__RouteToTargetPoint( 0.1 ) - end - end - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_A2G#TASK_A2G Task - function Fsm:onafterRouteToTargets( TaskUnit, Task ) - self:F( { 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_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() .. " )" - end - - -- @param #TASK_A2G 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_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 - ActRouteRendezVous:SetZone( RendezVousZone ) - end - - -- @param #TASK_A2G self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map. - function TASK_A2G: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_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 - -- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map. - function TASK_A2G: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_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 - -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. - function TASK_A2G:GetTargetZone( TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE - return ActRouteTarget:GetZone() - end - - function TASK_A2G:SetGoalTotal() - - self.GoalTotal = self.TargetSetUnit:CountAlive() - 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 ) - - 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:CountAlive() == 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() - self.TaskInfo:AddTaskName( 0, "MSOD" ) - self.TaskInfo:AddCoordinate( TargetCoordinate, 1, "SOD" ) - - local ThreatLevel, ThreatText - if DetectedItem then - ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( DetectedItem ) - else - ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G() - end - self.TaskInfo:AddThreat( ThreatText, ThreatLevel, 10, "MOD", true ) - - if self.Detection then - local DetectedItemsCount = self.TargetSetUnit:CountAlive() - local ReportTypes = REPORT:New() - local TargetTypes = {} - for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do - local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit ) - if not TargetTypes[TargetType] then - TargetTypes[TargetType] = TargetType - ReportTypes:Add( TargetType ) - end - end - self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", true ) - self.TaskInfo:AddTargets( DetectedItemsCount, ReportTypes:Text( ", " ), 20, "D", true ) - else - local DetectedItemsCount = self.TargetSetUnit:CountAlive() - local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames() - self.TaskInfo:AddTargetCount( DetectedItemsCount, 11, "O", 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 - 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 } ) - return math.floor( Distance ) - elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then - return 1 - end - - return 0 - end - -end - -do -- TASK_A2G_SEAD - - --- The TASK_A2G_SEAD class - -- @type TASK_A2G_SEAD - -- @field Core.Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - - --- Defines an Suppression or Extermination of Air Defenses task for a human player to be executed. - -- These tasks are important to be executed as they will help to achieve air superiority at the vicinity. - -- - -- The TASK_A2G_SEAD is used by the @{Tasking.Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks - -- based on detected enemy ground targets. - -- - -- @field #TASK_A2G_SEAD - TASK_A2G_SEAD = { - ClassName = "TASK_A2G_SEAD" - } - - --- Instantiates a new TASK_A2G_SEAD. - -- @param #TASK_A2G_SEAD 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_A2G_SEAD self - 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." ) - - return self - end - - --- Set a score when a target in scope of the A2G attack, has been destroyed . - -- @param #TASK_A2G_SEAD 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. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G_SEAD - function TASK_A2G_SEAD:SetScoreOnProgress( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has SEADed a target.", Score ) - - return self - end - - --- Set a score when all the targets in scope of the A2G attack, have been destroyed. - -- @param #TASK_A2G_SEAD self - -- @param #string PlayerName The name of the player. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G_SEAD - function TASK_A2G_SEAD:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Success", "All radar emitting targets have been successfully SEADed!", Score ) - - return self - end - - --- Set a penalty when the A2G attack has failed. - -- @param #TASK_A2G_SEAD self - -- @param #string PlayerName The name of the player. - -- @param #number Penalty The penalty in points, must be a negative value! - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G_SEAD - function TASK_A2G_SEAD:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) - self:F( { PlayerName, Penalty, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Failed", "The SEADing has failed!", Penalty ) - - return self - end - -end - -do -- TASK_A2G_BAI - - --- The TASK_A2G_BAI class - -- @type TASK_A2G_BAI - -- @field Core.Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - - --- Defines a Battlefield Air Interdiction task for a human player to be executed. - -- These tasks are more strategic in nature and are most of the time further away from friendly forces. - -- BAI tasks can also be used to express the abscence of friendly forces near the vicinity. - -- - -- The TASK_A2G_BAI is used by the @{Tasking.Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create BAI tasks - -- based on detected enemy ground targets. - -- - -- @field #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 - -- @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_A2G_BAI self - 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." ) - - return self - end - - --- Set a score when a target in scope of the A2G attack, has been destroyed . - -- @param #TASK_A2G_BAI 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. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G_BAI - function TASK_A2G_BAI:SetScoreOnProgress( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - 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 - - --- Set a score when all the targets in scope of the A2G attack, have been destroyed. - -- @param #TASK_A2G_BAI self - -- @param #string PlayerName The name of the player. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G_BAI - function TASK_A2G_BAI:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - 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 - - --- Set a penalty when the A2G attack has failed. - -- @param #TASK_A2G_BAI self - -- @param #string PlayerName The name of the player. - -- @param #number Penalty The penalty in points, must be a negative value! - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G_BAI - function TASK_A2G_BAI:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) - self:F( { PlayerName, Penalty, TaskUnit } ) - - 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 - -- @type TASK_A2G_CAS - -- @field Core.Set#SET_UNIT TargetSetUnit - -- @extends Tasking.Task#TASK - - --- Defines an Close Air Support task for a human player to be executed. - -- Friendly forces will be in the vicinity within 6km from the enemy. - -- - -- The TASK_A2G_CAS is used by the @{Tasking.Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create CAS tasks - -- based on detected enemy ground targets. - -- - -- @field #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 - -- @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_A2G_CAS self - 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! " ) ) - - return self - end - - --- Set a score when a target in scope of the A2G attack, has been destroyed . - -- @param #TASK_A2G_CAS 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. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G_CAS - function TASK_A2G_CAS:SetScoreOnProgress( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - 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 - - --- Set a score when all the targets in scope of the A2G attack, have been destroyed. - -- @param #TASK_A2G_CAS self - -- @param #string PlayerName The name of the player. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G_CAS - function TASK_A2G_CAS:SetScoreOnSuccess( PlayerName, Score, TaskUnit ) - self:F( { PlayerName, Score, TaskUnit } ) - - 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 - - --- Set a penalty when the A2G attack has failed. - -- @param #TASK_A2G_CAS self - -- @param #string PlayerName The name of the player. - -- @param #number Penalty The penalty in points, must be a negative value! - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_A2G_CAS - function TASK_A2G_CAS:SetScoreOnFail( PlayerName, Penalty, TaskUnit ) - self:F( { PlayerName, Penalty, TaskUnit } ) - - 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 deleted file mode 100644 index 9fc410de1..000000000 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ /dev/null @@ -1,830 +0,0 @@ ---- **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) 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. --- --- === --- --- ### Author: **FlightControl** --- --- ### Contributions: --- --- === --- --- @module Tasking.Task_A2G_Dispatcher --- @image Task_A2G_Dispatcher.JPG - -do -- TASK_A2G_DISPATCHER - - --- TASK\_A2G\_DISPATCHER class. - -- @type TASK_A2G_DISPATCHER - -- @field Core.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. - -- @field Tasking.Mission#MISSION Mission - -- @extends Tasking.DetectionManager#DETECTION_MANAGER - - --- Orchestrates dynamic **A2G Task Dispatching** based on the detection results of a linked @{Functional.Detection} object. - -- - -- ![Banner Image](..\Images\deprecated.png) - -- - -- 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 @{Core.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 @{Wrapper.Client}s of the @{Wrapper.Group} @{Core.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 @{Core.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 @{Tasking.CommandCenter}, which is orchestrating a @{Tasking.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 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. - -- - -- - -- ### 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 - -- F2. Mark Task Locations on Map - -- F3. Task Reports - -- F1. Tasks Summary - -- F2. Planned Tasks - -- F3. Assigned Tasks - -- 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 % 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 - -- F2. Mark Task Locations on Map - -- F3. Task Reports - -- 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 - -- F2. Mark Task Locations on Map - -- 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 - -- - -- 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 - -- F2. Mark Task Locations on Map - -- 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", - -- if a Mission "ALpha" contains 5 Planned Tasks, which would be: - -- - -- - 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 - -- F2. Mark Task Locations on Map - -- F3. Task Reports - -- F4. Mission Reports - -- F5. Join Planned Task - -- F2. BAI - -- F1. BAI.001 - -- F1. CAS - -- F1. CAS.002 - -- F3. SEAD - -- 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": - -- - -- Radio MENU Structure (F10. Other) - -- - -- F1. Command Center |Gori| - -- F1. Mission "Alpha" - -- F1. Mission Briefing - -- F2. Mark Task Locations on Map - -- F3. Task Reports - -- F4. Mission Reports - -- F5. Join Planned Task - -- F1. CAS - -- F1. CAS.001 - -- 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 - -- F2. Mark Task Locations on Map - -- F3. Task Reports - -- F4. Mission Reports - -- F5. Assigned Task - -- 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. - -- - -- ## 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 @{Core.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 @{Tasking.CommandCenter} object. The master communication channel. - -- - A @{Tasking.Mission} object. Each task belongs to a Mission. - -- - A @{Functional.Detection} object. There are several detection grouping methods to choose from. - -- - A @{Tasking.Task_A2G_Dispatcher} object. The master A2G task dispatcher. - -- - A @{Core.Set} of @{Wrapper.Group} objects that will detect the enemy, the RecceSet. This is attached to the @{Functional.Detection} object. - -- - A @{Core.Set} of @{Wrapper.Group} objects that will attack the enemy, the AttackSet. This is attached to the @{Tasking.Task_A2G_Dispatcher} object. - -- - -- 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. - -- 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 receive the dispatched Tasks. The DetectionAreas will report any detected enemies to the TaskDispatcher. - -- - -- - -- # Developer Note - -- - -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE - -- Therefore, this class is considered to be deprecated - -- - -- - -- @field #TASK_A2G_DISPATCHER - TASK_A2G_DISPATCHER = { - ClassName = "TASK_A2G_DISPATCHER", - Mission = nil, - Detection = nil, - Tasks = {} - } - - --- TASK_A2G_DISPATCHER constructor. - -- @param #TASK_A2G_DISPATCHER self - -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. - -- @param Core.Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. - -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. - -- @return #TASK_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.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 - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @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 - -- @param #TASK_A2G_DISPATCHER self - -- @param #boolean onoff Set messages on (true) or off (false) - function TASK_A2G_DISPATCHER:SetSendMessages( 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 - -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. - -- @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 - - -- Determine if the set has radar targets. If it does, construct a SEAD task. - local RadarCount = DetectedSet:HasSEAD() - - if RadarCount > 0 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:FilterHasSEAD() - TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection. - - return TargetSetUnit - end - - return nil - end - - --- Creates a CAS task when there are targets for it. - -- @param #TASK_A2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem - -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. - -- @return #nil If there are no targets to be set. - function TASK_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. - local GroundUnitCount = DetectedSet:HasGroundUnits() - local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem, Unit.Category.GROUND_UNIT ) -- Are there friendlies nearby of type GROUND_UNIT? - local RadarCount = DetectedSet:HasSEAD() - - if RadarCount == 0 and GroundUnitCount > 0 and FriendliesNearBy == true then - - -- Copy the Set - 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 - -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. - -- @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. - local GroundUnitCount = DetectedSet:HasGroundUnits() - local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem, Unit.Category.GROUND_UNIT ) -- Are there friendlies nearby of type GROUND_UNIT? - local RadarCount = DetectedSet:HasSEAD() - - if RadarCount == 0 and GroundUnitCount > 0 and FriendliesNearBy == false then - - -- Copy the Set - 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 - -- @param Tasking.Mission#MISSION Mission - -- @param Tasking.Task#TASK Task - -- @param #boolean DetectedItemID - -- @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() ) - 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 - -- @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_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! - for TaskIndex, TaskData in pairs( self.Tasks ) do - local Task = TaskData -- Tasking.Task#TASK - if Task:IsStatePlanned() then - local DetectedItem = Detection:GetDetectedItemByIndex( TaskIndex ) - if not DetectedItem then - local TaskText = Task:GetName() - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if self.FlashNewTask then - Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup ) - end - end - Task = self:RemoveTask( TaskIndex ) - end - end - end - - --- 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 ) - - 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 - if DetectedItemChanged == true then -- The detection has changed, thus a new TargetSet is to be evaluated and set - local TargetsReport = REPORT:New() - local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - if Task:IsInstanceOf( TASK_A2G_SEAD ) then - Task:SetTargetSetUnit( TargetSetUnit ) - Task:SetDetection( Detection, DetectedItem ) - Task:UpdateTaskInfo( DetectedItem ) - TargetsReport:Add( Detection:GetChangeText( DetectedItem ) ) - else - Task:Cancel() - end - else - local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed... - if TargetSetUnit then - if Task:IsInstanceOf( TASK_A2G_CAS ) then - Task:SetTargetSetUnit( TargetSetUnit ) - Task:SetDetection( Detection, DetectedItem ) - Task:UpdateTaskInfo( DetectedItem ) - TargetsReport:Add( Detection:GetChangeText( DetectedItem ) ) - else - Task:Cancel() - Task = self:RemoveTask( TaskIndex ) - end - else - local TargetSetUnit = self:EvaluateBAI( DetectedItem ) -- Returns a SetUnit if there are targets to be BAIed... - if TargetSetUnit then - if Task:IsInstanceOf( TASK_A2G_BAI ) then - Task:SetTargetSetUnit( TargetSetUnit ) - Task:SetDetection( Detection, DetectedItem ) - Task:UpdateTaskInfo( DetectedItem ) - TargetsReport:Add( Detection:GetChangeText( DetectedItem ) ) - else - Task:Cancel() - Task = self:RemoveTask( TaskIndex ) - end - 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 - 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 - if Task:IsInstanceOf( TASK_A2G_SEAD ) then - local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed... - if TargetSetUnit then - Task:SetTargetSetUnit( TargetSetUnit ) - Task:SetDetection( Detection, DetectedItem ) - Task:UpdateTaskInfo( DetectedItem ) - else - Task:Cancel() - Task = self:RemoveTask( TaskIndex ) - end - else - if Task:IsInstanceOf( TASK_A2G_CAS ) then - local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed... - if TargetSetUnit then - Task:SetTargetSetUnit( TargetSetUnit ) - Task:SetDetection( Detection, DetectedItem ) - Task:UpdateTaskInfo( DetectedItem ) - else - Task:Cancel() - Task = self:RemoveTask( TaskIndex ) - end - else - if Task:IsInstanceOf( TASK_A2G_BAI ) then - local TargetSetUnit = self:EvaluateBAI( DetectedItem ) -- Returns a SetUnit if there are targets to be BAIed... - if TargetSetUnit then - Task:SetTargetSetUnit( TargetSetUnit ) - Task:SetDetection( Detection, DetectedItem ) - Task:UpdateTaskInfo( DetectedItem ) - else - Task:Cancel() - Task = self:RemoveTask( TaskIndex ) - end - else - Task:Cancel() - Task = self:RemoveTask( TaskIndex ) - end - end - end - end - end - end - - -- Evaluate SEAD - if not Task then - 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 - Task:SetDetection( Detection, DetectedItem ) - end - - -- Evaluate CAS - if not Task then - 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 - Task:SetDetection( Detection, DetectedItem ) - end - - -- Evaluate BAI - if not Task then - 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 - 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 - - function Task.OnEnterCancelled( Task, From, Event, To ) - self:Cancelled( Task ) - end - - function Task.OnEnterFailed( Task, From, Event, To ) - self:Failed( Task ) - end - - function Task.OnEnterAborted( Task, From, Event, To ) - self:Aborted( Task ) - end - - TaskReport:Add( Task:GetName() ) - else - self:F( "This should not happen" ) - end - end - - -- OK, so the tasking has been done, now delete the changes reported for the area. - Detection:AcceptChanges( DetectedItem ) - end - - -- TODO set menus using the HQ coordinator - Mission:GetCommandCenter():SetMenu() - - local TaskText = TaskReport:Text( ", " ) - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if (not Mission:IsGroupAssigned( TaskGroup )) and TaskText ~= "" and self.FlashNewTask then - Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup ) - end - end - - end - - return true - end - -end diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua deleted file mode 100644 index 6bfcfffa1..000000000 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ /dev/null @@ -1,1412 +0,0 @@ ---- **Tasking** - Base class to model tasks for players to transport cargo. --- --- ## Features: --- --- * TASK_CARGO is the **base class** for: --- --- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} --- * @{Tasking.Task_Cargo_CSAR#TASK_CARGO_CSAR} --- --- --- === --- --- ![Banner Image](..\Images\deprecated.png) --- --- ## Test Missions: --- --- Test missions can be located on the main GITHUB site. --- --- [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/Tasking/Task_Cargo_Dispatcher) --- --- === --- --- ## Tasking system. --- --- #### If you are not yet aware what the MOOSE tasking system is about, read FIRST the explanation on the @{Tasking.Task} module. --- --- === --- --- ## Context of cargo tasking. --- --- The Moose framework provides various CARGO classes that allow DCS physical or logical objects to be transported or sling loaded by Carriers. --- The CARGO_ classes, as part of the MOOSE core, are able to Board, Load, UnBoard and UnLoad cargo between Carrier units. --- --- The TASK_CARGO class is not meant to use within your missions as a mission designer. It is a base class, and other classes are derived from it. --- --- The following TASK_CARGO_ classes are important, as they implement the CONCRETE tasks: --- --- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT}: Defines a task for a human player to transport a set of cargo between various zones. --- * @{Tasking.Task_Cargo_CSAR#TASK_CARGO_CSAR}: Defines a task for a human player to Search and Rescue wounded pilots. --- --- However! The menu system and basic usage of the TASK_CARGO classes is explained in the @{#TASK_CARGO} class description. --- So please browse further below to understand how to use it from a player perspective! --- --- === --- --- ## Cargo tasking from a player perspective. --- --- A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J). --- The player needs to accept the task from the task overview list within the mission, using the menus. --- --- Once the task is assigned to the player and accepted by the player, the player will obtain --- an extra **Cargo (Radio) Menu** that contains the CARGO objects that need to be transported. --- --- Each @{Tasking.Task_CARGO#TASK_CARGO} object has a certain state: --- --- * **UnLoaded**: The cargo is located within the battlefield. It may still need to be transported. --- * **Loaded**: The cargo is loaded within a Carrier. This can be your air unit, or another air unit, or even a vehicle. --- * **Boarding**: The cargo is running or moving towards your Carrier for loading. --- * **UnBoarding**: The cargo is driving or jumping out of your Carrier and moves to a location in the Deployment Zone. --- --- Cargo must be transported towards different Deployment @{Core.Zone}s. --- --- The Cargo Menu system allows to execute **various actions** to transport the cargo. --- In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed. --- Depending on the location of your Carrier unit, the menu options will vary. --- --- ### Joining a Cargo Transport Task --- --- Once you've joined a task, using the **Join Planned Task Menu**, --- you can Pickup cargo from a pickup location and Deploy cargo in deployment zones, using the **Task Action Menu**. --- --- ### Task Action Menu. --- --- When a player has joined a **`CARGO`** task (type), for that player only, --- it's **Task Action Menu** will show an additional menu options. --- --- From within this menu, you will be able to route to a cargo location, deploy zone, and load/unload cargo. --- --- ### Pickup cargo by Boarding, Loading and Sling Loading. --- --- There are three different ways how cargo can be picked up: --- --- - **Boarding**: Moveable cargo (like infantry or vehicles), can be boarded, that means, the cargo will move towards your carrier to board. --- However, it can only execute the boarding actions if it is within the foreseen **Reporting Range**. --- Therefore, it is important that you steer your Carrier within the Reporting Range around the cargo, --- so that boarding actions can be executed on the cargo. The reporting range is set by the mission designer. --- Fortunately, the cargo is reporting to you when it is within reporting range. --- --- - **Loading**: Stationary cargo (like crates), which are heavy, can only be loaded or sling loaded, meaning, --- your carrier must be close enough to the cargo to be able to load the cargo within the carrier bays. --- Moose provides you with an additional menu system to load stationary cargo into your carrier bays using the menu. --- These menu options will become available, when the carrier is within loading range. --- The Moose cargo will report to the carrier when the range is close enough. The load range is set by the mission designer. --- --- - **Sling Loading**: Stationary cargo (like crates), which are heavy, can only be loaded or sling loaded, meaning, --- your carrier must be close enough to the cargo to be able to load the cargo within the carrier bays. --- Sling loading cargo is done using the default DCS menu system. However, Moose cargo will report to the carrier that --- it is within sling loading range. --- --- In order to be able to pickup cargo, you'll need to know where the cargo is located, right? --- --- Fortunately, if your Carrier is not within the reporting range of the cargo, --- **the HQ can help to route you to the locations of cargo**. --- --- ![Task_Types](../Tasking/Task_Cargo_Main_Menu.JPG) --- --- Use the task action menu to receive HQ help for this. --- --- ![Task_Types](../Tasking/Task_Cargo_Action_Menu.JPG) --- --- Depending on the location within the battlefield, the task action menu will contain **Route options** that can be selected --- to start the HQ sending you routing messages. --- The **route options will vary**, depending on the position of your carrier, and the location of the cargo and the deploy zones. --- Note that the route options will **only be created** for cargo that is **in scope of your cargo transportation task**, --- so there may be other cargo objects within the DCS simulation, but if those belong to other cargo transportations tasks, --- then no routing options will be shown for these cargo. --- This is done to ensure that **different teams** have a **defined scope** for defined cargo, and that **multiple teams** can join --- **multiple tasks**, transporting cargo **simultaneously** in a **cooperation**. --- --- In this example, there is a menu option to **Route to pickup cargo...**. --- Use this menu to route towards cargo locations for pickup into your carrier. --- --- ![Task_Types](../Tasking/Task_Cargo_Types_Menu.JPG) --- --- When you select this menu, you'll see a new menu listing the different cargo types that are out there in the dcs simulator. --- These cargo types are symbolic names that are assigned by the mission designer, like oil, liquid, engineers, food, workers etc. --- MOOSE has introduced this concept to allow mission designers to make different cargo types for different purposes. --- Only the creativity of the mission designer limits now the things that can be done with cargo ... --- Okay, let's continue ..., and let's select Oil ... --- --- When selected, the HQ will send you routing messages. --- --- ![Task_Types](../Tasking/Task_Cargo_Routing_BR.JPG) --- --- An example of routing in BR mode. --- --- Note that the coordinate display format in the message can be switched between LL DMS, LL DDM, MGRS and BR. --- --- ![Task_Types](../Tasking/Main_Settings.JPG) --- --- Use the @{Core.Settings} menu to change your display format preferences. --- --- ![Task_Types](../Tasking/Settings_A2G_Coordinate.JPG) --- --- There you can change the display format to another format that suits your need. --- Because cargo transportation is Air 2 Ground oriented, you need to select the A2G coordinate format display options. --- Note that the main settings menu contains much more --- options to control your display formats, like switch to metric and imperial, or change the duration of the display messages. --- --- ![Task_Types](../Tasking/Task_Cargo_Routing_LL.JPG) --- --- Here I changed the routing display format to LL DMS. --- --- One important thing to know, is that the routing messages will flash at regular time intervals. --- When using BR coordinate display format, the **distance and angle will change accordingly** from your carrier position and the location of the cargo. --- --- Another important note is the routing towards deploy zones. --- These routing options will only be shown, when your carrier bays have cargo loaded. --- So, only when there is something to be deployed from your carrier, the deploy options will be shown. --- --- #### Pickup Cargo. --- --- In order to pickup cargo, use the **task action menu** to **route to a specific cargo**. --- When a cargo route is selected, the HQ will send you routing messages indicating the location of the cargo. --- --- Upon arrival at the cargo, and when the cargo is within **reporting range**, the cargo will contact you and **further instructions will be given**. --- --- - When your Carrier is airborne, you will receive instructions to land your Carrier. --- The action will not be completed until you've landed your Carrier. --- --- - For ground carriers, you can just drive to the optimal cargo board or load position. --- --- It takes a bit of skill to land a helicopter near a cargo to be loaded, but that is part of the game, isn't it? --- Expecially when you are landing in a "hot" zone, so when cargo is under immediate threat of fire. --- --- #### Board Cargo (infantry). --- --- ![](../Tasking/Boarding_Ready.png) --- --- If your Carrier is within the **Reporting Range of the cargo**, and the cargo is **moveable**, the **cargo can be boarded**! --- This type of cargo will be most of the time be infantry. --- --- ![](../Tasking/Boarding_Menu.png) --- --- A **Board cargo...** sub menu has appeared, because your carrier is in boarding range of the cargo (infantry). --- Select the **Board cargo...** menu. --- --- ![](../Tasking/Boarding_Menu_Engineers.png) --- --- Any cargo that can be boarded (thus movable cargo), within boarding range of the carrier, will be listed here! --- In this example, the cargo **Engineers** can be boarded, by selecting the menu option. --- --- ![](../Tasking/Boarding_Started.png) --- --- After the menu option to board the cargo has been selected, the boarding process is started. --- A message from the cargo is communicated to the pilot, that boarding is started. --- --- ![](../Tasking/Boarding_Ongoing.png) --- --- **The pilot must wait at the exact position until all cargo has been boarded!** --- --- The moveable cargo will run in formation to your carrier, and will board one by one, depending on the near range set by the mission designer. --- The near range as added because carriers can be large or small, depending on the object size of the carrier. --- --- ![](../Tasking/Boarding_In_Progress.png) --- --- ![](../Tasking/Boarding_Almost_Done.png) --- --- Note that multiple units may need to board your Carrier, so it is required to await the full boarding process. --- --- ![](../Tasking/Boarding_Done.png) --- --- Once the cargo is fully boarded within your Carrier, you will be notified of this. --- --- **Remarks:** --- --- * For airborne Carriers, it is required to land first before the Boarding process can be initiated. --- If during boarding the Carrier gets airborne, the boarding process will be cancelled. --- * The carrier must remain stationary when the boarding sequence has started until further notified. --- --- #### Load Cargo. --- --- Cargo can be loaded into vehicles or helicopters or airplanes, as long as the carrier is sufficiently near to the cargo object. --- --- ![](../Tasking/Loading_Ready.png) --- --- If your Carrier is within the **Loading Range of the cargo**, thus, sufficiently near to the cargo, and the cargo is **stationary**, the **cargo can be loaded**, but not boarded! --- --- ![](../Tasking/Loading_Menu.png) --- --- Select the task action menu and now a **Load cargo...** sub menu will be listed. --- Select the **Load cargo...** sub menu, and a further detailed menu will be shown. --- --- ![](../Tasking/Loading_Menu_Crate.png) --- --- For each non-moveable cargo object (crates etc), **within loading range of the carrier**, the cargo will be listed and can be loaded into the carrier! --- --- ![](../Tasking/Loading_Cargo_Loaded.png) --- --- Once the cargo is loaded within your Carrier, you will be notified of this. --- --- **Remarks:** --- --- * For airborne Carriers, it is required to **land first right near the cargo**, before the loading process can be initiated. --- As stated, this requires some pilot skills :-) --- --- #### Sling Load Cargo (helicopters only). --- --- If your Carrier is within the **Loading Range of the cargo**, and the cargo is **stationary**, the **cargo can also be sling loaded**! --- Note that this is only possible for helicopters. --- --- To sling load cargo, there is no task action menu required. Just follow the normal sling loading procedure and the cargo will report. --- Use the normal DCS sling loading menu system to hook the cargo you the cable attached on your helicopter. --- --- Again note that you may land firstly right next to the cargo, before the loading process can be initiated. --- As stated, this requires some pilot skills :-) --- --- --- ### Deploy cargo by Unboarding, Unloading and Sling Deploying. --- --- #### **Deploying the relevant cargo within deploy zones, will make you achieve cargo transportation tasks!!!** --- --- There are two different ways how cargo can be deployed: --- --- - **Unboarding**: Moveable cargo (like infantry or vehicles), can be unboarded, that means, --- the cargo will step out of the carrier and will run to a group location. --- Moose provides you with an additional menu system to unload stationary cargo from the carrier bays, --- using the menu. These menu options will become available, when the carrier is within the deploy zone. --- --- - **Unloading**: Stationary cargo (like crates), which are heavy, can only be unloaded or sling loaded. --- Moose provides you with an additional menu system to unload stationary cargo from the carrier bays, --- using the menu. These menu options will become available, when the carrier is within the deploy zone. --- --- - **Sling Deploying**: Stationary cargo (like crates), which are heavy, can also be sling deployed. --- Once the cargo is within the deploy zone, the cargo can be deployed from the sling onto the ground. --- --- In order to be able to deploy cargo, you'll need to know where the deploy zone is located, right? --- Fortunately, the HQ can help to route you to the locations of deploy zone. --- Use the task action menu to receive HQ help for this. --- --- ![](../Tasking/Routing_Deploy_Zone_Menu.png) --- --- Depending on the location within the battlefield, the task action menu will contain **Route options** that can be selected --- to start the HQ sending you routing messages. Also, if the carrier cargo bays contain cargo, --- then beside **Route options** there will also be **Deploy options** listed. --- These **Deploy options** are meant to route you to the deploy zone locations. --- --- ![](../Tasking/Routing_Deploy_Zone_Menu_Workplace.png) --- --- Depending on the task that you have selected, the deploy zones will be listed. --- **There may be multiple deploy zones within the mission, but only the deploy zones relevant for your task will be available in the menu!** --- --- ![](../Tasking/Routing_Deploy_Zone_Message.png) --- --- When a routing option is selected, you are sent routing messages in a selected coordinate format. --- Possible routing coordinate formats are: Bearing Range (BR), Lattitude Longitude (LL) or Military Grid System (MGRS). --- Note that for LL, there are two sub formats. (See pickup). --- --- ![](../Tasking/Routing_Deploy_Zone_Arrived.png) --- --- When you are within the range of the deploy zone (can be also a polygon!), a message is communicated by HQ that you have arrived within the zone! --- --- The routing messages are formulated in the coordinate format that is currently active as configured in your settings profile. --- Use the **Settings Menu** to select the coordinate format that you would like to use for location determination. --- --- #### Unboard Cargo. --- --- If your carrier contains cargo, and the cargo is **moveable**, the **cargo can be unboarded**! --- You can only unload cargo if there is cargo within your cargo bays within the carrier. --- --- ![](../Tasking/Unboarding_Menu.png) --- --- Select the task action menu and now an **Unboard cargo...** sub menu will be listed! --- Again, this option will only be listed if there is a non moveable cargo within your cargo bays. --- --- ![](../Tasking/Unboarding_Menu_Engineers.png) --- --- Now you will see a menu option to unload the non-moveable cargo. --- In this example, you can unload the **Engineers** that was loaded within your carrier cargo bays. --- Depending on the cargo loaded within your cargo bays, you will see other options here! --- Select the relevant menu option from the cargo unload menu, and the cargo will unloaded from your carrier. --- --- ![](../Tasking/Unboarding_Started.png) --- --- **The cargo will step out of your carrier and will move towards a grouping point.** --- When the unboarding process has started, you will be notified by a message to your carrier. --- --- ![](../Tasking/Unboarding_In_Progress.png) --- --- The moveable cargo will unboard one by one, so note that multiple units may need to unboard your Carrier, --- so it is required to await the full completion of the unboarding process. --- --- ![](../Tasking/Unboarding_Done.png) --- --- Once the cargo is fully unboarded from your carrier, you will be notified of this. --- --- **Remarks:** --- --- * For airborne carriers, it is required to land first before the unboarding process can be initiated. --- If during unboarding the Carrier gets airborne, the unboarding process will be cancelled. --- * Once the moveable cargo is unboarded, they will start moving towards a specified gathering point. --- * The moveable cargo will send a message to your carrier with unboarding status updates. --- --- **Deploying a cargo within a deployment zone, may complete a deployment task! So ensure that you deploy the right cargo at the right deployment zone!** --- --- #### Unload Cargo. --- --- If your carrier contains cargo, and the cargo is **stationary**, the **cargo can be unloaded**, but not unboarded! --- You can only unload cargo if there is cargo within your cargo bays within the carrier. --- --- ![](../Tasking/Unloading_Menu.png) --- --- Select the task action menu and now an **Unload cargo...** sub menu will be listed! --- Again, this option will only be listed if there is a non moveable cargo within your cargo bays. --- --- ![](../Tasking/Unloading_Menu_Crate.png) --- --- Now you will see a menu option to unload the non-moveable cargo. --- In this example, you can unload the **Crate** that was loaded within your carrier cargo bays. --- Depending on the cargo loaded within your cargo bays, you will see other options here! --- Select the relevant menu option from the cargo unload menu, and the cargo will unloaded from your carrier. --- --- ![](../Tasking/Unloading_Done.png) --- --- Once the cargo is unloaded fom your Carrier, you may be notified of this, when there is a truck near to the cargo. --- If there is no truck near to the unload area, no message will be sent to your carrier! --- --- **Remarks:** --- --- * For airborne Carriers, it is required to land first, before the unloading process can be initiated. --- * A truck must be near the unload area to get messages to your carrier of the unload event! --- * Unloading is only for non-moveable cargo. --- * The non-moveable cargo must be within your cargo bays, or no unload option will be available. --- --- **Deploying a cargo within a deployment zone, may complete a deployment task! So ensure that you deploy the right cargo at the right deployment zone!** --- --- --- #### Sling Deploy Cargo (helicopters only). --- --- If your Carrier is within the **deploy zone**, and the cargo is **stationary**, the **cargo can also be sling deploying**! --- Note that this is only possible for helicopters. --- --- To sling deploy cargo, there is no task action menu required. Just follow the normal sling deploying procedure. --- --- **Deploying a cargo within a deployment zone, may complete a deployment task! So ensure that you deploy the right cargo at the right deployment zone!** --- --- ## Cargo tasking from a mission designer perspective. --- --- Please consult the documentation how to implement the derived classes of SET_CARGO in: --- --- - @{Tasking.Task_CARGO#TASK_CARGO}: Documents the main methods how to handle the cargo tasking from a mission designer perspective. --- - @{Tasking.Task_CARGO#TASK_CARGO_TRANSPORT}: Documents the specific methods how to handle the cargo transportation tasking from a mission designer perspective. --- - @{Tasking.Task_CARGO#TASK_CARGO_CSAR}: Documents the specific methods how to handle the cargo CSAR tasking from a mission designer perspective. --- --- --- # Developer Note --- --- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE --- Therefore, this class is considered to be deprecated --- --- === --- --- ### Author: **FlightControl** --- --- ### Contributions: --- --- === --- --- @module Tasking.Task_CARGO --- @image MOOSE.JPG - -do -- TASK_CARGO - - -- @type TASK_CARGO - -- @extends Tasking.Task#TASK - - --- Model tasks for players to transport Cargo. - -- - -- This models the process of a flexible transporation tasking system of cargo. - -- - -- # 1) A flexible tasking system. - -- - -- The TASK_CARGO classes provide you with a flexible tasking sytem, - -- that allows you to transport cargo of various types between various locations - -- and various dedicated deployment zones. - -- - -- The cargo in scope of the TASK\_CARGO classes must be explicitly given, and is of type SET\_CARGO. - -- The SET_CARGO contains a collection of CARGO objects that must be handled by the players in the mission. - -- - -- # 2) Cargo Tasking from a mission designer perspective. - -- - -- A cargo task is governed by a @{Tasking.Mission} object. Tasks are of different types. - -- The @{#TASK} object is used or derived by more detailed tasking classes that will implement the task execution mechanisms - -- and goals. - -- - -- ## 2.1) Derived cargo task classes. - -- - -- The following TASK_CARGO classes are derived from @{#TASK}. - -- - -- TASK - -- TASK_CARGO - -- TASK_CARGO_TRANSPORT - -- TASK_CARGO_CSAR - -- - -- ### 2.1.1) Cargo Tasks - -- - -- - @{Tasking.Task_CARGO#TASK_CARGO_TRANSPORT} - Models the transportation of cargo to deployment zones. - -- - @{Tasking.Task_CARGO#TASK_CARGO_CSAR} - Models the rescue of downed friendly pilots from behind enemy lines. - -- - -- ## 2.2) Handle TASK_CARGO Events ... - -- - -- The TASK_CARGO classes define Cargo transport tasks, - -- based on the tasking capabilities defined in @{Tasking.Task#TASK}. - -- - -- ### 2.2.1) Boarding events. - -- - -- Specific Cargo event can be captured, that allow to trigger specific actions! - -- - -- * **Boarded**: Triggered when the Cargo has been Boarded into your Carrier. - -- * **UnBoarded**: Triggered when the cargo has been Unboarded from your Carrier and has arrived at the Deployment Zone. - -- - -- ### 2.2.2) Loading events. - -- - -- Specific Cargo event can be captured, that allow to trigger specific actions! - -- - -- * **Loaded**: Triggered when the Cargo has been Loaded into your Carrier. - -- * **UnLoaded**: Triggered when the cargo has been Unloaded from your Carrier and has arrived at the Deployment Zone. - -- - -- ### 2.2.2) Standard TASK_CARGO Events - -- - -- The TASK_CARGO is implemented using a @{Core.Fsm#FSM_TASK}, and has the following standard statuses: - -- - -- * **None**: Start of the process. - -- * **Planned**: The cargo task is planned. - -- * **Assigned**: The cargo task is assigned to a @{Wrapper.Group#GROUP}. - -- * **Success**: The cargo task is successfully completed. - -- * **Failed**: The cargo task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. - -- - -- - -- - -- === - -- - -- @field #TASK_CARGO - TASK_CARGO = { - ClassName = "TASK_CARGO", - } - - --- Instantiates a new TASK_CARGO. - -- @param #TASK_CARGO 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_CARGO SetCargo The scope of the cargo to be transported. - -- @param #string TaskType The type of Cargo task. - -- @param #string TaskBriefing The Cargo Task briefing. - -- @return #TASK_CARGO self - function TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, TaskType, TaskBriefing ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- #TASK_CARGO - self:F( {Mission, SetGroup, TaskName, SetCargo, TaskType}) - - self.SetCargo = SetCargo - self.TaskType = TaskType - self.SmokeColor = SMOKECOLOR.Red - - self.CargoItemCount = {} -- Map of Carriers having a cargo item count to check the cargo loading limits. - self.CargoLimit = 10 - - self.DeployZones = {} -- setmetatable( {}, { __mode = "v" } ) -- weak table on value - - self:AddTransition( "*", "CargoDeployed", "*" ) - - --- CargoDeployed Handler OnBefore for TASK_CARGO - -- @function [parent=#TASK_CARGO] OnBeforeCargoDeployed - -- @param #TASK_CARGO self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Cargo.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - -- @return #boolean - - --- CargoDeployed Handler OnAfter for TASK_CARGO - -- @function [parent=#TASK_CARGO] OnAfterCargoDeployed - -- @param #TASK_CARGO self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Cargo.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - -- @usage - -- - -- -- Add a Transport task to transport cargo of different types to a Transport Deployment Zone. - -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, TransportGroups ) - -- - -- local CargoSetWorkmaterials = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() - -- local EngineerCargoGroup = CARGO_GROUP:New( GROUP:FindByName( "Engineers" ), "Workmaterials", "Engineers", 250 ) - -- local ConcreteCargo = CARGO_SLINGLOAD:New( STATIC:FindByName( "Concrete" ), "Workmaterials", "Concrete", 150, 50 ) - -- local CrateCargo = CARGO_CRATE:New( STATIC:FindByName( "Crate" ), "Workmaterials", "Crate", 150, 50 ) - -- local EnginesCargo = CARGO_CRATE:New( STATIC:FindByName( "Engines" ), "Workmaterials", "Engines", 150, 50 ) - -- local MetalCargo = CARGO_CRATE:New( STATIC:FindByName( "Metal" ), "Workmaterials", "Metal", 150, 50 ) - -- - -- -- Here we add the task. We name the task "Build a Workplace". - -- -- We provide the CargoSetWorkmaterials, and a briefing as the 2nd and 3rd parameter. - -- -- The :AddTransportTask() returns a Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT object, which we keep as a reference for further actions. - -- -- The WorkplaceTask holds the created and returned Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT object. - -- local WorkplaceTask = TaskDispatcher:AddTransportTask( "Build a Workplace", CargoSetWorkmaterials, "Transport the workers, engineers and the equipment near the Workplace." ) - -- - -- -- Here we set a TransportDeployZone. We use the WorkplaceTask as the reference, and provide a ZONE object. - -- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) ) - -- - -- Helos = { SPAWN:New( "Helicopters 1" ), SPAWN:New( "Helicopters 2" ), SPAWN:New( "Helicopters 3" ), SPAWN:New( "Helicopters 4" ), SPAWN:New( "Helicopters 5" ) } - -- EnemyHelos = { SPAWN:New( "Enemy Helicopters 1" ), SPAWN:New( "Enemy Helicopters 2" ), SPAWN:New( "Enemy Helicopters 3" ) } - -- - -- -- This is our worker method! So when a cargo is deployed within a deployment zone, this method will be called. - -- -- By example we are spawning here a random friendly helicopter and a random enemy helicopter. - -- function WorkplaceTask:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone ) - -- Helos[ math.random(1,#Helos) ]:Spawn() - -- EnemyHelos[ math.random(1,#EnemyHelos) ]:Spawn() - -- end - - self:AddTransition( "*", "CargoPickedUp", "*" ) - - --- CargoPickedUp Handler OnBefore for TASK_CARGO - -- @function [parent=#TASK_CARGO] OnBeforeCargoPickedUp - -- @param #TASK_CARGO self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Cargo.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @return #boolean - - --- CargoPickedUp Handler OnAfter for TASK_CARGO - -- @function [parent=#TASK_CARGO] OnAfterCargoPickedUp - -- @param #TASK_CARGO self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Cargo.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - - - local Fsm = self:GetUnitProcess() - --- Fsm:SetStartState( "Planned" ) --- --- Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } ) - - Fsm:AddTransition( { "Planned", "Assigned", "Cancelled", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded", "Loaded", "UnLoaded", "Landed", "Boarding" }, "SelectAction", "*" ) - - Fsm:AddTransition( "*", "RouteToPickup", "RoutingToPickup" ) - Fsm:AddProcess ( "RoutingToPickup", "RouteToPickupPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtPickup", Cancelled = "CancelRouteToPickup" } ) - Fsm:AddTransition( "Arrived", "ArriveAtPickup", "ArrivedAtPickup" ) - Fsm:AddTransition( "Cancelled", "CancelRouteToPickup", "Cancelled" ) - - Fsm:AddTransition( "*", "RouteToDeploy", "RoutingToDeploy" ) - Fsm:AddProcess ( "RoutingToDeploy", "RouteToDeployZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtDeploy", Cancelled = "CancelRouteToDeploy" } ) - Fsm:AddTransition( "Arrived", "ArriveAtDeploy", "ArrivedAtDeploy" ) - Fsm:AddTransition( "Cancelled", "CancelRouteToDeploy", "Cancelled" ) - - Fsm:AddTransition( { "ArrivedAtPickup", "ArrivedAtDeploy", "Landing" }, "Land", "Landing" ) - Fsm:AddTransition( "Landing", "Landed", "Landed" ) - - Fsm:AddTransition( "*", "PrepareBoarding", "AwaitBoarding" ) - Fsm:AddTransition( "AwaitBoarding", "Board", "Boarding" ) - Fsm:AddTransition( "Boarding", "Boarded", "Boarded" ) - - Fsm:AddTransition( "*", "Load", "Loaded" ) - - Fsm:AddTransition( "*", "PrepareUnBoarding", "AwaitUnBoarding" ) - Fsm:AddTransition( "AwaitUnBoarding", "UnBoard", "UnBoarding" ) - Fsm:AddTransition( "UnBoarding", "UnBoarded", "UnBoarded" ) - - Fsm:AddTransition( "*", "Unload", "Unloaded" ) - - Fsm:AddTransition( "*", "Planned", "Planned" ) - - - Fsm:AddTransition( "Deployed", "Success", "Success" ) - Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) - Fsm:AddTransition( "Failed", "Fail", "Failed" ) - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param #TASK_CARGO Task - function Fsm:OnAfterAssigned( TaskUnit, Task ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - self:SelectAction() - end - - - - --- - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param #TASK_CARGO Task - function Fsm:onafterSelectAction( TaskUnit, Task ) - - local TaskUnitName = TaskUnit:GetName() - local MenuTime = Task:InitTaskControlMenu( TaskUnit ) - local MenuControl = Task:GetTaskControlMenu( TaskUnit ) - - Task.SetCargo:ForEachCargo( - - -- @param Cargo.Cargo#CARGO Cargo - function( Cargo ) - - if Cargo:IsAlive() then - --- if Task:is( "RoutingToPickup" ) then --- MENU_GROUP_COMMAND:New( --- TaskUnit:GetGroup(), --- "Cancel Route " .. Cargo.Name, --- MenuControl, --- self.MenuRouteToPickupCancel, --- self, --- Cargo --- ):SetTime(MenuTime) --- end - - --self:F( { CargoUnloaded = Cargo:IsUnLoaded(), CargoLoaded = Cargo:IsLoaded(), CargoItemCount = CargoItemCount } ) - - local TaskGroup = TaskUnit:GetGroup() - - if Cargo:IsUnLoaded() then - local CargoBayFreeWeight = TaskUnit:GetCargoBayFreeWeight() - local CargoWeight = Cargo:GetWeight() - - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then - if Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then - local NotInDeployZones = true - for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do - if Cargo:IsInZone( DeployZone ) then - NotInDeployZones = false - end - end - if NotInDeployZones then - if not TaskUnit:InAir() then - if Cargo:CanBoard() == true then - if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - Cargo:Report( "Ready for boarding.", "board", TaskUnit:GetGroup() ) - local BoardMenu = MENU_GROUP:New( TaskGroup, "Board cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, BoardMenu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() - else - Cargo:Report( "Board at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() .. "." ), "reporting", TaskUnit:GetGroup() ) - end - else - if Cargo:CanLoad() == true then - if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - Cargo:Report( "Ready for loading.", "load", TaskUnit:GetGroup() ) - local LoadMenu = MENU_GROUP:New( TaskGroup, "Load cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, LoadMenu, self.MenuLoadCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() - else - Cargo:Report( "Load at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ) .. " within " .. Cargo.NearRadius .. ".", "reporting", TaskUnit:GetGroup() ) - end - else - --local Cargo = Cargo -- Cargo.CargoSlingload#CARGO_SLINGLOAD - if Cargo:CanSlingload() == true then - if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - Cargo:Report( "Ready for sling loading.", "slingload", TaskUnit:GetGroup() ) - local SlingloadMenu = MENU_GROUP:New( TaskGroup, "Slingload cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, SlingloadMenu, self.MenuLoadCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() - else - Cargo:Report( "Slingload at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ) .. ".", "reporting", TaskUnit:GetGroup() ) - end - end - end - end - else - Cargo:ReportResetAll( TaskUnit:GetGroup() ) - end - end - else - if not Cargo:IsDeployed() == true then - local RouteToPickupMenu = MENU_GROUP:New( TaskGroup, "Route to pickup cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) - --MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, RouteToPickupMenu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() - Cargo:ReportResetAll( TaskUnit:GetGroup() ) - if Cargo:CanBoard() == true then - if not Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - local BoardMenu = MENU_GROUP:New( TaskGroup, "Board cargo", RouteToPickupMenu ):SetTime( MenuTime ):SetTag( "Cargo" ) - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, BoardMenu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() - end - else - if Cargo:CanLoad() == true then - if not Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - local LoadMenu = MENU_GROUP:New( TaskGroup, "Load cargo", RouteToPickupMenu ):SetTime( MenuTime ):SetTag( "Cargo" ) - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, LoadMenu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() - end - else - --local Cargo = Cargo -- Cargo.CargoSlingload#CARGO_SLINGLOAD - if Cargo:CanSlingload() == true then - if not Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - local SlingloadMenu = MENU_GROUP:New( TaskGroup, "Slingload cargo", RouteToPickupMenu ):SetTime( MenuTime ):SetTag( "Cargo" ) - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, SlingloadMenu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() - end - end - end - end - end - end - end - - -- Cargo in deployzones are flagged as deployed. - for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do - if Cargo:IsInZone( DeployZone ) then - Task:I( { CargoIsDeployed = Task.CargoDeployed and "true" or "false" } ) - if Cargo:IsDeployed() == false then - Cargo:SetDeployed( true ) - -- Now we call a callback method to handle the CargoDeployed event. - Task:I( { CargoIsAlive = Cargo:IsAlive() and "true" or "false" } ) - if Cargo:IsAlive() then - Task:CargoDeployed( TaskUnit, Cargo, DeployZone ) - end - end - end - end - - end - - if Cargo:IsLoaded() == true and Cargo:IsLoadedInCarrier( TaskUnit ) == true then - if not TaskUnit:InAir() then - if Cargo:CanUnboard() == true then - local UnboardMenu = MENU_GROUP:New( TaskGroup, "Unboard cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, UnboardMenu, self.MenuUnboardCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() - else - if Cargo:CanUnload() == true then - local UnloadMenu = MENU_GROUP:New( TaskGroup, "Unload cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, UnloadMenu, self.MenuUnloadCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() - end - end - end - end - - -- Deployzones are optional zones that can be selected to request routing information. - for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do - if not Cargo:IsInZone( DeployZone ) then - local RouteToDeployMenu = MENU_GROUP:New( TaskGroup, "Route to deploy cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Zone " .. DeployZoneName, RouteToDeployMenu, self.MenuRouteToDeploy, self, DeployZone ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() - end - end - end - - end - ) - - Task:RefreshTaskControlMenu( TaskUnit, MenuTime, "Cargo" ) - - self:__SelectAction( -1 ) - - end - - - --- - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param #TASK_CARGO Task - function Fsm:OnLeaveWaitingForCommand( TaskUnit, Task ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - --local MenuControl = Task:GetTaskControlMenu( TaskUnit ) - - --MenuControl:Remove() - end - - function Fsm:MenuBoardCargo( Cargo ) - self:__PrepareBoarding( 1.0, Cargo ) - end - - function Fsm:MenuLoadCargo( Cargo ) - self:__Load( 1.0, Cargo ) - end - - function Fsm:MenuUnboardCargo( Cargo, DeployZone ) - self:__PrepareUnBoarding( 1.0, Cargo, DeployZone ) - end - - function Fsm:MenuUnloadCargo( Cargo, DeployZone ) - self:__Unload( 1.0, Cargo, DeployZone ) - end - - function Fsm:MenuRouteToPickup( Cargo ) - self:__RouteToPickup( 1.0, Cargo ) - end - - function Fsm:MenuRouteToDeploy( DeployZone ) - self:__RouteToDeploy( 1.0, DeployZone ) - end - - - - --- - --#TASK_CAROG_TRANSPORT self - --#Wrapper.Unit#UNIT - - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - -- @param From - -- @param Event - -- @param To - -- @param Cargo.Cargo#CARGO Cargo - function Fsm:onafterRouteToPickup( TaskUnit, Task, From, Event, To, Cargo ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - if Cargo:IsAlive() then - self.Cargo = Cargo -- Cargo.Cargo#CARGO - Task:SetCargoPickup( self.Cargo, TaskUnit ) - self:__RouteToPickupPoint( -0.1 ) - end - - end - - - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterArriveAtPickup( TaskUnit, Task ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - if self.Cargo:IsAlive() then - if TaskUnit:IsAir() then - Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() ) - self:__Land( -0.1, "Pickup" ) - else - self:__SelectAction( -0.1 ) - end - end - end - - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterCancelRouteToPickup( TaskUnit, Task ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - Task:GetMission():GetCommandCenter():MessageToGroup( "Cancelled routing to Cargo " .. self.Cargo:GetName(), TaskUnit:GetGroup() ) - self:__SelectAction( -0.1 ) - end - - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - function Fsm:onafterRouteToDeploy( TaskUnit, Task, From, Event, To, DeployZone ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - self:F( DeployZone ) - self.DeployZone = DeployZone - Task:SetDeployZone( self.DeployZone, TaskUnit ) - self:__RouteToDeployZone( -0.1 ) - end - - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterArriveAtDeploy( TaskUnit, Task ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - if TaskUnit:IsAir() then - Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() ) - self:__Land( -0.1, "Deploy" ) - else - self:__SelectAction( -0.1 ) - end - end - - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterCancelRouteToDeploy( TaskUnit, Task ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - Task:GetMission():GetCommandCenter():MessageToGroup( "Cancelled routing to deploy zone " .. self.DeployZone:GetName(), TaskUnit:GetGroup() ) - self:__SelectAction( -0.1 ) - end - - - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterLand( TaskUnit, Task, From, Event, To, Action ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - if Action == "Pickup" then - if self.Cargo:IsAlive() then - if self.Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then - if TaskUnit:InAir() then - self:__Land( -10, Action ) - else - Task:GetMission():GetCommandCenter():MessageToGroup( "Landed at pickup location...", TaskUnit:GetGroup() ) - self:__Landed( -0.1, Action ) - end - else - self:__RouteToPickup( -0.1, self.Cargo ) - end - end - else - if TaskUnit:IsAlive() then - if TaskUnit:IsInZone( self.DeployZone ) then - if TaskUnit:InAir() then - self:__Land( -10, Action ) - else - Task:GetMission():GetCommandCenter():MessageToGroup( "Landed at deploy zone " .. self.DeployZone:GetName(), TaskUnit:GetGroup() ) - self:__Landed( -0.1, Action ) - end - else - self:__RouteToDeploy( -0.1, self.Cargo ) - end - end - end - end - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterLanded( TaskUnit, Task, From, Event, To, Action ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - if Action == "Pickup" then - if self.Cargo:IsAlive() then - if self.Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then - if TaskUnit:InAir() then - self:__Land( -0.1, Action ) - else - self:__SelectAction( -0.1 ) - end - else - self:__RouteToPickup( -0.1, self.Cargo ) - end - end - else - if TaskUnit:IsAlive() then - if TaskUnit:IsInZone( self.DeployZone ) then - if TaskUnit:InAir() then - self:__Land( -10, Action ) - else - self:__SelectAction( -0.1 ) - end - else - self:__RouteToDeploy( -0.1, self.Cargo ) - end - end - end - end - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterPrepareBoarding( TaskUnit, Task, From, Event, To, Cargo ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - if Cargo and Cargo:IsAlive() then - self:__Board( -0.1, Cargo ) - end - end - - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterBoard( TaskUnit, Task, From, Event, To, Cargo ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - function Cargo:OnEnterLoaded( From, Event, To, TaskUnit, TaskProcess ) - self:F({From, Event, To, TaskUnit, TaskProcess }) - TaskProcess:__Boarded( 0.1, self ) - end - - if Cargo:IsAlive() then - if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - if TaskUnit:InAir() then - --- ABORT the boarding. Split group if any and go back to select action. - else - Cargo:MessageToGroup( "Boarding ...", TaskUnit:GetGroup() ) - if not Cargo:IsBoarding() then - Cargo:Board( TaskUnit, nil, self ) - end - end - else - --self:__ArriveAtCargo( -0.1 ) - end - end - end - - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterBoarded( TaskUnit, Task, From, Event, To, Cargo ) - - local TaskUnitName = TaskUnit:GetName() - self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) - - Cargo:MessageToGroup( "Boarded cargo " .. Cargo:GetName(), TaskUnit:GetGroup() ) - - self:__Load( -0.1, Cargo ) - - end - - - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterLoad( TaskUnit, Task, From, Event, To, Cargo ) - - local TaskUnitName = TaskUnit:GetName() - self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) - - if not Cargo:IsLoaded() then - Cargo:Load( TaskUnit ) - end - - Cargo:MessageToGroup( "Loaded cargo " .. Cargo:GetName(), TaskUnit:GetGroup() ) - TaskUnit:AddCargo( Cargo ) - - Task:CargoPickedUp( TaskUnit, Cargo ) - - self:SelectAction( -1 ) - - end - - - --- - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - -- @param From - -- @param Event - -- @param To - -- @param Cargo - -- @param Core.Zone#ZONE_BASE DeployZone - function Fsm:onafterPrepareUnBoarding( TaskUnit, Task, From, Event, To, Cargo ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID(), From, Event, To, Cargo } ) - - self.Cargo = Cargo - self.DeployZone = nil - - -- Check if the Cargo is at a deployzone... If it is, provide it as a parameter! - if Cargo:IsAlive() then - for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do - if Cargo:IsInZone( DeployZone ) then - self.DeployZone = DeployZone -- Core.Zone#ZONE_BASE - break - end - end - self:__UnBoard( -0.1, Cargo, self.DeployZone ) - end - end - - --- - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - -- @param From - -- @param Event - -- @param To - -- @param Cargo - -- @param Core.Zone#ZONE_BASE DeployZone - function Fsm:onafterUnBoard( TaskUnit, Task, From, Event, To, Cargo, DeployZone ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID(), From, Event, To, Cargo, DeployZone } ) - - function self.Cargo:OnEnterUnLoaded( From, Event, To, DeployZone, TaskProcess ) - self:F({From, Event, To, DeployZone, TaskProcess }) - TaskProcess:__UnBoarded( -0.1 ) - end - - if self.Cargo:IsAlive() then - self.Cargo:MessageToGroup( "UnBoarding ...", TaskUnit:GetGroup() ) - if DeployZone then - self.Cargo:UnBoard( DeployZone:GetCoordinate():GetRandomCoordinateInRadius( 25, 10 ), 400, self ) - else - self.Cargo:UnBoard( TaskUnit:GetCoordinate():GetRandomCoordinateInRadius( 25, 10 ), 400, self ) - end - end - end - - - --- - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterUnBoarded( TaskUnit, Task ) - - local TaskUnitName = TaskUnit:GetName() - self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) - - self.Cargo:MessageToGroup( "UnBoarded cargo " .. self.Cargo:GetName(), TaskUnit:GetGroup() ) - - self:Unload( self.Cargo ) - end - - --- - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterUnload( TaskUnit, Task, From, Event, To, Cargo, DeployZone ) - - local TaskUnitName = TaskUnit:GetName() - self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) - - if not Cargo:IsUnLoaded() then - if DeployZone then - Cargo:UnLoad( DeployZone:GetCoordinate():GetRandomCoordinateInRadius( 25, 10 ), 400, self ) - else - Cargo:UnLoad( TaskUnit:GetCoordinate():GetRandomCoordinateInRadius( 25, 10 ), 400, self ) - end - end - TaskUnit:RemoveCargo( Cargo ) - - Cargo:MessageToGroup( "Unloaded cargo " .. Cargo:GetName(), TaskUnit:GetGroup() ) - - self:Planned() - self:__SelectAction( 1 ) - end - - return self - - end - - - --- Set a limit on the amount of cargo items that can be loaded into the Carriers. - -- @param #TASK_CARGO self - -- @param CargoLimit Specifies a number of cargo items that can be loaded in the helicopter. - -- @return #TASK_CARGO - function TASK_CARGO:SetCargoLimit( CargoLimit ) - self.CargoLimit = CargoLimit - return self - end - - - --@param Color Might be SMOKECOLOR.Blue, SMOKECOLOR.Red SMOKECOLOR.Orange, SMOKECOLOR.White or SMOKECOLOR.Green - function TASK_CARGO:SetSmokeColor(SmokeColor) - -- Makes sure Coloe is set - if SmokeColor == nil then - self.SmokeColor = SMOKECOLOR.Red -- Make sure a default color is exist - - elseif type(SmokeColor) == "number" then - self:F2(SmokeColor) - if SmokeColor > 0 and SmokeColor <=5 then -- Make sure number is within ragne, assuming first enum is one - self.SmokeColor = SMOKECOLOR.SmokeColor - end - end - end - - --@return SmokeColor - function TASK_CARGO:GetSmokeColor() - return self.SmokeColor - end - - -- @param #TASK_CARGO self - function TASK_CARGO:GetPlannedMenuText() - return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" - end - - -- @param #TASK_CARGO self - -- @return Core.Set#SET_CARGO The Cargo Set. - function TASK_CARGO:GetCargoSet() - - return self.SetCargo - end - - -- @param #TASK_CARGO self - -- @return #list The Deployment Zones. - function TASK_CARGO:GetDeployZones() - - return self.DeployZones - end - - -- @param #TASK_CARGO self - -- @param AI.AI_Cargo#AI_CARGO Cargo The cargo. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_CARGO - function TASK_CARGO:SetCargoPickup( Cargo, TaskUnit ) - - self:F({Cargo, TaskUnit}) - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local MenuTime = self:InitTaskControlMenu( TaskUnit ) - local MenuControl = self:GetTaskControlMenu( TaskUnit ) - - local ActRouteCargo = ProcessUnit:GetProcess( "RoutingToPickup", "RouteToPickupPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT - ActRouteCargo:Reset() - ActRouteCargo:SetCoordinate( Cargo:GetCoordinate() ) - ActRouteCargo:SetRange( Cargo:GetLoadRadius() ) - ActRouteCargo:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Cargo " .. Cargo:GetName(), MenuControl, MenuTime, "Cargo" ) - ActRouteCargo:Start() - - return self - end - - - -- @param #TASK_CARGO self - -- @param Core.Zone#ZONE DeployZone - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_CARGO - function TASK_CARGO:SetDeployZone( DeployZone, TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local MenuTime = self:InitTaskControlMenu( TaskUnit ) - local MenuControl = self:GetTaskControlMenu( TaskUnit ) - - local ActRouteDeployZone = ProcessUnit:GetProcess( "RoutingToDeploy", "RouteToDeployZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE - ActRouteDeployZone:Reset() - ActRouteDeployZone:SetZone( DeployZone ) - ActRouteDeployZone:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Deploy Zone" .. DeployZone:GetName(), MenuControl, MenuTime, "Cargo" ) - ActRouteDeployZone:Start() - - return self - end - - - -- @param #TASK_CARGO self - -- @param Core.Zone#ZONE DeployZone - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_CARGO - function TASK_CARGO:AddDeployZone( DeployZone, TaskUnit ) - - self.DeployZones[DeployZone:GetName()] = DeployZone - - return self - end - - -- @param #TASK_CARGO self - -- @param Core.Zone#ZONE DeployZone - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_CARGO - function TASK_CARGO:RemoveDeployZone( DeployZone, TaskUnit ) - - self.DeployZones[DeployZone:GetName()] = nil - - return self - end - - -- @param #TASK_CARGO self - -- @param #list DeployZones - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_CARGO - function TASK_CARGO:SetDeployZones( DeployZones, TaskUnit ) - - for DeployZoneID, DeployZone in pairs( DeployZones or {} ) do - self.DeployZones[DeployZone:GetName()] = DeployZone - end - - return self - end - - - - -- @param #TASK_CARGO self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. - function TASK_CARGO: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 progress is made. - -- @param #TASK_CARGO self - -- @param #string Text The text to display to the player, when there is progress on the task goals. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_CARGO - function TASK_CARGO:SetScoreOnProgress( 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 success is achieved. - -- @param #TASK_CARGO self - -- @param #string Text The text to display to the player, when the task goals have been achieved. - -- @param #number Score The score in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_CARGO - function TASK_CARGO: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 task goals have failed.. - -- @param #TASK_CARGO self - -- @param #string Text The text to display to the player, when the task goals has failed. - -- @param #number Penalty The penalty in points. - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_CARGO - function TASK_CARGO:SetScoreOnFail( Text, Penalty, TaskUnit ) - self:F( { Text, Score, TaskUnit } ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - ProcessUnit:AddScore( "Failed", Text, Penalty ) - - return self - end - - function TASK_CARGO:SetGoalTotal() - - self.GoalTotal = self.SetCargo:Count() - end - - function TASK_CARGO:GetGoalTotal() - - return self.GoalTotal - end - - -- @param #TASK_CARGO self - function TASK_CARGO:UpdateTaskInfo() - - if self:IsStatePlanned() or self:IsStateAssigned() then - self.TaskInfo:AddTaskName( 0, "MSOD" ) - self.TaskInfo:AddCargoSet( self.SetCargo, 10, "SOD", true ) - local Coordinates = {} - for CargoName, Cargo in pairs( self.SetCargo:GetSet() ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - if not Cargo:IsLoaded() then - Coordinates[#Coordinates+1] = Cargo:GetCoordinate() - end - end - self.TaskInfo:AddCoordinates( Coordinates, 1, "M" ) - end - end - - function TASK_CARGO:ReportOrder( ReportGroup ) - - return 0 - end - - --- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection. - -- @param #TASK_CARGO self - -- @param #number AutoAssignMethod The method to be applied to the task. - -- @param Wrapper.Group#GROUP TaskGroup The player group. - function TASK_CARGO:GetAutoAssignPriority( AutoAssignMethod, TaskGroup ) - - if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then - return math.random( 1, 9 ) - elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then - return 0 - elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then - return 1 - end - - return 0 - end - - - -end - - diff --git a/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua deleted file mode 100644 index fa334dcf4..000000000 --- a/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua +++ /dev/null @@ -1,404 +0,0 @@ ---- **Tasking** - Creates and manages player TASK_ZONE_CAPTURE tasks. --- --- The **TASK_CAPTURE_DISPATCHER** allows you to setup various tasks for let human --- players capture zones in a co-operation effort. --- --- The dispatcher will implement for you mechanisms to create capture zone tasks: --- --- * As setup by the mission designer. --- * Dynamically capture zone tasks. --- --- --- --- **Specific features:** --- --- * Creates a task to capture zones and achieve mission goals. --- * Orchestrate the task flow, so go from Planned to Assigned to Success, Failed or Cancelled. --- * Co-operation tasking, so a player joins a group of players executing the same task. --- --- --- **A complete task menu system to allow players to:** --- --- * Join the task, abort the task. --- * Mark the location of the zones to capture on the map. --- * Provide details of the zones. --- * Route to the zones. --- * Display the task briefing. --- --- --- **A complete mission menu system to allow players to:** --- --- * Join a task, abort the task. --- * Display task reports. --- * Display mission statistics. --- * Mark the task locations on the map. --- * Provide details of the zones. --- * Display the mission briefing. --- * Provide status updates as retrieved from the command center. --- * Automatically assign a random task as part of a mission. --- * Manually assign a specific task as part of a mission. --- --- --- **A settings system, using the settings menu:** --- --- * Tweak the duration of the display of messages. --- * Switch between metric and imperial measurement system. --- * Switch between coordinate formats used in messages: BR, BRA, LL DMS, LL DDM, MGRS. --- * Various other options. --- --- # Developer Note --- --- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE --- Therefore, this class is considered to be deprecated --- --- === --- --- ### Author: **FlightControl** --- --- ### Contributions: --- --- === --- --- @module Tasking.Task_Capture_Dispatcher --- @image MOOSE.JPG - -do -- TASK_CAPTURE_DISPATCHER - - --- TASK_CAPTURE_DISPATCHER class. - -- @type TASK_CAPTURE_DISPATCHER - -- @extends Tasking.Task_Manager#TASK_MANAGER - -- @field TASK_CAPTURE_DISPATCHER.ZONE ZONE - - -- @type TASK_CAPTURE_DISPATCHER.CSAR - -- @field Wrapper.Unit#UNIT PilotUnit - -- @field Tasking.Task#TASK Task - - - --- Implements the dynamic dispatching of capture zone tasks. - -- - -- ![Banner Image](..\Images\deprecated.png) - -- - -- The **TASK_CAPTURE_DISPATCHER** allows you to setup various tasks for let human - -- players capture zones in a co-operation effort. - -- - -- Let's explore **step by step** how to setup the task capture zone dispatcher. - -- - -- # 1. Setup a mission environment. - -- - -- It is easy, as it works just like any other task setup, so setup a command center and a mission. - -- - -- ## 1.1. Create a command center. - -- - -- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor. - -- The command assumes that you´ve setup a group in the mission editor with the name HQ. - -- This group will act as the command center object. - -- It is a good practice to mark this group as invisible and invulnerable. - -- - -- local CommandCenter = COMMANDCENTER - -- :New( GROUP:FindByName( "HQ" ), "HQ" ) -- Create the CommandCenter. - -- - -- ## 1.2. Create a mission. - -- - -- Tasks work in a **mission**, which groups these tasks to achieve a joint **mission goal**. A command center can **govern multiple missions**. - -- - -- Create a new mission, using the @{Tasking.Mission#MISSION.New}() constructor. - -- - -- -- Declare the Mission for the Command Center. - -- local Mission = MISSION - -- :New( CommandCenter, - -- "Overlord", - -- "High", - -- "Capture the blue zones.", - -- coalition.side.RED - -- ) - -- - -- - -- # 2. Dispatch a **capture zone** task. - -- - -- So, now that we have a command center and a mission, we now create the capture zone task. - -- We create the capture zone task using the @{#TASK_CAPTURE_DISPATCHER.AddCaptureZoneTask}() constructor. - -- - -- ## 2.1. Create the capture zones. - -- - -- Because a capture zone task will not generate the capture zones, you'll need to create them first. - -- - -- - -- -- We define here a capture zone; of the type ZONE_CAPTURE_COALITION. - -- -- The zone to be captured has the name Alpha, and was defined in the mission editor as a trigger zone. - -- CaptureZone = ZONE:New( "Alpha" ) - -- CaptureZoneCoalitionApha = ZONE_CAPTURE_COALITION:New( CaptureZone, coalition.side.RED ) - -- - -- ## 2.2. Create a set of player groups. - -- - -- What is also needed, is to have a set of @{Wrapper.Group}s defined that contains the clients of the players. - -- - -- -- Allocate the player slots, which must be aircraft (airplanes or helicopters), that can be manned by players. - -- -- We use the method FilterPrefixes to filter those player groups that have client slots, as defined in the mission editor. - -- -- In this example, we filter the groups where the name starts with "Blue Player", which captures the blue player slots. - -- local PlayerGroupSet = SET_GROUP:New():FilterPrefixes( "Blue Player" ):FilterStart() - -- - -- ## 2.3. Setup the capture zone task. - -- - -- First, we need to create a TASK_CAPTURE_DISPATCHER object. - -- - -- TaskCaptureZoneDispatcher = TASK_CAPTURE_DISPATCHER:New( Mission, PilotGroupSet ) - -- - -- So, the variable `TaskCaptureZoneDispatcher` will contain the object of class TASK_CAPTURE_DISPATCHER, - -- which will allow you to dispatch capture zone tasks: - -- - -- * for mission `Mission`, as was defined in section 1.2. - -- * for the group set `PilotGroupSet`, as was defined in section 2.2. - -- - -- Now that we have `TaskDispatcher` object, we can now **create the TaskCaptureZone**, using the @{#TASK_CAPTURE_DISPATCHER.AddCaptureZoneTask}() method! - -- - -- local TaskCaptureZone = TaskCaptureZoneDispatcher:AddCaptureZoneTask( - -- "Capture zone Alpha", - -- CaptureZoneCoalitionAlpha, - -- "Fly to zone Alpha and eliminate all enemy forces to capture it." ) - -- - -- As a result of this code, the `TaskCaptureZone` (returned) variable will contain an object of @{#TASK_CAPTURE_ZONE}! - -- We pass to the method the title of the task, and the `CaptureZoneCoalitionAlpha`, which is the zone to be captured, as defined in section 2.1! - -- This returned `TaskCaptureZone` object can now be used to setup additional task configurations, or to control this specific task with special events. - -- - -- And you're done! As you can see, it is a small bit of work, but the reward is great. - -- And, because all this is done using program interfaces, you can easily build a mission to capture zones yourself! - -- Based on various events happening within your mission, you can use the above methods to create new capture zones, - -- and setup a new capture zone task and assign it to a group of players, while your mission is running! - -- - -- - -- - -- @field #TASK_CAPTURE_DISPATCHER - TASK_CAPTURE_DISPATCHER = { - ClassName = "TASK_CAPTURE_DISPATCHER", - Mission = nil, - Tasks = {}, - Zones = {}, - ZoneCount = 0, - } - - - - TASK_CAPTURE_DISPATCHER.AI_A2G_Dispatcher = nil -- AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER - - --- TASK_CAPTURE_DISPATCHER constructor. - -- @param #TASK_CAPTURE_DISPATCHER self - -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. - -- @param Core.Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. - -- @return #TASK_CAPTURE_DISPATCHER self - function TASK_CAPTURE_DISPATCHER:New( Mission, SetGroup ) - - -- Inherits from DETECTION_MANAGER - local self = BASE:Inherit( self, TASK_MANAGER:New( SetGroup ) ) -- #TASK_CAPTURE_DISPATCHER - - self.Mission = Mission - self.FlashNewTask = false - - self:AddTransition( "Started", "Assign", "Started" ) - self:AddTransition( "Started", "ZoneCaptured", "Started" ) - - self:__StartTasks( 5 ) - - return self - end - - - --- Link a task capture dispatcher from the other coalition to understand its plan for defenses. - -- This is used for the tactical overview, so the players also know the zones attacked by the other coalition! - -- @param #TASK_CAPTURE_DISPATCHER self - -- @param #TASK_CAPTURE_DISPATCHER DefenseTaskCaptureDispatcher - function TASK_CAPTURE_DISPATCHER:SetDefenseTaskCaptureDispatcher( DefenseTaskCaptureDispatcher ) - - self.DefenseTaskCaptureDispatcher = DefenseTaskCaptureDispatcher - end - - - --- Get the linked task capture dispatcher from the other coalition to understand its plan for defenses. - -- This is used for the tactical overview, so the players also know the zones attacked by the other coalition! - -- @param #TASK_CAPTURE_DISPATCHER self - -- @return #TASK_CAPTURE_DISPATCHER - function TASK_CAPTURE_DISPATCHER:GetDefenseTaskCaptureDispatcher() - - return self.DefenseTaskCaptureDispatcher - end - - - --- Link an AI A2G dispatcher from the other coalition to understand its plan for defenses. - -- This is used for the tactical overview, so the players also know the zones attacked by the other AI A2G dispatcher! - -- @param #TASK_CAPTURE_DISPATCHER self - -- @param AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER DefenseAIA2GDispatcher - function TASK_CAPTURE_DISPATCHER:SetDefenseAIA2GDispatcher( DefenseAIA2GDispatcher ) - - self.DefenseAIA2GDispatcher = DefenseAIA2GDispatcher - end - - - --- Get the linked AI A2G dispatcher from the other coalition to understand its plan for defenses. - -- This is used for the tactical overview, so the players also know the zones attacked by the AI A2G dispatcher! - -- @param #TASK_CAPTURE_DISPATCHER self - -- @return AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER - function TASK_CAPTURE_DISPATCHER:GetDefenseAIA2GDispatcher() - - return self.DefenseAIA2GDispatcher - end - - - --- Add a capture zone task. - -- @param #TASK_CAPTURE_DISPATCHER self - -- @param #string TaskPrefix (optional) The prefix of the capture zone task. - -- If no TaskPrefix is given, then "Capture" will be used as the TaskPrefix. - -- The TaskPrefix will be appended with a . + a number of 3 digits, if the TaskPrefix already exists in the task collection. - -- @param Functional.ZoneCaptureCoalition#ZONE_CAPTURE_COALITION CaptureZone The zone of the coalition to be captured as the task goal. - -- @param #string Briefing The briefing of the task to be shown to the player. - -- @return Tasking.Task_Capture_Zone#TASK_CAPTURE_ZONE - -- @usage - -- - -- - function TASK_CAPTURE_DISPATCHER:AddCaptureZoneTask( TaskPrefix, CaptureZone, Briefing ) - - local TaskName = TaskPrefix or "Capture" - if self.Zones[TaskName] then - self.ZoneCount = self.ZoneCount + 1 - TaskName = string.format( "%s.%03d", TaskName, self.ZoneCount ) - end - - self.Zones[TaskName] = {} - self.Zones[TaskName].CaptureZone = CaptureZone - self.Zones[TaskName].Briefing = Briefing - self.Zones[TaskName].Task = nil - self.Zones[TaskName].TaskPrefix = TaskPrefix - - self:ManageTasks() - - return self.Zones[TaskName] and self.Zones[TaskName].Task - end - - - --- Link an AI_A2G_DISPATCHER to the TASK_CAPTURE_DISPATCHER. - -- @param #TASK_CAPTURE_DISPATCHER self - -- @param AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER AI_A2G_Dispatcher The AI Dispatcher to be linked to the tasking. - -- @return Tasking.Task_Capture_Zone#TASK_CAPTURE_ZONE - function TASK_CAPTURE_DISPATCHER:Link_AI_A2G_Dispatcher( AI_A2G_Dispatcher ) - - self.AI_A2G_Dispatcher = AI_A2G_Dispatcher -- AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER - AI_A2G_Dispatcher.Detection:LockDetectedItems() - - return self - end - - - --- Assigns tasks to the @{Core.Set#SET_GROUP}. - -- @param #TASK_CAPTURE_DISPATCHER self - -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. - function TASK_CAPTURE_DISPATCHER:ManageTasks() - self:F() - - local AreaMsg = {} - local TaskMsg = {} - local ChangeMsg = {} - - local Mission = self.Mission - - if Mission:IsIDLE() or Mission:IsENGAGED() then - - local TaskReport = REPORT:New() - - -- Checking the task queue for the dispatcher, and removing any obsolete task! - for TaskIndex, TaskData in pairs( self.Tasks ) do - local Task = TaskData -- Tasking.Task#TASK - if Task:IsStatePlanned() then - -- Here we need to check if the pilot is still existing. --- Task = self:RemoveTask( TaskIndex ) - end - - end - - -- Now that all obsolete tasks are removed, loop through the Zone tasks. - for TaskName, CaptureZone in pairs( self.Zones ) do - - if not CaptureZone.Task then - -- New Transport Task - CaptureZone.Task = TASK_CAPTURE_ZONE:New( Mission, self.SetGroup, TaskName, CaptureZone.CaptureZone, CaptureZone.Briefing ) - CaptureZone.Task.TaskPrefix = CaptureZone.TaskPrefix -- We keep the TaskPrefix for further reference! - Mission:AddTask( CaptureZone.Task ) - TaskReport:Add( TaskName ) - - -- Link the Task Dispatcher to the capture zone task, because it is used on the UpdateTaskInfo. - CaptureZone.Task:SetDispatcher( self ) - CaptureZone.Task:UpdateTaskInfo() - - function CaptureZone.Task.OnEnterAssigned( Task, From, Event, To ) - if self.AI_A2G_Dispatcher then - self.AI_A2G_Dispatcher:Unlock( Task.TaskZoneName ) -- This will unlock the zone to be defended by AI. - end - CaptureZone.Task:UpdateTaskInfo() - CaptureZone.Task.ZoneGoal.Attacked = true - end - - function CaptureZone.Task.OnEnterSuccess( Task, From, Event, To ) - --self:Success( Task ) - if self.AI_A2G_Dispatcher then - self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI. - end - CaptureZone.Task:UpdateTaskInfo() - CaptureZone.Task.ZoneGoal.Attacked = false - end - - function CaptureZone.Task.OnEnterCancelled( Task, From, Event, To ) - self:Cancelled( Task ) - if self.AI_A2G_Dispatcher then - self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI. - end - CaptureZone.Task:UpdateTaskInfo() - CaptureZone.Task.ZoneGoal.Attacked = false - end - - function CaptureZone.Task.OnEnterFailed( Task, From, Event, To ) - self:Failed( Task ) - if self.AI_A2G_Dispatcher then - self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI. - end - CaptureZone.Task:UpdateTaskInfo() - CaptureZone.Task.ZoneGoal.Attacked = false - end - - function CaptureZone.Task.OnEnterAborted( Task, From, Event, To ) - self:Aborted( Task ) - if self.AI_A2G_Dispatcher then - self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI. - end - CaptureZone.Task:UpdateTaskInfo() - CaptureZone.Task.ZoneGoal.Attacked = false - end - - -- Now broadcast the onafterCargoPickedUp event to the Task Cargo Dispatcher. - function CaptureZone.Task.OnAfterCaptured( Task, From, Event, To, TaskUnit ) - self:Captured( Task, Task.TaskPrefix, TaskUnit ) - if self.AI_A2G_Dispatcher then - self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI. - end - CaptureZone.Task:UpdateTaskInfo() - CaptureZone.Task.ZoneGoal.Attacked = false - end - - end - - end - - - -- TODO set menus using the HQ coordinator - Mission:GetCommandCenter():SetMenu() - - local TaskText = TaskReport:Text(", ") - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and ( not self.FlashNewTask ) then - Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup ) - end - end - - end - - return true - end - -end diff --git a/Moose Development/Moose/Tasking/Task_Capture_Zone.lua b/Moose Development/Moose/Tasking/Task_Capture_Zone.lua deleted file mode 100644 index 4c2372e8d..000000000 --- a/Moose Development/Moose/Tasking/Task_Capture_Zone.lua +++ /dev/null @@ -1,336 +0,0 @@ ---- **Tasking** - The TASK_Protect models tasks for players to protect or capture specific zones. --- --- === --- --- ### Author: **FlightControl** --- --- ### Contributions: MillerTime --- --- === --- --- @module Tasking.Task_Capture_Zone --- @image MOOSE.JPG - -do -- TASK_ZONE_GOAL - - --- The TASK_ZONE_GOAL class - -- @type TASK_ZONE_GOAL - -- @field Functional.ZoneGoal#ZONE_GOAL ZoneGoal - -- @extends Tasking.Task#TASK - - --- # TASK_ZONE_GOAL class, extends @{Tasking.Task#TASK} - -- - -- ![Banner Image](..\Images\deprecated.png) - -- - -- The TASK_ZONE_GOAL class defines the task to protect or capture a protection zone. - -- The TASK_ZONE_GOAL 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. - -- - -- ## Set the scoring of achievements in an A2G attack. - -- - -- Scoring or penalties can be given in the following circumstances: - -- - -- * @{#TASK_ZONE_GOAL.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed. - -- * @{#TASK_ZONE_GOAL.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed. - -- * @{#TASK_ZONE_GOAL.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed. - -- - -- # Developer Note - -- - -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE - -- Therefore, this class is considered to be deprecated - -- - -- @field #TASK_ZONE_GOAL - TASK_ZONE_GOAL = { - ClassName = "TASK_ZONE_GOAL", - } - - --- Instantiates a new TASK_ZONE_GOAL. - -- @param #TASK_ZONE_GOAL 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 Functional.ZoneGoalCoalition#ZONE_GOAL_COALITION ZoneGoal - -- @return #TASK_ZONE_GOAL self - function TASK_ZONE_GOAL:New( Mission, SetGroup, TaskName, ZoneGoal, TaskType, TaskBriefing ) - local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- #TASK_ZONE_GOAL - self:F() - - self.ZoneGoal = ZoneGoal - self.TaskType = TaskType - - local Fsm = self:GetUnitProcess() - - - Fsm:AddTransition( "Assigned", "StartMonitoring", "Monitoring" ) - Fsm:AddTransition( "Monitoring", "Monitor", "Monitoring", {} ) - Fsm:AddProcess( "Monitoring", "RouteToZone", ACT_ROUTE_ZONE:New(), {} ) - - Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) - Fsm:AddTransition( "Failed", "Fail", "Failed" ) - - self:SetTargetZone( self.ZoneGoal:GetZone() ) - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task#TASK Task - function Fsm:OnAfterAssigned( TaskUnit, Task ) - self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - - self:__StartMonitoring( 0.1 ) - self:__RouteToZone( 0.1 ) - end - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task#TASK_ZONE_GOAL Task - function Fsm:onafterStartMonitoring( TaskUnit, Task ) - self:F( { self } ) - self:__Monitor( 0.1 ) - end - - --- Monitor Loop - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task#TASK_ZONE_GOAL Task - function Fsm:onafterMonitor( TaskUnit, Task ) - self:F( { self } ) - self:__Monitor( 15 ) - end - - --- Test - -- @param #FSM_PROCESS self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_A2G#TASK_ZONE_GOAL Task - function Fsm:onafterRouteTo( 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:__RouteToZone( 0.1 ) - end - end - - return self - - end - - -- @param #TASK_ZONE_GOAL self - -- @param Functional.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal Engine. - function TASK_ZONE_GOAL:SetProtect( ZoneGoal ) - - self.ZoneGoal = ZoneGoal -- Functional.ZoneGoal#ZONE_GOAL - end - - - - -- @param #TASK_ZONE_GOAL self - function TASK_ZONE_GOAL:GetPlannedMenuText() - return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.ZoneGoal:GetZoneName() .. " )" - end - - - -- @param #TASK_ZONE_GOAL 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_ZONE_GOAL:SetTargetZone( TargetZone, TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteZone = ProcessUnit:GetProcess( "Monitoring", "RouteToZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE - ActRouteZone:SetZone( TargetZone ) - end - - - -- @param #TASK_ZONE_GOAL self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map. - function TASK_ZONE_GOAL:GetTargetZone( TaskUnit ) - - local ProcessUnit = self:GetUnitProcess( TaskUnit ) - - local ActRouteZone = ProcessUnit:GetProcess( "Monitoring", "RouteToZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE - return ActRouteZone:GetZone() - end - - function TASK_ZONE_GOAL:SetGoalTotal( GoalTotal ) - - self.GoalTotal = GoalTotal - end - - function TASK_ZONE_GOAL:GetGoalTotal() - - return self.GoalTotal - end - -end - - -do -- TASK_CAPTURE_ZONE - - --- The TASK_CAPTURE_ZONE class - -- @type TASK_CAPTURE_ZONE - -- @field Functional.ZoneGoalCoalition#ZONE_GOAL_COALITION ZoneGoal - -- @extends #TASK_ZONE_GOAL - - --- # TASK_CAPTURE_ZONE class, extends @{Tasking.Task_Capture_Zone#TASK_ZONE_GOAL} - -- - -- The TASK_CAPTURE_ZONE class defines an Suppression or Extermination of Air Defenses task for a human player to be executed. - -- These tasks are important to be executed as they will help to achieve air superiority at the vicinity. - -- - -- The TASK_CAPTURE_ZONE is used by the @{Tasking.Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks - -- based on detected enemy ground targets. - -- - -- @field #TASK_CAPTURE_ZONE - TASK_CAPTURE_ZONE = { - ClassName = "TASK_CAPTURE_ZONE", - } - - - --- Instantiates a new TASK_CAPTURE_ZONE. - -- @param #TASK_CAPTURE_ZONE 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 Functional.ZoneGoalCoalition#ZONE_GOAL_COALITION ZoneGoalCoalition - -- @param #string TaskBriefing The briefing of the task. - -- @return #TASK_CAPTURE_ZONE self - function TASK_CAPTURE_ZONE:New( Mission, SetGroup, TaskName, ZoneGoalCoalition, TaskBriefing) - local self = BASE:Inherit( self, TASK_ZONE_GOAL:New( Mission, SetGroup, TaskName, ZoneGoalCoalition, "CAPTURE", TaskBriefing ) ) -- #TASK_CAPTURE_ZONE - self:F() - - Mission:AddTask( self ) - - self.TaskCoalition = ZoneGoalCoalition:GetCoalition() - self.TaskCoalitionName = ZoneGoalCoalition:GetCoalitionName() - self.TaskZoneName = ZoneGoalCoalition:GetZoneName() - - ZoneGoalCoalition:MonitorDestroyedUnits() - - self:SetBriefing( - TaskBriefing or - "Capture Zone " .. self.TaskZoneName - ) - - self:UpdateTaskInfo( true ) - - self:SetGoal( self.ZoneGoal.Goal ) - - return self - end - - - --- Instantiates a new TASK_CAPTURE_ZONE. - -- @param #TASK_CAPTURE_ZONE self - function TASK_CAPTURE_ZONE:UpdateTaskInfo( Persist ) - - Persist = Persist or false - - local ZoneCoordinate = self.ZoneGoal:GetZone():GetCoordinate() - self.TaskInfo:AddTaskName( 0, "MSOD", Persist ) - self.TaskInfo:AddCoordinate( ZoneCoordinate, 1, "SOD", Persist ) --- self.TaskInfo:AddText( "Zone Name", self.ZoneGoal:GetZoneName(), 10, "MOD", Persist ) --- self.TaskInfo:AddText( "Zone Coalition", self.ZoneGoal:GetCoalitionName(), 11, "MOD", Persist ) - local SetUnit = self.ZoneGoal:GetScannedSetUnit() - local ThreatLevel, ThreatText = SetUnit:CalculateThreatLevelA2G() - local ThreatCount = SetUnit:Count() - self.TaskInfo:AddThreat( ThreatText, ThreatLevel, 20, "MOD", Persist ) - self.TaskInfo:AddInfo( "Remaining Units", ThreatCount, 21, "MOD", Persist, true) - - if self.Dispatcher then - local DefenseTaskCaptureDispatcher = self.Dispatcher:GetDefenseTaskCaptureDispatcher() -- Tasking.Task_Capture_Dispatcher#TASK_CAPTURE_DISPATCHER - - if DefenseTaskCaptureDispatcher then - -- Loop through all zones of the player Defenses, and check which zone has an assigned task! - -- The Zones collection contains a Task. This Task is checked if it is assigned. - -- If Assigned, then this task will be the task that is the closest to the defense zone. - for TaskName, CaptureZone in pairs( DefenseTaskCaptureDispatcher.Zones or {} ) do - local Task = CaptureZone.Task -- Tasking.Task_Capture_Zone#TASK_CAPTURE_ZONE - if Task and Task:IsStateAssigned() then -- We also check assigned. - -- Now we register the defense player zone information to the task report. - self.TaskInfo:AddInfo( "Defense Player Zone", Task.ZoneGoal:GetName(), 30, "MOD", Persist ) - self.TaskInfo:AddCoordinate( Task.ZoneGoal:GetZone():GetCoordinate(), 31, "MOD", Persist, false, "Defense Player Coordinate" ) - end - end - end - local DefenseAIA2GDispatcher = self.Dispatcher:GetDefenseAIA2GDispatcher() -- AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER - - if DefenseAIA2GDispatcher then - -- Loop through all the tasks of the AI Defenses, and check which zone is involved in the defenses and is active! - for Defender, Task in pairs( DefenseAIA2GDispatcher:GetDefenderTasks() or {} ) do - local DetectedItem = DefenseAIA2GDispatcher:GetDefenderTaskTarget( Defender ) - if DetectedItem then - local DetectedZone = DefenseAIA2GDispatcher.Detection:GetDetectedItemZone( DetectedItem ) - if DetectedZone then - self.TaskInfo:AddInfo( "Defense AI Zone", DetectedZone:GetName(), 40, "MOD", Persist ) - self.TaskInfo:AddCoordinate( DetectedZone:GetCoordinate(), 41, "MOD", Persist, false, "Defense AI Coordinate" ) - end - end - end - end - end - - end - - - function TASK_CAPTURE_ZONE:ReportOrder( ReportGroup ) - - local Coordinate = self.TaskInfo:GetCoordinate() - local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) - - return Distance - end - - - -- @param #TASK_CAPTURE_ZONE self - -- @param Wrapper.Unit#UNIT TaskUnit - function TASK_CAPTURE_ZONE:OnAfterGoal( From, Event, To, PlayerUnit, PlayerName ) - - self:F( { PlayerUnit = PlayerUnit, Achieved = self.ZoneGoal.Goal:IsAchieved() } ) - - if self.ZoneGoal then - if self.ZoneGoal.Goal:IsAchieved() then - local TotalContributions = self.ZoneGoal.Goal:GetTotalContributions() - local PlayerContributions = self.ZoneGoal.Goal:GetPlayerContributions() - self:F( { TotalContributions = TotalContributions, PlayerContributions = PlayerContributions } ) - for PlayerName, PlayerContribution in pairs( PlayerContributions ) do - local Scoring = self:GetScoring() - if Scoring then - Scoring:_AddMissionGoalScore( self.Mission, PlayerName, "Zone " .. self.ZoneGoal:GetZoneName() .." captured", PlayerContribution * 200 / TotalContributions ) - end - end - self:Success() - end - end - - self:__Goal( -10, PlayerUnit, PlayerName ) - end - - --- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection. - -- @param #TASK_CAPTURE_ZONE 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_CAPTURE_ZONE:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup, AutoAssignReference ) - - if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then - return math.random( 1, 9 ) - elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then - local Coordinate = self.TaskInfo:GetCoordinate() - local Distance = Coordinate:Get2DDistance( CommandCenter:GetPositionable():GetCoordinate() ) - return math.floor( Distance ) - elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then - return 1 - end - - return 0 - end - -end - diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua deleted file mode 100644 index ca71d64ef..000000000 --- a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua +++ /dev/null @@ -1,400 +0,0 @@ ---- **Tasking** - Orchestrates the task for players to execute CSAR for downed pilots. --- --- **Specific features:** --- --- * Creates a task to retrieve a pilot @{Cargo.Cargo} from behind enemy lines. --- * Derived from the TASK_CARGO class, which is derived from the TASK class. --- * Orchestrate the task flow, so go from Planned to Assigned to Success, Failed or Cancelled. --- * Co-operation tasking, so a player joins a group of players executing the same task. --- --- --- **A complete task menu system to allow players to:** --- --- * Join the task, abort the task. --- * Mark the task location on the map. --- * Provide details of the target. --- * Route to the cargo. --- * Route to the deploy zones. --- * Load/Unload cargo. --- * Board/Unboard cargo. --- * Slingload cargo. --- * Display the task briefing. --- --- --- **A complete mission menu system to allow players to:** --- --- * Join a task, abort the task. --- * Display task reports. --- * Display mission statistics. --- * Mark the task locations on the map. --- * Provide details of the targets. --- * Display the mission briefing. --- * Provide status updates as retrieved from the command center. --- * Automatically assign a random task as part of a mission. --- * Manually assign a specific task as part of a mission. --- --- --- **A settings system, using the settings menu:** --- --- * Tweak the duration of the display of messages. --- * Switch between metric and imperial measurement system. --- * Switch between coordinate formats used in messages: BR, BRA, LL DMS, LL DDM, MGRS. --- * Different settings modes for A2G and A2A operations. --- * Various other options. --- --- === --- --- ![Banner Image](..\Images\deprecated.png) --- --- Please read through the @{Tasking.Task_CARGO} process to understand the mechanisms of tasking and cargo tasking and handling. --- --- The cargo will be a downed pilot, which is located somwhere on the battlefield. Use the menus system and facilities to --- join the CSAR task, and retrieve the pilot from behind enemy lines. The menu system is generic, there is nothing --- specific on a CSAR task that requires further explanation, than reading the generic TASK_CARGO explanations. --- --- Enjoy! --- FC --- --- # Developer Note --- --- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE --- Therefore, this class is considered to be deprecated --- --- === --- --- ### Author: **FlightControl** --- --- ### Contributions: --- --- === --- --- @module Tasking.Task_Cargo_CSAR --- @image Task_Cargo_CSAR.JPG - - -do -- TASK_CARGO_CSAR - - -- @type TASK_CARGO_CSAR - -- @extends Tasking.Task_Cargo#TASK_CARGO - - --- Orchestrates the task for players to execute CSAR for downed pilots. - -- - -- CSAR tasks are suited to govern the process of return downed pilots behind enemy lines back to safetly. - -- Typically, this task is executed by helicopter pilots, but it can also be executed by ground forces! - -- - -- === - -- - -- A CSAR task can be created manually, but actually, it is better to **GENERATE** these tasks using the - -- @{Tasking.Task_Cargo_Dispatcher} module. - -- - -- Using the dispatcher, CSAR tasks will be created **automatically** when a pilot ejects from a damaged AI aircraft. - -- When this happens, the pilot actually will survive, but needs to be retrieved from behind enemy lines. - -- - -- # 1) Create a CSAR task manually (code it). - -- - -- Although it is recommended to use the dispatcher, you can create a CSAR task yourself as a mission designer. - -- It is easy, as it works just like any other task setup. - -- - -- ## 1.1) Create a command center. - -- - -- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor. - -- - -- local CommandCenter = COMMANDCENTER - -- :New( HQ, "Lima" ) -- Create the CommandCenter. - -- - -- ## 1.2) Create a mission. - -- - -- Tasks work in a mission, which groups these tasks to achieve a joint mission goal. - -- A command center can govern multiple missions. - -- Create a new mission, using the @{Tasking.Mission#MISSION.New}() constructor. - -- - -- -- Declare the Mission for the Command Center. - -- local Mission = MISSION - -- :New( CommandCenter, - -- "Overlord", - -- "High", - -- "Retrieve the downed pilots.", - -- coalition.side.RED - -- ) - -- - -- ## 1.3) Create the CSAR cargo task. - -- - -- So, now that we have a command center and a mission, we now create the CSAR task. - -- We create the CSAR task using the @{#TASK_CARGO_CSAR.New}() constructor. - -- - -- Because a CSAR task will not generate the cargo itself, you'll need to create it first. - -- The cargo in this case will be the downed pilot! - -- - -- -- Here we define the "cargo set", which is a collection of cargo objects. - -- -- The cargo set will be the input for the cargo transportation task. - -- -- So a transportation object is handling a cargo set, which is automatically refreshed when new cargo is added/deleted. - -- local CargoSet = SET_CARGO:New():FilterTypes( "Pilots" ):FilterStart() - -- - -- -- Now we add cargo into the battle scene. - -- local PilotGroup = GROUP:FindByName( "Pilot" ) - -- - -- -- CARGO_GROUP can be used to setup cargo with a GROUP object underneath. - -- -- We name this group Engineers. - -- -- Note that the name of the cargo is "Engineers". - -- -- The cargoset "CargoSet" will embed all defined cargo of type "Pilots" (prefix) into its set. - -- local CargoGroup = CARGO_GROUP:New( PilotGroup, "Pilots", "Downed Pilot", 500 ) - -- - -- What is also needed, is to have a set of @{Wrapper.Group}s defined that contains the clients of the players. - -- - -- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players. - -- local GroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart() - -- - -- Now that we have a CargoSet and a GroupSet, we can now create the CSARTask manually. - -- - -- -- Declare the CSAR task. - -- local CSARTask = TASK_CARGO_CSAR - -- :New( Mission, - -- GroupSet, - -- "CSAR Pilot", - -- CargoSet, - -- "Fly behind enemy lines, and retrieve the downed pilot." - -- ) - -- - -- So you can see, setting up a CSAR task manually is a lot of work. - -- It is better you use the cargo dispatcher to generate CSAR tasks and it will work as it is intended. - -- By doing this, CSAR tasking will become a dynamic experience. - -- - -- # 2) Create a task using the @{Tasking.Task_Cargo_Dispatcher} module. - -- - -- Actually, it is better to **GENERATE** these tasks using the @{Tasking.Task_Cargo_Dispatcher} module. - -- Using the dispatcher module, transport tasks can be created much more easy. - -- - -- Find below an example how to use the TASK_CARGO_DISPATCHER class: - -- - -- - -- -- Find the HQ group. - -- HQ = GROUP:FindByName( "HQ", "Bravo" ) - -- - -- -- Create the command center with the name "Lima". - -- CommandCenter = COMMANDCENTER - -- :New( HQ, "Lima" ) - -- - -- -- Create the mission, for the command center, with the name "CSAR Mission", a "Tactical" mission, with the mission briefing "Rescue downed pilots.", for the RED coalition. - -- Mission = MISSION - -- :New( CommandCenter, "CSAR Mission", "Tactical", "Rescue downed pilots.", coalition.side.RED ) - -- - -- -- Create the SET of GROUPs containing clients (players) that will transport the cargo. - -- -- These are have a name that start with "Rescue" and are of the "red" coalition. - -- AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Rescue" ):FilterStart() - -- - -- - -- -- Here we create the TASK_CARGO_DISPATCHER object! This is where we assign the dispatcher to generate tasks in the Mission for the AttackGroups. - -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, AttackGroups ) - -- - -- - -- -- Here the task dispatcher will generate automatically CSAR tasks once a pilot ejects. - -- TaskDispatcher:StartCSARTasks( - -- "CSAR", - -- { ZONE_UNIT:New( "Hospital", STATIC:FindByName( "Hospital" ), 100 ) }, - -- "One of our pilots has ejected. Go out to Search and Rescue our pilot!\n" .. - -- "Use the radio menu to let the command center assist you with the CSAR tasking." - -- ) - -- - -- # 3) Handle cargo task events. - -- - -- When a player is picking up and deploying cargo using his carrier, events are generated by the tasks. These events can be captured and tailored with your own code. - -- - -- In order to properly capture the events and avoid mistakes using the documentation, it is advised that you execute the following actions: - -- - -- * **Copy / Paste** the code section into your script. - -- * **Change** the CLASS literal to the task object name you have in your script. - -- * Within the function, you can now **write your own code**! - -- * **IntelliSense** will recognize the type of the variables provided by the function. Note: the From, Event and To variables can be safely ignored, - -- but you need to declare them as they are automatically provided by the event handling system of MOOSE. - -- - -- You can send messages or fire off any other events within the code section. The sky is the limit! - -- - -- NOTE: CSAR tasks are actually automatically created by the TASK_CARGO_DISPATCHER. So the underlying is not really applicable for mission designers as they will use the dispatcher instead - -- of capturing these events from manually created CSAR tasks! - -- - -- ## 3.1) Handle the **CargoPickedUp** event. - -- - -- Find below an example how to tailor the **CargoPickedUp** event, generated by the CSARTask: - -- - -- function CSARTask:OnAfterCargoPickedUp( From, Event, To, TaskUnit, Cargo ) - -- - -- MESSAGE:NewType( "Unit " .. TaskUnit:GetName().. " has picked up cargo.", MESSAGE.Type.Information ):ToAll() - -- - -- end - -- - -- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has picked up a cargo object in the CarrierGroup. - -- You can use this event handler to post messages to players, or provide status updates etc. - -- - -- --- CargoPickedUp event handler OnAfter for CLASS. - -- -- @param #CLASS self - -- -- @param #string From A string that contains the "*from state name*" when the event was triggered. - -- -- @param #string Event A string that contains the "*event name*" when the event was triggered. - -- -- @param #string To A string that contains the "*to state name*" when the event was triggered. - -- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has picked up the cargo. - -- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been picked up. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object! - -- function CLASS:OnAfterCargoPickedUp( From, Event, To, TaskUnit, Cargo ) - -- - -- -- Write here your own code. - -- - -- end - -- - -- - -- ## 3.2) Handle the **CargoDeployed** event. - -- - -- Find below an example how to tailor the **CargoDeployed** event, generated by the CSARTask: - -- - -- function CSARTask:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone ) - -- - -- MESSAGE:NewType( "Unit " .. TaskUnit:GetName().. " has deployed cargo at zone " .. DeployZone:GetName(), MESSAGE.Type.Information ):ToAll() - -- - -- end - -- - -- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has deployed a cargo object from the CarrierGroup. - -- You can use this event handler to post messages to players, or provide status updates etc. - -- - -- - -- --- CargoDeployed event handler OnAfter for CLASS. - -- -- @param #CLASS self - -- -- @param #string From A string that contains the "*from state name*" when the event was triggered. - -- -- @param #string Event A string that contains the "*event name*" when the event was triggered. - -- -- @param #string To A string that contains the "*to state name*" when the event was triggered. - -- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has deployed the cargo. - -- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been deployed. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object! - -- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. - -- function CLASS:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone ) - -- - -- -- Write here your own code. - -- - -- end - -- - -- === - -- - -- @field #TASK_CARGO_CSAR - TASK_CARGO_CSAR = { - ClassName = "TASK_CARGO_CSAR", - } - - --- Instantiates a new TASK_CARGO_CSAR. - -- @param #TASK_CARGO_CSAR 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_CARGO SetCargo The scope of the cargo to be transported. - -- @param #string TaskBriefing The Cargo Task briefing. - -- @return #TASK_CARGO_CSAR self - function TASK_CARGO_CSAR:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing ) - local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "CSAR", TaskBriefing ) ) -- #TASK_CARGO_CSAR - self:F() - - Mission:AddTask( self ) - - - -- Events - - self:AddTransition( "*", "CargoPickedUp", "*" ) - self:AddTransition( "*", "CargoDeployed", "*" ) - - self:F( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } ) - - --- OnAfter Transition Handler for Event CargoPickedUp. - -- @function [parent=#TASK_CARGO_CSAR] OnAfterCargoPickedUp - -- @param #TASK_CARGO_CSAR self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Cargo.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - - --- OnAfter Transition Handler for Event CargoDeployed. - -- @function [parent=#TASK_CARGO_CSAR] OnAfterCargoDeployed - -- @param #TASK_CARGO_CSAR self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Cargo.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - - local Fsm = self:GetUnitProcess() - - local CargoReport = REPORT:New( "Rescue a downed pilot from the following position:") - - SetCargo:ForEachCargo( - --- @param Cargo.Cargo#CARGO Cargo - function( Cargo ) - local CargoType = Cargo:GetType() - local CargoName = Cargo:GetName() - local CargoCoordinate = Cargo:GetCoordinate() - CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) ) - end - ) - - self:SetBriefing( - TaskBriefing or - CargoReport:Text() - ) - - - return self - end - - - - function TASK_CARGO_CSAR:ReportOrder( ReportGroup ) - - return 0 - end - - - --- - -- @param #TASK_CARGO_CSAR self - -- @return #boolean - function TASK_CARGO_CSAR:IsAllCargoTransported() - - local CargoSet = self:GetCargoSet() - local Set = CargoSet:GetSet() - - local DeployZones = self:GetDeployZones() - - local CargoDeployed = true - - -- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ). - for CargoID, CargoData in pairs( Set ) do - local Cargo = CargoData -- Cargo.Cargo#CARGO - - self:F( { Cargo = Cargo:GetName(), CargoDeployed = Cargo:IsDeployed() } ) - - if Cargo:IsDeployed() then - --- -- Loop the DeployZones set for the TASK_CARGO_CSAR. --- for DeployZoneID, DeployZone in pairs( DeployZones ) do --- --- -- If all cargo is in one of the deploy zones, then all is good. --- self:T( { Cargo.CargoObject } ) --- if Cargo:IsInZone( DeployZone ) == false then --- CargoDeployed = false --- end --- end - else - CargoDeployed = false - end - end - - self:F( { CargoDeployed = CargoDeployed } ) - - return CargoDeployed - end - - --- @param #TASK_CARGO_CSAR self - function TASK_CARGO_CSAR:onafterGoal( TaskUnit, From, Event, To ) - local CargoSet = self.CargoSet - - if self:IsAllCargoTransported() then - self:Success() - end - - self:__Goal( -10 ) - end - -end - diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua deleted file mode 100644 index b79a098bd..000000000 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ /dev/null @@ -1,921 +0,0 @@ ---- **Tasking** - Creates and manages player TASK_CARGO tasks. --- --- The **TASK_CARGO_DISPATCHER** allows you to setup various tasks for let human --- players transport cargo as part of a task. --- --- ![Banner Image](..\Images\deprecated.png) --- --- The cargo dispatcher will implement for you mechanisms to create cargo transportation tasks: --- --- * As setup by the mission designer. --- * Dynamically create CSAR missions (when a pilot is downed as part of a downed plane). --- * Dynamically spawn new cargo and create cargo taskings! --- --- --- --- **Specific features:** --- --- * Creates a task to transport @{Cargo.Cargo} to and between deployment zones. --- * Derived from the TASK_CARGO class, which is derived from the TASK class. --- * Orchestrate the task flow, so go from Planned to Assigned to Success, Failed or Cancelled. --- * Co-operation tasking, so a player joins a group of players executing the same task. --- --- --- **A complete task menu system to allow players to:** --- --- * Join the task, abort the task. --- * Mark the task location on the map. --- * Provide details of the target. --- * Route to the cargo. --- * Route to the deploy zones. --- * Load/Unload cargo. --- * Board/Unboard cargo. --- * Slingload cargo. --- * Display the task briefing. --- --- --- **A complete mission menu system to allow players to:** --- --- * Join a task, abort the task. --- * Display task reports. --- * Display mission statistics. --- * Mark the task locations on the map. --- * Provide details of the targets. --- * Display the mission briefing. --- * Provide status updates as retrieved from the command center. --- * Automatically assign a random task as part of a mission. --- * Manually assign a specific task as part of a mission. --- --- --- **A settings system, using the settings menu:** --- --- * Tweak the duration of the display of messages. --- * Switch between metric and imperial measurement system. --- * Switch between coordinate formats used in messages: BR, BRA, LL DMS, LL DDM, MGRS. --- * Different settings modes for A2G and A2A operations. --- * Various other options. --- --- # Developer Note --- --- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE --- Therefore, this class is considered to be deprecated --- --- === --- --- ### Author: **FlightControl** --- --- ### Contributions: --- --- === --- --- @module Tasking.Task_Cargo_Dispatcher --- @image Task_Cargo_Dispatcher.JPG - -do -- TASK_CARGO_DISPATCHER - - --- TASK_CARGO_DISPATCHER class. - -- @type TASK_CARGO_DISPATCHER - -- @extends Tasking.Task_Manager#TASK_MANAGER - -- @field TASK_CARGO_DISPATCHER.CSAR CSAR - -- @field Core.Set#SET_ZONE SetZonesCSAR - - -- @type TASK_CARGO_DISPATCHER.CSAR - -- @field Wrapper.Unit#UNIT PilotUnit - -- @field Tasking.Task#TASK Task - - - --- Implements the dynamic dispatching of cargo tasks. - -- - -- The **TASK_CARGO_DISPATCHER** allows you to setup various tasks for let human - -- players transport cargo as part of a task. - -- - -- There are currently **two types of tasks** that can be constructed: - -- - -- * A **normal cargo transport** task, which tasks humans to transport cargo from a location towards a deploy zone. - -- * A **CSAR** cargo transport task. CSAR tasks are **automatically generated** when a friendly (AI) plane is downed and the friendly pilot ejects... - -- You as a player (the helo pilot) can go out in the battlefield, fly behind enemy lines, and rescue the pilot (back to a deploy zone). - -- - -- Let's explore **step by step** how to setup the task cargo dispatcher. - -- - -- # 1. Setup a mission environment. - -- - -- It is easy, as it works just like any other task setup, so setup a command center and a mission. - -- - -- ## 1.1. Create a command center. - -- - -- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor. - -- - -- local CommandCenter = COMMANDCENTER - -- :New( HQ, "Lima" ) -- Create the CommandCenter. - -- - -- ## 1.2. Create a mission. - -- - -- Tasks work in a mission, which groups these tasks to achieve a joint mission goal. - -- A command center can govern multiple missions. - -- Create a new mission, using the @{Tasking.Mission#MISSION.New}() constructor. - -- - -- -- Declare the Mission for the Command Center. - -- local Mission = MISSION - -- :New( CommandCenter, - -- "Overlord", - -- "High", - -- "Transport the cargo.", - -- coalition.side.RED - -- ) - -- - -- - -- # 2. Dispatch a **transport cargo** task. - -- - -- So, now that we have a command center and a mission, we now create the transport task. - -- We create the transport task using the @{#TASK_CARGO_DISPATCHER.AddTransportTask}() constructor. - -- - -- ## 2.1. Create the cargo in the mission. - -- - -- Because a transport task will not generate the cargo itself, you'll need to create it first. - -- - -- -- Here we define the "cargo set", which is a collection of cargo objects. - -- -- The cargo set will be the input for the cargo transportation task. - -- -- So a transportation object is handling a cargo set, which is automatically updated when new cargo is added/deleted. - -- local WorkmaterialsCargoSet = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() - -- - -- -- Now we add cargo into the battle scene. - -- local PilotGroup = GROUP:FindByName( "Engineers" ) - -- - -- -- CARGO_GROUP can be used to setup cargo with a GROUP object underneath. - -- -- We name the type of this group "Workmaterials", so that this cargo group will be included within the WorkmaterialsCargoSet. - -- -- Note that the name of the cargo is "Engineer Team 1". - -- local CargoGroup = CARGO_GROUP:New( PilotGroup, "Workmaterials", "Engineer Team 1", 500 ) - -- - -- What is also needed, is to have a set of @{Wrapper.Group}s defined that contains the clients of the players. - -- - -- -- Allocate the Transport, which are the helicopters to retrieve the pilot, that can be manned by players. - -- -- The name of these helicopter groups containing one client begins with "Transport", as modelled within the mission editor. - -- local PilotGroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart() - -- - -- ## 2.2. Setup the cargo transport task. - -- - -- First, we need to create a TASK_CARGO_DISPATCHER object. - -- - -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, PilotGroupSet ) - -- - -- So, the variable `TaskDispatcher` will contain the object of class TASK_CARGO_DISPATCHER, which will allow you to dispatch cargo transport tasks: - -- - -- * for mission `Mission`. - -- * for the group set `PilotGroupSet`. - -- - -- Now that we have `TaskDispatcher` object, we can now **create the TransportTask**, using the @{#TASK_CARGO_DISPATCHER.AddTransportTask}() method! - -- - -- local TransportTask = TaskDispatcher:AddTransportTask( - -- "Transport workmaterials", - -- WorkmaterialsCargoSet, - -- "Transport the workers, engineers and the equipment near the Workplace." ) - -- - -- As a result of this code, the `TransportTask` (returned) variable will contain an object of @{#TASK_CARGO_TRANSPORT}! - -- We pass to the method the title of the task, and the `WorkmaterialsCargoSet`, which is the set of cargo groups to be transported! - -- This object can also be used to setup additional things, or to control this specific task with special actions. - -- - -- And you're done! As you can see, it is a bit of work, but the reward is great. - -- And, because all this is done using program interfaces, you can build a mission with a **dynamic cargo transport task mechanism** yourself! - -- Based on events happening within your mission, you can use the above methods to create new cargo, and setup a new task for cargo transportation to a group of players! - -- - -- - -- # 3. Dispatch CSAR tasks. - -- - -- CSAR tasks can be dynamically created when a friendly pilot ejects, or can be created manually. - -- We'll explore both options. - -- - -- ## 3.1. CSAR task dynamic creation. - -- - -- Because there is an "event" in a running simulation that creates CSAR tasks, the method @{#TASK_CARGO_DISPATCHER.StartCSARTasks}() will create automatically: - -- - -- 1. a new downed pilot at the location where the plane was shot - -- 2. declare that pilot as cargo - -- 3. creates a CSAR task automatically to retrieve that pilot - -- 4. requires deploy zones to be specified where to transport the downed pilot to, in order to complete that task. - -- - -- You create a CSAR task dynamically in a very easy way: - -- - -- TaskDispatcher:StartCSARTasks( - -- "CSAR", - -- { ZONE_UNIT:New( "Hospital", STATIC:FindByName( "Hospital" ), 100 ) }, - -- "One of our pilots has ejected. Go out to Search and Rescue our pilot!\n" .. - -- "Use the radio menu to let the command center assist you with the CSAR tasking." - -- ) - -- - -- The method @{#TASK_CARGO_DISPATCHER.StopCSARTasks}() will automatically stop with the creation of CSAR tasks when friendly pilots eject. - -- - -- **Remarks:** - -- - -- * the ZONE_UNIT can also be a ZONE, or a ZONE_POLYGON object, or any other ZONE_ object! - -- * you can declare the array of zones in another variable, or course! - -- - -- - -- ## 3.2. CSAR task manual creation. - -- - -- We create the CSAR task using the @{#TASK_CARGO_DISPATCHER.AddCSARTask}() constructor. - -- - -- The method will create a new CSAR task, and will generate the pilots cargo itself, at the specified coordinate. - -- - -- What is first needed, is to have a set of @{Wrapper.Group}s defined that contains the clients of the players. - -- - -- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players. - -- local GroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart() - -- - -- We need to create a TASK_CARGO_DISPATCHER object. - -- - -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, GroupSet ) - -- - -- So, the variable `TaskDispatcher` will contain the object of class TASK_CARGO_DISPATCHER, which will allow you to dispatch cargo CSAR tasks: - -- - -- * for mission `Mission`. - -- * for the group of players (pilots) captured within the `GroupSet` (those groups with a name starting with `"Transport"`). - -- - -- Now that we have a PilotsCargoSet and a GroupSet, we can now create the CSAR task manually. - -- - -- -- Declare the CSAR task. - -- local CSARTask = TaskDispatcher:AddCSARTask( - -- "CSAR Task", - -- Coordinate, - -- 270, - -- "Bring the pilot back!" - -- ) - -- - -- As a result of this code, the `CSARTask` (returned) variable will contain an object of @{#TASK_CARGO_CSAR}! - -- We pass to the method the title of the task, and the `WorkmaterialsCargoSet`, which is the set of cargo groups to be transported! - -- This object can also be used to setup additional things, or to control this specific task with special actions. - -- Note that when you declare a CSAR task manually, you'll still need to specify a deployment zone! - -- - -- # 4. Setup the deploy zone(s). - -- - -- The task cargo dispatcher also foresees methods to setup the deployment zones to where the cargo needs to be transported! - -- - -- There are two levels on which deployment zones can be configured: - -- - -- * Default deploy zones: The TASK_CARGO_DISPATCHER object can have default deployment zones, which will apply over all tasks active in the task dispatcher. - -- * Task specific deploy zones: The TASK_CARGO_DISPATCHER object can have specific deployment zones which apply to a specific task only! - -- - -- Note that for Task specific deployment zones, there are separate deployment zone creation methods per task type! - -- - -- ## 4.1. Setup default deploy zones. - -- - -- Use the @{#TASK_CARGO_DISPATCHER.SetDefaultDeployZone}() to setup one deployment zone, and @{#TASK_CARGO_DISPATCHER.SetDefaultDeployZones}() to setup multiple default deployment zones in one call. - -- - -- ## 4.2. Setup task specific deploy zones for a **transport task**. - -- - -- Use the @{#TASK_CARGO_DISPATCHER.SetTransportDeployZone}() to setup one deployment zone, and @{#TASK_CARGO_DISPATCHER.SetTransportDeployZones}() to setup multiple default deployment zones in one call. - -- - -- ## 4.3. Setup task specific deploy zones for a **CSAR task**. - -- - -- Use the @{#TASK_CARGO_DISPATCHER.SetCSARDeployZone}() to setup one deployment zone, and @{#TASK_CARGO_DISPATCHER.SetCSARDeployZones}() to setup multiple default deployment zones in one call. - -- - -- ## 4.4. **CSAR ejection zones**. - -- - -- Setup a set of zones where the pilots will only eject and a task is created for CSAR. When such a set of zones is given, any ejection outside those zones will not result in a pilot created for CSAR! - -- - -- Use the @{#TASK_CARGO_DISPATCHER.SetCSARZones}() to setup the set of zones. - -- - -- ## 4.5. **CSAR ejection maximum**. - -- - -- Setup how many pilots will eject the maximum. This to avoid an overload of CSAR tasks being created :-) The default is endless CSAR tasks. - -- - -- Use the @{#TASK_CARGO_DISPATCHER.SetMaxCSAR}() to setup the maximum of pilots that will eject for CSAR. - -- - -- - -- # 5) Handle cargo task events. - -- - -- When a player is picking up and deploying cargo using his carrier, events are generated by the dispatcher. These events can be captured and tailored with your own code. - -- - -- In order to properly capture the events and avoid mistakes using the documentation, it is advised that you execute the following actions: - -- - -- * **Copy / Paste** the code section into your script. - -- * **Change** the CLASS literal to the task object name you have in your script. - -- * Within the function, you can now **write your own code**! - -- * **IntelliSense** will recognize the type of the variables provided by the function. Note: the From, Event and To variables can be safely ignored, - -- but you need to declare them as they are automatically provided by the event handling system of MOOSE. - -- - -- You can send messages or fire off any other events within the code section. The sky is the limit! - -- - -- First, we need to create a TASK_CARGO_DISPATCHER object. - -- - -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, PilotGroupSet ) - -- - -- Second, we create a new cargo transport task for the transportation of workmaterials. - -- - -- TaskDispatcher:AddTransportTask( - -- "Transport workmaterials", - -- WorkmaterialsCargoSet, - -- "Transport the workers, engineers and the equipment near the Workplace." ) - -- - -- Note that we don't really need to keep the resulting task, it is kept internally also in the dispatcher. - -- - -- Using the `TaskDispatcher` object, we can now cpature the CargoPickedUp and CargoDeployed events. - -- - -- ## 5.1) Handle the **CargoPickedUp** event. - -- - -- Find below an example how to tailor the **CargoPickedUp** event, generated by the `TaskDispatcher`: - -- - -- function TaskDispatcher:OnAfterCargoPickedUp( From, Event, To, Task, TaskPrefix, TaskUnit, Cargo ) - -- - -- MESSAGE:NewType( "Unit " .. TaskUnit:GetName().. " has picked up cargo for task " .. Task:GetName() .. ".", MESSAGE.Type.Information ):ToAll() - -- - -- end - -- - -- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has picked up a cargo object in the CarrierGroup. - -- You can use this event handler to post messages to players, or provide status updates etc. - -- - -- --- CargoPickedUp event handler OnAfter for CLASS. - -- -- @param #CLASS self - -- -- @param #string From A string that contains the "*from state name*" when the event was triggered. - -- -- @param #string Event A string that contains the "*event name*" when the event was triggered. - -- -- @param #string To A string that contains the "*to state name*" when the event was triggered. - -- -- @param Tasking.Task_Cargo#TASK_CARGO Task The cargo task for which the cargo has been picked up. Note that this will be a derived TAKS_CARGO object! - -- -- @param #string TaskPrefix The prefix of the task that was provided when the task was created. - -- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has picked up the cargo. - -- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been picked up. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object! - -- function CLASS:OnAfterCargoPickedUp( From, Event, To, Task, TaskPrefix, TaskUnit, Cargo ) - -- - -- -- Write here your own code. - -- - -- end - -- - -- - -- ## 5.2) Handle the **CargoDeployed** event. - -- - -- Find below an example how to tailor the **CargoDeployed** event, generated by the `TaskDispatcher`: - -- - -- function WorkplaceTask:OnAfterCargoDeployed( From, Event, To, Task, TaskPrefix, TaskUnit, Cargo, DeployZone ) - -- - -- MESSAGE:NewType( "Unit " .. TaskUnit:GetName().. " has deployed cargo at zone " .. DeployZone:GetName() .. " for task " .. Task:GetName() .. ".", MESSAGE.Type.Information ):ToAll() - -- - -- Helos[ math.random(1,#Helos) ]:Spawn() - -- EnemyHelos[ math.random(1,#EnemyHelos) ]:Spawn() - -- end - -- - -- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has deployed a cargo object from the CarrierGroup. - -- You can use this event handler to post messages to players, or provide status updates etc. - -- - -- - -- --- CargoDeployed event handler OnAfter for CLASS. - -- -- @param #CLASS self - -- -- @param #string From A string that contains the "*from state name*" when the event was triggered. - -- -- @param #string Event A string that contains the "*event name*" when the event was triggered. - -- -- @param #string To A string that contains the "*to state name*" when the event was triggered. - -- -- @param Tasking.Task_Cargo#TASK_CARGO Task The cargo task for which the cargo has been deployed. Note that this will be a derived TAKS_CARGO object! - -- -- @param #string TaskPrefix The prefix of the task that was provided when the task was created. - -- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has deployed the cargo. - -- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been deployed. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object! - -- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. - -- function CLASS:OnAfterCargoDeployed( From, Event, To, Task, TaskPrefix, TaskUnit, Cargo, DeployZone ) - -- - -- -- Write here your own code. - -- - -- end - -- - -- - -- - -- @field #TASK_CARGO_DISPATCHER - TASK_CARGO_DISPATCHER = { - ClassName = "TASK_CARGO_DISPATCHER", - Mission = nil, - Tasks = {}, - CSAR = {}, - CSARSpawned = 0, - - Transport = {}, - TransportCount = 0, - } - - - --- TASK_CARGO_DISPATCHER constructor. - -- @param #TASK_CARGO_DISPATCHER self - -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. - -- @param Core.Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. - -- @return #TASK_CARGO_DISPATCHER self - function TASK_CARGO_DISPATCHER:New( Mission, SetGroup ) - - -- Inherits from DETECTION_MANAGER - local self = BASE:Inherit( self, TASK_MANAGER:New( SetGroup ) ) -- #TASK_CARGO_DISPATCHER - - self.Mission = Mission - - self:AddTransition( "Started", "Assign", "Started" ) - self:AddTransition( "Started", "CargoPickedUp", "Started" ) - self:AddTransition( "Started", "CargoDeployed", "Started" ) - - --- OnAfter Transition Handler for Event Assign. - -- @function [parent=#TASK_CARGO_DISPATCHER] OnAfterAssign - -- @param #TASK_CARGO_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:SetCSARRadius() - self:__StartTasks( 5 ) - - self.MaxCSAR = nil - self.CountCSAR = 0 - - -- For CSAR missions, we process the event when a pilot ejects. - - self:HandleEvent( EVENTS.Ejection ) - - return self - end - - - --- Sets the set of zones were pilots will only be spawned (eject) when the planes crash. - -- Note that because this is a set of zones, the MD can create the zones dynamically within his mission! - -- Just provide a set of zones, see usage, but find the tactical situation here: - -- - -- ![CSAR Zones](../Tasking/CSAR_Zones.JPG) - -- - -- @param #TASK_CARGO_DISPATCHER self - -- @param Core.Set#SET_ZONE SetZonesCSAR The set of zones where pilots will only be spawned for CSAR when they eject. - -- @usage - -- - -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, AttackGroups ) - -- - -- -- Use this call to pass the set of zones. - -- -- Note that you can create the set of zones inline, because the FilterOnce method (and other SET_ZONE methods return self). - -- -- So here the zones can be created as normal trigger zones (MOOSE creates a collection of ZONE objects when teh mission starts of all trigger zones). - -- -- Just name them as CSAR zones here. - -- TaskDispatcher:SetCSARZones( SET_ZONE:New():FilterPrefixes("CSAR"):FilterOnce() ) - -- - function TASK_CARGO_DISPATCHER:SetCSARZones( SetZonesCSAR ) - - self.SetZonesCSAR = SetZonesCSAR - - end - - - --- Sets the maximum of pilots that will be spawned (eject) when the planes crash. - -- @param #TASK_CARGO_DISPATCHER self - -- @param #number MaxCSAR The maximum of pilots that will eject for CSAR. - -- @usage - -- - -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, AttackGroups ) - -- - -- -- Use this call to the maximum of CSAR to 10. - -- TaskDispatcher:SetMaxCSAR( 10 ) - -- - function TASK_CARGO_DISPATCHER:SetMaxCSAR( MaxCSAR ) - - self.MaxCSAR = MaxCSAR - - end - - - - --- Handle the event when a pilot ejects. - -- @param #TASK_CARGO_DISPATCHER self - -- @param Core.Event#EVENTDATA EventData - function TASK_CARGO_DISPATCHER:OnEventEjection( EventData ) - self:F( { EventData = EventData } ) - - if self.CSARTasks == true then - - local CSARCoordinate = EventData.IniUnit:GetCoordinate() - local CSARCoalition = EventData.IniUnit:GetCoalition() - local CSARCountry = EventData.IniUnit:GetCountry() - local CSARHeading = EventData.IniUnit:GetHeading() - - -- Only add a CSAR task if the coalition of the mission is equal to the coalition of the ejected unit. - if CSARCoalition == self.Mission:GetCommandCenter():GetCoalition() then - -- And only add if the eject is in one of the zones, if defined. - if not self.SetZonesCSAR or ( self.SetZonesCSAR and self.SetZonesCSAR:IsCoordinateInZone( CSARCoordinate ) ) then - -- And only if the maximum of pilots is not reached that ejected! - if not self.MaxCSAR or ( self.MaxCSAR and self.CountCSAR < self.MaxCSAR ) then - local CSARTaskName = self:AddCSARTask( self.CSARTaskName, CSARCoordinate, CSARHeading, CSARCountry, self.CSARBriefing ) - self:SetCSARDeployZones( CSARTaskName, self.CSARDeployZones ) - self.CountCSAR = self.CountCSAR + 1 - end - end - end - end - - return self - end - - - --- Define one default deploy zone for all the cargo tasks. - -- @param #TASK_CARGO_DISPATCHER self - -- @param DefaultDeployZone A default deploy zone. - -- @return #TASK_CARGO_DISPATCHER - function TASK_CARGO_DISPATCHER:SetDefaultDeployZone( DefaultDeployZone ) - - self.DefaultDeployZones = { DefaultDeployZone } - - return self - end - - - --- Define the deploy zones for all the cargo tasks. - -- @param #TASK_CARGO_DISPATCHER self - -- @param DefaultDeployZones A list of the deploy zones. - -- @return #TASK_CARGO_DISPATCHER - -- - function TASK_CARGO_DISPATCHER:SetDefaultDeployZones( DefaultDeployZones ) - - self.DefaultDeployZones = DefaultDeployZones - - return self - end - - - --- Start the generation of CSAR tasks to retrieve a downed pilots. - -- You need to specify a task briefing, a task name, default deployment zone(s). - -- This method can only be used once! - -- @param #TASK_CARGO_DISPATCHER self - -- @param #string CSARTaskName The CSAR task name. - -- @param #string CSARDeployZones The zones to where the CSAR deployment should be directed. - -- @param #string CSARBriefing The briefing of the CSAR tasks. - -- @return #TASK_CARGO_DISPATCHER - function TASK_CARGO_DISPATCHER:StartCSARTasks( CSARTaskName, CSARDeployZones, CSARBriefing) - - if not self.CSARTasks then - self.CSARTasks = true - self.CSARTaskName = CSARTaskName - self.CSARDeployZones = CSARDeployZones - self.CSARBriefing = CSARBriefing - else - error( "TASK_CARGO_DISPATCHER: The generation of CSAR tasks has already started." ) - end - - return self - end - - - --- Stop the generation of CSAR tasks to retrieve a downed pilots. - -- @param #TASK_CARGO_DISPATCHER self - -- @return #TASK_CARGO_DISPATCHER - function TASK_CARGO_DISPATCHER:StopCSARTasks() - - if self.CSARTasks then - self.CSARTasks = nil - self.CSARTaskName = nil - self.CSARDeployZones = nil - self.CSARBriefing = nil - else - error( "TASK_CARGO_DISPATCHER: The generation of CSAR tasks was not yet started." ) - end - - return self - end - - - --- Add a CSAR task to retrieve a downed pilot. - -- You need to specify a coordinate from where the pilot will be spawned to be rescued. - -- @param #TASK_CARGO_DISPATCHER self - -- @param #string CSARTaskPrefix (optional) The prefix of the CSAR task. - -- @param Core.Point#COORDINATE CSARCoordinate The coordinate where a downed pilot will be spawned. - -- @param #number CSARHeading The heading of the pilot in degrees. - -- @param #DCSCountry CSARCountry The country ID of the pilot that will be spawned. - -- @param #string CSARBriefing The briefing of the CSAR task. - -- @return #string The CSAR Task Name as a string. The Task Name is the main key and is shown in the task list of the Mission Tasking menu. - -- @usage - -- - -- -- Add a CSAR task to rescue a downed pilot from within a coordinate. - -- local Coordinate = PlaneUnit:GetPointVec2() - -- TaskA2ADispatcher:AddCSARTask( "CSAR Task", Coordinate ) - -- - -- -- Add a CSAR task to rescue a downed pilot from within a coordinate of country RUSSIA, which is pointing to the west (270°). - -- local Coordinate = PlaneUnit:GetPointVec2() - -- TaskA2ADispatcher:AddCSARTask( "CSAR Task", Coordinate, 270, Country.RUSSIA ) - -- - function TASK_CARGO_DISPATCHER:AddCSARTask( CSARTaskPrefix, CSARCoordinate, CSARHeading, CSARCountry, CSARBriefing ) - - local CSARCoalition = self.Mission:GetCommandCenter():GetCoalition() - - CSARHeading = CSARHeading or 0 - CSARCountry = CSARCountry or self.Mission:GetCommandCenter():GetCountry() - - self.CSARSpawned = self.CSARSpawned + 1 - - local CSARTaskName = string.format( ( CSARTaskPrefix or "CSAR" ) .. ".%03d", self.CSARSpawned ) - - -- Create the CSAR Pilot SPAWN object. - -- Let us create the Template for the replacement Pilot :-) - local Template = { - ["visible"] = false, - ["hidden"] = false, - ["task"] = "Ground Nothing", - ["name"] = string.format( "CSAR Pilot#%03d", self.CSARSpawned ), - ["x"] = CSARCoordinate.x, - ["y"] = CSARCoordinate.z, - ["units"] = - { - [1] = - { - ["type"] = ( CSARCoalition == coalition.side.BLUE ) and "Soldier M4" or "Infantry AK", - ["name"] = string.format( "CSAR Pilot#%03d-01", self.CSARSpawned ), - ["skill"] = "Excellent", - ["playerCanDrive"] = false, - ["x"] = CSARCoordinate.x, - ["y"] = CSARCoordinate.z, - ["heading"] = CSARHeading, - }, -- end of [1] - }, -- end of ["units"] - } - - local CSARGroup = GROUP:NewTemplate( Template, CSARCoalition, Group.Category.GROUND, CSARCountry ) - - self.CSAR[CSARTaskName] = {} - self.CSAR[CSARTaskName].PilotGroup = CSARGroup - self.CSAR[CSARTaskName].Briefing = CSARBriefing - self.CSAR[CSARTaskName].Task = nil - self.CSAR[CSARTaskName].TaskPrefix = CSARTaskPrefix - - return CSARTaskName - end - - - --- Define the radius to when a CSAR task will be generated for any downed pilot within range of the nearest CSAR airbase. - -- @param #TASK_CARGO_DISPATCHER self - -- @param #number CSARRadius (Optional, Default = 50000) The radius in meters to decide whether a CSAR needs to be created. - -- @return #TASK_CARGO_DISPATCHER - -- @usage - -- - -- -- Set 20km as the radius to CSAR any downed pilot within range of the nearest CSAR airbase. - -- TaskA2ADispatcher:SetEngageRadius( 20000 ) - -- - -- -- Set 50km as the radius to to CSAR any downed pilot within range of the nearest CSAR airbase. - -- TaskA2ADispatcher:SetEngageRadius() -- 50000 is the default value. - -- - function TASK_CARGO_DISPATCHER:SetCSARRadius( CSARRadius ) - - self.CSARRadius = CSARRadius or 50000 - - return self - end - - - --- Define one deploy zone for the CSAR tasks. - -- @param #TASK_CARGO_DISPATCHER self - -- @param #string CSARTaskName (optional) The name of the CSAR task. - -- @param CSARDeployZone A CSAR deploy zone. - -- @return #TASK_CARGO_DISPATCHER - function TASK_CARGO_DISPATCHER:SetCSARDeployZone( CSARTaskName, CSARDeployZone ) - - if CSARTaskName then - self.CSAR[CSARTaskName].DeployZones = { CSARDeployZone } - end - - return self - end - - - --- Define the deploy zones for the CSAR tasks. - -- @param #TASK_CARGO_DISPATCHER self - -- @param #string CSARTaskName (optional) The name of the CSAR task. - -- @param CSARDeployZones A list of the CSAR deploy zones. - -- @return #TASK_CARGO_DISPATCHER - -- - function TASK_CARGO_DISPATCHER:SetCSARDeployZones( CSARTaskName, CSARDeployZones ) - - if CSARTaskName and self.CSAR[CSARTaskName] then - self.CSAR[CSARTaskName].DeployZones = CSARDeployZones - end - - return self - end - - - --- Add a Transport task to transport cargo from fixed locations to a deployment zone. - -- @param #TASK_CARGO_DISPATCHER self - -- @param #string TaskPrefix (optional) The prefix of the transport task. - -- This prefix will be appended with a . + a number of 3 digits. - -- If no TaskPrefix is given, then "Transport" will be used as the prefix. - -- @param Core.Set#SET_CARGO SetCargo The SetCargo to be transported. - -- @param #string Briefing The briefing of the task transport to be shown to the player. - -- @param #boolean Silent If true don't send a message that a new task is available. - -- @return Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT - -- @usage - -- - -- -- Add a Transport task to transport cargo of different types to a Transport Deployment Zone. - -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, TransportGroups ) - -- - -- local CargoSetWorkmaterials = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() - -- local EngineerCargoGroup = CARGO_GROUP:New( GROUP:FindByName( "Engineers" ), "Workmaterials", "Engineers", 250 ) - -- local ConcreteCargo = CARGO_SLINGLOAD:New( STATIC:FindByName( "Concrete" ), "Workmaterials", "Concrete", 150, 50 ) - -- local CrateCargo = CARGO_CRATE:New( STATIC:FindByName( "Crate" ), "Workmaterials", "Crate", 150, 50 ) - -- local EnginesCargo = CARGO_CRATE:New( STATIC:FindByName( "Engines" ), "Workmaterials", "Engines", 150, 50 ) - -- local MetalCargo = CARGO_CRATE:New( STATIC:FindByName( "Metal" ), "Workmaterials", "Metal", 150, 50 ) - -- - -- -- Here we add the task. We name the task "Build a Workplace". - -- -- We provide the CargoSetWorkmaterials, and a briefing as the 2nd and 3rd parameter. - -- -- The :AddTransportTask() returns a Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT object, which we keep as a reference for further actions. - -- -- The WorkplaceTask holds the created and returned Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT object. - -- local WorkplaceTask = TaskDispatcher:AddTransportTask( "Build a Workplace", CargoSetWorkmaterials, "Transport the workers, engineers and the equipment near the Workplace." ) - -- - -- -- Here we set a TransportDeployZone. We use the WorkplaceTask as the reference, and provide a ZONE object. - -- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) ) - -- - function TASK_CARGO_DISPATCHER:AddTransportTask( TaskPrefix, SetCargo, Briefing, Silent ) - - self.TransportCount = self.TransportCount + 1 - - local verbose = Silent or false - - local TaskName = string.format( ( TaskPrefix or "Transport" ) .. ".%03d", self.TransportCount ) - - self.Transport[TaskName] = {} - self.Transport[TaskName].SetCargo = SetCargo - self.Transport[TaskName].Briefing = Briefing - self.Transport[TaskName].Task = nil - self.Transport[TaskName].TaskPrefix = TaskPrefix - - self:ManageTasks(verbose) - - return self.Transport[TaskName] and self.Transport[TaskName].Task - end - - - --- Define one deploy zone for the Transport tasks. - -- @param #TASK_CARGO_DISPATCHER self - -- @param Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT Task The name of the Transport task. - -- @param TransportDeployZone A Transport deploy zone. - -- @return #TASK_CARGO_DISPATCHER - -- @usage - -- - -- - function TASK_CARGO_DISPATCHER:SetTransportDeployZone( Task, TransportDeployZone ) - - if self.Transport[Task.TaskName] then - self.Transport[Task.TaskName].DeployZones = { TransportDeployZone } - else - error( "Task does not exist" ) - end - - self:ManageTasks() - - return self - end - - - --- Define the deploy zones for the Transport tasks. - -- @param #TASK_CARGO_DISPATCHER self - -- @param Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT Task The name of the Transport task. - -- @param TransportDeployZones A list of the Transport deploy zones. - -- @return #TASK_CARGO_DISPATCHER - -- - function TASK_CARGO_DISPATCHER:SetTransportDeployZones( Task, TransportDeployZones ) - - if self.Transport[Task.TaskName] then - self.Transport[Task.TaskName].DeployZones = TransportDeployZones - else - error( "Task does not exist" ) - end - - self:ManageTasks() - - return self - end - - --- Evaluates of a CSAR task needs to be started. - -- @param #TASK_CARGO_DISPATCHER self - -- @return Core.Set#SET_CARGO The SetCargo to be rescued. - -- @return #nil If there is no CSAR task required. - function TASK_CARGO_DISPATCHER:EvaluateCSAR( CSARUnit ) - - local CSARCargo = CARGO_GROUP:New( CSARUnit, "Pilot", CSARUnit:GetName(), 80, 1500, 10 ) - - local SetCargo = SET_CARGO:New() - SetCargo:AddCargosByName( CSARUnit:GetName() ) - - SetCargo:Flush(self) - - return SetCargo - - end - - - - --- Assigns tasks to the @{Core.Set#SET_GROUP}. - -- @param #TASK_CARGO_DISPATCHER self - -- @param #boolean Silent Announce new task (nil/false) or not (true). - -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. - function TASK_CARGO_DISPATCHER:ManageTasks(Silent) - self:F() - local verbose = Silent and true - local AreaMsg = {} - local TaskMsg = {} - local ChangeMsg = {} - - local Mission = self.Mission - - if Mission:IsIDLE() or Mission:IsENGAGED() then - - local TaskReport = REPORT:New() - - -- Checking the task queue for the dispatcher, and removing any obsolete task! - for TaskIndex, TaskData in pairs( self.Tasks ) do - local Task = TaskData -- Tasking.Task#TASK - if Task:IsStatePlanned() then - -- Here we need to check if the pilot is still existing. --- local DetectedItem = Detection:GetDetectedItemByIndex( TaskIndex ) --- if not DetectedItem then --- local TaskText = Task:GetName() --- for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do --- Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2A task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup ) --- end --- Task = self:RemoveTask( TaskIndex ) --- end - end - end - - -- Now that all obsolete tasks are removed, loop through the CSAR pilots. - for CSARName, CSAR in pairs( self.CSAR ) do - - if not CSAR.Task then - -- New CSAR Task - local SetCargo = self:EvaluateCSAR( CSAR.PilotGroup ) - CSAR.Task = TASK_CARGO_CSAR:New( Mission, self.SetGroup, CSARName, SetCargo, CSAR.Briefing ) - CSAR.Task.TaskPrefix = CSAR.TaskPrefix -- We keep the TaskPrefix for further reference! - Mission:AddTask( CSAR.Task ) - TaskReport:Add( CSARName ) - if CSAR.DeployZones then - CSAR.Task:SetDeployZones( CSAR.DeployZones or {} ) - else - CSAR.Task:SetDeployZones( self.DefaultDeployZones or {} ) - end - - -- Now broadcast the onafterCargoPickedUp event to the Task Cargo Dispatcher. - function CSAR.Task.OnAfterCargoPickedUp( Task, From, Event, To, TaskUnit, Cargo ) - self:CargoPickedUp( Task, Task.TaskPrefix, TaskUnit, Cargo ) - end - - -- Now broadcast the onafterCargoDeployed event to the Task Cargo Dispatcher. - function CSAR.Task.OnAfterCargoDeployed( Task, From, Event, To, TaskUnit, Cargo, DeployZone ) - self:CargoDeployed( Task, Task.TaskPrefix, TaskUnit, Cargo, DeployZone ) - end - - end - end - - - -- Now that all obsolete tasks are removed, loop through the Transport tasks. - for TransportName, Transport in pairs( self.Transport ) do - - if not Transport.Task then - -- New Transport Task - Transport.Task = TASK_CARGO_TRANSPORT:New( Mission, self.SetGroup, TransportName, Transport.SetCargo, Transport.Briefing ) - Transport.Task.TaskPrefix = Transport.TaskPrefix -- We keep the TaskPrefix for further reference! - Mission:AddTask( Transport.Task ) - TaskReport:Add( TransportName ) - function Transport.Task.OnEnterSuccess( Task, From, Event, To ) - self:Success( Task ) - end - - function Transport.Task.OnEnterCancelled( Task, From, Event, To ) - self:Cancelled( Task ) - end - - function Transport.Task.OnEnterFailed( Task, From, Event, To ) - self:Failed( Task ) - end - - function Transport.Task.OnEnterAborted( Task, From, Event, To ) - self:Aborted( Task ) - end - - -- Now broadcast the onafterCargoPickedUp event to the Task Cargo Dispatcher. - function Transport.Task.OnAfterCargoPickedUp( Task, From, Event, To, TaskUnit, Cargo ) - self:CargoPickedUp( Task, Task.TaskPrefix, TaskUnit, Cargo ) - end - - -- Now broadcast the onafterCargoDeployed event to the Task Cargo Dispatcher. - function Transport.Task.OnAfterCargoDeployed( Task, From, Event, To, TaskUnit, Cargo, DeployZone ) - self:CargoDeployed( Task, Task.TaskPrefix, TaskUnit, Cargo, DeployZone ) - end - - end - - if Transport.DeployZones then - Transport.Task:SetDeployZones( Transport.DeployZones or {} ) - else - Transport.Task:SetDeployZones( self.DefaultDeployZones or {} ) - end - - end - - - -- TODO set menus using the HQ coordinator - Mission:GetCommandCenter():SetMenu() - - local TaskText = TaskReport:Text(", ") - - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and not verbose then - Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup ) - end - end - - end - - return true - end - -end diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua deleted file mode 100644 index 3a30eaf22..000000000 --- a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua +++ /dev/null @@ -1,365 +0,0 @@ ---- **Tasking** - Models tasks for players to transport cargo. --- --- ![Banner Image](..\Images\deprecated.png) --- --- **Specific features:** --- --- * Creates a task to transport #Cargo.Cargo to and between deployment zones. --- * Derived from the TASK_CARGO class, which is derived from the TASK class. --- * Orchestrate the task flow, so go from Planned to Assigned to Success, Failed or Cancelled. --- * Co-operation tasking, so a player joins a group of players executing the same task. --- --- --- **A complete task menu system to allow players to:** --- --- * Join the task, abort the task. --- * Mark the task location on the map. --- * Provide details of the target. --- * Route to the cargo. --- * Route to the deploy zones. --- * Load/Unload cargo. --- * Board/Unboard cargo. --- * Slingload cargo. --- * Display the task briefing. --- --- --- **A complete mission menu system to allow players to:** --- --- * Join a task, abort the task. --- * Display task reports. --- * Display mission statistics. --- * Mark the task locations on the map. --- * Provide details of the targets. --- * Display the mission briefing. --- * Provide status updates as retrieved from the command center. --- * Automatically assign a random task as part of a mission. --- * Manually assign a specific task as part of a mission. --- --- --- **A settings system, using the settings menu:** --- --- * Tweak the duration of the display of messages. --- * Switch between metric and imperial measurement system. --- * Switch between coordinate formats used in messages: BR, BRA, LL DMS, LL DDM, MGRS. --- * Different settings modes for A2G and A2A operations. --- * Various other options. --- --- # Developer Note --- --- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE --- Therefore, this class is considered to be deprecated --- --- === --- --- Please read through the #Tasking.Task_Cargo process to understand the mechanisms of tasking and cargo tasking and handling. --- --- Enjoy! --- FC --- --- === --- --- @module Tasking.Task_Cargo_Transport --- @image Task_Cargo_Transport.JPG - - -do -- TASK_CARGO_TRANSPORT - - -- @type TASK_CARGO_TRANSPORT - -- @extends Tasking.Task_CARGO#TASK_CARGO - - --- Orchestrates the task for players to transport cargo to or between deployment zones. - -- - -- Transport tasks are suited to govern the process of transporting cargo to specific deployment zones. - -- Typically, this task is executed by helicopter pilots, but it can also be executed by ground forces! - -- - -- === - -- - -- A transport task can be created manually. - -- - -- # 1) Create a transport task manually (code it). - -- - -- Although it is recommended to use the dispatcher, you can create a transport task yourself as a mission designer. - -- It is easy, as it works just like any other task setup. - -- - -- ## 1.1) Create a command center. - -- - -- First you need to create a command center using the Tasking.CommandCenter#COMMANDCENTER.New constructor. - -- - -- local CommandCenter = COMMANDCENTER - -- :New( HQ, "Lima" ) -- Create the CommandCenter. - -- - -- ## 1.2) Create a mission. - -- - -- Tasks work in a mission, which groups these tasks to achieve a joint mission goal. - -- A command center can govern multiple missions. - -- Create a new mission, using the Tasking.Mission#MISSION.New constructor. - -- - -- -- Declare the Mission for the Command Center. - -- local Mission = MISSION - -- :New( CommandCenter, - -- "Overlord", - -- "High", - -- "Transport the cargo to the deploy zones.", - -- coalition.side.RED - -- ) - -- - -- ## 1.3) Create the transport cargo task. - -- - -- So, now that we have a command center and a mission, we now create the transport task. - -- We create the transport task using the #TASK_CARGO_TRANSPORT.New constructor. - -- - -- Because a transport task will not generate the cargo itself, you'll need to create it first. - -- The cargo in this case will be the downed pilot! - -- - -- -- Here we define the "cargo set", which is a collection of cargo objects. - -- -- The cargo set will be the input for the cargo transportation task. - -- -- So a transportation object is handling a cargo set, which is automatically refreshed when new cargo is added/deleted. - -- local CargoSet = SET_CARGO:New():FilterTypes( "Cargo" ):FilterStart() - -- - -- -- Now we add cargo into the battle scene. - -- local PilotGroup = GROUP:FindByName( "Engineers" ) - -- - -- -- CARGO_GROUP can be used to setup cargo with a GROUP object underneath. - -- -- We name this group Engineers. - -- -- Note that the name of the cargo is "Engineers". - -- -- The cargoset "CargoSet" will embed all defined cargo of type "Pilots" (prefix) into its set. - -- local CargoGroup = CARGO_GROUP:New( PilotGroup, "Cargo", "Engineer Team 1", 500 ) - -- - -- What is also needed, is to have a set of @{Wrapper.Group}s defined that contains the clients of the players. - -- - -- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players. - -- local GroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart() - -- - -- Now that we have a CargoSet and a GroupSet, we can now create the TransportTask manually. - -- - -- -- Declare the transport task. - -- local TransportTask = TASK_CARGO_TRANSPORT - -- :New( Mission, - -- GroupSet, - -- "Transport Engineers", - -- CargoSet, - -- "Fly behind enemy lines, and retrieve the downed pilot." - -- ) - -- - -- So you can see, setting up a transport task manually is a lot of work. - -- It is better you use the cargo dispatcher to create transport tasks and it will work as it is intended. - -- By doing this, cargo transport tasking will become a dynamic experience. - -- - -- - -- # 2) Create a task using the Tasking.Task_Cargo_Dispatcher module. - -- - -- Actually, it is better to **GENERATE** these tasks using the Tasking.Task_Cargo_Dispatcher module. - -- Using the dispatcher module, transport tasks can be created easier. - -- - -- Find below an example how to use the TASK_CARGO_DISPATCHER class: - -- - -- - -- -- Find the HQ group. - -- HQ = GROUP:FindByName( "HQ", "Bravo" ) - -- - -- -- Create the command center with the name "Lima". - -- CommandCenter = COMMANDCENTER - -- :New( HQ, "Lima" ) - -- - -- -- Create the mission, for the command center, with the name "Operation Cargo Fun", a "Tactical" mission, with the mission briefing "Transport Cargo", for the BLUE coalition. - -- Mission = MISSION - -- :New( CommandCenter, "Operation Cargo Fun", "Tactical", "Transport Cargo", coalition.side.BLUE ) - -- - -- -- Create the SET of GROUPs containing clients (players) that will transport the cargo. - -- -- These are have a name that start with "Transport" and are of the "blue" coalition. - -- TransportGroups = SET_GROUP:New():FilterCoalitions( "blue" ):FilterPrefixes( "Transport" ):FilterStart() - -- - -- - -- -- Here we create the TASK_CARGO_DISPATCHER object! This is where we assign the dispatcher to generate tasks in the Mission for the TransportGroups. - -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, TransportGroups ) - -- - -- - -- -- Here we declare the SET of CARGOs called "Workmaterials". - -- local CargoSetWorkmaterials = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() - -- - -- -- Here we declare (add) CARGO_GROUP objects of various types, that are filtered and added in the CargoSetworkmaterials cargo set. - -- -- These cargo objects have the type "Workmaterials" which is exactly the type of cargo the CargoSetworkmaterials is filtering on. - -- local EngineerCargoGroup = CARGO_GROUP:New( GROUP:FindByName( "Engineers" ), "Workmaterials", "Engineers", 250 ) - -- local ConcreteCargo = CARGO_SLINGLOAD:New( STATIC:FindByName( "Concrete" ), "Workmaterials", "Concrete", 150, 50 ) - -- local CrateCargo = CARGO_CRATE:New( STATIC:FindByName( "Crate" ), "Workmaterials", "Crate", 150, 50 ) - -- local EnginesCargo = CARGO_CRATE:New( STATIC:FindByName( "Engines" ), "Workmaterials", "Engines", 150, 50 ) - -- local MetalCargo = CARGO_CRATE:New( STATIC:FindByName( "Metal" ), "Workmaterials", "Metal", 150, 50 ) - -- - -- -- And here we create a new WorkplaceTask, using the :AddTransportTask method of the TaskDispatcher. - -- local WorkplaceTask = TaskDispatcher:AddTransportTask( "Build a Workplace", CargoSetWorkmaterials, "Transport the workers, engineers and the equipment near the Workplace." ) - -- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) ) - -- - -- # 3) Handle cargo task events. - -- - -- When a player is picking up and deploying cargo using his carrier, events are generated by the tasks. These events can be captured and tailored with your own code. - -- - -- In order to properly capture the events and avoid mistakes using the documentation, it is advised that you execute the following actions: - -- - -- * **Copy / Paste** the code section into your script. - -- * **Change** the "myclass" literal to the task object name you have in your script. - -- * Within the function, you can now **write your own code**! - -- * **IntelliSense** will recognize the type of the variables provided by the function. Note: the From, Event and To variables can be safely ignored, - -- but you need to declare them as they are automatically provided by the event handling system of MOOSE. - -- - -- You can send messages or fire off any other events within the code section. The sky is the limit! - -- - -- - -- ## 3.1) Handle the CargoPickedUp event. - -- - -- Find below an example how to tailor the **CargoPickedUp** event, generated by the WorkplaceTask: - -- - -- function WorkplaceTask:OnAfterCargoPickedUp( From, Event, To, TaskUnit, Cargo ) - -- - -- MESSAGE:NewType( "Unit " .. TaskUnit:GetName().. " has picked up cargo.", MESSAGE.Type.Information ):ToAll() - -- - -- end - -- - -- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has picked up a cargo object in the CarrierGroup. - -- You can use this event handler to post messages to players, or provide status updates etc. - -- - -- --- CargoPickedUp event handler OnAfter for "myclass". - -- -- @param #string From A string that contains the "*from state name*" when the event was triggered. - -- -- @param #string Event A string that contains the "*event name*" when the event was triggered. - -- -- @param #string To A string that contains the "*to state name*" when the event was triggered. - -- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has picked up the cargo. - -- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been picked up. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object! - -- function myclass:OnAfterCargoPickedUp( From, Event, To, TaskUnit, Cargo ) - -- - -- -- Write here your own code. - -- - -- end - -- - -- - -- ## 3.2) Handle the CargoDeployed event. - -- - -- Find below an example how to tailor the **CargoDeployed** event, generated by the WorkplaceTask: - -- - -- function WorkplaceTask:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone ) - -- - -- MESSAGE:NewType( "Unit " .. TaskUnit:GetName().. " has deployed cargo at zone " .. DeployZone:GetName(), MESSAGE.Type.Information ):ToAll() - -- - -- Helos[ math.random(1,#Helos) ]:Spawn() - -- EnemyHelos[ math.random(1,#EnemyHelos) ]:Spawn() - -- end - -- - -- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has deployed a cargo object from the CarrierGroup. - -- You can use this event handler to post messages to players, or provide status updates etc. - -- - -- - -- --- CargoDeployed event handler OnAfter foR "myclass". - -- -- @param #string From A string that contains the "*from state name*" when the event was triggered. - -- -- @param #string Event A string that contains the "*event name*" when the event was triggered. - -- -- @param #string To A string that contains the "*to state name*" when the event was triggered. - -- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has deployed the cargo. - -- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been deployed. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object! - -- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. - -- function myclass:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone ) - -- - -- -- Write here your own code. - -- - -- end - -- - -- - -- - -- === - -- - -- @field #TASK_CARGO_TRANSPORT - TASK_CARGO_TRANSPORT = { - ClassName = "TASK_CARGO_TRANSPORT", - } - - --- Instantiates a new TASK_CARGO_TRANSPORT. - -- @param #TASK_CARGO_TRANSPORT 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_CARGO SetCargo The scope of the cargo to be transported. - -- @param #string TaskBriefing The Cargo Task briefing. - -- @return #TASK_CARGO_TRANSPORT self - function TASK_CARGO_TRANSPORT:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing ) - local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport", TaskBriefing ) ) -- #TASK_CARGO_TRANSPORT - self:F() - - Mission:AddTask( self ) - - local Fsm = self:GetUnitProcess() - - local CargoReport = REPORT:New( "Transport Cargo. The following cargo needs to be transported including initial positions:") - - SetCargo:ForEachCargo( - -- @param Core.Cargo#CARGO Cargo - function( Cargo ) - local CargoType = Cargo:GetType() - local CargoName = Cargo:GetName() - local CargoCoordinate = Cargo:GetCoordinate() - CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) ) - end - ) - - self:SetBriefing( - TaskBriefing or - CargoReport:Text() - ) - - - return self - end - - function TASK_CARGO_TRANSPORT:ReportOrder( ReportGroup ) - - return 0 - end - - - --- - -- @param #TASK_CARGO_TRANSPORT self - -- @return #boolean - function TASK_CARGO_TRANSPORT:IsAllCargoTransported() - - local CargoSet = self:GetCargoSet() - local Set = CargoSet:GetSet() - - local DeployZones = self:GetDeployZones() - - local CargoDeployed = true - - -- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ). - for CargoID, CargoData in pairs( Set ) do - local Cargo = CargoData -- Core.Cargo#CARGO - - self:F( { Cargo = Cargo:GetName(), CargoDeployed = Cargo:IsDeployed() } ) - - if Cargo:IsDeployed() then - --- -- Loop the DeployZones set for the TASK_CARGO_TRANSPORT. --- for DeployZoneID, DeployZone in pairs( DeployZones ) do --- --- -- If all cargo is in one of the deploy zones, then all is good. --- self:T( { Cargo.CargoObject } ) --- if Cargo:IsInZone( DeployZone ) == false then --- CargoDeployed = false --- end --- end - else - CargoDeployed = false - end - end - - self:F( { CargoDeployed = CargoDeployed } ) - - return CargoDeployed - end - - -- @param #TASK_CARGO_TRANSPORT self - function TASK_CARGO_TRANSPORT:onafterGoal( TaskUnit, From, Event, To ) - local CargoSet = self.CargoSet - - if self:IsAllCargoTransported() then - self:Success() - end - - self:__Goal( -10 ) - end - -end - diff --git a/Moose Development/Moose/Tasking/Task_Manager.lua b/Moose Development/Moose/Tasking/Task_Manager.lua deleted file mode 100644 index 7cd71c485..000000000 --- a/Moose Development/Moose/Tasking/Task_Manager.lua +++ /dev/null @@ -1,194 +0,0 @@ ---- **Tasking** - This module contains the TASK_MANAGER class and derived classes. --- --- === --- --- ![Banner Image](..\Images\deprecated.png) --- --- 1) @{Tasking.Task_Manager#TASK_MANAGER} class, extends @{Core.Fsm#FSM} --- === --- The @{Tasking.Task_Manager#TASK_MANAGER} class defines the core functions to report tasks to groups. --- Reportings can be done in several manners, and it is up to the derived classes if TASK_MANAGER to model the reporting behaviour. --- --- 1.1) TASK_MANAGER constructor: --- ----------------------------------- --- * @{Tasking.Task_Manager#TASK_MANAGER.New}(): Create a new TASK_MANAGER instance. --- --- 1.2) TASK_MANAGER reporting: --- --------------------------------- --- Derived TASK_MANAGER classes will manage tasks using the method @{Tasking.Task_Manager#TASK_MANAGER.ManageTasks}(). This method implements polymorphic behaviour. --- --- The time interval in seconds of the task management can be changed using the methods @{Tasking.Task_Manager#TASK_MANAGER.SetRefreshTimeInterval}(). --- To control how long a reporting message is displayed, use @{Tasking.Task_Manager#TASK_MANAGER.SetReportDisplayTime}(). --- Derived classes need to implement the method @{Tasking.Task_Manager#TASK_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report. --- --- Task management can be started and stopped using the methods @{Tasking.Task_Manager#TASK_MANAGER.StartTasks}() and @{Tasking.Task_Manager#TASK_MANAGER.StopTasks}() respectively. --- If an ad-hoc report is requested, use the method @{Tasking.Task_Manager#TASK_MANAGER#ManageTasks}(). --- --- The default task management interval is every 60 seconds. --- --- # Developer Note --- --- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE --- Therefore, this class is considered to be deprecated --- --- === --- --- ### Contributions: Mechanist, Prof_Hilactic, FlightControl - Concept & Testing --- ### Author: FlightControl - Framework Design & Programming --- --- @module Tasking.Task_Manager --- @image MOOSE.JPG - -do -- TASK_MANAGER - - --- TASK_MANAGER class. - -- @type TASK_MANAGER - -- @field Core.Set#SET_GROUP SetGroup The set of group objects containing players for which tasks are managed. - -- @extends Core.Fsm#FSM - TASK_MANAGER = { - ClassName = "TASK_MANAGER", - SetGroup = nil, - } - - --- TASK\_MANAGER constructor. - -- @param #TASK_MANAGER self - -- @param Core.Set#SET_GROUP SetGroup The set of group objects containing players for which tasks are managed. - -- @return #TASK_MANAGER self - function TASK_MANAGER:New( SetGroup ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM:New() ) -- #TASK_MANAGER - - self.SetGroup = SetGroup - - self:SetStartState( "Stopped" ) - self:AddTransition( "Stopped", "StartTasks", "Started" ) - - --- StartTasks Handler OnBefore for TASK_MANAGER - -- @function [parent=#TASK_MANAGER] OnBeforeStartTasks - -- @param #TASK_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @return #boolean - - --- StartTasks Handler OnAfter for TASK_MANAGER - -- @function [parent=#TASK_MANAGER] OnAfterStartTasks - -- @param #TASK_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - - --- StartTasks Trigger for TASK_MANAGER - -- @function [parent=#TASK_MANAGER] StartTasks - -- @param #TASK_MANAGER self - - --- StartTasks Asynchronous Trigger for TASK_MANAGER - -- @function [parent=#TASK_MANAGER] __StartTasks - -- @param #TASK_MANAGER self - -- @param #number Delay - - self:AddTransition( "Started", "StopTasks", "Stopped" ) - - --- StopTasks Handler OnBefore for TASK_MANAGER - -- @function [parent=#TASK_MANAGER] OnBeforeStopTasks - -- @param #TASK_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @return #boolean - - --- StopTasks Handler OnAfter for TASK_MANAGER - -- @function [parent=#TASK_MANAGER] OnAfterStopTasks - -- @param #TASK_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - - --- StopTasks Trigger for TASK_MANAGER - -- @function [parent=#TASK_MANAGER] StopTasks - -- @param #TASK_MANAGER self - - --- StopTasks Asynchronous Trigger for TASK_MANAGER - -- @function [parent=#TASK_MANAGER] __StopTasks - -- @param #TASK_MANAGER self - -- @param #number Delay - - self:AddTransition( "Started", "Manage", "Started" ) - - self:AddTransition( "Started", "Success", "Started" ) - - --- Success Handler OnAfter for TASK_MANAGER - -- @function [parent=#TASK_MANAGER] OnAfterSuccess - -- @param #TASK_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Tasking.Task#TASK Task - - self:AddTransition( "Started", "Failed", "Started" ) - - --- Failed Handler OnAfter for TASK_MANAGER - -- @function [parent=#TASK_MANAGER] OnAfterFailed - -- @param #TASK_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Tasking.Task#TASK Task - - self:AddTransition( "Started", "Aborted", "Started" ) - - --- Aborted Handler OnAfter for TASK_MANAGER - -- @function [parent=#TASK_MANAGER] OnAfterAborted - -- @param #TASK_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Tasking.Task#TASK Task - - self:AddTransition( "Started", "Cancelled", "Started" ) - - --- Cancelled Handler OnAfter for TASK_MANAGER - -- @function [parent=#TASK_MANAGER] OnAfterCancelled - -- @param #TASK_MANAGER self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Tasking.Task#TASK Task - - self:SetRefreshTimeInterval( 30 ) - - return self - end - - function TASK_MANAGER:onafterStartTasks( From, Event, To ) - self:Manage() - end - - function TASK_MANAGER:onafterManage( From, Event, To ) - - self:__Manage( -self._RefreshTimeInterval ) - - self:ManageTasks() - end - - --- Set the refresh time interval in seconds when a new task management action needs to be done. - -- @param #TASK_MANAGER self - -- @param #number RefreshTimeInterval The refresh time interval in seconds when a new task management action needs to be done. - -- @return #TASK_MANAGER self - function TASK_MANAGER:SetRefreshTimeInterval( RefreshTimeInterval ) - self:F2() - - self._RefreshTimeInterval = RefreshTimeInterval - end - - - --- Manages the tasks for the @{Core.Set#SET_GROUP}. - -- @param #TASK_MANAGER self - -- @return #TASK_MANAGER self - function TASK_MANAGER:ManageTasks() - - end - -end -