From c5cc069e46c228b51c07e7079ff9a2a3525375bf Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 7 Jul 2016 13:11:31 +0200 Subject: [PATCH] Progress, got now task acceptance working --- Moose Development/Moose/Base.lua | 3 +- Moose Development/Moose/Event.lua | 25 +- Moose Development/Moose/Menu.lua | 502 ++++++++++++------ Moose Development/Moose/Mission.lua | 10 +- Moose Development/Moose/Moose.lua | 5 +- Moose Development/Moose/Process.lua | 25 +- Moose Development/Moose/Process_Assign.lua | 105 ++++ Moose Development/Moose/Process_Route.lua | 31 +- Moose Development/Moose/Process_SEAD.lua | 51 +- Moose Development/Moose/Scheduler.lua | 1 + Moose Development/Moose/Scoring.lua | 15 +- Moose Development/Moose/StateMachine.lua | 37 +- Moose Development/Moose/Task.lua | 198 ++++++- Moose Development/Moose/Task_SEAD.lua | 53 +- Moose Development/Moose/Unit.lua | 18 + .../Moose_Test_TASK_SEAD.lua | 7 +- .../Moose_Test_TASK_SEAD.miz | Bin 18937 -> 19880 bytes .../DCS World - MOOSE - Tasking - SEAD.pptx | Bin 7343179 -> 7345157 bytes 18 files changed, 833 insertions(+), 253 deletions(-) create mode 100644 Moose Development/Moose/Process_Assign.lua diff --git a/Moose Development/Moose/Base.lua b/Moose Development/Moose/Base.lua index 0c7361e41..24121d57e 100644 --- a/Moose Development/Moose/Base.lua +++ b/Moose Development/Moose/Base.lua @@ -115,7 +115,6 @@ function BASE:New() self.__index = self _ClassID = _ClassID + 1 self.ClassID = _ClassID - self.ClassNameAndID = string.format( '%s#%09d', self.ClassName, self.ClassID ) return self end @@ -152,7 +151,7 @@ end -- @param #BASE self -- @return #string The ClassName + ClassID of the class instance. function BASE:GetClassNameAndID() - return self.ClassNameAndID + return string.format( '%s#%09d', self.ClassName, self.ClassID ) end --- Get the ClassName of the class instance. diff --git a/Moose Development/Moose/Event.lua b/Moose Development/Moose/Event.lua index bcad4497f..93e4d0907 100644 --- a/Moose Development/Moose/Event.lua +++ b/Moose Development/Moose/Event.lua @@ -272,6 +272,19 @@ function EVENT:OnDeadForUnit( EventDCSUnitName, EventFunction, EventSelf ) return self end +--- Set a new listener for an S_EVENT_PILOT_DEAD event. +-- @param #EVENT self +-- @param #function EventFunction The function to be called when the event occurs for the unit. +-- @param Base#BASE EventSelf +-- @return #EVENT +function EVENT:OnPilotDead( EventFunction, EventSelf ) + self:F2() + + self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_PILOT_DEAD ) + + return self +end + --- Set a new listener for an S_EVENT_PILOT_DEAD event. -- @param #EVENT self -- @param #string EventDCSUnitName @@ -468,7 +481,6 @@ end --- @param #EVENT self -- @param #EVENTDATA Event function EVENT:onEvent( Event ) - self:F2( { _EVENTCODES[Event.id], Event } ) if self and self.Events and self.Events[Event.id] then if Event.initiator and Event.initiator:getCategory() == Object.Category.UNIT then @@ -500,18 +512,23 @@ function EVENT:onEvent( Event ) Event.WeaponName = Event.Weapon:getTypeName() --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end - self:E( { _EVENTCODES[Event.id], Event.IniUnitName, Event.TgtUnitName, Event.WeaponName } ) + self:E( { _EVENTCODES[Event.id], Event } ) + --self:E( { _EVENTCODES[Event.id], Event.IniUnitName, Event.TgtUnitName, Event.WeaponName } ) for ClassName, EventData in pairs( self.Events[Event.id] ) do if Event.IniDCSUnitName and EventData.IniUnit and EventData.IniUnit[Event.IniDCSUnitName] then self:E( { "Calling event function for class ", ClassName, " unit ", Event.IniDCSUnitName } ) EventData.IniUnit[Event.IniDCSUnitName].EventFunction( EventData.IniUnit[Event.IniDCSUnitName].EventSelf, Event ) else if Event.IniDCSUnit and not EventData.IniUnit then - self:E( { "Calling event function for class ", ClassName } ) - EventData.EventFunction( EventData.EventSelf, Event ) + if ClassName == EventData.EventSelf:GetClassNameAndID() then + self:E( { "Calling event function for class ", ClassName } ) + EventData.EventFunction( EventData.EventSelf, Event ) + end end end end + else + self:E( { _EVENTCODES[Event.id], Event } ) end end diff --git a/Moose Development/Moose/Menu.lua b/Moose Development/Moose/Menu.lua index 7a723aff4..9efd8a757 100644 --- a/Moose Development/Moose/Menu.lua +++ b/Moose Development/Moose/Menu.lua @@ -70,180 +70,362 @@ function SUBMENU:New( MenuText, ParentMenu ) return Child end --- This local variable is used to cache the menus registered under clients. --- Menus don't dissapear when clients are destroyed and restarted. --- So every menu for a client created must be tracked so that program logic accidentally does not create --- the same menus twice during initialization logic. --- These menu classes are handling this logic with this variable. -local _MENUCLIENTS = {} +do ---- The MENU_CLIENT class --- @type MENU_CLIENT --- @extends Menu#MENU -MENU_CLIENT = { - ClassName = "MENU_CLIENT" -} - ---- Creates a new menu item for a group --- @param self --- @param Client#CLIENT MenuClient The Client owning the menu. --- @param #string MenuText The text for the menu. --- @param #table ParentMenu The parent menu. --- @return #MENU_CLIENT self -function MENU_CLIENT:New( MenuClient, MenuText, ParentMenu ) - - -- Arrange meta tables - local MenuParentPath = {} - if ParentMenu ~= nil then - MenuParentPath = ParentMenu.MenuPath - end - - local self = BASE:Inherit( self, MENU:New( MenuText, MenuParentPath ) ) - self:F( { MenuClient, MenuText, ParentMenu } ) - - self.MenuClient = MenuClient - self.MenuClientGroupID = MenuClient:GetClientGroupID() - self.MenuParentPath = MenuParentPath - self.MenuText = MenuText - self.ParentMenu = ParentMenu + -- This local variable is used to cache the menus registered under clients. + -- Menus don't dissapear when clients are destroyed and restarted. + -- So every menu for a client created must be tracked so that program logic accidentally does not create + -- the same menus twice during initialization logic. + -- These menu classes are handling this logic with this variable. + local _MENUCLIENTS = {} - self.Menus = {} - - if not _MENUCLIENTS[self.MenuClientGroupID] then - _MENUCLIENTS[self.MenuClientGroupID] = {} + --- The MENU_CLIENT class + -- @type MENU_CLIENT + -- @extends Menu#MENU + MENU_CLIENT = { + ClassName = "MENU_CLIENT" + } + + --- Creates a new menu item for a group + -- @param self + -- @param Client#CLIENT MenuClient The Client owning the menu. + -- @param #string MenuText The text for the menu. + -- @param #table ParentMenu The parent menu. + -- @return #MENU_CLIENT self + function MENU_CLIENT:New( MenuClient, MenuText, ParentMenu ) + + -- Arrange meta tables + local MenuParentPath = {} + if ParentMenu ~= nil then + MenuParentPath = ParentMenu.MenuPath + end + + local self = BASE:Inherit( self, MENU:New( MenuText, MenuParentPath ) ) + self:F( { MenuClient, MenuText, ParentMenu } ) + + self.MenuClient = MenuClient + self.MenuClientGroupID = MenuClient:GetClientGroupID() + self.MenuParentPath = MenuParentPath + self.MenuText = MenuText + self.ParentMenu = ParentMenu + + self.Menus = {} + + if not _MENUCLIENTS[self.MenuClientGroupID] then + _MENUCLIENTS[self.MenuClientGroupID] = {} + end + + local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] + + self:T( { MenuClient:GetClientGroupName(), MenuPath[table.concat(MenuParentPath)], MenuParentPath, MenuText } ) + + local MenuPathID = table.concat(MenuParentPath) .. "/" .. MenuText + if MenuPath[MenuPathID] then + missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] ) + end + + self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath ) + MenuPath[MenuPathID] = self.MenuPath + + self:T( { MenuClient:GetClientGroupName(), self.MenuPath } ) + + if ParentMenu and ParentMenu.Menus then + ParentMenu.Menus[self.MenuPath] = self + end + return self end - local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] - - self:T( { MenuClient:GetClientGroupName(), MenuPath[table.concat(MenuParentPath)], MenuParentPath, MenuText } ) - - local MenuPathID = table.concat(MenuParentPath) .. "/" .. MenuText - if MenuPath[MenuPathID] then - missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] ) + --- Removes the sub menus recursively of this MENU_CLIENT. + -- @param #MENU_CLIENT self + -- @return #MENU_CLIENT self + function MENU_CLIENT:RemoveSubMenus() + self:F( self.MenuPath ) + + for MenuID, Menu in pairs( self.Menus ) do + Menu:Remove() + end + end + + --- Removes the sub menus recursively of this MENU_CLIENT. + -- @param #MENU_CLIENT self + -- @return #MENU_CLIENT self + function MENU_CLIENT:Remove() + self:F( self.MenuPath ) + + self:RemoveSubMenus() + + if not _MENUCLIENTS[self.MenuClientGroupID] then + _MENUCLIENTS[self.MenuClientGroupID] = {} + end + + local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] + + if MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] then + MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] = nil + end + + missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), self.MenuPath ) + self.ParentMenu.Menus[self.MenuPath] = nil + return nil + end + + + --- The MENU_CLIENT_COMMAND class + -- @type MENU_CLIENT_COMMAND + -- @extends Menu#MENU + MENU_CLIENT_COMMAND = { + ClassName = "MENU_CLIENT_COMMAND" + } + + --- Creates a new radio command item for a group + -- @param self + -- @param Client#CLIENT MenuClient The Client owning the menu. + -- @param MenuText The text for the menu. + -- @param ParentMenu The parent menu. + -- @param CommandMenuFunction A function that is called when the menu key is pressed. + -- @param CommandMenuArgument An argument for the function. + -- @return Menu#MENU_CLIENT_COMMAND self + function MENU_CLIENT_COMMAND:New( MenuClient, MenuText, ParentMenu, CommandMenuFunction, CommandMenuArgument ) + + -- Arrange meta tables + + local MenuParentPath = {} + if ParentMenu ~= nil then + MenuParentPath = ParentMenu.MenuPath + end + + local self = BASE:Inherit( self, MENU:New( MenuText, MenuParentPath ) ) + + self.MenuClient = MenuClient + self.MenuClientGroupID = MenuClient:GetClientGroupID() + self.MenuParentPath = MenuParentPath + self.MenuText = MenuText + self.ParentMenu = ParentMenu + + if not _MENUCLIENTS[self.MenuClientGroupID] then + _MENUCLIENTS[self.MenuClientGroupID] = {} + end + + local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] + + self:T( { MenuClient:GetClientGroupName(), MenuPath[table.concat(MenuParentPath)], MenuParentPath, MenuText, CommandMenuFunction, CommandMenuArgument } ) + + local MenuPathID = table.concat(MenuParentPath) .. "/" .. MenuText + if MenuPath[MenuPathID] then + missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] ) + end + + self.MenuPath = missionCommands.addCommandForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath, CommandMenuFunction, CommandMenuArgument ) + MenuPath[MenuPathID] = self.MenuPath + + self.CommandMenuFunction = CommandMenuFunction + self.CommandMenuArgument = CommandMenuArgument + + ParentMenu.Menus[self.MenuPath] = self + + return self + end + + function MENU_CLIENT_COMMAND:Remove() + self:F( self.MenuPath ) + + if not _MENUCLIENTS[self.MenuClientGroupID] then + _MENUCLIENTS[self.MenuClientGroupID] = {} + end + + local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] + + if MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] then + MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] = nil + end + + missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), self.MenuPath ) + self.ParentMenu.Menus[self.MenuPath] = nil + return nil + end +end - self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath ) - MenuPath[MenuPathID] = self.MenuPath +--- MENU_GROUP - self:T( { MenuClient:GetClientGroupName(), self.MenuPath } ) +do + -- This local variable is used to cache the menus registered under clients. + -- Menus don't dissapear when clients are destroyed and restarted. + -- So every menu for a client created must be tracked so that program logic accidentally does not create + -- the same menus twice during initialization logic. + -- These menu classes are handling this logic with this variable. + local _MENUGROUPS = {} - if ParentMenu and ParentMenu.Menus then + --- The MENU_GROUP class + -- @type MENU_GROUP + -- @extends Menu#MENU + MENU_GROUP = { + ClassName = "MENU_GROUP" + } + + --- Creates a new menu item for a group + -- @param self + -- @param Group#GROUP MenuGroup The Group owning the menu. + -- @param #string MenuText The text for the menu. + -- @param #table ParentMenu The parent menu. + -- @return #MENU_GROUP self + function MENU_GROUP:New( MenuGroup, MenuText, ParentMenu ) + + -- Arrange meta tables + local MenuParentPath = {} + if ParentMenu ~= nil then + MenuParentPath = ParentMenu.MenuPath + end + + local self = BASE:Inherit( self, MENU:New( MenuText, MenuParentPath ) ) + self:F( { MenuGroup, MenuText, ParentMenu } ) + + self.MenuGroup = MenuGroup + self.MenuGroupID = MenuGroup:GetID() + self.MenuParentPath = MenuParentPath + self.MenuText = MenuText + self.ParentMenu = ParentMenu + + self.Menus = {} + + if not _MENUGROUPS[self.MenuGroupID] then + _MENUGROUPS[self.MenuGroupID] = {} + end + + local MenuPath = _MENUGROUPS[self.MenuGroupID] + + self:T( { MenuGroup:GetName(), MenuPath[table.concat(MenuParentPath)], MenuParentPath, MenuText } ) + + local MenuPathID = table.concat(MenuParentPath) .. "/" .. MenuText + if MenuPath[MenuPathID] then + missionCommands.removeItemForGroup( self.MenuGroupID, MenuPath[MenuPathID] ) + end + + self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuGroupID, MenuText, MenuParentPath ) + MenuPath[MenuPathID] = self.MenuPath + + self:T( { self.MenuGroupID, self.MenuPath } ) + + if ParentMenu and ParentMenu.Menus then + ParentMenu.Menus[self.MenuPath] = self + end + return self + end + + --- Removes the sub menus recursively of this MENU_GROUP. + -- @param #MENU_GROUP self + -- @return #MENU_GROUP self + function MENU_GROUP:RemoveSubMenus() + self:F( self.MenuPath ) + + for MenuID, Menu in pairs( self.Menus ) do + Menu:Remove() + end + + end + + --- Removes the sub menus recursively of this MENU_GROUP. + -- @param #MENU_GROUP self + -- @return #MENU_GROUP self + function MENU_GROUP:Remove() + self:F( self.MenuPath ) + + self:RemoveSubMenus() + + if not _MENUGROUPS[self.MenuGroupID] then + _MENUGROUPS[self.MenuGroupID] = {} + end + + local MenuPath = _MENUGROUPS[self.MenuGroupID] + + if MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] then + MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] = nil + end + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + if self.ParentMenu then + self.ParentMenu.Menus[self.MenuPath] = nil + end + return nil + end + + + --- The MENU_GROUP_COMMAND class + -- @type MENU_GROUP_COMMAND + -- @extends Menu#MENU + MENU_GROUP_COMMAND = { + ClassName = "MENU_GROUP_COMMAND" + } + + --- Creates a new radio command item for a group + -- @param #MENU_GROUP_COMMAND self + -- @param Group#GROUP MenuGroup The Group owning the menu. + -- @param MenuText The text for the menu. + -- @param ParentMenu The parent menu. + -- @param CommandMenuFunction A function that is called when the menu key is pressed. + -- @param CommandMenuArgument An argument for the function. + -- @return Menu#MENU_GROUP_COMMAND self + function MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, CommandMenuFunction, CommandMenuArgument ) + + -- Arrange meta tables + + local MenuParentPath = {} + if ParentMenu ~= nil then + MenuParentPath = ParentMenu.MenuPath + end + + local self = BASE:Inherit( self, MENU:New( MenuText, MenuParentPath ) ) + + self.MenuGroup = MenuGroup + self.MenuGroupID = MenuGroup:GetID() + self.MenuParentPath = MenuParentPath + self.MenuText = MenuText + self.ParentMenu = ParentMenu + + if not _MENUGROUPS[self.MenuGroupID] then + _MENUGROUPS[self.MenuGroupID] = {} + end + + local MenuPath = _MENUGROUPS[self.MenuGroupID] + + self:T( { MenuGroup:GetName(), MenuPath[table.concat(MenuParentPath)], MenuParentPath, MenuText, CommandMenuFunction, CommandMenuArgument } ) + + local MenuPathID = table.concat(MenuParentPath) .. "/" .. MenuText + if MenuPath[MenuPathID] then + missionCommands.removeItemForGroup( self.MenuGroupID, MenuPath[MenuPathID] ) + end + + self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, MenuParentPath, CommandMenuFunction, CommandMenuArgument ) + MenuPath[MenuPathID] = self.MenuPath + + self.CommandMenuFunction = CommandMenuFunction + self.CommandMenuArgument = CommandMenuArgument + ParentMenu.Menus[self.MenuPath] = self + + return self end - return self -end - ---- Removes the sub menus recursively of this MENU_CLIENT. --- @param #MENU_CLIENT self --- @return #MENU_CLIENT self -function MENU_CLIENT:RemoveSubMenus() - self:F( self.MenuPath ) - - for MenuID, Menu in pairs( self.Menus ) do - Menu:Remove() + + function MENU_GROUP_COMMAND:Remove() + self:F( self.MenuPath ) + + if not _MENUGROUPS[self.MenuGroupID] then + _MENUGROUPS[self.MenuGroupID] = {} + end + + local MenuPath = _MENUGROUPS[self.MenuGroupID] + + if MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] then + MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] = nil + end + + missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath ) + self.ParentMenu.Menus[self.MenuPath] = nil + return nil end end ---- Removes the sub menus recursively of this MENU_CLIENT. --- @param #MENU_CLIENT self --- @return #MENU_CLIENT self -function MENU_CLIENT:Remove() - self:F( self.MenuPath ) - - self:RemoveSubMenus() - - if not _MENUCLIENTS[self.MenuClientGroupID] then - _MENUCLIENTS[self.MenuClientGroupID] = {} - end - - local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] - - if MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] then - MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] = nil - end - - missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), self.MenuPath ) - self.ParentMenu.Menus[self.MenuPath] = nil - return nil -end - - ---- The MENU_CLIENT_COMMAND class --- @type MENU_CLIENT_COMMAND --- @extends Menu#MENU -MENU_CLIENT_COMMAND = { - ClassName = "MENU_CLIENT_COMMAND" -} - ---- Creates a new radio command item for a group --- @param self --- @param Client#CLIENT MenuClient The Client owning the menu. --- @param MenuText The text for the menu. --- @param ParentMenu The parent menu. --- @param CommandMenuFunction A function that is called when the menu key is pressed. --- @param CommandMenuArgument An argument for the function. --- @return Menu#MENU_CLIENT_COMMAND self -function MENU_CLIENT_COMMAND:New( MenuClient, MenuText, ParentMenu, CommandMenuFunction, CommandMenuArgument ) - - -- Arrange meta tables - - local MenuParentPath = {} - if ParentMenu ~= nil then - MenuParentPath = ParentMenu.MenuPath - end - - local self = BASE:Inherit( self, MENU:New( MenuText, MenuParentPath ) ) - - self.MenuClient = MenuClient - self.MenuClientGroupID = MenuClient:GetClientGroupID() - self.MenuParentPath = MenuParentPath - self.MenuText = MenuText - self.ParentMenu = ParentMenu - - if not _MENUCLIENTS[self.MenuClientGroupID] then - _MENUCLIENTS[self.MenuClientGroupID] = {} - end - - local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] - - self:T( { MenuClient:GetClientGroupName(), MenuPath[table.concat(MenuParentPath)], MenuParentPath, MenuText, CommandMenuFunction, CommandMenuArgument } ) - - local MenuPathID = table.concat(MenuParentPath) .. "/" .. MenuText - if MenuPath[MenuPathID] then - missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] ) - end - - self.MenuPath = missionCommands.addCommandForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath, CommandMenuFunction, CommandMenuArgument ) - MenuPath[MenuPathID] = self.MenuPath - - self.CommandMenuFunction = CommandMenuFunction - self.CommandMenuArgument = CommandMenuArgument - - ParentMenu.Menus[self.MenuPath] = self - - return self -end - -function MENU_CLIENT_COMMAND:Remove() - self:F( self.MenuPath ) - - if not _MENUCLIENTS[self.MenuClientGroupID] then - _MENUCLIENTS[self.MenuClientGroupID] = {} - end - - local MenuPath = _MENUCLIENTS[self.MenuClientGroupID] - - if MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] then - MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] = nil - end - - missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), self.MenuPath ) - self.ParentMenu.Menus[self.MenuPath] = nil - return nil -end - - --- The MENU_COALITION class -- @type MENU_COALITION -- @extends Menu#MENU diff --git a/Moose Development/Moose/Mission.lua b/Moose Development/Moose/Mission.lua index de27f15f4..901d8158f 100644 --- a/Moose Development/Moose/Mission.lua +++ b/Moose Development/Moose/Mission.lua @@ -44,7 +44,7 @@ end -- @param #string MissionName is the name of the mission. This name will be used to reference the status of each mission by the players. -- @param #string MissionPriority is a string indicating the "priority" of the Mission. f.e. "Primary", "Secondary" or "First", "Second". It is free format and up to the Mission designer to choose. There are no rules behind this field. -- @param #string MissionBriefing is a string indicating the mission briefing to be shown when a player joins a @{CLIENT}. --- @param #string MissionCoalition is a string indicating the coalition or party to which this mission belongs to. It is free format and can be chosen freely by the mission designer. Note that this field is not to be confused with the coalition concept of the ME. Examples of a Mission Coalition could be "NATO", "CCCP", "Intruders", "Terrorists"... +-- @param DCSCoalitionObject#coalition DCSCoalition is a string indicating the coalition or party to which this mission belongs to. It is free format and can be chosen freely by the mission designer. Note that this field is not to be confused with the coalition concept of the ME. Examples of a Mission Coalition could be "NATO", "CCCP", "Intruders", "Terrorists"... -- @return #MISSION self -- @usage -- -- Declare a few missions. @@ -56,15 +56,15 @@ end -- local Mission = MISSIONSCHEDULER.AddMission( 'SA-6 SAMs', 'Primary', 'Our intelligence reports that 3 SA-6 SAM defense batteries are located near Didmukha, Khetagurov and Berula. Eliminate the Russian SAMs.', 'NATO' ) -- local Mission = MISSIONSCHEDULER.AddMission( 'NATO Sling Load', 'Operational', 'Fly to the cargo pickup zone at Dzegvi or Kaspi, and sling the cargo to Soganlug airbase.', 'NATO' ) -- local Mission = MISSIONSCHEDULER.AddMission( 'Rescue secret agent', 'Tactical', 'In order to be in full control of the situation, we need you to rescue a secret agent from the woods behind enemy lines. Avoid the Russian defenses and rescue the agent. Keep south until Khasuri, and keep your eyes open for any SAM presence. The agent is located at waypoint 4 on your kneeboard.', 'NATO' ) -function MISSION:New( MissionName, MissionPriority, MissionBriefing, MissionCoalition ) +function MISSION:New( MissionName, MissionPriority, MissionBriefing, DCSCoalition ) self = MISSION:Meta() - self:T({ MissionName, MissionPriority, MissionBriefing, MissionCoalition }) + self:T( { MissionName, MissionPriority, MissionBriefing, DCSCoalition } ) self.Name = MissionName self.MissionPriority = MissionPriority self.MissionBriefing = MissionBriefing - self.MissionCoalition = MissionCoalition + self.DCSCoalition = DCSCoalition self:SetMissionMenu() @@ -97,7 +97,7 @@ end -- @param #MISSION self -- @return #MISSION self function MISSION:SetMissionMenu() - self.MissionMenu = MENU_COALITION:New( self.MissionCoalition, self.Name ) + self.MissionMenu = MENU_COALITION:New( self.DCSCoalition, self.Name ) end --- Gets the mission menu for the coalition. diff --git a/Moose Development/Moose/Moose.lua b/Moose Development/Moose/Moose.lua index 3581cd32f..b175eb9b3 100644 --- a/Moose Development/Moose/Moose.lua +++ b/Moose Development/Moose/Moose.lua @@ -48,15 +48,16 @@ Include.File( "Detection" ) Include.File( "FAC" ) Include.File( "StateMachine" ) Include.File( "Process" ) -Include.File( "Process_SEAD" ) +Include.File( "Process_Assign" ) Include.File( "Process_Route" ) +Include.File( "Process_SEAD" ) Include.File( "Task" ) Include.File( "Task_SEAD" ) -- The order of the declarations is important here. Don't touch it. --- Declare the event dispatcher based on the EVENT class -_EVENTDISPATCHER = EVENT:New() -- #EVENT +_EVENTDISPATCHER = EVENT:New() -- Event#EVENT --- Declare the main database object, which is used internally by the MOOSE classes. _DATABASE = DATABASE:New() -- Database#DATABASE diff --git a/Moose Development/Moose/Process.lua b/Moose Development/Moose/Process.lua index 7eab787e6..8b63d4c32 100644 --- a/Moose Development/Moose/Process.lua +++ b/Moose Development/Moose/Process.lua @@ -4,7 +4,7 @@ -- @type PROCESS -- @field Scheduler#SCHEDULER ProcessScheduler -- @field Unit#UNIT ProcessUnit --- @field Task#MISSION Task +-- @field Task#TASK Task -- @field StateMachine#STATEMACHINE_TASK Fsm -- @extends Base#BASE PROCESS = { @@ -25,14 +25,28 @@ function PROCESS:New( Task, ProcessUnit ) self.ProcessUnit = ProcessUnit self.Task = Task + self.AllowEvents = true + return self end --- @param #PROCESS self function PROCESS:NextEvent( NextEvent, ... ) - self:E( NextEvent ) + self:F2( arg ) + if self.AllowEvents == true then + self.ProcessScheduler = SCHEDULER:New( self.Fsm, NextEvent, { self, self.ProcessUnit, unpack( arg ) }, 1 ) + end +end - self.ProcessScheduler = SCHEDULER:New( self.Fsm, NextEvent, { self, self.ProcessUnit, unpack( arg ) }, 1 ) +--- @param #PROCESS self +function PROCESS:StopEvents( ) + self:F2() + if self.ProcessScheduler then + self:E( "Stop" ) + self.ProcessScheduler:Stop() + self.ProcessScheduler = nil + self.AllowEvents = false + end end --- Adds a score for the PROCESS to be achieved. @@ -60,10 +74,11 @@ function PROCESS:OnStateChange( Fsm, Event, From, To ) self:E( { Event, From, To, self.ProcessUnit.UnitName } ) if self.Scores[To] then - self.Unit:Message( "Score:" .. self.Scores[To].ScoreText .. " " .. To , 15 ) + + MESSAGE:New( "Score:" .. self.Scores[To].ScoreText .. " " .. To , 15 ):ToGroup( self.ProcessUnit:GetGroup() ) local Scoring = self.Task:GetScoring() if Scoring then - Scoring:_AddTaskProcessScore( self.ProcessUnit, self.Task:GetName(), self.Scores[To].Score ) + Scoring:_AddMissionTaskScore( self.Task.Mission, self.ProcessUnit, self.Scores[To].ScoreText, self.Scores[To].Score ) end end end diff --git a/Moose Development/Moose/Process_Assign.lua b/Moose Development/Moose/Process_Assign.lua new file mode 100644 index 000000000..6d65febab --- /dev/null +++ b/Moose Development/Moose/Process_Assign.lua @@ -0,0 +1,105 @@ +--- @module Task_Assign + +--- PROCESS_ASSIGN class +-- @type PROCESS_ASSIGN +-- @field Task#TASK_BASE Task +-- @field Unit#UNIT ProcessUnit +-- @field Zone#ZONE_BASE TargetZone +-- @extends Task2#TASK2 +PROCESS_ASSIGN = { + ClassName = "PROCESS_ASSIGN", +} + + +--- Creates a new task assignment state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. +-- @param #PROCESS_ASSIGN self +-- @param Task#TASK Task +-- @param Unit#UNIT Unit +-- @return #PROCESS_ASSIGN self +function PROCESS_ASSIGN:New( Task, ProcessUnit, TaskBriefing ) + + -- Inherits from BASE + local self = BASE:Inherit( self, PROCESS:New( Task, ProcessUnit ) ) -- #PROCESS_ASSIGN + + self.TaskBriefing = TaskBriefing + + self.Fsm = STATEMACHINE_PROCESS:New( self, { + initial = 'UnAssigned', + events = { + { name = 'Menu', from = 'UnAssigned', to = 'AwaitAccept' }, + { name = 'Assign', from = 'AwaitAccept', to = 'Assigned' }, + { name = 'Reject', from = 'AwaitAccept', to = 'Rejected' }, + { name = 'Fail', from = 'AwaitAccept', to = 'Rejected' }, + }, + callbacks = { + onMenu = self.OnMenu, + onAssign = self.OnAssign, + onReject = self.OnReject, + }, + endstates = { + 'Assigned', 'Rejected' + }, + } ) + + return self +end + +--- StateMachine callback function for a TASK2 +-- @param #PROCESS_ASSIGN self +-- @param StateMachine#STATEMACHINE_TASK Fsm +-- @param #string Event +-- @param #string From +-- @param #string To +function PROCESS_ASSIGN:OnMenu( Fsm, Event, From, To ) + self:E( { Event, From, To, self.ProcessUnit.UnitName} ) + + MESSAGE:New( self.TaskBriefing .. "\nAccess the radio menu to accept the task. You have 30 seconds or the assignment will be cancelled.", 30, "Assignment" ):ToGroup( self.ProcessUnit:GetGroup() ) + self.MenuText = self.Task.TaskName + + local ProcessGroup = self.ProcessUnit:GetGroup() + self.Menu = MENU_GROUP:New( ProcessGroup, "Task " .. self.MenuText .. " acceptance" ) + self.MenuAcceptTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Accept task " .. self.MenuText, self.Menu, self.MenuAssign, self ) + self.MenuRejectTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Reject task " .. self.MenuText, self.Menu, self.MenuReject, self ) +end + +--- Menu function. +-- @param #PROCESS_ASSIGN self +function PROCESS_ASSIGN:MenuAssign() + self:E( ) + + self:NextEvent( self.Fsm.Assign ) +end + +--- Menu function. +-- @param #PROCESS_ASSIGN self +function PROCESS_ASSIGN:MenuReject() + self:E( ) + + self:NextEvent( self.Fsm.Reject ) +end + +--- StateMachine callback function for a TASK2 +-- @param #PROCESS_ASSIGN self +-- @param StateMachine#STATEMACHINE_PROCESS Fsm +-- @param #string Event +-- @param #string From +-- @param #string To +function PROCESS_ASSIGN:OnAssign( Fsm, Event, From, To ) + self:E( { Event, From, To, self.ProcessUnit.UnitName} ) + + self.Menu:Remove() +end + +--- StateMachine callback function for a TASK2 +-- @param #PROCESS_ASSIGN self +-- @param StateMachine#STATEMACHINE_PROCESS Fsm +-- @param #string Event +-- @param #string From +-- @param #string To +function PROCESS_ASSIGN:OnReject( Fsm, Event, From, To ) + self:E( { Event, From, To, self.ProcessUnit.UnitName} ) + + self.Menu:Remove() + self.Task:UnAssignFromUnit( self.ProcessUnit ) + self.ProcessUnit:Destroy() +end diff --git a/Moose Development/Moose/Process_Route.lua b/Moose Development/Moose/Process_Route.lua index 71b7e45cf..35cc6a676 100644 --- a/Moose Development/Moose/Process_Route.lua +++ b/Moose Development/Moose/Process_Route.lua @@ -1,9 +1,9 @@ --- @module Task_Route ---- TASK2_ROUTE_CLIENT class --- @type TASK2_ROUTE_CLIENT --- @field Mission#MISSION Mission --- @field Unit#UNIT TaskUnit +--- PROCESS_ROUTE class +-- @type PROCESS_ROUTE +-- @field Task#TASK TASK +-- @field Unit#UNIT ProcessUnit -- @field Zone#ZONE_BASE TargetZone -- @extends Task2#TASK2 PROCESS_ROUTE = { @@ -12,14 +12,14 @@ PROCESS_ROUTE = { --- Creates a new routing state machine. The task will route a CLIENT to a ZONE until the CLIENT is within that ZONE. --- @param #TASK2_ROUTE_CLIENT self --- @param Mission#MISSION Mission +-- @param #PROCESS_ROUTE self +-- @param Task#TASK Task -- @param Unit#UNIT Unit --- @return #TASK2_ROUTE_CLIENT self -function PROCESS_ROUTE:New( Mission, TaskUnit, TargetZone ) +-- @return #PROCESS_ROUTE self +function PROCESS_ROUTE:New( Task, ProcessUnit, TargetZone ) -- Inherits from BASE - local self = BASE:Inherit( self, TASK2:New( Mission, TaskUnit ) ) -- #TASK2_ROUTE_CLIENT + local self = BASE:Inherit( self, PROCESS:New( Task, ProcessUnit ) ) -- #PROCESS_ROUTE self.TargetZone = TargetZone self.DisplayInterval = 30 @@ -28,7 +28,7 @@ function PROCESS_ROUTE:New( Mission, TaskUnit, TargetZone ) self.DisplayTime = 10 -- 10 seconds is the default self.DisplayCategory = "Route" -- Route is the default display category - self.Fsm = STATEMACHINE_TASK:New( self, { + self.Fsm = STATEMACHINE_PROCESS:New( self, { initial = 'UnArrived', events = { { name = 'Route', from = 'UnArrived', to = 'Arrived' }, @@ -49,24 +49,23 @@ end --- Task Events --- StateMachine callback function for a TASK2 --- @param #TASK2_ROUTE_CLIENT self --- @param StateMachine#STATEMACHINE_TASK Fsm +-- @param #PROCESS_ROUTE self +-- @param StateMachine#STATEMACHINE_PROCESS Fsm -- @param #string Event -- @param #string From -- @param #string To function PROCESS_ROUTE:OnLeaveUnArrived( Fsm, Event, From, To ) - self:E( { Event, From, To, self.TaskUnit.UnitName } ) - local IsInZone = self.TaskUnit:IsInZone( self.TargetZone ) + local IsInZone = self.ProcessUnit:IsInZone( self.TargetZone ) if self.DisplayCount >= self.DisplayInterval then if not IsInZone then local ZoneVec2 = self.TargetZone:GetVec2() local ZonePointVec2 = POINT_VEC2:New( ZoneVec2.x, ZoneVec2.y ) - local TaskUnitVec2 = self.TaskUnit:GetVec2() + local TaskUnitVec2 = self.ProcessUnit:GetVec2() local TaskUnitPointVec2 = POINT_VEC2:New( TaskUnitVec2.x, TaskUnitVec2.y ) local RouteText = TaskUnitPointVec2:GetBRText( ZonePointVec2 ) - self.TaskUnit:Message( RouteText, self.DisplayTime, self.DisplayCategory ) + MESSAGE:New( RouteText, self.DisplayTime, self.DisplayCategory ):ToGroup( self.ProcessUnit:GetGroup() ) end self.DisplayCount = 1 else diff --git a/Moose Development/Moose/Process_SEAD.lua b/Moose Development/Moose/Process_SEAD.lua index ec8f1fdac..0c3b314d3 100644 --- a/Moose Development/Moose/Process_SEAD.lua +++ b/Moose Development/Moose/Process_SEAD.lua @@ -3,38 +3,38 @@ --- PROCESS_SEAD class -- @type PROCESS_SEAD -- @field Unit#UNIT ProcessUnit --- @field Set#SET_UNIT TargetSet +-- @field Set#SET_UNIT TargetSetUnit -- @extends Process#PROCESS PROCESS_SEAD = { ClassName = "PROCESS_SEAD", Fsm = {}, - TargetSet = nil, + TargetSetUnit = nil, } --- Creates a new SEAD task. -- @param #PROCESS_SEAD self --- @param Task#MISSION Task +-- @param Task#TASK Task -- @param Unit#UNIT ProcessUnit --- @param Set#SET_UNIT TargetSet +-- @param Set#SET_UNIT TargetSetUnit -- @return #PROCESS_SEAD self -function PROCESS_SEAD:New( Task, ProcessUnit, TargetSet ) +function PROCESS_SEAD:New( Task, ProcessUnit, TargetSetUnit ) -- Inherits from BASE local self = BASE:Inherit( self, PROCESS:New( Task, ProcessUnit ) ) -- #PROCESS_SEAD - self.TargetSet = TargetSet + self.TargetSetUnit = TargetSetUnit - self.Fsm = STATEMACHINE_TASK:New( self, { + self.Fsm = STATEMACHINE_PROCESS:New( self, { initial = 'Assigned', events = { { name = 'Await', from = 'Assigned', to = 'Waiting' }, { name = 'HitTarget', from = 'Waiting', to = 'Destroy' }, { name = 'MoreTargets', from = 'Destroy', to = 'Waiting' }, { name = 'Destroyed', from = 'Destroy', to = 'Success' }, - { name = 'Killed', from = 'Assigned', to = 'Failed' }, - { name = 'Killed', from = 'Waiting', to = 'Failed' }, - { name = 'Killed', from = 'Destroy', to = 'Failed' }, + { name = 'Fail', from = 'Assigned', to = 'Failed' }, + { name = 'Fail', from = 'Waiting', to = 'Failed' }, + { name = 'Fail', from = 'Destroy', to = 'Failed' }, }, callbacks = { onAwait = self.OnAwait, @@ -48,8 +48,6 @@ function PROCESS_SEAD:New( Task, ProcessUnit, TargetSet ) _EVENTDISPATCHER:OnHit( self.EventHit, self ) - _EVENTDISPATCHER:OnDead( self.EventKilled, self ) - _EVENTDISPATCHER:OnCrash( self.EventKilled, self ) return self end @@ -58,28 +56,26 @@ end --- StateMachine callback function for a PROCESS -- @param #PROCESS_SEAD self --- @param StateMachine#STATEMACHINE_TASK Fsm +-- @param StateMachine#STATEMACHINE_PROCESS Fsm -- @param #string Event -- @param #string From -- @param #string To function PROCESS_SEAD:OnAwait( Fsm, Event, From, To ) self:E( { Event, From, To, self.ProcessUnit.UnitName} ) - self.ProcessUnit:Message( "Waiting", 15 ) self:NextEvent( Fsm.Await ) end --- StateMachine callback function for a PROCESS -- @param #PROCESS_SEAD self --- @param StateMachine#STATEMACHINE_TASK Fsm +-- @param StateMachine#STATEMACHINE_PROCESS Fsm -- @param #string Event -- @param #string From -- @param #string To -- @param Event#EVENTDATA Event function PROCESS_SEAD:OnHitTarget( Fsm, Event, From, To, Event ) - self.ProcessUnit:Message( "Hit Target", 15 ) - if self.TargetSet:Count() > 0 then + if self.TargetSetUnit:Count() > 0 then self:NextEvent( Fsm.MoreTargets ) else self:NextEvent( Fsm.Destroyed ) @@ -88,46 +84,43 @@ end --- StateMachine callback function for a PROCESS -- @param #PROCESS_SEAD self --- @param StateMachine#STATEMACHINE_TASK Fsm +-- @param StateMachine#STATEMACHINE_PROCESS Fsm -- @param #string Event -- @param #string From -- @param #string To function PROCESS_SEAD:OnMoreTargets( Fsm, Event, From, To ) - self.ProcessUnit:Message( "More Targets", 15 ) end --- StateMachine callback function for a PROCESS -- @param #PROCESS_SEAD self --- @param StateMachine#STATEMACHINE_TASK Fsm +-- @param StateMachine#STATEMACHINE_PROCESS Fsm -- @param #string Event -- @param #string From -- @param #string To -- @param Event#EVENTDATA DCSEvent function PROCESS_SEAD:OnKilled( Fsm, Event, From, To ) - self.ProcessUnit:Message( "Player got killed", 15 ) self:NextEvent( Fsm.Restart ) end --- StateMachine callback function for a PROCESS -- @param #PROCESS_SEAD self --- @param StateMachine#STATEMACHINE_TASK Fsm +-- @param StateMachine#STATEMACHINE_PROCESS Fsm -- @param #string Event -- @param #string From -- @param #string To function PROCESS_SEAD:OnRestart( Fsm, Event, From, To ) - self.ProcessUnit:Message( "Restart SEAD Process", 15 ) self:NextEvent( Fsm.Menu ) end --- StateMachine callback function for a PROCESS -- @param #PROCESS_SEAD self --- @param StateMachine#STATEMACHINE_TASK Fsm +-- @param StateMachine#STATEMACHINE_PROCESS Fsm -- @param #string Event -- @param #string From -- @param #string To @@ -148,14 +141,4 @@ function PROCESS_SEAD:EventHit( Event ) end end ---- @param #PROCESS_SEAD self --- @param Event#EVENTDATA Event -function PROCESS_SEAD:EventKilled( Event ) - - if Event.IniUnit then - if Event.IniUnitName == self.ProcessUnit.UnitName then - self:NextEvent( self.Fsm.Killed, Event ) - end - end -end diff --git a/Moose Development/Moose/Scheduler.lua b/Moose Development/Moose/Scheduler.lua index 9d6b66295..afc2352f2 100644 --- a/Moose Development/Moose/Scheduler.lua +++ b/Moose Development/Moose/Scheduler.lua @@ -95,6 +95,7 @@ function SCHEDULER:Stop() self.Repeat = false if self.ScheduleID then + self:E( "Stop Schedule" ) timer.removeFunction( self.ScheduleID ) end self.ScheduleID = nil diff --git a/Moose Development/Moose/Scoring.lua b/Moose Development/Moose/Scoring.lua index facb2d92b..c3b642ce6 100644 --- a/Moose Development/Moose/Scoring.lua +++ b/Moose Development/Moose/Scoring.lua @@ -263,13 +263,16 @@ end --- Registers Scores the players completing a Mission Task. -- @param #SCORING self +-- @param Mission#MISSION Mission -- @param Unit#UNIT PlayerUnit --- @param #string MissionName +-- @param #string Text -- @param #number Score -function SCORING:_AddMissionTaskScore( PlayerUnit, MissionName, Score ) - self:F( { PlayerUnit.UnitName, MissionName, Score } ) +function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score ) local PlayerName = PlayerUnit:GetPlayerName() + local MissionName = Mission:GetName() + + self:F( { Mission:GetName(), PlayerUnit.UnitName, PlayerName, Text, Score } ) if not self.Players[PlayerName].Mission[MissionName] then self.Players[PlayerName].Mission[MissionName] = {} @@ -283,9 +286,9 @@ function SCORING:_AddMissionTaskScore( PlayerUnit, MissionName, Score ) self.Players[PlayerName].Score = self.Players[PlayerName].Score + Score self.Players[PlayerName].Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score - MESSAGE:New( "Player '" .. PlayerName .. "' has finished another Task in Mission '" .. MissionName .. "'. " .. - Score .. " Score points added.", - 20 ):ToAll() + MESSAGE:New( "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " .. + Score .. " points!", + 30 ):ToAll() self:ScoreCSV( PlayerName, "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() ) end diff --git a/Moose Development/Moose/StateMachine.lua b/Moose Development/Moose/StateMachine.lua index 1bd7833ed..02e2167f5 100644 --- a/Moose Development/Moose/StateMachine.lua +++ b/Moose Development/Moose/StateMachine.lua @@ -113,6 +113,8 @@ function STATEMACHINE:_create_transition(name) local fsmparent, event = self:_isendstate( to ) if fsmparent and event then + self:_call_handler(self["onenter" .. to] or self["on" .. to], params) + self:_call_handler(self["onafter" .. name] or self["on" .. name], params) self:_call_handler(self["onstatechange"], params) fsmparent[event]( fsmparent ) execute = false @@ -146,11 +148,13 @@ function STATEMACHINE:_isendstate( state ) self:E( { state = state, endstates = self.endstates, endstate = self.endstates[state] } ) local returnevent = nil local fromstate = fsmparent.current + self:E( fromstate ) + self:E( self.returnevents ) for _, eventname in pairs( self.returnevents ) do local event = fsmparent.events[eventname] self:E( event ) local to = event and event.map[fromstate] or event.map['*'] - if to then + if to and to == state then return fsmparent, eventname end end @@ -232,3 +236,34 @@ function STATEMACHINE_PROCESS:_call_handler( handler, params ) return handler( self.Process, unpack( params ) ) end end + +--- STATEMACHINE_TASK class +-- @type STATEMACHINE_TASK +-- @field Task#TASK_BASE Task +-- @extends StateMachine#STATEMACHINE +STATEMACHINE_TASK = { + ClassName = "STATEMACHINE_TASK", +} + +--- Creates a new STATEMACHINE_TASK object. +-- @param #STATEMACHINE_TASK self +-- @return #STATEMACHINE_TASK +function STATEMACHINE_TASK:New( Task, options ) + + local FsmTask = routines.utils.deepCopy( self ) -- Create a new self instance + local Parent = STATEMACHINE:New(options) + + setmetatable( FsmTask, Parent ) + FsmTask.__index = FsmTask + + FsmTask["onstatechange"] = Task.OnStateChange + FsmTask.Task = Task + + return FsmTask +end + +function STATEMACHINE_TASK:_call_handler( handler, params ) + if handler then + return handler( self.Task, unpack( params ) ) + end +end diff --git a/Moose Development/Moose/Task.lua b/Moose Development/Moose/Task.lua index 32b9438b4..395158d33 100644 --- a/Moose Development/Moose/Task.lua +++ b/Moose Development/Moose/Task.lua @@ -10,18 +10,22 @@ TASK_BASE = { ClassName = "TASK_BASE", TaskScheduler = nil, Processes = {}, + Players = nil, Scores = {}, } --- Instantiates a new TASK_BASE. Should never be used. Interface Class. -- @param #TASK_BASE self -- @return #TASK_BASE self -function TASK_BASE:New() +function TASK_BASE:New( Mission, TaskName ) local self = BASE:Inherit( self, BASE:New() ) self:F() self.Processes = {} self.Fsm = {} + self.Mission = Mission + self.TaskName = TaskName + self.TaskBriefing = "You are assigned to the task: " .. self.TaskName .. "." return self end @@ -31,7 +35,7 @@ end -- @param Group#GROUP TaskGroup -- @return #TASK_BASE self function TASK_BASE:AssignToGroup( TaskGroup ) - self:FZ( TaskGroup:GetName() ) + self:F2( TaskGroup:GetName() ) local TaskUnits = TaskGroup:GetUnits() for UnitID, UnitData in pairs( TaskUnits ) do @@ -44,6 +48,8 @@ function TASK_BASE:AssignToGroup( TaskGroup ) return self end + + --- Add Process to @{Task} with key @{Unit} -- @param #TASK_BASE self -- @param Unit#UNIT TaskUnit @@ -58,11 +64,19 @@ end --- Remove Processes from @{Task} with key @{Unit} -- @param #TASK_BASE self -- @return #TASK_BASE self -function TASK_BASE:RemoveProcesses( TaskUnit ) +function TASK_BASE:RemoveProcesses( TaskUnit, FailProcesses ) local TaskUnitName = TaskUnit:GetName() - for _, Process in pairs( self.Processes[TaskUnitName] ) do + for _, ProcessData in pairs( self.Processes[TaskUnitName] ) do + local Process = ProcessData -- Process#PROCESS + if FailProcesses then + Process.Fsm:Fail() + end + Process:StopEvents() Process = nil + self.Processes[TaskUnitName][_] = nil + self:E( self.Processes[TaskUnitName][_] ) end + self.Processes[TaskUnitName] = nil end --- Add a FiniteStateMachine to @{Task} with key @{Unit} @@ -83,7 +97,20 @@ function TASK_BASE:RemoveStateMachines( TaskUnit ) local TaskUnitName = TaskUnit:GetName() for _, Fsm in pairs( self.Fsm[TaskUnitName] ) do Fsm = nil + self.Fsm[TaskUnitName][_] = nil + self:E( self.Fsm[TaskUnitName][_] ) end + self.Fsm[TaskUnitName] = nil +end + +--- Checks if there is a FiniteStateMachine assigned to @{Unit} for @{Task} +-- @param #TASK_BASE self +-- @param Unit#UNIT TaskUnit +-- @return #TASK_BASE self +function TASK_BASE:HasStateMachine( TaskUnit ) + local TaskUnitName = TaskUnit:GetName() + self:F( { TaskUnitName, self.Fsm[TaskUnitName] ~= nil } ) + return ( self.Fsm[TaskUnitName] ~= nil ) end @@ -94,10 +121,173 @@ end -- @param Unit#UNIT TaskUnit -- @return #TASK_BASE self function TASK_BASE:AssignToUnit( TaskUnit ) + self:F( TaskUnit:GetName() ) return nil end +--- UnAssign the @{Task} from an alive @{Unit}. +-- @param #TASK_BASE self +-- @param Unit#UNIT TaskUnit +-- @return #TASK_BASE self +function TASK_BASE:UnAssignFromUnit( TaskUnit, FailProcesses ) + self:F( TaskUnit:GetName() ) + + if self:HasStateMachine( TaskUnit ) == true then + self:RemoveStateMachines( TaskUnit ) + self:RemoveProcesses( TaskUnit, FailProcesses ) + end + + return self +end + +--- Register a potential new assignment for a new spawned @{Unit}. +-- Tasks only get assigned if there are players in it. +-- @param #TASK_BASE self +-- @param Event#EVENTDATA Event +-- @return #TASK_BASE self +function TASK_BASE:_EventAssignUnit( Event ) + if Event.IniUnit then + self:F( Event ) + local TaskUnit = Event.IniUnit + if TaskUnit:IsAlive() then + local TaskPlayerName = TaskUnit:GetPlayerName() + if TaskPlayerName ~= nil then + if not self:HasStateMachine( TaskUnit ) then + self:AssignToUnit( TaskUnit ) + end + end + end + end + return nil +end + +--- UnAssigns a @{Unit} that is left by a player, crashed, dead, .... +-- There are only assignments if there are players in it. +-- @param #TASK_BASE self +-- @param Event#EVENTDATA Event +-- @return #TASK_BASE self +function TASK_BASE:_EventUnAssignUnit( Event ) + self:F( Event ) + if Event.IniUnit then + local TaskUnit = Event.IniUnit + self:F( TaskUnit:GetName() ) + self:UnAssignFromUnit( TaskUnit, true ) + end + return nil +end + +--- Gets the scoring of the task +-- @param #TASK_BASE self +-- @return Scoring#SCORING Scoring +function TASK_BASE:GetScoring() + return self.Mission:GetScoring() +end + +--- Sets the name of the task +-- @param #TASK_BASE self +-- @param #string TaskName +-- @return Scoring#SCORING Scoring +function TASK_BASE:SetName( TaskName ) + self.TaskName = TaskName +end + +--- Gets the name of the task +-- @param #TASK_BASE self +-- @return Scoring#SCORING Scoring +function TASK_BASE:GetName() + return self.TaskName +end + + +--- Sets a @{Task} to status **Success**. +-- @param #TASK_BASE self +function TASK_BASE:StateSuccess() + self:SetState( self, "State", "Success" ) +end + +--- Is the @{Task} status **Success**. +-- @param #TASK_BASE self +function TASK_BASE:IsStateSuccess() + return self:GetStateString() == "Success" +end + +--- Sets a @{Task} to status **Failed**. +-- @param #TASK_BASE self +function TASK_BASE:StateFailed() + self:SetState( self, "State", "Failed" ) +end + +--- Is the @{Task} status **Failed**. +-- @param #TASK_BASE self +function TASK_BASE:IsStateFailed() + return self:GetStateString() == "Failed" +end + +--- Sets a @{Task} to status **Planned**. +-- @param #TASK_BASE self +function TASK_BASE:StatePlanned() + self:SetState( self, "State", "Planned" ) +end + +--- Is the @{Task} status **Planned**. +-- @param #TASK_BASE self +function TASK_BASE:IsStatePlanned() + return self:GetStateString() == "Planned" +end + +--- Sets a @{Task} to status **Assigned**. +-- @param #TASK_BASE self +function TASK_BASE:StateAssigned() + self:SetState( self, "State", "Assigned" ) +end + +--- Is the @{Task} status **Assigned**. +-- @param #TASK_BASE self +function TASK_BASE:IsStateAssigned() + return self:GetStateString() == "Assigned" +end + +--- Sets a @{Task} to status **Hold**. +-- @param #TASK_BASE self +function TASK_BASE:StateHold() + self:SetState( self, "State", "Hold" ) +end + +--- Is the @{Task} status **Hold**. +-- @param #TASK_BASE self +function TASK_BASE:IsStateHold() + return self:GetStateString() == "Hold" +end + +--- Sets a @{Task} to status **Replanned**. +-- @param #TASK_BASE self +function TASK_BASE:StateReplanned() + self:SetState( self, "State", "Replanned" ) +end + +--- Is the @{Task} status **Replanned**. +-- @param #TASK_BASE self +function TASK_BASE:IsStateReplanned() + return self:GetStateString() == "Replanned" +end + +--- Gets the @{Task} status. +-- @param #TASK_BASE self +function TASK_BASE:GetStateString() + return self:GetState( self, "State" ) +end + +--- Sets a @{Task} briefing. +-- @param #TASK_BASE self +-- @param #string TaskBriefing +-- @return self +function TASK_BASE:SetBriefing( TaskBriefing ) + self.TaskBriefing = TaskBriefing + return self +end + + --- @param #TASK_BASE self function TASK_BASE:_Schedule() diff --git a/Moose Development/Moose/Task_SEAD.lua b/Moose Development/Moose/Task_SEAD.lua index 31a3856af..c80aa1023 100644 --- a/Moose Development/Moose/Task_SEAD.lua +++ b/Moose Development/Moose/Task_SEAD.lua @@ -9,15 +9,24 @@ TASK_SEAD = { --- Instantiates a new TASK_SEAD. Should never be used. Interface Class. -- @param #TASK_SEAD self +-- @param Mission#MISSION Mission -- @param Set#SET_UNIT UnitSetTargets +-- @param Zone#ZONE_BASE TargetZone -- @return #TASK_SEAD self -function TASK_SEAD:New( TargetSetUnit, TargetZone ) - local self = BASE:Inherit( self, BASE:New() ) +function TASK_SEAD:New( Mission, TargetSetUnit, TargetZone ) + local self = BASE:Inherit( self, TASK_BASE:New( Mission, "SEAD" ) ) self:F() - self.TargetSetUnit= TargetSetUnit + self.TargetSetUnit = TargetSetUnit self.TargetZone = TargetZone + _EVENTDISPATCHER:OnBirth( self._EventAssignUnit, self ) + _EVENTDISPATCHER:OnPlayerEnterUnit(self._EventAssignUnit, self ) + _EVENTDISPATCHER:OnPlayerLeaveUnit(self._EventUnAssignUnit, self ) + _EVENTDISPATCHER:OnCrash(self._EventUnAssignUnit, self ) + _EVENTDISPATCHER:OnDead(self._EventUnAssignUnit, self ) + _EVENTDISPATCHER:OnPilotDead(self._EventUnAssignUnit, self ) + return self end @@ -26,33 +35,55 @@ end -- @param Unit#UNIT TaskUnit -- @return #TASK_SEAD self function TASK_SEAD:AssignToUnit( TaskUnit ) + self:F( TaskUnit:GetName() ) + local ProcessAssign = self:AddProcess( TaskUnit, PROCESS_ASSIGN:New( self, TaskUnit, self.TaskBriefing ) ) local ProcessRoute = self:AddProcess( TaskUnit, PROCESS_ROUTE:New( self, TaskUnit, self.TargetZone ) ) - local ProcessSEAD = self:AddProcess( TaskUnit, PROCESS_SEAD:New( self, TaskUnit, self.TargetUnitSet ) ) + local ProcessSEAD = self:AddProcess( TaskUnit, PROCESS_SEAD:New( self, TaskUnit, self.TargetSetUnit ) ) - local Process = self:AddStateMachine( TaskUnit, STATEMACHINE:New( { + local Process = self:AddStateMachine( TaskUnit, STATEMACHINE_TASK:New( self, { initial = 'None', events = { - { name = 'Start', from = 'None', to = 'Assigned' }, - { name = 'Next', from = 'Unassigned', to = 'Assigned' }, + { name = 'Next', from = 'None', to = 'Planned' }, + { name = 'Next', from = 'Planned', to = 'Assigned' }, + { name = 'Reject', from = 'Planned', to = 'Rejected' }, { name = 'Next', from = 'Assigned', to = 'Success' }, { name = 'Fail', from = 'Assigned', to = 'Failed' }, { name = 'Fail', from = 'Arrived', to = 'Failed' } }, + callbacks = { + onNext = self.OnNext, + onRemove = self.OnRemove, + }, subs = { - Route = { onstateparent = 'Assigned', oneventparent = 'Start', fsm = ProcessRoute.Fsm, event = 'Route' }, + Assign = { onstateparent = 'Planned', oneventparent = 'Next', fsm = ProcessAssign.Fsm, event = 'Menu', returnevents = { 'Next', 'Reject' } }, + Route = { onstateparent = 'Assigned', oneventparent = 'Next', fsm = ProcessRoute.Fsm, event = 'Route' }, Sead = { onstateparent = 'Assigned', oneventparent = 'Next', fsm = ProcessSEAD.Fsm, event = 'Await', returnevents = { 'Next' } } } } ) ) - ---Task_Client_Sead:AddScore( "Destroy", "Destroyed RADAR", 25 ) - ---Task_Client_Sead:AddScore( "Success", "Destroyed all radars!!!", 100 ) + ProcessRoute:AddScore( "Failed", "failed to destroy a radar", -100 ) + ProcessSEAD:AddScore( "Destroy", "destroyed a radar", 25 ) + ProcessSEAD:AddScore( "Failed", "failed to destroy a radar", -100 ) - Process:Start() + Process:Next() return self end +--- StateMachine callback function for a TASK +-- @param #TASK_SEAD self +-- @param StateMachine#STATEMACHINE_TASK Fsm +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Event#EVENTDATA Event +function TASK_SEAD:OnNext( Fsm, Event, From, To, Event ) + + self:SetState( self, "State", To ) + +end + --- @param #TASK_SEAD self function TASK_SEAD:_Schedule() self:F2() diff --git a/Moose Development/Moose/Unit.lua b/Moose Development/Moose/Unit.lua index 32b4d7bb2..a528f3782 100644 --- a/Moose Development/Moose/Unit.lua +++ b/Moose Development/Moose/Unit.lua @@ -179,6 +179,24 @@ function UNIT:IsActive() return nil end +--- Destroys the @{Unit}. +-- @param Unit#UNIT self +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:Destroy() + self:F2( self.UnitName ) + + local DCSUnit = self:GetDCSObject() + + if DCSUnit then + + DCSUnit:destroy() + end + + return nil +end + + + --- Returns the Unit's callsign - the localized string. -- @param Unit#UNIT self -- @return #string The Callsign of the Unit. diff --git a/Moose Test Missions/Moose_Test_TASK_SEAD/Moose_Test_TASK_SEAD.lua b/Moose Test Missions/Moose_Test_TASK_SEAD/Moose_Test_TASK_SEAD.lua index 467645d6f..cb4f2a967 100644 --- a/Moose Test Missions/Moose_Test_TASK_SEAD/Moose_Test_TASK_SEAD.lua +++ b/Moose Test Missions/Moose_Test_TASK_SEAD/Moose_Test_TASK_SEAD.lua @@ -1,15 +1,16 @@ -local Mission = MISSION:New( 'SEAD Targets', "Strategic", "SEAD the enemy", "RUSSIA" ) +local Mission = MISSION:New( 'SEAD Targets', "Strategic", "SEAD the enemy", coalition.side.RED ) local Scoring = SCORING:New( "SEAD" ) Mission:AddScoring( Scoring ) -local Client = CLIENT:FindByName( "Test SEAD" ) +local SEADGroup = GROUP:FindByName( "Test SEAD" ) local TargetSet = SET_UNIT:New():FilterPrefixes( "US Hawk SR" ):FilterStart() local TargetZone = ZONE:New( "Target Zone" ) -local Task_SEAD = TASK_SEAD:New( TargetSet, TargetZone ) +local TaskSEAD = TASK_SEAD:New( Mission, TargetSet, TargetZone ):SetName( "SEAD Radars" ):AssignToGroup( SEADGroup ) + diff --git a/Moose Test Missions/Moose_Test_TASK_SEAD/Moose_Test_TASK_SEAD.miz b/Moose Test Missions/Moose_Test_TASK_SEAD/Moose_Test_TASK_SEAD.miz index 90fefdb1784c77b601a086c9383dc38a0919c5d1..e1f933157793914a29276d3453a73e2d139314d7 100644 GIT binary patch delta 17772 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 delta 16783 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 delta 10825 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