diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index a64e5a64b..180b892d0 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -737,7 +737,7 @@ function EVENT:onEvent( Event ) Event.WeaponName = Event.Weapon:getTypeName() --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end - self:E( { _EVENTCODES[Event.id], Event.initiator, Event.IniDCSUnitName, Event.target, Event.TgtDCSUnitName, Event.weapon, Event.WeaponName } ) + self:E( { _EVENTCODES[Event.id], Event, Event.IniDCSUnitName, Event.TgtDCSUnitName } ) -- Okay, we got the event from DCS. Now loop the self.Events[] table for the received Event.id, and for each EventData registered, check if a function needs to be called. for EventClass, EventData in pairs( self.Events[Event.id] ) do diff --git a/Moose Development/Moose/Core/StateMachine.lua b/Moose Development/Moose/Core/StateMachine.lua index 132144b7b..ecbf176f8 100644 --- a/Moose Development/Moose/Core/StateMachine.lua +++ b/Moose Development/Moose/Core/StateMachine.lua @@ -451,6 +451,12 @@ function STATEMACHINE_PROCESS:onenterAssigned( ProcessUnit ) self.Task:Assign() end +function STATEMACHINE_PROCESS:onenterFailed( ProcessUnit ) + self:E( "Failed" ) + + self.Task:Fail() +end + function STATEMACHINE_PROCESS:onenterSuccess( ProcessUnit ) self:E( "Success" ) diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index dd0d5826e..9d7245a8b 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -53,7 +53,7 @@ -- @type TASK_BASE -- @field Scheduler#SCHEDULER TaskScheduler -- @field Mission#MISSION Mission --- @field StateMachine#STATEMACHINE Fsm +-- @field Core.StateMachine#STATEMACHINE_PROCESS FsmTemplate -- @field Set#SET_GROUP SetGroup The Set of Groups assigned to the Task -- @extends Core.StateMachine#STATEMACHINE_TASK TASK_BASE = { @@ -65,6 +65,7 @@ TASK_BASE = { Scores = {}, Menu = {}, SetGroup = nil, + FsmTemplate = nil, } --- Instantiates a new TASK_BASE. Should never be used. Interface Class. @@ -73,12 +74,11 @@ TASK_BASE = { -- @param Set#SET_GROUP SetGroupAssign The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task -- @param #string TaskType The type of the Task --- @param #string TaskCategory The category of the Task (A2G, A2A, Transport, ... ) -- @return #TASK_BASE self -function TASK_BASE:New( Mission, SetGroupAssign, TaskName, TaskType, TaskCategory ) +function TASK_BASE:New( Mission, SetGroupAssign, TaskName, TaskType ) - local self = BASE:Inherit( self, STATEMACHINE_TASK:New( {} ) ) + local self = BASE:Inherit( self, STATEMACHINE_TASK:New( {} ) ) -- Core.StateMachine#STATEMACHINE_TASK self:SetInitialState( "Planned" ) self:AddAction( "Planned", "Assign", "Assigned" ) @@ -93,7 +93,6 @@ function TASK_BASE:New( Mission, SetGroupAssign, TaskName, TaskType, TaskCategor self.Mission = Mission self.SetGroup = SetGroupAssign - self:SetCategory( TaskCategory ) self:SetType( TaskType ) self:SetName( TaskName ) self:SetID( Mission:GetNextTaskID( self ) ) -- The Mission orchestrates the task sequences .. @@ -118,19 +117,6 @@ function TASK_BASE:GetGroups() return self.SetGroup end ---- Cleans all references of a TASK_BASE. --- @param #TASK_BASE self --- @return #nil -function TASK_BASE:CleanUp() - - _EVENTDISPATCHER:OnPlayerLeaveRemove( self ) - _EVENTDISPATCHER:OnDeadRemove( self ) - _EVENTDISPATCHER:OnCrashRemove( self ) - _EVENTDISPATCHER:OnPilotDeadRemove( self ) - - return nil -end - function TASK_BASE:GetFsmTemplate() return self.FsmTemplate @@ -183,7 +169,7 @@ function TASK_BASE:AssignToUnit( TaskUnit ) -- Copy the FsmTemplate, which is not assigned to a Unit. -- Assign the FsmTemplate to the TaskUnit. local FsmTemplate = self:GetFsmTemplate() - local FsmUnit = UTILS.DeepCopy( FsmTemplate ) + local FsmUnit = UTILS.DeepCopy( FsmTemplate ) -- Core.StateMachine#STATEMACHINE_PROCESS FsmUnit:Assign( self, TaskUnit ) -- Assign each FsmSub in FsmUnit to the TaskUnit. @@ -192,28 +178,15 @@ function TASK_BASE:AssignToUnit( TaskUnit ) for FsmSubID, FsmSub in pairs( FsmUnit:GetSubs() ) do self:E( { "Sub ID", FsmSub.fsm:GetClassNameAndID(), FsmSubID } ) FsmSub.fsm:Assign( self, TaskUnit ) - --FsmSub.fsm:_SetDestructor() - - - --FsmSub.fsm = nil - --collectgarbage() end - --- for TransitionID, TransitionTemplate in ipairs( self.TransitionTemplates ) do --- self:E( TransitionTemplate ) --- FSM:AddTransition( TransitionTemplate.From, TransitionTemplate.Event, TransitionTemplate.To ) --- end - - -- Copy each ProcessTemplate for the TaskUnit that is alive, as set as a template at the Parent. - -- Each Process will start From a state, upon a fired Event. - -- Upon finalization of the Process, the ReturnEvents contain for which Return state which Event of the Parent needs to be fired. - -- The Return state of the Process is transferred to the Parent. --- for ProcessID, ProcessTemplate in ipairs( self.ProcessTemplates ) do --- FSM:AddProcess( ProcessTemplate.From, ProcessTemplate.Event, Process, ProcessTemplate.ReturnEvents ) --- self:E( { "Process ID", Process:GetClassNameAndID() } ) --- Process:Assign( self, TaskUnit ) --- end + -- Set the events + FsmUnit:EventOnPilotDead( + --- @param Core.Event#EVENTDATA EventData + function( self, EventData ) + self:__Fail( 1 ) + end + ) FsmUnit:SetInitialState( "Planned" ) FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. @@ -552,16 +525,15 @@ function TASK_BASE:GetScoring() end ---- Gets the Task Index, which is a combination of the Task category, the Task type, the Task name. +--- Gets the Task Index, which is a combination of the Task type, the Task name. -- @param #TASK_BASE self -- @return #string The Task ID function TASK_BASE:GetTaskIndex() - local TaskCategory = self:GetCategory() local TaskType = self:GetType() local TaskName = self:GetName() - return TaskCategory .. "." ..TaskType .. "." .. TaskName + return TaskType .. "." .. TaskName end --- Sets the Name of the Task @@ -592,20 +564,6 @@ function TASK_BASE:GetType() return self.TaskType end ---- Sets the Category of the Task --- @param #TASK_BASE self --- @param #string TaskCategory -function TASK_BASE:SetCategory( TaskCategory ) - self.TaskCategory = TaskCategory -end - ---- Gets the Category of the Task --- @param #TASK_BASE self --- @return #string TaskCategory -function TASK_BASE:GetCategory() - return self.TaskCategory -end - --- Sets the ID of the Task -- @param #TASK_BASE self -- @param #string TaskID @@ -810,14 +768,11 @@ function TASK_BASE:onstatechange( Event, From, To ) if self:IsTrace() then MESSAGE:New( "Task " .. self.TaskName .. " : " .. Event .. " changed to state " .. To, 15 ):ToAll() end - - self:E( { Event, From, To, self:IsTrace() } ) - self:E( self.Scores ) if self.Scores[To] then local Scoring = self:GetScoring() - self:E( Scoring ) if Scoring then + self:E( { self.Scores[To].ScoreText, self.Scores[To].Score } ) Scoring:_AddMissionScore( self.Mission, self.Scores[To].ScoreText, self.Scores[To].Score ) end end diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 066d19b5c..4ecfdffab 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -38,7 +38,7 @@ do -- TASK_A2G -- @param Zone#ZONE_BASE TargetZone -- @return #TASK_A2G self function TASK_A2G:New( Mission, SetGroup, TaskName, TaskType, TargetSetUnit, TargetZone, FACUnit ) - local self = BASE:Inherit( self, TASK_BASE:New( Mission, SetGroup, TaskName, TaskType, "A2G" ) ) + local self = BASE:Inherit( self, TASK_BASE:New( Mission, SetGroup, TaskName, TaskType ) ) self:F() self.TargetSetUnit = TargetSetUnit diff --git a/Moose Development/Moose/Tasking/Task_SEAD.lua b/Moose Development/Moose/Tasking/Task_SEAD.lua index 89fe1ae45..4242f17aa 100644 --- a/Moose Development/Moose/Tasking/Task_SEAD.lua +++ b/Moose Development/Moose/Tasking/Task_SEAD.lua @@ -39,7 +39,7 @@ do -- TASK_SEAD -- @param Zone#ZONE_BASE TargetZone -- @return #TASK_SEAD self function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TargetZone ) - local self = BASE:Inherit( self, TASK_BASE:New( Mission, SetGroup, TaskName, "SEAD", "A2G" ) ) -- Tasking.Task_SEAD#TASK_SEAD + local self = BASE:Inherit( self, TASK_BASE:New( Mission, SetGroup, TaskName, "SEAD" ) ) -- Tasking.Task_SEAD#TASK_SEAD self:F() self.TargetSetUnit = TargetSetUnit diff --git a/Moose Test Missions/Moose_Test_Tasking/Moose_Test_Task_SEAD/Moose_Test_Task_SEAD.lua b/Moose Test Missions/Moose_Test_Tasking/Moose_Test_Task_SEAD/Moose_Test_Task_SEAD.lua index 5e715d2ca..9b785f21b 100644 --- a/Moose Test Missions/Moose_Test_Tasking/Moose_Test_Task_SEAD/Moose_Test_Task_SEAD.lua +++ b/Moose Test Missions/Moose_Test_Tasking/Moose_Test_Task_SEAD/Moose_Test_Task_SEAD.lua @@ -1,41 +1,103 @@ +-- This test mission is a test bed for the TASKING framework. +-- It creates an head quarters (HQ), which contains one mission with one task to be accomplished. +-- When the pilot joins the plane, it will need to accept the task using the HQ menu. +-- Once the task is accepted, the group of the pilot will be assigned to the task. +-- The pilot will need to fly to the attack zone and elimitate all targets reported. +-- A smoking system is available that the pilot can use the acquire targets. +-- Once all targets are elimitated, the task is finished, and the mission is set to complete. +-- If the pilot crashes during flying, the task will fail, and the mission is set to failed. -env.info( "Lua Version = " .. _VERSION ) - --- Test Garbage control of one declared PROCESS. -do - local Process = PROCESS_ASSIGN_ACCEPT:New( "SEAD the Area" ) -end - -collectgarbage() - +-- Create the HQ object. local HQ = COMMANDCENTER:New( GROUP:FindByName( "HQ" ) ) -local Mission = MISSION:New( HQ, 'SEAD Targets', "Strategic", "SEAD the enemy", coalition.side.RED ) +-- MOOSE contains a MISSION class. Use the MISSION class to setup missions, containing tasks to be executed. +-- Create the Mission object, and attach the Mission to the HQ object. +-- The Mission accepts 4 parameters: +-- 1. The HQ object +-- 2. The name of the Mission +-- 3. The type of Mission, this can be any word like "Strategic", "Tactical", "Urgent", "Optional", "Secondary"... +-- 4. The briefing of the Mission. This briefing is shown when the pilot joins a Task within the Mission. +local Mission = MISSION:New( HQ, 'SEAD Targets', "Strategic", "SEAD the enemy" ) + + +-- MOOSE contains a SCORING class. Use the SCORING class to account the scores of achievements made by the pilots. +-- The scoring system is a standalone object, so here the Scoring object is created. local Scoring = SCORING:New( "SEAD" ) +-- The Scoring object is attached to the Mission object. +-- By doing this, now the Mission can set at defined states in tasks ( and in processes within the tasks ) scoring values, and a text. See later. Mission:AddScoring( Scoring ) +-- Define the set of group of planes that can be assigned to the Mission object. local SEADSet = SET_GROUP:New():FilterPrefixes( "Test SEAD"):FilterStart() + +-- Define the set of units that are the targets. +-- Note that I use FilterOnce, which means that the set will be defined only once, +-- and will not be continuously updated! local TargetSet = SET_UNIT:New():FilterPrefixes( "US Hawk SR" ):FilterOnce() +-- Define the zone to where the pilot needs to navigate. local TargetZone = ZONE:New( "Target Zone" ) -local TaskSEAD = TASK_BASE:New( Mission, SEADSet, "SEAD Radars", "A2G", "SEAD" ) -- Tasking.Task#TASK_BASE - --:New( Mission, SEADSet, "SEAD Radars", TargetSet, TargetZone ) - +-- MOOSE contains a TASK_BASE class. Use the TASK class to define a new Task object and attach it to a Mission object. +-- Here we define a new TaskSEAD object, and attach it to the Mission object. +-- ( The TASK_BASE class is the base class for ALL derived Task templates. +-- Task templates are TASK classes that quickly setup a Task scenario with given parameters. ) +-- +-- The TASK_BASE class is thus the primary task, and a task scenario will need to be provided to the TaskSEAD of the states and events that form the task. +-- TASK_BASE gets a couple of parameters: +-- 1. The Mission for which the Task needs to be achieved. +-- 2. The set of groups of planes that pilots can join. +-- 3. The name of the Task... This can be any name, and will be provided when the Pilot joins the task. +-- 4. A type of the Task. When Tasks are in state Planned, then a menu can be provided that group the task based on this given type. +local TaskSEAD = TASK_BASE:New( Mission, SEADSet, "SEAD Radars", "SEAD" ) -- Tasking.Task#TASK_BASE + +-- This is now an important part of the Task process definition. +-- Each TASK contains a "Process Template". +-- You need to define this process Template by added Actions and Processes, otherwise, the task won't do anything. +-- This call retrieves the Finite State Machine template of the Task. +-- This template WILL NEVER DIRECTLY BE EXECUTED. +-- But, when a Pilot joins a UNIT as defined within the SEADSet, the TaskSEAD will COPY the FsmSEAD to a NEW INTERNAL OBJECT and assign the COPIED FsmSEAD to the UNIT of the player. +-- There can be many copied FsmSEAD objects internally active within TaskSEAD, for each pilot that joined the Task one is instantiated. +-- The reason why this is done, is that each unit as a role within the Task, and can have different status. +-- Therefore, the FsmSEAD is a TEMPLATE PROCESS of the TASK, and must be designed as a UNIT with a player is executing that PROCESS. local FsmSEAD = TaskSEAD:GetFsmTemplate() +-- Adding a new sub-process to the Task Template. +-- At first, the task needs to be accepted by a pilot. +-- We use for this the SUB-PROCESS PROCESS_ASSIGN_ACCEPT. +-- The method on the FsmSEAD AddProcess accepts the following parameters: +-- 1. State From "Planned". When the Fsm is in state "Planned", allow the event "Accept". +-- 2. Event "Accept". This event can be triggered through FsmSEAD:Accept() or FsmSEAD:__Accept( 1 ). See documentation on state machines. +-- 3. The PROCESS derived class. In this case, we use the PROCESS_ASSIGN_ACCEPT to accept the task and provide a briefing. So, when the event "Accept" is fired, this process is executed. +-- 4. A table with the "return" states of the PROCESS_ASSIGN_ACCEPT process. This table indicates that for a certain return state, a further event needs to be called. +-- 4.1 When the return state is Assigned, fire the event in the Task FsmSEAD:Route() +-- 4.2 When the return state is Rejected, fire the event in the Task FsmSEAD:Eject() +-- All other AddProcess calls are working in a similar manner. FsmSEAD:AddProcess( "Planned", "Accept", PROCESS_ASSIGN_ACCEPT:New( "SEAD the Area" ), { Assigned = "Route", Rejected = "Eject" } ) + +-- Same, adding a process. FsmSEAD:AddProcess( "Assigned", "Route", PROCESS_ROUTE_ZONE:New( TargetZone, 3000 ), { Arrived = "Update" } ) + +-- Adding a new Action... +-- Actions define also the flow of the Task, but the actions will need to be programmed within your script. +-- See the state machine explanation for further details. +-- The AddAction received a couple of parameters: +-- 1. State From "Rejected". When the FsmSEAD is in state "Rejected", the event "Eject" can be fired. +-- 2. Event "Eject". This event can be triggered synchronously through FsmSEAD:Eject() or asynchronously through FsmSEAD:__Eject(secs). +-- 3. State To "Planned". After the event has been fired, the FsmSEAD will transition to Planned. FsmSEAD:AddAction ( "Rejected", "Eject", "Planned" ) FsmSEAD:AddAction ( "Arrived", "Update", "Updated" ) FsmSEAD:AddProcess( "Updated", "Account", PROCESS_ACCOUNT_DEADS:New( TargetSet, "SEAD" ), { Accounted = "Success" } ) FsmSEAD:AddProcess( "Updated", "Smoke", PROCESS_SMOKE_TARGETS_ZONE:New( TargetSet, TargetZone ) ) FsmSEAD:AddAction ( "Accounted", "Success", "Success" ) -FsmSEAD:AddAction ( "Failed", "Fail", "Failed" ) +FsmSEAD:AddAction ( "*", "Fail", "Failed" ) +-- Now we will set the SCORING. Scoring is set using the TaskSEAD object. +-- Scores can be set on the status of the Task, and on Process level. TaskSEAD:AddScoreTask( "Success", "Destroyed all target radars", 250 ) TaskSEAD:AddScoreTask( "Failed", "Failed to destroy all target radars", -100 ) + TaskSEAD:AddScoreProcess( "Account", "Account", "destroyed a radar", 25 ) TaskSEAD:AddScoreProcess( "Account", "Fail", "failed to destroy a radar", -100 ) diff --git a/Moose Test Missions/Moose_Test_Tasking/Moose_Test_Task_SEAD/Moose_Test_Task_SEAD.miz b/Moose Test Missions/Moose_Test_Tasking/Moose_Test_Task_SEAD/Moose_Test_Task_SEAD.miz index 709d26d82..0f5cd8705 100644 Binary files a/Moose Test Missions/Moose_Test_Tasking/Moose_Test_Task_SEAD/Moose_Test_Task_SEAD.miz and b/Moose Test Missions/Moose_Test_Tasking/Moose_Test_Task_SEAD/Moose_Test_Task_SEAD.miz differ