Delete Moose Development/Moose/Tasking directory

prep master-ng
This commit is contained in:
Thomas
2026-02-06 12:48:08 +01:00
committed by GitHub
parent 8cb06cc3b0
commit d28ab00443
16 changed files with 0 additions and 11658 deletions
@@ -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<Tasking.Mission#MISSION> 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<Tasking.Mission#MISSION>
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
@@ -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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -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<Core.Point#COORDINATE> 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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large Load Diff
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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