From 4034461488b8b96315827c56effd5c3c4feb4450 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 2 Dec 2016 14:47:58 +0100 Subject: [PATCH] Templates progress, not perfect yet... issues with smoke issues with accounting issues with scoring minor things, should be solvable. --- Moose Development/Moose/Core/StateMachine.lua | 210 ++++++++++++------ Moose Development/Moose/Process/Account.lua | 36 ++- Moose Development/Moose/Process/Assign.lua | 23 +- Moose Development/Moose/Process/Route.lua | 34 ++- Moose Development/Moose/Process/Smoke.lua | 6 +- Moose Development/Moose/Tasking/Mission.lua | 19 +- Moose Development/Moose/Tasking/Task.lua | 41 +--- .../Moose_Test_Task_SEAD.lua | 18 +- .../Moose_Test_Task_SEAD.miz | Bin 28428 -> 28390 bytes 9 files changed, 198 insertions(+), 189 deletions(-) diff --git a/Moose Development/Moose/Core/StateMachine.lua b/Moose Development/Moose/Core/StateMachine.lua index 1cdf636d7..429916fec 100644 --- a/Moose Development/Moose/Core/StateMachine.lua +++ b/Moose Development/Moose/Core/StateMachine.lua @@ -40,49 +40,28 @@ function STATEMACHINE:New( FsmT ) --setmetatable( self, MT ) --self.__index = self - for TransitionID, Transition in pairs( FsmT:GetTransitions() ) do - self:AddAction( Transition.From, Transition.Event, Transition.To ) - self.FsmT:CopyCallHandler( self, "onenter", Transition.From ) - self.FsmT:CopyCallHandler( self, "onleave", Transition.From ) - self.FsmT:CopyCallHandler( self, "onenter", Transition.To ) - self.FsmT:CopyCallHandler( self, "onleave", Transition.To ) - self.FsmT:CopyCallHandler( self, "onbefore", Transition.Event ) - self.FsmT:CopyCallHandler( self, "onafter", Transition.Event ) - end - - for ProcessID, Process in pairs( self.FsmT:GetProcesses() ) do - self:E( Process ) - local FsmProcess = self:AddProcess(Process.From, Process.Event, Process.Process:New( unpack( Process.Arguments ) ), Process.ReturnEvents ) - self.FsmT:CopyCallHandler( FsmProcess, "onenter", Process.From ) - self.FsmT:CopyCallHandler( FsmProcess, "onleave", Process.From ) - self.FsmT:CopyCallHandler( FsmProcess, "onbefore", Process.Event ) - self.FsmT:CopyCallHandler( FsmProcess, "onafter", Process.Event ) - - end - - for EndStateID, EndState in pairs( FsmT:EndStates() ) do - self:E( EndState ) - self:AddEndState( EndState ) - end - - self:SetStartState( FsmT:GetStartState() ) - - self.options = options or {} self.options.subs = self.options.subs or {} self.current = self.options.initial or 'none' self.events = {} self.subs = {} self.endstates = {} + + self.Scores = {} + FsmT = FsmT or STATEMACHINE_TEMPLATE:New( "" ) + + self:SetStartState( FsmT:GetStartState() ) + + for TransitionID, Transition in pairs( FsmT:GetTransitions() ) do + self:AddTransition( Transition.From, Transition.Event, Transition.To ) + end + + self:CopyCallHandlers( FsmT ) + return self end -function STATEMACHINE:SetInitialState( State ) - self.current = State -end - - function STATEMACHINE:AddTransition( From, Event, To ) @@ -110,13 +89,9 @@ function STATEMACHINE:AddProcess( From, Event, Process, ReturnEvents ) sub.event = "Start" sub.ReturnEvents = ReturnEvents - -- Make the reference table weak. - -- setmetatable( self.options.subs, { __mode = "v" } ) - self.options.subs[Event] = sub - self:_submap( self.subs, sub, nil ) - self:AddAction( From, Event, From ) + self:AddTransition( From, Event, From ) return Process end @@ -125,6 +100,10 @@ function STATEMACHINE:AddEndState( State ) self.endstates[State] = State end +function STATEMACHINE:SetStartState( State ) + self.current = State +end + function STATEMACHINE:GetSubs() return self.options.subs @@ -157,7 +136,7 @@ function STATEMACHINE:_submap( subs, sub, name ) subs[sub.FromParent][sub.EventParent] = subs[sub.FromParent][sub.EventParent] or {} -- Make the reference table weak. - setmetatable( subs[sub.FromParent][sub.EventParent], { __mode = "k" } ) + -- setmetatable( subs[sub.FromParent][sub.EventParent], { __mode = "k" } ) subs[sub.FromParent][sub.EventParent][sub] = {} subs[sub.FromParent][sub.EventParent][sub].fsm = sub.fsm @@ -325,6 +304,27 @@ function STATEMACHINE:cannot(e) return not self:can(e) end +function STATEMACHINE:CopyCallHandlers( FsmT ) + + local Parent = BASE:GetParent( FsmT ) + if Parent then + self:CopyCallHandlers( Parent ) + end + for ElementID, Element in pairs( FsmT ) do + self:E( { ElementID = ElementID } ) + if type( Element ) == "function" then + if ElementID.find( ElementID, "^onbefore" ) or + ElementID.find( ElementID, "^onafter" ) or + ElementID.find( ElementID, "^onenter" ) or + ElementID.find( ElementID, "^onleave" ) or + ElementID.find( ElementID, "^onfunc" ) then + self[ ElementID ] = Element + end + end + end +end + + function STATEMACHINE:todot(filename) local dotfile = io.open(filename,'w') dotfile:write('digraph {\n') @@ -388,9 +388,21 @@ function STATEMACHINE_CONTROLLABLE:GetControllable() end function STATEMACHINE_CONTROLLABLE:_call_handler( handler, params ) + + local ErrorHandler = function( errmsg ) + + env.info( "Error in SCHEDULER function:" .. errmsg ) + if debug ~= nil then + env.info( debug.traceback() ) + end + + return errmsg + end + if self[handler] then self:E( "Calling " .. handler ) - return self[handler]( self, self.Controllable, unpack( params ) ) + return xpcall( function() return self[handler]( self, self.Controllable, unpack( params ) ) end, ErrorHandler ) + --return self[handler]( self, self.Controllable, unpack( params ) ) end end @@ -406,9 +418,26 @@ STATEMACHINE_PROCESS = { --- Creates a new STATEMACHINE_PROCESS object. -- @param #STATEMACHINE_PROCESS self -- @return #STATEMACHINE_PROCESS -function STATEMACHINE_PROCESS:New( FSMT ) +function STATEMACHINE_PROCESS:New( FsmT, Controllable, Task ) - local self = BASE:Inherit( self, STATEMACHINE_CONTROLLABLE:New( FSMT ) ) -- StateMachine#STATEMACHINE_PROCESS + local self = BASE:Inherit( self, STATEMACHINE_CONTROLLABLE:New( FsmT ) ) -- StateMachine#STATEMACHINE_PROCESS + + self:Assign( Controllable, Task ) + self.ClassName = FsmT._Name + + for ParameterID, Parameter in pairs( FsmT:GetParameters() ) do + self[ ParameterID ] = Parameter + end + + for ProcessID, Process in pairs( FsmT:GetProcesses() ) do + self:E( Process ) + local FsmProcess = self:AddProcess(Process.From, Process.Event, STATEMACHINE_PROCESS:New( Process.Process, Controllable, Task ), Process.ReturnEvents ) + end + + for EndStateID, EndState in pairs( FsmT:GetEndStates() ) do + self:E( EndState ) + self:AddEndState( EndState ) + end return self end @@ -442,21 +471,17 @@ end --- Assign the process to a @{Unit} and activate the process. --- @param #PROCESS self +-- @param #STATEMACHINE_PROCESS self -- @param Task.Tasking#TASK_BASE Task -- @param Wrapper.Unit#UNIT ProcessUnit --- @return #PROCESS self -function STATEMACHINE_PROCESS:Assign( Task, ProcessUnit ) +-- @return #STATEMACHINE_PROCESS self +function STATEMACHINE_PROCESS:Assign( ProcessUnit, Task ) self:E( { Task, ProcessUnit } ) self:SetControllable( ProcessUnit ) self:SetTask( Task ) - self.ProcessGroup = ProcessUnit:GetGroup() - --Task:RemoveMenuForGroup( self.ProcessGroup ) - --Task:SetAssignedMenuForGroup( self.ProcessGroup ) - - --self:Activate() + --self.ProcessGroup = ProcessUnit:GetGroup() return self end @@ -486,7 +511,7 @@ end -- @param #string From -- @param #string To function STATEMACHINE_PROCESS:onstatechange( ProcessUnit, Event, From, To, Dummy ) - self:E( { ProcessUnit, Event, From, To, Dummy } ) + self:E( { ProcessUnit, Event, From, To, Dummy, self:IsTrace() } ) if self:IsTrace() then MESSAGE:New( "Process " .. self.ProcessName .. " : " .. Event .. " changed to state " .. To, 15 ):ToAll() @@ -601,12 +626,13 @@ function STATEMACHINE_TEMPLATE:New( Name ) -- Inherits from BASE local self = BASE:Inherit( self, BASE:New() ) -- #STATEMACHINE_TEMPLATE - self._Transitions = self.Transitions or {} - self._Processes = self.Processes or {} - self._EndStates = self.EndStates or {} self._StartState = "none" + self._Transitions = {} + self._Processes = {} + self._EndStates = {} + self._Scores = {} - self._Name = Name + self._Name = Name or "" return self end @@ -623,20 +649,24 @@ end function STATEMACHINE_TEMPLATE:GetTransitions() - return self._Transitions + return self._Transitions or {} end --- Set the default @{Process} template with key ProcessName providing the ProcessClass and the process object when it is assigned to a @{Controllable} by the task. -- @return Process#PROCESS -function STATEMACHINE_TEMPLATE:AddProcess( From, Event, ProcessTemplate, ProcessArguments, ReturnEvents ) +function STATEMACHINE_TEMPLATE:AddProcess( From, Event, ProcessTemplate, ReturnEvents ) + + self:E( { ProcessTemplate = ProcessTemplate } ) local Process = {} Process.From = From Process.Event = Event Process.Process = ProcessTemplate - Process.Arguments = ProcessArguments + Process.Parameters = ProcessTemplate:GetParameters() Process.ReturnEvents = ReturnEvents + self:E( { From = Process.From, Event = Process.Event, Process = Process.Process._Name, Parameters = Process.Parameters, ReturnEvents = Process.ReturnEvents } ) + -- Make the reference table weak. -- setmetatable( self.options.subs, { __mode = "v" } ) self._Processes[Process] = Process @@ -646,27 +676,48 @@ end function STATEMACHINE_TEMPLATE:GetProcesses() - return self._Processes + return self._Processes or {} end +function STATEMACHINE_TEMPLATE:GetProcess( From, Event ) + + for ProcessID, Process in pairs( self:GetProcesses() ) do + if Process.From == From and Process.Event == Event then + self:E( Process ) + return Process.Process + end + end + + error( "Sub-Process from state " .. From .. " with event " .. Event .. " not found!" ) +end + +function STATEMACHINE_TEMPLATE:SetParameters( Parameters ) + self._Parameters = Parameters +end + +function STATEMACHINE_TEMPLATE:GetParameters() + return self._Parameters or {} +end + + function STATEMACHINE_TEMPLATE:AddEndState( State ) - self._EndStates[EndState] = EndState + self._EndStates[State] = State end function STATEMACHINE_TEMPLATE:GetEndStates() - return self._EndStates + return self._EndStates or {} end -function STATEMACHINE_TEMPLATE:AddStartState() +function STATEMACHINE_TEMPLATE:SetStartState( State ) - self._StartState = StartState + self._StartState = State end function STATEMACHINE_TEMPLATE:GetStartState() - return self._StartState + return self._StartState or {} end --- Adds a score for the STATEMACHINE_PROCESS to be achieved. @@ -678,16 +729,31 @@ end function STATEMACHINE_TEMPLATE:AddScore( State, ScoreText, Score ) self:F2( { State, ScoreText, Score } ) - self.Scores[State] = self.Scores[State] or {} - self.Scores[State].ScoreText = ScoreText - self.Scores[State].Score = Score + self._Scores[State] = self._Scores[State] or {} + self._Scores[State].ScoreText = ScoreText + self._Scores[State].Score = Score return self end -function STATEMACHINE_TEMPLATE:CopyCallHandler( Fsm, OnAction, Transition ) - self:E( { Fsm.ClassName, OnAction, Transition } ) - if OnAction and Transition and self[OnAction .. Transition] then - Fsm[OnAction .. Transition] = self[OnAction .. Transition] - end +--- Adds a score for the STATEMACHINE_PROCESS to be achieved. +-- @param #STATEMACHINE_TEMPLATE self +-- @param #string From is the From State of the main process. +-- @param #string Event is the Event of the main process. +-- @param #string State is the state of the process when the score needs to be given. (See the relevant state descriptions of the process). +-- @param #string ScoreText is a text describing the score that is given according the status. +-- @param #number Score is a number providing the score of the status. +-- @return #STATEMACHINE_TEMPLATE self +function STATEMACHINE_TEMPLATE:AddScoreProcess( From, Event, State, ScoreText, Score ) + self:F2( { Event, State, ScoreText, Score } ) + + local Process = self:GetProcess( From, Event ) + + self:E( { Process = Process._Name, Scores = Process._Scores, State = State, ScoreText = ScoreText, Score = Score } ) + Process._Scores[State] = Process._Scores[State] or {} + Process._Scores[State].ScoreText = ScoreText + Process._Scores[State].Score = Score + + return Process end + diff --git a/Moose Development/Moose/Process/Account.lua b/Moose Development/Moose/Process/Account.lua index 4fc0774e7..769607aee 100644 --- a/Moose Development/Moose/Process/Account.lua +++ b/Moose Development/Moose/Process/Account.lua @@ -102,7 +102,7 @@ do -- PROCESS_ACCOUNT self:AddEndState( "Accounted" ) self:AddEndState( "Failed" ) - self:AddStartState( "Assigned" ) + self:SetStartState( "Assigned" ) return self end @@ -117,14 +117,7 @@ do -- PROCESS_ACCOUNT -- @param #string To function PROCESS_ACCOUNT:onafterStart( ProcessUnit, Event, From, To ) - -- TODO: need to generalize the timing. - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - self.DisplayCategory = "HQ" -- Targets is the default display category - - self:EventOnDead( self.EventDead ) + self:EventOnDead( self.onfuncEventDead ) self:__Wait( 1 ) end @@ -181,23 +174,20 @@ do -- PROCESS_ACCOUNT_DEADS -- Inherits from BASE local self = BASE:Inherit( self, PROCESS_ACCOUNT:New() ) -- #PROCESS_ACCOUNT_DEADS - return self, { TargetSetUnit, TaskName } - end - - - --- Creates a new DESTROY process. - -- @param #PROCESS_ACCOUNT_DEADS self - -- @param Set#SET_UNIT TargetSetUnit - -- @param #string TaskName - -- @return #PROCESS_ACCOUNT_DEADS self - function PROCESS_ACCOUNT_DEADS:Init( TargetSetUnit, TaskName ) - - self.TargetSetUnit = TargetSetUnit - self.TaskName = TaskName + self:SetParameters( { + TargetSetUnit = TargetSetUnit, + TaskName = TaskName, + DisplayInterval = 30, + DisplayCount = 30, + DisplayMessage = true, + DisplayTime = 10, -- 10 seconds is the default + DisplayCategory = "HQ", -- Targets is the default display category + } ) return self end + function PROCESS_ACCOUNT_DEADS:_Destructor() self:E("_Destructor") @@ -260,7 +250,7 @@ do -- PROCESS_ACCOUNT_DEADS --- @param #PROCESS_ACCOUNT_DEADS self -- @param Event#EVENTDATA EventData - function PROCESS_ACCOUNT_DEADS:EventDead( EventData ) + function PROCESS_ACCOUNT_DEADS:onfuncEventDead( EventData ) self:T( { "EventDead", EventData } ) if EventData.IniDCSUnit then diff --git a/Moose Development/Moose/Process/Assign.lua b/Moose Development/Moose/Process/Assign.lua index ac7ef2c0b..64fc918bc 100644 --- a/Moose Development/Moose/Process/Assign.lua +++ b/Moose Development/Moose/Process/Assign.lua @@ -110,7 +110,7 @@ do -- PROCESS_ASSIGN self:AddEndState( "Rejected" ) self:AddEndState( "Failed" ) - self:AddStartState( "UnAssigned" ) + self:SetStartState( "UnAssigned" ) return self end @@ -136,26 +136,14 @@ do -- PROCESS_ASSIGN_ACCEPT -- @param #PROCESS_ASSIGN_ACCEPT self -- @param #string TaskBriefing function PROCESS_ASSIGN_ACCEPT:New( TaskBriefing ) - -- Inherits from BASE + local self = BASE:Inherit( self, PROCESS_ASSIGN:New() ) -- #PROCESS_ASSIGN_ACCEPT - return self, { TaskBriefing } - end - - - --- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted. - -- @param #PROCESS_ASSIGN_ACCEPT self - -- @param #string TaskBriefing - -- @return #PROCESS_ASSIGN_ACCEPT The task acceptance process. - function PROCESS_ASSIGN_ACCEPT:Init( TaskBriefing ) - - - self.TaskBriefing = TaskBriefing + self:SetParameters( { TaskBriefing = TaskBriefing } ) return self end - --- StateMachine callback function -- @param #PROCESS_ASSIGN_ACCEPT self -- @param Wrapper.Unit#UNIT ProcessUnit @@ -178,6 +166,7 @@ do -- PROCESS_ASSIGN_ACCEPT -- @param #string From -- @param #string To function PROCESS_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, Event, From, To ) + env.info( "in here" ) self:E( { ProcessUnit, Event, From, To } ) local ProcessGroup = ProcessUnit:GetGroup() @@ -211,8 +200,10 @@ do -- PROCESS_ASSIGN_MENU_ACCEPT -- Inherits from BASE local self = BASE:Inherit( self, PROCESS_ASSIGN:New() ) -- #PROCESS_ASSIGN_MENU_ACCEPT + + self:SetParameters( { TaskName = TaskName, TaskBriefing = TaskBriefing } ) - return self, { TaskName, TaskBriefing } + return self end diff --git a/Moose Development/Moose/Process/Route.lua b/Moose Development/Moose/Process/Route.lua index bd9ed812b..21b55e44b 100644 --- a/Moose Development/Moose/Process/Route.lua +++ b/Moose Development/Moose/Process/Route.lua @@ -110,7 +110,7 @@ do -- PROCESS_ROUTE self:AddEndState( "Arrived" ) self:AddEndState( "Failed" ) - self:AddStartState( "None" ) + self:SetStartState( "None" ) return self end @@ -125,11 +125,6 @@ do -- PROCESS_ROUTE -- @param #string To function PROCESS_ROUTE:onafterStart( ProcessUnit, Event, From, To ) - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - self.DisplayCategory = "HQ" -- Route is the default display category self:__Route( 1 ) end @@ -138,7 +133,7 @@ do -- PROCESS_ROUTE -- @param #PROCESS_ROUTE self -- @param Controllable#CONTROLLABLE ProcessUnit -- @return #boolean - function PROCESS_ROUTE:HasArrived( ProcessUnit ) + function PROCESS_ROUTE:onfuncHasArrived( ProcessUnit ) return false end @@ -151,7 +146,7 @@ do -- PROCESS_ROUTE function PROCESS_ROUTE:onbeforeRoute( ProcessUnit, Event, From, To ) if ProcessUnit:IsAlive() then - local HasArrived = self:HasArrived( ProcessUnit ) -- Polymorphic + local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic if self.DisplayCount >= self.DisplayInterval then self:T( { HasArrived = HasArrived } ) if not HasArrived then @@ -200,26 +195,23 @@ do -- PROCESS_ROUTE_ZONE function PROCESS_ROUTE_ZONE:New( TargetZone ) local self = BASE:Inherit( self, PROCESS_ROUTE:New() ) -- #PROCESS_ROUTE_ZONE - return self, { TargetZone } - end - - - --- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE. - -- @param #PROCESS_ROUTE_ZONE self - -- @param Zone#ZONE_BASE TargetZone - -- @return #PROCESS_ROUTE_ZONE self - function PROCESS_ROUTE_ZONE:New( TargetZone ) - - self.TargetZone = TargetZone + self:SetParameters( { + TargetZone = TargetZone, + DisplayInterval = 30, + DisplayCount = 30, + DisplayMessage = true, + DisplayTime = 10, -- 10 seconds is the default + DisplayCategory = "HQ", -- Route is the default display category + } ) return self end - + --- Method override to check if the controllable has arrived. -- @param #PROCESS_ROUTE self -- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit -- @return #boolean - function PROCESS_ROUTE_ZONE:HasArrived( ProcessUnit ) + function PROCESS_ROUTE_ZONE:onfuncHasArrived( ProcessUnit ) if ProcessUnit:IsInZone( self.TargetZone ) then local RouteText = ProcessUnit:GetCallsign() .. ": You have arrived within the zone!" diff --git a/Moose Development/Moose/Process/Smoke.lua b/Moose Development/Moose/Process/Smoke.lua index 653d27ca2..6b8017815 100644 --- a/Moose Development/Moose/Process/Smoke.lua +++ b/Moose Development/Moose/Process/Smoke.lua @@ -92,7 +92,7 @@ do -- PROCESS_SMOKE self:AddEndState( "Failed" ) self:AddEndState( "Success" ) - self:AddStartState( "None" ) + self:SetStartState( "None" ) return self end @@ -153,7 +153,9 @@ do -- PROCESS_SMOKE_TARGETS_ZONE function PROCESS_SMOKE_TARGETS_ZONE:New( TargetSetUnit, TargetZone ) local self = BASE:Inherit( self, PROCESS_SMOKE:New() ) -- #PROCESS_SMOKE - return self, { TargetSetUnit, TargetZone } + self:SetParameters( { TargetSetUnit, TargetZone } ) + + return self end --- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 0af1475f8..78db18589 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -30,15 +30,6 @@ MISSION = { _GoalTasks = {} } ---- @type MISSION.Clients --- @list - -function MISSION:Meta() - - - return self -end - --- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. -- @param #MISSION self -- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter @@ -50,6 +41,12 @@ end function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition ) local self = BASE:Inherit( self, STATEMACHINE:New() ) -- Core.StateMachine#STATEMACHINE + + self:SetStartState( "Idle" ) + + self:AddTransition( "Idle", "Start", "Ongoing" ) + self:AddTransition( "Ongoing", "Stop", "Idle" ) + self:AddTransition( "Ongoing", "Finish", "Finished" ) self:T( { MissionName, MissionPriority, MissionBriefing, MissionCoalition } ) @@ -66,10 +63,6 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi -- Build the Fsm for the mission. - self:SetInitialState( "Idle" ) - self:AddAction( "Idle", "Start", "Ongoing" ) - self:AddAction( "Ongoing", "Stop", "Idle" ) - self:AddAction( "Ongoing", "Finish", "Finished" ) return self end diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 7670b2d28..e7b6897b9 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -77,13 +77,12 @@ TASK_BASE = { -- @return #TASK_BASE self function TASK_BASE:New( Mission, SetGroupAssign, TaskName, TaskType ) + local self = BASE:Inherit( self, STATEMACHINE_TASK:New() ) -- Core.StateMachine#STATEMACHINE_TASK - local self = BASE:Inherit( self, STATEMACHINE_TASK:New( {} ) ) -- Core.StateMachine#STATEMACHINE_TASK - - self:SetInitialState( "Planned" ) - self:AddAction( "Planned", "Assign", "Assigned" ) - self:AddAction( "Assigned", "Success", "Success" ) - self:AddAction( "*", "Fail", "Failed" ) + self:SetStartState( "Planned" ) + self:AddTransition( "Planned", "Assign", "Assigned" ) + self:AddTransition( "Assigned", "Success", "Success" ) + self:AddTransition( "*", "Fail", "Failed" ) self:E( "New TASK " .. TaskName ) @@ -99,7 +98,7 @@ function TASK_BASE:New( Mission, SetGroupAssign, TaskName, TaskType ) self.TaskBriefing = "You are invited for the task: " .. self.TaskName .. "." - self.FsmTemplate = self.FsmTemplate or STATEMACHINE_TEMPLATE:New( {} ) + self.FsmTemplate = self.FsmTemplate or STATEMACHINE_TEMPLATE:New( "MAIN" ) -- Handle the birth of new planes within the assigned set. self:EventOnPlayerEnterUnit( @@ -186,33 +185,9 @@ function TASK_BASE:AssignToUnit( TaskUnit ) self:F( TaskUnit:GetName() ) -- Assign a new FsmUnit to TaskUnit. - local FsmUnit = self:SetStateMachine( TaskUnit, STATEMACHINE_PROCESS:New( self:GetFsmTemplate() ) ) -- Core.StateMachine#STATEMACHINE_PROCESS + local FsmUnit = self:SetStateMachine( TaskUnit, STATEMACHINE_PROCESS:New( self:GetFsmTemplate(), TaskUnit, self ) ) -- Core.StateMachine#STATEMACHINE_PROCESS self:E({"Address FsmUnit", tostring( FsmUnit ) } ) - --TODO: need to check all this with templates ... - FsmUnit:Assign( self, TaskUnit ) - - for TransitionID, Transition in pairs( self.FsmTemplate:GetTransitions() ) do - FsmUnit:AddAction( Transition.From, Transition.Event, Transition.To ) - self.FsmTemplate:CopyCallHandler( FsmUnit, "onenter", Transition.From ) - self.FsmTemplate:CopyCallHandler( FsmUnit, "onleave", Transition.From ) - self.FsmTemplate:CopyCallHandler( FsmUnit, "onenter", Transition.To ) - self.FsmTemplate:CopyCallHandler( FsmUnit, "onleave", Transition.To ) - self.FsmTemplate:CopyCallHandler( FsmUnit, "onbefore", Transition.Event ) - self.FsmTemplate:CopyCallHandler( FsmUnit, "onafter", Transition.Event ) - end - - for ProcessID, Process in pairs( self.FsmTemplate:GetProcesses() ) do - self:E( Process ) - local FsmProcess = FsmUnit:AddProcess(Process.From, Process.Event, Process.Process:New( unpack( Process.Arguments ) ), Process.ReturnEvents ) - self.FsmTemplate:CopyCallHandler( FsmProcess, "onenter", Process.From ) - self.FsmTemplate:CopyCallHandler( FsmProcess, "onleave", Process.From ) - self.FsmTemplate:CopyCallHandler( FsmProcess, "onbefore", Process.Event ) - self.FsmTemplate:CopyCallHandler( FsmProcess, "onafter", Process.Event ) - - FsmProcess:Assign( self, TaskUnit ) - end - -- Set the events FsmUnit:EventOnPilotDead( --- @param Core.Event#EVENTDATA EventData @@ -221,7 +196,7 @@ function TASK_BASE:AssignToUnit( TaskUnit ) end ) - FsmUnit:SetInitialState( "Planned" ) + FsmUnit:SetStartState( "Planned" ) FsmUnit:Accept() -- Each Task needs to start with an Accept event to start the flow. return self 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 341b9130d..c5c6dc151 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 @@ -75,32 +75,32 @@ local FsmSEADTemplate = TaskSEAD:GetFsmTemplate() -- 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. -FsmSEADTemplate:AddProcess ( "Planned", "Accept", PROCESS_ASSIGN_ACCEPT:Init( "SEAD the Area" ), { Assigned = "Route", Rejected = "Eject" } ) +FsmSEADTemplate:AddProcess ( "Planned", "Accept", PROCESS_ASSIGN_ACCEPT:New( "SEAD the Area" ), { Assigned = "Route", Rejected = "Eject" } ) -- Same, adding a process. -FsmSEADTemplate:AddProcess ( "Assigned", "Route", PROCESS_ROUTE_ZONE:Init( TargetZone, 3000 ), { Arrived = "Update" } ) +FsmSEADTemplate:AddProcess ( "Assigned", "Route", PROCESS_ROUTE_ZONE:New( TargetZone ), { 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: +-- The AddTransition 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. FsmSEADTemplate:AddTransition ( "Rejected", "Eject", "Planned" ) FsmSEADTemplate:AddTransition ( "Arrived", "Update", "Updated" ) -FsmSEADTemplate:AddProcess ( "Updated", "Account", PROCESS_ACCOUNT_DEADS:Init( TargetSet, "SEAD" ), { Accounted = "Success" } ) -FsmSEADTemplate:AddProcess ( "Updated", "Smoke", PROCESS_SMOKE_TARGETS_ZONE:Init( TargetSet, TargetZone ) ) +FsmSEADTemplate:AddProcess ( "Updated", "Account", PROCESS_ACCOUNT_DEADS:New( TargetSet, "SEAD" ), { Accounted = "Success" } ) +FsmSEADTemplate:AddProcess ( "Updated", "Smoke", PROCESS_SMOKE_TARGETS_ZONE:New( TargetSet, TargetZone ) ) FsmSEADTemplate:AddTransition ( "Accounted", "Success", "Success" ) FsmSEADTemplate:AddTransition ( "*", "Fail", "Failed" ) -TaskSEAD:AddScoreProcess( "Account", "Account", "destroyed a radar", 25 ) -TaskSEAD:AddScoreProcess( "Account", "Failed", "failed to destroy a radar", -10 ) +FsmSEADTemplate:AddScoreProcess( "Updated", "Account", "Account", "destroyed a radar", 25 ) +FsmSEADTemplate:AddScoreProcess( "Updated", "Account", "Failed", "failed to destroy a radar", -10 ) -- 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. -FsmSEADTemplate:AddScoreTask( "Success", "Destroyed all target radars", 250 ) -FsmSEADTemplate:AddScoreTask( "Failed", "Failed to destroy all target radars", -100 ) +FsmSEADTemplate:AddScore( "Success", "Destroyed all target radars", 250 ) +FsmSEADTemplate:AddScore( "Failed", "Failed to destroy all target radars", -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 8877febb9e034e643fc513ba1938de64b454dcc2..e49d55005be7aabe4aab573fae0628fc4097fefb 100644 GIT binary patch delta 13881 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 VaDmje&k;KH#E}@L*6zQ%{|7?xSf&5~ delta 13944 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